package com.ylx.point.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ylx.common.core.domain.model.WxLoginUser; import com.ylx.common.exception.ServiceException; import com.ylx.common.exception.base.BaseException; import com.ylx.common.utils.DateUtils; import com.ylx.common.utils.SecurityUtils; import com.ylx.point.domain.*; import com.ylx.point.domain.dto.*; import com.ylx.point.domain.vo.*; import com.ylx.point.enums.PointActivityStatusEnum; import com.ylx.point.enums.PointActivityTypeEnum; import com.ylx.point.mapper.PointActivityMapper; import com.ylx.point.service.*; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.time.*; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; /** * 积分活动主Service业务层处理 * * @author wzj * @date 2026-03-25 */ @Slf4j @Service public class PointActivityServiceImpl extends ServiceImpl implements IPointActivityService { @Resource private PointActivityMapper pointActivityMapper; @Resource private IPointActivityTaskService pointActivityTaskService; @Resource private IPointSignTaskService pointSignTaskService; @Resource private IPointSignRewardService pointSignRewardService; @Resource private IPointUserActivityTaskCompletionService pointUserActivityTaskCompletionService; @Resource private IPointActivityExpirePolicyService pointActivityExpirePolicyService; @Resource private IPointUserSignLogService pointUserSignLogService; @Resource private IPointUserSignStatusService pointUserSignStatusService; @Resource private IPointAccountService pointAccountService; private static final int BATCH_SIZE = 1000; private static final int TARGET_COUNT = 2; private static final String INVALID_EXPIRE_POLICY_TYPE_MESSAGE = "无效的过期策略类型"; private static final String EXPIRE_DAYS_REQUIRED_MESSAGE = "过期天数不能为空"; private static final String EXPIRE_YEAR_REQUIRED_MESSAGE = "过期年数不能为空"; private static final String UPDATE_FAILED_MESSAGE = "更新积分活动过期策略失败"; // 优先级顺序 private static final List PRIORITY_TYPES = Arrays.asList( PointActivityTypeEnum.NEW_USER_ACTIVITY.getCode(), PointActivityTypeEnum.DAILY_ACTIVITY.getCode(), PointActivityTypeEnum.MONTHLY_ACTIVITY.getCode()); // 新手, 每日, 每月 @Override public List list(PointActivityPageDTO dto) { return pointActivityMapper.list(dto); } @Override public PointActivityDetailsVo getDetailsInfo(Long id) { PointActivity pointActivity = this.pointActivityMapper.selectPointActivityById(id); if (ObjectUtil.isNull(pointActivity)) { throw new BaseException("参数有误,活动不存在"); } Integer activityType = pointActivity.getActivityType(); if (PointActivityTypeEnum.SIGN_TASK.getCode().equals(activityType)) { PointSignTaskVo vo = this.pointSignTaskService.selectPointSignTaskByActivityId(pointActivity.getId()); BeanUtil.copyProperties(pointActivity, vo); return PointActivityDetailsVo.of(activityType, vo); } else { PointActivityTaskVo vo = new PointActivityTaskVo(); BeanUtil.copyProperties(pointActivity, vo); List activityTaskList = this.pointActivityTaskService.selectPointActivityTaskByActivityId(pointActivity.getId()); vo.setActivityTaskList(activityTaskList); return PointActivityDetailsVo.of(activityType, vo); } } @Override @Transactional(rollbackFor = Exception.class) public void add(PointActivityDTO dto) { // 校验参数 validateActivityDTO(dto); // 保存活动信息 PointActivity entity = createActivityEntity(dto); saveActivity(entity); // 保存活动详情信息 saveActivityDetails(dto, entity.getId()); } @Override @Transactional(rollbackFor = Exception.class) public void edit(PointActivityDTO dto) { // 校验参数 Long id = dto.getId(); if (ObjectUtil.isNull(id)) { throw new BaseException("活动主键id不能为空"); } // 根据id获取活动详情 PointActivity entity = this.pointActivityMapper.selectPointActivityById(id); if (ObjectUtil.isNull(entity)) { throw new BaseException("参数有误,活动不存在"); } // 校验活动状态 if (PointActivityStatusEnum.IN_PROGRESS.getCode().equals(entity.getStatus())) { throw new BaseException("活动进行中,不可编辑"); } updateActivity(entity, dto); // 更新活动详情信息 updateActivityDetails(dto, id); } @Override public PointActivityStatVo getStatInfo(Long id) { // 根据id获取活动详情 PointActivity entity = this.pointActivityMapper.selectPointActivityById(id); if (ObjectUtil.isNull(entity)) { throw new BaseException("参数有误,活动不存在"); } PointActivityStatVo vo = new PointActivityStatVo(); BeanUtil.copyProperties(entity, vo); // 数据总览 PointActivityOverviewVO dataOverview = this.pointUserActivityTaskCompletionService.getPointActivityOverviewByActivityId(id); vo.setDataOverview(dataOverview); // 数据详情 List taskDetailList = pointActivityMapper.getTaskDetailListById(id); vo.setTaskDetailList(taskDetailList); return vo; } @Override @Transactional(rollbackFor = Exception.class) public void editValidity(PointActivityValidityDTO dto) { // 根据dto中activityType获取全部的活动list PointActivity pointActivity = new PointActivity(); pointActivity.setActivityType(dto.getActivityType()); List activityList = pointActivityMapper.selectPointActivityList(pointActivity); // 根据类型获取积分活动过期策略表数据 PointActivityExpirePolicy expirePolicy = pointActivityExpirePolicyService.selectPointActivityExpirePolicyByActivityType(dto.getActivityType()); if (CollectionUtil.isEmpty(activityList)) { // 更新积分活动过期策略表 this.saveOrUpdatePointActivityExpirePolicy(expirePolicy, dto); } else { // 判断活动list中只要有活动的状态为"进行中",修改就失败 boolean hasOngoingActivity = activityList.stream() .anyMatch(activity -> PointActivityStatusEnum.IN_PROGRESS.getCode().equals(activity.getStatus())); if (hasOngoingActivity) { throw new BaseException("存在进行中的活动,无法修改有效期"); } // 更新积分活动过期策略表 this.saveOrUpdatePointActivityExpirePolicy(expirePolicy, dto); // 批量更新活动的有效期策略 batchUpdateExpirePolicy(activityList, expirePolicy.getId()); } } @Override public List validityDetails() { try { // 查询所有过期策略 List expirePolicies = this.pointActivityExpirePolicyService.list(); // 如果没有数据,返回空列表 if (CollectionUtil.isEmpty(expirePolicies)) { log.debug("没有查询到积分活动过期策略数据"); return Collections.emptyList(); } // 转换为VO对象 List result = expirePolicies.stream() .map(this::convertToVo) .collect(Collectors.toList()); log.info("成功查询到 {} 条积分活动有效期详情", result.size()); return result; } catch (Exception e) { log.error("查询积分活动有效期详情失败", e); throw new BaseException("查询积分活动有效期详情失败"); } } @Override public Integer selectTotalActiveTasks(String cityCode) { return pointActivityMapper.selectTotalActiveTasks(cityCode); } @Override public List getUserPointActivityList(UserPointActivityPageDTO dto) { // 当前登录用户信息 WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser(); if (ObjectUtil.isNull(wxLoginUser)) { throw new RuntimeException("用户不存在"); } String openId = wxLoginUser.getCOpenid(); dto.setOpenId(openId); return pointActivityMapper.selectTaskWithProgress(dto); } @Override public List activityList() { // 当前登录用户信息 WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser(); if (ObjectUtil.isNull(wxLoginUser)) { throw new RuntimeException("用户不存在"); } String openId = wxLoginUser.getCOpenid(); List resultList = new ArrayList<>(); // 2. 按优先级遍历活动类型 for (Integer activityType : PRIORITY_TYPES) { if (resultList.size() >= TARGET_COUNT) { break; } // 3. 查询该类型下 进行中 的任务 List taskList = pointActivityTaskService.selectTasksByActivityType(activityType); if (CollectionUtil.isEmpty(taskList)) { continue; } // 4. 批量查询用户完成记录 List taskIds = taskList.stream().map(PointActivityTask::getId).collect(Collectors.toList()); List completionList = pointUserActivityTaskCompletionService.selectCompletionsByOpenIdAndTaskIds(openId, taskIds); Map completionMap = completionList.stream() .collect(Collectors.toMap(PointUserActivityTaskCompletion::getTaskId, c -> c)); // 5. 筛选【未完成】的任务 for (PointActivityTask task : taskList) { if (resultList.size() >= TARGET_COUNT) break; PointUserActivityTaskCompletion completion = completionMap.get(task.getId()); if (isTaskIncomplete(task, completion)) { resultList.add(convertToVo(task, completion)); } } } return resultList.stream().limit(2).collect(Collectors.toList()); } @Override public List getSignInfo(SignDTO dto) { // ... (前面获取用户、任务、奖励配置的代码保持不变) ... // 1. 获取用户信息 & 任务 & 奖励列表 WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser(); if (ObjectUtil.isNull(wxLoginUser)) throw new ServiceException("用户不存在"); String openId = wxLoginUser.getCOpenid(); PointSignTask task = getEnabledTask(dto.getCityCode()); if (task == null) throw new ServiceException("当前城市暂无签到活动"); Long taskId = task.getId(); Long activityId = task.getActivityId(); LambdaQueryWrapper rewardQuery = new LambdaQueryWrapper<>(); rewardQuery.eq(PointSignReward::getSignTaskId, taskId); rewardQuery.orderByAsc(PointSignReward::getContinueDays); rewardQuery.last("LIMIT 7"); List rewardList = pointSignRewardService.list(rewardQuery); // 2. 获取状态 boolean signedToday = pointUserSignLogService.countTodaySign(openId, taskId) > 0; PointUserSignStatus status = pointUserSignStatusService.selectByOpenIdAndActivityIdForUpdate(openId, activityId); int continuousDays = (status == null) ? 0 : status.getCurrentContinuousDays(); // --- 核心修改:计算本周日期 & 状态判断 --- LocalDate today = LocalDate.now(); // 计算本周周日 DayOfWeek dayOfWeek = today.getDayOfWeek(); int daysSinceSunday = (dayOfWeek.getValue() == 7) ? 0 : dayOfWeek.getValue(); LocalDate thisWeekSunday = today.minusDays(daysSinceSunday); // 定义“过期”判定阈值:例如,超过 7 天未签视为过期 // 注意:这里的逻辑是,如果是“本周”的视图,通常只有“断签”和“未签”。 // 但如果你的业务是“补签卡”界面,或者需要展示历史状态,这个逻辑才有意义。 // 这里演示:如果漏签日期距离今天超过 7 天,标记为 4。 int expireThresholdDays = 7; List voList = new ArrayList<>(); for (int i = 0; i < 7; i++) { LocalDate date = thisWeekSunday.plusDays(i); int dayIndex = i + 1; SignDayVo vo = new SignDayVo(); vo.setDate(date); boolean isToday = date.equals(today); boolean isPast = date.isBefore(today); // 查询该天是否已签 (建议优化为批量查询) boolean actuallySigned = false; if (isToday) { actuallySigned = signedToday; } else { // 模拟查询 actuallySigned = pointUserSignLogService.count(new LambdaQueryWrapper() .eq(PointUserSignLog::getOpenId, openId) .eq(PointUserSignLog::getTaskId, taskId) .eq(PointUserSignLog::getSignDate, date)) > 0; } // --- 状态赋值逻辑 --- if (isToday && !actuallySigned) { vo.setStatus(2); // 2: 今天未签到 } else if (actuallySigned) { vo.setStatus(1); // 1: 已签 } else { // 没签到的情况 (且不是今天) // 计算该日期距离今天的天数 long daysDiff = ChronoUnit.DAYS.between(date, today); if (daysDiff > expireThresholdDays) { vo.setStatus(4); // 4: 已过期 (漏签太久,无法补签) } else { vo.setStatus(3); // 3: 断签 (漏签在可接受范围内) } } // --- 奖励匹配 --- PointSignReward reward = rewardList.stream() .filter(r -> r.getContinueDays() == dayIndex) .findFirst() .orElse(null); vo.setPoints(reward != null ? reward.getRewardPoints() : 0); voList.add(vo); } return voList; } @Override @Transactional(rollbackFor = Exception.class) public void sign(SignDTO dto) { // 1. 获取用户信息 WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser(); if (ObjectUtil.isNull(wxLoginUser)) { throw new RuntimeException("用户未登录"); } String openId = wxLoginUser.getCOpenid(); // 2. 获取当前城市有效的签到任务配置 // 建议:这里应该加缓存,避免每次签到都查库 PointSignTask task = getEnabledTask(dto.getCityCode()); if (ObjectUtil.isNull(task)) { throw new RuntimeException("当前城市暂无签到活动"); } Long activityId = task.getActivityId(); Long taskId = task.getId(); // 3. 防重复签到 (利用数据库唯一索引兜底,这里做快速失败) // 优化:直接查库比 count 快,且逻辑更清晰 PointUserSignLog todayLog = pointUserSignLogService.getOne(new LambdaQueryWrapper() .eq(PointUserSignLog::getOpenId, openId) .eq(PointUserSignLog::getTaskId, taskId) .eq(PointUserSignLog::getSignDate, DateUtils.getNowDate()) // 假设日志表有 sign_date 字段,如果没有用 create_time 转 date .last("LIMIT 1")); if (ObjectUtil.isNotNull(todayLog)) { throw new RuntimeException("今日已签到"); } // 4. 获取并锁定用户签到状态 (悲观锁) // 注意:selectByOpenIdAndActivityIdForUpdate 必须使用 FOR UPDATE 语法 PointUserSignStatus status = pointUserSignStatusService.selectByOpenIdAndActivityIdForUpdate(openId, activityId); int newContinuousDays = 1; DateTime today = DateUtil.date(); DateTime yesterday = DateUtil.yesterday(); if (ObjectUtil.isNull(status)) { // --- 首次签到 --- status = new PointUserSignStatus(); status.setOpenId(openId); status.setActivityId(activityId); status.setLastSignDate(today); status.setCurrentContinuousDays(1); status.setLastRewardCycleDays(0); pointUserSignStatusService.save(status); } else { // --- 非首次签到:计算连续天数 --- DateTime lastSignDate = status.getLastSignDate(); // 判断是否断签:昨天不是最后签到日期,即为断签 boolean isBreak = !DateUtil.isSameDay(lastSignDate, yesterday); if (isBreak) { if (task.getBreakRule() == 0) { // 规则0:断签重置 newContinuousDays = 1; } else { // 规则1:断签保留进度 (这里逻辑通常是 +1,除非你想做补签逻辑) newContinuousDays = status.getCurrentContinuousDays() + 1; } } else { // 正常连续 newContinuousDays = status.getCurrentContinuousDays() + 1; } // 更新状态 status.setLastSignDate(today); status.setCurrentContinuousDays(newContinuousDays); pointUserSignStatusService.updateById(status); } // 5. 计算奖励积分 // 优化:匹配 <= 当前天数的最大奖励配置 (例如配置了3天、7天,第5天应该拿3天的奖,或者拿基础奖) int rewardPoints = task.getBasePoints(); // 默认基础积分 PointSignReward reward = pointSignRewardService.getOne(new LambdaQueryWrapper() .eq(PointSignReward::getSignTaskId, taskId) .le(PointSignReward::getContinueDays, newContinuousDays) // 小于等于当前天数 .orderByDesc(PointSignReward::getContinueDays) .last("LIMIT 1")); if (ObjectUtil.isNotNull(reward)) { rewardPoints = reward.getRewardPoints(); } // 6. 写入签到流水日志 PointUserSignLog signLog = new PointUserSignLog(); signLog.setOpenId(openId); signLog.setActivityId(activityId); signLog.setTaskId(taskId); signLog.setSignDate(today); // 需确保数据库字段支持 Date 类型 signLog.setContinuousDays(newContinuousDays); signLog.setPoints(rewardPoints); signLog.setIsMakeUp(0); pointUserSignLogService.save(signLog); // 7. 增加用户积分账户余额 // 注意:这里假设 pointAccountService 内部会写入 point_user_log (积分总流水表) try { pointAccountService.addPoints(openId, rewardPoints, null, activityId, taskId, PointActivityTypeEnum.SIGN_TASK.getCode()); } catch (Exception e) { log.error("签到发放积分失败", e); throw new RuntimeException("签到成功,但积分发放失败,请联系客服"); } } /** * 判断任务是否未完成 */ private boolean isTaskIncomplete(PointActivityTask task, PointUserActivityTaskCompletion completion) { // 1. 获取规则要求的次数 int requiredCount = 0; try { requiredCount = Integer.parseInt(task.getTriggerValue()); } catch (NumberFormatException e) { // 如果配置错误,为了安全起见,默认视为未完成(或根据业务抛出异常) return true; } // 2. 获取用户实际完成的次数 int actualCount = 0; if (ObjectUtil.isNotNull(completion) && ObjectUtil.isNotNull(completion.getCompletedCount())) { actualCount = completion.getCompletedCount(); } // 3. 核心判断逻辑 // 如果 实际次数 < 要求次数,则任务“未完成” (返回 true) return actualCount < requiredCount; } /** * 组装 VO 对象 */ private UserPointActivityVo convertToVo(PointActivityTask task, PointUserActivityTaskCompletion completion) { UserPointActivityVo vo = new UserPointActivityVo(); vo.setActivityId(task.getActivityId()); vo.setTaskName(task.getTaskName()); // 或者是从字典表查出的中文名称 vo.setRewardPoints(task.getRewardPoints()); // 完成次数:如果没有记录则为 "0" vo.setCompletedCount(completion != null ? String.valueOf(completion.getCompletedCount()) : "0"); // 触发条件:直接从任务配置中获取,或者根据 taskCode 查字典 vo.setTriggerValue(task.getTriggerValue()); return vo; } private PointActivityValidityVo convertToVo(PointActivityExpirePolicy policy) { PointActivityValidityVo vo = new PointActivityValidityVo(); BeanUtil.copyProperties(policy, vo); // 可以在这里添加额外的转换逻辑 return vo; } private void validateActivityDTO(PointActivityDTO dto) { if (ObjectUtil.isNull(dto)) { throw new BaseException("活动信息不能为空"); } PointActivityDTO.ActivityTimeConfig timeConfig = dto.getTimeConfig(); if (ObjectUtil.isNull(timeConfig) || (!timeConfig.getIsPermanent() && (ObjectUtil.isNull(timeConfig.getStartTime()) || ObjectUtil.isNull(timeConfig.getEndTime())))) { throw new BaseException("活动时间配置不能为空"); } } private PointActivity createActivityEntity(PointActivityDTO dto) { // 创建基础实体 PointActivity entity = new PointActivity(); BeanUtil.copyProperties(dto, entity); // 处理时间配置 PointActivityDTO.ActivityTimeConfig timeConfig = dto.getTimeConfig(); entity.setStartTime(DateUtil.beginOfDay(timeConfig.getStartTime())); entity.setEndTime(processEndTime(timeConfig.getEndTime())); entity.setIsPermanent(timeConfig.getIsPermanent() ? 1 : 0); // 处理状态 if (PointActivityStatusEnum.PUBLISHED.getCode().equals(dto.getStatus())) { entity.setStatus(determineActivityStatus( entity.getStartTime(), entity.getEndTime(), timeConfig.getIsPermanent() )); } return entity; } private void saveActivity(PointActivity entity) { entity.setCreateBy(SecurityUtils.getUsername()); entity.setCreateTime(new Date()); int affectedRows = pointActivityMapper.insertPointActivity(entity); if (affectedRows <= 0) { throw new BaseException("保存积分活动失败"); } } private void saveOrUpdatePointActivityExpirePolicy(PointActivityExpirePolicy expirePolicy, PointActivityValidityDTO dto) { // 参数校验 if (ObjectUtil.isNull(dto)) { throw new IllegalArgumentException("参数不能为空"); } // 根据expirePolicy类型设置对应的过期时间 Integer expirePolicyCode = dto.getExpirePolicy(); if (ObjectUtil.isNull(expirePolicyCode)) { throw new IllegalArgumentException("过期策略类型不能为空"); } // 准备要保存或更新的实体 PointActivityExpirePolicy entity = expirePolicy != null ? expirePolicy : new PointActivityExpirePolicy(); // 设置基本信息 entity.setActivityType(dto.getActivityType()); entity.setExpirePolicy(dto.getExpirePolicy()); // 根据过期策略类型设置对应的过期时间 setExpireTimeByPolicyType(entity, dto, expirePolicyCode); // 保存或更新 saveOrUpdateExpirePolicy(entity, dto.getActivityType()); } private void setExpireTimeByPolicyType(PointActivityExpirePolicy entity, PointActivityValidityDTO dto, Integer expirePolicyCode) { // 先清空所有过期时间字段 entity.setExpireDays(null); entity.setExpireYear(null); // 根据策略类型设置对应的过期时间 switch (expirePolicyCode) { case 1: // PERMANENT_VALID - 永久有效 // 已经清空了过期天数和年数,无需额外操作 break; case 2: // TRANSACTION_EXPIRE - 按交易过期 if (dto.getExpireDays() == null) { throw new IllegalArgumentException(EXPIRE_DAYS_REQUIRED_MESSAGE); } entity.setExpireDays(dto.getExpireDays()); break; case 3: // YEARLY_EXPIRE - 按年过期 if (dto.getExpireYear() == null) { throw new IllegalArgumentException(EXPIRE_YEAR_REQUIRED_MESSAGE); } entity.setExpireYear(dto.getExpireYear()); break; default: throw new IllegalArgumentException(INVALID_EXPIRE_POLICY_TYPE_MESSAGE); } } private void saveOrUpdateExpirePolicy(PointActivityExpirePolicy entity, Integer activityType) { try { entity.setUpdateBy(SecurityUtils.getUsername()); entity.setUpdateTime(DateUtils.getNowDate()); boolean isSuccess = pointActivityExpirePolicyService.updateById(entity); if (!isSuccess) { log.error("保存或更新积分活动过期策略失败, activityType: {}", activityType); throw new BaseException(UPDATE_FAILED_MESSAGE); } } catch (Exception e) { log.error("保存或更新积分活动过期策略时发生异常, activityType: {}", activityType, e); throw new BaseException(UPDATE_FAILED_MESSAGE); } } private void saveActivityDetails(PointActivityDTO dto, Long activityId) { if (PointActivityTypeEnum.SIGN_TASK.getCode().equals(dto.getActivityType())) { saveSignTaskDetails(dto, activityId); } else { saveActivityTaskDetails(dto, activityId); } } private void updateActivityDetails(PointActivityDTO dto, Long activityId) { if (PointActivityTypeEnum.SIGN_TASK.getCode().equals(dto.getActivityType())) { updateSignTaskDetails(dto, activityId); } else { updateActivityTaskDetails(dto, activityId); } } private void updateActivity(PointActivity entity, PointActivityDTO dto) { // 更新活动信息 BeanUtil.copyProperties(dto, entity); // 处理时间配置 PointActivityDTO.ActivityTimeConfig timeConfig = dto.getTimeConfig(); if (timeConfig != null) { entity.setStartTime(DateUtil.beginOfDay(timeConfig.getStartTime())); entity.setEndTime(processEndTime(timeConfig.getEndTime())); entity.setIsPermanent(timeConfig.getIsPermanent() ? 1 : 0); } // 处理状态 if (PointActivityStatusEnum.PUBLISHED.getCode().equals(dto.getStatus())) { entity.setStatus(determineActivityStatus( entity.getStartTime(), entity.getEndTime(), timeConfig.getIsPermanent() )); } entity.setUpdateBy(SecurityUtils.getUsername()); entity.setUpdateTime(DateUtils.getNowDate()); int affectedRows = pointActivityMapper.updatePointActivity(entity); if (affectedRows <= 0) { throw new BaseException("更新积分活动失败"); } } private void saveSignTaskDetails(PointActivityDTO dto, Long activityId) { PointSignTaskDTO signTaskDTO = dto.getSignTaskDTO(); if (ObjectUtil.isNull(signTaskDTO)) { throw new BaseException("签到任务配置不能为空"); } PointSignTask pointSignTask = new PointSignTask(); pointSignTask.setActivityId(activityId); pointSignTask.setBasePoints(signTaskDTO.getBasePoints()); pointSignTask.setBreakRule(signTaskDTO.getBreakRule()); int affectedRows = pointSignTaskService.insertPointSignTask(pointSignTask); if (affectedRows <= 0) { throw new BaseException("保存积分签到任务规则失败"); } saveSignRewards(signTaskDTO.getSignRewardList(), pointSignTask.getId()); } private void saveSignRewards(List rewardList, Long taskId) { if (CollectionUtil.isEmpty(rewardList)) { return; } List rewards = rewardList.stream() .map(dto -> { PointSignReward reward = new PointSignReward(); reward.setRewardPoints(dto.getRewardPoints()); reward.setContinueDays(dto.getContinueDays()); reward.setCreateBy(SecurityUtils.getUsername()); reward.setCreateTime(DateUtils.getNowDate()); reward.setUpdateTime(reward.getCreateTime()); reward.setIsDeleted(0); reward.setSignTaskId(taskId); return reward; }) .collect(Collectors.toList()); pointSignRewardService.batchInsertPointSignReward(rewards); } private void saveActivityTaskDetails(PointActivityDTO dto, Long activityId) { List tasks = dto.getTasks(); if (CollectionUtil.isEmpty(tasks)) { return; } List activityTasks = tasks.stream() .map(task -> { PointActivityTask activityTask = new PointActivityTask(); BeanUtil.copyProperties(task, activityTask); activityTask.setActivityId(activityId); activityTask.setIsDeleted(0); activityTask.setCreateBy(SecurityUtils.getUsername()); activityTask.setCreateTime(DateUtils.getNowDate()); activityTask.setUpdateTime(activityTask.getCreateTime()); return activityTask; }) .collect(Collectors.toList()); pointActivityTaskService.batchInsertPointActivityTask(activityTasks); } /** * 处理结束时间,设置为当天的23:59:59 */ private Date processEndTime(Date endTime) { if (endTime == null) { return null; } try { LocalDateTime localDateTime = LocalDateTime.ofInstant( endTime.toInstant(), ZoneId.systemDefault() ); LocalDateTime endOfDay = localDateTime.with( java.time.LocalTime.of(23, 59, 59)); return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant()); } catch (DateTimeException e) { throw new IllegalArgumentException("Invalid end time", e); } } /** * 确定活动状态 */ private Integer determineActivityStatus(Date startTime, Date endTime, Boolean isPermanent) { if (isPermanent) { return PointActivityStatusEnum.IN_PROGRESS.getCode(); } Date now = DateUtils.getNowDate(); if (startTime.after(now)) { return PointActivityStatusEnum.NOT_START.getCode(); } else if (endTime != null && endTime.before(now)) { return PointActivityStatusEnum.FINISHED.getCode(); } else { return PointActivityStatusEnum.IN_PROGRESS.getCode(); } } /** * 更新签到任务详情 */ private void updateSignTaskDetails(PointActivityDTO dto, Long activityId) { PointSignTaskDTO signTaskDTO = dto.getSignTaskDTO(); if (ObjectUtil.isNull(signTaskDTO)) { throw new BaseException("签到任务配置不能为空"); } // 获取现有的签到任务 PointSignTaskVo existingTask = pointSignTaskService.selectPointSignTaskByActivityId(activityId); // 更新签到任务基本信息 PointSignTask pointSignTask = new PointSignTask(); pointSignTask.setId(existingTask.getSignTaskId()); pointSignTask.setBasePoints(signTaskDTO.getBasePoints()); pointSignTask.setBreakRule(signTaskDTO.getBreakRule()); // 更新签到任务 int affectedRows = pointSignTaskService.updatePointSignTask(pointSignTask); if (affectedRows <= 0) { throw new BaseException("更新积分签到任务规则失败"); } // 更新签到奖励配置 updateSignRewards(signTaskDTO.getSignRewardList(), pointSignTask.getId()); } /** * 更新签到奖励配置 */ private void updateSignRewards(List rewardList, Long taskId) { // 先删除原有的奖励配置 PointSignReward query = new PointSignReward(); query.setSignTaskId(taskId); List existingRewards = pointSignRewardService.selectPointSignRewardList(query); if (CollectionUtil.isNotEmpty(existingRewards)) { String[] ids = existingRewards.stream() .map(reward -> String.valueOf(reward.getId())) .toArray(String[]::new); pointSignRewardService.deletePointSignRewardByIds(ids); } // 重新插入新的奖励配置 if (CollectionUtil.isEmpty(rewardList)) { return; } List rewards = rewardList.stream() .map(dto -> { PointSignReward reward = new PointSignReward(); reward.setRewardPoints(dto.getRewardPoints()); reward.setContinueDays(dto.getContinueDays()); reward.setCreateTime(DateUtils.getNowDate()); reward.setUpdateTime(reward.getCreateTime()); reward.setIsDeleted(0); reward.setSignTaskId(taskId); return reward; }) .collect(Collectors.toList()); pointSignRewardService.batchInsertPointSignReward(rewards); } /** * 更新活动任务详情 */ private void updateActivityTaskDetails(PointActivityDTO dto, Long activityId) { // 先删除原有的活动任务 pointActivityTaskService.deletePointActivityTaskByActivityId(activityId); // 重新插入新的活动任务 List tasks = dto.getTasks(); if (CollectionUtil.isEmpty(tasks)) { return; } List activityTasks = tasks.stream() .map(task -> { PointActivityTask activityTask = new PointActivityTask(); BeanUtil.copyProperties(task, activityTask); activityTask.setActivityId(activityId); activityTask.setIsDeleted(0); activityTask.setUpdateBy(SecurityUtils.getUsername()); activityTask.setUpdateTime(DateUtils.getNowDate()); return activityTask; }) .collect(Collectors.toList()); pointActivityTaskService.batchInsertPointActivityTask(activityTasks); } private void batchUpdateExpirePolicy(List activityList, Long expirePolicyId) { // 参数校验 if (CollectionUtil.isEmpty(activityList)) { log.debug("活动列表为空,无需更新过期策略"); return; } if (expirePolicyId == null) { throw new IllegalArgumentException("过期策略ID不能为空"); } try { // 使用Stream处理活动列表,设置过期策略ID List activitiesToUpdate = activityList.stream() .map(activity -> { activity.setExpirePolicyId(expirePolicyId); activity.setUpdateTime(DateUtils.getNowDate()); activity.setUpdateBy(SecurityUtils.getUsername()); return activity; }) .collect(Collectors.toList()); // 批量更新数据库 super.updateBatchById(activitiesToUpdate, BATCH_SIZE); log.info("成功更新 {} 个活动的过期策略为 {}", activitiesToUpdate.size(), expirePolicyId); } catch (Exception e) { log.error("批量更新活动过期策略失败, expirePolicyId: {}", expirePolicyId, e); throw new BaseException("批量更新活动过期策略失败"); } } private PointSignTask getEnabledTask(String cityCode) { // 1. 根据城市查询当前生效的活动 PointActivity activity = pointActivityMapper.selectOne( new QueryWrapper() .eq("city_code", cityCode) .eq("status", 1) // 活动启用 .last("LIMIT 1") ); if (ObjectUtil.isNull(activity)) { throw new RuntimeException("当前城市未配置签到活动"); } // 2. 根据活动绑定的 taskId 查询签到任务 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(PointSignTask::getActivityId, activity.getId()); queryWrapper.eq(PointSignTask::getIsDeleted, 1); List list = pointSignTaskService.list(queryWrapper); PointSignTask task = new PointSignTask(); if (CollectionUtil.isNotEmpty(list)) { task = CollectionUtil.getLast(list); } return task; } }