456 lines
16 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.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<User> wrapperA = new LambdaQueryWrapper<>();
wrapperA.eq(User::getDeptId, teamPK.getTeamADeptId()).eq(User::getDeleted, 0);
List<User> teamA = userMapper.selectList(wrapperA);
// 获取乙方成员
LambdaQueryWrapper<User> wrapperB = new LambdaQueryWrapper<>();
wrapperB.eq(User::getDeptId, teamPK.getTeamBDeptId()).eq(User::getDeleted, 0);
List<User> 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<TeamPK> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TeamPK::getStatus, "active")
.and(w -> w.eq(TeamPK::getTeamADeptId, deptId).or().eq(TeamPK::getTeamBDeptId, deptId));
List<TeamPK> 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<Task> wrapperA = new LambdaQueryWrapper<>();
wrapperA.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, startTime)
.le(Task::getCompleteTime, endTime);
// 这里需要根据接收人的部门来统计,简化处理
// 获取甲方成员
LambdaQueryWrapper<User> userWrapperA = new LambdaQueryWrapper<>();
userWrapperA.eq(User::getDeptId, pk.getTeamADeptId()).eq(User::getDeleted, 0);
List<User> teamAUsers = userMapper.selectList(userWrapperA);
int teamATaskCount = 0;
int teamAFiveStarCount = 0;
int teamAOnTimeCount = 0;
int teamATotalCount = 0;
for (User user : teamAUsers) {
LambdaQueryWrapper<Task> taskWrapper = new LambdaQueryWrapper<>();
taskWrapper.eq(Task::getReceiverId, user.getId())
.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, startTime)
.le(Task::getCompleteTime, endTime);
List<Task> 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<User> userWrapperB = new LambdaQueryWrapper<>();
userWrapperB.eq(User::getDeptId, pk.getTeamBDeptId()).eq(User::getDeleted, 0);
List<User> teamBUsers = userMapper.selectList(userWrapperB);
int teamBTaskCount = 0;
int teamBFiveStarCount = 0;
int teamBOnTimeCount = 0;
int teamBTotalCount = 0;
for (User user : teamBUsers) {
LambdaQueryWrapper<Task> taskWrapper = new LambdaQueryWrapper<>();
taskWrapper.eq(Task::getReceiverId, user.getId())
.eq(Task::getStatus, "completed")
.ge(Task::getCompleteTime, startTime)
.le(Task::getCompleteTime, endTime);
List<Task> 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<String, Map<String, Integer>> dimensions = objectMapper.readValue(
pk.getDimensions(), new TypeReference<Map<String, Map<String, Integer>>>() {});
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<TeamPK> getPKList(Integer page, Integer pageSize, String status) {
Page<TeamPK> pageParam = new Page<>(page, pageSize);
LambdaQueryWrapper<TeamPK> 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<String, Integer> rewards = objectMapper.readValue(
pk.getRewards(), new TypeReference<Map<String, Integer>>() {});
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<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeptId, deptId).eq(User::getDeleted, 0);
List<User> 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<TeamPK> startWrapper = new LambdaQueryWrapper<>();
startWrapper.eq(TeamPK::getStatus, "scheduled")
.le(TeamPK::getStartTime, now);
List<TeamPK> toStart = teamPKMapper.selectList(startWrapper);
for (TeamPK pk : toStart) {
pk.setStatus("active");
teamPKMapper.updateById(pk);
notifyTeamPKStart(pk);
log.info("团队PK自动开始: id={}", pk.getId());
}
// 自动结束
LambdaQueryWrapper<TeamPK> endWrapper = new LambdaQueryWrapper<>();
endWrapper.eq(TeamPK::getStatus, "active")
.le(TeamPK::getEndTime, now);
List<TeamPK> 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);
}
}