|
|
@@ -251,9 +251,10 @@ public class AIEvaluationService {
|
|
|
// 没有智能评价记录时,进行重试,等待整体智能批改将记录写入数据库
|
|
|
// 使用编程式事务在新事务中查询,避免REPEATABLE READ隔离级别导致看不到其他事务提交的数据
|
|
|
int retryTime = 0;
|
|
|
+ Evaluation evaluationJudge = null;
|
|
|
while (retryTime < 3) {
|
|
|
final int currentVersion = engagement.getVersion();
|
|
|
- Evaluation evaluationJudge = requiresNewTransactionTemplate.execute(status ->
|
|
|
+ evaluationJudge = requiresNewTransactionTemplate.execute(status ->
|
|
|
evaluationMapperServiceImpl.getByVersion(assignmentId, studentId, currentVersion)
|
|
|
);
|
|
|
log.info("rewritePerSentence find evaluation record, assignmentId: {}, studentId: {}, version: {}", assignmentId, studentId, engagement.getVersion());
|
|
|
@@ -263,78 +264,152 @@ public class AIEvaluationService {
|
|
|
Thread.sleep(1500);
|
|
|
retryTime++;
|
|
|
} catch (InterruptedException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "处理被中断: " + e.getMessage());
|
|
|
}
|
|
|
} else {
|
|
|
- if(evaluationJudge.getSentence() != 0){
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "作业已进行过逐句智能评价");
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
+ break;
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
if(retryTime >= 3) {
|
|
|
log.info("rewritePerSentence failed, assignmentId: {}, studentId: {}, reason: over max retry times(3)", assignmentId, studentId);
|
|
|
throw MyException.create(HttpStatus.BAD_REQUEST, "逐句批改失败,超过重试次数,请重新发起请求");
|
|
|
}
|
|
|
|
|
|
- String content = engagement.getTextContent();
|
|
|
- String[] sentences = getSentenceList(content);
|
|
|
- log.info("rewritePerSentence 开始处理, assignmentId: {}, studentId: {}, 句子数量: {}", assignmentId, studentId, sentences.length);
|
|
|
-
|
|
|
- // 使用线程安全的集合,避免多线程并发写入导致数据丢失
|
|
|
- List<SentenceEvaluation> sentenceEvaluationsList = Collections.synchronizedList(new ArrayList<>());
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- CompletableFuture<SentenceEvaluation>[] futures = new CompletableFuture[sentences.length];
|
|
|
+ try {
|
|
|
+ String content = engagement.getTextContent();
|
|
|
+ String[] sentences = getSentenceList(content);
|
|
|
+
|
|
|
+ log.info("rewritePerSentence start, assignmentId: {}, studentId: {}, sentenceCount: {}, version: {}",
|
|
|
+ assignmentId, studentId, sentences.length, engagement.getVersion());
|
|
|
+
|
|
|
+ List<SentenceEvaluation> sentenceEvaluationsList = new ArrayList<>();
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ CompletableFuture<SentenceEvaluation>[] futures = new CompletableFuture[sentences.length];
|
|
|
+
|
|
|
+ for (int i = 0; i < sentences.length; i++) {
|
|
|
+ final int sentenceNo = i;
|
|
|
+ final String sentence = sentences[i];
|
|
|
+
|
|
|
+ futures[i] = CompletableFuture.supplyAsync(
|
|
|
+ () -> processSingleSentenceWithRetry(
|
|
|
+ assignmentId, studentId,
|
|
|
+ content, assignment.getDescription(),
|
|
|
+ sentence, sentenceNo,
|
|
|
+ engagement.getVersion()
|
|
|
+ ),
|
|
|
+ aiPerSentenceExecutor
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- for (int i = 0; i < sentences.length; i++) {
|
|
|
- final int sentenceNo = i;
|
|
|
- final String sentence = sentences[i];
|
|
|
- futures[i] = CompletableFuture.supplyAsync(() ->
|
|
|
- processSingleSentenceWithRetry(assignmentId, studentId, content, assignment.getDescription(), sentence, sentenceNo, engagement.getVersion()),
|
|
|
- aiPerSentenceExecutor);
|
|
|
- }
|
|
|
+ try {
|
|
|
+ CompletableFuture.allOf(futures).get(80, TimeUnit.SECONDS);
|
|
|
|
|
|
- // 等待所有任务完成
|
|
|
- try {
|
|
|
- CompletableFuture.allOf(futures).get(60, TimeUnit.SECONDS);
|
|
|
- // 收集所有结果
|
|
|
- for (CompletableFuture<SentenceEvaluation> future : futures) {
|
|
|
- SentenceEvaluation result = future.get();
|
|
|
- if (result != null) {
|
|
|
- sentenceEvaluationsList.add(result);
|
|
|
+ for (CompletableFuture<SentenceEvaluation> future : futures) {
|
|
|
+ SentenceEvaluation result = future.get();
|
|
|
+ if (result != null) {
|
|
|
+ sentenceEvaluationsList.add(result);
|
|
|
+ }
|
|
|
}
|
|
|
+ } catch (TimeoutException e) {
|
|
|
+ log.warn("rewritePerSentence timeout, assignmentId: {}, studentId: {}", assignmentId, studentId);
|
|
|
+ Arrays.stream(futures).forEach(f -> f.cancel(true));
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "处理超时,请重试");
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "处理被中断: " + e.getMessage());
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw MyException.create(HttpStatus.BAD_REQUEST, "处理失败: " + e.getMessage());
|
|
|
}
|
|
|
- log.info("rewritePerSentence allOf success, assignmentId: {}, studentId: {}, version: {}, sentenceCount: {}, resultCount: {}",
|
|
|
- assignmentId, studentId, engagement.getVersion(), sentences.length, sentenceEvaluationsList.size());
|
|
|
- } catch (TimeoutException e) {
|
|
|
- log.warn("rewritePerSentence failed, assignmentId: {}, studentId: {}, reason: timeout", assignmentId, studentId);
|
|
|
- Arrays.stream(futures).forEach(f -> f.cancel(true));
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "处理超时,请重试");
|
|
|
- } catch (InterruptedException e) {
|
|
|
- Thread.currentThread().interrupt();
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "处理被中断: " + e.getMessage());
|
|
|
- } catch (Exception e){
|
|
|
- throw MyException.create(HttpStatus.BAD_REQUEST, "处理失败: " + e.getMessage());
|
|
|
- }
|
|
|
|
|
|
- // 所有任务完成后批量插入数据库
|
|
|
- log.info("rewritePerSentence准备保存, listSize: {}, list: {}", sentenceEvaluationsList.size(), sentenceEvaluationsList);
|
|
|
- if(!sentenceEvaluationsList.isEmpty()) {
|
|
|
- sentenceEvaluationMapperServiceImpl.saveBatch(sentenceEvaluationsList);
|
|
|
- log.info("rewritePerSentence saveBatch完成, assignmentId: {}, studentId: {}", assignmentId, studentId);
|
|
|
- } else {
|
|
|
- log.warn("rewritePerSentence 列表为空,跳过保存! assignmentId: {}, studentId: {}", assignmentId, studentId);
|
|
|
- }
|
|
|
+ // 3) 批量保存逐句结果
|
|
|
+ if (!sentenceEvaluationsList.isEmpty()) {
|
|
|
+ sentenceEvaluationMapperServiceImpl.saveBatch(sentenceEvaluationsList);
|
|
|
+ }
|
|
|
|
|
|
+ // 4) 完成态:2 -> 1(只允许从处理中改成完成,避免误覆盖)
|
|
|
+ evaluationMapperServiceImpl.update(new UpdateWrapper<Evaluation>()
|
|
|
+ .set("sentence", 1)
|
|
|
+ .eq("student_id", studentId)
|
|
|
+ .eq("assignment_id", assignmentId)
|
|
|
+ .eq("version", engagement.getVersion())
|
|
|
+ .eq("sentence", 2)
|
|
|
+ );
|
|
|
+
|
|
|
+ log.info("rewritePerSentence success, assignmentId: {}, studentId: {}, cost: {}",
|
|
|
+ assignmentId, studentId, sw.formatTime());
|
|
|
+
|
|
|
+ return sentenceEvaluationsList;
|
|
|
+
|
|
|
+ } catch (RuntimeException ex) {
|
|
|
+ // 5) 出现任何异常:释放处理中状态(2 -> 0),允许重试,避免永久卡死
|
|
|
evaluationMapperServiceImpl.update(new UpdateWrapper<Evaluation>()
|
|
|
- .set("sentence", 1)
|
|
|
+ .set("sentence", 0)
|
|
|
.eq("student_id", studentId)
|
|
|
.eq("assignment_id", assignmentId)
|
|
|
- .eq("version", engagement.getVersion()));
|
|
|
+ .eq("version", engagement.getVersion())
|
|
|
+ .eq("sentence", 2)
|
|
|
+ );
|
|
|
+ throw ex;
|
|
|
+ }
|
|
|
+ // String content = engagement.getTextContent();
|
|
|
+ // String[] sentences = getSentenceList(content);
|
|
|
+ // log.info("rewritePerSentence 开始处理, assignmentId: {}, studentId: {}, 句子数量: {}", assignmentId, studentId, sentences.length);
|
|
|
+
|
|
|
+ // // 使用线程安全的集合,避免多线程并发写入导致数据丢失
|
|
|
+ // List<SentenceEvaluation> sentenceEvaluationsList = Collections.synchronizedList(new ArrayList<>());
|
|
|
+ // @SuppressWarnings("unchecked")
|
|
|
+ // CompletableFuture<SentenceEvaluation>[] futures = new CompletableFuture[sentences.length];
|
|
|
+
|
|
|
+ // for (int i = 0; i < sentences.length; i++) {
|
|
|
+ // final int sentenceNo = i;
|
|
|
+ // final String sentence = sentences[i];
|
|
|
+ // futures[i] = CompletableFuture.supplyAsync(() ->
|
|
|
+ // processSingleSentenceWithRetry(assignmentId, studentId, content, assignment.getDescription(), sentence, sentenceNo, engagement.getVersion()),
|
|
|
+ // aiPerSentenceExecutor);
|
|
|
+ // }
|
|
|
|
|
|
- log.info("rewritePerSentence cost: {}", sw.formatTime());
|
|
|
- return sentenceEvaluationsList;
|
|
|
+ // // 等待所有任务完成
|
|
|
+ // try {
|
|
|
+ // CompletableFuture.allOf(futures).get(60, TimeUnit.SECONDS);
|
|
|
+ // // 收集所有结果
|
|
|
+ // for (CompletableFuture<SentenceEvaluation> future : futures) {
|
|
|
+ // SentenceEvaluation result = future.get();
|
|
|
+ // if (result != null) {
|
|
|
+ // sentenceEvaluationsList.add(result);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // log.info("rewritePerSentence allOf success, assignmentId: {}, studentId: {}, version: {}, sentenceCount: {}, resultCount: {}",
|
|
|
+ // assignmentId, studentId, engagement.getVersion(), sentences.length, sentenceEvaluationsList.size());
|
|
|
+ // } catch (TimeoutException e) {
|
|
|
+ // log.warn("rewritePerSentence failed, assignmentId: {}, studentId: {}, reason: timeout", assignmentId, studentId);
|
|
|
+ // Arrays.stream(futures).forEach(f -> f.cancel(true));
|
|
|
+ // throw MyException.create(HttpStatus.BAD_REQUEST, "处理超时,请重试");
|
|
|
+ // } catch (InterruptedException e) {
|
|
|
+ // Thread.currentThread().interrupt();
|
|
|
+ // throw MyException.create(HttpStatus.BAD_REQUEST, "处理被中断: " + e.getMessage());
|
|
|
+ // } catch (Exception e){
|
|
|
+ // throw MyException.create(HttpStatus.BAD_REQUEST, "处理失败: " + e.getMessage());
|
|
|
+ // }
|
|
|
+
|
|
|
+ // // 所有任务完成后批量插入数据库
|
|
|
+ // log.info("rewritePerSentence准备保存, listSize: {}, list: {}", sentenceEvaluationsList.size(), sentenceEvaluationsList);
|
|
|
+ // if(!sentenceEvaluationsList.isEmpty()) {
|
|
|
+ // sentenceEvaluationMapperServiceImpl.saveBatch(sentenceEvaluationsList);
|
|
|
+ // log.info("rewritePerSentence saveBatch完成, assignmentId: {}, studentId: {}", assignmentId, studentId);
|
|
|
+ // } else {
|
|
|
+ // log.warn("rewritePerSentence 列表为空,跳过保存! assignmentId: {}, studentId: {}", assignmentId, studentId);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // evaluationMapperServiceImpl.update(new UpdateWrapper<Evaluation>()
|
|
|
+ // .set("sentence", 1)
|
|
|
+ // .eq("student_id", studentId)
|
|
|
+ // .eq("assignment_id", assignmentId)
|
|
|
+ // .eq("version", engagement.getVersion()));
|
|
|
+
|
|
|
+ // log.info("rewritePerSentence cost: {}", sw.formatTime());
|
|
|
+ // return sentenceEvaluationsList;
|
|
|
+
|
|
|
}
|
|
|
//原来的getSentenceList方法存在问题,无法正确处理多种标点符号和不同语言的句子分割,这里改用BreakIterator来实现更准确的句子分割
|
|
|
private static String[] getSentenceList(String content) {
|