package com.designhub.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.designhub.common.ResultCode; import com.designhub.entity.Contest; import com.designhub.entity.ContestParticipant; import com.designhub.entity.User; import com.designhub.exception.BusinessException; import com.designhub.mapper.ContestMapper; import com.designhub.mapper.ContestParticipantMapper; import com.designhub.mapper.UserMapper; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; 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.util.List; import java.util.Map; /** * 竞赛活动Service */ @Slf4j @Service @RequiredArgsConstructor public class ContestService { private final ContestMapper contestMapper; private final ContestParticipantMapper participantMapper; private final UserMapper userMapper; private final NotificationService notificationService; private final ObjectMapper objectMapper; /** * 创建竞赛 */ @Transactional public Contest createContest(Contest contest) { // 验证时间 if (contest.getStartTime().isAfter(contest.getEndTime())) { throw new BusinessException(ResultCode.PARAM_ERROR, "开始时间不能晚于结束时间"); } contest.setStatus("draft"); contest.setParticipantCount(0); contest.setDeleted(0); contestMapper.insert(contest); log.info("创建竞赛成功: id={}, name={}", contest.getId(), contest.getName()); return contest; } /** * 更新竞赛 */ @Transactional public Contest updateContest(Long id, Contest contest) { Contest existing = contestMapper.selectById(id); if (existing == null) { throw new BusinessException(ResultCode.NOT_FOUND, "竞赛不存在"); } // 进行中的竞赛不能修改规则 if ("active".equals(existing.getStatus()) && contest.getRules() != null) { throw new BusinessException(ResultCode.PARAM_ERROR, "进行中的竞赛不能修改规则"); } contest.setId(id); contestMapper.updateById(contest); return contestMapper.selectById(id); } /** * 发布竞赛(从草稿变为待开始或进行中) */ @Transactional public Contest publishContest(Long id) { Contest contest = contestMapper.selectById(id); if (contest == null) { throw new BusinessException(ResultCode.NOT_FOUND, "竞赛不存在"); } if (!"draft".equals(contest.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "只有草稿状态的竞赛可以发布"); } LocalDateTime now = LocalDateTime.now(); if (contest.getStartTime().isBefore(now)) { contest.setStatus("active"); } else { contest.setStatus("scheduled"); } contestMapper.updateById(contest); // 初始化参与者 initParticipants(contest); // 发送通知 notifyContestStart(contest); return contest; } /** * 初始化竞赛参与者 */ private void initParticipants(Contest contest) { List users; if ("all".equals(contest.getParticipantScope())) { // 全体设计师 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getDeleted, 0); users = userMapper.selectList(wrapper); } else if ("department".equals(contest.getParticipantScope())) { // 指定部门 try { List deptIds = objectMapper.readValue(contest.getDepartmentIds(), new TypeReference>() {}); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.in(User::getDeptId, deptIds).eq(User::getDeleted, 0); users = userMapper.selectList(wrapper); } catch (JsonProcessingException e) { throw new BusinessException(ResultCode.PARAM_ERROR, "部门ID格式错误"); } } else { // 指定用户 try { List userIds = objectMapper.readValue(contest.getUserIds(), new TypeReference>() {}); users = userMapper.selectBatchIds(userIds); } catch (JsonProcessingException e) { throw new BusinessException(ResultCode.PARAM_ERROR, "用户ID格式错误"); } } for (User user : users) { ContestParticipant participant = new ContestParticipant(); participant.setContestId(contest.getId()); participant.setUserId(user.getId()); participant.setUserName(user.getRealName()); participant.setDeptId(user.getDeptId()); participant.setDeptName(user.getDeptName()); participant.setTaskCompleted(0); participant.setFiveStarCount(0); participant.setEarlyCompletionCount(0); participant.setPraiseCount(0); participant.setNoDelayDays(0); participant.setTotalScore(0); participant.setCurrentRank(0); participant.setRewardClaimed(0); participant.setRewardScore(0); participantMapper.insert(participant); } // 更新参与人数 contest.setParticipantCount(users.size()); contestMapper.updateById(contest); } /** * 发送竞赛开始通知 */ private void notifyContestStart(Contest contest) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ContestParticipant::getContestId, contest.getId()); List participants = participantMapper.selectList(wrapper); for (ContestParticipant p : participants) { notificationService.sendNotification( p.getUserId(), "竞赛活动开始", String.format("竞赛「%s」已开始!快来参与吧!", contest.getName()), "contest", contest.getId() ); } } /** * 获取竞赛列表 */ public IPage getContestList(Integer page, Integer pageSize, String status) { Page pageParam = new Page<>(page, pageSize); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (status != null && !status.isEmpty()) { wrapper.eq(Contest::getStatus, status); } wrapper.orderByDesc(Contest::getCreateTime); return contestMapper.selectPage(pageParam, wrapper); } /** * 获取竞赛详情 */ public Contest getContestById(Long id) { Contest contest = contestMapper.selectById(id); if (contest == null) { throw new BusinessException(ResultCode.NOT_FOUND, "竞赛不存在"); } return contest; } /** * 获取竞赛排行榜 */ public List getLeaderboard(Long contestId, Integer limit) { List list = participantMapper.getLeaderboard(contestId); if (limit != null && list.size() > limit) { return list.subList(0, limit); } return list; } /** * 获取用户在竞赛中的排名和数据 */ public ContestParticipant getUserContestData(Long contestId, Long userId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ContestParticipant::getContestId, contestId) .eq(ContestParticipant::getUserId, userId); ContestParticipant participant = participantMapper.selectOne(wrapper); if (participant != null) { // 计算排名 Integer rank = participantMapper.getUserRank(contestId, userId); participant.setCurrentRank(rank); } return participant; } /** * 更新参与者数据(当任务完成时调用) */ @Transactional public void updateParticipantStats(Long userId, boolean fiveStar, boolean earlyCompletion) { // 获取用户参与的所有进行中的竞赛 LocalDateTime now = LocalDateTime.now(); LambdaQueryWrapper contestWrapper = new LambdaQueryWrapper<>(); contestWrapper.eq(Contest::getStatus, "active"); List activeContests = contestMapper.selectList(contestWrapper); for (Contest contest : activeContests) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ContestParticipant::getContestId, contest.getId()) .eq(ContestParticipant::getUserId, userId); ContestParticipant participant = participantMapper.selectOne(wrapper); if (participant != null) { participant.setTaskCompleted(participant.getTaskCompleted() + 1); if (fiveStar) { participant.setFiveStarCount(participant.getFiveStarCount() + 1); } if (earlyCompletion) { participant.setEarlyCompletionCount(participant.getEarlyCompletionCount() + 1); } // 重新计算总分 int totalScore = calculateScore(contest, participant); participant.setTotalScore(totalScore); participantMapper.updateById(participant); } } } /** * 计算参与者得分 */ private int calculateScore(Contest contest, ContestParticipant participant) { try { Map> rules = objectMapper.readValue( contest.getRules(), new TypeReference>>() {}); int score = 0; if (rules.containsKey("taskCompleted")) { score += participant.getTaskCompleted() * rules.get("taskCompleted").getOrDefault("score", 2); } if (rules.containsKey("fiveStarReview")) { score += participant.getFiveStarCount() * rules.get("fiveStarReview").getOrDefault("score", 10); } if (rules.containsKey("earlyCompletion")) { score += participant.getEarlyCompletionCount() * rules.get("earlyCompletion").getOrDefault("score", 5); } if (rules.containsKey("praise")) { score += participant.getPraiseCount() * rules.get("praise").getOrDefault("score", 15); } return score; } catch (JsonProcessingException e) { log.error("解析竞赛规则失败", e); return 0; } } /** * 结束竞赛并发放奖励 */ @Transactional public void endContest(Long id) { Contest contest = contestMapper.selectById(id); if (contest == null) { throw new BusinessException(ResultCode.NOT_FOUND, "竞赛不存在"); } if (!"active".equals(contest.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "只有进行中的竞赛可以结束"); } contest.setStatus("ended"); contestMapper.updateById(contest); // 发放奖励 distributeRewards(contest); log.info("竞赛结束: id={}, name={}", contest.getId(), contest.getName()); } /** * 发放竞赛奖励 */ private void distributeRewards(Contest contest) { try { List> rewards = objectMapper.readValue( contest.getRewards(), new TypeReference>>() {}); List leaderboard = participantMapper.getLeaderboard(contest.getId()); for (int i = 0; i < leaderboard.size(); i++) { ContestParticipant participant = leaderboard.get(i); int rank = i + 1; participant.setCurrentRank(rank); // 查找对应排名的奖励 for (Map reward : rewards) { int rewardRank = (Integer) reward.get("rank"); if (rank == rewardRank) { int rewardScore = (Integer) reward.get("score"); participant.setRewardScore(rewardScore); // 发送通知 notificationService.sendNotification( participant.getUserId(), "竞赛获奖通知", String.format("恭喜!您在竞赛「%s」中获得第%d名,奖励%d积分!", contest.getName(), rank, rewardScore), "contest", contest.getId() ); break; } } participantMapper.updateById(participant); } } catch (JsonProcessingException e) { log.error("解析竞赛奖励配置失败", e); } } /** * 领取竞赛奖励 */ @Transactional public boolean claimReward(Long contestId, Long userId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ContestParticipant::getContestId, contestId) .eq(ContestParticipant::getUserId, userId); ContestParticipant participant = participantMapper.selectOne(wrapper); if (participant == null) { throw new BusinessException(ResultCode.NOT_FOUND, "您未参与此竞赛"); } if (participant.getRewardClaimed() == 1) { throw new BusinessException(ResultCode.PARAM_ERROR, "奖励已领取"); } if (participant.getRewardScore() == null || participant.getRewardScore() <= 0) { throw new BusinessException(ResultCode.PARAM_ERROR, "暂无奖励可领取"); } // 更新用户积分 User user = userMapper.selectById(userId); int currentScore = user.getScore() != null ? user.getScore() : 0; user.setScore(currentScore + participant.getRewardScore()); userMapper.updateById(user); // 标记已领取 participant.setRewardClaimed(1); participant.setRewardClaimTime(LocalDateTime.now()); participantMapper.updateById(participant); return true; } /** * 定时任务:自动开始和结束竞赛 */ @Scheduled(fixedRate = 60000) // 每分钟检查一次 @Transactional public void checkContestStatus() { LocalDateTime now = LocalDateTime.now(); // 自动开始待开始的竞赛 LambdaQueryWrapper startWrapper = new LambdaQueryWrapper<>(); startWrapper.eq(Contest::getStatus, "scheduled") .le(Contest::getStartTime, now); List toStart = contestMapper.selectList(startWrapper); for (Contest contest : toStart) { contest.setStatus("active"); contestMapper.updateById(contest); notifyContestStart(contest); log.info("竞赛自动开始: id={}, name={}", contest.getId(), contest.getName()); } // 自动结束已到期的竞赛 LambdaQueryWrapper endWrapper = new LambdaQueryWrapper<>(); endWrapper.eq(Contest::getStatus, "active") .le(Contest::getEndTime, now); List toEnd = contestMapper.selectList(endWrapper); for (Contest contest : toEnd) { endContest(contest.getId()); } } /** * 取消竞赛 */ @Transactional public void cancelContest(Long id) { Contest contest = contestMapper.selectById(id); if (contest == null) { throw new BusinessException(ResultCode.NOT_FOUND, "竞赛不存在"); } if ("ended".equals(contest.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "已结束的竞赛不能取消"); } contest.setStatus("cancelled"); contestMapper.updateById(contest); log.info("竞赛已取消: id={}, name={}", contest.getId(), contest.getName()); } /** * 删除竞赛 */ @Transactional public void deleteContest(Long id) { Contest contest = contestMapper.selectById(id); if (contest == null) { throw new BusinessException(ResultCode.NOT_FOUND, "竞赛不存在"); } if ("active".equals(contest.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "进行中的竞赛不能删除"); } contestMapper.deleteById(id); // 删除参与者数据 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ContestParticipant::getContestId, id); participantMapper.delete(wrapper); } }