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.Task; import com.designhub.entity.TeamPK; import com.designhub.entity.User; import com.designhub.exception.BusinessException; import com.designhub.mapper.TaskMapper; import com.designhub.mapper.TeamPKMapper; 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; /** * 团队PK Service */ @Slf4j @Service @RequiredArgsConstructor public class TeamPKService { // 手动添加Logger以解决Lombok编译问题 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TeamPKService.class); private final TeamPKMapper teamPKMapper; private final UserMapper userMapper; private final TaskMapper taskMapper; private final NotificationService notificationService; private final ObjectMapper objectMapper; /** * 创建团队PK */ @Transactional public TeamPK createTeamPK(TeamPK teamPK) { // 验证时间 if (teamPK.getStartTime().isAfter(teamPK.getEndTime())) { throw new BusinessException(ResultCode.PARAM_ERROR, "开始时间不能晚于结束时间"); } // 验证部门 if (teamPK.getTeamADeptId().equals(teamPK.getTeamBDeptId())) { throw new BusinessException(ResultCode.PARAM_ERROR, "不能与自己PK"); } teamPK.setStatus("draft"); teamPK.setTeamAScore(0); teamPK.setTeamBScore(0); teamPK.setTeamATaskCount(0); teamPK.setTeamBTaskCount(0); teamPK.setTeamAFiveStarCount(0); teamPK.setTeamBFiveStarCount(0); teamPK.setTeamAOnTimeRate(0); teamPK.setTeamBOnTimeRate(0); teamPK.setDeleted(0); teamPKMapper.insert(teamPK); log.info("创建团队PK成功: id={}, name={}", teamPK.getId(), teamPK.getName()); return teamPK; } /** * 发布团队PK */ @Transactional public TeamPK publishTeamPK(Long id) { TeamPK teamPK = teamPKMapper.selectById(id); if (teamPK == null) { throw new BusinessException(ResultCode.NOT_FOUND, "PK不存在"); } if (!"draft".equals(teamPK.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "只有草稿状态的PK可以发布"); } LocalDateTime now = LocalDateTime.now(); if (teamPK.getStartTime().isBefore(now)) { teamPK.setStatus("active"); } else { teamPK.setStatus("scheduled"); } teamPKMapper.updateById(teamPK); // 发送通知给双方部门成员 notifyTeamPKStart(teamPK); return teamPK; } /** * 发送PK开始通知 */ private void notifyTeamPKStart(TeamPK teamPK) { // 获取甲方成员 LambdaQueryWrapper wrapperA = new LambdaQueryWrapper<>(); wrapperA.eq(User::getDeptId, teamPK.getTeamADeptId()).eq(User::getDeleted, 0); List teamA = userMapper.selectList(wrapperA); // 获取乙方成员 LambdaQueryWrapper wrapperB = new LambdaQueryWrapper<>(); wrapperB.eq(User::getDeptId, teamPK.getTeamBDeptId()).eq(User::getDeleted, 0); List teamB = userMapper.selectList(wrapperB); String message = String.format("团队PK「%s」已开始!%s VS %s,为团队荣誉而战!", teamPK.getName(), teamPK.getTeamAName(), teamPK.getTeamBName()); for (User user : teamA) { notificationService.sendNotification(user.getId(), "团队PK开始", message, "team_pk", teamPK.getId()); } for (User user : teamB) { notificationService.sendNotification(user.getId(), "团队PK开始", message, "team_pk", teamPK.getId()); } } /** * 更新PK数据 */ @Transactional public void updatePKStats(Long deptId) { LocalDateTime now = LocalDateTime.now(); // 查找包含该部门的进行中的PK LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(TeamPK::getStatus, "active") .and(w -> w.eq(TeamPK::getTeamADeptId, deptId).or().eq(TeamPK::getTeamBDeptId, deptId)); List activePKs = teamPKMapper.selectList(wrapper); for (TeamPK pk : activePKs) { refreshPKStats(pk); } } /** * 刷新PK统计数据 */ @Transactional public void refreshPKStats(TeamPK pk) { LocalDateTime startTime = pk.getStartTime(); LocalDateTime endTime = pk.getEndTime().isBefore(LocalDateTime.now()) ? pk.getEndTime() : LocalDateTime.now(); // 统计甲方数据 LambdaQueryWrapper wrapperA = new LambdaQueryWrapper<>(); wrapperA.eq(Task::getStatus, "completed") .ge(Task::getCompleteTime, startTime) .le(Task::getCompleteTime, endTime); // 这里需要根据接收人的部门来统计,简化处理 // 获取甲方成员 LambdaQueryWrapper userWrapperA = new LambdaQueryWrapper<>(); userWrapperA.eq(User::getDeptId, pk.getTeamADeptId()).eq(User::getDeleted, 0); List teamAUsers = userMapper.selectList(userWrapperA); int teamATaskCount = 0; int teamAFiveStarCount = 0; int teamAOnTimeCount = 0; int teamATotalCount = 0; for (User user : teamAUsers) { LambdaQueryWrapper taskWrapper = new LambdaQueryWrapper<>(); taskWrapper.eq(Task::getReceiverId, user.getId()) .eq(Task::getStatus, "completed") .ge(Task::getCompleteTime, startTime) .le(Task::getCompleteTime, endTime); List tasks = taskMapper.selectList(taskWrapper); teamATotalCount += tasks.size(); for (Task task : tasks) { teamATaskCount++; if (task.getRating() != null && task.getRating() >= 5) { teamAFiveStarCount++; } if (task.getCompleteTime() != null && task.getDeadline() != null && !task.getCompleteTime().isAfter(task.getDeadline())) { teamAOnTimeCount++; } } } // 获取乙方成员 LambdaQueryWrapper userWrapperB = new LambdaQueryWrapper<>(); userWrapperB.eq(User::getDeptId, pk.getTeamBDeptId()).eq(User::getDeleted, 0); List teamBUsers = userMapper.selectList(userWrapperB); int teamBTaskCount = 0; int teamBFiveStarCount = 0; int teamBOnTimeCount = 0; int teamBTotalCount = 0; for (User user : teamBUsers) { LambdaQueryWrapper taskWrapper = new LambdaQueryWrapper<>(); taskWrapper.eq(Task::getReceiverId, user.getId()) .eq(Task::getStatus, "completed") .ge(Task::getCompleteTime, startTime) .le(Task::getCompleteTime, endTime); List tasks = taskMapper.selectList(taskWrapper); teamBTotalCount += tasks.size(); for (Task task : tasks) { teamBTaskCount++; if (task.getRating() != null && task.getRating() >= 5) { teamBFiveStarCount++; } if (task.getCompleteTime() != null && task.getDeadline() != null && !task.getCompleteTime().isAfter(task.getDeadline())) { teamBOnTimeCount++; } } } pk.setTeamATaskCount(teamATaskCount); pk.setTeamBTaskCount(teamBTaskCount); pk.setTeamAFiveStarCount(teamAFiveStarCount); pk.setTeamBFiveStarCount(teamBFiveStarCount); pk.setTeamAOnTimeRate(teamATotalCount > 0 ? (teamAOnTimeCount * 100 / teamATotalCount) : 0); pk.setTeamBOnTimeRate(teamBTotalCount > 0 ? (teamBOnTimeCount * 100 / teamBTotalCount) : 0); // 计算总分 pk.setTeamAScore(calculateTeamScore(pk, true)); pk.setTeamBScore(calculateTeamScore(pk, false)); teamPKMapper.updateById(pk); } /** * 计算团队得分 */ private int calculateTeamScore(TeamPK pk, boolean isTeamA) { try { Map> dimensions = objectMapper.readValue( pk.getDimensions(), new TypeReference>>() {}); int score = 0; if (dimensions.containsKey("taskCount")) { int weight = dimensions.get("taskCount").getOrDefault("weight", 30); int value = isTeamA ? pk.getTeamATaskCount() : pk.getTeamBTaskCount(); score += value * weight / 10; } if (dimensions.containsKey("fiveStarCount")) { int weight = dimensions.get("fiveStarCount").getOrDefault("weight", 25); int value = isTeamA ? pk.getTeamAFiveStarCount() : pk.getTeamBFiveStarCount(); score += value * weight / 5; } if (dimensions.containsKey("onTimeRate")) { int weight = dimensions.get("onTimeRate").getOrDefault("weight", 25); int value = isTeamA ? pk.getTeamAOnTimeRate() : pk.getTeamBOnTimeRate(); score += value * weight / 10; } return score; } catch (JsonProcessingException e) { log.error("解析PK维度配置失败", e); return 0; } } /** * 获取PK列表 */ public IPage getPKList(Integer page, Integer pageSize, String status) { Page pageParam = new Page<>(page, pageSize); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (status != null && !status.isEmpty()) { wrapper.eq(TeamPK::getStatus, status); } wrapper.orderByDesc(TeamPK::getCreateTime); return teamPKMapper.selectPage(pageParam, wrapper); } /** * 获取PK详情 */ public TeamPK getPKById(Long id) { TeamPK pk = teamPKMapper.selectById(id); if (pk == null) { throw new BusinessException(ResultCode.NOT_FOUND, "PK不存在"); } // 如果进行中,刷新数据 if ("active".equals(pk.getStatus())) { refreshPKStats(pk); } return pk; } /** * 结束PK并发放奖励 */ @Transactional public void endTeamPK(Long id) { TeamPK pk = teamPKMapper.selectById(id); if (pk == null) { throw new BusinessException(ResultCode.NOT_FOUND, "PK不存在"); } if (!"active".equals(pk.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "只有进行中的PK可以结束"); } // 最后刷新一次数据 refreshPKStats(pk); // 判断胜负 if (pk.getTeamAScore() > pk.getTeamBScore()) { pk.setWinner("A"); } else if (pk.getTeamBScore() > pk.getTeamAScore()) { pk.setWinner("B"); } else { pk.setWinner("draw"); } pk.setStatus("ended"); teamPKMapper.updateById(pk); // 发放奖励 distributeRewards(pk); log.info("团队PK结束: id={}, winner={}", pk.getId(), pk.getWinner()); } /** * 发放PK奖励 */ private void distributeRewards(TeamPK pk) { try { Map rewards = objectMapper.readValue( pk.getRewards(), new TypeReference>() {}); int winnerReward = rewards.getOrDefault("winnerReward", 200); int loserReward = rewards.getOrDefault("loserReward", 100); Long winnerDeptId = "A".equals(pk.getWinner()) ? pk.getTeamADeptId() : pk.getTeamBDeptId(); Long loserDeptId = "A".equals(pk.getWinner()) ? pk.getTeamBDeptId() : pk.getTeamADeptId(); String winnerName = "A".equals(pk.getWinner()) ? pk.getTeamAName() : pk.getTeamBName(); if ("draw".equals(pk.getWinner())) { // 平局,双方都获得参与奖 distributeToTeam(pk.getTeamADeptId(), loserReward, pk, "平局"); distributeToTeam(pk.getTeamBDeptId(), loserReward, pk, "平局"); } else { distributeToTeam(winnerDeptId, winnerReward, pk, "胜利"); distributeToTeam(loserDeptId, loserReward, pk, "参与"); } } catch (JsonProcessingException e) { log.error("解析PK奖励配置失败", e); } } /** * 向团队成员发放奖励 */ private void distributeToTeam(Long deptId, int rewardScore, TeamPK pk, String result) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getDeptId, deptId).eq(User::getDeleted, 0); List users = userMapper.selectList(wrapper); for (User user : users) { int currentScore = user.getScore() != null ? user.getScore() : 0; user.setScore(currentScore + rewardScore); userMapper.updateById(user); notificationService.sendNotification( user.getId(), "团队PK奖励", String.format("团队PK「%s」已结束,您的团队%s!获得%d积分奖励。", pk.getName(), result, rewardScore), "team_pk", pk.getId() ); } } /** * 定时任务:自动开始和结束PK */ @Scheduled(fixedRate = 60000) // 每分钟检查一次 @Transactional public void checkPKStatus() { LocalDateTime now = LocalDateTime.now(); // 自动开始 LambdaQueryWrapper startWrapper = new LambdaQueryWrapper<>(); startWrapper.eq(TeamPK::getStatus, "scheduled") .le(TeamPK::getStartTime, now); List toStart = teamPKMapper.selectList(startWrapper); for (TeamPK pk : toStart) { pk.setStatus("active"); teamPKMapper.updateById(pk); notifyTeamPKStart(pk); log.info("团队PK自动开始: id={}", pk.getId()); } // 自动结束 LambdaQueryWrapper endWrapper = new LambdaQueryWrapper<>(); endWrapper.eq(TeamPK::getStatus, "active") .le(TeamPK::getEndTime, now); List toEnd = teamPKMapper.selectList(endWrapper); for (TeamPK pk : toEnd) { endTeamPK(pk.getId()); } } /** * 取消PK */ @Transactional public void cancelTeamPK(Long id) { TeamPK pk = teamPKMapper.selectById(id); if (pk == null) { throw new BusinessException(ResultCode.NOT_FOUND, "PK不存在"); } pk.setStatus("cancelled"); teamPKMapper.updateById(pk); } /** * 删除PK */ @Transactional public void deleteTeamPK(Long id) { TeamPK pk = teamPKMapper.selectById(id); if (pk == null) { throw new BusinessException(ResultCode.NOT_FOUND, "PK不存在"); } if ("active".equals(pk.getStatus())) { throw new BusinessException(ResultCode.PARAM_ERROR, "进行中的PK不能删除"); } teamPKMapper.deleteById(id); } }