|
|
@@ -1,5 +1,6 @@
|
|
|
package com.njuzr.eaibackend.service;
|
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
import com.njuzr.eaibackend.constant.EvaluationPromptConstant;
|
|
|
@@ -9,6 +10,7 @@ import com.njuzr.eaibackend.dto.deepseek.DeepSeekResponse;
|
|
|
import com.njuzr.eaibackend.exception.MyException;
|
|
|
import com.njuzr.eaibackend.mapper.*;
|
|
|
import com.njuzr.eaibackend.po.*;
|
|
|
+import com.njuzr.eaibackend.vo.EvaluationCurrentVO;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.time.StopWatch;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
@@ -21,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.transaction.PlatformTransactionManager;
|
|
|
import org.springframework.transaction.support.TransactionTemplate;
|
|
|
import org.springframework.transaction.TransactionDefinition;
|
|
|
+import org.springframework.dao.DuplicateKeyException;
|
|
|
|
|
|
import java.text.BreakIterator;
|
|
|
import java.util.*;
|
|
|
@@ -105,6 +108,10 @@ public class AIEvaluationService {
|
|
|
if(engagement == null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业参与不存在");
|
|
|
Assignment assignment = assignmentMapper.selectById(assignmentId);
|
|
|
if (assignment == null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
|
|
|
+ final int version = engagement.getVersion();
|
|
|
+ log.info("[EVAL][OVERALL] request start assignmentId={}, studentId={}, version={}, textLen={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ engagement.getTextContent() == null ? -1 : engagement.getTextContent().length());
|
|
|
/**
|
|
|
* AI 作业评估主流程。
|
|
|
*
|
|
|
@@ -131,37 +138,76 @@ public class AIEvaluationService {
|
|
|
* - 防止系统异常导致任务永久卡死。
|
|
|
* - 保证 evaluation 表始终只存在一条有效执行记录。
|
|
|
*/
|
|
|
+ // 1) 先检查当前版本是否已完成整体批改:已完成则直接返回历史结果(幂等)
|
|
|
+ Evaluation exist = evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, version);
|
|
|
+ log.info("[EVAL][OVERALL] current evaluation record assignmentId={}, studentId={}, version={}, exist={}, overall={}, sentence={}, time={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ exist != null,
|
|
|
+ exist == null ? null : exist.getOverall(),
|
|
|
+ exist == null ? null : exist.getSentence(),
|
|
|
+ exist == null ? null : exist.getTime());
|
|
|
+ if (exist != null && exist.getOverall() != null && exist.getOverall() == 1) {
|
|
|
+ OverallEvaluation overall = getOverallEvaluationIfExists(assignmentId, studentId, version);
|
|
|
+ if (overall != null) {
|
|
|
+ log.info("[EVAL][OVERALL] hit cached result assignmentId={}, studentId={}, version={}, evalLen={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ overall.getEvaluation() == null ? -1 : overall.getEvaluation().length());
|
|
|
+ return new AIEntry("assistant", overall.getEvaluation());
|
|
|
+ }
|
|
|
+ // 理论上不应该出现:标记完成但无结果;允许重新触发
|
|
|
+ log.warn("evaluation overall flag=1 but overall_evaluation missing, will re-run. assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2) 抢占执行权:不存在则插入占位记录;并发时依赖唯一索引冲突
|
|
|
try {
|
|
|
initEvaluationRecord(assignmentId, studentId, engagement);
|
|
|
- } catch (org.springframework.dao.DuplicateKeyException ex) {
|
|
|
- Evaluation exist = evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, engagement.getVersion());
|
|
|
- if (exist == null) {
|
|
|
+ log.info("[EVAL][OVERALL] init evaluation lock success assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ } catch (DuplicateKeyException ex) {
|
|
|
+ log.info("[EVAL][OVERALL] init evaluation lock duplicate assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ Evaluation locked = evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, version);
|
|
|
+ if (locked == null) {
|
|
|
throw MyException.create(HttpStatus.BAD_REQUEST, "作业正在进行AI分析,请稍后重试");
|
|
|
}
|
|
|
- if (exist.getOverall() == 0) {
|
|
|
|
|
|
+ // overall==0: 处理中;overall==1: 已完成(上面已处理直接返回)
|
|
|
+ if (locked.getOverall() != null && locked.getOverall() == 0) {
|
|
|
long timeoutMs = 10L * 60 * 1000;
|
|
|
Date expireBefore = new Date(System.currentTimeMillis() - timeoutMs);
|
|
|
-
|
|
|
- if (exist.getTime() != null && exist.getTime().before(expireBefore)){
|
|
|
- evaluationMapperServiceImpl.deleteByVersion(assignmentId, studentId, engagement.getVersion());
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "之前的 AI 分析已超时,已为您清理,请重新发起");
|
|
|
-
|
|
|
- } else {
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "作业正在进行AI分析,请勿重复发起请求");
|
|
|
+ if (locked.getTime() != null && locked.getTime().before(expireBefore)) {
|
|
|
+ evaluationMapperServiceImpl.deleteByVersion(assignmentId, studentId, version);
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "之前的 AI 分析已超时,已为您清理,请重新发起");
|
|
|
}
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "作业正在进行AI分析,请勿重复发起请求");
|
|
|
+ }
|
|
|
+
|
|
|
+ OverallEvaluation overall = getOverallEvaluationIfExists(assignmentId, studentId, version);
|
|
|
+ if (overall != null) {
|
|
|
+ log.info("[EVAL][OVERALL] duplicate request but result exists assignmentId={}, studentId={}, version={}, evalLen={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ overall.getEvaluation() == null ? -1 : overall.getEvaluation().length());
|
|
|
+ return new AIEntry("assistant", overall.getEvaluation());
|
|
|
}
|
|
|
throw MyException.create(HttpStatus.BAD_REQUEST, "作业已进行过AI分析");
|
|
|
}
|
|
|
+
|
|
|
try {
|
|
|
String prompt = String.format(EvaluationPromptConstant.OVERALL_EVALUATION_PROMPT,
|
|
|
assignment.getDescription(), engagement.getTextContent());
|
|
|
+ log.info("[EVAL][OVERALL] llm request start assignmentId={}, studentId={}, version={}, promptLen={}",
|
|
|
+ assignmentId, studentId, version, prompt.length());
|
|
|
AIRequestService.AIResponse response = requestAIWithRetry(prompt, 0);
|
|
|
String retContent = response.getChoices().get(0).getMessage().getContent();
|
|
|
String role = response.getChoices().get(0).getMessage().getRole();
|
|
|
|
|
|
// 保存Markdown格式的评估结果
|
|
|
saveOverallEvaluationResult(assignmentId, studentId, engagement, retContent);
|
|
|
+ log.info("[EVAL][OVERALL] save success assignmentId={}, studentId={}, version={}, role={}, evalLen={}, cost={}",
|
|
|
+ assignmentId, studentId, version, role,
|
|
|
+ retContent == null ? -1 : retContent.length(),
|
|
|
+ sw.formatTime());
|
|
|
|
|
|
log.info("【评分完成】作文长度: {}, 评价长度: {}",
|
|
|
engagement.getTextContent().length(), retContent.length());
|
|
|
@@ -170,7 +216,7 @@ public class AIEvaluationService {
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
log.error("evaluation requestAI error: {}", e.getMessage(), e);
|
|
|
- evaluationMapperServiceImpl.deleteByVersion(assignmentId, studentId, engagement.getVersion());
|
|
|
+ evaluationMapperServiceImpl.deleteByVersion(assignmentId, studentId, version);
|
|
|
throw new MyException(HttpStatus.INTERNAL_SERVER_ERROR.value(),HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase() + ":" + "请求失败,请重新尝试"
|
|
|
);
|
|
|
}
|
|
|
@@ -216,19 +262,30 @@ public class AIEvaluationService {
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void saveOverallEvaluationResult(Long assignmentId, Long studentId, Engagement engagement, String retContent) {
|
|
|
+ final int version = engagement.getVersion();
|
|
|
OverallEvaluation overallEvaluation = new OverallEvaluation()
|
|
|
.setStudentId(studentId)
|
|
|
.setAssignmentId(assignmentId)
|
|
|
.setContent(engagement.getTextContent())
|
|
|
.setEvaluation(retContent)
|
|
|
- .setVersion(engagement.getVersion());
|
|
|
- overallEvaluationMapper.insert(overallEvaluation);
|
|
|
+ .setVersion(version);
|
|
|
+ try {
|
|
|
+ overallEvaluationMapper.insert(overallEvaluation);
|
|
|
+ log.info("[EVAL][OVERALL] overall_evaluation insert ok assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ } catch (DuplicateKeyException e) {
|
|
|
+ // 幂等:同版本重复写入时忽略(依赖唯一索引)
|
|
|
+ log.info("overall_evaluation already exists, ignore duplicate insert. assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ }
|
|
|
|
|
|
evaluationMapperServiceImpl.update(new UpdateWrapper<Evaluation>()
|
|
|
.set("overall", 1)
|
|
|
.eq("student_id", studentId)
|
|
|
.eq("assignment_id", assignmentId)
|
|
|
- .eq("version", engagement.getVersion()));
|
|
|
+ .eq("version", version));
|
|
|
+ log.info("[EVAL][OVERALL] evaluation flag update overall=1 assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
}
|
|
|
|
|
|
private AIRequestService.AIResponse requestAIWithRetry(String prompt, int retryTime) {
|
|
|
@@ -274,41 +331,56 @@ public class AIEvaluationService {
|
|
|
if(engagement == null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业参与不存在");
|
|
|
Assignment assignment = assignmentMapper.selectById(assignmentId);
|
|
|
if (assignment == null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业不存在");
|
|
|
+ final int version = engagement.getVersion();
|
|
|
+ log.info("[EVAL][SENTENCE] request start assignmentId={}, studentId={}, version={}, textLen={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ engagement.getTextContent() == null ? -1 : engagement.getTextContent().length());
|
|
|
+
|
|
|
+ // 逐句批改可独立触发:确保 evaluation 占位记录存在(不依赖整体先跑)
|
|
|
+ Evaluation evaluationJudge = ensureEvaluationRecordExists(assignmentId, studentId, engagement);
|
|
|
+ log.info("[EVAL][SENTENCE] current evaluation record assignmentId={}, studentId={}, version={}, sentence={}, overall={}, time={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ evaluationJudge == null ? null : evaluationJudge.getSentence(),
|
|
|
+ evaluationJudge == null ? null : evaluationJudge.getOverall(),
|
|
|
+ evaluationJudge == null ? null : evaluationJudge.getTime());
|
|
|
+
|
|
|
+ // 检查是否已完成逐句批改(sentence=1表示已完成)
|
|
|
+ if (evaluationJudge != null && evaluationJudge.getSentence() != null && evaluationJudge.getSentence() == 1) {
|
|
|
+ log.info("逐句批改已完成,直接返回现有结果 - assignmentId: {}, studentId: {}, version: {}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ LambdaQueryWrapper<SentenceEvaluation> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(SentenceEvaluation::getAssignmentId, assignmentId)
|
|
|
+ .eq(SentenceEvaluation::getStudentId, studentId)
|
|
|
+ .eq(SentenceEvaluation::getVersion, version);
|
|
|
+ List<SentenceEvaluation> list = sentenceEvaluationMapperServiceImpl.list(queryWrapper);
|
|
|
+ log.info("[EVAL][SENTENCE] hit cached result assignmentId={}, studentId={}, version={}, count={}",
|
|
|
+ assignmentId, studentId, version, list == null ? -1 : list.size());
|
|
|
+ return list;
|
|
|
+ }
|
|
|
|
|
|
- // 没有智能评价记录时,进行重试,等待整体智能批改将记录写入数据库
|
|
|
- // 使用编程式事务在新事务中查询,避免REPEATABLE READ隔离级别导致看不到其他事务提交的数据
|
|
|
- int retryTime = 0;
|
|
|
- Evaluation evaluationJudge = null;
|
|
|
- while (retryTime < 3) {
|
|
|
- final int currentVersion = engagement.getVersion();
|
|
|
- evaluationJudge = requiresNewTransactionTemplate.execute(status ->
|
|
|
- evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, currentVersion)
|
|
|
- );
|
|
|
- log.info("rewritePerSentence find evaluation record, assignmentId: {}, studentId: {}, version: {}", assignmentId, studentId, engagement.getVersion());
|
|
|
- if (evaluationJudge == null){
|
|
|
- try {
|
|
|
- log.info("rewritePerSentence find evaluation record failed, assignmentId: {}, studentId: {}, retryTime: {}", assignmentId, studentId, retryTime + 1);
|
|
|
- Thread.sleep(1500);
|
|
|
- retryTime++;
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "处理被中断: " + e.getMessage());
|
|
|
- }
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if(retryTime >= 3) {
|
|
|
- log.info("rewritePerSentence failed, assignmentId: {}, studentId: {}, reason: over max retry times(3)", assignmentId, studentId);
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "逐句批改失败,超过重试次数,请重新发起请求");
|
|
|
+ // sentence=2 表示处理中,直接提示前端轮询读取即可
|
|
|
+ if (evaluationJudge != null && evaluationJudge.getSentence() != null && evaluationJudge.getSentence() == 2) {
|
|
|
+ log.info("[EVAL][SENTENCE] already processing assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "逐句批改正在进行中,请稍后刷新");
|
|
|
}
|
|
|
|
|
|
+ // 设置状态为处理中(2),避免重复处理
|
|
|
+ evaluationMapperServiceImpl.update(new UpdateWrapper<Evaluation>()
|
|
|
+ .set("sentence", 2)
|
|
|
+ .eq("student_id", studentId)
|
|
|
+ .eq("assignment_id", assignmentId)
|
|
|
+ .eq("version", version)
|
|
|
+ );
|
|
|
+ log.info("[EVAL][SENTENCE] mark processing sentence=2 assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+
|
|
|
try {
|
|
|
String content = engagement.getTextContent();
|
|
|
String[] sentences = getSentenceList(content);
|
|
|
|
|
|
log.info("rewritePerSentence start, assignmentId: {}, studentId: {}, sentenceCount: {}, version: {}",
|
|
|
- assignmentId, studentId, sentences.length, engagement.getVersion());
|
|
|
+ assignmentId, studentId, sentences.length, version);
|
|
|
|
|
|
List<SentenceEvaluation> sentenceEvaluationsList = new ArrayList<>();
|
|
|
@SuppressWarnings("unchecked")
|
|
|
@@ -323,7 +395,7 @@ public class AIEvaluationService {
|
|
|
assignmentId, studentId,
|
|
|
content, assignment.getDescription(),
|
|
|
sentence, sentenceNo,
|
|
|
- engagement.getVersion()
|
|
|
+ version
|
|
|
),
|
|
|
aiPerSentenceExecutor
|
|
|
);
|
|
|
@@ -352,6 +424,8 @@ public class AIEvaluationService {
|
|
|
// 3) 批量保存逐句结果
|
|
|
if (!sentenceEvaluationsList.isEmpty()) {
|
|
|
sentenceEvaluationMapperServiceImpl.saveBatch(sentenceEvaluationsList);
|
|
|
+ log.info("[EVAL][SENTENCE] sentence_evaluation batch insert ok assignmentId={}, studentId={}, version={}, count={}",
|
|
|
+ assignmentId, studentId, version, sentenceEvaluationsList.size());
|
|
|
}
|
|
|
|
|
|
// 4) 完成态:2 -> 1(只允许从处理中改成完成,避免误覆盖)
|
|
|
@@ -359,9 +433,11 @@ public class AIEvaluationService {
|
|
|
.set("sentence", 1)
|
|
|
.eq("student_id", studentId)
|
|
|
.eq("assignment_id", assignmentId)
|
|
|
- .eq("version", engagement.getVersion())
|
|
|
+ .eq("version", version)
|
|
|
.eq("sentence", 2)
|
|
|
);
|
|
|
+ log.info("[EVAL][SENTENCE] mark done sentence=1 assignmentId={}, studentId={}, version={}, cost={}",
|
|
|
+ assignmentId, studentId, version, sw.formatTime());
|
|
|
|
|
|
log.info("rewritePerSentence success, assignmentId: {}, studentId: {}, cost: {}",
|
|
|
assignmentId, studentId, sw.formatTime());
|
|
|
@@ -374,7 +450,7 @@ public class AIEvaluationService {
|
|
|
.set("sentence", 0)
|
|
|
.eq("student_id", studentId)
|
|
|
.eq("assignment_id", assignmentId)
|
|
|
- .eq("version", engagement.getVersion())
|
|
|
+ .eq("version", version)
|
|
|
.eq("sentence", 2)
|
|
|
);
|
|
|
throw ex;
|
|
|
@@ -545,14 +621,19 @@ public class AIEvaluationService {
|
|
|
* @return
|
|
|
*/
|
|
|
public OverallEvaluation getRewrite(Long assignmentId, Long studentId, int version){
|
|
|
+ log.info("[EVAL][SELECT][OVERALL] select assignmentId={}, studentId={}, version={}", assignmentId, studentId, version);
|
|
|
QueryWrapper<OverallEvaluation> queryWrapper = new QueryWrapper<>();
|
|
|
queryWrapper.eq("assignment_id", assignmentId);
|
|
|
queryWrapper.eq("student_id", studentId);
|
|
|
queryWrapper.eq("version", version);
|
|
|
OverallEvaluation overallEvaluation = overallEvaluationMapper.selectOne(queryWrapper);
|
|
|
if(overallEvaluation == null){
|
|
|
+ log.info("[EVAL][SELECT][OVERALL] not found assignmentId={}, studentId={}, version={}", assignmentId, studentId, version);
|
|
|
throw MyException.create(HttpStatus.BAD_REQUEST, "AI批改不存在");
|
|
|
}
|
|
|
+ log.info("[EVAL][SELECT][OVERALL] found assignmentId={}, studentId={}, version={}, evalLen={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ overallEvaluation.getEvaluation() == null ? -1 : overallEvaluation.getEvaluation().length());
|
|
|
return overallEvaluation;
|
|
|
}
|
|
|
|
|
|
@@ -564,15 +645,19 @@ public class AIEvaluationService {
|
|
|
* @return
|
|
|
*/
|
|
|
public List<SentenceEvaluation> getRewritePerSentence(Long assignmentId, Long studentId, int version){
|
|
|
+ log.info("[EVAL][SELECT][SENTENCE] select assignmentId={}, studentId={}, version={}", assignmentId, studentId, version);
|
|
|
List<SentenceEvaluation> sentenceEvaluations = sentenceEvaluationMapperServiceImpl.list(new QueryWrapper<SentenceEvaluation>()
|
|
|
.eq("assignment_id", assignmentId)
|
|
|
.eq("student_id", studentId)
|
|
|
.eq("version", version));
|
|
|
|
|
|
if(sentenceEvaluations.isEmpty()){
|
|
|
+ log.info("[EVAL][SELECT][SENTENCE] not found assignmentId={}, studentId={}, version={}", assignmentId, studentId, version);
|
|
|
throw MyException.create(HttpStatus.BAD_REQUEST, "AI批改不存在");
|
|
|
}
|
|
|
|
|
|
+ log.info("[EVAL][SELECT][SENTENCE] found assignmentId={}, studentId={}, version={}, count={}",
|
|
|
+ assignmentId, studentId, version, sentenceEvaluations.size());
|
|
|
return sentenceEvaluations.stream().sorted(Comparator.comparing(SentenceEvaluation::getNo)).toList();
|
|
|
}
|
|
|
|
|
|
@@ -617,6 +702,83 @@ public class AIEvaluationService {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ private OverallEvaluation getOverallEvaluationIfExists(Long assignmentId, Long studentId, int version) {
|
|
|
+ try {
|
|
|
+ QueryWrapper<OverallEvaluation> queryWrapper = new QueryWrapper<>();
|
|
|
+ queryWrapper.eq("assignment_id", assignmentId);
|
|
|
+ queryWrapper.eq("student_id", studentId);
|
|
|
+ queryWrapper.eq("version", version);
|
|
|
+ return overallEvaluationMapper.selectOne(queryWrapper);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("getOverallEvaluationIfExists failed: {}", e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Evaluation ensureEvaluationRecordExists(Long assignmentId, Long studentId, Engagement engagement) {
|
|
|
+ final int version = engagement.getVersion();
|
|
|
+ Evaluation existing = requiresNewTransactionTemplate.execute(status ->
|
|
|
+ evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, version)
|
|
|
+ );
|
|
|
+ if (existing != null) {
|
|
|
+ log.info("[EVAL][ENSURE] evaluation record exists assignmentId={}, studentId={}, version={}, overall={}, sentence={}",
|
|
|
+ assignmentId, studentId, version, existing.getOverall(), existing.getSentence());
|
|
|
+ return existing;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ initEvaluationRecord(assignmentId, studentId, engagement);
|
|
|
+ log.info("[EVAL][ENSURE] init evaluation record ok assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ } catch (DuplicateKeyException e) {
|
|
|
+ // 并发插入:忽略并重新查
|
|
|
+ log.info("[EVAL][ENSURE] init evaluation record duplicate assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ }
|
|
|
+ Evaluation created = requiresNewTransactionTemplate.execute(status ->
|
|
|
+ evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, version)
|
|
|
+ );
|
|
|
+ if (created == null) {
|
|
|
+ log.warn("[EVAL][ENSURE] init evaluation record failed assignmentId={}, studentId={}, version={}",
|
|
|
+ assignmentId, studentId, version);
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "初始化逐句批改记录失败,请重试");
|
|
|
+ }
|
|
|
+ log.info("[EVAL][ENSURE] evaluation record ready assignmentId={}, studentId={}, version={}, overall={}, sentence={}",
|
|
|
+ assignmentId, studentId, version, created.getOverall(), created.getSentence());
|
|
|
+ return created;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前最新提交版本号(student_assignment.version)以及该版本的整体/逐句批改状态。
|
|
|
+ */
|
|
|
+ public EvaluationCurrentVO getCurrentEvaluationStatus(Long assignmentId, Long studentId) {
|
|
|
+ Engagement engagement = studentAssignmentMapper.findEngagementByStudentIdAndAssignmentId(studentId, assignmentId);
|
|
|
+ if (engagement == null) throw MyException.create(HttpStatus.BAD_REQUEST, "作业参与不存在");
|
|
|
+ int version = engagement.getVersion();
|
|
|
+
|
|
|
+ Evaluation evaluation = evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, version);
|
|
|
+ Integer overall = evaluation == null ? 0 : Optional.ofNullable(evaluation.getOverall()).orElse(0);
|
|
|
+ Integer sentence = evaluation == null ? 0 : Optional.ofNullable(evaluation.getSentence()).orElse(0);
|
|
|
+
|
|
|
+ OverallEvaluation overallRow = getOverallEvaluationIfExists(assignmentId, studentId, version);
|
|
|
+ int sentenceCount = Math.toIntExact(sentenceEvaluationMapperServiceImpl.count(new LambdaQueryWrapper<SentenceEvaluation>()
|
|
|
+ .eq(SentenceEvaluation::getAssignmentId, assignmentId)
|
|
|
+ .eq(SentenceEvaluation::getStudentId, studentId)
|
|
|
+ .eq(SentenceEvaluation::getVersion, version)));
|
|
|
+ log.info("[EVAL][CURRENT] assignmentId={}, studentId={}, version={}, evalRow={}, overallStatus={}, sentenceStatus={}, overallRow={}, sentenceCount={}",
|
|
|
+ assignmentId, studentId, version,
|
|
|
+ evaluation != null,
|
|
|
+ overall, sentence,
|
|
|
+ overallRow != null,
|
|
|
+ sentenceCount);
|
|
|
+
|
|
|
+ return new EvaluationCurrentVO()
|
|
|
+ .setAssignmentId(assignmentId)
|
|
|
+ .setStudentId(studentId)
|
|
|
+ .setVersion(version)
|
|
|
+ .setOverallStatus(overall)
|
|
|
+ .setSentenceStatus(sentence);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 双智评:同时触发整体智评和逐句智评
|
|
|
* 1. 创建智评记录
|