Ver Fonte

3.4.5 update-20250417,新功能开发

xiaofei há 2 meses atrás
pai
commit
cffb9175cd
18 ficheiros alterados com 296 adições e 163 exclusões
  1. 3 3
      distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkAiQuestionParamController.java
  2. 16 0
      distributed-print/src/main/java/com/qmth/distributed/print/start/StartRunning.java
  3. 1 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/enums/LockType.java
  4. 4 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/mapper/MarkQuestionMapper.java
  5. 0 15
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/AiMarkService.java
  6. 7 2
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkQuestionService.java
  7. 1 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkService.java
  8. 1 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkSyncService.java
  9. 0 92
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/AiMarkServiceImpl.java
  10. 24 10
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkQuestionServiceImpl.java
  11. 67 21
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkServiceImpl.java
  12. 14 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkSyncServiceImpl.java
  13. 55 0
      teachcloud-mark/src/main/resources/mapper/MarkQuestionMapper.xml
  14. 3 1
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/enums/JobEnum.java
  15. 26 0
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/MarkAiJob.java
  16. 26 0
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/MarkAiQuestionOcrJob.java
  17. 4 0
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/JobService.java
  18. 44 19
      teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/impl/JobServiceImpl.java

+ 3 - 3
distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkAiQuestionParamController.java

@@ -213,9 +213,9 @@ public class MarkAiQuestionParamController {
                 default:
                     break;
             }
-            if (update) {
-                markQuestionService.updateClearOcrResult(markAiQuestionParam.getQuestionId(), true);
-            }
+//            if (update) {
+//                markQuestionService.updateClearOcrResult(markAiQuestionParam.getQuestionId(), true);
+//            }
             markAiQuestionParamService.saveOrUpdate(markAiQuestionParam);
             markQuestionService.updateAiMark(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId());
 

+ 16 - 0
distributed-print/src/main/java/com/qmth/distributed/print/start/StartRunning.java

@@ -145,6 +145,22 @@ public class StartRunning implements CommandLineRunner {
         quartzService.addJob(MarkScoreCalculateJob.class, JobEnum.MARK_SCORE_CALCULATE_JOB.name(), JobEnum.MARK_SCORE_CALCULATE_JOB.getGroupName(), "0 0/2 * * * ?", markScoreCalculateJobMap);
         log.info("服务器启动时执行,阅卷统分定时任务 end");
 
+        // 每2分钟一次
+        log.info("服务器启动时执行,考生作答OCR识别定时任务 start");
+        Map markQuestionOcrJobMap = new HashMap();
+        markQuestionOcrJobMap.computeIfAbsent("name", v -> MarkAiQuestionOcrJob.class.getName());
+        quartzService.deleteJob(JobEnum.MARK_AI_QUESTION_ORC_JOB.name(), JobEnum.MARK_AI_QUESTION_ORC_JOB.getGroupName());
+        quartzService.addJob(MarkAiQuestionOcrJob.class, JobEnum.MARK_AI_QUESTION_ORC_JOB.name(), JobEnum.MARK_AI_QUESTION_ORC_JOB.getGroupName(), "0 0/2 * * * ?", markQuestionOcrJobMap);
+        log.info("服务器启动时执行,考生作答OCR识别定时任务 end");
+
+        // 每2分钟一次
+        log.info("服务器启动时执行,AI评卷定时任务 start");
+        Map markAIJobMap = new HashMap();
+        markAIJobMap.computeIfAbsent("name", v -> MarkAiJob.class.getName());
+        quartzService.deleteJob(JobEnum.MARK_AI_JOB.name(), JobEnum.MARK_AI_JOB.getGroupName());
+        quartzService.addJob(MarkAiJob.class, JobEnum.MARK_AI_JOB.name(), JobEnum.MARK_AI_JOB.getGroupName(), "0 0/2 * * * ?", markAIJobMap);
+        log.info("服务器启动时执行,AI评卷定时任务 end");
+
         // 数据同步
         dataSync();
 

+ 1 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/enums/LockType.java

@@ -24,6 +24,7 @@ public enum LockType {
     CUSTOM_CARD_SAVE("custom_card_save"),
     CUSTOM_MODEL_FOUR_CARD_SAVE("custom_model_four_card_save"),
 	AI_TASK_RESET("ai_task_reset"),
+	OCR_MARK("ocr_mark"),
 	AI_MARK("ai_mark"), ;
 
 	private String name;

+ 4 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/mapper/MarkQuestionMapper.java

@@ -20,4 +20,8 @@ public interface MarkQuestionMapper extends BaseMapper<MarkQuestion> {
     List<MarkQuestionAnswerVo> listQuestionAnswerByExamIdAndPaperNumberAndPaperType(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("paperType") String paperType, @Param("objective") Boolean objective);
 
     int countUnBindMarkerQuestion(@Param("examId") Long examId, @Param("paperNumber") String paperNumber);
+
+    List<MarkQuestion> listUnOcrQuestion();
+
+    List<MarkQuestion> listUnMarkQuestion();
 }

+ 0 - 15
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/AiMarkService.java

@@ -1,15 +0,0 @@
-package com.qmth.teachcloud.mark.service;
-
-import com.qmth.teachcloud.common.entity.BasicSchool;
-import com.qmth.teachcloud.mark.entity.MarkOcrStudentQuestion;
-
-import java.util.List;
-
-/**
- * <p>
- * AI评卷相关 服务类
- */
-public interface AiMarkService {
-
-
-}

+ 7 - 2
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkQuestionService.java

@@ -125,7 +125,12 @@ public interface MarkQuestionService extends IService<MarkQuestion> {
     boolean enableAi(Long questionId, Boolean enableAi);
     boolean updateClearOcrResult(Long questionId, Boolean clearOcrResult);
 
-    void aiMarkByMarkPaper(MarkPaper markPaper);
-
     void updateAiMark(Long examId, String paperNumber, Long questionId);
+
+    List<MarkQuestion> listUnOcrQuestion();
+
+    void ocrMarkQuestion(MarkQuestion markQuestion);
+    void autoAiMark(MarkQuestion markQuestion);
+
+    List<MarkQuestion> listUnMarkQuestion();
 }

+ 1 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkService.java

@@ -110,6 +110,7 @@ public interface MarkService {
 
     void resetMarkedQuestionId(MarkUserQuestion markUserQuestion);
 
+    void ocrMarkQuestion(MarkQuestion markQuestion);
     void aiMark(MarkQuestion markQuestion);
 
     void resetAiTask(Long examId, String paperNumber, Long questionId);

+ 1 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkSyncService.java

@@ -21,5 +21,6 @@ public interface MarkSyncService {
 
     void aiTaskResetSync(MarkQuestion markQuestion, Long questionId);
 
+    void ocrMarkQuestion(MarkQuestion markQuestion);
     void aiAutoMark(MarkQuestion markQuestion);
 }

+ 0 - 92
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/AiMarkServiceImpl.java

@@ -1,92 +0,0 @@
-package com.qmth.teachcloud.mark.service.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.qmth.boot.core.ai.client.OcrApiClient;
-import com.qmth.boot.core.ai.model.llm.score.AutoScoreRequest;
-import com.qmth.boot.core.ai.model.llm.score.AutoScoreResult;
-import com.qmth.boot.core.ai.model.llm.score.StandardAnswer;
-import com.qmth.boot.core.ai.model.ocr.OcrType;
-import com.qmth.boot.core.ai.service.AiService;
-import com.qmth.boot.core.retrofit.utils.UploadFile;
-import com.qmth.teachcloud.common.bean.dto.mark.PictureConfig;
-import com.qmth.teachcloud.common.bean.vo.FilePathVo;
-import com.qmth.teachcloud.common.contant.SystemConstant;
-import com.qmth.teachcloud.common.entity.BasicCourse;
-import com.qmth.teachcloud.common.entity.BasicSchool;
-import com.qmth.teachcloud.common.entity.MarkQuestion;
-import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
-import com.qmth.teachcloud.common.mapper.BasicCourseMapper;
-import com.qmth.teachcloud.common.mapper.BasicSchoolMapper;
-import com.qmth.teachcloud.common.service.FileUploadService;
-import com.qmth.teachcloud.common.util.FileUtil;
-import com.qmth.teachcloud.mark.dto.ai.SheetImageDto;
-import com.qmth.teachcloud.mark.entity.*;
-import com.qmth.teachcloud.mark.enums.AiQuestionParamModeStatus;
-import com.qmth.teachcloud.mark.lock.LockService;
-import com.qmth.teachcloud.mark.service.*;
-import com.qmth.teachcloud.mark.utils.AiUtil;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.io.File;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-@Service
-public class AiMarkServiceImpl implements AiMarkService {
-
-    private final static Logger log = LoggerFactory.getLogger(AiMarkServiceImpl.class);
-
-    private Map<Long, Long> markerLastUpdateTime = new ConcurrentHashMap<>();
-
-    @Resource
-    private BasicSchoolMapper basicSchoolMapper;
-    @Resource
-    private BasicCourseMapper basicCourseMapper;
-    @Resource
-    private MarkService markService;
-    @Resource
-    private AiService aiService;
-    @Resource
-    private FileUploadService fileUploadService;
-    @Resource
-    private MarkOcrStudentQuestionService markOcrStudentQuestionService;
-    @Resource
-    private MarkQuestionService markQuestionService;
-    @Resource
-    private MarkAiQuestionParamService markAiQuestionParamService;
-    @Resource
-    private MarkAiQuestionPointService markAiQuestionPointService;
-    @Resource
-    private MarkAiQuestionLevelService markAiQuestionLevelService;
-    @Resource
-    private MarkUserQuestionService markUserQuestionService;
-    @Resource
-    private OcrApiClient ocrApiClient;
-
-    @Resource
-    private MarkTaskService markTaskService;
-
-    @Resource
-    private MarkStudentService markStudentService;
-
-    @Resource
-    private MarkProblemHistoryService markProblemHistoryService;
-
-    @Resource
-    private MarkArbitrateHistoryService markArbitrateHistoryService;
-
-    @Resource
-    private MarkSubjectiveScoreService markSubjectiveScoreService;
-    @Resource
-    LockService lockService;
-
-
-
-}

+ 24 - 10
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkQuestionServiceImpl.java

@@ -938,16 +938,6 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
         return this.update(updateWrapper);
     }
 
-    @Override
-    public void aiMarkByMarkPaper(MarkPaper markPaper) {
-        List<MarkQuestion> markQuestionList = this.listByExamIdAndPaperNumberAndObjective(markPaper.getExamId(), markPaper.getPaperNumber(), false);
-        for (MarkQuestion markQuestion : markQuestionList) {
-            if (lockService.trylock(LockType.AI_MARK, markQuestion.getId())) {
-                markSyncService.aiAutoMark(markQuestion);
-            }
-        }
-    }
-
     @Override
     public void updateAiMark(Long examId, String paperNumber, Long questionId) {
         MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
@@ -957,4 +947,28 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
                 .eq(MarkQuestion::getId, questionId);
         this.update(updateWrapper);
     }
+
+    @Override
+    public List<MarkQuestion> listUnOcrQuestion() {
+        return this.baseMapper.listUnOcrQuestion();
+    }
+
+    @Override
+    public void ocrMarkQuestion(MarkQuestion markQuestion) {
+        if (lockService.trylock(LockType.OCR_MARK, markQuestion.getId())) {
+            markSyncService.ocrMarkQuestion(markQuestion);
+        }
+    }
+
+    @Override
+    public void autoAiMark(MarkQuestion markQuestion) {
+        if (lockService.trylock(LockType.AI_MARK, markQuestion.getId())) {
+            markSyncService.aiAutoMark(markQuestion);
+        }
+    }
+
+    @Override
+    public List<MarkQuestion> listUnMarkQuestion() {
+        return this.baseMapper.listUnMarkQuestion();
+    }
 }

+ 67 - 21
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkServiceImpl.java

@@ -67,7 +67,6 @@ import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 @Service
 public class MarkServiceImpl implements MarkService {
@@ -1520,6 +1519,45 @@ public class MarkServiceImpl implements MarkService {
     }
 
 
+    @Override
+    public void ocrMarkQuestion(MarkQuestion markQuestion) {
+        Long examId = markQuestion.getExamId();
+        String paperNumber = markQuestion.getPaperNumber();
+        Long questionId = markQuestion.getId();
+
+        // 终止AI评卷
+        if (!markQuestion.getEnableAi()) {
+            return;
+        }
+        // AI评卷且未设置评卷参数
+        if (MarkPaperAiMark.NONE.equals(markQuestion.getAiMark())
+                || !markAiQuestionParamService.existMarkAiQuestionPointOrLevel(examId, paperNumber, questionId)) {
+            return;
+        }
+
+        BasicSchool basicSchool = basicSchoolMapper.selectByExamId(examId);
+        List<MarkTask> markTasks;
+        int pageNumber = 1;
+        do {
+            markTasks = markTaskService.findAiUnMarked(new Page<>(pageNumber, 20), examId, paperNumber, questionId);
+            if (markTasks.isEmpty() || !markQuestion.getEnableAi()) {
+                break;
+            }
+            for (MarkTask t : markTasks) {
+                // 考生作答
+                try {
+                    this.ocrStudentQuestionResult(basicSchool, t.getStudentId(), t.getQuestionId());
+                } catch (Exception e) {
+                    log.error("OCR识别错误:", e.getMessage());
+                }
+            }
+            pageNumber++;
+            // 获取终止评卷标记
+            markQuestion = markQuestionService.getById(questionId);
+            markTasks = markTaskService.findAiUnMarked(new Page<>(pageNumber, 20), examId, paperNumber, questionId);
+        } while (CollectionUtils.isNotEmpty(markTasks));
+    }
+
     @Override
     public void aiMark(MarkQuestion markQuestion) {
         Long examId = markQuestion.getExamId();
@@ -1556,7 +1594,10 @@ public class MarkServiceImpl implements MarkService {
                     // 考生作答
                     try {
                         long startTime = System.currentTimeMillis();
-                        List<MarkOcrStudentQuestion> markOcrStudentQuestions = this.listStudentQuestionOcrResult(basicSchool, t.getStudentId(), t.getQuestionId());
+                        List<MarkOcrStudentQuestion> markOcrStudentQuestions = markOcrStudentQuestionService.listByStudentIdAndQuestionId(t.getStudentId(), t.getQuestionId());
+                        if (CollectionUtils.isEmpty(markOcrStudentQuestions)) {
+                            continue;
+                        }
                         String studentQuestionAnswer = this.mergeQuestionOcrResult(markOcrStudentQuestions);
 
                         AutoScoreRequest request = new AutoScoreRequest();
@@ -1570,7 +1611,7 @@ public class MarkServiceImpl implements MarkService {
                         // AI评卷
                         AutoScoreResult autoScoreResult = aiService.autoScore(request, AiUtil.signatureAi(basicSchool));
                         long endTime = System.currentTimeMillis();
-                        submitAiTask(t, aiUser.getId(), markQuestion, autoScoreResult, buildTrack(autoScoreResult, markQuestion, markOcrStudentQuestions), endTime - startTime);
+                        submitAiTask(t, aiUser.getId(), markQuestion, autoScoreResult, buildTrack(markAiQuestionParam.getMode(), autoScoreResult, markQuestion, markOcrStudentQuestions), endTime - startTime);
                     } catch (Exception e) {
                         t.setAiMarkErrorMsg(e.getMessage());
                     } finally {
@@ -1632,16 +1673,20 @@ public class MarkServiceImpl implements MarkService {
         }
     }
 
-    private List<TrackDTO> buildTrack(AutoScoreResult autoScoreResult, MarkQuestion markQuestion, List<MarkOcrStudentQuestion> markOcrStudentQuestions) {
+    private List<TrackDTO> buildTrack(AiQuestionParamModeStatus mode, AutoScoreResult autoScoreResult, MarkQuestion markQuestion, List<MarkOcrStudentQuestion> markOcrStudentQuestions) {
         List<TrackDTO> list = new ArrayList<>();
         if (markOcrStudentQuestions.isEmpty()) {
             return list;
         }
         double[] doubles = autoScoreResult.getStepScore();
+        // 按档次时,stepScore中为所有档次的分值,实际使用只需要一个最终档次的值
+        if (AiQuestionParamModeStatus.LEVEL.equals(mode)) {
+            doubles = new double[]{autoScoreResult.getTotalScore()};
+        }
         // 轨迹打印在第一个评卷区上
         List<MarkConfigItem> pictureConfigList = markQuestion.getPictureConfigList();
 
-        int initXAdd = 100, initYAdd = 100, stepXAdd = 100, stepYAdd = 30;
+        int initXAdd = 100, initYAdd = 100, stepXAdd = 100, stepYAdd = 50;
         int count = 0;
         for (int i = 0; i < pictureConfigList.size(); i++) {
             MarkConfigItem markConfigItem = pictureConfigList.get(i);
@@ -1724,26 +1769,28 @@ public class MarkServiceImpl implements MarkService {
         return markOcrStudentQuestionList.stream().filter(m -> StringUtils.isNotBlank(m.getOcrContent())).map(MarkOcrStudentQuestion::getOcrContent).collect(Collectors.joining(SystemConstant.COMMA_OF_CHINESE));
     }
 
-    private List<MarkOcrStudentQuestion> listStudentQuestionOcrResult(BasicSchool basicSchool, Long studentId, Long questionId) {
+    private void ocrStudentQuestionResult(BasicSchool basicSchool, Long studentId, Long questionId) {
         List<MarkOcrStudentQuestion> markOcrStudentQuestionList = markOcrStudentQuestionService.listByStudentIdAndQuestionId(studentId, questionId);
         if (CollectionUtils.isNotEmpty(markOcrStudentQuestionList)) {
-            return markOcrStudentQuestionList;
-        }
-        MarkQuestion markQuestion = markQuestionService.getById(questionId);
-        if (markQuestion == null || StringUtils.isBlank(markQuestion.getPicList())) {
-            throw ExceptionResultEnum.ERROR.exception("大题号[" + markQuestion.getMainNumber() + "]、小题号[" + markQuestion.getSubNumber() + "]未设置评卷区");
-        }
-        // 读取考生图片
-        MarkStudent markStudent = markStudentService.getById(studentId);
-        if (markStudent == null) {
-            throw ExceptionResultEnum.ERROR.exception("未找到考生" + ",考生ID[" + studentId);
-        } else if (StringUtils.isBlank(markStudent.getSheetPath())) {
-            throw ExceptionResultEnum.ERROR.exception("未查询到原图" + ",考号[" + markStudent.getStudentCode());
+            return;
         }
         List<File> deleteFileList = new ArrayList<>();
-        Map<Integer, SheetImageDto> fileMap = new HashMap<>();
-        //考生题卡原图
         try {
+            MarkQuestion markQuestion = markQuestionService.getById(questionId);
+            if (markQuestion == null || StringUtils.isBlank(markQuestion.getPicList())) {
+                throw ExceptionResultEnum.ERROR.exception("大题号[" + markQuestion.getMainNumber() + "]、小题号[" + markQuestion.getSubNumber() + "]未设置评卷区");
+            }
+            // 读取考生图片
+            MarkStudent markStudent = markStudentService.getById(studentId);
+            if (markStudent == null) {
+                throw ExceptionResultEnum.ERROR.exception("未找到考生" + ",考生ID[" + studentId);
+            } else if (StringUtils.isBlank(markStudent.getSheetPath())) {
+                throw ExceptionResultEnum.ERROR.exception("未查询到原图" + ",考号[" + markStudent.getStudentCode());
+            }
+
+            Map<Integer, SheetImageDto> fileMap = new HashMap<>();
+            //考生题卡原图
+
             List<FilePathVo> filePathVoList = markStudent.listSheetPath();
             for (int i = 0; i < filePathVoList.size(); i++) {
                 FilePathVo filePathVo = filePathVoList.get(i);
@@ -1790,7 +1837,6 @@ public class MarkServiceImpl implements MarkService {
                 deleteFileList.add(file);
             }
             markOcrStudentQuestionService.saveBatch(markOcrStudentQuestions);
-            return markOcrStudentQuestions;
         } catch (RuntimeException e) {
             throw ExceptionResultEnum.ERROR.exception(e.getMessage());
         } finally {

+ 14 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkSyncServiceImpl.java

@@ -116,6 +116,20 @@ public class MarkSyncServiceImpl implements MarkSyncService {
         }
     }
 
+    @Async
+    @Override
+    public void ocrMarkQuestion(MarkQuestion markQuestion) {
+        try {
+            if (markQuestion != null) {
+                markService.ocrMarkQuestion(markQuestion);
+            }
+        } catch (Exception e) {
+            log.error("ai auto mark error", e);
+        } finally {
+            lockService.unlock(LockType.OCR_MARK, markQuestion.getId());
+        }
+    }
+
     @Async
     @Override
     public void aiAutoMark(MarkQuestion markQuestion) {

+ 55 - 0
teachcloud-mark/src/main/resources/mapper/MarkQuestionMapper.xml

@@ -64,4 +64,59 @@
                             AND muq.enable = 1
                             AND muq.question_id = mq.id)
     </select>
+    <select id="listUnOcrQuestion" resultType="com.qmth.teachcloud.common.entity.MarkQuestion">
+        SELECT
+            mq.*
+        FROM
+            mark_question mq
+        WHERE
+            mq.ai_mark != 'NONE'
+        AND mq.task_count > 0
+        AND mq.task_count > mq.marked_count
+        AND EXISTS( SELECT
+            1
+        FROM
+            mark_paper mp
+        WHERE
+            mq.exam_id = mp.exam_id
+                AND mq.paper_number = mp.paper_number
+                AND mp.status = 'FORMAL')
+        AND EXISTS( SELECT
+            1
+        FROM
+            mark_task et
+        WHERE
+            mq.exam_id = et.exam_id
+                AND mq.paper_number = et.paper_number
+                AND mq.id = et.question_id
+                AND et.ai_marked = 1)
+    </select>
+    <select id="listUnMarkQuestion" resultType="com.qmth.teachcloud.common.entity.MarkQuestion">
+        SELECT
+            mq.*
+        FROM
+            mark_question mq
+        WHERE
+            mq.ai_mark != 'NONE'
+        AND mq.task_count > 0
+        AND mq.task_count > mq.marked_count
+        AND EXISTS( SELECT
+            1
+        FROM
+            mark_paper mp
+        WHERE
+            mq.exam_id = mp.exam_id
+                AND mq.paper_number = mp.paper_number
+                AND mp.status = 'FORMAL')
+        AND EXISTS( SELECT
+            1
+        FROM
+            mark_task et
+        WHERE
+            mq.exam_id = et.exam_id
+                AND mq.paper_number = et.paper_number
+                AND mq.id = et.question_id
+                AND et.ai_marked = 1
+                AND et.status = 'WAITING')
+    </select>
 </mapper>

+ 3 - 1
teachcloud-task/src/main/java/com/qmth/teachcloud/task/enums/JobEnum.java

@@ -22,7 +22,9 @@ public enum JobEnum {
     INIT_MARK_DATA("INIT_MARK_DATA_GROUP", "初始化阅卷数据"),
     DATA_SYNC("DATA_SYNC_GROUP", "数据同步"),
     CLEAR_TIMEOUT_TASK("CLEAR_TIMEOUT_TASK_GROUP", "清空过期任务"),
-    MARK_SCORE_CALCULATE_JOB("MARK_SCORE_CALCULATE_JOB_GROUP", "阅卷统分定时任务");
+    MARK_SCORE_CALCULATE_JOB("MARK_SCORE_CALCULATE_JOB_GROUP", "阅卷统分定时任务"),
+    MARK_AI_QUESTION_ORC_JOB("MARK_QUESTION_ORC_JOB_GROUP", "AI评卷OCR识别"),
+    MARK_AI_JOB("MARK_AI_JOB_GROUP", "AI评卷");
 
     private String groupName;
 

+ 26 - 0
teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/MarkAiJob.java

@@ -0,0 +1,26 @@
+package com.qmth.teachcloud.task.job;
+
+import com.qmth.teachcloud.task.job.service.JobService;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+import javax.annotation.Resource;
+
+/**
+ * AI评卷OCR识别定时任务
+ */
+@DisallowConcurrentExecution//串行执行
+public class MarkAiJob extends QuartzJobBean {
+    private final static Logger log = LoggerFactory.getLogger(MarkAiJob.class);
+
+    @Resource
+    JobService jobService;
+
+    @Override
+    protected void executeInternal(JobExecutionContext jobExecutionContext) {
+        jobService.autoAiMark();
+    }
+}

+ 26 - 0
teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/MarkAiQuestionOcrJob.java

@@ -0,0 +1,26 @@
+package com.qmth.teachcloud.task.job;
+
+import com.qmth.teachcloud.task.job.service.JobService;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+import javax.annotation.Resource;
+
+/**
+ * AI评卷OCR识别定时任务
+ */
+@DisallowConcurrentExecution//串行执行
+public class MarkAiQuestionOcrJob extends QuartzJobBean {
+    private final static Logger log = LoggerFactory.getLogger(MarkAiQuestionOcrJob.class);
+
+    @Resource
+    JobService jobService;
+
+    @Override
+    protected void executeInternal(JobExecutionContext jobExecutionContext) {
+        jobService.ocrMarkQuestion();
+    }
+}

+ 4 - 0
teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/JobService.java

@@ -58,4 +58,8 @@ public interface JobService {
      * 统分定时任务
      */
     void markScoreCalculate();
+
+    void ocrMarkQuestion();
+
+    void autoAiMark();
 }

+ 44 - 19
teachcloud-task/src/main/java/com/qmth/teachcloud/task/job/service/impl/JobServiceImpl.java

@@ -1,20 +1,5 @@
 package com.qmth.teachcloud.task.job.service.impl;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import javax.annotation.Resource;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.qmth.boot.redis.uid.RedisMachineService;
 import com.qmth.distributed.print.business.bean.dto.initMarkData.ExamDetailCourseInitMarkDto;
@@ -44,6 +29,18 @@ import com.qmth.teachcloud.mark.lock.LockService;
 import com.qmth.teachcloud.mark.service.*;
 import com.qmth.teachcloud.mark.utils.TaskLockUtil;
 import com.qmth.teachcloud.task.job.service.JobService;
+import org.apache.commons.collections4.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @Description: job service impl
@@ -155,10 +152,6 @@ public class JobServiceImpl implements JobService {
             for (MarkPaper markPaper : markPaperList) {
                 // 生成评卷任务
                 markService.buildMarkTask(markPaper);
-
-                // AI评卷任务自动评卷
-                markQuestionService.aiMarkByMarkPaper(markPaper);
-
                 // 给评卷员发送短信
                 basicMessageService.smsMarkTask(markPaper.getExamId(), markPaper.getCourseId(), markPaper.getPaperNumber());
             }
@@ -277,6 +270,38 @@ public class JobServiceImpl implements JobService {
         }
     }
 
+    @Override
+    public void ocrMarkQuestion() {
+        log.info("start auto ocr_question");
+        try {
+            List<MarkQuestion> markQuestionList = markQuestionService.listUnOcrQuestion();
+            for (MarkQuestion markQuestion : markQuestionList) {
+                // AI评卷任务自动评卷
+                markQuestionService.ocrMarkQuestion(markQuestion);
+            }
+        } catch (Exception e) {
+            log.error("auto ocr_question error", e);
+        } finally {
+            log.info("finish auto ocr_question");
+        }
+    }
+
+    @Override
+    public void autoAiMark() {
+        log.info("start auto ai_question");
+        try {
+            List<MarkQuestion> markQuestionList = markQuestionService.listUnMarkQuestion();
+            for (MarkQuestion markQuestion : markQuestionList) {
+                // AI评卷任务自动评卷
+                markQuestionService.autoAiMark(markQuestion);
+            }
+        } catch (Exception e) {
+            log.error("auto ai_question error", e);
+        } finally {
+            log.info("finish auto ai_question");
+        }
+    }
+
     /**
      * 组装job
      *