Browse Source

考生端修改

master
luoweijian 2 months ago
parent
commit
e651d2208e
  1. 2
      src/main/java/com/project/ding/domain/service/impl/SearchUserDomainServiceImpl.java
  2. 38
      src/main/java/com/project/exam/domain/job/ShortAnswerScoringJob.java
  3. 17
      src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java
  4. 2
      src/main/java/com/project/exam/domain/service/strategy/ClassicPaperStrategy.java
  5. 3
      src/main/java/com/project/task/domain/enums/CandidateTaskStatusEnum.java
  6. 3
      src/main/java/com/project/task/domain/enums/TaskUserStatusEnum.java
  7. 2
      src/main/java/com/project/task/domain/param/TaskParam.java
  8. 60
      src/main/java/com/project/task/domain/service/impl/CandidateSearchTaskDomainServiceImpl.java
  9. 7
      src/main/java/com/project/task/domain/service/impl/SearchTaskDomainServiceImpl.java
  10. 2
      src/main/resources/application-test.yml

2
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()); .in(TaskUserEntity::getStatus, List.of(TaskUserStatusEnum.Not_Start.getValue(), TaskUserStatusEnum.Fail.getValue(), TaskUserStatusEnum.Pass.getValue())))).intValue());
dto.setParticipatedExamTaskCount(Long.valueOf(taskUserBaseService.count(new LambdaQueryWrapper<TaskUserEntity>() dto.setParticipatedExamTaskCount(Long.valueOf(taskUserBaseService.count(new LambdaQueryWrapper<TaskUserEntity>()
.eq(TaskUserEntity::getUserId, dto.getId()) .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<TaskUserEntity>() dto.setPassedExamTaskCount(Long.valueOf(taskUserBaseService.count(new LambdaQueryWrapper<TaskUserEntity>()
.eq(TaskUserEntity::getUserId, dto.getId()) .eq(TaskUserEntity::getUserId, dto.getId())
.in(TaskUserEntity::getStatus, List.of(TaskUserStatusEnum.Pass.getValue())))).intValue()); .in(TaskUserEntity::getStatus, List.of(TaskUserStatusEnum.Pass.getValue())))).intValue());

38
src/main/java/com/project/exam/domain/job/ShortAnswerScoringJob.java

@ -311,30 +311,44 @@ public class ShortAnswerScoringJob {
log.warn(">>> [简答题评分任务] 查询及格线失败,使用默认60分", e); log.warn(">>> [简答题评分任务] 查询及格线失败,使用默认60分", e);
} }
boolean oldPass = record.getPass() != null && record.getPass();
boolean newPass = total >= passScore; boolean newPass = total >= passScore;
record.setPass(newPass); record.setPass(newPass);
// 如果及格状态变了,更新相关冗余字段 // 更新 TaskUser 状态
if (oldPass != newPass && task != null && tu != null) { if (tu != null) {
try { 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) { if (newPass) {
// 不及格→及格:增加 pass_num,更新用户状态
taskBaseService.update() taskBaseService.update()
.setSql("pass_num = pass_num + 1") .setSql("pass_num = pass_num + 1")
.eq("id", task.getId()) .eq("id", task.getId())
.update(); .update();
}
if (!TaskUserStatusEnum.Pass.getValue().equals(tu.getStatus())) { }
// 规则3:旧数据兼容(之前 Fail,评分完才及格 → 升级 Pass)
else if (TaskUserStatusEnum.Fail.getValue().equals(currentStatus) && newPass) {
tu.setStatus(TaskUserStatusEnum.Pass.getValue()); tu.setStatus(TaskUserStatusEnum.Pass.getValue());
tu.setLastRecordId(record.getId()); tu.setLastRecordId(record.getId());
taskUserMapper.updateById(tu); taskUserMapper.updateById(tu);
taskBaseService.update()
.setSql("pass_num = pass_num + 1")
.eq("id", task.getId())
.update();
} }
} // 规则4:其他情况(Fail 且未及格、Not_Start等)→ 不做变更
// 及格→不及格:理论上不会发生(简答题只加分不减分),暂不处理
} catch (Exception e) {
log.error(">>> [简答题评分任务] 更新及格状态失败, recordId={}", record.getId(), e);
}
} }
} }
} }

17
src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java

@ -138,7 +138,7 @@ public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService {
} }
// 更新 TaskUser 冗余字段 // 更新 TaskUser 冗余字段
updateTaskUserStatus(taskUser , isPassed , recordId); updateTaskUserStatus(taskUser , isPassed , recordId , hasShortAnswer);
// 异步收集易错统计 // 异步收集易错统计
statisticsApplicationService.collectErrorProneStatistics( statisticsApplicationService.collectErrorProneStatistics(
@ -170,12 +170,23 @@ public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService {
return 0.0; 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); taskUser.setAttemptNum((taskUser.getAttemptNum() == null ? 0 : taskUser.getAttemptNum()) + 1);
if (!TaskUserStatusEnum.Pass.getValue().equals(taskUser.getStatus())) { // 如果已经通过,不降级
if (TaskUserStatusEnum.Pass.getValue().equals(taskUser.getStatus())) {
taskUserMapper.updateById(taskUser);
return;
}
taskUser.setLastRecordId(recordId); taskUser.setLastRecordId(recordId);
if (hasShortAnswer) {
// 有简答题 → 先设为评分中,等异步评分完成再定最终结果
taskUser.setStatus(TaskUserStatusEnum.Scoring.getValue());
} else {
// 纯客观题 → 直接出最终结果
taskUser.setStatus(isPassed ? TaskUserStatusEnum.Pass.getValue() : TaskUserStatusEnum.Fail.getValue()); taskUser.setStatus(isPassed ? TaskUserStatusEnum.Pass.getValue() : TaskUserStatusEnum.Fail.getValue());
} }

2
src/main/java/com/project/exam/domain/service/strategy/ClassicPaperStrategy.java

@ -97,7 +97,7 @@ public class ClassicPaperStrategy implements PaperAssemblyStrategy {
// 6. 过滤出该版本的快照,按 sortOrder 排序 // 6. 过滤出该版本的快照,按 sortOrder 排序
List<TaskPaperSnapshotEntity> selectedSnapshots = allSnapshots.stream() List<TaskPaperSnapshotEntity> selectedSnapshots = allSnapshots.stream()
.filter(s -> selectedSetIndex.equals(s.getSetIndex())) .filter(s -> selectedSetIndex.equals(s.getSetIndex()))
.sorted(Comparator.comparing(TaskPaperSnapshotEntity::getSortOrder)) .sorted(Comparator.comparing(TaskPaperSnapshotEntity::getQuestionType))
.collect(Collectors.toList()); .collect(Collectors.toList());
// 7. 构建 QuestionSnapshot 列表 // 7. 构建 QuestionSnapshot 列表

3
src/main/java/com/project/task/domain/enums/CandidateTaskStatusEnum.java

@ -11,7 +11,8 @@ public enum CandidateTaskStatusEnum implements HasValueEnum<String> {
Pending_Exam("待开考") , Pending_Exam("待开考") ,
Not_Exam("未考试") , Not_Exam("未考试") ,
Has_Exam("已考试") , Has_Exam("已考试") ,
End("已截止") End("已截止") ,
Scoring("评分中")
; ;
private final String value; private final String value;
} }

3
src/main/java/com/project/task/domain/enums/TaskUserStatusEnum.java

@ -9,7 +9,8 @@ import lombok.RequiredArgsConstructor;
public enum TaskUserStatusEnum implements HasValueEnum<Integer> { public enum TaskUserStatusEnum implements HasValueEnum<Integer> {
Not_Start(0 , "未参加") , Not_Start(0 , "未参加") ,
Fail(1 , "未通过") , Fail(1 , "未通过") ,
Pass(2 ,"已通过"); Pass(2 ,"已通过") ,
Scoring(3 , "评分中");
private final Integer value; private final Integer value;

2
src/main/java/com/project/task/domain/param/TaskParam.java

@ -23,4 +23,6 @@ public class TaskParam extends BaseParam {
private String taskStatus; private String taskStatus;
private Boolean excludeEnd = Boolean.TRUE;
} }

60
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.collection.CollUtil;
import cn.hutool.core.date.DateUtil; 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.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.PageResult;
import com.project.base.domain.result.Result; import com.project.base.domain.result.Result;
import com.project.base.domain.utils.PageConverter; 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.ding.utils.SecurityUtils;
import com.project.exam.domain.entity.ExamRecordEntity; import com.project.exam.domain.entity.ExamRecordEntity;
import com.project.exam.mapper.ExamRecordMapper; 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.dto.TaskDTO;
import com.project.task.domain.entity.TaskEntity; import com.project.task.domain.entity.TaskEntity;
import com.project.task.domain.entity.TaskUserEntity; import com.project.task.domain.entity.TaskUserEntity;
import com.project.task.domain.enums.CandidateExamResultEnum; import com.project.task.domain.enums.CandidateExamResultEnum;
import com.project.task.domain.enums.CandidateTaskStatusEnum; 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.enums.TaskUserStatusEnum;
import com.project.task.domain.param.TaskParam; import com.project.task.domain.param.TaskParam;
import com.project.task.domain.service.CandidateSearchTaskDomainService; import com.project.task.domain.service.CandidateSearchTaskDomainService;
import com.project.task.mapper.TaskMapper; import com.project.task.mapper.TaskMapper;
import com.project.task.mapper.TaskUserMapper; import com.project.task.mapper.TaskUserMapper;
import io.vavr.control.Try;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -44,6 +54,15 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask
@Autowired @Autowired
private ExamRecordMapper examRecordMapper; private ExamRecordMapper examRecordMapper;
@Autowired
private ProductLineBaseService productLineBaseService;
@Autowired
private ClassicPaperSetBaseService classicPaperSetBaseService;
@Autowired
private ClassicCategoryBaseService classicCategoryBaseService;
@Override @Override
public Result<PageResult<TaskDTO>> candidateSearch(TaskParam param) throws Exception { public Result<PageResult<TaskDTO>> candidateSearch(TaskParam param) throws Exception {
@ -74,6 +93,11 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask
if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.End.name())) { if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.End.name())) {
queryWrapper.lt(TaskEntity::getEndTime , beginOfToday); 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())) { if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.Not_Exam.name())) {
queryWrapper.le(TaskEntity::getStartTime, endOfToday); queryWrapper.le(TaskEntity::getStartTime, endOfToday);
@ -81,7 +105,7 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask
queryWrapper.inSql(TaskEntity::getId, 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())); 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())) { if (StrUtil.equals(param.getCandidateTaskStatus() , CandidateTaskStatusEnum.Has_Exam.name())) {
queryWrapper.le(TaskEntity::getStartTime, endOfToday); queryWrapper.le(TaskEntity::getStartTime, endOfToday);
queryWrapper.ge(TaskEntity::getEndTime, beginOfToday); 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()) 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())) { if (Objects.nonNull(param.getSortType())) {
switch (param.getSortType()) { switch (param.getSortType()) {
@ -149,6 +181,11 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask
dto.setCandidateTaskStatus(CandidateTaskStatusEnum.Has_Exam.name()); dto.setCandidateTaskStatus(CandidateTaskStatusEnum.Has_Exam.name());
dto.setCandidateTaskStatusText(CandidateTaskStatusEnum.Has_Exam.getValue()); 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())) { if (TaskUserStatusEnum.Pass.getValue().equals(taskUserEntity.getStatus())) {
dto.setExamResult(CandidateExamResultEnum.Pass.name()); dto.setExamResult(CandidateExamResultEnum.Pass.name());
@ -158,6 +195,10 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask
dto.setExamResult(CandidateExamResultEnum.Fail.name()); dto.setExamResult(CandidateExamResultEnum.Fail.name());
dto.setExamResultText(CandidateExamResultEnum.Fail.getValue()); dto.setExamResultText(CandidateExamResultEnum.Fail.getValue());
} }
if (TaskUserStatusEnum.Scoring.getValue().equals(taskUserEntity.getStatus())) {
dto.setExamResult("Scoring");
dto.setExamResultText("评分中");
}
dto.setTaskUserStatus(TaskUserStatusEnum.getNameByValue(taskUserEntity.getStatus())); dto.setTaskUserStatus(TaskUserStatusEnum.getNameByValue(taskUserEntity.getStatus()));
dto.setLastRecordId(taskUserEntity.getLastRecordId()); dto.setLastRecordId(taskUserEntity.getLastRecordId());
if (Objects.nonNull(taskUserEntity.getLastRecordId())) { if (Objects.nonNull(taskUserEntity.getLastRecordId())) {
@ -168,6 +209,23 @@ public class CandidateSearchTaskDomainServiceImpl implements CandidateSearchTask
dto.setLastRecordScoreStr(lastRecordScoreStr); dto.setLastRecordScoreStr(lastRecordScoreStr);
} }
dto.setTotalScore((int) Math.floor(entity.getTotalScore())); 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; return dto;
} }
} }

7
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.collection.CollUtil;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -98,6 +99,11 @@ public class SearchTaskDomainServiceImpl implements SearchTaskDomainService {
queryWrapper.le(TaskEntity::getStartTime, endOfToday); queryWrapper.le(TaskEntity::getStartTime, endOfToday);
queryWrapper.ge(TaskEntity::getEndTime, beginOfToday); queryWrapper.ge(TaskEntity::getEndTime, beginOfToday);
} }
if (BooleanUtil.isTrue(param.getExcludeEnd())) {
// 强制要求:结束时间必须 >= 今天 00:00:00
// 这样,截止时间是今天的任务会被【保留】,只有昨天及以前截止的任务会被【排除】
queryWrapper.ge(TaskEntity::getEndTime, beginOfToday);
}
queryWrapper.orderByDesc(TaskEntity::getId); queryWrapper.orderByDesc(TaskEntity::getId);
IPage<TaskEntity> taskEntityPage = taskMapper.selectPage( IPage<TaskEntity> taskEntityPage = taskMapper.selectPage(
PageConverter.toMpPage(param), PageConverter.toMpPage(param),
@ -137,6 +143,7 @@ public class SearchTaskDomainServiceImpl implements SearchTaskDomainService {
case 0 -> notParticipatedNum.getAndIncrement(); case 0 -> notParticipatedNum.getAndIncrement();
case 1 -> notPassedNum.getAndIncrement(); case 1 -> notPassedNum.getAndIncrement();
case 2 -> passedNum.getAndIncrement(); case 2 -> passedNum.getAndIncrement();
case 3 -> notPassedNum.getAndIncrement();
} }
return userDTO; return userDTO;
}).toList(); }).toList();

2
src/main/resources/application-test.yml

@ -114,4 +114,4 @@ scoring:
# 每批最多处理多少道简答题 # 每批最多处理多少道简答题
batch-size: 5 batch-size: 5
# 执行间隔(毫秒),每60秒扫描一次 # 执行间隔(毫秒),每60秒扫描一次
interval-ms: 60000 interval-ms: 600000
Loading…
Cancel
Save