feat(缓存): 添加快速API缓存并优化暂存柜到期通知
添加fastApiCache用于快速API请求频率控制,过期时间为3秒 修改CabinetCellController使用fastApiCache替代apiCache并优化错误提示 在StorageCabinetExpiryJob中添加发送企业微信通知功能
This commit is contained in:
parent
f74f53520b
commit
98cee3f84e
|
|
@ -15,6 +15,15 @@ import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService;
|
||||||
|
import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity;
|
||||||
|
import com.agileboot.domain.qywx.api.QywxApiUtil;
|
||||||
|
import com.agileboot.domain.qywx.api.response.NewsArticle;
|
||||||
|
import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService;
|
||||||
|
import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
|
||||||
|
import com.agileboot.domain.qywx.user.db.QyUserService;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -26,6 +35,9 @@ public class StorageCabinetExpiryJob {
|
||||||
private final ShopService shopService;
|
private final ShopService shopService;
|
||||||
private final SmartCabinetService smartCabinetService;
|
private final SmartCabinetService smartCabinetService;
|
||||||
private final CabinetCellService cabinetCellService;
|
private final CabinetCellService cabinetCellService;
|
||||||
|
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
|
||||||
|
private final AccessTokenApplicationService accessTokenApplicationService;
|
||||||
|
private final QyUserService qyUserService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时任务:处理暂存柜到期格口
|
* 定时任务:处理暂存柜到期格口
|
||||||
|
|
@ -86,7 +98,7 @@ public class StorageCabinetExpiryJob {
|
||||||
log.info("商店[{}]下查找到 {} 个智能柜.", shop.getShopId(), cabinets.size());
|
log.info("商店[{}]下查找到 {} 个智能柜.", shop.getShopId(), cabinets.size());
|
||||||
|
|
||||||
for (SmartCabinetEntity cabinet : cabinets) {
|
for (SmartCabinetEntity cabinet : cabinets) {
|
||||||
int cabinetResetCount = processStorageCabinet(cabinet);
|
int cabinetResetCount = processStorageCabinet(shop, cabinet);
|
||||||
resetCount += cabinetResetCount;
|
resetCount += cabinetResetCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +118,7 @@ public class StorageCabinetExpiryJob {
|
||||||
/**
|
/**
|
||||||
* 处理单个智能柜的到期格口
|
* 处理单个智能柜的到期格口
|
||||||
*/
|
*/
|
||||||
private int processStorageCabinet(SmartCabinetEntity cabinet) {
|
private int processStorageCabinet(ShopEntity shop, SmartCabinetEntity cabinet) {
|
||||||
Integer returnDeadlineDays = cabinet.getReturnDeadline();
|
Integer returnDeadlineDays = cabinet.getReturnDeadline();
|
||||||
|
|
||||||
if (returnDeadlineDays == null || returnDeadlineDays <= 0) {
|
if (returnDeadlineDays == null || returnDeadlineDays <= 0) {
|
||||||
|
|
@ -124,12 +136,14 @@ public class StorageCabinetExpiryJob {
|
||||||
log.info("智能柜[{}]查找到 {} 个需重置的到期格口.", cabinet.getCabinetId(), expiredCells.size());
|
log.info("智能柜[{}]查找到 {} 个需重置的到期格口.", cabinet.getCabinetId(), expiredCells.size());
|
||||||
|
|
||||||
int resetCount = 0;
|
int resetCount = 0;
|
||||||
|
Date deadlineDate = DateUtil.offsetDay(new Date(), -returnDeadlineDays);
|
||||||
for (CabinetCellEntity cell : expiredCells) {
|
for (CabinetCellEntity cell : expiredCells) {
|
||||||
try {
|
try {
|
||||||
cabinetCellService.resetCellById(cell.getCellId());
|
cabinetCellService.resetCellById(cell.getCellId());
|
||||||
log.info("成功重置到期格口:格口ID[{}], 格口号[{}], 柜机ID[{}].",
|
log.info("成功重置到期格口:格口ID[{}], 格口号[{}], 柜机ID[{}].",
|
||||||
cell.getCellId(), cell.getCellNo(), cabinet.getCabinetId());
|
cell.getCellId(), cell.getCellNo(), cabinet.getCabinetId());
|
||||||
resetCount++;
|
resetCount++;
|
||||||
|
sendStorageExpiryNotification(shop, cabinet, cell, deadlineDate);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("重置格口[{}]失败", cell.getCellId(), e);
|
log.error("重置格口[{}]失败", cell.getCellId(), e);
|
||||||
}
|
}
|
||||||
|
|
@ -156,4 +170,42 @@ public class StorageCabinetExpiryJob {
|
||||||
|
|
||||||
return cabinetCellService.list(queryWrapper);
|
return cabinetCellService.list(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendStorageExpiryNotification(ShopEntity shop, SmartCabinetEntity cabinet, CabinetCellEntity cell, Date expiredDate) {
|
||||||
|
try {
|
||||||
|
List<String> adminUserIds = qyUserService.selectAdminUserIds(shop.getCorpid());
|
||||||
|
if (CollectionUtils.isEmpty(adminUserIds)) {
|
||||||
|
log.warn("商店[{}]未找到管理员用户,无法发送通知.", shop.getShopId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String toUser = String.join("|", adminUserIds);
|
||||||
|
String toparty = "";
|
||||||
|
String totag = "";
|
||||||
|
List<NewsArticle> articles = new ArrayList<>();
|
||||||
|
NewsArticle article = new NewsArticle();
|
||||||
|
article.setTitle("暂存柜格口逾期自动重置通知");
|
||||||
|
article.setDescription(String.format("商店名称: %s, 柜机名称: %s, 格口号: %s, 逾期时间: %s",
|
||||||
|
shop.getShopName(), cabinet.getCabinetName(), cell.getCellNo(), DateUtil.formatDateTime(expiredDate)));
|
||||||
|
|
||||||
|
QyAuthCorpInfoEntity authCorpInfo = authCorpInfoApplicationService.selectByCorpid(shop.getCorpid());
|
||||||
|
if (authCorpInfo == null) {
|
||||||
|
log.warn("商店[{}]未找到企业微信配置信息.", shop.getShopId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(authCorpInfo.getAppid(), authCorpInfo.getCorpid());
|
||||||
|
if (accessToken == null) {
|
||||||
|
log.warn("商店[{}]未获取到企业微信accessToken.", shop.getShopId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
articles.add(article);
|
||||||
|
QywxApiUtil.sendNewsMessage(accessToken.getAccessToken(), Integer.valueOf(authCorpInfo.getAgentid()),
|
||||||
|
toUser, toparty, totag, articles);
|
||||||
|
log.info("成功发送暂存柜格口逾期通知给商店[{}]的管理员.", shop.getShopId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送暂存柜格口逾期通知失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -240,12 +240,12 @@ public class CabinetCellController {
|
||||||
String cacheKey = "storeItem:" + command.getShopId() + ":" + command.getCellType();
|
String cacheKey = "storeItem:" + command.getShopId() + ":" + command.getCellType();
|
||||||
|
|
||||||
// 重复请求过滤
|
// 重复请求过滤
|
||||||
Boolean cached = caffeineCacheService.apiCache.get(cacheKey);
|
Boolean cached = caffeineCacheService.fastApiCache.get(cacheKey);
|
||||||
if (cached != null && cached) {
|
if (cached != null && cached) {
|
||||||
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "重复请求,请稍后重试"));
|
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "请求过于频繁,请稍后重试"));
|
||||||
}
|
}
|
||||||
// 放入缓存,标记请求已处理
|
// 放入缓存,标记请求已处理
|
||||||
caffeineCacheService.apiCache.put(cacheKey, Boolean.TRUE);
|
caffeineCacheService.fastApiCache.put(cacheKey, Boolean.TRUE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
CabinetCellDTO result = cabinetCellApplicationService.storeItemToCell(command);
|
CabinetCellDTO result = cabinetCellApplicationService.storeItemToCell(command);
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ public class CacheCenter {
|
||||||
|
|
||||||
public static AbstractCaffeineCacheTemplate<Boolean> apiCache;
|
public static AbstractCaffeineCacheTemplate<Boolean> apiCache;
|
||||||
|
|
||||||
|
public static AbstractCaffeineCacheTemplate<Boolean> fastApiCache;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
GuavaCacheService guavaCache = SpringUtil.getBean(GuavaCacheService.class);
|
GuavaCacheService guavaCache = SpringUtil.getBean(GuavaCacheService.class);
|
||||||
|
|
@ -62,6 +64,7 @@ public class CacheCenter {
|
||||||
accessTokenCache = caffeineCache.accessTokenCache;
|
accessTokenCache = caffeineCache.accessTokenCache;
|
||||||
corpidCache = caffeineCache.corpidCache;
|
corpidCache = caffeineCache.corpidCache;
|
||||||
apiCache = caffeineCache.apiCache;
|
apiCache = caffeineCache.apiCache;
|
||||||
|
fastApiCache = caffeineCache.fastApiCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,17 @@ public class CaffeineCacheService {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 快速API缓存:3秒过期和刷新(API调用参数)
|
||||||
|
public AbstractCaffeineCacheTemplate<Boolean> fastApiCache = new AbstractCaffeineCacheTemplate<Boolean>(
|
||||||
|
3, TimeUnit.SECONDS,
|
||||||
|
3, TimeUnit.SECONDS) {
|
||||||
|
@Override
|
||||||
|
public Boolean getObjectFromDb(Object id) {
|
||||||
|
// API缓存通常不需要从数据库获取,这里返回null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取缓存统计信息
|
* 获取缓存统计信息
|
||||||
* @return 统计信息字符串
|
* @return 统计信息字符串
|
||||||
|
|
@ -156,6 +167,7 @@ public class CaffeineCacheService {
|
||||||
stats.append("Access Token Cache: ").append(accessTokenCache.getStats()).append("\n");
|
stats.append("Access Token Cache: ").append(accessTokenCache.getStats()).append("\n");
|
||||||
stats.append("Corpid Cache: ").append(corpidCache.getStats()).append("\n");
|
stats.append("Corpid Cache: ").append(corpidCache.getStats()).append("\n");
|
||||||
stats.append("API Cache: ").append(apiCache.getStats()).append("\n");
|
stats.append("API Cache: ").append(apiCache.getStats()).append("\n");
|
||||||
|
stats.append("Fast API Cache: ").append(fastApiCache.getStats()).append("\n");
|
||||||
return stats.toString();
|
return stats.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,6 +198,8 @@ public class CaffeineCacheService {
|
||||||
return corpidCache;
|
return corpidCache;
|
||||||
case "apiCache":
|
case "apiCache":
|
||||||
return apiCache;
|
return apiCache;
|
||||||
|
case "fastApiCache":
|
||||||
|
return fastApiCache;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +212,7 @@ public class CaffeineCacheService {
|
||||||
public String[] getAllCacheNames() {
|
public String[] getAllCacheNames() {
|
||||||
return new String[]{
|
return new String[]{
|
||||||
"captchaCache", "loginUserCache", "userCache",
|
"captchaCache", "loginUserCache", "userCache",
|
||||||
"roleCache", "postCache", "qyUseridCache", "dynamicCodeCache", "accessTokenCache", "corpidCache", "apiCache"
|
"roleCache", "postCache", "qyUseridCache", "dynamicCodeCache", "accessTokenCache", "corpidCache", "apiCache", "fastApiCache"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue