|
@@ -2,6 +2,8 @@ package com.ylx.massage.task;
|
|
|
|
|
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
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.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.ylx.common.constant.MassageConstants;
|
|
import com.ylx.common.constant.MassageConstants;
|
|
|
import com.ylx.common.core.domain.entity.SysDept;
|
|
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.common.utils.StringUtils;
|
|
|
import com.ylx.massage.domain.*;
|
|
import com.ylx.massage.domain.*;
|
|
|
import com.ylx.massage.enums.*;
|
|
import com.ylx.massage.enums.*;
|
|
|
|
|
+import com.ylx.point.domain.PointUserLog;
|
|
|
import com.ylx.massage.mapper.TConsumptionLogMapper;
|
|
import com.ylx.massage.mapper.TConsumptionLogMapper;
|
|
|
import com.ylx.massage.service.*;
|
|
import com.ylx.massage.service.*;
|
|
|
import com.ylx.massage.utils.DateTimeUtils;
|
|
import com.ylx.massage.utils.DateTimeUtils;
|
|
@@ -68,6 +71,9 @@ public class massageTask {
|
|
|
@Resource
|
|
@Resource
|
|
|
private WeChatUtil weChatUtil;
|
|
private WeChatUtil weChatUtil;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private com.ylx.point.service.IPointUserLogService pointUserLogService;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 取消超时未支付的订单
|
|
* 取消超时未支付的订单
|
|
|
*/
|
|
*/
|
|
@@ -461,4 +467,127 @@ public class massageTask {
|
|
|
log.info("结束执行同步技师二维码,{}", DateUtils.getNowDate());
|
|
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();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|