Merge remote-tracking branch 'origin/master_develop' into master_develop

This commit is contained in:
En 2026-04-08 09:36:50 +08:00
commit 9b5c24c49c
6 changed files with 162 additions and 24 deletions

View File

@ -173,8 +173,37 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
Long currentTenantId = SecurityUtil.getCurrentTenantId(); Long currentTenantId = SecurityUtil.getCurrentTenantId();
Long judgeTenantId = getJudgeTenantId(); Long judgeTenantId = getJudgeTenantId();
// 查询当前租户和平台评委租户的 judge 角色 ID
Set<Long> judgeRoleIds = new HashSet<>();
for (Long tid : List.of(currentTenantId, judgeTenantId)) {
if (tid == null) continue;
LambdaQueryWrapper<SysRole> roleWrapper = new LambdaQueryWrapper<>();
roleWrapper.eq(SysRole::getCode, "judge");
roleWrapper.eq(SysRole::getTenantId, tid);
SysRole role = sysRoleMapper.selectOne(roleWrapper);
if (role != null) {
judgeRoleIds.add(role.getId());
}
}
// 查询拥有 judge 角色的用户 ID
Set<Long> judgeUserIds = new HashSet<>();
if (!judgeRoleIds.isEmpty()) {
LambdaQueryWrapper<SysUserRole> urWrapper = new LambdaQueryWrapper<>();
urWrapper.in(SysUserRole::getRoleId, judgeRoleIds);
List<SysUserRole> userRoles = sysUserRoleMapper.selectList(urWrapper);
for (SysUserRole ur : userRoles) {
judgeUserIds.add(ur.getUserId());
}
}
if (judgeUserIds.isEmpty()) {
return new PageResult<>(Collections.emptyList(), 0L, page, pageSize);
}
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
// 查询当前租户评委 + 平台评委 // 只查询拥有 judge 角色且属于当前租户或平台评委租户的用户
wrapper.in(SysUser::getId, judgeUserIds);
wrapper.in(SysUser::getTenantId, List.of(currentTenantId, judgeTenantId)); wrapper.in(SysUser::getTenantId, List.of(currentTenantId, judgeTenantId));
if (keyword != null && !keyword.isBlank()) { if (keyword != null && !keyword.isBlank()) {

View File

@ -3,6 +3,7 @@ package com.competition.modules.biz.review.controller;
import com.competition.common.result.Result; import com.competition.common.result.Result;
import com.competition.common.util.SecurityUtil; import com.competition.common.util.SecurityUtil;
import com.competition.modules.biz.review.dto.CreatePresetCommentDto; import com.competition.modules.biz.review.dto.CreatePresetCommentDto;
import com.competition.modules.biz.review.dto.SyncPresetCommentsDto;
import com.competition.modules.biz.review.entity.BizPresetComment; import com.competition.modules.biz.review.entity.BizPresetComment;
import com.competition.modules.biz.review.service.IPresetCommentService; import com.competition.modules.biz.review.service.IPresetCommentService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -65,11 +66,10 @@ public class PresetCommentController {
return Result.success(); return Result.success();
} }
@SuppressWarnings("unchecked")
@PostMapping("/batch-delete") @PostMapping("/batch-delete")
@Operation(summary = "批量删除预设评语") @Operation(summary = "批量删除预设评语")
public Result<Void> batchDelete(@RequestBody Map<String, Object> body) { public Result<Void> batchDelete(@RequestBody Map<String, List<Long>> body) {
List<Long> ids = (List<Long>) body.get("ids"); List<Long> ids = body.get("ids");
Long judgeId = SecurityUtil.getCurrentUserId(); Long judgeId = SecurityUtil.getCurrentUserId();
presetCommentService.batchDelete(ids, judgeId); presetCommentService.batchDelete(ids, judgeId);
return Result.success(); return Result.success();
@ -77,12 +77,9 @@ public class PresetCommentController {
@PostMapping("/sync") @PostMapping("/sync")
@Operation(summary = "同步评语到其他赛事") @Operation(summary = "同步评语到其他赛事")
public Result<Map<String, Object>> syncComments(@RequestBody Map<String, Object> body) { public Result<Map<String, Object>> syncComments(@Valid @RequestBody SyncPresetCommentsDto dto) {
Long sourceContestId = Long.valueOf(body.get("sourceContestId").toString());
@SuppressWarnings("unchecked")
List<Long> targetContestIds = (List<Long>) body.get("targetContestIds");
Long judgeId = SecurityUtil.getCurrentUserId(); Long judgeId = SecurityUtil.getCurrentUserId();
return Result.success(presetCommentService.syncComments(sourceContestId, targetContestIds, judgeId)); return Result.success(presetCommentService.syncComments(dto.getSourceContestId(), dto.getTargetContestIds(), judgeId));
} }
@PostMapping("/{id}/use") @PostMapping("/{id}/use")

View File

@ -0,0 +1,21 @@
package com.competition.modules.biz.review.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "同步预设评语DTO")
public class SyncPresetCommentsDto {
@NotNull(message = "源赛事ID不能为空")
@Schema(description = "源赛事ID")
private Long sourceContestId;
@NotEmpty(message = "目标赛事列表不能为空")
@Schema(description = "目标赛事ID列表")
private List<Long> targetContestIds;
}

View File

@ -9,7 +9,9 @@ import com.competition.modules.biz.review.entity.BizContestWorkJudgeAssignment;
import com.competition.modules.biz.review.mapper.ContestJudgeMapper; import com.competition.modules.biz.review.mapper.ContestJudgeMapper;
import com.competition.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper; import com.competition.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
import com.competition.modules.biz.review.service.IContestJudgeService; import com.competition.modules.biz.review.service.IContestJudgeService;
import com.competition.modules.sys.entity.SysTenant;
import com.competition.modules.sys.entity.SysUser; import com.competition.modules.sys.entity.SysUser;
import com.competition.modules.sys.mapper.SysTenantMapper;
import com.competition.modules.sys.mapper.SysUserMapper; import com.competition.modules.sys.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -27,6 +29,7 @@ public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, Biz
private final ContestJudgeMapper contestJudgeMapper; private final ContestJudgeMapper contestJudgeMapper;
private final ContestWorkJudgeAssignmentMapper assignmentMapper; private final ContestWorkJudgeAssignmentMapper assignmentMapper;
private final SysUserMapper sysUserMapper; private final SysUserMapper sysUserMapper;
private final SysTenantMapper sysTenantMapper;
@Override @Override
public BizContestJudge createJudge(Long contestId, Long judgeId, String specialty, BigDecimal weight, String description) { public BizContestJudge createJudge(Long contestId, Long judgeId, String specialty, BigDecimal weight, String description) {
@ -57,6 +60,10 @@ public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, Biz
public List<Map<String, Object>> findByContest(Long contestId) { public List<Map<String, Object>> findByContest(Long contestId) {
log.info("查询赛事评委列表赛事ID{}", contestId); log.info("查询赛事评委列表赛事ID{}", contestId);
// 获取平台评委租户 ID
Long judgeTenantId = getJudgeTenantId();
// 1. 查询已显式分配的评委
LambdaQueryWrapper<BizContestJudge> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<BizContestJudge> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(BizContestJudge::getContestId, contestId); wrapper.eq(BizContestJudge::getContestId, contestId);
wrapper.eq(BizContestJudge::getValidState, 1); wrapper.eq(BizContestJudge::getValidState, 1);
@ -64,11 +71,32 @@ public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, Biz
List<BizContestJudge> judges = contestJudgeMapper.selectList(wrapper); List<BizContestJudge> judges = contestJudgeMapper.selectList(wrapper);
// 收集所有需要查询的用户 ID已分配评委
Set<Long> assignedJudgeIds = judges.stream().map(BizContestJudge::getJudgeId).collect(Collectors.toSet());
// 2. 查询平台评委自动对所有赛事可用
List<SysUser> platformJudges = new ArrayList<>();
if (judgeTenantId != null) {
LambdaQueryWrapper<SysUser> platformWrapper = new LambdaQueryWrapper<>();
platformWrapper.eq(SysUser::getTenantId, judgeTenantId);
platformWrapper.eq(SysUser::getStatus, "enabled");
platformJudges = sysUserMapper.selectList(platformWrapper);
}
// 平台评委中去掉已显式分配的避免重复
Set<Long> platformOnlyIds = platformJudges.stream()
.map(SysUser::getId)
.filter(id -> !assignedJudgeIds.contains(id))
.collect(Collectors.toSet());
// 合并所有需要查询的用户 ID
Set<Long> allUserIds = new HashSet<>(assignedJudgeIds);
allUserIds.addAll(platformOnlyIds);
// 批量查询用户信息 // 批量查询用户信息
Set<Long> userIds = judges.stream().map(BizContestJudge::getJudgeId).collect(Collectors.toSet());
Map<Long, SysUser> userMap = new HashMap<>(); Map<Long, SysUser> userMap = new HashMap<>();
if (!userIds.isEmpty()) { if (!allUserIds.isEmpty()) {
List<SysUser> users = sysUserMapper.selectBatchIds(userIds); List<SysUser> users = sysUserMapper.selectBatchIds(allUserIds);
for (SysUser user : users) { for (SysUser user : users) {
userMap.put(user.getId(), user); userMap.put(user.getId(), user);
} }
@ -76,14 +104,17 @@ public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, Biz
// 批量查询每个评委在该赛事下的已分配作品数 // 批量查询每个评委在该赛事下的已分配作品数
Map<Long, Long> assignedCountMap = new HashMap<>(); Map<Long, Long> assignedCountMap = new HashMap<>();
for (BizContestJudge j : judges) { for (Long judgeId : allUserIds) {
LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignWrapper = new LambdaQueryWrapper<>();
assignWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId); assignWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
assignWrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, j.getJudgeId()); assignWrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, judgeId);
assignedCountMap.put(j.getJudgeId(), assignmentMapper.selectCount(assignWrapper)); assignedCountMap.put(judgeId, assignmentMapper.selectCount(assignWrapper));
} }
return judges.stream().map(j -> { List<Map<String, Object>> result = new ArrayList<>();
// 构建已显式分配的评委数据
for (BizContestJudge j : judges) {
Map<String, Object> map = new LinkedHashMap<>(); Map<String, Object> map = new LinkedHashMap<>();
map.put("id", j.getId()); map.put("id", j.getId());
map.put("contestId", j.getContestId()); map.put("contestId", j.getContestId());
@ -98,9 +129,50 @@ public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, Biz
if (user != null) { if (user != null) {
map.put("judgeName", user.getNickname()); map.put("judgeName", user.getNickname());
map.put("judgeUsername", user.getUsername()); map.put("judgeUsername", user.getUsername());
map.put("tenantId", user.getTenantId());
map.put("status", user.getStatus());
map.put("organization", user.getOrganization());
map.put("isPlatform", judgeTenantId != null && judgeTenantId.equals(user.getTenantId()));
} else {
map.put("isPlatform", false);
} }
return map; result.add(map);
}).collect(Collectors.toList()); }
// 追加未显式分配的平台评委
for (SysUser platformJudge : platformJudges) {
if (assignedJudgeIds.contains(platformJudge.getId())) {
continue; // 已在显式分配列表中跳过
}
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", null);
map.put("contestId", contestId);
map.put("judgeId", platformJudge.getId());
map.put("specialty", null);
map.put("weight", null);
map.put("description", null);
map.put("createTime", null);
map.put("assignedCount", assignedCountMap.getOrDefault(platformJudge.getId(), 0L));
map.put("judgeName", platformJudge.getNickname());
map.put("judgeUsername", platformJudge.getUsername());
map.put("tenantId", platformJudge.getTenantId());
map.put("status", platformJudge.getStatus());
map.put("organization", platformJudge.getOrganization());
map.put("isPlatform", true);
result.add(map);
}
return result;
}
/**
* 获取平台评委租户 ID
*/
private Long getJudgeTenantId() {
LambdaQueryWrapper<SysTenant> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysTenant::getCode, "judge");
SysTenant tenant = sysTenantMapper.selectOne(wrapper);
return tenant != null ? tenant.getId() : null;
} }
@Override @Override

View File

@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.competition.common.enums.ErrorCode; import com.competition.common.enums.ErrorCode;
import com.competition.common.exception.BusinessException; import com.competition.common.exception.BusinessException;
import com.competition.modules.biz.review.dto.CreatePresetCommentDto; import com.competition.modules.biz.review.dto.CreatePresetCommentDto;
import com.competition.modules.biz.contest.entity.BizContest;
import com.competition.modules.biz.contest.mapper.ContestMapper;
import com.competition.modules.biz.review.entity.BizContestJudge; import com.competition.modules.biz.review.entity.BizContestJudge;
import com.competition.modules.biz.review.entity.BizPresetComment; import com.competition.modules.biz.review.entity.BizPresetComment;
import com.competition.modules.biz.review.mapper.ContestJudgeMapper; import com.competition.modules.biz.review.mapper.ContestJudgeMapper;
@ -25,6 +27,7 @@ public class PresetCommentServiceImpl extends ServiceImpl<PresetCommentMapper, B
private final PresetCommentMapper presetCommentMapper; private final PresetCommentMapper presetCommentMapper;
private final ContestJudgeMapper contestJudgeMapper; private final ContestJudgeMapper contestJudgeMapper;
private final ContestMapper contestMapper;
@Override @Override
public BizPresetComment createComment(CreatePresetCommentDto dto, Long judgeId) { public BizPresetComment createComment(CreatePresetCommentDto dto, Long judgeId) {
@ -162,7 +165,14 @@ public class PresetCommentServiceImpl extends ServiceImpl<PresetCommentMapper, B
return judgeRecords.stream().map(j -> { return judgeRecords.stream().map(j -> {
Map<String, Object> map = new LinkedHashMap<>(); Map<String, Object> map = new LinkedHashMap<>();
map.put("contestId", j.getContestId()); map.put("id", j.getContestId());
// 查询赛事详情获取名称和状态
BizContest contest = contestMapper.selectById(j.getContestId());
if (contest != null) {
map.put("contestName", contest.getContestName());
map.put("contestState", contest.getContestState());
map.put("status", contest.getStatus());
}
return map; return map;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
@ -201,9 +211,8 @@ public class PresetCommentServiceImpl extends ServiceImpl<PresetCommentMapper, B
log.info("预设评语同步完成,新建数量:{}", created); log.info("预设评语同步完成,新建数量:{}", created);
Map<String, Object> result = new LinkedHashMap<>(); Map<String, Object> result = new LinkedHashMap<>();
result.put("sourceCount", sourceComments.size()); result.put("message", "同步成功");
result.put("targetContests", targetContestIds.size()); result.put("count", created);
result.put("createdCount", created);
return result; return result;
} }

View File

@ -607,13 +607,23 @@ const handleViewWork = (record: ContestWork) => {
} }
// //
const handleAssignJudge = (record: ContestWork) => { const handleAssignJudge = async (record: ContestWork) => {
currentAssignWork.value = record currentAssignWork.value = record
isBatchAssign.value = false isBatchAssign.value = false
selectedJudgeKeys.value = [] selectedJudgeKeys.value = []
selectedJudgeRows.value = [] selectedJudgeRows.value = []
assignModalVisible.value = true assignModalVisible.value = true
fetchJudgeList() await fetchJudgeList()
//
if (record.assignments && record.assignments.length > 0) {
const assignedJudgeUserIds = record.assignments.map((a) => a.judgeId)
const matchedJudges = judgeList.value.filter((judge) =>
assignedJudgeUserIds.includes(judge.judgeId)
)
selectedJudgeKeys.value = matchedJudges.map((j) => j.id)
selectedJudgeRows.value = matchedJudges
}
} }
// //