456 lines
16 KiB
Java
456 lines
16 KiB
Java
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);
|
||
}
|
||
}
|