feat: 作品管理分配状态/评委回显 + 评委管理租户隔离
Changes: 1. ContestWorkServiceImpl: findAll 返回 assignments、_count 数据 + assignStatus 搜索 2. ContestJudgeServiceImpl: 评委列表返回 assignedCount(已分配作品数) 3. JudgesManagementServiceImpl: 评委库租户隔离(查询当前租户+平台评委,创建在当前租户、平台评委只读) 4. judges/Index.vue: 增加"来源"列 + 平台评委操作限制 5. judges-management.ts: 类型增加 isPlatform/tenantId 6. WorksDetail.vue: 小修改 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3fd7002e2a
commit
170d904081
@ -18,12 +18,17 @@ import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||
import com.competition.modules.biz.contest.mapper.ContestWorkAttachmentMapper;
|
||||
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
||||
import com.competition.modules.biz.contest.service.IContestWorkService;
|
||||
import com.competition.modules.biz.review.entity.BizContestWorkJudgeAssignment;
|
||||
import com.competition.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
|
||||
import com.competition.modules.sys.entity.SysUser;
|
||||
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.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
@ -39,6 +44,8 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
||||
private final ContestWorkAttachmentMapper contestWorkAttachmentMapper;
|
||||
private final ContestRegistrationMapper contestRegistrationMapper;
|
||||
private final ContestMapper contestMapper;
|
||||
private final ContestWorkJudgeAssignmentMapper assignmentMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@ -160,6 +167,57 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
||||
} else if (!isSuperTenant && tenantId != null) {
|
||||
wrapper.eq(BizContestWork::getTenantId, tenantId);
|
||||
}
|
||||
// username 筛选:对应报名表的 account_no
|
||||
if (StringUtils.hasText(dto.getUsername())) {
|
||||
LambdaQueryWrapper<BizContestRegistration> userRegWrapper = new LambdaQueryWrapper<>();
|
||||
if (dto.getContestId() != null) {
|
||||
userRegWrapper.eq(BizContestRegistration::getContestId, dto.getContestId());
|
||||
}
|
||||
if (dto.getTenantId() != null) {
|
||||
userRegWrapper.eq(BizContestRegistration::getTenantId, dto.getTenantId());
|
||||
} else if (!isSuperTenant && tenantId != null) {
|
||||
userRegWrapper.eq(BizContestRegistration::getTenantId, tenantId);
|
||||
}
|
||||
userRegWrapper.eq(BizContestRegistration::getValidState, 1);
|
||||
userRegWrapper.like(BizContestRegistration::getAccountNo, dto.getUsername());
|
||||
List<BizContestRegistration> userRegs = contestRegistrationMapper.selectList(userRegWrapper);
|
||||
Set<Long> userRegIds = userRegs.stream()
|
||||
.map(BizContestRegistration::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (!userRegIds.isEmpty()) {
|
||||
wrapper.in(BizContestWork::getRegistrationId, userRegIds);
|
||||
} else {
|
||||
// 没有匹配的报名记录,返回空结果
|
||||
wrapper.eq(BizContestWork::getId, -1L);
|
||||
}
|
||||
}
|
||||
|
||||
// name 筛选:对应报名表的 account_name
|
||||
if (StringUtils.hasText(dto.getName())) {
|
||||
LambdaQueryWrapper<BizContestRegistration> nameRegWrapper = new LambdaQueryWrapper<>();
|
||||
if (dto.getContestId() != null) {
|
||||
nameRegWrapper.eq(BizContestRegistration::getContestId, dto.getContestId());
|
||||
}
|
||||
if (dto.getTenantId() != null) {
|
||||
nameRegWrapper.eq(BizContestRegistration::getTenantId, dto.getTenantId());
|
||||
} else if (!isSuperTenant && tenantId != null) {
|
||||
nameRegWrapper.eq(BizContestRegistration::getTenantId, tenantId);
|
||||
}
|
||||
nameRegWrapper.eq(BizContestRegistration::getValidState, 1);
|
||||
nameRegWrapper.like(BizContestRegistration::getAccountName, dto.getName());
|
||||
List<BizContestRegistration> nameRegs = contestRegistrationMapper.selectList(nameRegWrapper);
|
||||
Set<Long> nameRegIds = nameRegs.stream()
|
||||
.map(BizContestRegistration::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (!nameRegIds.isEmpty()) {
|
||||
wrapper.in(BizContestWork::getRegistrationId, nameRegIds);
|
||||
} else {
|
||||
wrapper.eq(BizContestWork::getId, -1L);
|
||||
}
|
||||
}
|
||||
|
||||
Set<Long> keywordRegistrationIds = Collections.emptySet();
|
||||
if (StringUtils.hasText(dto.getKeyword())) {
|
||||
String keyword = dto.getKeyword();
|
||||
@ -200,12 +258,34 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(dto.getSubmitStartTime())) {
|
||||
wrapper.ge(BizContestWork::getSubmitTime,
|
||||
LocalDateTime.parse(dto.getSubmitStartTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||
wrapper.ge(BizContestWork::getSubmitTime, parseDateTime(dto.getSubmitStartTime(), true));
|
||||
}
|
||||
if (StringUtils.hasText(dto.getSubmitEndTime())) {
|
||||
wrapper.le(BizContestWork::getSubmitTime,
|
||||
LocalDateTime.parse(dto.getSubmitEndTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||
wrapper.le(BizContestWork::getSubmitTime, parseDateTime(dto.getSubmitEndTime(), false));
|
||||
}
|
||||
|
||||
// assignStatus 筛选:基于分配表判断已分配/未分配
|
||||
if (StringUtils.hasText(dto.getAssignStatus()) && dto.getContestId() != null) {
|
||||
LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignQueryWrapper = new LambdaQueryWrapper<>();
|
||||
assignQueryWrapper.eq(BizContestWorkJudgeAssignment::getContestId, dto.getContestId());
|
||||
assignQueryWrapper.select(BizContestWorkJudgeAssignment::getWorkId);
|
||||
assignQueryWrapper.groupBy(BizContestWorkJudgeAssignment::getWorkId);
|
||||
List<BizContestWorkJudgeAssignment> assignedRecords = assignmentMapper.selectList(assignQueryWrapper);
|
||||
Set<Long> assignedWorkIds = assignedRecords.stream()
|
||||
.map(BizContestWorkJudgeAssignment::getWorkId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if ("assigned".equals(dto.getAssignStatus())) {
|
||||
if (assignedWorkIds.isEmpty()) {
|
||||
wrapper.eq(BizContestWork::getId, -1L);
|
||||
} else {
|
||||
wrapper.in(BizContestWork::getId, assignedWorkIds);
|
||||
}
|
||||
} else if ("unassigned".equals(dto.getAssignStatus())) {
|
||||
if (!assignedWorkIds.isEmpty()) {
|
||||
wrapper.notIn(BizContestWork::getId, assignedWorkIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 默认只查最新版本
|
||||
@ -241,8 +321,39 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
||||
.collect(Collectors.toMap(BizContest::getId, c -> c));
|
||||
}
|
||||
|
||||
// 批量查询分配信息
|
||||
Set<Long> workIds = result.getRecords().stream()
|
||||
.map(BizContestWork::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Map<Long, List<BizContestWorkJudgeAssignment>> assignmentMap = new HashMap<>();
|
||||
Map<Long, String> judgeNameMap = new HashMap<>();
|
||||
if (!workIds.isEmpty()) {
|
||||
LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignWrapper = new LambdaQueryWrapper<>();
|
||||
assignWrapper.in(BizContestWorkJudgeAssignment::getWorkId, workIds);
|
||||
if (dto.getContestId() != null) {
|
||||
assignWrapper.eq(BizContestWorkJudgeAssignment::getContestId, dto.getContestId());
|
||||
}
|
||||
List<BizContestWorkJudgeAssignment> allAssignments = assignmentMapper.selectList(assignWrapper);
|
||||
assignmentMap = allAssignments.stream()
|
||||
.collect(Collectors.groupingBy(BizContestWorkJudgeAssignment::getWorkId));
|
||||
|
||||
// 批量查询评委用户信息
|
||||
Set<Long> judgeIds = allAssignments.stream()
|
||||
.map(BizContestWorkJudgeAssignment::getJudgeId)
|
||||
.collect(Collectors.toSet());
|
||||
if (!judgeIds.isEmpty()) {
|
||||
List<SysUser> judgeUsers = sysUserMapper.selectBatchIds(judgeIds);
|
||||
for (SysUser u : judgeUsers) {
|
||||
judgeNameMap.put(u.getId(), u.getNickname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<Long, BizContestRegistration> finalRegistrationMap = registrationMap;
|
||||
Map<Long, BizContest> finalContestMap = contestMap;
|
||||
Map<Long, List<BizContestWorkJudgeAssignment>> finalAssignmentMap = assignmentMap;
|
||||
Map<Long, String> finalJudgeNameMap = judgeNameMap;
|
||||
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||
.map(work -> {
|
||||
Map<String, Object> map = workToMap(work);
|
||||
@ -279,6 +390,28 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
||||
map.put("accountName", reg.getAccountName());
|
||||
map.put("userId", reg.getUserId());
|
||||
}
|
||||
|
||||
// 分配信息
|
||||
List<BizContestWorkJudgeAssignment> workAssignments = finalAssignmentMap.getOrDefault(work.getId(), Collections.emptyList());
|
||||
List<Map<String, Object>> assignmentVoList = workAssignments.stream().map(a -> {
|
||||
Map<String, Object> assignVo = new LinkedHashMap<>();
|
||||
assignVo.put("id", a.getId());
|
||||
assignVo.put("judgeId", a.getJudgeId());
|
||||
assignVo.put("status", a.getStatus());
|
||||
assignVo.put("assignmentTime", a.getAssignmentTime());
|
||||
Map<String, Object> judgeVo = new LinkedHashMap<>();
|
||||
judgeVo.put("id", a.getJudgeId());
|
||||
judgeVo.put("nickname", finalJudgeNameMap.getOrDefault(a.getJudgeId(), ""));
|
||||
assignVo.put("judge", judgeVo);
|
||||
return assignVo;
|
||||
}).collect(Collectors.toList());
|
||||
map.put("assignments", assignmentVoList);
|
||||
|
||||
// _count 用于分配状态判断
|
||||
Map<String, Object> countVo = new LinkedHashMap<>();
|
||||
countVo.put("assignments", workAssignments.size());
|
||||
map.put("_count", countVo);
|
||||
|
||||
return map;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
@ -435,6 +568,18 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
||||
|
||||
// ====== 私有辅助方法 ======
|
||||
|
||||
/**
|
||||
* 解析时间参数,支持 "yyyy-MM-dd" 和 "yyyy-MM-ddTHH:mm:ss" 两种格式。
|
||||
* isStart=true 时纯日期补 00:00:00,isStart=false 时纯日期补 23:59:59。
|
||||
*/
|
||||
private LocalDateTime parseDateTime(String value, boolean isStart) {
|
||||
if (value.contains("T")) {
|
||||
return LocalDateTime.parse(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
|
||||
}
|
||||
LocalDate date = LocalDate.parse(value, DateTimeFormatter.ISO_LOCAL_DATE);
|
||||
return isStart ? date.atStartOfDay() : date.atTime(23, 59, 59);
|
||||
}
|
||||
|
||||
private String generateWorkNo(Long contestId) {
|
||||
LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(BizContestWork::getContestId, contestId);
|
||||
|
||||
@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.competition.common.enums.ErrorCode;
|
||||
import com.competition.common.exception.BusinessException;
|
||||
import com.competition.common.result.PageResult;
|
||||
import com.competition.common.util.SecurityUtil;
|
||||
import com.competition.modules.biz.judge.service.IJudgesManagementService;
|
||||
import com.competition.modules.sys.entity.SysRole;
|
||||
import com.competition.modules.sys.entity.SysTenant;
|
||||
@ -48,23 +49,56 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评委角色 ID
|
||||
* 获取或自动创建评委角色 ID
|
||||
*/
|
||||
private Long getJudgeRoleId(Long tenantId) {
|
||||
private Long getOrCreateJudgeRoleId(Long tenantId) {
|
||||
LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysRole::getCode, "judge");
|
||||
wrapper.eq(SysRole::getTenantId, tenantId);
|
||||
SysRole role = sysRoleMapper.selectOne(wrapper);
|
||||
if (role == null) {
|
||||
throw BusinessException.of(ErrorCode.BAD_REQUEST, "评委角色不存在,请先在评委租户下创建 code='judge' 的角色");
|
||||
if (role != null) {
|
||||
return role.getId();
|
||||
}
|
||||
// 自动创建 judge 角色
|
||||
role = new SysRole();
|
||||
role.setTenantId(tenantId);
|
||||
role.setCode("judge");
|
||||
role.setName("评委");
|
||||
role.setDescription("评委角色");
|
||||
sysRoleMapper.insert(role);
|
||||
log.info("自动创建评委角色,租户ID:{},角色ID:{}", tenantId, role.getId());
|
||||
return role.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证评委是否可被当前租户操作(查看/编辑/删除)
|
||||
* 返回 true 表示是平台评委(只读),false 表示是本租户评委
|
||||
*/
|
||||
private boolean checkJudgeOwnership(SysUser user) {
|
||||
Long currentTenantId = SecurityUtil.getCurrentTenantId();
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
|
||||
// 不属于当前租户也不属于平台评委租户
|
||||
if (!currentTenantId.equals(user.getTenantId()) && !judgeTenantId.equals(user.getTenantId())) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
return judgeTenantId.equals(user.getTenantId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证评委可被修改操作(非平台评委 + 属于当前租户)
|
||||
*/
|
||||
private void checkJudgeWritable(SysUser user) {
|
||||
if (checkJudgeOwnership(user)) {
|
||||
throw BusinessException.of(ErrorCode.FORBIDDEN, "平台评委不允许修改");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 SysUser 转为前端需要的 Map
|
||||
*/
|
||||
private Map<String, Object> toMap(SysUser user) {
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("id", user.getId());
|
||||
map.put("username", user.getUsername());
|
||||
@ -77,6 +111,8 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
map.put("userSource", user.getUserSource());
|
||||
map.put("createTime", user.getCreateTime());
|
||||
map.put("modifyTime", user.getModifyTime());
|
||||
map.put("tenantId", user.getTenantId());
|
||||
map.put("isPlatform", judgeTenantId.equals(user.getTenantId()));
|
||||
return map;
|
||||
}
|
||||
|
||||
@ -97,19 +133,19 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
throw BusinessException.of(ErrorCode.BAD_REQUEST, "密码不能为空");
|
||||
}
|
||||
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
Long currentTenantId = SecurityUtil.getCurrentTenantId();
|
||||
|
||||
// 检查用户名在评委租户内唯一
|
||||
// 检查用户名在当前租户内唯一
|
||||
LambdaQueryWrapper<SysUser> dupWrapper = new LambdaQueryWrapper<>();
|
||||
dupWrapper.eq(SysUser::getTenantId, judgeTenantId);
|
||||
dupWrapper.eq(SysUser::getTenantId, currentTenantId);
|
||||
dupWrapper.eq(SysUser::getUsername, username);
|
||||
if (sysUserMapper.selectCount(dupWrapper) > 0) {
|
||||
throw BusinessException.of(ErrorCode.BAD_REQUEST, "该用户名已存在");
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
// 创建用户(归属当前租户)
|
||||
SysUser user = new SysUser();
|
||||
user.setTenantId(judgeTenantId);
|
||||
user.setTenantId(currentTenantId);
|
||||
user.setUsername(username);
|
||||
user.setPassword(passwordEncoder.encode(password));
|
||||
user.setNickname(nickname);
|
||||
@ -121,8 +157,8 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
user.setStatus("enabled");
|
||||
sysUserMapper.insert(user);
|
||||
|
||||
// 分配评委角色
|
||||
Long judgeRoleId = getJudgeRoleId(judgeTenantId);
|
||||
// 分配评委角色(在当前租户下查找或自动创建 judge 角色)
|
||||
Long judgeRoleId = getOrCreateJudgeRoleId(currentTenantId);
|
||||
SysUserRole userRole = new SysUserRole();
|
||||
userRole.setUserId(user.getId());
|
||||
userRole.setRoleId(judgeRoleId);
|
||||
@ -134,10 +170,12 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
|
||||
@Override
|
||||
public PageResult<Map<String, Object>> findAll(Long page, Long pageSize, String keyword, String status) {
|
||||
Long currentTenantId = SecurityUtil.getCurrentTenantId();
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SysUser::getTenantId, judgeTenantId);
|
||||
// 查询当前租户评委 + 平台评委
|
||||
wrapper.in(SysUser::getTenantId, List.of(currentTenantId, judgeTenantId));
|
||||
|
||||
if (keyword != null && !keyword.isBlank()) {
|
||||
wrapper.and(w -> w
|
||||
@ -168,12 +206,7 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
if (user == null) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
|
||||
checkJudgeOwnership(user);
|
||||
return toMap(user);
|
||||
}
|
||||
|
||||
@ -184,11 +217,7 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
if (user == null) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
checkJudgeWritable(user);
|
||||
|
||||
if (params.containsKey("nickname")) {
|
||||
user.setNickname((String) params.get("nickname"));
|
||||
@ -231,11 +260,7 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
if (user == null) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
checkJudgeWritable(user);
|
||||
|
||||
user.setStatus(status);
|
||||
sysUserMapper.updateById(user);
|
||||
@ -247,11 +272,7 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
if (user == null) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||
}
|
||||
checkJudgeWritable(user);
|
||||
|
||||
sysUserMapper.deleteById(id);
|
||||
log.info("评委已删除,ID:{}", id);
|
||||
@ -264,15 +285,15 @@ public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||
return;
|
||||
}
|
||||
|
||||
Long judgeTenantId = getJudgeTenantId();
|
||||
Long currentTenantId = SecurityUtil.getCurrentTenantId();
|
||||
|
||||
// 校验所有 ID 都属于评委租户
|
||||
// 校验所有 ID 都属于当前租户(不允许删除平台评委)
|
||||
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(SysUser::getId, ids);
|
||||
wrapper.eq(SysUser::getTenantId, judgeTenantId);
|
||||
wrapper.eq(SysUser::getTenantId, currentTenantId);
|
||||
Long count = sysUserMapper.selectCount(wrapper);
|
||||
if (count != ids.size()) {
|
||||
throw BusinessException.of(ErrorCode.BAD_REQUEST, "部分评委不存在或不属于评委库");
|
||||
throw BusinessException.of(ErrorCode.BAD_REQUEST, "部分评委不存在或不属于当前机构");
|
||||
}
|
||||
|
||||
sysUserMapper.deleteBatchIds(ids);
|
||||
|
||||
@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.competition.common.enums.ErrorCode;
|
||||
import com.competition.common.exception.BusinessException;
|
||||
import com.competition.modules.biz.review.entity.BizContestJudge;
|
||||
import com.competition.modules.biz.review.entity.BizContestWorkJudgeAssignment;
|
||||
import com.competition.modules.biz.review.mapper.ContestJudgeMapper;
|
||||
import com.competition.modules.biz.review.mapper.ContestWorkJudgeAssignmentMapper;
|
||||
import com.competition.modules.biz.review.service.IContestJudgeService;
|
||||
import com.competition.modules.sys.entity.SysUser;
|
||||
import com.competition.modules.sys.mapper.SysUserMapper;
|
||||
@ -23,6 +25,7 @@ import java.util.stream.Collectors;
|
||||
public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, BizContestJudge> implements IContestJudgeService {
|
||||
|
||||
private final ContestJudgeMapper contestJudgeMapper;
|
||||
private final ContestWorkJudgeAssignmentMapper assignmentMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
|
||||
@Override
|
||||
@ -71,6 +74,15 @@ public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, Biz
|
||||
}
|
||||
}
|
||||
|
||||
// 批量查询每个评委在该赛事下的已分配作品数
|
||||
Map<Long, Long> assignedCountMap = new HashMap<>();
|
||||
for (BizContestJudge j : judges) {
|
||||
LambdaQueryWrapper<BizContestWorkJudgeAssignment> assignWrapper = new LambdaQueryWrapper<>();
|
||||
assignWrapper.eq(BizContestWorkJudgeAssignment::getContestId, contestId);
|
||||
assignWrapper.eq(BizContestWorkJudgeAssignment::getJudgeId, j.getJudgeId());
|
||||
assignedCountMap.put(j.getJudgeId(), assignmentMapper.selectCount(assignWrapper));
|
||||
}
|
||||
|
||||
return judges.stream().map(j -> {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("id", j.getId());
|
||||
@ -80,6 +92,7 @@ public class ContestJudgeServiceImpl extends ServiceImpl<ContestJudgeMapper, Biz
|
||||
map.put("weight", j.getWeight());
|
||||
map.put("description", j.getDescription());
|
||||
map.put("createTime", j.getCreateTime());
|
||||
map.put("assignedCount", assignedCountMap.getOrDefault(j.getJudgeId(), 0L));
|
||||
|
||||
SysUser user = userMap.get(j.getJudgeId());
|
||||
if (user != null) {
|
||||
|
||||
@ -16,6 +16,8 @@ export interface Judge {
|
||||
modifier?: number;
|
||||
createTime?: string;
|
||||
modifyTime?: string;
|
||||
tenantId?: number;
|
||||
isPlatform?: boolean;
|
||||
roles?: Array<{
|
||||
id: number;
|
||||
role: {
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
:row-selection="rowSelection"
|
||||
row-key="id"
|
||||
row-key="assignmentId"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
@ -75,18 +75,18 @@
|
||||
<a @click="handleViewWork(record)">{{ record.workNo || "-" }}</a>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'score'">
|
||||
<span v-if="record.score !== null" class="score">
|
||||
{{ Number(record.score).toFixed(2) }}
|
||||
<span v-if="record.totalScore != null" class="score">
|
||||
{{ Number(record.totalScore).toFixed(2) }}
|
||||
</span>
|
||||
<span v-else class="text-gray">-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'reviewStatus'">
|
||||
<a-tag v-if="record.reviewStatus === 'reviewed'" color="success">已评审</a-tag>
|
||||
<a-tag v-if="record.status === 'completed'" color="success">已评审</a-tag>
|
||||
<a-tag v-else color="default">未评审</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-button type="link" size="small" @click="handleReview(record)">
|
||||
{{ record.reviewStatus === 'reviewed' ? '查看' : '评审' }}
|
||||
{{ record.status === 'completed' ? '查看' : '评审' }}
|
||||
</a-button>
|
||||
</template>
|
||||
</template>
|
||||
@ -169,7 +169,7 @@ const columns = [
|
||||
{
|
||||
title: "报名账号",
|
||||
key: "accountNo",
|
||||
dataIndex: "accountNo",
|
||||
dataIndex: "submitterAccountNo",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
@ -200,7 +200,7 @@ const currentWorkIndex = ref<number>(0)
|
||||
const workListForNav = computed(() => {
|
||||
return dataSource.value.map((item: any) => ({
|
||||
workId: item.workId,
|
||||
assignmentId: item.id,
|
||||
assignmentId: item.assignmentId,
|
||||
}))
|
||||
})
|
||||
|
||||
@ -258,10 +258,10 @@ const handleViewWork = (record: any) => {
|
||||
|
||||
// 评审作品
|
||||
const handleReview = (record: any) => {
|
||||
currentAssignmentId.value = record.id
|
||||
currentAssignmentId.value = record.assignmentId
|
||||
currentWorkId.value = record.workId
|
||||
// 查找当前作品在列表中的索引
|
||||
const index = dataSource.value.findIndex((item: any) => item.id === record.id)
|
||||
const index = dataSource.value.findIndex((item: any) => item.assignmentId === record.assignmentId)
|
||||
currentWorkIndex.value = index >= 0 ? index : 0
|
||||
reviewModalVisible.value = true
|
||||
}
|
||||
@ -270,7 +270,7 @@ const handleReview = (record: any) => {
|
||||
const handleNavigate = (index: number) => {
|
||||
const item = dataSource.value[index]
|
||||
if (item) {
|
||||
currentAssignmentId.value = item.id
|
||||
currentAssignmentId.value = item.assignmentId
|
||||
currentWorkId.value = item.workId
|
||||
currentWorkIndex.value = index
|
||||
}
|
||||
|
||||
@ -27,10 +27,10 @@
|
||||
<a-popconfirm
|
||||
v-permission="'judge:delete'"
|
||||
title="确定要删除选中的评委吗?"
|
||||
:disabled="selectedRowKeys.length === 0"
|
||||
:disabled="selectedRowKeys.length === 0 || selectedRows.every(r => r.isPlatform)"
|
||||
@confirm="handleBatchDelete"
|
||||
>
|
||||
<a-button danger :disabled="selectedRowKeys.length === 0">
|
||||
<a-button danger :disabled="selectedRowKeys.length === 0 || selectedRows.every(r => r.isPlatform)">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
删除
|
||||
</a-button>
|
||||
@ -116,6 +116,10 @@
|
||||
<span v-else-if="record.gender === 'female'">女</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'source'">
|
||||
<a-tag v-if="record.isPlatform" color="blue">平台</a-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="record.status === 'enabled' ? 'success' : 'error'">
|
||||
{{ record.status === "enabled" ? "启用" : "停用" }}
|
||||
@ -140,23 +144,28 @@
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button
|
||||
v-permission="'judge:update'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleToggleStatus(record)"
|
||||
>
|
||||
{{ record.status === "enabled" ? "冻结" : "解冻" }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-permission="'judge:update'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>
|
||||
编辑
|
||||
</a-button>
|
||||
<template v-if="!record.isPlatform">
|
||||
<a-button
|
||||
v-permission="'judge:update'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleToggleStatus(record)"
|
||||
>
|
||||
{{ record.status === "enabled" ? "冻结" : "解冻" }}
|
||||
</a-button>
|
||||
</template>
|
||||
<template v-if="!record.isPlatform">
|
||||
<a-button
|
||||
v-permission="'judge:update'"
|
||||
type="link"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>
|
||||
编辑
|
||||
</a-button>
|
||||
</template>
|
||||
<a-popconfirm
|
||||
v-if="!record.isPlatform"
|
||||
v-permission="'judge:delete'"
|
||||
title="确定要删除这个评委吗?"
|
||||
@confirm="handleDelete(record.id)"
|
||||
@ -346,6 +355,11 @@ const columns = [
|
||||
key: "index",
|
||||
width: 70,
|
||||
},
|
||||
{
|
||||
title: "来源",
|
||||
key: "source",
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: "所属单位",
|
||||
key: "organization",
|
||||
|
||||
@ -255,16 +255,16 @@
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'judgeName'">
|
||||
{{ record.judge?.nickname || record.judge?.username || "-" }}
|
||||
{{ record.judgeName || record.judgeUsername || "-" }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'phone'">
|
||||
{{ record.judge?.phone || "-" }}
|
||||
{{ record.phone || "-" }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'tenant'">
|
||||
{{ record.judge?.organization || record.judge?.tenant?.name || "-" }}
|
||||
{{ record.organization || record.tenantName || "-" }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'assignedCount'">
|
||||
{{ record._count?.assignedContestWorks || 0 }}
|
||||
{{ record.assignedCount || 0 }}
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
@ -283,10 +283,10 @@
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template #title>
|
||||
{{ item.judge?.nickname || item.judge?.username || "-" }}
|
||||
{{ item.judgeName || item.judgeUsername || "-" }}
|
||||
</template>
|
||||
<template #description>
|
||||
{{ item.judge?.organization || item.judge?.tenant?.name || "-" }}
|
||||
{{ item.organization || item.tenantName || "-" }}
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
<template #actions>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user