浏览代码

3.4.5 update-20250414,新功能开发

xiaofei 2 月之前
父节点
当前提交
c2700ac9b8

+ 5 - 0
pom.xml

@@ -189,6 +189,11 @@
                 <artifactId>data-upgrade</artifactId>
                 <version>${qmth.boot.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.qmth.boot</groupId>
+                <artifactId>core-ai</artifactId>
+                <version>${qmth.boot.version}</version>
+            </dependency>
 <!--            <dependency>-->
 <!--                <groupId>io.springfox</groupId>-->
 <!--                <artifactId>springfox-swagger-ui</artifactId>-->

+ 4 - 0
teachcloud-common/pom.xml

@@ -62,6 +62,10 @@
             <groupId>com.qmth.boot</groupId>
             <artifactId>data-upgrade</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>core-ai</artifactId>
+        </dependency>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>

+ 2 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/mapper/BasicCourseMapper.java

@@ -61,4 +61,6 @@ public interface BasicCourseMapper extends BaseMapper<BasicCourse> {
     int countExamTaskByCourseId(Long courseId);
 
     void deleteTeachCourseByCourseIds(@Param("idList") List<Long> idList);
+
+    BasicCourse selectByExamIdAndPaperNumber(@Param("examId") Long examId, @Param("paperNumber") String paperNumber);
 }

+ 2 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/mapper/BasicSchoolMapper.java

@@ -17,4 +17,6 @@ import java.util.List;
 public interface BasicSchoolMapper extends CustomBaseMapper<BasicSchool> {
 
     List<BasicSchool> listSchool(@Param("code") String code);
+
+    BasicSchool selectByExamId(@Param("examId") Long examId);
 }

+ 3 - 0
teachcloud-common/src/main/resources/mapper/BasicCourseMapper.xml

@@ -165,4 +165,7 @@
     <select id="countExamTaskByCourseId" resultType="java.lang.Integer">
         select count(1) from exam_task where course_id = #{courseId}
     </select>
+    <select id="selectByExamIdAndPaperNumber" resultType="com.qmth.teachcloud.common.entity.BasicCourse">
+        select * from basic_course where id = (select course_id from mark_paper where exam_id = #{examId} and paper_number = #{paperNumber})
+    </select>
 </mapper>

+ 4 - 0
teachcloud-common/src/main/resources/mapper/BasicSchoolMapper.xml

@@ -28,5 +28,9 @@
         </if>
         order by create_time desc
     </select>
+    <select id="selectByExamId" resultType="com.qmth.teachcloud.common.entity.BasicSchool"
+            parameterType="java.lang.Long">
+        select * from basic_school bs where bs.id = (select school_id from basic_exam where id = #{examId})
+    </select>
 
 </mapper>

+ 61 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/ai/SheetImageDto.java

@@ -0,0 +1,61 @@
+package com.qmth.teachcloud.mark.dto.ai;
+
+import java.io.File;
+
+public class SheetImageDto {
+
+    private int i;
+    private File file;
+    private String md5;
+
+    private int width;
+    private int height;
+
+    public SheetImageDto(int i, File file, String md5, int[] ints) {
+        this.i = i;
+        this.file = file;
+        this.md5 = md5;
+        this.width = ints[0];
+        this.height = ints[1];
+    }
+
+    public int getI() {
+        return i;
+    }
+
+    public void setI(int i) {
+        this.i = i;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    public String getMd5() {
+        return md5;
+    }
+
+    public void setMd5(String md5) {
+        this.md5 = md5;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+}

+ 5 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/MarkStudent.java

@@ -22,6 +22,7 @@ import org.apache.commons.lang3.StringUtils;
 
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -771,6 +772,10 @@ public class MarkStudent implements Serializable {
         this.missScan = missScan;
     }
 
+    public List<FilePathVo> listSheetPath() {
+        return StringUtils.isNotBlank(this.sheetPath) ? JSON.parseArray(this.sheetPath, FilePathVo.class) : Collections.emptyList();
+    }
+
     @Override
     public String toString() {
         return "MarkStudent{" +

+ 4 - 97
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/AiMarkService.java

@@ -1,21 +1,9 @@
 package com.qmth.teachcloud.mark.service;
 
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.qmth.teachcloud.common.entity.MarkQuestion;
-import com.qmth.teachcloud.common.entity.SysUser;
-import com.qmth.teachcloud.mark.dto.mark.manage.Task;
-import com.qmth.teachcloud.mark.dto.mark.mark.MarkSettingDto;
-import com.qmth.teachcloud.mark.dto.mark.mark.MarkStatusDto;
-import com.qmth.teachcloud.mark.dto.mark.mark.SubmitResult;
-import com.qmth.teachcloud.mark.entity.*;
-import com.qmth.teachcloud.mark.enums.QuestionModel;
-import com.qmth.teachcloud.mark.params.MarkArbitrateResult;
-import com.qmth.teachcloud.mark.params.MarkResult;
-import com.qmth.teachcloud.mark.params.MarkResultQuestion;
-import io.lettuce.core.GeoArgs.Sort;
+import com.qmth.teachcloud.common.entity.BasicSchool;
+import com.qmth.teachcloud.mark.entity.MarkOcrStudentQuestion;
 
 import java.util.List;
-import java.util.Set;
 
 /**
  * <p>
@@ -23,88 +11,7 @@ import java.util.Set;
  */
 public interface AiMarkService {
 
-    void releaseStudent(Long examId, String paperNumber, Long studentId, Long userId);
-
-    int applyCurrentCount(MarkQuestion markQuestion);
-
-    Set<Long> listCurrentStudent(Long examId, String paperNumber);
-
-    void releaseByMarkUserGroup(MarkUserQuestion markUserGroup);
-
-    int applyCurrentCount(MarkQuestion markQuestion, Long markUserGroupId);
-
-    void resetMarker(MarkUserQuestion markUserGroup);
-
-    void updateMarkedCount(Long examId, String paperNumber, Long questionId);
-
-    void updatePersonTask(Long examId, String paperNumber, Long questionId);
-
-    boolean rejectMarkTask(MarkProblemHistory markProblemHistory, MarkTask markTask, Long userId);
-
-    String getGroupKey(MarkQuestion markQuestion);
-
-    void updateQuality(MarkUserQuestion markUserGroup);
-
-    boolean needUpdateQuality(MarkUserQuestion marker, int expireMinutes);
-
-    void processArbitrate(MarkArbitrateResult markResult, Long userId);
-
-    void checkStudentSubjective(Long studentId, Long examId, String paperNumber, Integer version, boolean autoCalc);
-
-    void buildMarkTask(MarkPaper markPaper);
-
-    void deleteMarkTaskByStudent(MarkStudent student);
-
-    void updateGroupAllCount(Long examId, String paperNumber);
-
-    MarkSettingDto getSetting(SysUser user, Long examId, String paperNumber);
-
-    List<MarkStatusDto> getStatus(Long userId, Long examId, String paperNumber, QuestionModel questionModel, Long questionId);
-
-    Task getTask(Long userId, Long examId, String paperNumber, QuestionModel questionModel, Long questionId);
-
-    /**
-     * /** 释放某个大题的锁定任务
-     */
-//    void releaseByMarkGroup(MarkGroup markGroup);
-
-    void releaseByStudent(MarkStudent student);
-
-    /**
-     * 申请领取某个任务
-     */
-    boolean applyStudent(MarkStudent student, Long userId);
-
-    void submitHeaderTask(List<MarkResultQuestion> markResult, MarkStudent markUserGroup);
-
-    IPage<Task> getHistory(Long userId, int pageNumber, int pageSize, Sort sort, String order, Long examId, String paperNumber, String secretNumber, Double markerScore);
-
-    SubmitResult saveTask(Long examId, String paperNumber, Long userId, MarkResult markResult);
-
-    void clear(Long userId, Long examId, String paperNumber);
-
-    boolean applyTask(Long examId, String paperNumber, Long studentId, Long userId, Set<Long> questions, List<Long> taskIds);
-
-    boolean applyTask(Long examId, String paperNumber, Long studentId, Long userId, Set<Long> questions);
-
-    boolean hasApplied(MarkTask t, Long userId);
-
-    void deleteInitMarkData(Long examId, String paperNumber);
-
-    void calcObjectiveScore(MarkPaper markPaper);
-
-    void checkStudentSubjectiveScore(Long examId, String coursePaperId);
-
-    boolean rejectMarkTask(MarkTask markTask, Long userId, String reason);
-
-    void deleteMarkTask(MarkQuestion markQuestion, boolean b);
-
-    /**
-     * /** 释放整修科目的锁定任务
-     */
-    void releaseByMarkQuestion(MarkQuestion markQuestion);
-
-    void resetMarkedQuestionId(MarkUserQuestion markUserQuestion);
-
     void aiMark(Long examId, String paperNumber, Long questionId);
+
+    List<MarkOcrStudentQuestion> listStudentQuestionOcrResult(BasicSchool basicSchool, Long studentId, Long questionId);
 }

+ 3 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkOcrStudentQuestionService.java

@@ -3,6 +3,8 @@ package com.qmth.teachcloud.mark.service;
 import com.qmth.teachcloud.mark.entity.MarkOcrStudentQuestion;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
+
 /**
  * <p>
  * 考生主观题小题OCR识别结果 服务类
@@ -13,4 +15,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface MarkOcrStudentQuestionService extends IService<MarkOcrStudentQuestion> {
 
+    List<MarkOcrStudentQuestion> listByStudentIdAndQuestionId(Long studentId, Long questionId);
 }

+ 169 - 1433
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/AiMarkServiceImpl.java

@@ -1,53 +1,41 @@
 package com.qmth.teachcloud.mark.service.impl;
 
 import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.qmth.boot.api.exception.ApiException;
+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.entity.SysUser;
 import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
-import com.qmth.teachcloud.common.enums.ScorePolicy;
-import com.qmth.teachcloud.common.enums.mark.*;
-import com.qmth.teachcloud.common.service.BasicCourseService;
-import com.qmth.teachcloud.common.service.BasicOperationLogService;
-import com.qmth.teachcloud.common.service.SysUserService;
-import com.qmth.teachcloud.common.util.ServletUtil;
-import com.qmth.teachcloud.mark.dto.mark.ScoreItem;
-import com.qmth.teachcloud.mark.dto.mark.manage.Task;
-import com.qmth.teachcloud.mark.dto.mark.mark.MarkSettingDto;
-import com.qmth.teachcloud.mark.dto.mark.mark.MarkStatusDto;
-import com.qmth.teachcloud.mark.dto.mark.mark.SubmitResult;
+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.ExamType;
-import com.qmth.teachcloud.mark.enums.LockType;
-import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
-import com.qmth.teachcloud.mark.enums.QuestionModel;
+import com.qmth.teachcloud.mark.enums.AiQuestionParamModeStatus;
 import com.qmth.teachcloud.mark.lock.LockService;
-import com.qmth.teachcloud.mark.params.MarkArbitrateResult;
-import com.qmth.teachcloud.mark.params.MarkResult;
-import com.qmth.teachcloud.mark.params.MarkResultQuestion;
 import com.qmth.teachcloud.mark.service.*;
-import com.qmth.teachcloud.mark.utils.BigDecimalUtils;
-import com.qmth.teachcloud.mark.utils.TaskLock;
-import com.qmth.teachcloud.mark.utils.TaskLockUtil;
-import io.lettuce.core.GeoArgs.Sort;
+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 org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.math.BigDecimal;
+import java.io.File;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 @Service
@@ -58,18 +46,29 @@ public class AiMarkServiceImpl implements AiMarkService {
     private Map<Long, Long> markerLastUpdateTime = new ConcurrentHashMap<>();
 
     @Resource
-    private BasicCourseService basicCourseService;
+    private BasicSchoolMapper basicSchoolMapper;
     @Resource
-    private MarkPaperService markPaperService;
-
+    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 MarkUserPaperService markUserPaperService;
+    private MarkAiQuestionParamService markAiQuestionParamService;
+    @Resource
+    private MarkAiQuestionPointService markAiQuestionPointService;
+    @Resource
+    private MarkAiQuestionLevelService markAiQuestionLevelService;
     @Resource
     private MarkUserQuestionService markUserQuestionService;
     @Resource
-    private MarkUserClassService markUserClassService;
+    private OcrApiClient ocrApiClient;
 
     @Resource
     private MarkTaskService markTaskService;
@@ -88,1426 +87,163 @@ public class AiMarkServiceImpl implements AiMarkService {
     @Resource
     LockService lockService;
 
-    @Resource
-    ScanAnswerCardService scanAnswerCardService;
-
-    @Resource
-    TaskService taskService;
-
-    @Resource
-    private MarkHeaderHistoryService markHeaderHistoryService;
-    @Resource
-    private SysUserService sysUserService;
-    @Resource
-    private MarkRejectHistoryService markRejectHistoryService;
-    @Resource
-    private BasicOperationLogService basicOperationLogService;
-    @Resource
-    private MarkAiQuestionParamService markAiQuestionParamService;
-
-    /**
-     * 释放某个评卷员的考生
-     *
-     * @param exmId       考试ID
-     * @param paperNumber 试卷编号
-     * @param studentId   考生ID
-     * @param userId      用户ID
-     */
-    @Override
-    public void releaseStudent(Long exmId, String paperNumber, Long studentId, Long userId) {
-        if (studentId != null) {
-            TaskLock taskLock = TaskLockUtil.getFormalTask(getKey(exmId, paperNumber));
-            taskLock.remove(studentId, userId);
-            taskLock.refresh(userId);
-        }
-    }
-
-    /**
-     * 某个评卷分组已申请的评卷任务数量
-     *
-     * @return
-     */
-    @Override
-    public int applyCurrentCount(MarkQuestion markQuestion) {
-        TaskLock taskLock = getTaskLock(markQuestion);
-        int count = 0;
-        if (taskLock != null) {
-            count = taskLock.count(markQuestion.getId());
-        }
-        return count;
-    }
-
-    @Override
-    public Set<Long> listCurrentStudent(Long examId, String paperNumber) {
-        TaskLock taskLock = TaskLockUtil.getFormalTask(examId + "_" + paperNumber);
-        return taskLock.list().stream().map(m -> Long.valueOf(m.get("studentId").toString())).collect(Collectors.toSet());
-    }
-
-    private TaskLock getTaskLock(MarkQuestion markQuestion) {
-        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(markQuestion.getExamId(),
-                markQuestion.getPaperNumber());
-        if (markPaper.getStatus() == MarkPaperStatus.FORMAL) {
-            return TaskLockUtil.getFormalTask(getKey(markQuestion.getExamId(), markQuestion.getPaperNumber()));
-        }
-        return null;
-    }
-
-    /**
-     * 释放某个评卷员的所有锁定任务
-     *
-     * @param markUserQuestion
-     */
-    @Override
-    public void releaseByMarkUserGroup(MarkUserQuestion markUserQuestion) {
-        TaskLock taskLock = TaskLockUtil.getFormalTask(getKey(markUserQuestion.getExamId(), markUserQuestion.getPaperNumber()));
-        taskLock.clear(markUserQuestion.getUserId());
-    }
-
-    @Override
-    public int applyCurrentCount(MarkQuestion markQuestion, Long userId) {
-        int count = 0;
-        TaskLock taskLock = getTaskLock(markQuestion);
-        if (taskLock != null) {
-            count = taskLock.count(markQuestion.getId(), userId);
-        }
-        return count;
-    }
-
-    @Override
-    public void resetMarker(MarkUserQuestion markUserQuestion) {
-        Long examId = markUserQuestion.getExamId();
-        String paperNumber = markUserQuestion.getPaperNumber();
-        Long questionId = markUserQuestion.getQuestionId();
-        Long userId = markUserQuestion.getUserId();
-        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
-        if (markPaper == null || markUserQuestionService.getById(markUserQuestion.getId()) == null) {
-            return;
-        }
-        if (markPaper.getStatus() == MarkPaperStatus.FORMAL) {
-            // 遍历相关评卷任务的模式
-            List<MarkTaskStatus> statusList = Arrays.asList(MarkTaskStatus.WAITING);
-            List<MarkTask> markTaskList = markTaskService.listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndStatusNotIn(examId, paperNumber, questionId, userId, statusList);
-            for (MarkTask markTask : markTaskList) {
-                Long studentId = markTask.getStudentId();
-                if (MarkTaskStatus.ARBITRATED.equals(markTask.getStatus()) || MarkTaskStatus.WAIT_ARBITRATE.equals(markTask.getStatus())) {
-                    markTaskService.resetArbitrateStatusByStudentIdAndQuestionIdAndTaskNumber(studentId, questionId, markTask.getTaskNumber());
-                }
-                markTaskService.resetById(markTask.getId(), null, null, null, null, MarkTaskStatus.WAITING);
-                markSubjectiveScoreService.deleteByStudentIdAndQuestionId(studentId, questionId);
-                markProblemHistoryService.deleteByExamIdAndPaperNumberAndTaskId(examId, paperNumber, markTask.getId());
-                markRejectHistoryService.deleteByTaskId(markTask.getId());
-                markArbitrateHistoryService.deleteByExamIdAndPaperNumberAndStudentIdAndQuestionId(examId, paperNumber, studentId, questionId);
-                lockService.waitlock(LockType.STUDENT, markTask.getStudentId());
-                markStudentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK);
-                lockService.unlock(LockType.STUDENT, markTask.getStudentId());
-            }
-            markUserQuestionService.resetById(markUserQuestion.getId());
-
-        }
-        this.updateMarkedCount(examId, paperNumber, questionId);
-        releaseByMarkUserGroup(markUserQuestion);
-    }
-
-    @Override
-    public void updateMarkedCount(Long examId, String paperNumber, Long questionId) {
-        List<MarkTaskStatus> markTaskStatuses = Arrays.asList(MarkTaskStatus.MARKED, MarkTaskStatus.ARBITRATED);
-        //查询已评和已仲裁的任务数
-        int count = markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndStatusIn(examId, paperNumber,
-                questionId, markTaskStatuses);
-        //更新当前小题的已评数量
-        markQuestionService.updateMarkedCount(questionId, count);
-    }
-
-    @Override
-    public void updatePersonTask(Long examId, String paperNumber, Long questionId) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId)
-                .last(" limit 1");
-        MarkTask markTask = markTaskService.getOne(queryWrapper);
-
-        UpdateWrapper<MarkQuestion> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().set(MarkQuestion::getPersonTask, markTask != null)
-                .eq(MarkQuestion::getId, questionId);
-        markQuestionService.update(updateWrapper);
-    }
-
-    @Override
-    public boolean rejectMarkTask(MarkProblemHistory markProblemHistory, MarkTask markTask, Long userId) {
-        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(markTask.getExamId(), markTask.getPaperNumber());
-        if (markPaper.getStatus() == MarkPaperStatus.FINISH) {
-            return false;
-        }
-        Long now = System.currentTimeMillis();
-        if (markTaskService.resetById(markTask.getId(), null, null, userId, now, MarkTaskStatus.WAITING)) {
-            resetStudentStatus(markTask.getStudentId());
-            markProblemHistoryService.resetByMarkProblemId(markProblemHistory.getId(), MarkProblemStatus.WAITING,
-                    userId, MarkProblemStatus.BACK, now);
-            updateMarkedCount(markTask.getExamId(), markTask.getPaperNumber(), markTask.getQuestionId());
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private void resetStudentStatus(Long studentId) {
-        markStudentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK);
-    }
-
-
-    @Override
-    public String getGroupKey(MarkQuestion markQuestion) {
-        return getKey(markQuestion.getExamId(), markQuestion.getPaperNumber());
-    }
-
-    @Override
-    public void updateQuality(MarkUserQuestion markUserQuestion) {
-        List<MarkTask> list = markTaskService.listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(
-                markUserQuestion.getExamId(), markUserQuestion.getPaperNumber(), markUserQuestion.getQuestionId(),
-                markUserQuestion.getUserId(), null);
-        int finishCount = 0;
-        int validCount = 0;
-        int headerFinishCount = 0;
-        double sumScore = 0;
-        double sumScore2 = 0;
-        double avgScore = 0;
-        double stdevScore = 0;
-        double sumSpent = 0;
-        double avgSpent = 0;
-        double maxScore = 0;
-        double minScore = 0;
-        for (MarkTask markTask : list) {
-            if (!markTask.getStatus().equals(MarkTaskStatus.PROBLEM)) {
-                finishCount++;
-            }
-            if (markTask.getStatus() == MarkTaskStatus.MARKED && markTask.getHeaderScore() == null) {
-                validCount++;
-            }
-            // 管理员打分数量(主观题检查,仲裁)
-            if ((markTask.getStatus() == MarkTaskStatus.MARKED || markTask.getStatus() == MarkTaskStatus.ARBITRATED) && markTask.getHeaderScore() != null) {
-                headerFinishCount++;
-            }
-            double score = markTask.getMarkerScore() != null ? markTask.getMarkerScore() : 0;
-            int spent = markTask.getMarkerSpent() != null ? markTask.getMarkerSpent() : 0;
-
-            sumScore += score;
-            sumScore2 += Math.pow(score, 2);
-            sumSpent += spent;
-            if (finishCount > 0) {
-                avgScore = sumScore / finishCount;
-                avgSpent = sumSpent / finishCount;
-                // 递归法计算标准差
-                stdevScore = Math.sqrt(sumScore2 / finishCount - Math.pow(sumScore / finishCount, 2));
-            }
-
-            // 最高分
-            if (score > maxScore) {
-                maxScore = score;
-            }
-            // 最低分
-            if (finishCount == 1) {
-                minScore = score;
-            } else if (score < minScore) {
-                minScore = score;
-            }
-        }
-        markUserQuestionService.updateQualityById(markUserQuestion.getId(), finishCount, headerFinishCount, validCount,
-                avgSpent / 1000, avgScore, stdevScore, maxScore, minScore);
-        markerLastUpdateTime.put(markUserQuestion.getId(), System.currentTimeMillis());
-    }
-
-    @Override
-    public boolean needUpdateQuality(MarkUserQuestion markUserQuestion, int expireMinutes) {
-        if (markUserQuestion == null) {
-            return false;
-        }
-        Long lastUpdateTime = markerLastUpdateTime.get(markUserQuestion.getId());
-        MarkTask markTask = markTaskService.getLastOneByUserIdAndStatus(markUserQuestion.getExamId(),
-                markUserQuestion.getPaperNumber(), markUserQuestion.getQuestionId(), markUserQuestion.getUserId(),
-                MarkTaskStatus.MARKED);
-        if (markTask != null && markTask.getMarkerTime() != null) {
-            long lastMarkTime = markTask.getMarkerTime();
-            if (lastUpdateTime != null && lastUpdateTime > lastMarkTime) {
-                return false;
-            } else {
-                return (System.currentTimeMillis() - lastMarkTime) < (expireMinutes * 60 * 1000);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 管理员/组长处理仲裁卷
-     *
-     * @param markResult
-     * @param userId
-     */
-    @Transactional
     @Override
-    public void processArbitrate(MarkArbitrateResult markResult, Long userId) {
-        MarkArbitrateHistory markArbitrateHistory = markArbitrateHistoryService.getById(markResult.getArbitrateId());
-        markArbitrateHistory.setUpdateUserId(userId);
-        markArbitrateHistory.setTotalScore(markResult.getMarkerScore());
-//        markArbitrateHistory.setScoreList(markResult.isUnselective() ? null : markResult.getScoreList());
-        markArbitrateHistory.setStatus(MarkArbitrateStatus.MARKED);
-        markArbitrateHistory.setUpdateTime(System.currentTimeMillis());
-//        // 保存阅卷轨迹
-//        if (markResult.getTrackList() != null && !markResult.isUnselective()) {
-//            markHeaderTrackService.deleteByExamIdAndPaperNumberAndGroupNumberAndStudentId(
-//                    markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(),
-//                    markArbitrateHistory.getGroupNumber(), markArbitrateHistory.getStudentId());
-//            List<MarkHeaderTrack> tracks = markResult.getTrackList(markArbitrateHistory);
-//            markHeaderTrackService.saveOrUpdateBatchByMultiId(tracks);
-//        }
-//        // 保存特殊标记
-//        if (markResult.getSpecialTagList() != null) {
-//            markHeaderTagService.deleteByStudentIdAndGroupNumber(markArbitrateHistory.getStudentId(),
-//                    markArbitrateHistory.getGroupNumber());
-//            markHeaderTagService.saveBatch(markResult.getHeaderTagList(markArbitrateHistory));
-//        }
-        markArbitrateHistoryService.saveOrUpdate(markArbitrateHistory);
-        markTaskService.updateHeaderResult(markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(),
-                markArbitrateHistory.getQuestionId(), markArbitrateHistory.getStudentId(),
-                markArbitrateHistory.getUpdateUserId(), markResult.getMarkerScore(), markResult.getMarkerTrackList(), markResult.getMarkerTagList(), markArbitrateHistory.getUpdateTime(), MarkTaskStatus.ARBITRATED);
-        updateMarkedCount(markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(),
-                markArbitrateHistory.getQuestionId());
-        MarkQuestion markQuestion = markQuestionService.getById(markArbitrateHistory.getQuestionId());
-        markQuestion.setMarkScore(markResult.getMarkerScore());
-        checkStudentQuestion(markArbitrateHistory.getStudentId(), markQuestion);
-        // 评卷质量重新统计
-        List<MarkUserQuestion> markUserGroups = markUserQuestionService.listByExamIdAndPaperNumberAndQuestionId(markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(), markArbitrateHistory.getQuestionId());
-        markUserGroups.forEach(m -> this.updateQuality(m));
-    }
-
-    /**
-     * 考生分组判断是否评卷完成,以及后续的统一处理动作
-     *
-     * @param studentId
-     * @param markQuestion
-     */
-    private void checkStudentQuestion(Long studentId, MarkQuestion markQuestion) {
-        if (calculateQuestion(markQuestion, studentId)) {
-            //更新考生分组分数
-            updateStudentQuestionScore(studentId, markQuestion, markQuestion.getMarkScore());
-//            checkStudentSubjective(studentId, markQuestion.getExamId(), markQuestion.getPaperNumber(), version);
-        } else {
-            markStudentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK);
-        }
-    }
-
-    private void checkStudentQuestionForInspect(MarkStudent markStudent, MarkQuestion markQuestion) {
-        Long studentId = markStudent.getId();
-        if (calculateQuestion(markQuestion, studentId)) {
-            //更新考生分组分数
-            updateStudentQuestionScore(studentId, markQuestion, markQuestion.getMarkScore());
-            checkStudentSubjective(studentId, markQuestion.getExamId(), markQuestion.getPaperNumber(), markStudent.getVersion(), false);
-        } else {
-            markStudentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK);
-        }
-    }
-
-    @Override
-    @Transactional
-    public void checkStudentSubjective(Long studentId, Long examId, String paperNumber, Integer version, boolean autoCalc) {
-        try {
-            List<MarkQuestion> markQuestionList = markQuestionService.listByExamIdAndPaperNumberAndObjective(examId, paperNumber, false);
-            List<MarkSubjectiveScore> markSubjectiveScoreList = markSubjectiveScoreService.listByStudentIdAndExamIdAndPn(studentId, examId, paperNumber);
-            //校验阅卷题目和评分题目数量和题型是否一致
-            if (CollectionUtils.isNotEmpty(markQuestionList) && CollectionUtils.isNotEmpty(markSubjectiveScoreList)
-                    && this.megreQuestion(markQuestionList, markSubjectiveScoreList)) {
-                scoreCalculate(studentId, version, autoCalc);
+    public void aiMark(Long examId, String paperNumber, Long questionId) {
+        BasicSchool basicSchool = basicSchoolMapper.selectByExamId(examId);
+        BasicCourse basicCourse = basicCourseMapper.selectByExamIdAndPaperNumber(examId, paperNumber);
+        MarkQuestion markQuestion = markQuestionService.getById(questionId);
+        MarkAiQuestionParam markAiQuestionParam = markAiQuestionParamService.getByExamIdAndPaperNumberAndQuestionId(examId, paperNumber, questionId);
+        List<MarkTask> markTasks = null;
+        int pageNumber = 1;
+        do {
+            Set<Long> questions = new HashSet<>(Arrays.asList(questionId));
+            markTasks = markTaskService.findAiUnMarked(new Page<>(pageNumber, 20), examId, paperNumber, questionId);
+            if (markTasks.isEmpty()) {
+                break;
             }
-        } catch (Exception e) {
-            log.error(SystemConstant.LOG_ERROR, e);
-            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
-        }
-    }
-
-    /**
-     * 题型匹配
-     *
-     * @param markQuestionList
-     * @param markSubjectiveScoreList
-     * @return
-     */
-    protected boolean megreQuestion(List<MarkQuestion> markQuestionList, List<MarkSubjectiveScore> markSubjectiveScoreList) {
-        boolean calculate = true;
-        Map<String, MarkQuestion> markQuestionMap = markQuestionList.stream().collect(Collectors.toMap(k -> k.getMainNumber() + "-" + k.getSubNumber(), Function.identity(), (dto1, dto2) -> dto1));
-        Map<String, MarkSubjectiveScore> markSubjectiveScoreMap = markSubjectiveScoreList.stream().collect(Collectors.toMap(k -> k.getMainNumber() + "-" + k.getSubNumber(), Function.identity(), (dto1, dto2) -> dto1));
-        if (markQuestionMap.size() != markSubjectiveScoreMap.size()) {
-            calculate = false;
-        }
-        if (calculate) {
-            for (Map.Entry<String, MarkQuestion> entry : markQuestionMap.entrySet()) {
-                if (!markSubjectiveScoreMap.containsKey(entry.getKey())) {
-                    calculate = false;
+            for (MarkTask t : markTasks) {
+                if (markService.applyTask(examId, paperNumber, t.getStudentId(), null, questions, Arrays.asList(t.getId()))) {
                     break;
                 }
-            }
-        }
-        return calculate;
-    }
+                // 考生作答
+                try {
+                    String studentQuestionAnswer = this.mergeQuestionOcrResult(this.listStudentQuestionOcrResult(basicSchool, t.getStudentId(), t.getQuestionId()));
 
-    @Override
-    public void buildMarkTask(MarkPaper markPaper) {
-        try {
-            lockService.watch(LockType.EXAM_SUBJECT, markPaper.getExamId(), markPaper.getPaperNumber());
-            log.info("start create mark_task for examId=" + markPaper.getExamId() + ", paperNumber="
-                    + markPaper.getPaperNumber());
-            // 清除缺考考生和违纪考生(违纪考生参与阅卷,update by 2024-10-17)
-            List<MarkStudent> markStudentList = markStudentService
-                    .listAbsentOrBreachMarkTaskStudent(markPaper.getExamId(), markPaper.getPaperNumber());
-            if (CollectionUtils.isNotEmpty(markStudentList)) {
-                for (MarkStudent student : markStudentList) {
-                    try {
-                        lockService.waitlock(LockType.STUDENT, student.getId());
-                        log.info("delete mark_task for studentId=" + student.getId());
-                        this.deleteMarkTaskByStudent(student);
-                    } catch (Exception e) {
-                        log.error("delete student mark_task error", e);
-                    } finally {
-                        lockService.unlock(LockType.STUDENT, student.getId());
-                    }
-                }
-            }
-            // 处理正常考生
-            List<MarkQuestion> markQuestionList = markQuestionService.listByExamIdAndPaperNumberAndObjective(markPaper.getExamId(), markPaper.getPaperNumber(), false);
-            for (MarkQuestion markQuestion : markQuestionList) {
-                if (!MarkPaperAiMark.NONE.equals(markQuestion.getAiMark()) && !markAiQuestionParamService.existMarkAiQuestionPointOrLevel(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId())) {
-                    continue;
-                }
-                // 生成正评任务
-                buildFormalTask(markQuestion);
-                if (markQuestion.getDoubleRate() != null && markQuestion.getDoubleRate() > 0) {
-                    // 补双评任务
-                    fillFormalTask(markQuestion);
-                }
-            }
-        } finally {
-            lockService.unwatch(LockType.EXAM_SUBJECT, markPaper.getExamId(), markPaper.getPaperNumber());
-        }
-    }
+                    AutoScoreRequest request = new AutoScoreRequest();
+                    request.setSubjectName(basicCourse.getName());
+                    request.setTotalScore(markQuestion.getTotalScore());
+                    request.setIntervalScore(markAiQuestionParam.getMinScore());
+                    request.setQuestionBody(markAiQuestionParam.getMainTitle());
+                    request.setStudentAnswer(studentQuestionAnswer);
+                    request.setStandardAnswer(buildStandardAnswer(markAiQuestionParam));
+                    // AI评卷
+                    AutoScoreResult autoScoreResult = aiService.autoScore(request, AiUtil.signature(basicSchool));
 
-    private void buildFormalTask(MarkQuestion markQuestion) {
-        int pageSize = 100;
-        try {
-            lockService.watch(LockType.QUESTION, markQuestion.getId());
-            // 上锁后重复验证分组状态
-            MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(markQuestion.getExamId(), markQuestion.getPaperNumber());
-            if (MarkPaperStatus.FINISH.equals(markPaper.getStatus())) {
-                return;
-            }
-            int count = 0;
-            List<MarkStudent> studentList = markStudentService.listUnMarkTaskStudent(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), pageSize);
-            while (CollectionUtils.isNotEmpty(studentList)) {
-                // 已生成的双评任务总数的第一组任务数
-                int doubleMarkTaskCount1 = markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(
-                        markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), 1);
-                // 已生成的双评任务总数的第二组任务数
-                int doubleMarkTaskCount2 = markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(
-                        markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), 2);
-                List<MarkTask> taskList = new ArrayList<>();
-                int doubleCount = 0;
-                int studentCount = studentList.size();
-                for (MarkStudent student : studentList) {
-                    MarkTask markTask = new MarkTask();
-                    markTask.setId(SystemConstant.getDbUuid());
-                    markTask.setExamId(student.getExamId());
-                    markTask.setCourseId(student.getCourseId());
-                    markTask.setPaperNumber(student.getPaperNumber());
-                    markTask.setQuestionId(markQuestion.getId());
-                    markTask.setMainNumber(markQuestion.getMainNumber());
-                    markTask.setSubNumber(markQuestion.getSubNumber());
-                    markTask.setStudentId(student.getId());
-                    markTask.setBasicStudentId(student.getBasicStudentId());
-                    markTask.setStudentCode(student.getStudentCode());
-                    markTask.setSecretNumber(student.getSecretNumber());
-                    markTask.setTaskNumber(1);
-                    markTask.setAiMarked(getAiMarked(markQuestion, markTask.getTaskNumber()));
-                    markTask.setStatus(MarkTaskStatus.WAITING);
-                    taskList.add(markTask);
-                    // 开启双评时需要判断是否生成第二份评卷任务
-                    if (markQuestion.getDoubleRate() != null && markQuestion.getDoubleRate() > 0) {
-                        boolean needDouble;
-                        if (markQuestion.getDoubleRate() == 100) {
-                            needDouble = true;
-                        } else {
-                            double libraryCount = taskList.size();
-                            int expectCount = (int) ((doubleMarkTaskCount1 + studentCount) * markQuestion.getDoubleRate()
-                                    / 100);
-                            // 随机数判断加入当前已经生成双评任务的比例加权
-                            // 实际双评任务数小于理论生成数 &&(剩余未生成双评的考生数量小于剩余应生成的数量)
-                            needDouble = (doubleMarkTaskCount2 + doubleCount) < expectCount
-                                    && ((studentCount - libraryCount + doubleCount) <= (expectCount
-                                    - doubleMarkTaskCount2 - doubleCount));
-                        }
-                        if (needDouble) {
-                            markTask = new MarkTask();
-                            markTask.setId(SystemConstant.getDbUuid());
-                            markTask.setExamId(student.getExamId());
-                            markTask.setCourseId(student.getCourseId());
-                            markTask.setPaperNumber(student.getPaperNumber());
-                            markTask.setQuestionId(markQuestion.getId());
-                            markTask.setMainNumber(markQuestion.getMainNumber());
-                            markTask.setSubNumber(markQuestion.getSubNumber());
-                            markTask.setStudentId(student.getId());
-                            markTask.setBasicStudentId(student.getBasicStudentId());
-                            markTask.setStudentCode(student.getStudentCode());
-                            markTask.setSecretNumber(student.getSecretNumber());
-                            markTask.setTaskNumber(2);
-                            markTask.setAiMarked(getAiMarked(markQuestion, markTask.getTaskNumber()));
-                            markTask.setStatus(MarkTaskStatus.WAITING);
-                            taskList.add(markTask);
-                            doubleCount++;
-                        }
-                    }
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                } finally {
                 }
-                // 有新任务创建
-                if (CollectionUtils.isNotEmpty(taskList)) {
-                    count += taskList.size();
-                    // 任务乱序
-                    Collections.shuffle(taskList);
-                    // 批量保存
-                    markTaskService.saveBatch(taskList);
-                    // 正评状态才需要更新任务数量
-                    if (MarkPaperStatus.FORMAL.equals(markPaper.getStatus())) {
-                        this.updateMarkTaskCount(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-                        this.updateMarkedCount(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-                        this.updatePersonTask(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-                    }
-                }
-                studentList = markStudentService.listUnMarkTaskStudent(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), pageSize);
-            }
-            if (count != 0) {
-                log.info("finish create " + count + " markTask for examId=" + markQuestion.getExamId() + ", paperNumber="
-                        + markQuestion.getPaperNumber() + ", questionId=" + markQuestion.getId());
             }
-        } catch (Exception e) {
-            log.error("build formal markTask error for examId=" + markQuestion.getExamId() + ", paperNumber="
-                    + markQuestion.getPaperNumber() + ", questionId=" + markQuestion.getId(), e);
-        } finally {
-            lockService.unwatch(LockType.QUESTION, markQuestion.getId());
-        }
-    }
-
-    private Boolean getAiMarked(MarkQuestion markQuestion, Integer taskNumber) {
-        if (MarkPaperAiMark.NONE.equals(markQuestion.getAiMark())) {
-            return false;
-        } else if (MarkPaperAiMark.AI_ONLY.equals(markQuestion.getAiMark()) || MarkPaperAiMark.MAN_MACHINE.equals(markQuestion.getAiMark())) {
-            return taskNumber == 1;
+            pageNumber++;
+            markTasks = markTaskService.findAiUnMarked(new Page<>(pageNumber, 20), examId, paperNumber, questionId);
+        }while (CollectionUtils.isNotEmpty(markTasks));
+    }
+
+    private List<StandardAnswer> buildStandardAnswer(MarkAiQuestionParam markAiQuestionParam) {
+        List<StandardAnswer> standardAnswerList = new ArrayList<>();
+        // 得分点
+        if (AiQuestionParamModeStatus.POINT.equals(markAiQuestionParam.getMode())) {
+            List<MarkAiQuestionPoint> markAiQuestionPoints = markAiQuestionPointService.listByAiQuestionId(markAiQuestionParam.getId());
+            if (CollectionUtils.isEmpty(markAiQuestionPoints)) {
+                throw ExceptionResultEnum.ERROR.exception("未设置得分点");
+            }
+            standardAnswerList = markAiQuestionPoints.stream().map(m -> {
+                StandardAnswer standardAnswer = new StandardAnswer();
+                standardAnswer.setScore(m.getScore());
+                standardAnswer.setContent(m.getAnswer());
+                return standardAnswer;
+            }).collect(Collectors.toList());
+        }
+        // 档次
+        else if (AiQuestionParamModeStatus.LEVEL.equals(markAiQuestionParam.getMode())) {
+            List<MarkAiQuestionLevel> markAiQuestionLevels = markAiQuestionLevelService.listByAiQuestionId(markAiQuestionParam.getId());
+            if (CollectionUtils.isEmpty(markAiQuestionLevels)) {
+                throw ExceptionResultEnum.ERROR.exception("未设置档次");
+            }
+            standardAnswerList = markAiQuestionLevels.stream().map(m -> {
+                StandardAnswer standardAnswer = new StandardAnswer();
+                standardAnswer.setLowScore(m.getMinScore());
+                standardAnswer.setHighScore(m.getMaxScore());
+                standardAnswer.setContent(m.getAnswer());
+                return standardAnswer;
+            }).collect(Collectors.toList());
         } else {
-            return false;
-        }
-    }
-
-    private void fillFormalTask(MarkQuestion markQuestion) {
-        int pageSize = 100;
-        try {
-            lockService.watch(LockType.QUESTION, markQuestion.getId());
-            // 上锁后重复验证分组状态
-            MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(markQuestion.getExamId(), markQuestion.getPaperNumber());
-            if (MarkPaperStatus.FINISH.equals(markPaper.getStatus())) {
-                return;
-            }
-            int count = 0;
-            List<MarkStudent> studentList = markStudentService.listUnMarkDoubleTaskStudent(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), pageSize);
-            while (CollectionUtils.isNotEmpty(studentList)) {
-                // 已生成的双评任务总数的第一组任务数
-                int doubleMarkTaskCount1 = markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(
-                        markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), 1);
-                // 已生成的双评任务总数的第二组任务数
-                int doubleMarkTaskCount2 = markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(
-                        markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), 2);
-                List<MarkTask> taskList = new ArrayList<>();
-                int doubleCount = 0;
-                int studentCount = studentList.size();
-                for (MarkStudent student : studentList) {
-                    // 开启双评时需要判断是否生成第二份评卷任务
-                    if (markQuestion.getDoubleRate() != null && markQuestion.getDoubleRate() > 0) {
-                        boolean needDouble;
-                        if (markQuestion.getDoubleRate() == 100) {
-                            needDouble = true;
-                        } else {
-                            double libraryCount = taskList.size();
-                            int expectCount = (int) ((doubleMarkTaskCount1 + studentCount) * markQuestion.getDoubleRate()
-                                    / 100);
-                            // 随机数判断加入当前已经生成双评任务的比例加权
-                            // 实际双评任务数小于理论生成数 &&(剩余未生成双评的考生数量小于剩余应生成的数量)
-                            needDouble = (doubleMarkTaskCount2 + doubleCount) < expectCount
-                                    && ((studentCount - libraryCount + doubleCount) <= (expectCount
-                                    - doubleMarkTaskCount2 - doubleCount));
-                        }
-                        if (needDouble) {
-                            MarkTask markTask = new MarkTask();
-                            markTask.setId(SystemConstant.getDbUuid());
-                            markTask.setExamId(student.getExamId());
-                            markTask.setCourseId(student.getCourseId());
-                            markTask.setPaperNumber(student.getPaperNumber());
-                            markTask.setQuestionId(markQuestion.getId());
-                            markTask.setMainNumber(markQuestion.getMainNumber());
-                            markTask.setSubNumber(markQuestion.getSubNumber());
-                            markTask.setStudentId(student.getId());
-                            markTask.setBasicStudentId(student.getBasicStudentId());
-                            markTask.setStudentCode(student.getStudentCode());
-                            markTask.setSecretNumber(student.getSecretNumber());
-                            markTask.setTaskNumber(2);
-                            markTask.setAiMarked(getAiMarked(markQuestion, markTask.getTaskNumber()));
-                            markTask.setStatus(MarkTaskStatus.WAITING);
-                            taskList.add(markTask);
-                            doubleCount++;
-                        }
-                    }
-                }
-                // 有新任务创建
-                if (CollectionUtils.isNotEmpty(taskList)) {
-                    count += taskList.size();
-                    // 任务乱序
-                    Collections.shuffle(taskList);
-                    // 批量保存
-                    markTaskService.saveBatch(taskList);
-                    // 正评状态才需要更新任务数量
-                    if (MarkPaperStatus.FORMAL.equals(markPaper.getStatus())) {
-                        this.updateMarkTaskCount(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-                        this.updateMarkedCount(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-                        this.updatePersonTask(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-                    }
-                }
-                studentList = markStudentService.listUnMarkTaskStudent(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId(), pageSize);
-            }
-            if (count != 0) {
-                log.info("finish create " + count + " markTask for examId=" + markQuestion.getExamId() + ", paperNumber="
-                        + markQuestion.getPaperNumber() + ", questionId=" + markQuestion.getId());
-            }
-        } catch (Exception e) {
-            log.error("build formal markTask error for examId=" + markQuestion.getExamId() + ", paperNumber="
-                    + markQuestion.getPaperNumber() + ", questionId=" + markQuestion.getId(), e);
-        } finally {
-            lockService.unwatch(LockType.QUESTION, markQuestion.getId());
-        }
-    }
-
-    @Transactional
-    @Override
-    public void deleteMarkTaskByStudent(MarkStudent student) {
-        markStudentService.updateSubjectiveStatusAndScore(student.getId(), SubjectiveStatus.UNMARK);
-        // 正评相关数据
-        markArbitrateHistoryService.deleteByStudentId(student.getId());
-        markProblemHistoryService.deleteByStudentId(student.getId());
-        markTaskService.deleteByStudentId(student.getId());
-        // 主观状态与得分明细
-        markSubjectiveScoreService.deleteByStudentId(student.getId());
-        updateGroupAllCount(student.getExamId(), student.getPaperNumber());
-        // 复核记录
-        // inspectedService.clearByStudent(student.getId());
-        // 打回记录
-        // rejectHistoryDao.deleteByStudentId(student.getId());
-    }
-
-    @Transactional
-    @Override
-    public void updateGroupAllCount(Long examId, String paperNumber) {
-        List<MarkQuestion> markQuestionList = markQuestionService.listByExamIdAndPaperNumberAndObjective(examId, paperNumber, false);
-        for (MarkQuestion markQuestion : markQuestionList) {
-            this.updateMarkedCount(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-            this.updateMarkTaskCount(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-            this.updatePersonTask(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
+            throw ExceptionResultEnum.ERROR.exception("评分模式不存在");
         }
+        return standardAnswerList;
     }
 
-    private void updateMarkTaskCount(Long examId, String paperNumber, Long questionId) {
-        int count = markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndUserId(examId, paperNumber, questionId,
-                null);
-        markQuestionService.updateTaskCount(questionId, count);
-    }
-
-    private boolean calculateQuestion(MarkQuestion markQuestion, Long studentId) {
-        double score = 0;
-        int count = 0;
-        // 未设置算分策略的情况下,默认取平均分
-        ScorePolicy policy = markQuestion.getScorePolicy() != null ? markQuestion.getScorePolicy() : ScorePolicy.AVG;
-        List<MarkTask> list = markTaskService.listByExamIdAndPaperNumberAndQuestionIdAndStudentId(markQuestion.getExamId(),
-                markQuestion.getPaperNumber(), markQuestion.getId(), studentId);
-        if (list.isEmpty()) {
-            return false;
-        }
-        for (MarkTask markTask : list) {
-            if (markTask.getStatus() != MarkTaskStatus.MARKED && markTask.getStatus() != MarkTaskStatus.ARBITRATED) {
-                // 有非完成状态的评卷任务,直接返回
-                return false;
-            }
-        }
-        for (MarkTask markTask : list) {
-            count++;
-            Double current = markTask.getHeaderScore() != null ? markTask.getHeaderScore() : markTask.getMarkerScore();
-            if (count == 1) {
-                // 首份评卷任务,直接取总分与明细
-                score = current;
-            } else {
-                switch (policy) {
-                    case AVG:
-                        // 直接累加
-                        score = BigDecimalUtils.add(score, current);
-                        break;
-                    case MAX:
-                        // 高分优先
-                        if (current > score) {
-                            score = current;
-                        }
-                        break;
-                    case MIN:
-                        // 低分优先
-                        if (current < score) {
-                            score = current;
-                        }
-                        break;
-                    default:
-                        break;
-                }
-            }
-        }
-        // 取平均分策略下,累计分数需要重新计算一次
-        if (policy == ScorePolicy.AVG && count > 1) {
-            score = BigDecimalUtils.div(score, count);
-        }
-        markQuestion.setMarkScore(score);
-        return true;
-    }
-
-    private void updateStudentQuestionScore(Long studentId, MarkQuestion markQuestion, Double markScore) {
-        MarkSubjectiveScore ss = markSubjectiveScoreService.getByStudentIdAndQuestionId(studentId, markQuestion.getId());
-        if (ss == null) {
-            ss = new MarkSubjectiveScore();
-        }
-        ss.setStudentId(studentId);
-        ss.setExamId(markQuestion.getExamId());
-        ss.setPaperNumber(markQuestion.getPaperNumber());
-        ss.setQuestionId(markQuestion.getId());
-        ss.setMainNumber(markQuestion.getMainNumber());
-        ss.setSubNumber(markQuestion.getSubNumber());
-        ss.setScore(markScore);
-        ss.setMainScore(0.0);
-        markSubjectiveScoreService.saveOrUpdateByMultiId(ss);
-    }
-
-    private void scoreCalculate(Long studentId, Integer version, boolean autoCalc) {
-        List<ScoreItem> scoreList = new ArrayList<>();
-        Map<Integer, List<MarkSubjectiveScore>> mainScoreMap = new HashMap<>();
-        Map<Integer, Double> scoreMap = new HashMap<>();
-
-        // 循环所有主观得分明细
-        List<MarkSubjectiveScore> list = markSubjectiveScoreService.listByStudentId(studentId);
-        // list.sort(null);
-        for (MarkSubjectiveScore ss : list) {
-            List<MarkSubjectiveScore> mainScoreList = mainScoreMap.get(ss.getMainNumber());
-            if (mainScoreList == null) {
-                mainScoreList = new ArrayList<>();
-            }
-            mainScoreList.add(ss);
-            mainScoreMap.put(ss.getMainNumber(), mainScoreList);
-            scoreList.add(new ScoreItem(ss));
-        }
-        // 计算大题分
-        for (Integer mainNumber : mainScoreMap.keySet()) {
-            List<MarkSubjectiveScore> mainScoreList = mainScoreMap.get(mainNumber);
-            BigDecimal mainScore = BigDecimal.ZERO;
-            for (MarkSubjectiveScore subjectiveScore : mainScoreList) {
-                mainScore = mainScore.add(BigDecimal.valueOf(subjectiveScore.getScore()));
-            }
-            for (MarkSubjectiveScore subjectiveScore : mainScoreList) {
-                subjectiveScore.setMainScore(mainScore.doubleValue());
-                markSubjectiveScoreService.saveOrUpdateByMultiId(subjectiveScore);
-            }
-            scoreMap.put(mainNumber, mainScore.doubleValue());
+    private String mergeQuestionOcrResult(List<MarkOcrStudentQuestion> markOcrStudentQuestionList) {
+        if (CollectionUtils.isEmpty(markOcrStudentQuestionList)) {
+            return null;
         }
-        // 计算选做题分数
-        BigDecimal totalScore = BigDecimal.ZERO;
-        // 计算非选做题总分
-        for (Integer mainNumber : scoreMap.keySet()) {
-            totalScore = totalScore.add(BigDecimal.valueOf(scoreMap.get(mainNumber)));
-        }
-        // 全部评完,更新考生主观题得分
-//        markStudentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.MARKED, totalScore.doubleValue(),
-//                MarkStudent.buildScoreList(scoreList));
-        markStudentService.updateSubjectiveScoreByVersion(studentId, SubjectiveStatus.MARKED, totalScore.doubleValue(), MarkStudent.buildScoreList(scoreList), version, autoCalc);
-    }
-
-    @Override
-    public MarkSettingDto getSetting(SysUser user, Long examId, String paperNumber) {
-        MarkUserPaper markUserPaper = markUserPaperService.getByExamIdAndPaperNumberAndUserId(examId, paperNumber, user.getId());
-        user = sysUserService.getById(user.getId());
-        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
-
-        MarkSettingDto dto = new MarkSettingDto();
-        dto.setExamType(ExamType.SCAN_IMAGE);//考试类型,默认SCAN_IMAGE
-        dto.setMode(markUserPaper != null && markUserPaper.getMode() != null ? markUserPaper.getMode()
-                : MarkMode.UNLIMITED.equals(markPaper.getMarkMode()) ? MarkMode.TRACK : markPaper.getMarkMode());//模式 TRACK/COMMON
-        dto.setQuestionModel(markUserPaper != null && markUserPaper.getQuestionModel() != null ? markUserPaper.getQuestionModel() : QuestionModel.MULTI);
-        dto.setForceMode(markPaper.getForceMode());//强制模式切换
-        dto.setSheetView(markPaper.getSheetView());//是否显示原图功能
-        dto.setSheetConfig(markPaper.getSheetConfig());//原图遮盖规则
-        dto.setEnableAllZore(false);//是否开启全零分(知学知考阅卷默认false)
-        dto.setFileServer(null);//图片服务地址
-        dto.setUserName(user.getRealName());//评卷员名称
-        dto.getSubject().setAnswerUrl(markQuestionService.previewAnswerFileByExamIdAndPaperNumber(examId, paperNumber));
-        dto.getSubject().setPaperUrl(markQuestionService.previewPaperFileByExamIdAndPaperNumber(examId, paperNumber));
-        BasicCourse basicCourse = basicCourseService.getById(markPaper.getCourseId());
-        dto.getSubject().setCode(markPaper.getPaperNumber());
-        dto.getSubject().setName(basicCourse != null ? basicCourse.getName() : null);
-        dto.setForceSpecialTag(false);//强制标记是否开启 必须要有标记(分数轨迹或特殊标记)(知学知考阅卷默认true)
-        dto.setUiSetting(user.getUiSetting());//新交互模式下,以下字段要重新定义或新增(初始值为空,按评卷员保存)
-        dto.setStatusValue(markPaper.getStatus());//只显示试评名称 FORMAL("正评"), FINISH("结束")
-        dto.setProblemTypes(MarkProblemType.listTypes());//问题卷类型
-//        dto.setTopCount(markUserGroup != null && markUserGroup.getTopCount() != null ? markUserGroup.getTopCount() : 0);
-        dto.setSplitConfig(new Double[0]);//使用裁切整图时的裁切配置
-        dto.setPrefetchCount(3);//预加载任务数量
-        dto.setStartTime(markPaper.getMarkStartTime());//评卷开始时间
-        dto.setEndTime(markPaper.getMarkEndTime());//评卷结束时间
-        dto.setSelective(false);//是否为选做题(默认false)
-        dto.setAutoScroll(markPaper.getAutoScroll());//是否自动跳转
-        dto.setEnableSplit(false);//是否裁切(默认false)
-        dto.setShowObjectScore(markPaper.getShowObjectScore());//是否显示客观分
-        return dto;
+        return markOcrStudentQuestionList.stream().filter(m -> StringUtils.isNotBlank(m.getOcrContent())).map(MarkOcrStudentQuestion::getOcrContent).collect(Collectors.joining(SystemConstant.COMMA_OF_CHINESE));
     }
 
     @Override
-    public List<MarkStatusDto> getStatus(Long userId, Long examId, String paperNumber, QuestionModel questionModel, Long questionId) {
-        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
-
-        List<String> classNames = null;
-        //校验是否有分班阅
-        if (markPaper != null && markPaper.getClassMark() != null && markPaper.getClassMark().booleanValue()) {
-            List<MarkUserClass> markUserClassList = markUserClassService.listByExamIdAndPaperNumberAndUserId(examId, paperNumber, userId);
-            if (CollectionUtils.isNotEmpty(markUserClassList)) {
-                classNames = markUserClassList.stream().map(MarkUserClass::getClassName).collect(Collectors.toList());
-            }
-        }
-
-        List<MarkStatusDto> dtoList = new ArrayList<>();
-        List<MarkUserQuestion> markUserQuestionList = markUserQuestionService.listByExamIdAndPaperNumberAndUserIdAndEnableTure(examId, paperNumber, userId);
-        // 评卷方式
-        if (QuestionModel.SINGLE.equals(questionModel)) {
-            if (questionId != null) {
-                markUserQuestionList = markUserQuestionList.stream().filter(m -> m.getQuestionId().equals(questionId)).collect(Collectors.toList());
-            }
-            MarkUserPaper markUserPaper = markUserPaperService.getByExamIdAndPaperNumberAndUserId(examId, paperNumber, userId);
-            MarkStatusDto dto;
-            for (MarkUserQuestion question : markUserQuestionList) {
-                dto = new MarkStatusDto(markQuestionService.getById(question.getQuestionId()), markUserPaper.getMarkedQuestionId());
-                List<Long> questionIds = Arrays.asList(question.getQuestionId());
-                MarkStatusDto statusDto = getDto(dto, examId, paperNumber, userId, classNames, questionIds);
-                statusDto.setLeftCount(countLeftCountForSingle(examId, paperNumber, statusDto.getLeftCount(), question.getQuestionId(), userId, classNames));
-                dtoList.add(statusDto);
-            }
-        } else if (QuestionModel.MULTI.equals(questionModel)) {
-            MarkStatusDto dto = new MarkStatusDto();
-            List<Long> questionIds = markUserQuestionList.stream().filter(m -> m.getUserId().equals(userId)).map(MarkUserQuestion::getQuestionId).collect(Collectors.toList());
-            dtoList.add(getDto(dto, examId, paperNumber, userId, classNames, questionIds));
-        } else {
-            throw ExceptionResultEnum.ERROR.exception("参数有误");
+    public List<MarkOcrStudentQuestion> listStudentQuestionOcrResult(BasicSchool basicSchool, Long studentId, Long questionId) {
+        List<MarkOcrStudentQuestion> markOcrStudentQuestionList = markOcrStudentQuestionService.listByStudentIdAndQuestionId(studentId, questionId);
+        if (CollectionUtils.isNotEmpty(markOcrStudentQuestionList)) {
+            return markOcrStudentQuestionList;
         }
-        dtoList.sort(Comparator.comparing(MarkStatusDto::getMainNumber).thenComparing(MarkStatusDto::getSubNumber));
-        return dtoList;
-    }
-
-    private int countLeftCountForSingle(Long examId, String paperNumber, int leftCount, Long questionId, Long userId, List<String> classNames) {
         MarkQuestion markQuestion = markQuestionService.getById(questionId);
-        Set<Long> currentStudent = this.listCurrentStudent(examId, paperNumber);
-
-        int userCurrentCount = this.applyCurrentCount(markQuestion, userId);
-        if (CollectionUtils.isEmpty(classNames)) {
-            return leftCount - currentStudent.size() + userCurrentCount;
-        } else {
-//            List<MarkUserClass> markUserClassList = markUserClassService.listByExamIdAndPaperNumber(markQuestion.getExamId(), markQuestion.getPaperNumber());
-//            Set<MarkUserClass> markUserClasses = markUserClassList.stream().filter(m -> classNames.contains(m.getClassName()) && !m.getUserId().equals(userId)).collect(Collectors.toSet());
-//            int count = 0;
-//            for (MarkUserClass markUserClass : markUserClasses) {
-//                count += this.applyCurrentCount(markQuestion, markUserClass.getUserId());
-//            }
-//            return leftCount - count;
-            Set<Long> studentIds = markStudentService.listStudentIds(examId, paperNumber, classNames);
-            Collection<Long> collection = CollectionUtils.intersection(currentStudent, studentIds);
-            return leftCount - collection.size() + userCurrentCount;
-        }
-    }
-
-    private MarkStatusDto getDto(MarkStatusDto dto, Long examId, String paperNumber, Long userId, List<String> classNames, List<Long> questionIds) {
-        //待仲裁卷数量
-        dto.setArbitrateCount(markArbitrateHistoryService.waitArbitrateCount(examId, paperNumber, null, classNames));
-        //总数量
-        dto.setTotalCount(markTaskService.countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(examId, paperNumber, null, classNames, questionIds));
-        // 未评
-        int unmarkCount = markTaskService.countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(examId, paperNumber, null, classNames, questionIds, MarkTaskStatus.WAITING, MarkTaskStatus.WAIT_ARBITRATE, MarkTaskStatus.REJECTED);
-        dto.setLeftCount(unmarkCount);
-        //总评卷数量(考生数)
-        dto.setMarkedCount(dto.getTotalCount() == 0 ? 0 : dto.getTotalCount() - unmarkCount);
-        //个人评卷数量
-        dto.setPersonCount(markTaskService.countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(examId, paperNumber, userId, classNames, questionIds, MarkTaskStatus.MARKED, MarkTaskStatus.ARBITRATED,
-                MarkTaskStatus.WAIT_ARBITRATE, MarkTaskStatus.PROBLEM));
-        //问题卷数量
-        dto.setProblemCount(markProblemHistoryService.countByExamIdAndPaperNumberAndStatusAndClassNameIn(examId, paperNumber, MarkProblemStatus.WAITING, classNames));
-        return dto;
-    }
-
-    @Override
-    public void clear(Long userId, Long examId, String paperNumber) {
-        TaskLock taskLock = TaskLockUtil.getFormalTask(getKey(examId, paperNumber));
-        taskLock.clear(userId);
-    }
-
-    @Override
-    public void releaseByStudent(MarkStudent student) {
-        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student.getExamId(), student.getPaperNumber()));
-        taskLock.remove(student.getId());
-    }
-
-    @Override
-    public boolean applyStudent(MarkStudent student, Long userId) {
-        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student.getExamId(), student.getPaperNumber()));
-        boolean lock = taskLock.add(student.getId(), userId, new HashSet<>());
-        // 上锁失败直接返回
-        if (!lock) {
-            return false;
-        }
-        // 重复校验任务状态
-        if (student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED)) {
-            return true;
-        } else {
-            taskLock.remove(student.getId(), userId);
-            return false;
-        }
-    }
-
-    @Override
-    public void submitHeaderTask(List<MarkResultQuestion> resultQuestions, MarkStudent markStudent) {
-        Long userId = ServletUtil.getRequestUserId();
-        for (MarkResultQuestion result : resultQuestions) {
-            try {
-                lockService.watch(LockType.QUESTION, result.getQuestionId());
-                Long currentTime = System.currentTimeMillis();
-                updateMarkSubjectScore(markStudent, result, userId);
-                markTaskService.updateHeaderResult(markStudent.getExamId(), markStudent.getPaperNumber(),
-                        result.getQuestionId(), markStudent.getId(), userId, result.getMarkerScore(), result.getMarkerTrackList(), result.getMarkerTagList(), currentTime, MarkTaskStatus.MARKED);
-                updateMarkedCount(markStudent.getExamId(), markStudent.getPaperNumber(), result.getQuestionId());
-//                resetStudentStatus(markStudent.getId());
-                markStudentService.updateCheckInfo(markStudent.getId(), userId);
-                MarkQuestion markQuestion = markQuestionService.getById(result.getQuestionId());
-                markQuestion.setMarkScore(result.getMarkerScore());
-                checkStudentQuestionForInspect(markStudent, markQuestion);
-            } catch (ApiException e) {
-                throw ExceptionResultEnum.ERROR.exception(e.getMessage());
-            } finally {
-                lockService.unwatch(LockType.QUESTION, result.getQuestionId());
-            }
-        }
-    }
-
-    private void updateMarkSubjectScore(MarkStudent markStudent, MarkResultQuestion resultQuestion, Long userId) {
-        // 记录修改日志(按小题)
-//        List<BasicOperationLog> basicOperationLogs = new ArrayList<>();
-        MarkSubjectiveScore markSubjectiveScore = markSubjectiveScoreService.getByStudentIdAndQuestionId(markStudent.getId(), resultQuestion.getQuestionId());
-        double score = resultQuestion.getMarkerScore();
-        // 分数有变动,记录日志
-        if (markSubjectiveScore.getScore() != score) {
-//            MarkStudentVo markStudentVo = markStudentService.getDetailById(markStudent.getId());
-//            String detail = String.format("%s(%s)课程%s试卷编号下,将%s(%s)的%s题从%s分修改为%s分", markStudentVo.getCourseName(), markStudentVo.getCourseCode(), markStudentVo.getPaperNumber(), markStudentVo.getStudentName(), markStudentVo.getStudentCode(), markSubjectiveScore.getMainNumber() + "-" + markSubjectiveScore.getSubNumber(), markSubjectiveScore.getScore(), score);
-//            basicOperationLogs.add(new BasicOperationLog(Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString()), ServletUtil.getCurrentPrivilegeId(), OperationTypeEnum.SUBJECTIVE, OperationTypeEnum.SUBJECTIVE.getName(), ServletUtil.getRequest().getServletPath(), detail, String.valueOf(groupResult.getScore()), "成功", ServletUtil.getRequestUserId()));
-            MarkHeaderHistory headerHistory = new MarkHeaderHistory();
-            headerHistory.setId(SystemConstant.getDbUuid());
-            headerHistory.setExamId(markStudent.getExamId());
-            headerHistory.setPaperNumber(markStudent.getPaperNumber());
-            headerHistory.setStudentId(markStudent.getId());
-            headerHistory.setQuestionId(markSubjectiveScore.getQuestionId());
-            headerHistory.setMainNumber(markSubjectiveScore.getMainNumber());
-            headerHistory.setSubNumber(markSubjectiveScore.getSubNumber());
-            headerHistory.setUserId(userId);
-            headerHistory.setScore(score);
-            headerHistory.setTrackList(JSON.toJSONString(resultQuestion.getMarkerTrackList()));
-            headerHistory.setOriginalScore(markSubjectiveScore.getScore());
-//            headerHistory.setOriginalTrackList();
-            headerHistory.setCreateTime(System.currentTimeMillis());
-            markHeaderHistoryService.save(headerHistory);
-        }
-//        markSubjectiveScore.setScore(score);
-//        markSubjectiveScoreService.saveOrUpdateByMultiId(markSubjectiveScore);
-
-        // 记录日志
-//        if (CollectionUtils.isNotEmpty(basicOperationLogs)) {
-//            basicOperationLogService.saveBatch(basicOperationLogs);
-//        }
-    }
-
-    private String getKey(Long examId, String paperNumber) {
-        return examId + "_" + paperNumber;
-    }
-
-    @Override
-    public IPage<Task> getHistory(Long userId, int pageNumber, int pageSize, Sort sort, String order, Long examId,
-                                  String paperNumber, String secretNumber, Double markerScore) {
-        if (!"marker_time".equals(order) && !"marker_score".equals(order)) {
-            order = "marker_time";
-        }
-        Page<Long> page = new Page<>(pageNumber, pageSize);
-        OrderItem orderItem = new OrderItem(order, sort.equals(Sort.asc));
-        page.addOrder(orderItem);
-        IPage<MarkTask> list = markTaskService.listPageHistory(page, userId, examId, paperNumber, secretNumber, markerScore, MarkTaskStatus.MARKED, MarkTaskStatus.ARBITRATED, MarkTaskStatus.WAIT_ARBITRATE, MarkTaskStatus.PROBLEM);
-        List<Task> recordsDtos = new ArrayList<>();
-        List<MarkTaskStatus> markTaskStatuses = Arrays.asList(MarkTaskStatus.MARKED, MarkTaskStatus.ARBITRATED, MarkTaskStatus.WAIT_ARBITRATE, MarkTaskStatus.PROBLEM);
-        for (MarkTask markTask : list.getRecords()) {
-            List<MarkTask> markTaskList = markTaskService.listByStudentIdAndMarkerId(markTask.getStudentId(), userId, markTaskStatuses);
-            Task dto = taskService.build(userId, markTaskList);
-            dto.setMarkerTime(markTask.getMarkerTime());
-            recordsDtos.add(dto);
-        }
-        IPage<Task> result = new Page<>();
-        result.setCurrent(list.getCurrent());
-        result.setPages(list.getPages());
-        result.setRecords(recordsDtos);
-        result.setSize(list.getSize());
-        result.setTotal(list.getTotal());
-        return result;
-    }
-
-    @Override
-    public Task getTask(Long userId, Long examId, String paperNumber, QuestionModel questionModel, Long questionId) {
-        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
-        if (markPaper == null) {
-            throw ExceptionResultEnum.ERROR.exception("评卷试卷不存在");
-        }
-        int count = markUserQuestionService.countByExamIdAndPaperNumberAndAndUserId(examId, paperNumber, userId);
-        if (count == 0) {
-            throw ExceptionResultEnum.ERROR.exception("评卷任务被重置,请点击右上角返回按钮,重新在评卷入口菜单点击评卷");
-        }
-        List<MarkUserQuestion> markUserQuestions = markUserQuestionService.listByExamIdAndPaperNumberAndUserIdAndEnableTure(examId, paperNumber, userId);
-        if (markUserQuestions.isEmpty()) {
-            throw ExceptionResultEnum.ERROR.exception("评卷员未设置评卷题目");
-        }
-        List<String> classNames = null;
-        //校验是否有分班阅
-        if (markPaper != null && markPaper.getClassMark() != null && markPaper.getClassMark().booleanValue()) {
-            List<MarkUserClass> markUserClassList = markUserClassService.listByExamIdAndPaperNumberAndUserId(examId, paperNumber, userId);
-            if (CollectionUtils.isNotEmpty(markUserClassList)) {
-                classNames = markUserClassList.stream().map(MarkUserClass::getClassName).collect(Collectors.toList());
-            }
-        }
-
-        Task task = null;
-        List<Long> studentIds;
-        int pageNumber = 1;
-        while (task == null) {
-            if (questionModel.equals(QuestionModel.SINGLE)) {
-                Set<Long> questions = new HashSet<>(Arrays.asList(questionId));
-                List<MarkTask> list = markTaskService.findUnMarkedFilterClass(new Page<>(pageNumber, 20), examId, paperNumber, userId, questionId, classNames);
-                if (list.isEmpty()) {
-                    break;
-                }
-                for (MarkTask t : list) {
-                    if (this.applyTask(examId, paperNumber, t.getStudentId(), userId, questions, Arrays.asList(t.getId()))) {
-                        task = taskService.build(userId, Arrays.asList(t));
-                        break;
-                    }
-                }
-            } else if (questionModel.equals(QuestionModel.MULTI)) {
-                Set<Long> questions = markUserQuestions.stream().map(MarkUserQuestion::getQuestionId).collect(Collectors.toSet());
-                studentIds = markStudentService.findUnMarked(new Page<>(pageNumber, 20), examId, paperNumber, questions, classNames);
-                if (studentIds.isEmpty()) {
-                    break;
-                }
-                for (Long studentId : studentIds) {
-                    if (this.applyTask(examId, paperNumber, studentId, userId, questions)) {
-                        List<MarkTask> markTaskList = markTaskService.listByStudentId(studentId);
-                        if (markTaskList.isEmpty()) {
-                            releaseStudent(examId, paperNumber, studentId, userId);
-                            continue;
-                        }
-                        Map<Long, List<MarkTask>> map = markTaskList.stream().collect(Collectors.groupingBy(MarkTask::getQuestionId));
-                        List<MarkTask> markTasks = new ArrayList<>();
-                        for (MarkUserQuestion markUserQuestion : markUserQuestions) {
-                            List<MarkTask> markTasks1 = map.get(markUserQuestion.getQuestionId());
-                            if (markTasks1.size() == 1) {
-                                markTasks.add(markTasks1.get(0));
-                            } else {
-                                MarkTask markTask = markTasks1.stream().filter(m -> userId.equals(m.getUserId())).findFirst().orElse(null);
-                                if (markTask == null) {
-                                    markTask = markTasks1.stream().filter(m -> m.getUserId() == null).findFirst().orElse(null);
-                                    if (markTask == null) {
-                                        markTask = markTasks1.get(0);
-                                    }
-                                }
-                                markTasks.add(markTask);
-                            }
-                        }
-                        // 所有题目都已评,跳过
-                        if (markTasks.stream().filter(m -> m.getUserId() == null).count() == 0) {
-                            releaseStudent(examId, paperNumber, studentId, userId);
-                            continue;
-                        }
-                        markTasks.sort(Comparator.comparing(MarkTask::getMainNumber).thenComparing(MarkTask::getSubNumber));
-                        task = taskService.build(userId, markTasks);
-                        break;
-                    }
-                }
-            } else {
-                break;
-            }
-
-            if (task == null) {
-                pageNumber++;
-            }
-        }
-        return task;
-    }
-
-    @Override
-    public boolean applyTask(Long examId, String paperNumber, Long studentId, Long userId, Set<Long> questions, List<Long> taskIds) {
-        // 查询待领取任务时,已经做了多评同一studentId互斥处理
-        TaskLock taskLock = TaskLockUtil.getFormalTask(getKey(examId, paperNumber));
-        boolean lock = taskLock.add(studentId, userId, questions);
-        // 上锁失败直接返回
-        if (!lock) {
-            return false;
-        }
-        // 检验是否有待评任务
-        if (markTaskService.countByStatusAndIdIn(taskIds, MarkTaskStatus.WAITING, MarkTaskStatus.REJECTED) > 0) {
-            return true;
-        } else {
-            taskLock.remove(studentId, userId);
-            return false;
-        }
-    }
-
-    @Override
-    public boolean applyTask(Long examId, String paperNumber, Long studentId, Long userId, Set<Long> questions) {
-        // 查询待领取任务时,已经做了多评同一studentId互斥处理
-        TaskLock taskLock = TaskLockUtil.getFormalTask(getKey(examId, paperNumber));
-        boolean lock = taskLock.add(studentId, userId, questions);
-        // 上锁失败直接返回
-        if (!lock) {
-            return false;
-        }
-//        taskLock.remove(studentId, userId);
-        return true;
-    }
-
-    @Override
-    public boolean hasApplied(MarkTask t, Long userId) {
-        TaskLock taskLock = TaskLockUtil.getFormalTask(getKey(t.getExamId(), t.getPaperNumber()));
-        return taskLock.exist(t.getStudentId(), userId);
-    }
-
-    @Override
-    public void deleteInitMarkData(Long examId, String paperNumber) {
-        markPaperService.deleteByExamIdAndPaperNumber(examId, paperNumber);
-        markStudentService.deleteByExamIdAndPaperNumber(examId, paperNumber);
-        scanAnswerCardService.deleteByExamIdAndPaperNumber(examId, paperNumber);
-        markQuestionService.deleteByExamIdAndPaperNumber(examId, paperNumber);
-    }
-
-    @Override
-    public void calcObjectiveScore(MarkPaper markPaper) {
-        int pageNumber = 1;
-        int pageSize = 1000;
-        IPage<MarkStudent> iPage = markStudentService.pageByExamAndPaperNumber(markPaper.getExamId(),
-                markPaper.getPaperNumber(), pageNumber, pageSize);
-        while (CollectionUtils.isNotEmpty(iPage.getRecords())) {
-            for (MarkStudent student : iPage.getRecords()) {
-                calculate(student);
-            }
-            pageNumber++;
-            iPage = markStudentService.pageByExamAndPaperNumber(markPaper.getExamId(), markPaper.getPaperNumber(),
-                    pageNumber, pageSize);
-        }
-    }
-
-    private void calculate(MarkStudent student) {
-        // 未上传、缺考的考生不统分
-        if (!student.getUpload() || student.getAbsent() || student.getOmrAbsent() || (student.getManualAbsent() != null && student.getManualAbsent())) {
-            return;
-        }
-        try {
-            // 更新客观题得分
-            markStudentService.calculateObjectiveScore(student);
-            // 增加主观题总分统计
-            // markService.scoreCalculate(student,
-            // findMarkGroup(student.getSubjectCode()));
-        } catch (Exception e) {
-            log.error("calculate error for studentId=" + student.getId(), e);
-        }
-    }
-
-    @Transactional
-    @Override
-    public SubmitResult saveTask(Long examId, String paperNumber, Long userId, MarkResult result) {
-        for (MarkResultQuestion question : result.getQuestionList()) {
-            MarkUserQuestion markUserQuestion = markUserQuestionService.getByExamIdAndPaperNumberAndQuestionIdAndUserId(examId, paperNumber, question.getQuestionId(), userId);
-            if (markUserQuestion == null) {
-                throw ExceptionResultEnum.ERROR.exception("评卷员未绑定题目" + question.getMainNumber() + "-" + question.getSubNumber());
-            }
-        }
-
-        SubmitResult sr;
+        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());
+        }
+        List<File> deleteFileList = new ArrayList<>();
+        //考生题卡原图
         try {
-            lockService.watch(LockType.EXAM_SUBJECT, examId, paperNumber);
-            lockService.watch(LockType.MARK_USER_QUESTION, userId);
-            lockService.waitlock(LockType.STUDENT, result.getStudentId());
-            sr = submitResult(examId, paperNumber, userId, result);
-            if (sr.isSuccess()) {
-                releaseStudent(examId, paperNumber, sr.getStudentId(), userId);
-            }
-        } catch (Exception e) {
-            log.error("save task error", e);
+            List<FilePathVo> filePathVoList = markStudent.listSheetPath();
+            Map<Integer, SheetImageDto> fileMap = new HashMap<>();
+            for (int i = 0; i < filePathVoList.size(); i++) {
+                FilePathVo filePathVo = filePathVoList.get(i);
+                File file = SystemConstant.getFileTempDirVar(SystemConstant.JPG_PREFIX);
+                try {
+                    fileUploadService.downloadFile(filePathVo.getPath(), filePathVo.getUploadType(), filePathVo.getType(), file.getPath());
+                } catch (Exception e) {
+                    throw ExceptionResultEnum.ERROR.exception("读取考生原图失败" + ",考号[" + markStudent.getStudentCode() + "。" + e.getMessage());
+                }
+                fileMap.put(i + 1, new SheetImageDto(i + 1, file, filePathVo.getMd5(), AiUtil.imageDim(file)));
+                deleteFileList.add(file);
+            }
+
+            // 题目评卷区
+            List<PictureConfig> pictureConfigList = JSON.parseArray(markQuestion.getPicList(), PictureConfig.class);
+
+            List<MarkOcrStudentQuestion> markOcrStudentQuestions = new ArrayList<>();
+            for (int i = 0; i < pictureConfigList.size(); i++) {
+                PictureConfig pictureConfig = pictureConfigList.get(i);
+                SheetImageDto sheetImageDto = fileMap.get(pictureConfig.getI());
+                File file = SystemConstant.getFileTempDirVar(SystemConstant.JPG_PREFIX);
+                AiUtil.subImg(sheetImageDto, pictureConfig, file);
+
+                // OCR识别
+                String ocrResult;
+                try {
+                    ocrResult = ocrApiClient.forImage(AiUtil.signature(basicSchool), OcrType.HANDWRITING, UploadFile.build("image", "", file));
+                } catch (Exception e) {
+                    throw ExceptionResultEnum.ERROR.exception("OCR识别失败" + ",考号[" + markStudent.getStudentCode() + "。" + e.getMessage());
+                }
+                MarkOcrStudentQuestion markOcrStudentQuestion = new MarkOcrStudentQuestion();
+                markOcrStudentQuestion.setId(SystemConstant.getDbUuid());
+                markOcrStudentQuestion.setExamId(markStudent.getExamId());
+                markOcrStudentQuestion.setPaperNumber(markStudent.getPaperNumber());
+                markOcrStudentQuestion.setStudentId(studentId);
+                markOcrStudentQuestion.setQuestionId(questionId);
+                markOcrStudentQuestion.setNumber(i + 1);
+                markOcrStudentQuestion.setMd5(sheetImageDto.getMd5());
+                markOcrStudentQuestion.setOcrContent(ocrResult);
+                markOcrStudentQuestion.setCreateTime(System.currentTimeMillis());
+                markOcrStudentQuestions.add(markOcrStudentQuestion);
+                deleteFileList.add(file);
+            }
+            markOcrStudentQuestionService.saveBatch(markOcrStudentQuestions);
+            return markOcrStudentQuestions;
+        } catch (RuntimeException e) {
             throw ExceptionResultEnum.ERROR.exception(e.getMessage());
         } finally {
-            lockService.unlock(LockType.STUDENT, result.getStudentId());
-            lockService.unwatch(LockType.MARK_USER_QUESTION, userId);
-            lockService.unwatch(LockType.EXAM_SUBJECT, examId, paperNumber);
-        }
-
-        if (sr == null || !sr.isSuccess()) {
-            throw ExceptionResultEnum.ERROR.exception("评卷任务提交失败,请刷新页面");
-        }
-        return sr;
-    }
-
-    private SubmitResult submitResult(Long examId, String paperNumber, Long userId, MarkResult result) {
-        int spentAvg = result.getSpent() / result.getQuestionList().size();
-        int count = 0;
-        for (MarkResultQuestion markResultQuestion : result.getQuestionList()) {
-            try {
-                lockService.watch(LockType.QUESTION, markResultQuestion.getQuestionId());
-                markResultQuestion.setSpent(spentAvg);
-                MarkUserQuestion markUserQuestion = markUserQuestionService.getByExamIdAndPaperNumberAndQuestionIdAndUserId(examId, paperNumber, markResultQuestion.getQuestionId(), userId);
-                MarkQuestion markQuestion = markQuestionService.getById(markResultQuestion.getQuestionId());
-                MarkTask task = markTaskService.getById(markResultQuestion.getTaskId());
-                if (task != null && task.getExamId().equals(markUserQuestion.getExamId())
-                        && task.getPaperNumber().equals(markUserQuestion.getPaperNumber())
-                        && task.getQuestionId().equals(markUserQuestion.getQuestionId())) {
-                    // 问题卷
-                    Long now = System.currentTimeMillis();
-                    if (markResultQuestion.isProblem()) {
-                        // 状态更新
-                        if (markTaskService.updateProblemResult(task.getId(), userId, now, markResultQuestion.getSpent())) {
-                            saveProblemHistory(markResultQuestion, task, userId);
-                            updateMarkedCount(markUserQuestion.getExamId(), markUserQuestion.getPaperNumber(), markUserQuestion.getQuestionId());
-                            // 未评完
-                            resetStudentStatus(task.getStudentId());
-                            count++;
-                        }
-                    } else if (MarkTaskStatus.ARBITRATED.equals(task.getStatus()) || MarkTaskStatus.WAIT_ARBITRATE.equals(task.getStatus())) {
-                        // 待仲裁、已仲裁,直接跳过
-                        count++;
-                    } else if (markResultQuestion.getMarkerScore() <= markQuestion.getTotalScore()) {//阅卷分是否小于等于该组总分
-                        if (submitTask(task, userId, markQuestion, markResultQuestion)) {
-                            updateMarkedCount(markUserQuestion.getExamId(), markUserQuestion.getPaperNumber(), markUserQuestion.getQuestionId());
-                            count++;
-                        }
-                    }
+            if (CollectionUtils.isNotEmpty(deleteFileList)) {
+                for (File file : deleteFileList) {
+                    FileUtil.deleteFile(file);
                 }
-            } catch (Exception e) {
-                throw ExceptionResultEnum.ERROR.exception(e.getMessage());
-            } finally {
-                lockService.unwatch(LockType.QUESTION, markResultQuestion.getQuestionId());
-            }
-        }
-        if (CollectionUtils.size(result.getQuestionList()) == count) {
-            // 更新当前正在阅卷的questionId
-            markUserPaperService.update(examId, paperNumber, userId, null, null, result.getMarkedQuestionId());
-            return SubmitResult.success(result.getStudentId());
-        } else {
-            return SubmitResult.faile();
-        }
-    }
-
-    private void saveProblemHistory(MarkResultQuestion result, MarkTask task, Long userId) {
-        MarkProblemHistory history = markProblemHistoryService.findByTaskIdAndStatus(task.getId(),
-                MarkProblemStatus.WAITING);
-        if (history == null) {
-            history = new MarkProblemHistory();
-            history.setId(SystemConstant.getDbUuid());
-        }
-        history.setExamId(task.getExamId());
-        history.setSecretNumber(task.getSecretNumber());
-        history.setStudentCode(task.getStudentCode());
-        history.setStudentId(task.getStudentId());
-        history.setTaskId(task.getId());
-        history.setPaperNumber(task.getPaperNumber());
-        history.setQuestionId(task.getQuestionId());
-        history.setType(result.getProblemType());
-        if (MarkProblemType.OTHER.equals(result.getProblemType())) {
-            history.setRemark(result.getProblemRemark());
-        }
-        history.setCreateTime(System.currentTimeMillis());
-        history.setStatus(MarkProblemStatus.WAITING);
-        history.setUserId(userId);
-        markProblemHistoryService.saveOrUpdate(history);
-    }
-
-    private boolean submitTask(MarkTask task, Long userId, MarkQuestion markQuestion, MarkResultQuestion result) {
-        // 非本人领取的待评任务
-        if ((task.getStatus() == MarkTaskStatus.WAITING || task.getStatus() == MarkTaskStatus.REJECTED)
-                && !hasApplied(task, userId)) {
-            return false;
-        }
-        // 非本人的回评任务
-        if ((task.getStatus() == MarkTaskStatus.MARKED) && !task.getUserId().equals(userId)) {
-            return false;
-        }
-        // 是否多评情况下已处理过该考生评卷任务
-        if (markTaskService.countByStudentIdAndMarkerIdAndIdNotEqual(task.getStudentId(), task.getExamId(),
-                task.getPaperNumber(), task.getQuestionId(), userId, task.getId()) > 0) {
-            return false;
-        }
-        // 尝试提交评卷结果
-        Long now = System.currentTimeMillis();
-        //更新阅卷任务状态为已阅卷[阅卷分数,给分明细,阅卷时间,评卷时长]
-        if (!markTaskService.updateMarkerResult(task.getId(), MarkTaskStatus.MARKED, userId,
-                result, now, null, null, null, MarkTaskStatus.WAITING,
-                MarkTaskStatus.MARKED, MarkTaskStatus.REJECTED)) {
-            // 条件不符更新失败,直接返回
-            return false;
-        }
-        // 判断多评模式下是否需要仲裁
-        MarkArbitrateHistory history = null;
-        if (markQuestion.getArbitrateThreshold() != null && markQuestion.getArbitrateThreshold() > 0) {
-            // 多评模式
-            List<MarkTask> list = markTaskService.findByStudentIdAndQuestionIdAndStatus(task.getStudentId(), task.getQuestionId(), MarkTaskStatus.MARKED);
-            for (MarkTask other : list) {
-                // 本评卷任务或组长已打分,则跳过该任务
-                if (other.getId().equals(task.getId()) || other.getHeaderScore() != null) {
-                    continue;
-                }
-                // 分差超过阀值
-                if (Math.abs(other.getMarkerScore() - result.getMarkerScore()) > markQuestion.getArbitrateThreshold()) {
-                    history = buildArbitrateHistory(task, now);
-                    break;
-                }
-            }
-        }
-        if (history != null) {
-            // 保存仲裁记录
-            markArbitrateHistoryService.save(history);
-            // 触发仲裁后续状态更新
-            markTaskService.updateStatusByStudentIdAndQuestionId(task.getStudentId(), task.getQuestionId(), MarkTaskStatus.WAIT_ARBITRATE);
-            // 未评完
-            resetStudentStatus(task.getStudentId());
-        } else {
-            // 回评时,分数不一致,需要重新统分
-            if (task.getMarkerScore() != null && !task.getMarkerScore().equals(result.getMarkerScore())) {
-                resetStudentStatus(task.getStudentId());
-                markTaskService.resetHeaderByStudentIdAndQuestionId(task.getStudentId(), task.getQuestionId());
-                markHeaderHistoryService.deleteByStudentIdAndQuestionId(task.getStudentId(), task.getQuestionId());
             }
-            // 判断当前分组是否已完成评卷
-            markQuestion.setMarkScore(result.getMarkerScore());
-            checkStudentQuestion(task.getStudentId(), markQuestion);
-        }
-        return true;
-    }
-
-    private MarkArbitrateHistory buildArbitrateHistory(MarkTask task, Long now) {
-        MarkArbitrateHistory history = new MarkArbitrateHistory();
-        history.setId(SystemConstant.getDbUuid());
-        history.setExamId(task.getExamId());
-        history.setPaperNumber(task.getPaperNumber());
-        history.setQuestionId(task.getQuestionId());
-        history.setStudentId(task.getStudentId());
-        history.setStudentCode(task.getStudentCode());
-        history.setSecretNumber(task.getSecretNumber());
-        history.setStatus(MarkArbitrateStatus.WAITING);
-        history.setCreateTime(now);
-        return history;
-    }
-
-    @Override
-    public void checkStudentSubjectiveScore(Long examId, String coursePaperId) {
-        List<MarkStudent> markStudentList = markStudentService.listByExamIdAndCoursePaperId(examId, coursePaperId);
-        for (MarkStudent markStudent : markStudentList) {
-            resetStudentStatus(markStudent.getId());
-        }
-    }
-
-    @Override
-    @Transactional
-    public boolean rejectMarkTask(MarkTask markTask, Long userId, String reason) {
-        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(markTask.getExamId(), markTask.getPaperNumber());
-        if (MarkPaperStatus.FINISH.equals(markPaper.getStatus())) {
-            throw ExceptionResultEnum.MARK_PAPER_FINISH.exception();
-        }
-        MarkRejectHistory history = new MarkRejectHistory(markTask);
-        MarkStudent markStudent = markStudentService.getById(markTask.getStudentId());
-        history.setBasicStudentId(markStudent.getBasicStudentId());
-        history.setRejectUserId(userId);
-        history.setRejectReason(reason);
-        Long now = System.currentTimeMillis();
-        if (markTaskService.resetById(markTask.getId(), null, reason, userId, now, MarkTaskStatus.REJECTED)) {
-            markUserQuestionService.updateRejectCountByExamIdAndPaperNumberAndQuestionIdAndUserId(markTask.getExamId(), markTask.getPaperNumber(), markTask.getQuestionId(), markTask.getUserId());
-            markRejectHistoryService.save(history);
-//            markSubjectiveScoreService.updateRejected(markTask.getStudentId(), markTask.getQuestionId(), true);
-            markSubjectiveScoreService.deleteByStudentIdAndQuestionId(markTask.getStudentId(), markTask.getQuestionId());
-            resetStudentStatus(markTask.getStudentId());
-            markTaskService.resetHeaderByStudentIdAndQuestionId(markTask.getStudentId(), markTask.getQuestionId());
-            markHeaderHistoryService.deleteByStudentIdAndQuestionId(markTask.getStudentId(), markTask.getQuestionId());
-            updateMarkedCount(markTask.getExamId(), markTask.getPaperNumber(), markTask.getQuestionId());
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public void deleteMarkTask(MarkQuestion markQuestion, boolean b) {
-        // 正评相关数据
-        if (b) {
-            markArbitrateHistoryService.deleteByExamIdAndPaperNumberAndQuestionId(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-            markProblemHistoryService.deleteByExamIdAndPaperNumberAndQuestionId(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-            markTaskService.deleteByExamIdAndPaperNumberAndQuestionId(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-            markRejectHistoryService.deleteByExamIdAndPaperNumberAndQuestionId(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-        }
-        markSubjectiveScoreService.deleteByExamIdAndPaperNumberAndQuestionId(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-        // 释放本小题所有评卷员的任务
-        List<MarkUserQuestion> markUserQuestions = markUserQuestionService.listByExamIdAndPaperNumberAndQuestionId(markQuestion.getExamId(), markQuestion.getPaperNumber(), markQuestion.getId());
-        for (MarkUserQuestion markUserQuestion : markUserQuestions) {
-            releaseByMarkUserGroup(markUserQuestion);
-        }
-    }
-
-    @Override
-    public void releaseByMarkQuestion(MarkQuestion markQuestion) {
-        TaskLock taskLock = getTaskLock(markQuestion);
-        taskLock.clear();
-    }
-
-    @Override
-    public void resetMarkedQuestionId(MarkUserQuestion markUserQuestion) {
-        List<MarkQuestion> markQuestionList = markQuestionService.listByExamIdAndPaperNumberAndObjective(markUserQuestion.getExamId(), markUserQuestion.getPaperNumber(), false);
-        List<Long> list = markQuestionList.stream().map(MarkQuestion::getId).collect(Collectors.toList());
-        MarkUserPaper markUserPaper = markUserPaperService.getByExamIdAndPaperNumberAndUserId(markUserQuestion.getExamId(), markUserQuestion.getPaperNumber(), markUserQuestion.getUserId());
-        if (markUserPaper != null && QuestionModel.SINGLE.equals(markUserPaper.getQuestionModel()) && markUserPaper.getMarkedQuestionId() != null) {
-            int currentQuestionIdIndex = list.indexOf(markUserQuestion.getQuestionId());
-            int oldQuestionIdIndex = list.indexOf(markUserPaper.getMarkedQuestionId());
-            if (currentQuestionIdIndex < oldQuestionIdIndex) {
-                markUserPaper.setMarkedQuestionId(markUserQuestion.getQuestionId());
-                markUserPaperService.updateById(markUserPaper);
-            }
-        }
-    }
-
-    @Override
-    public void aiMark(Long examId, String paperNumber, Long questionId) {
-        List<MarkTask> markTasks = null;
-        int pageNumber = 1;
-        while (CollectionUtils.isNotEmpty(markTasks)) {
-            Set<Long> questions = new HashSet<>(Arrays.asList(questionId));
-            markTasks = markTaskService.findAiUnMarked(new Page<>(pageNumber, 20), examId, paperNumber, questionId);
-            if (markTasks.isEmpty()) {
-                break;
-            }
-            for (MarkTask t : markTasks) {
-                if (this.applyTask(examId, paperNumber, t.getStudentId(), null, questions, Arrays.asList(t.getId()))) {
-                    break;
-                }
-            }
-            pageNumber++;
         }
     }
 

+ 12 - 1
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkOcrStudentQuestionServiceImpl.java

@@ -1,11 +1,14 @@
 package com.qmth.teachcloud.mark.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.teachcloud.mark.entity.MarkOcrStudentQuestion;
 import com.qmth.teachcloud.mark.mapper.MarkOcrStudentQuestionMapper;
 import com.qmth.teachcloud.mark.service.MarkOcrStudentQuestionService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * <p>
  * 考生主观题小题OCR识别结果 服务实现类
@@ -17,4 +20,12 @@ import org.springframework.stereotype.Service;
 @Service
 public class MarkOcrStudentQuestionServiceImpl extends ServiceImpl<MarkOcrStudentQuestionMapper, MarkOcrStudentQuestion> implements MarkOcrStudentQuestionService {
 
+    @Override
+    public List<MarkOcrStudentQuestion> listByStudentIdAndQuestionId(Long studentId, Long questionId) {
+        QueryWrapper<MarkOcrStudentQuestion> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkOcrStudentQuestion::getStudentId, studentId)
+                .eq(MarkOcrStudentQuestion::getQuestionId, questionId)
+                .orderByAsc(MarkOcrStudentQuestion::getNumber);
+        return this.list(queryWrapper);
+    }
 }

+ 38 - 2
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/utils/AiUtil.java

@@ -1,6 +1,10 @@
 package com.qmth.teachcloud.mark.utils;
 
+import com.qmth.boot.core.retrofit.utils.SignatureInfo;
 import com.qmth.teachcloud.common.bean.dto.mark.PictureConfig;
+import com.qmth.teachcloud.common.entity.BasicSchool;
+import com.qmth.teachcloud.common.entity.BasicSemester;
+import com.qmth.teachcloud.mark.dto.ai.SheetImageDto;
 import com.qmth.teachcloud.mark.dto.mark.manage.TrackDTO;
 
 import javax.imageio.ImageIO;
@@ -15,13 +19,37 @@ import java.util.Iterator;
 
 public class AiUtil {
 
+    public static SignatureInfo signature(BasicSchool school) {
+        return SignatureInfo.secret(school.getAccessKey(), school.getAccessSecret());
+    }
+
+    /**
+     * 图片尺寸
+     *
+     * @param subFile 原图
+     */
+    public static int[] imageDim(File subFile) {
+        try {
+            BufferedImage image = ImageIO.read(subFile);
+            int width = image.getWidth();
+            int height = image.getHeight();
+
+            int[] ins = new int[2];
+            ins[0] = width;
+            ins[1] = height;
+            return ins;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * 生成评卷区在原图中的位置
      *
      * @param subFile       原图
      * @param pictureConfig 评卷区坐标
      */
-    public TrackDTO setOffset(File subFile, PictureConfig pictureConfig) {
+    public static TrackDTO setOffset(File subFile, PictureConfig pictureConfig) {
         try {
             BufferedImage image = ImageIO.read(subFile);
             int width = image.getWidth();
@@ -51,7 +79,7 @@ public class AiUtil {
      * @param height     调试
      * @param outFile    输出文件
      */
-    public File subImg(File sourceFile, int x, int y, int width, int height, File outFile) {
+    public static File subImg(File sourceFile, int x, int y, int width, int height, File outFile) {
         ImageInputStream iis = null;
         try {
             if (outFile.exists()) {
@@ -90,4 +118,12 @@ public class AiUtil {
         return null;
     }
 
+    public static void subImg(SheetImageDto sheetImageDto, PictureConfig pictureConfig, File outFile) {
+        File file = sheetImageDto.getFile();
+        int x = (int) (sheetImageDto.getWidth() * pictureConfig.getX());
+        int y = (int) (sheetImageDto.getHeight() * pictureConfig.getY());
+        int w = (int) (sheetImageDto.getWidth() * pictureConfig.getW());
+        int h = (int) (sheetImageDto.getHeight() * pictureConfig.getH());
+        subImg(file, x, y, w, h, outFile);
+    }
 }