Преглед на файлове

添加积分过期定时任务

wangzhijun преди 21 часа
родител
ревизия
0f8971ebe7
променени са 1 файла, в които са добавени 129 реда и са изтрити 0 реда
  1. 129 0
      nightFragrance-massage/src/main/java/com/ylx/massage/task/massageTask.java

+ 129 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/task/massageTask.java

@@ -2,6 +2,8 @@ package com.ylx.massage.task;
 
 import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ylx.common.constant.MassageConstants;
 import com.ylx.common.core.domain.entity.SysDept;
@@ -9,6 +11,7 @@ import com.ylx.common.utils.DateUtils;
 import com.ylx.common.utils.StringUtils;
 import com.ylx.massage.domain.*;
 import com.ylx.massage.enums.*;
+import com.ylx.point.domain.PointUserLog;
 import com.ylx.massage.mapper.TConsumptionLogMapper;
 import com.ylx.massage.service.*;
 import com.ylx.massage.utils.DateTimeUtils;
@@ -68,6 +71,9 @@ public class massageTask {
     @Resource
     private WeChatUtil weChatUtil;
 
+    @Resource
+    private com.ylx.point.service.IPointUserLogService pointUserLogService;
+
     /**
      * 取消超时未支付的订单
      */
@@ -461,4 +467,127 @@ public class massageTask {
         log.info("结束执行同步技师二维码,{}", DateUtils.getNowDate());
     }
 
+    /**
+     * 定时任务入口:批量更新过期的积分记录
+     */
+    public void expirePoints() {
+        Date nowDate = new Date();
+        log.info("开始执行积分过期任务,当前时间:{}", nowDate);
+
+        // 建议添加分布式锁,防止多实例并发执行
+        // String lockKey = "lock:points:expire:" + DateUtils.getDate();
+        // if (redisLock.tryLock(lockKey)) { ... }
+
+        try {
+            // 使用游标模式,避免深分页问题
+            Long lastId = 0L;
+            int batchSize = 500; // 适当调大批次
+
+            while (true) {
+                // 获取最后一批处理的ID,作为下一批的起点
+                Long currentLastId = processBatch(nowDate, lastId, batchSize);
+
+                // 如果返回的ID没有变化,或者小于等于上一轮ID,说明没有更多数据了
+                if (currentLastId <= lastId) {
+                    break;
+                }
+                lastId = currentLastId;
+            }
+        } finally {
+            // redisLock.unlock(lockKey);
+            log.info("结束执行积分过期任务,当前时间:{}", DateUtils.getNowDate());
+        }
+    }
+
+    /**
+     * 单批次处理逻辑
+     */
+    private Long processBatch(Date nowDate, Long lastId, int batchSize) {
+        // 1. 查询:使用 ID > lastId 代替分页,并限制数量
+        LambdaQueryWrapper<PointUserLog> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(PointUserLog::getOpType, 1)
+                .eq(PointUserLog::getIsExpired, 0)
+                .lt(PointUserLog::getExpireTime, nowDate)
+                .isNotNull(PointUserLog::getExpireTime)
+                .gt(PointUserLog::getId, lastId) // 关键优化:游标推进
+                .orderByAsc(PointUserLog::getId); // 必须按ID排序
+
+        Page<PointUserLog> page = new Page<>(1, batchSize);
+        Page<PointUserLog> resPage = pointUserLogService.page(page, queryWrapper);
+        List<PointUserLog> records = resPage.getRecords();
+
+        if (CollectionUtil.isEmpty(records)) {
+            return lastId;
+        }
+
+        // 准备批量数据
+        List<PointUserLog> insertList = new ArrayList<>();
+        List<Long> updateIds = new ArrayList<>();
+        long currentMaxId = lastId;
+
+        for (PointUserLog pointLog : records) {
+            // 2. 构建过期流水(插入)
+            PointUserLog expireLog = new PointUserLog();
+            // ... (属性拷贝,建议使用 BeanUtil.copyProperties)
+            expireLog.setOpenId(pointLog.getOpenId());
+            expireLog.setActivityId(pointLog.getActivityId());
+            expireLog.setActivityName(pointLog.getActivityName());
+            expireLog.setTaskId(pointLog.getTaskId());
+            expireLog.setTaskType(pointLog.getTaskType());
+            expireLog.setOpType(3); // 过期
+            expireLog.setSourceLogId(pointLog.getId());
+            expireLog.setPoints(-pointLog.getPoints()); // 注意:过期通常是扣减,这里应该是负数,具体看业务定义
+            expireLog.setIsExpired(1); // 这里的1通常指这条流水本身是“过期”类型的记录,状态是生效的
+
+            insertList.add(expireLog);
+            updateIds.add(pointLog.getId());
+
+            if (pointLog.getId() > currentMaxId) {
+                currentMaxId = pointLog.getId();
+            }
+        }
+
+        // 3. 批量执行:利用数据库事务保证原子性
+        boolean success = false;
+        try {
+            // 开启事务 (如果是Spring环境,建议在Service层加@Transactional)
+            // 批量插入过期流水
+            pointUserLogService.saveBatch(insertList);
+
+            // 批量更新原记录状态 (使用 LambdaUpdateWrapper in 查询)
+            LambdaUpdateWrapper<PointUserLog> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.in(PointUserLog::getId, updateIds)
+                    .set(PointUserLog::getIsExpired, 1);
+            pointUserLogService.update(updateWrapper);
+
+            success = true;
+            log.info("批次处理成功,处理条数:{}", records.size());
+        } catch (Exception e) {
+            log.error("批次处理失败", e);
+            // 根据业务需求决定是否抛出异常中断,或者记录错误日志继续
+            throw new RuntimeException("积分过期处理失败", e);
+        }
+
+        return success ? currentMaxId : lastId;
+    }
+
+    /**
+     * 获取用户当前可用积分余额
+     * @param openId 用户openId
+     * @return 当前余额
+     */
+    private Integer getBalance(String openId) {
+        QueryWrapper<PointUserLog> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("SUM(points)");
+        queryWrapper.eq("open_id", openId);
+
+        PointUserLog pointUserLog = pointUserLogService.getOne(queryWrapper);
+
+        if (pointUserLog == null || pointUserLog.getPoints() == null) {
+            return 0;
+        }
+
+        return pointUserLog.getPoints();
+    }
+
 }