|
|
@@ -347,100 +347,128 @@ public class PointActivityServiceImpl extends ServiceImpl<PointActivityMapper, P
|
|
|
Long taskId = task.getId();
|
|
|
Long activityId = task.getActivityId();
|
|
|
|
|
|
- PointActivity entity = this.pointActivityMapper.selectPointActivityById(activityId);
|
|
|
- if (ObjectUtil.isNull(entity)) {
|
|
|
+ PointActivity activity = this.pointActivityMapper.selectPointActivityById(activityId);
|
|
|
+ if (ObjectUtil.isNull(activity)) {
|
|
|
throw new IllegalArgumentException("参数有误,活动不存在");
|
|
|
}
|
|
|
|
|
|
- Date activityStartTime = entity.getStartTime();
|
|
|
+ // 3. 基础配置
|
|
|
+ ZoneId zoneId = ZoneId.of("Asia/Shanghai");
|
|
|
+ LocalDate today = LocalDate.now(zoneId);
|
|
|
int basePoints = task.getBasePoints() == null ? 0 : task.getBasePoints();
|
|
|
|
|
|
- // 3. 查询连续签到奖励配置(按continue_days升序,保证累加顺序)
|
|
|
- LambdaQueryWrapper<PointSignReward> rewardQuery = new LambdaQueryWrapper<>();
|
|
|
- rewardQuery.eq(PointSignReward::getSignTaskId, taskId)
|
|
|
- .eq(PointSignReward::getIsDeleted, 0)
|
|
|
- .orderByAsc(PointSignReward::getContinueDays);
|
|
|
- List<PointSignReward> rewardList = pointSignRewardService.list(rewardQuery);
|
|
|
+ LocalDate activityStartDate = activity.getStartTime() == null ? null
|
|
|
+ : activity.getStartTime().toInstant().atZone(zoneId).toLocalDate();
|
|
|
+ LocalDate activityEndDate = activity.getEndTime() == null ? null
|
|
|
+ : activity.getEndTime().toInstant().atZone(zoneId).toLocalDate();
|
|
|
+ boolean isPermanent = "1".equals(activity.getIsPermanent());
|
|
|
|
|
|
- // 4. 查询用户签到状态 & 当前连续天数
|
|
|
- boolean signedToday = pointUserSignLogService.countTodaySign(openId, taskId) > 0;
|
|
|
- PointUserSignStatus status = pointUserSignStatusService.selectByOpenIdAndActivityIdForUpdate(openId, activityId);
|
|
|
- int currentContinuousDays = (status == null) ? 0 : status.getCurrentContinuousDays();
|
|
|
+ // 4. 阶梯奖励配置(按 continue_days 升序,满足“累加阶梯奖”)
|
|
|
+ List<PointSignReward> rewardList = pointSignRewardService.list(new LambdaQueryWrapper<PointSignReward>()
|
|
|
+ .eq(PointSignReward::getSignTaskId, taskId)
|
|
|
+ .eq(PointSignReward::getIsDeleted, 0)
|
|
|
+ .orderByAsc(PointSignReward::getContinueDays));
|
|
|
|
|
|
- // ==================== 计算本周日期 ====================
|
|
|
- LocalDate today = LocalDate.now();
|
|
|
+ // 5. 用户当前连续天数(读接口不加锁)
|
|
|
+ PointUserSignStatus status = pointUserSignStatusService.getOne(new LambdaQueryWrapper<PointUserSignStatus>()
|
|
|
+ .eq(PointUserSignStatus::getOpenId, openId)
|
|
|
+ .eq(PointUserSignStatus::getActivityId, activityId)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ int currentContinuousDays = (status == null || status.getCurrentContinuousDays() == null) ? 0 : status.getCurrentContinuousDays();
|
|
|
+ // 今天是否已签到:直接让 DB 判断 DATE 是否为今天,避免 JDBC/时区导致的跨天偏移
|
|
|
+ boolean signedToday = pointUserSignStatusService.count(new LambdaQueryWrapper<PointUserSignStatus>()
|
|
|
+ .eq(PointUserSignStatus::getOpenId, openId)
|
|
|
+ .eq(PointUserSignStatus::getActivityId, activityId)
|
|
|
+ .apply("last_sign_date = CURDATE()")) > 0;
|
|
|
+
|
|
|
+ // 6. 本周范围(周日-周六)
|
|
|
DayOfWeek dayOfWeek = today.getDayOfWeek();
|
|
|
int daysSinceSunday = (dayOfWeek.getValue() == 7) ? 0 : dayOfWeek.getValue();
|
|
|
- LocalDate thisWeekSunday = today.minusDays(daysSinceSunday);
|
|
|
-
|
|
|
- // ==================== 查询本周签到记录 ====================
|
|
|
- ZoneId zoneId = ZoneId.of("Asia/Shanghai");
|
|
|
- Set<LocalDate> signedDateSet = new HashSet<>();
|
|
|
- List<PointUserSignLog> signLogList = pointUserSignLogService.list(
|
|
|
- new LambdaQueryWrapper<PointUserSignLog>()
|
|
|
- .eq(PointUserSignLog::getOpenId, openId)
|
|
|
- .eq(PointUserSignLog::getTaskId, taskId)
|
|
|
- );
|
|
|
+ LocalDate weekStart = today.minusDays(daysSinceSunday);
|
|
|
+ LocalDate weekEndExclusive = weekStart.plusDays(7);
|
|
|
+
|
|
|
+ // 7. 本周签到日志(只取本周,避免历史污染)
|
|
|
+ // 注意:历史数据里 PointUserSignLog.points 可能仅存“阶梯加成”(如 5/6/7),
|
|
|
+ // 面板展示需要按 continuousDays + 当前配置重算“基础分+阶梯累加”。
|
|
|
+ Map<LocalDate, PointUserSignLog> signedLogMap = new HashMap<>();
|
|
|
+ Date weekStartDate = Date.from(weekStart.atStartOfDay(zoneId).toInstant());
|
|
|
+ Date weekEndDateExclusive = Date.from(weekEndExclusive.atStartOfDay(zoneId).toInstant());
|
|
|
+ // 显式列名,避免字段映射差异导致过滤失效
|
|
|
+ List<PointUserSignLog> signLogList = pointUserSignLogService.list(new LambdaQueryWrapper<PointUserSignLog>()
|
|
|
+ .eq(PointUserSignLog::getOpenId, openId)
|
|
|
+ .eq(PointUserSignLog::getTaskId, taskId)
|
|
|
+ .ge(PointUserSignLog::getSignDate, weekStartDate)
|
|
|
+ .lt(PointUserSignLog::getSignDate, weekEndDateExclusive));
|
|
|
if (CollUtil.isNotEmpty(signLogList)) {
|
|
|
- signedDateSet = signLogList.stream()
|
|
|
- .map(log -> log.getSignDate().toInstant().atZone(zoneId).toLocalDate())
|
|
|
- .collect(Collectors.toSet());
|
|
|
+ for (PointUserSignLog log : signLogList) {
|
|
|
+ if (log == null || log.getSignDate() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ LocalDate signDate = log.getSignDate().toInstant().atZone(zoneId).toLocalDate();
|
|
|
+ signedLogMap.put(signDate, log);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // ==================== 生成7天签到数据 ====================
|
|
|
- List<SignDayVo> voList = new ArrayList<>();
|
|
|
- // 核心:未来连续天数
|
|
|
- int futureContinues = 1;
|
|
|
+ // 8. 生成 7 天面板数据
|
|
|
+ List<SignDayVo> voList = new ArrayList<>(7);
|
|
|
+ // 面板口径:今天未签到时,今天永远按“连续第 1 天”预估
|
|
|
+ int virtualTodayContinuousDays = signedToday ? currentContinuousDays : 1;
|
|
|
+ int nextContinuousDays = virtualTodayContinuousDays + 1;
|
|
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
|
- LocalDate date = thisWeekSunday.plusDays(i);
|
|
|
- boolean isToday = date.equals(today);
|
|
|
- boolean signed = signedDateSet.contains(date);
|
|
|
+ LocalDate date = weekStart.plusDays(i);
|
|
|
+ PointUserSignLog signedLog = signedLogMap.get(date);
|
|
|
+ // 今天是否已签,以状态表为准(更权威且是 date 类型)
|
|
|
+ boolean signed = date.equals(today) ? signedToday : signedLog != null;
|
|
|
|
|
|
SignDayVo vo = new SignDayVo();
|
|
|
vo.setDate(date);
|
|
|
|
|
|
- // 活动未开始
|
|
|
- if (activityStartTime != null) {
|
|
|
- LocalDate activityStartDate = activityStartTime.toInstant().atZone(zoneId).toLocalDate();
|
|
|
- if (date.isBefore(activityStartDate)) {
|
|
|
- vo.setStatus(4);
|
|
|
- vo.setPoints(basePoints);
|
|
|
- voList.add(vo);
|
|
|
- continue;
|
|
|
- }
|
|
|
+ // 8.1 活动有效性校验:不在活动时间范围内,直接过期(status=4)
|
|
|
+ if ((activityStartDate != null && date.isBefore(activityStartDate))
|
|
|
+ || (!isPermanent && activityEndDate != null && date.isAfter(activityEndDate))) {
|
|
|
+ vo.setStatus(4);
|
|
|
+ vo.setPoints(basePoints);
|
|
|
+ voList.add(vo);
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- // 状态判断
|
|
|
- if (isToday && !signed) {
|
|
|
- vo.setStatus(2);
|
|
|
- } else if (signed) {
|
|
|
+ // 8.2 状态计算
|
|
|
+ if (signed) {
|
|
|
vo.setStatus(1);
|
|
|
+ } else if (date.equals(today)) {
|
|
|
+ vo.setStatus(2);
|
|
|
} else if (date.isAfter(today)) {
|
|
|
vo.setStatus(0);
|
|
|
} else {
|
|
|
- long daysDiff = ChronoUnit.DAYS.between(date, today);
|
|
|
- vo.setStatus(daysDiff > 1 ? 4 : 3);
|
|
|
+ // 过去未签:昨天算断签(3),更早算过期(4)
|
|
|
+ vo.setStatus(date.equals(today.minusDays(1)) ? 3 : 4);
|
|
|
}
|
|
|
|
|
|
- // ==================== 积分核心:完全动态累加 ====================
|
|
|
- int points = basePoints;
|
|
|
- if (vo.getStatus() == 4) {
|
|
|
- points = basePoints;
|
|
|
- } else if (isToday) {
|
|
|
- points = basePoints;
|
|
|
- } else if (date.isAfter(today)) {
|
|
|
- int totalReward = 0;
|
|
|
- // 累加所有满足「连续天数 ≥ 配置天数」的奖励
|
|
|
- for (PointSignReward reward : rewardList) {
|
|
|
- if (futureContinues >= reward.getContinueDays()) {
|
|
|
- totalReward += reward.getRewardPoints();
|
|
|
- }
|
|
|
+ // 8.3 积分计算(基础分 + 阶梯累加)
|
|
|
+ int points;
|
|
|
+ if (vo.getStatus() == 1) {
|
|
|
+ Integer continuousDays = (signedLog == null) ? null : signedLog.getContinuousDays();
|
|
|
+ // 优先按连续天数重算(兼容旧数据 points=5/6/7 的情况)
|
|
|
+ if (continuousDays != null) {
|
|
|
+ points = calcCumulativeSignPoints(basePoints, continuousDays, rewardList);
|
|
|
+ } else if (signedLog != null && signedLog.getPoints() != null) {
|
|
|
+ // 兜底:如果日志里就是实际发放值(新数据),则直接回显
|
|
|
+ points = signedLog.getPoints();
|
|
|
+ } else if (date.equals(today) && signedToday) {
|
|
|
+ // 今天已签但本周日志未查到(极端情况):用状态表连续天数兜底
|
|
|
+ points = calcCumulativeSignPoints(basePoints, currentContinuousDays, rewardList);
|
|
|
+ } else {
|
|
|
+ points = basePoints;
|
|
|
}
|
|
|
- points = basePoints + totalReward;
|
|
|
- futureContinues++;
|
|
|
+ } else if (vo.getStatus() == 2) {
|
|
|
+ points = calcCumulativeSignPoints(basePoints, virtualTodayContinuousDays, rewardList);
|
|
|
+ } else if (vo.getStatus() == 0) {
|
|
|
+ points = calcCumulativeSignPoints(basePoints, nextContinuousDays, rewardList);
|
|
|
+ nextContinuousDays++;
|
|
|
} else {
|
|
|
- points = signed ? basePoints : 0;
|
|
|
+ // 断签/过期:未签到不展示可得积分
|
|
|
+ points = 0;
|
|
|
}
|
|
|
|
|
|
vo.setPoints(points);
|
|
|
@@ -450,6 +478,33 @@ public class PointActivityServiceImpl extends ServiceImpl<PointActivityMapper, P
|
|
|
return voList;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 签到积分计算:基础分 + 阶梯奖励累加
|
|
|
+ *
|
|
|
+ * 配置口径:reward.continueDays 表示“本档位需要再连续签到 N 天”,需要做累计阈值:
|
|
|
+ * 例如配置 1/2/3 天,对应阈值为 1、(1+2)=3、(1+2+3)=6。
|
|
|
+ *
|
|
|
+ * 业务规则:首日仅基础分,阶梯奖励从“超过阈值的下一天”开始生效,
|
|
|
+ * 即连续天数满足 (continuousDays > threshold) 才加对应档位奖励。
|
|
|
+ */
|
|
|
+ private int calcCumulativeSignPoints(int basePoints, int continuousDays, List<PointSignReward> rewardList) {
|
|
|
+ int total = Math.max(basePoints, 0);
|
|
|
+ if (continuousDays <= 0 || CollUtil.isEmpty(rewardList)) {
|
|
|
+ return total;
|
|
|
+ }
|
|
|
+ int threshold = 0;
|
|
|
+ for (PointSignReward reward : rewardList) {
|
|
|
+ if (reward == null || reward.getContinueDays() == null || reward.getRewardPoints() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ threshold += reward.getContinueDays();
|
|
|
+ if (continuousDays > threshold) {
|
|
|
+ total += reward.getRewardPoints();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return total;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void sign(SignDTO dto) {
|
|
|
@@ -504,16 +559,13 @@ public class PointActivityServiceImpl extends ServiceImpl<PointActivityMapper, P
|
|
|
pointUserSignStatusService.updateById(status);
|
|
|
}
|
|
|
|
|
|
- // 4. 计算奖励积分
|
|
|
- int rewardPoints = task.getBasePoints();
|
|
|
- PointSignReward reward = pointSignRewardService.getOne(new LambdaQueryWrapper<PointSignReward>()
|
|
|
+ // 4. 计算奖励积分(基础分 + 阶梯奖励累加)
|
|
|
+ int basePoints = task.getBasePoints() == null ? 0 : task.getBasePoints();
|
|
|
+ List<PointSignReward> rewardList = pointSignRewardService.list(new LambdaQueryWrapper<PointSignReward>()
|
|
|
.eq(PointSignReward::getSignTaskId, taskId)
|
|
|
- .le(PointSignReward::getContinueDays, newContinuousDays)
|
|
|
- .orderByDesc(PointSignReward::getContinueDays)
|
|
|
- .last("LIMIT 1"));
|
|
|
- if (reward != null) {
|
|
|
- rewardPoints = reward.getRewardPoints();
|
|
|
- }
|
|
|
+ .eq(PointSignReward::getIsDeleted, 0)
|
|
|
+ .orderByAsc(PointSignReward::getContinueDays));
|
|
|
+ int rewardPoints = calcCumulativeSignPoints(basePoints, newContinuousDays, rewardList);
|
|
|
|
|
|
// 5. 写入签到流水
|
|
|
PointUserSignLog signLog = new PointUserSignLog();
|