refactor(statistics): 重构应用统计漏斗服务的Redis缓存逻辑

- 移除固定的Redis缓存key,改为包含日期维度的动态key
- 修复日期范围检查逻辑中的时间顺序问题
- 统一多处相同的日期范围条件判断代码
- 移除实时模式下的数据持久化操作以避免缓存污染
This commit is contained in:
2026-02-05 00:58:14 +08:00
parent ee1eb8cde9
commit 6c330764ea
2 changed files with 14 additions and 16 deletions

View File

@@ -145,7 +145,6 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
@Override @Override
public ApiResponse<AppStatisticsFunnelVO> userConversionFunnel(CommonQueryReq query) { public ApiResponse<AppStatisticsFunnelVO> userConversionFunnel(CommonQueryReq query) {
String redisKey = "statistics:tmp_cache:"+query.getScenicId();
AppStatisticsFunnelVO vo = new AppStatisticsFunnelVO(); AppStatisticsFunnelVO vo = new AppStatisticsFunnelVO();
if(query.getEndTime()==null && query.getStartTime()==null){ if(query.getEndTime()==null && query.getStartTime()==null){
// 没有传时间,则代表用户没有自定义查询时间,使用standard来判断查询时间范围 // 没有传时间,则代表用户没有自定义查询时间,使用standard来判断查询时间范围
@@ -156,8 +155,12 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
//获取当前周期的具体时间范围 //获取当前周期的具体时间范围
standardToNewSpecificTime(query); standardToNewSpecificTime(query);
} }
// 构建包含日期维度的 Redis 缓存 key
String redisKey = String.format("statistics:tmp_cache:%s:%s",
query.getScenicId(),
query.getStartTime() != null ? DateUtil.formatDate(query.getStartTime()) : "null");
if (!query.isRealtime()) { if (!query.isRealtime()) {
if (!(DateUtil.isIn(query.getStartTime(), DateUtil.tomorrow(), DateUtil.yesterday()) && DateUtil.isIn(query.getEndTime(), DateUtil.tomorrow(), DateUtil.yesterday()))) { if (!(DateUtil.isIn(query.getStartTime(), DateUtil.yesterday(), DateUtil.tomorrow()) && DateUtil.isIn(query.getEndTime(), DateUtil.yesterday(), DateUtil.tomorrow()))) {
// 查询缓存数据 // 查询缓存数据
List<AppStatisticsFunnelVO> list = statisticsMapper.listStatByScenic(query.getScenicId(), query.getStartTime(), query.getEndTime()); List<AppStatisticsFunnelVO> list = statisticsMapper.listStatByScenic(query.getScenicId(), query.getStartTime(), query.getEndTime());
AppStatisticsFunnelVO resp = new AppStatisticsFunnelVO(); AppStatisticsFunnelVO resp = new AppStatisticsFunnelVO();
@@ -189,7 +192,7 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
} }
} }
if (!query.isRealtime()) { if (!query.isRealtime()) {
if (DateUtil.isIn(query.getStartTime(), DateUtil.tomorrow(), DateUtil.yesterday()) && DateUtil.isIn(query.getEndTime(), DateUtil.tomorrow(), DateUtil.yesterday())) { if (DateUtil.isIn(query.getStartTime(), DateUtil.yesterday(), DateUtil.tomorrow()) && DateUtil.isIn(query.getEndTime(), DateUtil.yesterday(), DateUtil.tomorrow())) {
// 缓存 // 缓存
if (redisTemplate.hasKey(redisKey)) { if (redisTemplate.hasKey(redisKey)) {
String s = redisTemplate.opsForValue().get(redisKey); String s = redisTemplate.opsForValue().get(redisKey);
@@ -203,7 +206,7 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
try { try {
// 缓存 // 缓存
if (!query.isRealtime()) { if (!query.isRealtime()) {
if (DateUtil.isIn(query.getStartTime(), DateUtil.tomorrow(), DateUtil.yesterday()) && DateUtil.isIn(query.getEndTime(), DateUtil.tomorrow(), DateUtil.yesterday())) { if (DateUtil.isIn(query.getStartTime(), DateUtil.yesterday(), DateUtil.tomorrow()) && DateUtil.isIn(query.getEndTime(), DateUtil.yesterday(), DateUtil.tomorrow())) {
// 缓存 // 缓存
if (redisTemplate.hasKey(redisKey)) { if (redisTemplate.hasKey(redisKey)) {
String s = redisTemplate.opsForValue().get(redisKey); String s = redisTemplate.opsForValue().get(redisKey);
@@ -270,14 +273,6 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
vo.setPayOfOrderAmount(payOfOrderAmount.setScale(2, RoundingMode.HALF_UP)); vo.setPayOfOrderAmount(payOfOrderAmount.setScale(2, RoundingMode.HALF_UP));
vo.setRefundOfOrderNum(refundOfOrderNum); vo.setRefundOfOrderNum(refundOfOrderNum);
vo.setRefundOfOrderAmount(refundOfOrderAmount.setScale(2, RoundingMode.HALF_UP)); vo.setRefundOfOrderAmount(refundOfOrderAmount.setScale(2, RoundingMode.HALF_UP));
// 仅在非 realtime 模式下写入缓存
// realtime=true 时由调用方(如定时任务)自行控制写入目标日期,不污染当天缓存
if (!query.isRealtime()) {
if (query.getScenicId() != null) {
statisticsMapper.insertStat(query.getScenicId(), new Date(), vo);
}
redisTemplate.opsForValue().set(redisKey, JacksonUtil.toJSONString(vo), 60, TimeUnit.SECONDS);
}
return ApiResponse.success(vo); return ApiResponse.success(vo);
} finally { } finally {
lock.unlock(); lock.unlock();

View File

@@ -93,8 +93,8 @@ public class ScenicStatsTask {
// 写入数据库(REPLACE INTO 会自动更新已存在的记录) // 写入数据库(REPLACE INTO 会自动更新已存在的记录)
statisticsMapper.insertStat(scenicId, startTime, data); statisticsMapper.insertStat(scenicId, startTime, data);
// 删除该景区的缓存,确保下次查询时获取最新数据 // 删除该景区该日期的缓存,确保下次查询时获取最新数据
invalidateStatisticsCache(scenicId); invalidateStatisticsCache(scenicId, startTime);
} catch (Exception e) { } catch (Exception e) {
log.error("统计景区 {} 在日期 {} 的数据时发生错误", scenic.getId(), DateUtil.formatDate(startTime), e); log.error("统计景区 {} 在日期 {} 的数据时发生错误", scenic.getId(), DateUtil.formatDate(startTime), e);
} }
@@ -109,9 +109,12 @@ public class ScenicStatsTask {
/** /**
* 删除景区统计缓存 * 删除景区统计缓存
* @param scenicId 景区ID * @param scenicId 景区ID
* @param date 统计日期
*/ */
private void invalidateStatisticsCache(Long scenicId) { private void invalidateStatisticsCache(Long scenicId, Date date) {
String redisKey = "statistics:tmp_cache:" + scenicId; String redisKey = String.format("statistics:tmp_cache:%s:%s",
scenicId,
DateUtil.formatDate(date));
redisTemplate.delete(redisKey); redisTemplate.delete(redisKey);
} }
} }