From 18c5a437204728a7bdae743eaec83736284f527c Mon Sep 17 00:00:00 2001 From: luoweijian <1329394916@qq.com> Date: Tue, 3 Feb 2026 16:44:23 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AD=94=E9=A2=98=E7=9B=B8=E5=85=B3=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExamRecordApplicationService.java | 4 + .../ExamRecordApplicationServiceImpl.java | 20 +++ .../exam/controller/ExamRecordController.java | 12 ++ .../exam/domain/dto/ExamRecordDTO.java | 3 + .../service/SubmitPaperDomainService.java | 7 + .../service/SummitPaperDomainService.java | 4 - .../BuildExamRecordDomainServiceImpl.java | 7 + .../StepSaveExamRecordDomainServiceImpl.java | 2 +- .../impl/SubmitPaperDomainServiceImpl.java | 127 ++++++++++++++++++ 9 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/project/exam/domain/service/SubmitPaperDomainService.java delete mode 100644 src/main/java/com/project/exam/domain/service/SummitPaperDomainService.java create mode 100644 src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java diff --git a/src/main/java/com/project/exam/application/ExamRecordApplicationService.java b/src/main/java/com/project/exam/application/ExamRecordApplicationService.java index 3f1ec35..e0b439d 100644 --- a/src/main/java/com/project/exam/application/ExamRecordApplicationService.java +++ b/src/main/java/com/project/exam/application/ExamRecordApplicationService.java @@ -6,4 +6,8 @@ import com.project.exam.domain.dto.ExamRecordDTO; public interface ExamRecordApplicationService { Result assemblePaper(Long taskId) throws Exception; + + Result submitPaper(Long recordId) throws Exception; + + Result stepSave(Long recordId, int index, String answer) throws Exception; } diff --git a/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java b/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java index 63e77c5..aeffbc1 100644 --- a/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java +++ b/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java @@ -5,6 +5,8 @@ import com.project.ding.utils.SecurityUtils; import com.project.exam.application.ExamRecordApplicationService; import com.project.exam.domain.dto.ExamRecordDTO; import com.project.exam.domain.service.AssemblePaperDomainService; +import com.project.exam.domain.service.StepSaveExamRecordDomainService; +import com.project.exam.domain.service.SubmitPaperDomainService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -12,8 +14,26 @@ import org.springframework.stereotype.Service; public class ExamRecordApplicationServiceImpl implements ExamRecordApplicationService { @Autowired private AssemblePaperDomainService assemblePaperDomainService; + + @Autowired + private StepSaveExamRecordDomainService stepSaveExamRecordDomainService; + + @Autowired + private SubmitPaperDomainService submitPaperDomainService; + @Override public Result assemblePaper(Long taskId) throws Exception { return Result.success(assemblePaperDomainService.assemblePaper(taskId , SecurityUtils.getUserId())); } + + @Override + public Result submitPaper(Long recordId) throws Exception { + return Result.success(submitPaperDomainService.submitPaper(recordId)); + } + + @Override + public Result stepSave(Long recordId, int index, String answer) throws Exception { + stepSaveExamRecordDomainService.stepSave(recordId , index , answer); + return Result.success("提交成功"); + } } diff --git a/src/main/java/com/project/exam/controller/ExamRecordController.java b/src/main/java/com/project/exam/controller/ExamRecordController.java index 84b1f02..6cf48a9 100644 --- a/src/main/java/com/project/exam/controller/ExamRecordController.java +++ b/src/main/java/com/project/exam/controller/ExamRecordController.java @@ -18,4 +18,16 @@ public class ExamRecordController { public Result assemblePaper(Long taskId) throws Exception{ return examRecordApplicationService.assemblePaper(taskId); } + + @PostMapping("/submitPaper") + public Result submitPaper(Long recordId) throws Exception{ + return examRecordApplicationService.submitPaper(recordId); + } + + @PostMapping("/stepSave") + public Result stepSave(Long recordId, int index, String answer) throws Exception{ + return examRecordApplicationService.stepSave(recordId , index , answer); + } + + } diff --git a/src/main/java/com/project/exam/domain/dto/ExamRecordDTO.java b/src/main/java/com/project/exam/domain/dto/ExamRecordDTO.java index 5e1ecc5..9d2c5cb 100644 --- a/src/main/java/com/project/exam/domain/dto/ExamRecordDTO.java +++ b/src/main/java/com/project/exam/domain/dto/ExamRecordDTO.java @@ -43,7 +43,10 @@ public class ExamRecordDTO extends BaseDTO { private String analysis; // 该题是否已发起申诉 private Boolean hasAppealed; + // 该题分值 private Double score; + // 用户得分 + private Double userScore; } @Override diff --git a/src/main/java/com/project/exam/domain/service/SubmitPaperDomainService.java b/src/main/java/com/project/exam/domain/service/SubmitPaperDomainService.java new file mode 100644 index 0000000..70a9f3a --- /dev/null +++ b/src/main/java/com/project/exam/domain/service/SubmitPaperDomainService.java @@ -0,0 +1,7 @@ +package com.project.exam.domain.service; + +import com.project.exam.domain.dto.ExamRecordDTO; + +public interface SubmitPaperDomainService { + ExamRecordDTO submitPaper(Long recordId) throws Exception; +} diff --git a/src/main/java/com/project/exam/domain/service/SummitPaperDomainService.java b/src/main/java/com/project/exam/domain/service/SummitPaperDomainService.java deleted file mode 100644 index fb4bc40..0000000 --- a/src/main/java/com/project/exam/domain/service/SummitPaperDomainService.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.project.exam.domain.service; - -public interface SummitPaperDomainService { -} diff --git a/src/main/java/com/project/exam/domain/service/impl/BuildExamRecordDomainServiceImpl.java b/src/main/java/com/project/exam/domain/service/impl/BuildExamRecordDomainServiceImpl.java index 302f1e4..2d95760 100644 --- a/src/main/java/com/project/exam/domain/service/impl/BuildExamRecordDomainServiceImpl.java +++ b/src/main/java/com/project/exam/domain/service/impl/BuildExamRecordDomainServiceImpl.java @@ -1,5 +1,6 @@ package com.project.exam.domain.service.impl; +import cn.hutool.core.util.BooleanUtil; import com.project.exam.domain.dto.ExamRecordDTO; import com.project.exam.domain.service.BuildExamRecordDomainService; import com.project.task.domain.enums.QuestionTypeEnum; @@ -21,10 +22,16 @@ public class BuildExamRecordDomainServiceImpl implements BuildExamRecordDomainSe dto.getAnswerSnapshotDTOList().stream().map(answerSnapshotDTO -> { if (QuestionTypeEnum.SINGLE_CHOICE.getValue().equals(answerSnapshotDTO.getType())) { answerSnapshotDTO.setScore(dto.getTaskDTO().getSingleChoiceScore()); + answerSnapshotDTO.setUserScore(BooleanUtil.isTrue(answerSnapshotDTO.getIsRight()) ? + dto.getTaskDTO().getSingleChoiceScore() : 0); } else if (QuestionTypeEnum.TRUE_FALSE.getValue().equals(answerSnapshotDTO.getType())) { answerSnapshotDTO.setScore(dto.getTaskDTO().getTrueFalseScore()); + answerSnapshotDTO.setUserScore(BooleanUtil.isTrue(answerSnapshotDTO.getIsRight()) ? + dto.getTaskDTO().getTrueFalseScore() : 0); } else { answerSnapshotDTO.setScore(dto.getTaskDTO().getMultipleChoiceScore()); + answerSnapshotDTO.setUserScore(BooleanUtil.isTrue(answerSnapshotDTO.getIsRight()) ? + dto.getTaskDTO().getMultipleChoiceScore() : 0); } return answerSnapshotDTO; }); diff --git a/src/main/java/com/project/exam/domain/service/impl/StepSaveExamRecordDomainServiceImpl.java b/src/main/java/com/project/exam/domain/service/impl/StepSaveExamRecordDomainServiceImpl.java index 3394c40..4582737 100644 --- a/src/main/java/com/project/exam/domain/service/impl/StepSaveExamRecordDomainServiceImpl.java +++ b/src/main/java/com/project/exam/domain/service/impl/StepSaveExamRecordDomainServiceImpl.java @@ -17,7 +17,7 @@ public class StepSaveExamRecordDomainServiceImpl implements StepSaveExamRecordDo if (index < 1) { throw new BusinessErrorException("非法题号"); } - if (StrUtil.isNotBlank(answer)) { + if (StrUtil.isBlank(answer)) { throw new BusinessErrorException("答案不能为空"); } int rows = examRecordMapper.updateAnswerIncremental(recordId, index - 1, answer); 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 new file mode 100644 index 0000000..50e9770 --- /dev/null +++ b/src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java @@ -0,0 +1,127 @@ +package com.project.exam.domain.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.project.base.domain.exception.BusinessErrorException; +import com.project.exam.domain.dto.ExamRecordDTO; +import com.project.exam.domain.entity.ExamRecordEntity; +import com.project.exam.domain.service.BuildExamRecordDomainService; +import com.project.exam.domain.service.SubmitPaperDomainService; +import com.project.exam.mapper.ExamRecordMapper; +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.QuestionTypeEnum; +import com.project.task.domain.enums.TaskUserStatusEnum; +import com.project.task.domain.service.TaskBaseService; +import com.project.task.mapper.TaskMapper; +import com.project.task.mapper.TaskUserMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService { + @Autowired + private ExamRecordMapper examRecordMapper; + @Autowired + private TaskUserMapper taskUserMapper; + @Autowired + private TaskMapper taskMapper; + @Autowired + private TaskBaseService taskBaseService; + + @Autowired + private BuildExamRecordDomainService buildExamRecordDomainService; + + @Override + @Transactional(rollbackFor = Exception.class) + public ExamRecordDTO submitPaper(Long recordId) throws Exception { + ExamRecordEntity record = examRecordMapper.selectById(recordId); + if (record == null || record.getSubmitTime() != null) { + throw new BusinessErrorException("试卷已提交或记录不存在"); + } + // 获取任务配置与用户进度记录 + TaskUserEntity taskUser = taskUserMapper.selectById(record.getTaskUserId()); + TaskEntity task = taskMapper.selectById(taskUser.getTaskId()); + + // 判分 + double finalScore = 0.0; + List snapshotList = record.getAnswerSnapshot(); + for (ExamRecordEntity.QuestionSnapshot snapshot : snapshotList) { + // 比对答案 + if (StrUtil.equals(formatAnswer(snapshot.getUserAnswer()) , formatAnswer(snapshot.getRightAnswer()))) { + snapshot.setIsRight(true); + // 根据题型获取任务创建时算好的单题分值 + finalScore += getQuestionWeight(snapshot.getType(), task); + } else { + snapshot.setIsRight(false); + } + } + // 四舍五入处理 + finalScore = new BigDecimal(finalScore).setScale(2, RoundingMode.HALF_UP).doubleValue(); + boolean isPassed = finalScore >= task.getPassScore(); + record.setScore(finalScore); + record.setPass(isPassed); + record.setSubmitTime(new Date()); + record.setAnswerSnapshot(snapshotList); + examRecordMapper.updateById(record); + + // 更新 TaskEntity 冗余字段 + if (isPassed) { + taskBaseService.update() + .setSql("pass_num = pass_num + 1") + .eq("id", task.getId()) + .update(); + } + + // 更新 TaskUser 冗余字段 + updateTaskUserStatus(taskUser , isPassed , recordId); + // 构建dto + ExamRecordDTO dto = record.toDTO(ExamRecordDTO::new); + dto.setTaskDTO(task.toDTO(TaskDTO::new)); + return buildExamRecordDomainService.buildDTO(dto); + } + + /** + * 根据题型映射任务表中的动态分值 + */ + private double getQuestionWeight(Integer type, TaskEntity task) { + if (QuestionTypeEnum.SINGLE_CHOICE.getValue().equals(type)) { + return task.getSingleChoiceScore(); + } else if (QuestionTypeEnum.MULTIPLE_CHOICE.getValue().equals(type)) { + return task.getMultipleChoiceScore(); + } + else if (QuestionTypeEnum.TRUE_FALSE.getValue().equals(type)) { + return task.getTrueFalseScore(); + } + return 0.0; + } + + private void updateTaskUserStatus(TaskUserEntity taskUser, boolean isPassed, Long recordId) { + taskUser.setLastRecordId(recordId); + taskUser.setAttemptNum((taskUser.getAttemptNum() == null ? 0 : taskUser.getAttemptNum()) + 1); + + if (!TaskUserStatusEnum.Pass.getValue().equals(taskUser.getStatus())) { + taskUser.setStatus(isPassed ? TaskUserStatusEnum.Pass.getValue() : TaskUserStatusEnum.Fail.getValue()); + } + + taskUserMapper.updateById(taskUser); + } + + private String formatAnswer(String ans) { + if (StrUtil.isBlank(ans)) return ""; + // 去空格 -> 转大写 -> 排序(解决 B,A vs A,B 的问题) + return Arrays.stream(ans.split(",")) + .map(String::trim) + .map(String::toUpperCase) + .sorted() + .collect(Collectors.joining(",")); + } +}