From 631d5c175febfd1d03407bfb554b995dbd5ac15e Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 31 Oct 2025 13:46:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(payment):=20=E6=94=AF=E4=BB=98=E4=B8=8E?= =?UTF-8?q?=E9=80=80=E6=AC=BE=E5=90=8E=E6=B8=85=E9=99=A4=E6=99=AF=E5=8C=BA?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在支付成功、取消、退款回调后增加缓存删除逻辑 - 新增 `invalidateStatisticsCache` 方法用于删除 Redis 缓存 - 定时任务中统计景区数据后也调用缓存清除方法 - 调整景区统计任务时间并扩展统计周期为近7天 - 增强定时任务日志记录和异常处理机制 --- .../service/mobile/impl/WxPayServiceImpl.java | 22 ++++++ .../com/ycwl/basic/task/ScenicStatsTask.java | 68 +++++++++++++++---- 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java b/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java index 07502660..de9680cc 100644 --- a/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/mobile/impl/WxPayServiceImpl.java @@ -30,6 +30,7 @@ import com.ycwl.basic.service.pc.ScenicService; import com.ycwl.basic.utils.SnowFlakeUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import jakarta.servlet.http.HttpServletRequest; @@ -60,6 +61,8 @@ public class WxPayServiceImpl implements WxPayService { private OrderMapper orderMapper; @Autowired private ScenicService scenicService; + @Autowired + private RedisTemplate redisTemplate; @Override public Map createOrder(Long scenicId, WXPayOrderReqVO req) { @@ -100,10 +103,14 @@ public class WxPayServiceImpl implements WxPayService { long orderId = Long.parseLong(callbackResponse.getOrderNo()); if (callbackResponse.isPay()) { orderBiz.paidOrder(orderId); + // 支付成功后删除统计缓存,确保下次查询获取最新数据 + invalidateStatisticsCache(scenicId); } else if (callbackResponse.isCancel()) { orderBiz.cancelOrder(orderId); } else if (callbackResponse.isRefund()) { orderBiz.refundOrder(orderId); + // 退款后删除统计缓存 + invalidateStatisticsCache(scenicId); } }); } catch (Exception e) { @@ -165,6 +172,10 @@ public class WxPayServiceImpl implements WxPayService { statisticsRecordAddReq.setScenicId(order.getScenicId()); statisticsRecordAddReq.setMorphId(orderId); statisticsMapper.addStatisticsRecord(statisticsRecordAddReq); + + // 退款成功后删除统计缓存,确保下次查询获取最新数据 + invalidateStatisticsCache(scenicId); + return true; } catch (Exception e) { log.error("微信退款回调失败!", e); @@ -180,4 +191,15 @@ public class WxPayServiceImpl implements WxPayService { .setOrderNo(orderId); scenicPayAdapter.cancelOrder(request); } + + /** + * 删除景区统计缓存 + * 在支付或退款回调后调用,确保下次查询时重新计算统计数据 + * @param scenicId 景区ID + */ + private void invalidateStatisticsCache(Long scenicId) { + String redisKey = "statistics:tmp_cache:" + scenicId; + Boolean deleted = redisTemplate.delete(redisKey); + log.info("[缓存删除] 景区 {} 的统计缓存删除结果: {}", scenicId, deleted); + } } diff --git a/src/main/java/com/ycwl/basic/task/ScenicStatsTask.java b/src/main/java/com/ycwl/basic/task/ScenicStatsTask.java index 4b3764c5..c20584f9 100644 --- a/src/main/java/com/ycwl/basic/task/ScenicStatsTask.java +++ b/src/main/java/com/ycwl/basic/task/ScenicStatsTask.java @@ -11,8 +11,10 @@ import com.ycwl.basic.model.pc.scenicDeviceStats.entity.ScenicDeviceStatsEntity; import com.ycwl.basic.repository.ScenicRepository; import com.ycwl.basic.service.mobile.AppStatisticsService; import com.ycwl.basic.utils.ApiResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -20,6 +22,7 @@ import org.springframework.stereotype.Component; import java.util.Date; import java.util.List; +@Slf4j @Component @EnableScheduling @Profile("prod") @@ -32,6 +35,8 @@ public class ScenicStatsTask { private AppStatisticsService statisticsService; @Autowired private ScenicRepository scenicRepository; + @Autowired + private RedisTemplate redisTemplate; @Scheduled(cron = "0 1 0 * * *") public void countDeviceStats() { @@ -55,23 +60,58 @@ public class ScenicStatsTask { }); } } - @Scheduled(cron = "0 20 0 * * *") + @Scheduled(cron = "0 0 2 * * *") public void countScenicStats() { - Date yesterdayStart = DateUtil.beginOfDay(DateUtil.yesterday()); - Date yesterdayEnd = DateUtil.endOfDay(yesterdayStart); + log.info("开始执行景区统计任务,统计前7天至昨天的数据"); + + // 获取所有景区列表 ScenicReqQuery query = new ScenicReqQuery(); query.setPageSize(1000); List scenicList = scenicRepository.list(query); - scenicList.forEach((scenic) -> { - CommonQueryReq commonQueryReq = new CommonQueryReq(); - Long scenicId = Long.valueOf(scenic.getId()); - commonQueryReq.setScenicId(scenicId); - commonQueryReq.setStartTime(yesterdayStart); - commonQueryReq.setEndTime(yesterdayEnd); - commonQueryReq.setRealtime(true); - ApiResponse resp = statisticsService.userConversionFunnel(commonQueryReq); - AppStatisticsFunnelVO data = resp.getData(); - statisticsMapper.insertStat(scenicId, yesterdayStart, data); - }); + + // 循环统计前7天(至昨天)的数据 + for (int daysAgo = 1; daysAgo <= 7; daysAgo++) { + Date targetDate = DateUtil.offsetDay(new Date(), -daysAgo); + Date startTime = DateUtil.beginOfDay(targetDate); + Date endTime = DateUtil.endOfDay(targetDate); + + log.info("正在统计第{}天前的数据,日期: {}", daysAgo, DateUtil.formatDate(startTime)); + + scenicList.forEach((scenic) -> { + try { + CommonQueryReq commonQueryReq = new CommonQueryReq(); + Long scenicId = Long.valueOf(scenic.getId()); + commonQueryReq.setScenicId(scenicId); + commonQueryReq.setStartTime(startTime); + commonQueryReq.setEndTime(endTime); + commonQueryReq.setRealtime(true); + + // 执行统计查询 + ApiResponse resp = statisticsService.userConversionFunnel(commonQueryReq); + AppStatisticsFunnelVO data = resp.getData(); + + // 写入数据库(REPLACE INTO 会自动更新已存在的记录) + statisticsMapper.insertStat(scenicId, startTime, data); + + // 删除该景区的缓存,确保下次查询时获取最新数据 + invalidateStatisticsCache(scenicId); + } catch (Exception e) { + log.error("统计景区 {} 在日期 {} 的数据时发生错误", scenic.getId(), DateUtil.formatDate(startTime), e); + } + }); + + log.info("第{}天前的数据统计完成,日期: {}", daysAgo, DateUtil.formatDate(startTime)); + } + + log.info("景区统计任务执行完成"); + } + + /** + * 删除景区统计缓存 + * @param scenicId 景区ID + */ + private void invalidateStatisticsCache(Long scenicId) { + String redisKey = "statistics:tmp_cache:" + scenicId; + redisTemplate.delete(redisKey); } }