package com.designhub.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.designhub.common.ResultCode; import com.designhub.entity.User; import com.designhub.entity.UserGoal; import com.designhub.exception.BusinessException; import com.designhub.mapper.UserGoalMapper; import com.designhub.mapper.UserMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.time.temporal.TemporalAdjusters; import java.util.List; /** * 用户目标Service */ @Slf4j @Service @RequiredArgsConstructor public class UserGoalService { private final UserGoalMapper userGoalMapper; private final UserMapper userMapper; private final NotificationService notificationService; /** * 创建目标 */ @Transactional public UserGoal createGoal(UserGoal goal) { // 计算周期时间 calculatePeriodTime(goal); goal.setCurrentValue(0); goal.setStatus("active"); goal.setRewardClaimed(0); goal.setDeleted(0); userGoalMapper.insert(goal); log.info("创建目标成功: userId={}, goalName={}", goal.getUserId(), goal.getGoalName()); return goal; } /** * 计算周期开始和结束时间 */ private void calculatePeriodTime(UserGoal goal) { LocalDateTime now = LocalDateTime.now(); switch (goal.getPeriod()) { case "daily": goal.setPeriodStart(now.toLocalDate().atStartOfDay()); goal.setPeriodEnd(now.toLocalDate().atTime(23, 59, 59)); break; case "weekly": goal.setPeriodStart(now.with(TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY)).toLocalDate().atStartOfDay()); goal.setPeriodEnd(now.with(TemporalAdjusters.nextOrSame(java.time.DayOfWeek.SUNDAY)).toLocalDate().atTime(23, 59, 59)); break; case "monthly": goal.setPeriodStart(now.with(TemporalAdjusters.firstDayOfMonth()).toLocalDate().atStartOfDay()); goal.setPeriodEnd(now.with(TemporalAdjusters.lastDayOfMonth()).toLocalDate().atTime(23, 59, 59)); break; case "quarterly": int quarter = (now.getMonthValue() - 1) / 3; LocalDateTime quarterStart = LocalDateTime.of(now.getYear(), quarter * 3 + 1, 1, 0, 0); goal.setPeriodStart(quarterStart); goal.setPeriodEnd(quarterStart.plusMonths(3).minusSeconds(1)); break; case "yearly": goal.setPeriodStart(now.with(TemporalAdjusters.firstDayOfYear()).toLocalDate().atStartOfDay()); goal.setPeriodEnd(now.with(TemporalAdjusters.lastDayOfYear()).toLocalDate().atTime(23, 59, 59)); break; case "custom": // 自定义周期,使用传入的时间 if (goal.getPeriodStart() == null || goal.getPeriodEnd() == null) { throw new BusinessException(ResultCode.PARAM_ERROR, "自定义周期必须指定开始和结束时间"); } break; default: goal.setPeriodStart(now); goal.setPeriodEnd(now.plusMonths(1)); } } /** * 更新目标 */ @Transactional public UserGoal updateGoal(Long id, UserGoal goal) { UserGoal existing = userGoalMapper.selectById(id); if (existing == null) { throw new BusinessException(ResultCode.NOT_FOUND, "目标不存在"); } goal.setId(id); userGoalMapper.updateById(goal); return userGoalMapper.selectById(id); } /** * 更新目标进度 */ @Transactional public UserGoal updateProgress(Long goalId, Integer progressValue) { UserGoal goal = userGoalMapper.selectById(goalId); if (goal == null) { throw new BusinessException(ResultCode.NOT_FOUND, "目标不存在"); } goal.setCurrentValue(progressValue); // 检查是否完成 if (progressValue >= goal.getTargetValue() && !"completed".equals(goal.getStatus())) { goal.setStatus("completed"); goal.setCompletedTime(LocalDateTime.now()); // 发送完成通知 notificationService.sendNotification( goal.getUserId(), "目标达成", String.format("恭喜!您的目标「%s」已完成!", goal.getGoalName()), "goal", goal.getId() ); } userGoalMapper.updateById(goal); return goal; } /** * 增量更新目标进度 */ @Transactional public void incrementProgress(Long userId, String goalType, int increment) { // 获取用户当前进行中的该类型目标 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(UserGoal::getUserId, userId) .eq(UserGoal::getGoalType, goalType) .eq(UserGoal::getStatus, "active") .le(UserGoal::getPeriodStart, LocalDateTime.now()) .ge(UserGoal::getPeriodEnd, LocalDateTime.now()); List goals = userGoalMapper.selectList(wrapper); for (UserGoal goal : goals) { int newValue = goal.getCurrentValue() + increment; updateProgress(goal.getId(), newValue); } } /** * 获取用户目标列表 */ public List getUserGoals(Long userId, String status) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(UserGoal::getUserId, userId); if (status != null && !status.isEmpty()) { wrapper.eq(UserGoal::getStatus, status); } wrapper.orderByDesc(UserGoal::getCreateTime); return userGoalMapper.selectList(wrapper); } /** * 获取用户当前活跃目标 */ public List getActiveGoals(Long userId) { LocalDateTime now = LocalDateTime.now(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(UserGoal::getUserId, userId) .eq(UserGoal::getStatus, "active") .le(UserGoal::getPeriodStart, now) .ge(UserGoal::getPeriodEnd, now) .orderByAsc(UserGoal::getPeriodEnd); return userGoalMapper.selectList(wrapper); } /** * 获取目标详情 */ public UserGoal getGoalById(Long id) { UserGoal goal = userGoalMapper.selectById(id); if (goal == null) { throw new BusinessException(ResultCode.NOT_FOUND, "目标不存在"); } return goal; } /** * 领取目标奖励 */ @Transactional public boolean claimReward(Long goalId, Long userId) { UserGoal goal = userGoalMapper.selectById(goalId); if (goal == null) { throw new BusinessException(ResultCode.NOT_FOUND, "目标不存在"); } if (!goal.getUserId().equals(userId)) { throw new BusinessException(ResultCode.FORBIDDEN, "无权操作此目标"); } if (!"completed".equals(goal.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "目标未完成,无法领取奖励"); } if (goal.getRewardClaimed() == 1) { throw new BusinessException(ResultCode.PARAM_ERROR, "奖励已领取"); } if (goal.getRewardScore() == null || goal.getRewardScore() <= 0) { throw new BusinessException(ResultCode.PARAM_ERROR, "此目标无积分奖励"); } // 更新用户积分 User user = userMapper.selectById(userId); int currentScore = user.getScore() != null ? user.getScore() : 0; user.setScore(currentScore + goal.getRewardScore()); userMapper.updateById(user); // 标记已领取 goal.setRewardClaimed(1); userGoalMapper.updateById(goal); return true; } /** * 取消目标 */ @Transactional public void cancelGoal(Long id, Long userId) { UserGoal goal = userGoalMapper.selectById(id); if (goal == null) { throw new BusinessException(ResultCode.NOT_FOUND, "目标不存在"); } if (!goal.getUserId().equals(userId)) { throw new BusinessException(ResultCode.FORBIDDEN, "无权操作此目标"); } goal.setStatus("cancelled"); userGoalMapper.updateById(goal); } /** * 删除目标 */ @Transactional public void deleteGoal(Long id, Long userId) { UserGoal goal = userGoalMapper.selectById(id); if (goal == null) { throw new BusinessException(ResultCode.NOT_FOUND, "目标不存在"); } if (!goal.getUserId().equals(userId)) { throw new BusinessException(ResultCode.FORBIDDEN, "无权操作此目标"); } userGoalMapper.deleteById(id); } /** * 定时任务:检查过期目标 */ @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行 @Transactional public void checkExpiredGoals() { LocalDateTime now = LocalDateTime.now(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(UserGoal::getStatus, "active") .lt(UserGoal::getPeriodEnd, now); List expiredGoals = userGoalMapper.selectList(wrapper); for (UserGoal goal : expiredGoals) { if (goal.getCurrentValue() >= goal.getTargetValue()) { goal.setStatus("completed"); goal.setCompletedTime(now); } else { goal.setStatus("failed"); // 发送未完成通知 notificationService.sendNotification( goal.getUserId(), "目标未完成", String.format("很遗憾,您的目标「%s」未能在期限内完成。继续努力!", goal.getGoalName()), "goal", goal.getId() ); } userGoalMapper.updateById(goal); } log.info("检查过期目标完成,处理了{}个目标", expiredGoals.size()); } /** * 创建预设目标(系统推荐) */ @Transactional public UserGoal createPresetGoal(Long userId, String presetType) { UserGoal goal = new UserGoal(); goal.setUserId(userId); switch (presetType) { case "weekly_task": goal.setGoalType("task_count"); goal.setGoalName("本周完成10个任务"); goal.setDescription("在本周内完成10个设计任务"); goal.setTargetValue(10); goal.setPeriod("weekly"); goal.setRewardScore(50); break; case "monthly_task": goal.setGoalType("task_count"); goal.setGoalName("本月完成40个任务"); goal.setDescription("在本月内完成40个设计任务"); goal.setTargetValue(40); goal.setPeriod("monthly"); goal.setRewardScore(200); break; case "monthly_score": goal.setGoalType("score"); goal.setGoalName("本月获得1000积分"); goal.setDescription("在本月内累计获得1000积分"); goal.setTargetValue(1000); goal.setPeriod("monthly"); goal.setRewardScore(100); break; case "top_10": goal.setGoalType("rank"); goal.setGoalName("进入排行榜前10"); goal.setDescription("在本月排行榜中进入前10名"); goal.setTargetValue(10); goal.setPeriod("monthly"); goal.setRewardScore(150); break; default: throw new BusinessException(ResultCode.PARAM_ERROR, "未知的预设目标类型"); } return createGoal(goal); } }