Browse Source

简单题评分完后再统计

master
luogw 2 months ago
parent
commit
b5ef00ab9f
  1. 34
      src/main/java/com/project/exam/domain/job/ShortAnswerScoringJob.java
  2. 2
      src/main/java/com/project/exam/domain/service/impl/SubmitPaperDomainServiceImpl.java
  3. 5
      src/main/java/com/project/statistics/application/StatisticsApplicationService.java
  4. 20
      src/main/java/com/project/statistics/application/impl/StatisticsApplicationServiceImpl.java
  5. 16
      src/main/java/com/project/statistics/domain/service/SaveErrorProneStatisticsDomainService.java
  6. 61
      src/main/java/com/project/statistics/domain/service/impl/SaveSaveErrorProneStatisticsDomainServiceImpl.java

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

@ -9,10 +9,12 @@ import com.project.question.domain.entity.QuestionEntity;
import com.project.information.domain.entity.KnowledgePointEntity;
import com.project.information.domain.service.KnowledgePointBaseService;
import com.project.question.domain.service.QuestionBaseService;
import com.project.task.domain.dto.TaskDTO;
import com.project.task.domain.entity.TaskEntity;
import com.project.task.domain.enums.QuestionTypeEnum;
import com.project.task.domain.enums.TaskUserStatusEnum;
import com.project.exam.domain.enums.ExamRecordScoringStatusEnum;
import com.project.statistics.application.StatisticsApplicationService;
import com.project.task.mapper.TaskMapper;
import com.project.task.mapper.TaskUserMapper;
import com.project.task.domain.service.TaskBaseService;
@ -56,6 +58,9 @@ public class ShortAnswerScoringJob {
@Autowired
private TaskBaseService taskBaseService;
@Autowired
private StatisticsApplicationService statisticsApplicationService;
/** 每批最多处理多少道简答题 */
@Value("${scoring.batch-size:5}")
private int batchSize;
@ -126,6 +131,9 @@ public class ShortAnswerScoringJob {
recalcTotalScore(record);
examRecordMapper.updateById(record);
log.info(">>> [简答题评分任务] recordId={} 快照已更新, 总分={}", record.getId(), record.getScore());
// 简答题评分完成后,单独统计易错数据
collectShortAnswerErrorProneStatistics(record, snapshots);
}
}
@ -207,7 +215,7 @@ public class ShortAnswerScoringJob {
hitPoints.add(i);
}
if (ps.getReason() != null && !ps.getReason().isEmpty()) {
if (comment.length() > 0) {
if (!comment.isEmpty()) {
comment.append("; ");
}
comment.append(ps.getReason());
@ -233,6 +241,30 @@ public class ShortAnswerScoringJob {
}
}
/**
* 统计简答题
*/
private void collectShortAnswerErrorProneStatistics(ExamRecordEntity record, List<ExamRecordEntity.QuestionSnapshot> snapshots) {
try {
Long taskUserId = record.getTaskUserId();
if (taskUserId == null) {
return;
}
com.project.task.domain.entity.TaskUserEntity tu = taskUserMapper.selectById(taskUserId);
if (tu == null || tu.getTaskId() == null) {
return;
}
TaskEntity task = taskMapper.selectById(tu.getTaskId());
if (task == null) {
return;
}
statisticsApplicationService.collectShortAnswerErrorProneStatistics(task.toDTO(TaskDTO::new), snapshots);
} catch (Exception e) {
log.error(">>> [简答题评分任务] 简答题易错统计失败, recordId={}", record.getId(), e);
}
}
private void recalcTotalScore(ExamRecordEntity record) {
List<ExamRecordEntity.QuestionSnapshot> snapshots = record.getAnswerSnapshot();
if (snapshots == null || snapshots.isEmpty()) {

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

@ -142,7 +142,7 @@ public class SubmitPaperDomainServiceImpl implements SubmitPaperDomainService {
// 异步收集易错统计
statisticsApplicationService.collectErrorProneStatistics(
task.getId(), task.getExamMode(), record.getAnswerSnapshot());
task.toDTO(TaskDTO::new), record.getAnswerSnapshot());
// 构建dto
ExamRecordDTO dto = record.toDTO(ExamRecordDTO::new);

5
src/main/java/com/project/statistics/application/StatisticsApplicationService.java

@ -4,6 +4,7 @@ import com.project.base.domain.result.Result;
import com.project.exam.domain.entity.ExamRecordEntity;
import com.project.statistics.domain.dto.ErrorProneStatisticsDTO;
import com.project.statistics.domain.dto.ExamTaskStatisticsDTO;
import com.project.task.domain.dto.TaskDTO;
import java.util.List;
@ -11,7 +12,9 @@ public interface StatisticsApplicationService {
Result<ExamTaskStatisticsDTO> examTaskStatistics(Long taskId);
void collectErrorProneStatistics(Long id, Integer examMode, List<ExamRecordEntity.QuestionSnapshot> answerSnapshot);
void collectErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> answerSnapshot);
void collectShortAnswerErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> answerSnapshot);
Result<List<ErrorProneStatisticsDTO>> searchErrorProneTop(Long taskId);

20
src/main/java/com/project/statistics/application/impl/StatisticsApplicationServiceImpl.java

@ -1,5 +1,6 @@
package com.project.statistics.application.impl;
import cn.hutool.cron.task.Task;
import com.project.base.domain.result.Result;
import com.project.exam.domain.dto.ExamRecordDTO;
import com.project.exam.domain.entity.ExamRecordEntity;
@ -10,6 +11,8 @@ import com.project.statistics.domain.dto.ExamTaskStatisticsDTO;
import com.project.statistics.domain.service.SaveErrorProneStatisticsDomainService;
import com.project.statistics.domain.service.ExamTaskStatisticsDomainService;
import com.project.statistics.domain.service.SearchErrorProneStatisticsDomainService;
import com.project.task.domain.dto.TaskDTO;
import com.project.task.domain.service.TaskBaseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -26,6 +29,8 @@ public class StatisticsApplicationServiceImpl implements StatisticsApplicationSe
private SearchErrorProneStatisticsDomainService searchErrorProneStatisticsDomainService;
@Autowired
private GetDetailExamRecordDomainService getDetailExamRecordDomainService;
@Autowired
private TaskBaseService taskBaseService;
/**
* 统计考试任务数据
@ -39,8 +44,13 @@ public class StatisticsApplicationServiceImpl implements StatisticsApplicationSe
* 统计高频易错数据
*/
@Override
public void collectErrorProneStatistics(Long id, Integer examMode, List<ExamRecordEntity.QuestionSnapshot> answerSnapshot) {
saveErrorProneStatisticsDomainService.collectErrorProneStatistics(id, examMode, answerSnapshot);
public void collectErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> answerSnapshot) {
saveErrorProneStatisticsDomainService.collectErrorProneStatistics(taskDTO, answerSnapshot);
}
@Override
public void collectShortAnswerErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> answerSnapshot) {
saveErrorProneStatisticsDomainService.collectShortAnswerErrorProneStatistics(taskDTO, answerSnapshot);
}
@Override
@ -52,6 +62,10 @@ public class StatisticsApplicationServiceImpl implements StatisticsApplicationSe
public void singleExamStatistics(Long examRecordId) throws Exception {
ExamRecordDTO detail = getDetailExamRecordDomainService.getDetail(examRecordId).getData();
ExamRecordEntity entity = detail.toEntity(ExamRecordEntity::new);
saveErrorProneStatisticsDomainService.collectErrorProneStatistics(detail.getTaskId(), 1, entity.getAnswerSnapshot());
TaskDTO taskDTO = taskBaseService.getById(detail.getTaskId()).toDTO(TaskDTO::new);
//统计客观题
saveErrorProneStatisticsDomainService.collectErrorProneStatistics(taskDTO, entity.getAnswerSnapshot());
//统计简答题
saveErrorProneStatisticsDomainService.collectShortAnswerErrorProneStatistics(taskDTO, entity.getAnswerSnapshot());
}
}

16
src/main/java/com/project/statistics/domain/service/SaveErrorProneStatisticsDomainService.java

@ -1,17 +1,25 @@
package com.project.statistics.domain.service;
import com.project.exam.domain.entity.ExamRecordEntity;
import com.project.task.domain.dto.TaskDTO;
import java.util.List;
public interface SaveErrorProneStatisticsDomainService {
/**
* 异步收集易错统计
* 异步收集易错统计仅客观题提交时调用
*
* @param taskId 考试任务ID
* @param examMode 考试模式
* @param taskDTO 考试任务
* @param snapshots 答题快照
*/
void collectErrorProneStatistics(Long taskId, Integer examMode, List<ExamRecordEntity.QuestionSnapshot> snapshots);
void collectErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> snapshots);
/**
* 异步收集简答题易错统计简答题评分完成后调用
*
* @param taskDTO 考试任务
* @param snapshots 已评分的简答题快照
*/
void collectShortAnswerErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> snapshots);
}

61
src/main/java/com/project/statistics/domain/service/impl/SaveSaveErrorProneStatisticsDomainServiceImpl.java

@ -2,6 +2,7 @@ package com.project.statistics.domain.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.project.exam.domain.entity.ExamRecordEntity;
import com.project.question.domain.entity.QuestionEntity;
@ -11,6 +12,7 @@ import com.project.question.domain.service.TaskKnowledgePointBaseService;
import com.project.statistics.domain.entity.ErrorProneStatisticsEntity;
import com.project.statistics.domain.service.ErrorProneStatisticsBaseService;
import com.project.statistics.domain.service.SaveErrorProneStatisticsDomainService;
import com.project.task.domain.dto.TaskDTO;
import com.project.task.domain.entity.TaskEntity;
import com.project.task.domain.enums.ExamModeEnum;
import com.project.task.domain.enums.QuestionTypeEnum;
@ -26,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Service
@ -41,9 +44,6 @@ public class SaveSaveErrorProneStatisticsDomainServiceImpl implements SaveErrorP
@Autowired
private TaskKnowledgePointBaseService taskKnowledgePointBaseService;
@Autowired
private TaskBaseService taskBaseService;
@Autowired
private StringRedisTemplate redisTemplate;
@ -53,28 +53,44 @@ public class SaveSaveErrorProneStatisticsDomainServiceImpl implements SaveErrorP
// 每次抢锁失败后,休眠50ms再重试
private static final long LOCK_RETRY_INTERVAL_MS = 50;
/**
* 收集客观题易错统计试卷提交时调用此时简答题尚未评分
*/
@Override
@Async
public void collectErrorProneStatistics(Long taskId, Integer examMode, List<ExamRecordEntity.QuestionSnapshot> snapshots) {
if (taskId == null || CollUtil.isEmpty(snapshots)) {
public void collectErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> snapshots) {
collectByFilter(taskDTO, snapshots, this::isNotShortAnswer);
}
/**
* 收集简答题易错统计简答题评分完成后调用
*/
@Override
@Async
public void collectShortAnswerErrorProneStatistics(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> snapshots) {
collectByFilter(taskDTO, snapshots, this::isShortAnswer);
}
private void collectByFilter(TaskDTO taskDTO, List<ExamRecordEntity.QuestionSnapshot> snapshots, Predicate<ExamRecordEntity.QuestionSnapshot> filter) {
if (ObjectUtil.isNull(taskDTO) || CollUtil.isEmpty(snapshots)) {
return;
}
TaskEntity task = taskBaseService.getById(taskId);
if (Objects.isNull(task)){
throw new IllegalArgumentException("考试任务不存在,taskId: " + taskId);
List<ExamRecordEntity.QuestionSnapshot> filtered = snapshots.stream().filter(filter).collect(Collectors.toList());
if (CollUtil.isEmpty(filtered)) {
return;
}
try {
Double shortAnswerScore = task.getShortAnswerScore();
Double shortAnswerScore = taskDTO.getShortAnswerScore();
if (ExamModeEnum.GENERATIVE.getValue().equals(examMode)) {
collectGenerativeStatistics(taskId, shortAnswerScore, snapshots);
} else if (ExamModeEnum.CLASSIC.getValue().equals(examMode)) {
collectClassicStatistics(taskId, shortAnswerScore, snapshots);
if (ExamModeEnum.GENERATIVE.getValue().equals(taskDTO.getExamMode())) {
collectGenerativeStatistics(taskDTO.getId(), shortAnswerScore, filtered);
} else if (ExamModeEnum.CLASSIC.getValue().equals(taskDTO.getExamMode())) {
collectClassicStatistics(taskDTO.getId(), shortAnswerScore, filtered);
}
} catch (Exception e) {
log.error("[易错统计] 收集失败, taskId={}", taskId, e);
log.error("[易错统计] 收集失败, taskId={}", taskDTO.getId(), e);
}
}
@ -180,7 +196,7 @@ public class SaveSaveErrorProneStatisticsDomainServiceImpl implements SaveErrorP
entity.setAppearCount(0);
//构建题目详细信息
List<ErrorProneStatisticsEntity.OptionDetail> optionDetails;
if (isShortAnswer(snapshot.getType())) {
if (isShortAnswer(snapshot)) {
optionDetails = buildOptionDetail(question.getQuestionDetail());
} else {
optionDetails = buildOptionDetail(snapshot);
@ -275,7 +291,7 @@ public class SaveSaveErrorProneStatisticsDomainServiceImpl implements SaveErrorP
*/
private void updateOptionDetail(ErrorProneStatisticsEntity entity, ExamRecordEntity.QuestionSnapshot snapshot) {
String normalizedUserAnswer;
if(isShortAnswer(snapshot.getType())){
if (isShortAnswer(snapshot)) {
List<Integer> hitPoints = snapshot.getHitPoints();
normalizedUserAnswer = CollectionUtil.isEmpty(hitPoints) ? "" : hitPoints.stream().map(String::valueOf).collect(Collectors.joining(","));
}else{
@ -310,15 +326,22 @@ public class SaveSaveErrorProneStatisticsDomainServiceImpl implements SaveErrorP
/**
* 是否为简答题
*/
private boolean isShortAnswer(Integer type) {
return Objects.equals(type, QuestionTypeEnum.SHORT_ANSWER.getValue());
private boolean isShortAnswer(ExamRecordEntity.QuestionSnapshot snapshot) {
return Objects.equals(snapshot.getType(), QuestionTypeEnum.SHORT_ANSWER.getValue());
}
/**
* 是否为非简答题
*/
private boolean isNotShortAnswer(ExamRecordEntity.QuestionSnapshot snapshot) {
return !isShortAnswer(snapshot);
}
/**
* 判断是否为错题客观题按 isRight简答题按 aiScore 是否得满
*/
private boolean isErrorQuestion(ExamRecordEntity.QuestionSnapshot snapshot, Double shortAnswerScore) {
if (isShortAnswer(snapshot.getType())) {
if (isShortAnswer(snapshot)) {
Double aiScore = snapshot.getAiScore();
return aiScore == null || aiScore < shortAnswerScore;
}

Loading…
Cancel
Save