From e651d2208eeb1f79607fc25cba23897c10ffc056 Mon Sep 17 00:00:00 2001 From: luoweijian <1329394916@qq.com> Date: Sat, 4 Jul 2026 10:53:51 +0800 Subject: [PATCH] =?UTF-8?q?=E8=80=83=E7=94=9F=E7=AB=AF=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/SearchUserDomainServiceImpl.java | 2 +- .../domain/job/ShortAnswerScoringJob.java | 42 ++++++++----- .../impl/SubmitPaperDomainServiceImpl.java | 19 ++++-- .../strategy/ClassicPaperStrategy.java | 2 +- .../domain/enums/CandidateTaskStatusEnum.java | 3 +- .../task/domain/enums/TaskUserStatusEnum.java | 3 +- .../project/task/domain/param/TaskParam.java | 2 + .../CandidateSearchTaskDomainServiceImpl.java | 60 ++++++++++++++++++- .../impl/SearchTaskDomainServiceImpl.java | 7 +++ src/main/resources/application-test.yml | 2 +- 10 files changed, 118 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/project/ding/domain/service/impl/SearchUserDomainServiceImpl.java b/src/main/java/com/project/ding/domain/service/impl/SearchUserDomainServiceImpl.java index 34c43cf..f9c3d29 100644 --- a/src/main/java/com/project/ding/domain/service/impl/SearchUserDomainServiceImpl.java +++ b/src/main/java/com/project/ding/domain/service/impl/SearchUserDomainServiceImpl.java @@ -71,7 +71,7 @@ public class SearchUserDomainServiceImpl implements SearchUserDomainService { .in(TaskUserEntity::getStatus, List.of(TaskUserStatusEnum.Not_Start.getValue(), TaskUserStatusEnum.Fail.getValue(), TaskUserStatusEnum.Pass.getValue())))).intValue()); dto.setParticipatedExamTaskCount(Long.valueOf(taskUserBaseService.count(new LambdaQueryWrapper() .eq(TaskUserEntity::getUserId, dto.getId()) - .in(TaskUserEntity::getStatus, List.of(TaskUserStatusEnum.Fail.getValue(), TaskUserStatusEnum.Pass.getValue())))).intValue()); + .in(TaskUserEntity::getStatus, List.of(TaskUserStatusEnum.Fail.getValue(), TaskUserStatusEnum.Pass.getValue(), TaskUserStatusEnum.Scoring.getValue())))).intValue()); dto.setPassedExamTaskCount(Long.valueOf(taskUserBaseService.count(new LambdaQueryWrapper() .eq(TaskUserEntity::getUserId, dto.getId()) .in(TaskUserEntity::getStatus, List.of(TaskUserStatusEnum.Pass.getValue())))).intValue()); diff --git a/src/main/java/com/project/exam/domain/job/ShortAnswerScoringJob.java b/src/main/java/com/project/exam/domain/job/ShortAnswerScoringJob.java index e993007..d15015e 100644 --- a/src/main/java/com/project/exam/domain/job/ShortAnswerScoringJob.java +++ b/src/main/java/com/project/exam/domain/job/ShortAnswerScoringJob.java @@ -311,30 +311,44 @@ public class ShortAnswerScoringJob { log.warn(">>> [简答题评分任务] 查询及格线失败,使用默认60分", e); } - boolean oldPass = record.getPass() != null && record.getPass(); boolean newPass = total >= passScore; record.setPass(newPass); - // 如果及格状态变了,更新相关冗余字段 - if (oldPass != newPass && task != null && tu != null) { - try { + // 更新 TaskUser 状态 + if (tu != null) { + Integer currentStatus = tu.getStatus(); + + // 规则1:已经是 Pass → 不降级 + if (TaskUserStatusEnum.Pass.getValue().equals(currentStatus)) { + // 不做任何变更 + } + // 规则2:评分中(新数据) → 转最终结果 + else if (TaskUserStatusEnum.Scoring.getValue().equals(currentStatus)) { + tu.setLastRecordId(record.getId()); + tu.setStatus(newPass + ? TaskUserStatusEnum.Pass.getValue() + : TaskUserStatusEnum.Fail.getValue()); + taskUserMapper.updateById(tu); + if (newPass) { - // 不及格→及格:增加 pass_num,更新用户状态 taskBaseService.update() .setSql("pass_num = pass_num + 1") .eq("id", task.getId()) .update(); - - if (!TaskUserStatusEnum.Pass.getValue().equals(tu.getStatus())) { - tu.setStatus(TaskUserStatusEnum.Pass.getValue()); - tu.setLastRecordId(record.getId()); - taskUserMapper.updateById(tu); - } } - // 及格→不及格:理论上不会发生(简答题只加分不减分),暂不处理 - } catch (Exception e) { - log.error(">>> [简答题评分任务] 更新及格状态失败, recordId={}", record.getId(), e); } + // 规则3:旧数据兼容(之前 Fail,评分完才及格 → 升级 Pass) + else if (TaskUserStatusEnum.Fail.getValue().equals(currentStatus) && newPass) { + tu.setStatus(TaskUserStatusEnum.Pass.getValue()); + tu.setLastRecordId(record.getId()); + taskUserMapper.updateById(tu); + + taskBaseService.update() + .setSql("pass_num = pass_num + 1") + .eq("id", task.getId()) + .update(); + } + // 规则4:其他情况(Fail 且未及格、Not_Start等)→ 不做变更 } } } diff --git a/src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java b/src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java index 1c7c01b..1d4574a 100644 --- a/src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java +++ b/src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java @@ -138,7 +138,7 @@ public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService { } // 更新 TaskUser 冗余字段 - updateTaskUserStatus(taskUser , isPassed , recordId); + updateTaskUserStatus(taskUser , isPassed , recordId , hasShortAnswer); // 异步收集易错统计 statisticsApplicationService.collectErrorProneStatistics( @@ -170,12 +170,23 @@ public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService { return 0.0; } - private void updateTaskUserStatus(TaskUserEntity taskUser, boolean isPassed, Long recordId) { + private void updateTaskUserStatus(TaskUserEntity taskUser, boolean isPassed, Long recordId, boolean hasShortAnswer) { taskUser.setAttemptNum((taskUser.getAttemptNum() == null ? 0 : taskUser.getAttemptNum()) + 1); - if (!TaskUserStatusEnum.Pass.getValue().equals(taskUser.getStatus())) { - taskUser.setLastRecordId(recordId); + // 如果已经通过,不降级 + if (TaskUserStatusEnum.Pass.getValue().equals(taskUser.getStatus())) { + taskUserMapper.updateById(taskUser); + return; + } + + taskUser.setLastRecordId(recordId); + + if (hasShortAnswer) { + // 有简答题 → 先设为评分中,等异步评分完成再定最终结果 + taskUser.setStatus(TaskUserStatusEnum.Scoring.getValue()); + } else { + // 纯客观题 → 直接出最终结果 taskUser.setStatus(isPassed ? TaskUserStatusEnum.Pass.getValue() : TaskUserStatusEnum.Fail.getValue()); } diff --git a/src/main/java/com/project/exam/domain/service/strategy/ClassicPaperStrategy.java b/src/main/java/com/project/exam/domain/service/strategy/ClassicPaperStrategy.java index 63b482f..d3bad0a 100644 --- a/src/main/java/com/project/exam/domain/service/strategy/ClassicPaperStrategy.java +++ b/src/main/java/com/project/exam/domain/service/strategy/ClassicPaperStrategy.java @@ -97,7 +97,7 @@ public class ClassicPaperStrategy implements PaperAssemblyStrategy { // 6. 过滤出该版本的快照,按 sortOrder 排序 List selectedSnapshots = allSnapshots.stream() .filter(s -> selectedSetIndex.equals(s.getSetIndex())) - .sorted(Comparator.comparing(TaskPaperSnapshotEntity::getSortOrder)) + .sorted(Comparator.comparing(TaskPaperSnapshotEntity::getQuestionType)) .collect(Collectors.toList()); // 7. 构建 QuestionSnapshot 列表 diff --git a/src/main/java/com/project/task/domain/enums/CandidateTaskStatusEnum.java b/src/main/java/com/project/task/domain/enums/CandidateTaskStatusEnum.java index 287bca3..5154555 100644 --- a/src/main/java/com/project/task/domain/enums/CandidateTaskStatusEnum.java +++ b/src/main/java/com/project/task/domain/enums/CandidateTaskStatusEnum.java @@ -11,7 +11,8 @@ public enum CandidateTaskStatusEnum implements HasValueEnum { Pending_Exam("待开考") , Not_Exam("未考试") , Has_Exam("已考试") , - End("已截止") + End("已截止") , + Scoring("评分中") ; private final String value; } diff --git a/src/main/java/com/project/task/domain/enums/TaskUserStatusEnum.java b/src/main/java/com/project/task/domain/enums/TaskUserStatusEnum.java index 80ecda5..b41b94c 100644 --- a/src/main/java/com/project/task/domain/enums/TaskUserStatusEnum.java +++ b/src/main/java/com/project/task/domain/enums/TaskUserStatusEnum.java @@ -9,7 +9,8 @@ import lombok.RequiredArgsConstructor; public enum TaskUserStatusEnum implements HasValueEnum { Not_Start(0 , "未参加") , Fail(1 , "未通过") , - Pass(2 ,"已通过"); + Pass(2 ,"已通过") , + Scoring(3 , "评分中"); private final Integer value; diff --git a/src/main/java/com/project/task/domain/param/TaskParam.java b/src/main/java/com/project/task/domain/param/TaskParam.java index bfeda8f..072eccb 100644 --- a/src/main/java/com/project/task/domain/param/TaskParam.java +++ b/src/main/java/com/project/task/domain/param/TaskParam.java @@ -23,4 +23,6 @@ public class TaskParam extends BaseParam { private String taskStatus; + private Boolean excludeEnd = Boolean.TRUE; + } diff --git a/src/main/java/com/project/task/domain/service/impl/CandidateSearchTaskDomainServiceImpl.java b/src/main/java/com/project/task/domain/service/impl/CandidateSearchTaskDomainServiceImpl.java index c281d7b..02b61cb 100644 --- a/src/main/java/com/project/task/domain/service/impl/CandidateSearchTaskDomainServiceImpl.java +++ b/src/main/java/com/project/task/domain/service/impl/CandidateSearchTaskDomainServiceImpl.java @@ -2,6 +2,8 @@ package com.project.task.domain.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -9,19 +11,27 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.project.base.domain.result.PageResult; import com.project.base.domain.result.Result; import com.project.base.domain.utils.PageConverter; +import com.project.classicpaper.domain.entity.ClassicCategoryEntity; +import com.project.classicpaper.domain.entity.ClassicPaperSetEntity; +import com.project.classicpaper.domain.service.ClassicCategoryBaseService; +import com.project.classicpaper.domain.service.ClassicPaperSetBaseService; import com.project.ding.utils.SecurityUtils; import com.project.exam.domain.entity.ExamRecordEntity; import com.project.exam.mapper.ExamRecordMapper; +import com.project.information.domain.entity.ProductLineEntity; +import com.project.information.domain.service.ProductLineBaseService; import com.project.task.domain.dto.TaskDTO; import com.project.task.domain.entity.TaskEntity; import com.project.task.domain.entity.TaskUserEntity; import com.project.task.domain.enums.CandidateExamResultEnum; import com.project.task.domain.enums.CandidateTaskStatusEnum; +import com.project.task.domain.enums.ExamModeEnum; import com.project.task.domain.enums.TaskUserStatusEnum; import com.project.task.domain.param.TaskParam; import com.project.task.domain.service.CandidateSearchTaskDomainService; import com.project.task.mapper.TaskMapper; import com.project.task.mapper.TaskUserMapper; +import io.vavr.control.Try; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -44,6 +54,15 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask @Autowired private ExamRecordMapper examRecordMapper; + @Autowired + private ProductLineBaseService productLineBaseService; + + @Autowired + private ClassicPaperSetBaseService classicPaperSetBaseService; + + @Autowired + private ClassicCategoryBaseService classicCategoryBaseService; + @Override public Result> candidateSearch(TaskParam param) throws Exception { @@ -74,6 +93,11 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.End.name())) { queryWrapper.lt(TaskEntity::getEndTime , beginOfToday); } + if (BooleanUtil.isTrue(param.getExcludeEnd())) { + // 强制要求:结束时间必须 >= 今天 00:00:00 + // 这样,截止时间是今天的任务会被【保留】,只有昨天及以前截止的任务会被【排除】 + queryWrapper.ge(TaskEntity::getEndTime, beginOfToday); + } // 未考试:在有效期内,且用户状态为未参加考试 if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.Not_Exam.name())) { queryWrapper.le(TaskEntity::getStartTime, endOfToday); @@ -81,7 +105,7 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask queryWrapper.inSql(TaskEntity::getId, String.format("SELECT task_id FROM evaluator_task_user WHERE user_id = '%s' AND status = %s and deleted = 0 ", SecurityUtils.getUserId() , TaskUserStatusEnum.Not_Start.getValue())); } - // 已考试:在有效期内,用户状态为已通过或未通过 + // 已考试:在有效期内,用户状态为已通过或未通过(不含评分中) if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.Has_Exam.name())) { queryWrapper.le(TaskEntity::getStartTime, endOfToday); queryWrapper.ge(TaskEntity::getEndTime, beginOfToday); @@ -90,6 +114,14 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask SecurityUtils.getUserId() , CollUtil.join(List.of(TaskUserStatusEnum.Fail.getValue() , TaskUserStatusEnum.Pass.getValue()) , ","))); } + // 评分中:在有效期内,用户状态为评分中 + if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.Scoring.name())) { + queryWrapper.le(TaskEntity::getStartTime, endOfToday); + queryWrapper.ge(TaskEntity::getEndTime, beginOfToday); + queryWrapper.inSql(TaskEntity::getId, + String.format("SELECT task_id FROM evaluator_task_user WHERE user_id = '%s' AND status = %s and deleted = 0 ", + SecurityUtils.getUserId() , TaskUserStatusEnum.Scoring.getValue())); + } // 排序 if (Objects.nonNull(param.getSortType())) { switch (param.getSortType()) { @@ -149,6 +181,11 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask dto.setCandidateTaskStatus(CandidateTaskStatusEnum.Has_Exam.name()); dto.setCandidateTaskStatusText(CandidateTaskStatusEnum.Has_Exam.getValue()); } + + if (TaskUserStatusEnum.Scoring.getValue().equals(taskUserEntity.getStatus())) { + dto.setCandidateTaskStatus(CandidateTaskStatusEnum.Scoring.name()); + dto.setCandidateTaskStatusText(CandidateTaskStatusEnum.Scoring.getValue()); + } } if (TaskUserStatusEnum.Pass.getValue().equals(taskUserEntity.getStatus())) { dto.setExamResult(CandidateExamResultEnum.Pass.name()); @@ -158,6 +195,10 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask dto.setExamResult(CandidateExamResultEnum.Fail.name()); dto.setExamResultText(CandidateExamResultEnum.Fail.getValue()); } + if (TaskUserStatusEnum.Scoring.getValue().equals(taskUserEntity.getStatus())) { + dto.setExamResult("Scoring"); + dto.setExamResultText("评分中"); + } dto.setTaskUserStatus(TaskUserStatusEnum.getNameByValue(taskUserEntity.getStatus())); dto.setLastRecordId(taskUserEntity.getLastRecordId()); if (Objects.nonNull(taskUserEntity.getLastRecordId())) { @@ -168,6 +209,23 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask dto.setLastRecordScoreStr(lastRecordScoreStr); } dto.setTotalScore((int) Math.floor(entity.getTotalScore())); + + // 题目类型 + dto.setExamModeTag(Try.of(() -> EnumUtil.getBy(ExamModeEnum::getValue, dto.getExamMode())) + .getOrElse(ExamModeEnum.GENERATIVE) + .getDesc()); + if (ExamModeEnum.GENERATIVE.getValue().equals(dto.getExamMode())) { + // 生成式 + ProductLineEntity byId = productLineBaseService.getById(dto.getSubLineId()); + dto.setClassificationTag(String.format("%s - %s" , byId.getParentName() , byId.getName())); + + } else { + // 经典 + ClassicPaperSetEntity byId = classicPaperSetBaseService.getById(dto.getClassicPaperSetId()); + ClassicCategoryEntity classicCategoryBaseServiceById = classicCategoryBaseService.getById(byId.getClassicCategoryId()); + ClassicCategoryEntity classicCategoryBaseServiceByParentId = classicCategoryBaseService.getById(classicCategoryBaseServiceById.getParentId()); + dto.setClassificationTag(String.format("%s - %s" , classicCategoryBaseServiceByParentId.getName() , byId.getName())); + } return dto; } } diff --git a/src/main/java/com/project/task/domain/service/impl/SearchTaskDomainServiceImpl.java b/src/main/java/com/project/task/domain/service/impl/SearchTaskDomainServiceImpl.java index 0533c00..d1bc44f 100644 --- a/src/main/java/com/project/task/domain/service/impl/SearchTaskDomainServiceImpl.java +++ b/src/main/java/com/project/task/domain/service/impl/SearchTaskDomainServiceImpl.java @@ -2,6 +2,7 @@ package com.project.task.domain.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -98,6 +99,11 @@ public class SearchTaskDomainServiceImpl implements SearchTaskDomainService { queryWrapper.le(TaskEntity::getStartTime, endOfToday); queryWrapper.ge(TaskEntity::getEndTime, beginOfToday); } + if (BooleanUtil.isTrue(param.getExcludeEnd())) { + // 强制要求:结束时间必须 >= 今天 00:00:00 + // 这样,截止时间是今天的任务会被【保留】,只有昨天及以前截止的任务会被【排除】 + queryWrapper.ge(TaskEntity::getEndTime, beginOfToday); + } queryWrapper.orderByDesc(TaskEntity::getId); IPage taskEntityPage = taskMapper.selectPage( PageConverter.toMpPage(param), @@ -137,6 +143,7 @@ public class SearchTaskDomainServiceImpl implements SearchTaskDomainService { case 0 -> notParticipatedNum.getAndIncrement(); case 1 -> notPassedNum.getAndIncrement(); case 2 -> passedNum.getAndIncrement(); + case 3 -> notPassedNum.getAndIncrement(); } return userDTO; }).toList(); diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 782764e..444690f 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -114,4 +114,4 @@ scoring: # 每批最多处理多少道简答题 batch-size: 5 # 执行间隔(毫秒),每60秒扫描一次 - interval-ms: 60000 \ No newline at end of file + interval-ms: 600000 \ No newline at end of file