Pārlūkot izejas kodu

3.4.5 update-20250416,新功能开发

xiaofei 2 mēneši atpakaļ
vecāks
revīzija
a4831725a5
17 mainītis faili ar 177 papildinājumiem un 41 dzēšanām
  1. 1 1
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/templete/service/impl/ImportLogicServiceImpl.java
  2. 6 1
      distributed-print/install/mysql/upgrade/3.4.5.sql
  3. 28 5
      distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkAiQuestionParamController.java
  4. 4 4
      distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkArchiveController.java
  5. 12 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/MarkQuestion.java
  6. 9 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/mark/manage/MarkGroupProgressDto.java
  7. 10 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/mark/setting/MarkGroupTaskDto.java
  8. 23 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/MarkOcrStudentQuestion.java
  9. 2 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkQuestionService.java
  10. 1 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkService.java
  11. 1 1
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/MarkTaskService.java
  12. 15 2
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkQuestionServiceImpl.java
  13. 39 25
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkServiceImpl.java
  14. 1 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkSyncServiceImpl.java
  15. 3 2
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkTaskServiceImpl.java
  16. 10 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/TaskServiceImpl.java
  17. 12 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/utils/AiUtil.java

+ 1 - 1
distributed-print-business/src/main/java/com/qmth/distributed/print/business/templete/service/impl/ImportLogicServiceImpl.java

@@ -842,7 +842,7 @@ public class ImportLogicServiceImpl implements ImportLogicService {
                                 Field declaredField = aClass.getDeclaredField(enumResult.getCode());
                                 declaredField.setAccessible(true);
                                 // 学生姓名、任课老师姓名存在英文名,只去掉前后空格
-                                declaredField.set(basicExamStudentImport, RequiredFieldsEnum.TEACHER_NAME.getCode().equals(enumResult.getCode()) || RequiredFieldsEnum.STUDENT_NAME.getCode().equals(enumResult.getCode()) ? entry.getValue().trim() : StringUtils.deleteWhitespace(entry.getValue()));
+                                declaredField.set(basicExamStudentImport, StringUtils.trim(entry.getValue()));
                             } catch (NoSuchFieldException e) {
                                 throw ExceptionResultEnum.ERROR.exception("未获取到表头为[" + entry.getKey() + "]的属性值");
                             } catch (IllegalAccessException e) {

+ 6 - 1
distributed-print/install/mysql/upgrade/3.4.5.sql

@@ -115,4 +115,9 @@ INSERT INTO `sys_privilege` (`id`, `name`, `url`, `type`, `parent_id`, `sequence
 
 UPDATE sys_privilege
 SET name='评卷设置', url='MarkSetting', `type`='MENU', parent_id=486, `sequence`=2, property='AUTH', related='1222,1223', enable=1, default_auth=0, front_display=1
-WHERE id=897;
+WHERE id=897;
+
+-- 2025-04-16
+ALTER TABLE `mark_ocr_student_question`
+    ADD COLUMN `image_width` INT NULL DEFAULT 0 COMMENT '图片宽度' AFTER `md5`,
+ADD COLUMN `image_height` INT NULL DEFAULT 0 COMMENT '图片高度' AFTER `image_width`;

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

@@ -5,6 +5,7 @@ import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.boot.api.exception.ApiException;
 import com.qmth.distributed.print.business.bean.result.EditResult;
 import com.qmth.teachcloud.common.contant.SystemConstant;
+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.FieldUniqueEnum;
@@ -13,10 +14,7 @@ import com.qmth.teachcloud.common.util.RedisUtil;
 import com.qmth.teachcloud.common.util.Result;
 import com.qmth.teachcloud.common.util.ResultUtil;
 import com.qmth.teachcloud.common.util.ServletUtil;
-import com.qmth.teachcloud.mark.entity.MarkAiQuestionLevel;
-import com.qmth.teachcloud.mark.entity.MarkAiQuestionParam;
-import com.qmth.teachcloud.mark.entity.MarkAiQuestionPoint;
-import com.qmth.teachcloud.mark.entity.MarkPaper;
+import com.qmth.teachcloud.mark.entity.*;
 import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
 import com.qmth.teachcloud.mark.service.*;
 import io.swagger.annotations.*;
@@ -31,6 +29,7 @@ import org.springframework.web.bind.annotation.*;
 import javax.annotation.Resource;
 import javax.validation.Valid;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -64,6 +63,8 @@ public class MarkAiQuestionParamController {
     private MarkPaperService markPaperService;
     @Resource
     private MarkTaskService markTaskService;
+    @Resource
+    private MarkService markService;
 
     @Resource
     RedisUtil redisUtil;
@@ -89,7 +90,9 @@ public class MarkAiQuestionParamController {
             MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber());
             Objects.requireNonNull(markPaper, "试卷信息为空");
             Objects.requireNonNull(markPaper.getAiMark(), "AI评卷信息为空");
-            if (!MarkPaperAiMark.NONE.equals(markPaper.getAiMark()) && markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndAiMarkedAndStatusNotIn(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId(), false, Arrays.asList(MarkTaskStatus.WAITING)) > 0) {
+            if (!MarkPaperAiMark.NONE.equals(markPaper.getAiMark())
+                    && (CollectionUtils.isNotEmpty(markAiQuestionParam.getPointList()) || CollectionUtils.isEmpty(markAiQuestionParam.getLevelList()))
+                    && markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndTaskNumberAndAiMarkedAndStatusNotIn(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId(), 1, false, Arrays.asList(MarkTaskStatus.WAITING)) > 0) {
                 throw ExceptionResultEnum.ERROR.exception("评卷员已开始评卷,请先重置评卷任务后再设置");
             }
             switch (markAiQuestionParam.getMode()) {
@@ -97,6 +100,11 @@ public class MarkAiQuestionParamController {
                     if (CollectionUtils.isEmpty(markAiQuestionParam.getPointList())) {
                         throw ExceptionResultEnum.ERROR.exception("得分点信息为空");
                     }
+                    // 最小分校验
+                    MarkAiQuestionPoint markAiQuestionPoint = markAiQuestionParam.getPointList().stream().min(Comparator.comparingDouble(MarkAiQuestionPoint::getScore)).get();
+                    if(markAiQuestionParam.getMinScore() > markAiQuestionPoint.getScore()){
+                        throw ExceptionResultEnum.ERROR.exception("最小分不能超过得分点的最小分值");
+                    }
                     if (Objects.isNull(markAiQuestionParam.getId())) {//新增
                         markAiQuestionParam.insertInfo(sysUser.getId());
                         markAiQuestionParam.getPointList().stream().peek(s -> {
@@ -137,6 +145,11 @@ public class MarkAiQuestionParamController {
                     if (CollectionUtils.isEmpty(markAiQuestionParam.getLevelList())) {
                         throw ExceptionResultEnum.ERROR.exception("档次信息为空");
                     }
+                    // 最小分校验
+                    MarkAiQuestionLevel markAiQuestionLevel = markAiQuestionParam.getLevelList().stream().min(Comparator.comparingDouble(MarkAiQuestionLevel::getMinScore)).get();
+                    if(markAiQuestionParam.getMinScore() > markAiQuestionLevel.getMinScore()){
+                        throw ExceptionResultEnum.ERROR.exception("最小分不能超过档次的最小分值");
+                    }
                     if (Objects.isNull(markAiQuestionParam.getId())) {//新增
                         markAiQuestionParam.insertInfo(sysUser.getId());
                         markAiQuestionParam.getLevelList().stream().peek(s -> {
@@ -180,6 +193,16 @@ public class MarkAiQuestionParamController {
                 markQuestionService.updateClearOcrResult(markAiQuestionParam.getQuestionId(), true);
             }
             markAiQuestionParamService.saveOrUpdate(markAiQuestionParam);
+            markQuestionService.updateAiMark(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId());
+
+            if (markAiQuestionParamService.existMarkAiQuestionPointOrLevel(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId())
+                    && markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndTaskNumberAndAiMarkedAndStatusNotIn(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId(), 1, false, null) > 0) {
+                MarkQuestion markQuestion = markQuestionService.getById(markAiQuestionParam.getQuestionId());
+                markService.deleteMarkTask(markQuestion, true);
+                markService.checkStudentSubjectiveScore(markAiQuestionParam.getExamId(), markPaper.getCoursePaperId());
+                markService.updateMarkedCount(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId());
+                markService.updateMarkTaskCount(markAiQuestionParam.getExamId(), markAiQuestionParam.getPaperNumber(), markAiQuestionParam.getQuestionId());
+            }
         } catch (Exception e) {
             log.error(SystemConstant.LOG_ERROR, e);
             if (e instanceof DuplicateKeyException) {

+ 4 - 4
distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkArchiveController.java

@@ -61,7 +61,7 @@ public class MarkArchiveController {
 
     @ApiOperation(value = "总量分析(导出)")
     @RequestMapping(value = "score/list/export", method = RequestMethod.POST)
-    public Result scoreListExport(@Validated ArchiveScoreQuery query,HttpServletResponse response) throws IOException {
+    public Result scoreListExport(@Validated ArchiveScoreQuery query, HttpServletResponse response) throws IOException {
         markPaperService.scoreListExport(query, response);
         return ResultUtil.ok();
     }
@@ -88,8 +88,8 @@ public class MarkArchiveController {
 
     @ApiOperation(value = "成绩报告导出")
     @RequestMapping(value = "/score/report/download", method = RequestMethod.POST)
-    public Result scoreReportDownload(@Validated ArchiveStudentQuery query,MultipartFile file, HttpServletResponse response) {
-        return ResultUtil.success(markStudentService.scoreReportDownload(query,file, response));
+    public Result scoreReportDownload(@Validated ArchiveStudentQuery query, MultipartFile file, HttpServletResponse response) {
+        return ResultUtil.success(markStudentService.scoreReportDownload(query, file, response));
     }
 
     @ApiOperation(value = "成绩报告")
@@ -109,7 +109,7 @@ public class MarkArchiveController {
         markStudentService.historyExport(query, response);
     }
 
-    @ApiOperation(value = "考生所有题目信息")
+    @ApiOperation(value = "归档轨迹图")
     @RequestMapping(value = "student/question/track", method = RequestMethod.POST)
     public Result studentQuestionTrack(@ApiParam(value = "考生ID", required = true) @RequestParam Long studentId) {
         return ResultUtil.ok(markStudentService.studentQuestionTrack(studentId));

+ 12 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/MarkQuestion.java

@@ -137,6 +137,9 @@ public class MarkQuestion extends BaseEntity implements Serializable {
     @TableField(exist = false)
     private List<PictureConfig> pictureConfigs;
 
+    @TableField(exist = false)
+    private Boolean aiQuestionPramSet;
+
     public Long getExamId() {
         return examId;
     }
@@ -393,6 +396,15 @@ public class MarkQuestion extends BaseEntity implements Serializable {
     public List<MarkConfigItem> getPictureConfigList() {
         return MarkConfigItem.parse(this.picList);
     }
+
+    public Boolean getAiQuestionPramSet() {
+        return aiQuestionPramSet;
+    }
+
+    public void setAiQuestionPramSet(Boolean aiQuestionPramSet) {
+        this.aiQuestionPramSet = aiQuestionPramSet;
+    }
+
     @Override
     public String toString() {
         return "MarkQuestion{" +

+ 9 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/mark/manage/MarkGroupProgressDto.java

@@ -17,6 +17,7 @@ public class MarkGroupProgressDto {
     private Integer arbitrateCount;
     private String questionNumber;
     private Boolean aiMark;
+    private Boolean enableAi;
 
     public Long getQuestionId() {
         return questionId;
@@ -105,4 +106,12 @@ public class MarkGroupProgressDto {
     public void setAiMark(Boolean aiMark) {
         this.aiMark = aiMark;
     }
+
+    public Boolean getEnableAi() {
+        return enableAi;
+    }
+
+    public void setEnableAi(Boolean enableAi) {
+        this.enableAi = enableAi;
+    }
 }

+ 10 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/mark/setting/MarkGroupTaskDto.java

@@ -18,6 +18,8 @@ public class MarkGroupTaskDto {
     private Boolean doubleMark;
     // 是否开启填空题整体设置评卷员(开关)
     private Boolean mergeMarker;
+    // 是否开启AI评卷
+    private Boolean aiMarkSet;
 
     public Long getExamId() {
         return examId;
@@ -66,4 +68,12 @@ public class MarkGroupTaskDto {
     public void setDoubleMark(Boolean doubleMark) {
         this.doubleMark = doubleMark;
     }
+
+    public Boolean getAiMarkSet() {
+        return aiMarkSet;
+    }
+
+    public void setAiMarkSet(Boolean aiMarkSet) {
+        this.aiMarkSet = aiMarkSet;
+    }
 }

+ 23 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/MarkOcrStudentQuestion.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import java.io.Serializable;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import io.swagger.models.auth.In;
 
 /**
  * <p>
@@ -42,6 +43,11 @@ public class MarkOcrStudentQuestion implements Serializable {
     @ApiModelProperty(value = "图片md5")
     private String md5;
 
+    @ApiModelProperty(value = "图片宽度")
+    private Integer imageWidth;
+    @ApiModelProperty(value = "图片高度")
+    private Integer imageHeight;
+
     @ApiModelProperty(value = "ocr识别内容")
     private String ocrContent;
 
@@ -97,6 +103,23 @@ public class MarkOcrStudentQuestion implements Serializable {
     public void setMd5(String md5) {
         this.md5 = md5;
     }
+
+    public Integer getImageWidth() {
+        return imageWidth;
+    }
+
+    public void setImageWidth(Integer imageWidth) {
+        this.imageWidth = imageWidth;
+    }
+
+    public Integer getImageHeight() {
+        return imageHeight;
+    }
+
+    public void setImageHeight(Integer imageHeight) {
+        this.imageHeight = imageHeight;
+    }
+
     public String getOcrContent() {
         return ocrContent;
     }

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

@@ -126,4 +126,6 @@ public interface MarkQuestionService extends IService<MarkQuestion> {
     boolean updateClearOcrResult(Long questionId, Boolean clearOcrResult);
 
     void aiMarkByMarkPaper(MarkPaper markPaper);
+
+    void updateAiMark(Long examId, String paperNumber, Long questionId);
 }

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

@@ -38,6 +38,7 @@ public interface MarkService {
 
     void resetMarker(MarkUserQuestion markUserGroup);
 
+    void updateMarkTaskCount(Long examId, String paperNumber, Long questionId);
     void updateMarkedCount(Long examId, String paperNumber, Long questionId);
 
     void updatePersonTask(Long examId, String paperNumber, Long questionId);

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

@@ -37,7 +37,7 @@ public interface MarkTaskService extends IService<MarkTask> {
 
     List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndStatusNotIn(Long examId, String paperNumber, Long questionId, Long userId, List<MarkTaskStatus> statusList);
     List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndAiMarkedAndStatusNotIn(Long examId, String paperNumber, Long questionId, Boolean aiMarked, List<MarkTaskStatus> statusList);
-    int countByExamIdAndPaperNumberAndQuestionIdAndAiMarkedAndStatusNotIn(Long examId, String paperNumber, Long questionId, Boolean aiMarked, List<MarkTaskStatus> statusList);
+    int countByExamIdAndPaperNumberAndQuestionIdAndTaskNumberAndAiMarkedAndStatusNotIn(Long examId, String paperNumber, Long questionId, Integer taskNumber, Boolean aiMarked, List<MarkTaskStatus> statusList);
 
     boolean resetById(Long markTaskId, Long userId, String rejectReason, Long rejectId, Long date, MarkTaskStatus newStatus);
 

+ 15 - 2
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkQuestionServiceImpl.java

@@ -589,6 +589,7 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
             // 评卷员
             List<MarkUser> markUserList = markUserQuestionService.listGroupUserByExamIdAndPaperNumberAndQuestionIdAndClassName(examId, paperNumber, m.getId(), null);
             m.setMarkers(markUserList.stream().filter(MarkUser::getEnable).collect(Collectors.toList()));
+            m.setAiQuestionPramSet(markAiQuestionParamService.existMarkAiQuestionPointOrLevel(examId, paperNumber, m.getId()));
         }
         markGroupTaskDto.setQuestions(markQuestionList);
         // 分班阅参数
@@ -596,6 +597,7 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
         markGroupTaskDto.setClassMark(markPaper != null && markPaper.getClassMark());
         markGroupTaskDto.setDoubleMark(markPaper != null && markPaper.getDoubleMark());
         markGroupTaskDto.setMergeMarker(markPaper != null && markPaper.getMergeMarker());
+        markGroupTaskDto.setAiMarkSet(markPaper != null && !MarkPaperAiMark.NONE.equals(markPaper.getAiMark()));
         return markGroupTaskDto;
     }
 
@@ -672,6 +674,7 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
             markGroupProgressDto.setPercent(Calculator.divide2String(Calculator.multiply(markGroupProgressDto.getMarkedCount(), 100), Double.valueOf(markGroupProgressDto.getTaskCount()), 2));
             markGroupProgressDto.setArbitrateCount(markArbitrateHistoryService.waitArbitrateCount(examId, paperNumber, markQuestion.getId(), null));
             markGroupProgressDto.setAiMark(markQuestion.getAiMark() != null && !MarkPaperAiMark.NONE.equals(markQuestion.getAiMark()));
+            markGroupProgressDto.setEnableAi(markQuestion.getEnableAi());
             markGroupProgressDtoList.add(markGroupProgressDto);
         }
         markGroupSummaryProgressDto.setGroupInfo(markGroupProgressDtoList);
@@ -930,8 +933,8 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
     @Override
     public boolean updateClearOcrResult(Long questionId, Boolean clearOcrResult) {
         UpdateWrapper<MarkQuestion> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().eq(MarkQuestion::getClearOcrResult, clearOcrResult)
-                .set(MarkQuestion::getId, questionId);
+        updateWrapper.lambda().set(MarkQuestion::getClearOcrResult, clearOcrResult)
+                .eq(MarkQuestion::getId, questionId);
         return this.update(updateWrapper);
     }
 
@@ -944,4 +947,14 @@ public class MarkQuestionServiceImpl extends ServiceImpl<MarkQuestionMapper, Mar
             }
         }
     }
+
+    @Override
+    public void updateAiMark(Long examId, String paperNumber, Long questionId) {
+        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
+        boolean isExist = markAiQuestionParamService.existMarkAiQuestionPointOrLevel(examId, paperNumber, questionId);
+        UpdateWrapper<MarkQuestion> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkQuestion::getAiMark, isExist && !MarkPaperAiMark.NONE.equals(markPaper.getAiMark()) ? markPaper.getAiMark() : MarkPaperAiMark.NONE)
+                .eq(MarkQuestion::getId, questionId);
+        this.update(updateWrapper);
+    }
 }

+ 39 - 25
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkServiceImpl.java

@@ -8,6 +8,7 @@ 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.AutoScoreModel;
 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;
@@ -753,7 +754,8 @@ public class MarkServiceImpl implements MarkService {
         }
     }
 
-    private void updateMarkTaskCount(Long examId, String paperNumber, Long questionId) {
+    @Override
+    public void updateMarkTaskCount(Long examId, String paperNumber, Long questionId) {
         int count = markTaskService.countByExamIdAndPaperNumberAndQuestionIdAndUserId(examId, paperNumber, questionId,
                 null);
         markQuestionService.updateTaskCount(questionId, count);
@@ -1528,7 +1530,8 @@ public class MarkServiceImpl implements MarkService {
             return;
         }
         // AI评卷且未设置评卷参数
-        if (!MarkPaperAiMark.NONE.equals(markQuestion.getAiMark()) && !markAiQuestionParamService.existMarkAiQuestionPointOrLevel(examId, paperNumber, questionId)) {
+        if (MarkPaperAiMark.NONE.equals(markQuestion.getAiMark())
+                || !markAiQuestionParamService.existMarkAiQuestionPointOrLevel(examId, paperNumber, questionId)) {
             return;
         }
 
@@ -1542,33 +1545,36 @@ public class MarkServiceImpl implements MarkService {
             do {
                 Set<Long> questions = new HashSet<>(Arrays.asList(questionId));
                 markTasks = markTaskService.findAiUnMarked(new Page<>(pageNumber, 20), examId, paperNumber, questionId);
-                if (markTasks.isEmpty() || markQuestion.getEnableAi()) {
+                if (markTasks.isEmpty() || !markQuestion.getEnableAi()) {
                     break;
                 }
                 for (MarkTask t : markTasks) {
-                    if (this.applyTask(examId, paperNumber, t.getStudentId(), null, questions, Arrays.asList(t.getId()))) {
-                        break;
+                    if (this.applyTask(examId, paperNumber, t.getStudentId(), aiUser.getId(), questions, Arrays.asList(t.getId()))) {
+                        continue;
                     }
                     // 考生作答
                     try {
                         long startTime = System.currentTimeMillis();
-                        Map<Integer, SheetImageDto> fileMap = new HashMap<>();
-                        String studentQuestionAnswer = this.mergeQuestionOcrResult(this.listStudentQuestionOcrResult(basicSchool, t.getStudentId(), t.getQuestionId(), fileMap));
+                        List<MarkOcrStudentQuestion> markOcrStudentQuestions = this.listStudentQuestionOcrResult(basicSchool, t.getStudentId(), t.getQuestionId());
+                        String studentQuestionAnswer = this.mergeQuestionOcrResult(markOcrStudentQuestions);
 
                         AutoScoreRequest request = new AutoScoreRequest();
                         request.setSubjectName(basicCourse.getName());
+                        request.setScoreModel(AutoScoreModel.valueOf(markAiQuestionParam.getMode().name()));
                         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));
+                        AutoScoreResult autoScoreResult = aiService.autoScore(request, AiUtil.signatureAi(basicSchool));
                         long endTime = System.currentTimeMillis();
-                        submitAiTask(t, aiUser.getId(), markQuestion, autoScoreResult, buildTrack(autoScoreResult, markQuestion, fileMap), endTime - startTime);
+                        submitAiTask(t, aiUser.getId(), markQuestion, autoScoreResult, buildTrack(autoScoreResult, markQuestion, markOcrStudentQuestions), endTime - startTime);
                     } catch (Exception e) {
                         t.setAiMarkErrorMsg(e.getMessage());
                     } finally {
+                        this.releaseStudent(examId, paperNumber, t.getStudentId(), aiUser.getId());
+                        this.updateMarkedCount(examId, paperNumber, t.getQuestionId());
                         t.setAiMarkErrorMsg(null);
                         markTaskService.updateAiMarkErrorMsg(t);
                     }
@@ -1593,16 +1599,14 @@ public class MarkServiceImpl implements MarkService {
             return;
         }
         if (markPaper.getStatus() == MarkPaperStatus.FORMAL) {
+            List<MarkTaskStatus> statusList = Arrays.asList(MarkTaskStatus.WAITING);
+            boolean pointOrLevel = markAiQuestionParamService.existMarkAiQuestionPointOrLevel(examId, paperNumber, questionId);
             // 未设置AI智能评卷参数,删除所有任务
-            if (!markAiQuestionParamService.existMarkAiQuestionPointOrLevel(examId, paperNumber, questionId)) {
-                markTaskService.deleteByExamIdAndPaperNumberAndQuestionId(examId, paperNumber, questionId);
-                markSubjectiveScoreService.deleteByExamIdAndPaperNumberAndQuestionId(examId, paperNumber, questionId);
-                markRejectHistoryService.deleteByExamIdAndPaperNumberAndQuestionId(examId, paperNumber, questionId);
-                markArbitrateHistoryService.deleteByExamIdAndPaperNumberAndQuestionId(examId, paperNumber, questionId);
+            if (!pointOrLevel) {
+                this.deleteMarkTask(markQuestion, true);
                 this.checkStudentSubjectiveScore(examId, markPaper.getCoursePaperId());
             } else {
                 // 遍历相关评卷任务的模式
-                List<MarkTaskStatus> statusList = Arrays.asList(MarkTaskStatus.WAITING);
                 List<MarkTask> markTaskList = markTaskService.listByExamIdAndPaperNumberAndQuestionIdAndAiMarkedAndStatusNotIn(examId, paperNumber, questionId, true, statusList);
                 for (MarkTask markTask : markTaskList) {
                     Long studentId = markTask.getStudentId();
@@ -1621,24 +1625,27 @@ public class MarkServiceImpl implements MarkService {
                 markOcrStudentQuestionService.deleteByExamIdAndPaperNumberAndQuestionId(examId, paperNumber, questionId);
                 markQuestionService.updateClearOcrResult(questionId, false);
             }
+
+            this.updateMarkTaskCount(examId, paperNumber, questionId);
+            this.updateMarkedCount(examId, paperNumber, questionId);
         }
-        this.updateMarkedCount(examId, paperNumber, questionId);
     }
 
-    private List<TrackDTO> buildTrack(AutoScoreResult autoScoreResult, MarkQuestion markQuestion, Map<Integer, SheetImageDto> fileMap) {
+    private List<TrackDTO> buildTrack(AutoScoreResult autoScoreResult, MarkQuestion markQuestion, List<MarkOcrStudentQuestion> markOcrStudentQuestions) {
         List<TrackDTO> list = new ArrayList<>();
-        if (fileMap.isEmpty()) {
+        if (markOcrStudentQuestions.isEmpty()) {
             return list;
         }
         double[] doubles = autoScoreResult.getStepScore();
         // 轨迹打印在第一个评卷区上
         List<MarkConfigItem> pictureConfigList = markQuestion.getPictureConfigList();
         MarkConfigItem markConfigItem = pictureConfigList.get(0);
-        SheetImageDto sheetImageDto = fileMap.get(markConfigItem.getI());
-        int step = 10;
+//        SheetImageDto sheetImageDto = fileMap.get(markConfigItem.getI());
+        MarkOcrStudentQuestion markOcrStudentQuestion = markOcrStudentQuestions.stream().filter(m -> m.getNumber().equals(markConfigItem.getI())).findFirst().get();
+        int step = 150;
         for (int i = 0; i < doubles.length; i++) {
-            int offsetX = (int) (sheetImageDto.getWidth() * markConfigItem.getX()) + step * (i + 1);
-            int offsetY = (int) (sheetImageDto.getHeight() * markConfigItem.getY()) + step;
+            int offsetX = (int) (markOcrStudentQuestion.getImageWidth() * markConfigItem.getX()) + 200 + step * (i + 1);
+            int offsetY = (int) (markOcrStudentQuestion.getImageHeight() * markConfigItem.getY()) + 300;
             list.add(new TrackDTO(markQuestion, i + 1, doubles[i], markConfigItem.getI(), offsetX, offsetY));
         }
         return list;
@@ -1685,7 +1692,7 @@ public class MarkServiceImpl implements MarkService {
         return markOcrStudentQuestionList.stream().filter(m -> StringUtils.isNotBlank(m.getOcrContent())).map(MarkOcrStudentQuestion::getOcrContent).collect(Collectors.joining(SystemConstant.COMMA_OF_CHINESE));
     }
 
-    private List<MarkOcrStudentQuestion> listStudentQuestionOcrResult(BasicSchool basicSchool, Long studentId, Long questionId, Map<Integer, SheetImageDto> fileMap) {
+    private List<MarkOcrStudentQuestion> listStudentQuestionOcrResult(BasicSchool basicSchool, Long studentId, Long questionId) {
         List<MarkOcrStudentQuestion> markOcrStudentQuestionList = markOcrStudentQuestionService.listByStudentIdAndQuestionId(studentId, questionId);
         if (CollectionUtils.isNotEmpty(markOcrStudentQuestionList)) {
             return markOcrStudentQuestionList;
@@ -1702,6 +1709,7 @@ public class MarkServiceImpl implements MarkService {
             throw ExceptionResultEnum.ERROR.exception("未查询到原图" + ",考号[" + markStudent.getStudentCode());
         }
         List<File> deleteFileList = new ArrayList<>();
+        Map<Integer, SheetImageDto> fileMap = new HashMap<>();
         //考生题卡原图
         try {
             List<FilePathVo> filePathVoList = markStudent.listSheetPath();
@@ -1709,7 +1717,7 @@ public class MarkServiceImpl implements MarkService {
                 FilePathVo filePathVo = filePathVoList.get(i);
                 File file = SystemConstant.getFileTempDirVar(SystemConstant.JPG_PREFIX);
                 try {
-                    fileUploadService.downloadFile(filePathVo.getPath(), filePathVo.getUploadType(), filePathVo.getType(), file.getPath());
+                    fileUploadService.downloadFile(filePathVo.getType(), filePathVo.getUploadType(), filePathVo.getPath(), file.getPath());
                 } catch (Exception e) {
                     throw ExceptionResultEnum.ERROR.exception("读取考生原图失败" + ",考号[" + markStudent.getStudentCode() + "。" + e.getMessage());
                 }
@@ -1730,7 +1738,7 @@ public class MarkServiceImpl implements MarkService {
                 // OCR识别
                 String ocrResult;
                 try {
-                    ocrResult = ocrApiClient.forImage(AiUtil.signature(basicSchool), OcrType.HANDWRITING, UploadFile.build("image", "", file));
+                    ocrResult = ocrApiClient.forImage(AiUtil.signatureOcr(basicSchool), OcrType.HANDWRITING, UploadFile.build("image", "", file));
                 } catch (Exception e) {
                     throw ExceptionResultEnum.ERROR.exception("OCR识别失败" + ",考号[" + markStudent.getStudentCode() + "。" + e.getMessage());
                 }
@@ -1742,6 +1750,8 @@ public class MarkServiceImpl implements MarkService {
                 markOcrStudentQuestion.setQuestionId(questionId);
                 markOcrStudentQuestion.setNumber(i + 1);
                 markOcrStudentQuestion.setMd5(sheetImageDto.getMd5());
+                markOcrStudentQuestion.setImageWidth(sheetImageDto.getWidth());
+                markOcrStudentQuestion.setImageHeight(sheetImageDto.getHeight());
                 markOcrStudentQuestion.setOcrContent(ocrResult);
                 markOcrStudentQuestion.setCreateTime(System.currentTimeMillis());
                 markOcrStudentQuestions.add(markOcrStudentQuestion);
@@ -1801,6 +1811,10 @@ public class MarkServiceImpl implements MarkService {
             markTaskService.updateStatusByStudentIdAndQuestionId(task.getStudentId(), task.getQuestionId(), MarkTaskStatus.WAIT_ARBITRATE);
             // 未评完
             resetStudentStatus(task.getStudentId());
+        } else {
+            // 判断当前分组是否已完成评卷
+            markQuestion.setMarkScore(result.getTotalScore());
+            checkStudentQuestion(task.getStudentId(), markQuestion);
         }
         return true;
     }

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

@@ -112,6 +112,7 @@ public class MarkSyncServiceImpl implements MarkSyncService {
         } finally {
             lockService.unlock(LockType.QUESTION, questionId);
             lockService.unlock(LockType.EXAM_SUBJECT, examId, paperNumber);
+            lockService.unlock(LockType.AI_TASK_RESET, questionId);
         }
     }
 

+ 3 - 2
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkTaskServiceImpl.java

@@ -155,13 +155,14 @@ public class MarkTaskServiceImpl extends ServiceImpl<MarkTaskMapper, MarkTask> i
     }
 
     @Override
-    public int countByExamIdAndPaperNumberAndQuestionIdAndAiMarkedAndStatusNotIn(Long examId, String paperNumber, Long questionId, Boolean aiMarked, List<MarkTaskStatus> statusList) {
+    public int countByExamIdAndPaperNumberAndQuestionIdAndTaskNumberAndAiMarkedAndStatusNotIn(Long examId, String paperNumber, Long questionId, Integer taskNumber, Boolean aiMarked, List<MarkTaskStatus> statusList) {
         QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
         queryWrapper.lambda().eq(MarkTask::getExamId, examId)
                 .eq(MarkTask::getPaperNumber, paperNumber)
                 .eq(MarkTask::getQuestionId, questionId)
+                .eq(MarkTask::getTaskNumber, taskNumber)
                 .eq(MarkTask::getAiMarked, aiMarked)
-                .notIn(MarkTask::getStatus, statusList);
+                .notIn(CollectionUtils.isNotEmpty(statusList), MarkTask::getStatus, statusList);
         return this.count(queryWrapper);
     }
 

+ 10 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/TaskServiceImpl.java

@@ -49,6 +49,8 @@ public class TaskServiceImpl implements TaskService {
     private MarkArchiveStudentService markArchiveStudentService;
     @Resource
     private MarkArchiveStudentCardService markArchiveStudentCardService;
+    @Resource
+    private MarkUserQuestionService markUserQuestionService;
 
     @Override
     public Task build(MarkArbitrateHistory markArbitrateHistory, MarkQuestion markQuestion) {
@@ -293,12 +295,20 @@ public class TaskServiceImpl implements TaskService {
             if (!scoreList.isEmpty() && scoreList.size() == sList.size()) {
                 step.setMarkerScore(score);
             }
+
             // 增加阅卷轨迹列表获取
             List<MarkTask> markTaskList = markTaskService.listByStudentIdAndQuestionId(markStudent.getId(), question.getId());
+            List<Long> markTaskUserIds = markTaskList.stream().filter(m -> m.getUserId() != null).map(MarkTask::getUserId).collect(Collectors.toList());
+            List<MarkUserQuestion> markUserQuestions = markUserQuestionService.listByExamIdAndPaperNumberAndQuestionId(examId, paperNumber, question.getId());
+            List<Long> userIds = markUserQuestions.stream().filter(m -> m.getEnable() && !markTaskUserIds.contains(m.getUserId())).map(MarkUserQuestion::getUserId).collect(Collectors.toList());
             // 不管单评还是多评显示所有评卷员给分轨迹
             for (MarkTask markTask : markTaskList) {
                 for (TrackDTO track : markTask.listMarkerTrack()) {
                     SysUser user = sysUserService.getByUserId(markTask.getUserId());
+                    // 替换AI评卷员为真实老师
+                    if (CollectionUtils.isNotEmpty(userIds) && user != null && user.getLoginName().equals(SystemConstant.AI_USER)) {
+                        user = sysUserService.getByUserId(userIds.get(0));
+                    }
                     track.setUserId(markTask.getUserId());
                     track.setUserName(user.getRealName() + "(" + user.getLoginName() + ")");
                     step.addMarkTrack(track);

+ 12 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/utils/AiUtil.java

@@ -23,6 +23,18 @@ public class AiUtil {
         return SignatureInfo.secret(school.getAccessKey(), school.getAccessSecret());
     }
 
+    public static SignatureInfo signatureOcr(BasicSchool school) {
+         String accessKey = "41a9c68b71c9486db62de90980bd7e9a";
+         String accessSecret = "yhe22bNXMRljatkvJA4f56by9dIAJBjv";
+        return SignatureInfo.secret(accessKey, accessSecret);
+    }
+
+    public static SignatureInfo signatureAi(BasicSchool school) {
+        String accessKey = "ce38d020b3e14badb73026b2a376cafc";
+        String accessSecret = "bRtNV77TrhyP32AL8tG2C9wTLrO3yyRj";
+        return SignatureInfo.secret(accessKey, accessSecret);
+    }
+
     /**
      * 图片尺寸
      *