diff --git a/backend-java/src/main/java/com/competition/modules/biz/review/service/impl/ContestReviewServiceImpl.java b/backend-java/src/main/java/com/competition/modules/biz/review/service/impl/ContestReviewServiceImpl.java index e3924b8..2f2b85b 100644 --- a/backend-java/src/main/java/com/competition/modules/biz/review/service/impl/ContestReviewServiceImpl.java +++ b/backend-java/src/main/java/com/competition/modules/biz/review/service/impl/ContestReviewServiceImpl.java @@ -27,6 +27,7 @@ import com.competition.modules.sys.mapper.SysUserMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.math.BigDecimal; @@ -52,13 +53,42 @@ public class ContestReviewServiceImpl implements IContestReviewService { // ====== 作品分配 ====== @Override + @Transactional(rollbackFor = Exception.class) public Map assignWork(Long contestId, Long workId, List judgeIds, Long creatorId) { - log.info("分配作品,赛事ID:{},作品ID:{},评委数:{}", contestId, workId, judgeIds.size()); + if (judgeIds == null) { + judgeIds = Collections.emptyList(); + } + // 去重后保持顺序,作为本次期望的评委集合 + List desiredOrder = new ArrayList<>(new LinkedHashSet<>(judgeIds)); + Set desired = new HashSet<>(desiredOrder); + + log.info("分配作品(同步),赛事ID:{},作品ID:{},期望评委数:{}", contestId, workId, desired.size()); + + // 1. 取消已分配但不在本次列表中的评委(仅删除未评分的分配行) + LambdaQueryWrapper existWrapper = new LambdaQueryWrapper<>(); + existWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId); + existWrapper.eq(BizContestWorkJudgeAssignment::getWorkId, workId); + List existing = assignmentMapper.selectList(existWrapper); + + int removed = 0; + for (BizContestWorkJudgeAssignment a : existing) { + if (desired.contains(a.getJudgeId())) { + continue; + } + LambdaQueryWrapper scoreWrapper = new LambdaQueryWrapper<>(); + scoreWrapper.eq(BizContestWorkScore::getAssignmentId, a.getId()); + scoreWrapper.eq(BizContestWorkScore::getValidState, 1); + if (scoreMapper.selectCount(scoreWrapper) > 0) { + throw BusinessException.of(ErrorCode.BAD_REQUEST, "部分评委已提交评分,无法取消其分配,请先处理评分记录"); + } + assignmentMapper.deleteById(a.getId()); + removed++; + } int created = 0; int skipped = 0; - for (Long judgeId : judgeIds) { + for (Long judgeId : desiredOrder) { // 检查是否已分配 LambdaQueryWrapper dupWrapper = new LambdaQueryWrapper<>(); dupWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId); @@ -82,10 +112,11 @@ public class ContestReviewServiceImpl implements IContestReviewService { created++; } - log.info("作品分配完成,新建:{},跳过:{}", created, skipped); + log.info("作品分配完成,移除:{},新建:{},跳过(已存在):{}", removed, created, skipped); Map result = new LinkedHashMap<>(); result.put("workId", workId); + result.put("removed", removed); result.put("created", created); result.put("skipped", skipped); return result; diff --git a/frontend/src/views/contests/works/WorksDetail.vue b/frontend/src/views/contests/works/WorksDetail.vue index a2ba400..caa5b2f 100644 --- a/frontend/src/views/contests/works/WorksDetail.vue +++ b/frontend/src/views/contests/works/WorksDetail.vue @@ -227,8 +227,7 @@