diff --git a/src/main/java/com/youlai/boot/common/constant/CommonConstants.java b/src/main/java/com/youlai/boot/common/constant/CommonConstants.java index cf1063f..131ff6f 100644 --- a/src/main/java/com/youlai/boot/common/constant/CommonConstants.java +++ b/src/main/java/com/youlai/boot/common/constant/CommonConstants.java @@ -1,5 +1,7 @@ package com.youlai.boot.common.constant; +import java.time.format.DateTimeFormatter; + public class CommonConstants { //最多上传的图片数量 @@ -13,7 +15,11 @@ public class CommonConstants { public static final String SIGN_DAY_KEY = "sign:user:%s:%s"; //连续签到天数缓存Key:占位符=用户ID public static final String SIGN_CONTINUOUS_KEY = "sign:continuous:%s"; - //用户分享次数Key:占位符1=周期类型,占位符2=用户ID,占位符3=周期标识 - public static final String SHARE_COUNT_KEY = "share:count:%s:%s:%s"; + + // 积分奖励周期计数Key:占位符1=业务前缀,占位符2=周期类型(all/day/week/month/year),占位符3=用户ID,占位符4=周期标识 + public static final String REWARD_COUNT_KEY = "reward:count:%s:%s:%s:%s"; + // 公用日期格式化器,避免重复创建 + public static final DateTimeFormatter DATE_FORMATTER_DAY = DateTimeFormatter.ofPattern("yyyyMMdd"); + public static final DateTimeFormatter DATE_FORMATTER_MONTH = DateTimeFormatter.ofPattern("yyyyMM"); } diff --git a/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java index fb810b3..2a7e1c2 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.admin.service.PointManageService; +import com.youlai.boot.common.constant.CommonConstants; import com.youlai.boot.common.exception.MsgException; import com.youlai.boot.mini.mapper.MiniPointAccountMapper; import com.youlai.boot.mini.mapper.MiniPointRecordMapper; @@ -28,6 +29,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAdjusters; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @Service @@ -54,7 +56,7 @@ public class MiniPointRecordServiceImpl extends ServiceImpl() .eq(MiniPointRule::getRuleCode, ruleCode) .eq(MiniPointRule::getStatus, false) @@ -62,56 +64,40 @@ public class MiniPointRecordServiceImpl extends ServiceImpl= limitCount) { - return new Object[]{false, 0, null, 0L, limitPeriod, "该奖励仅可领取" + limitCount + "次", redisNormal}; - } - // ALL周期缓存设置10年过期,足够覆盖用户生命周期 - return new Object[]{true, rewardPoint, countKey, 3650L, limitPeriod, null, redisNormal}; - } - // limit_period = null 才是完全无限制,不做任何次数校验 + // 完全无限制场景:不做次数校验 if (limitPeriod == null) { return new Object[]{true, rewardPoint, null, 0L, limitPeriod, null, true}; } - // 3. 周期计算 + // 2. 统一处理所有周期 String periodKey; long expireDays = 0; String limitTip = ""; switch (limitPeriod) { + case "ALL": + periodKey = "all"; + expireDays = 3650L; // 10年过期,覆盖用户生命周期 + limitTip = "该奖励仅可领取" + limitCount + "次"; + break; case "DAY": - periodKey = now.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + periodKey = now.format(CommonConstants.DATE_FORMATTER_DAY); expireDays = 1L; limitTip = "今日奖励已达上限,请明天再来"; break; case "WEEK": - //用本周一日期作为周期key,解决跨年周计数分裂问题 LocalDate monday = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); - periodKey = monday.format(DateTimeFormatter.ofPattern("yyyyMMdd")); - expireDays = 8L; + periodKey = monday.format(CommonConstants.DATE_FORMATTER_DAY); + expireDays = 8L; // 多留1天避免时区/临界点问题 limitTip = "本周奖励已达上限,请下周再来"; break; case "MONTH": - periodKey = now.format(DateTimeFormatter.ofPattern("yyyyMM")); + periodKey = now.format(CommonConstants.DATE_FORMATTER_MONTH); expireDays = 32L; limitTip = "本月奖励已达上限,请下月再来"; break; @@ -124,22 +110,43 @@ public class MiniPointRecordServiceImpl extends ServiceImpl { + try { + // 用setIfAbsent避免Redis恢复期间已有新请求更新了计数导致覆盖 + redisTemplate.opsForValue().setIfAbsent(countKey, String.valueOf(finalCurrentCount), finalExpireDays, TimeUnit.DAYS); + log.info("Redis恢复,回写周期计数成功,key={}, count={}", countKey, finalCurrentCount); + } catch (Exception ex) { + // 回写失败只打日志,不影响主业务 + log.warn("Redis回写计数失败,key={}", countKey, ex); + } + }); } + + // 计数超限校验 if (currentCount >= limitCount) { return new Object[]{false, 0, null, 0L, limitPeriod, limitTip, redisNormal}; } + // 返回顺序完全和原来一致,不影响现有调用方 return new Object[]{true, rewardPoint, countKey, expireDays, limitPeriod, null, redisNormal}; } @@ -191,7 +198,7 @@ public class MiniPointRecordServiceImpl extends ServiceImpl