359 lines
12 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<UserGoal> 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<UserGoal> goals = userGoalMapper.selectList(wrapper);
for (UserGoal goal : goals) {
int newValue = goal.getCurrentValue() + increment;
updateProgress(goal.getId(), newValue);
}
}
/**
* 获取用户目标列表
*/
public List<UserGoal> getUserGoals(Long userId, String status) {
LambdaQueryWrapper<UserGoal> 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<UserGoal> getActiveGoals(Long userId) {
LocalDateTime now = LocalDateTime.now();
LambdaQueryWrapper<UserGoal> 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<UserGoal> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserGoal::getStatus, "active")
.lt(UserGoal::getPeriodEnd, now);
List<UserGoal> 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);
}
}