Эх сурвалжийг харах

新增ai智能评卷导出

wangliang 2 сар өмнө
parent
commit
bb3b50326a

+ 0 - 1
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/GradePaperStructService.java

@@ -5,7 +5,6 @@ import com.qmth.distributed.print.business.bean.dto.open.PaperStructure;
 import com.qmth.distributed.print.business.bean.params.analyze.GradePaperStructParam;
 import com.qmth.distributed.print.business.bean.params.analyze.GradePaperStructParam;
 import com.qmth.distributed.print.business.bean.result.analyze.GradePaperStructResult;
 import com.qmth.distributed.print.business.bean.result.analyze.GradePaperStructResult;
 import com.qmth.distributed.print.business.entity.GradePaperStruct;
 import com.qmth.distributed.print.business.entity.GradePaperStruct;
-import com.qmth.teachcloud.common.entity.MarkQuestion;
 import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.mark.dto.mark.MarkQuestionAnswerVo;
 import com.qmth.teachcloud.mark.dto.mark.MarkQuestionAnswerVo;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;

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

@@ -74,4 +74,12 @@ INSERT INTO sys_privilege
 VALUES(1224, 'AI智能评卷设置查询', '/api/admin/set/ai/mark/set/select', 'URL', 508, 21, 'AUTH', NULL, 1, 0, 1);
 VALUES(1224, 'AI智能评卷设置查询', '/api/admin/set/ai/mark/set/select', 'URL', 508, 21, 'AUTH', NULL, 1, 0, 1);
 INSERT INTO sys_privilege
 INSERT INTO sys_privilege
 (id, name, url, `type`, parent_id, `sequence`, property, related, enable, default_auth, front_display)
 (id, name, url, `type`, parent_id, `sequence`, property, related, enable, default_auth, front_display)
-VALUES(1225, 'AI智能评卷设置保存', '/api/admin/set/ai/mark/set/save', 'URL', 508, 22, 'AUTH', NULL, 1, 0, 1);
+VALUES(1225, 'AI智能评卷设置保存', '/api/admin/set/ai/mark/set/save', 'URL', 508, 22, 'AUTH', NULL, 1, 0, 1);
+
+-- 2025-04-14
+INSERT INTO sys_privilege
+(id, name, url, `type`, parent_id, `sequence`, property, related, enable, default_auth, front_display)
+VALUES(1226, 'AI智能评卷导出', '/api/admin/mark/setting/ai/mark/export', 'URL', 917, 3, 'AUTH', NULL, 1, 1, 1);
+INSERT INTO sys_privilege
+(id, name, url, `type`, parent_id, `sequence`, property, related, enable, default_auth, front_display)
+VALUES(1227, 'AI智能评卷统计', 'Export', 'BUTTON', 917, 4, 'AUTH', '1226', 1, 0, 1);

+ 0 - 1
distributed-print/src/main/java/com/qmth/distributed/print/api/GradePaperStructController.java

@@ -6,7 +6,6 @@ import com.qmth.distributed.print.business.bean.result.analyze.GradePaperStructR
 import com.qmth.distributed.print.business.service.GradePaperStructService;
 import com.qmth.distributed.print.business.service.GradePaperStructService;
 import com.qmth.teachcloud.common.annotation.OperationLogDetail;
 import com.qmth.teachcloud.common.annotation.OperationLogDetail;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 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.entity.SysUser;
 import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.enums.log.OperationTypeEnum;
 import com.qmth.teachcloud.common.enums.log.OperationTypeEnum;

+ 239 - 230
distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkSettingController.java

@@ -1,230 +1,239 @@
-package com.qmth.distributed.print.api.mark;
-
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.qmth.boot.api.constant.ApiConstant;
-import com.qmth.distributed.print.business.entity.ExamTaskDetail;
-import com.qmth.distributed.print.business.service.BasicExamService;
-import com.qmth.distributed.print.business.service.ExamTaskDetailService;
-import com.qmth.distributed.print.business.service.PrintCommonService;
-import com.qmth.distributed.print.business.templete.execute.AsyncObjectiveStructImportService;
-import com.qmth.distributed.print.business.templete.execute.AsyncSubjectiveStructImportService;
-import com.qmth.teachcloud.common.annotation.OperationLogDetail;
-import com.qmth.teachcloud.common.bean.dto.mark.MarkSettingDto;
-import com.qmth.teachcloud.common.bean.params.mark.setting.MarkPaperSettingParam;
-import com.qmth.teachcloud.common.bean.vo.PaperInfoVo;
-import com.qmth.teachcloud.common.contant.SystemConstant;
-import com.qmth.teachcloud.common.entity.BasicExam;
-import com.qmth.teachcloud.common.enums.ExamModelEnum;
-import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
-import com.qmth.teachcloud.common.enums.TaskTypeEnum;
-import com.qmth.teachcloud.common.enums.log.OperationTypeEnum;
-import com.qmth.distributed.print.business.util.ExamTaskUtil;
-import com.qmth.teachcloud.common.util.Result;
-import com.qmth.teachcloud.common.util.ResultUtil;
-import com.qmth.teachcloud.mark.dto.mark.manage.MarkManageDto;
-import com.qmth.teachcloud.mark.dto.mark.score.CheckScoreListDto;
-import com.qmth.teachcloud.mark.entity.MarkPaper;
-import com.qmth.teachcloud.mark.service.MarkPaperService;
-import com.qmth.teachcloud.mark.service.MarkTaskService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletResponse;
-import javax.validation.constraints.Max;
-import javax.validation.constraints.Min;
-import java.util.List;
-import java.util.Map;
-
-/**
- * <p>
- * 小题信息表 前端控制器
- * </p>
- *
- * @author xf
- * @since 2023-09-22
- */
-@Api(tags = "评卷-试卷结构")
-@RestController
-@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + SystemConstant.PREFIX_URL_MARK + "/setting")
-public class MarkSettingController {
-
-    @Resource
-    private BasicExamService basicExamService;
-    @Resource
-    private MarkPaperService markPaperService;
-    @Resource
-    private MarkTaskService markTaskService;
-    @Resource
-    private ExamTaskDetailService examTaskDetailService;
-    @Resource
-    private PrintCommonService printCommonService;
-    @Resource
-    private AsyncObjectiveStructImportService asyncObjectiveStructImportService;
-    @Resource
-    private AsyncSubjectiveStructImportService asyncSubjectiveStructImportService;
-
-    /**
-     * 评卷设置数据列表
-     */
-    @ApiOperation(value = "评卷设置数据列表")
-    @RequestMapping(value = "/list", method = RequestMethod.POST)
-    public Result list(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
-                       @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
-                       @ApiParam(value = "开课学院id") @RequestParam(required = false) Long openCollegeId,
-                       @ApiParam(value = "课程Id") @RequestParam(required = false) Long courseId,
-                       @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
-                       @ApiParam(value = "提交状态") @RequestParam(required = false) Boolean groupStatus,
-                       @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber, @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
-        if (semesterId == null) {
-            throw ExceptionResultEnum.SEMESTER_ID_IS_NULL.exception();
-        }
-        if (examId == null) {
-            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
-        }
-        IPage<MarkSettingDto> markSettingDtoIPage = markPaperService.listPaperSetting(examId, openCollegeId, courseId, paperNumber,
-                groupStatus, pageNumber, pageSize);
-        for (MarkSettingDto record : markSettingDtoIPage.getRecords()) {
-            ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamIdAndPaperNumberAndSerialNumber(record.getExamId(), record.getPaperNumber(), record.getSerialNumber());
-            if (examTaskDetail != null) {
-                List<PaperInfoVo> paperInfoVos = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds(), record.getPaperType());
-                if (CollectionUtils.isNotEmpty(paperInfoVos)) {
-                    record.setCardId(Long.valueOf(paperInfoVos.get(0).getCardId()));
-                }
-            }
-        }
-        return ResultUtil.ok(markSettingDtoIPage);
-    }
-
-    /**
-     * 评卷设置数据获取
-     */
-    @ApiOperation(value = "评卷设置数据获取")
-    @RequestMapping(value = "/paper/get", method = RequestMethod.POST)
-    public Result paperGet(@ApiParam(value = "考试ID", required = true) @RequestParam Long examId, @ApiParam(value = "试卷编号", required = true) @RequestParam String paperNumber) {
-        return ResultUtil.ok(markPaperService.getByExamIdAndPaperNumber(examId, paperNumber));
-    }
-
-    /**
-     * 评判设置数据保存
-     */
-    @ApiOperation(value = "评卷设置数据保存")
-    @RequestMapping(value = "/paper/save", method = RequestMethod.POST)
-    @OperationLogDetail(operationType = OperationTypeEnum.SAVE)
-    public Result paperSave(@RequestBody MarkPaper markPaper) {
-        markPaperService.savePaperSetting(markPaper);
-        return ResultUtil.ok(true);
-    }
-
-    /**
-     * 评判设置数据保存
-     */
-    @ApiOperation(value = "评卷设置数据批量保存")
-    @RequestMapping(value = "/paper/batch_save", method = RequestMethod.POST)
-    @OperationLogDetail(operationType = OperationTypeEnum.SAVE)
-    public Result paperBatchSave(@RequestBody MarkPaperSettingParam markPaperSettingParam) {
-        markPaperService.saveBatchPaperSetting(markPaperSettingParam);
-        return ResultUtil.ok(true);
-    }
-
-    /**
-     * 评卷管理数据列表
-     */
-    @ApiOperation(value = "评卷管理数据列表")
-    @RequestMapping(value = "/summary", method = RequestMethod.POST)
-    public Result summary(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
-                          @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
-                          @ApiParam(value = "开课学院id") @RequestParam(required = false) Long openCollegeId,
-                          @ApiParam(value = "课程ID") @RequestParam(required = false) Long courseId,
-                          @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
-                          @ApiParam(value = "完成进度") @RequestParam(required = false) Boolean progressStatus,
-                          @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
-                          @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
-        if (semesterId == null) {
-            throw ExceptionResultEnum.SEMESTER_ID_IS_NULL.exception();
-        }
-        if (examId == null) {
-            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
-        }
-        IPage<MarkManageDto> markManageDtoIPage = markTaskService.listPaperManage(examId, openCollegeId, courseId,
-                paperNumber, progressStatus, pageNumber, pageSize);
-        return ResultUtil.ok(markManageDtoIPage);
-    }
-
-    /**
-     * 导出评卷员工作量
-     */
-    @ApiOperation(value = "导出评卷员工作量")
-    @RequestMapping(value = "/export_marker", method = RequestMethod.POST)
-    public void exportMarker(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
-                             @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
-                             @ApiParam(value = "课程ID") @RequestParam(required = false) Long courseId,
-                             @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
-                             HttpServletResponse response) {
-        if (examId == null) {
-            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
-        }
-        markTaskService.exportMarker(examId, courseId, paperNumber, response);
-    }
-
-    /**
-     * 成绩检查数据列表
-     */
-    @ApiOperation(value = "成绩检查数据列表")
-    @RequestMapping(value = "/scoreList", method = RequestMethod.POST)
-    public Result scoreList(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
-                            @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
-                            @ApiParam(value = "开课学院id") @RequestParam(required = false) Long openCollegeId,
-                            @ApiParam(value = "课程ID") @RequestParam(required = false) Long courseId,
-                            @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
-                            @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
-                            @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN)
-                            @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
-        if (semesterId == null) {
-            throw ExceptionResultEnum.SEMESTER_ID_IS_NULL.exception();
-        }
-        if (examId == null) {
-            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
-        }
-        IPage<CheckScoreListDto> scoreListDtoIPage = markPaperService.listStudentScoreList(examId, openCollegeId,
-                courseId, paperNumber, pageNumber, pageSize);
-        return ResultUtil.ok(scoreListDtoIPage);
-    }
-
-    @ApiOperation(value = "客观题导入")
-    @RequestMapping(value = "/objective_struct/import", method = RequestMethod.POST)
-    public Result objectiveStructImport(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
-                                        @ApiParam(value = "标答excel文件", required = true) @RequestParam MultipartFile file) throws Exception {
-        Map<String, Object> map = printCommonService.saveTask(file, SystemConstant.convertIdToLong(examId), TaskTypeEnum.OBJECTIVE_STRUCT_IMPORT);
-        map.put("examId", SystemConstant.convertIdToLong(examId));
-        asyncObjectiveStructImportService.importTask(map);
-        return ResultUtil.success(true);
-    }
-
-    @ApiOperation(value = "主观题导入")
-    @RequestMapping(value = "/subjective_struct/import", method = RequestMethod.POST)
-    public Result subjectiveStructImport(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
-                                         @ApiParam(value = "标答excel文件", required = true) @RequestParam MultipartFile file) throws Exception {
-        Map<String, Object> map = printCommonService.saveTask(file, SystemConstant.convertIdToLong(examId), TaskTypeEnum.SUBJECTIVE_STRUCT_IMPORT);
-        map.put("examId", SystemConstant.convertIdToLong(examId));
-        asyncSubjectiveStructImportService.importTask(map);
-        return ResultUtil.success(true);
-    }
-
-    @ApiOperation(value = "导入数据包")
-    @RequestMapping(value = "/card_json/import", method = RequestMethod.POST)
-    public Result cardJsonImport(@ApiParam(value = "考试ID", required = true) @RequestParam Long examId,
-                                 @ApiParam(value = "数据文件", required = true) @RequestParam MultipartFile file,
-                                 @ApiParam(value = "md5", required = true) @RequestParam String md5) {
-        BasicExam basicExam = basicExamService.getById(examId);
-        if (basicExam == null || !ExamModelEnum.MODEL4.equals(basicExam.getExamModel())) {
-            throw ExceptionResultEnum.ERROR.exception("数据包导入方式仅支持模式4考试");
-        }
-        markPaperService.importCardJsonData(examId, file, md5);
-        return ResultUtil.success(true);
-    }
-}
+package com.qmth.distributed.print.api.mark;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.distributed.print.business.entity.ExamTaskDetail;
+import com.qmth.distributed.print.business.service.BasicExamService;
+import com.qmth.distributed.print.business.service.ExamTaskDetailService;
+import com.qmth.distributed.print.business.service.PrintCommonService;
+import com.qmth.distributed.print.business.templete.execute.AsyncObjectiveStructImportService;
+import com.qmth.distributed.print.business.templete.execute.AsyncSubjectiveStructImportService;
+import com.qmth.distributed.print.business.util.ExamTaskUtil;
+import com.qmth.teachcloud.common.annotation.OperationLogDetail;
+import com.qmth.teachcloud.common.bean.dto.mark.MarkSettingDto;
+import com.qmth.teachcloud.common.bean.params.mark.setting.MarkPaperSettingParam;
+import com.qmth.teachcloud.common.bean.vo.PaperInfoVo;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.entity.BasicExam;
+import com.qmth.teachcloud.common.enums.ExamModelEnum;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.enums.TaskTypeEnum;
+import com.qmth.teachcloud.common.enums.log.OperationTypeEnum;
+import com.qmth.teachcloud.common.util.Result;
+import com.qmth.teachcloud.common.util.ResultUtil;
+import com.qmth.teachcloud.mark.dto.mark.manage.MarkManageDto;
+import com.qmth.teachcloud.mark.dto.mark.score.CheckScoreListDto;
+import com.qmth.teachcloud.mark.entity.MarkPaper;
+import com.qmth.teachcloud.mark.service.MarkPaperService;
+import com.qmth.teachcloud.mark.service.MarkTaskService;
+import io.swagger.annotations.*;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * 小题信息表 前端控制器
+ * </p>
+ *
+ * @author xf
+ * @since 2023-09-22
+ */
+@Api(tags = "评卷-试卷结构")
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + SystemConstant.PREFIX_URL_MARK + "/setting")
+public class MarkSettingController {
+
+    @Resource
+    private BasicExamService basicExamService;
+    @Resource
+    private MarkPaperService markPaperService;
+    @Resource
+    private MarkTaskService markTaskService;
+    @Resource
+    private ExamTaskDetailService examTaskDetailService;
+    @Resource
+    private PrintCommonService printCommonService;
+    @Resource
+    private AsyncObjectiveStructImportService asyncObjectiveStructImportService;
+    @Resource
+    private AsyncSubjectiveStructImportService asyncSubjectiveStructImportService;
+
+    /**
+     * 评卷设置数据列表
+     */
+    @ApiOperation(value = "评卷设置数据列表")
+    @RequestMapping(value = "/list", method = RequestMethod.POST)
+    public Result list(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
+                       @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
+                       @ApiParam(value = "开课学院id") @RequestParam(required = false) Long openCollegeId,
+                       @ApiParam(value = "课程Id") @RequestParam(required = false) Long courseId,
+                       @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
+                       @ApiParam(value = "提交状态") @RequestParam(required = false) Boolean groupStatus,
+                       @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber, @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        if (semesterId == null) {
+            throw ExceptionResultEnum.SEMESTER_ID_IS_NULL.exception();
+        }
+        if (examId == null) {
+            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
+        }
+        IPage<MarkSettingDto> markSettingDtoIPage = markPaperService.listPaperSetting(examId, openCollegeId, courseId, paperNumber,
+                groupStatus, pageNumber, pageSize);
+        for (MarkSettingDto record : markSettingDtoIPage.getRecords()) {
+            ExamTaskDetail examTaskDetail = examTaskDetailService.getByExamIdAndPaperNumberAndSerialNumber(record.getExamId(), record.getPaperNumber(), record.getSerialNumber());
+            if (examTaskDetail != null) {
+                List<PaperInfoVo> paperInfoVos = ExamTaskUtil.parsePaperAttachmentPath(examTaskDetail.getPaperAttachmentIds(), record.getPaperType());
+                if (CollectionUtils.isNotEmpty(paperInfoVos)) {
+                    record.setCardId(Long.valueOf(paperInfoVos.get(0).getCardId()));
+                }
+            }
+        }
+        return ResultUtil.ok(markSettingDtoIPage);
+    }
+
+    /**
+     * 评卷设置数据获取
+     */
+    @ApiOperation(value = "评卷设置数据获取")
+    @RequestMapping(value = "/paper/get", method = RequestMethod.POST)
+    public Result paperGet(@ApiParam(value = "考试ID", required = true) @RequestParam Long examId, @ApiParam(value = "试卷编号", required = true) @RequestParam String paperNumber) {
+        return ResultUtil.ok(markPaperService.getByExamIdAndPaperNumber(examId, paperNumber));
+    }
+
+    /**
+     * 评判设置数据保存
+     */
+    @ApiOperation(value = "评卷设置数据保存")
+    @RequestMapping(value = "/paper/save", method = RequestMethod.POST)
+    @OperationLogDetail(operationType = OperationTypeEnum.SAVE)
+    public Result paperSave(@RequestBody MarkPaper markPaper) {
+        markPaperService.savePaperSetting(markPaper);
+        return ResultUtil.ok(true);
+    }
+
+    /**
+     * 评判设置数据保存
+     */
+    @ApiOperation(value = "评卷设置数据批量保存")
+    @RequestMapping(value = "/paper/batch_save", method = RequestMethod.POST)
+    @OperationLogDetail(operationType = OperationTypeEnum.SAVE)
+    public Result paperBatchSave(@RequestBody MarkPaperSettingParam markPaperSettingParam) {
+        markPaperService.saveBatchPaperSetting(markPaperSettingParam);
+        return ResultUtil.ok(true);
+    }
+
+    /**
+     * 评卷管理数据列表
+     */
+    @ApiOperation(value = "评卷管理数据列表")
+    @RequestMapping(value = "/summary", method = RequestMethod.POST)
+    public Result summary(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
+                          @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
+                          @ApiParam(value = "开课学院id") @RequestParam(required = false) Long openCollegeId,
+                          @ApiParam(value = "课程ID") @RequestParam(required = false) Long courseId,
+                          @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
+                          @ApiParam(value = "完成进度") @RequestParam(required = false) Boolean progressStatus,
+                          @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                          @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        if (semesterId == null) {
+            throw ExceptionResultEnum.SEMESTER_ID_IS_NULL.exception();
+        }
+        if (examId == null) {
+            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
+        }
+        IPage<MarkManageDto> markManageDtoIPage = markTaskService.listPaperManage(examId, openCollegeId, courseId,
+                paperNumber, progressStatus, pageNumber, pageSize);
+        return ResultUtil.ok(markManageDtoIPage);
+    }
+
+    /**
+     * 导出评卷员工作量
+     */
+    @ApiOperation(value = "导出评卷员工作量")
+    @RequestMapping(value = "/export_marker", method = RequestMethod.POST)
+    public void exportMarker(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
+                             @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
+                             @ApiParam(value = "课程ID") @RequestParam(required = false) Long courseId,
+                             @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
+                             HttpServletResponse response) {
+        if (examId == null) {
+            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
+        }
+        markTaskService.exportMarker(examId, courseId, paperNumber, response);
+    }
+
+    /**
+     * 成绩检查数据列表
+     */
+    @ApiOperation(value = "成绩检查数据列表")
+    @RequestMapping(value = "/scoreList", method = RequestMethod.POST)
+    public Result scoreList(@ApiParam(value = "学期ID") @RequestParam(required = false) Long semesterId,
+                            @ApiParam(value = "考试ID") @RequestParam(required = false) Long examId,
+                            @ApiParam(value = "开课学院id") @RequestParam(required = false) Long openCollegeId,
+                            @ApiParam(value = "课程ID") @RequestParam(required = false) Long courseId,
+                            @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber,
+                            @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                            @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN)
+                            @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        if (semesterId == null) {
+            throw ExceptionResultEnum.SEMESTER_ID_IS_NULL.exception();
+        }
+        if (examId == null) {
+            throw ExceptionResultEnum.EXAM_ID_IS_NULL.exception();
+        }
+        IPage<CheckScoreListDto> scoreListDtoIPage = markPaperService.listStudentScoreList(examId, openCollegeId,
+                courseId, paperNumber, pageNumber, pageSize);
+        return ResultUtil.ok(scoreListDtoIPage);
+    }
+
+    @ApiOperation(value = "客观题导入")
+    @RequestMapping(value = "/objective_struct/import", method = RequestMethod.POST)
+    public Result objectiveStructImport(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
+                                        @ApiParam(value = "标答excel文件", required = true) @RequestParam MultipartFile file) throws Exception {
+        Map<String, Object> map = printCommonService.saveTask(file, SystemConstant.convertIdToLong(examId), TaskTypeEnum.OBJECTIVE_STRUCT_IMPORT);
+        map.put("examId", SystemConstant.convertIdToLong(examId));
+        asyncObjectiveStructImportService.importTask(map);
+        return ResultUtil.success(true);
+    }
+
+    @ApiOperation(value = "主观题导入")
+    @RequestMapping(value = "/subjective_struct/import", method = RequestMethod.POST)
+    public Result subjectiveStructImport(@ApiParam(value = "考试ID", required = true) @RequestParam String examId,
+                                         @ApiParam(value = "标答excel文件", required = true) @RequestParam MultipartFile file) throws Exception {
+        Map<String, Object> map = printCommonService.saveTask(file, SystemConstant.convertIdToLong(examId), TaskTypeEnum.SUBJECTIVE_STRUCT_IMPORT);
+        map.put("examId", SystemConstant.convertIdToLong(examId));
+        asyncSubjectiveStructImportService.importTask(map);
+        return ResultUtil.success(true);
+    }
+
+    @ApiOperation(value = "导入数据包")
+    @RequestMapping(value = "/card_json/import", method = RequestMethod.POST)
+    public Result cardJsonImport(@ApiParam(value = "考试ID", required = true) @RequestParam Long examId,
+                                 @ApiParam(value = "数据文件", required = true) @RequestParam MultipartFile file,
+                                 @ApiParam(value = "md5", required = true) @RequestParam String md5) {
+        BasicExam basicExam = basicExamService.getById(examId);
+        if (basicExam == null || !ExamModelEnum.MODEL4.equals(basicExam.getExamModel())) {
+            throw ExceptionResultEnum.ERROR.exception("数据包导入方式仅支持模式4考试");
+        }
+        markPaperService.importCardJsonData(examId, file, md5);
+        return ResultUtil.success(true);
+    }
+
+    @ApiOperation(value = "AI智能评卷导出")
+    @RequestMapping(value = "/ai/mark/export", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "AI评卷参数信息", response = Object.class)})
+    @OperationLogDetail(operationType = OperationTypeEnum.EXPORT)
+    public void export(@ApiParam(value = "学期ID", required = true) @RequestParam Long semesterId,
+                       @ApiParam(value = "考试ID", required = true) @RequestParam Long examId,
+                       @ApiParam(value = "课程ID") @RequestParam(required = false) Long courseId,
+                       @ApiParam(value = "试卷编号") @RequestParam(required = false) String paperNumber) throws Exception {
+        markTaskService.exportAiMark(semesterId, examId, courseId, paperNumber);
+    }
+}

+ 70 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/mark/manage/MarkAiExport.java

@@ -0,0 +1,70 @@
+package com.qmth.teachcloud.mark.dto.mark.manage;
+
+import com.qmth.teachcloud.common.annotation.ExcelProperty;
+
+import java.io.Serializable;
+
+/**
+ * @Description: AI智能评卷导出
+ * @Param:
+ * @return:
+ * @Author: wangliang
+ * @Date: 2025/4/14
+ */
+public class MarkAiExport implements Serializable {
+
+    @ExcelProperty(name = "课程名称", width = 20, index = 1)
+    private String courseName;
+
+    @ExcelProperty(name = "课程代码", width = 20, index = 2)
+    private String courseCode;
+
+    @ExcelProperty(name = "试卷编号", width = 20, index = 3)
+    private String paperNumber;
+
+    @ExcelProperty(name = "采用AI评卷题目数", width = 20, index = 4)
+    private Integer questionNum;
+
+    @ExcelProperty(name = "采用AI评卷任务数", width = 20, index = 5)
+    private Integer taskNum;
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getPaperNumber() {
+        return paperNumber;
+    }
+
+    public void setPaperNumber(String paperNumber) {
+        this.paperNumber = paperNumber;
+    }
+
+    public Integer getQuestionNum() {
+        return questionNum;
+    }
+
+    public void setQuestionNum(Integer questionNum) {
+        this.questionNum = questionNum;
+    }
+
+    public Integer getTaskNum() {
+        return taskNum;
+    }
+
+    public void setTaskNum(Integer taskNum) {
+        this.taskNum = taskNum;
+    }
+}

+ 68 - 54
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/mapper/MarkTaskMapper.java

@@ -1,54 +1,68 @@
-package com.qmth.teachcloud.mark.mapper;
-
-import java.util.List;
-
-import com.qmth.teachcloud.mark.dto.mark.manage.MarkTaskDto;
-import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
-import org.apache.ibatis.annotations.Param;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
-import com.qmth.teachcloud.mark.dto.mark.manage.MarkManageDto;
-import com.qmth.teachcloud.mark.dto.mark.manage.MarkerInfoDto;
-import com.qmth.teachcloud.mark.entity.MarkTask;
-
-/**
- * <p>
- * 评卷任务表 Mapper 接口
- * </p>
- *
- * @author xf
- * @since 2023-09-22
- */
-public interface MarkTaskMapper extends BaseMapper<MarkTask> {
-
-    IPage<MarkManageDto> listPaperManage(@Param("page") Page<MarkManageDto> page, @Param("examId") Long examId, @Param("openCollegeId") Long openCollegeId, @Param("courseId") Long courseId, @Param("paperNumber") String paperNumber, @Param("progressStatus") Boolean progressStatus, @Param("dpr") DataPermissionRule dpr);
-
-    List<MarkerInfoDto> listUserMarkedCount(@Param("examId") Long examId, @Param("courseId") Long courseId, @Param("paperNumber") String paperNumber, @Param("dpr") DataPermissionRule dpr);
-
-    MarkTask getLastOneByUserIdAndStatus(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("userId") Long userId, @Param("status") String status);
-
-    IPage<MarkTask> listPageHistory(@Param("page") Page<Long> page, @Param("userId") Long userId, @Param("examId") Long examId, @Param("paperNumber") String paperNumber,
-                                    @Param("secretNumber") String secretNumber, @Param("markerScore") Double markerScore, @Param("statuses") MarkTaskStatus...statuses);
-
-    List<MarkTask> findUnMarked(@Param("page") Page<MarkTask> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber,
-                                @Param("userId") Long userId);
-
-    List<MarkTask> findUnMarkedFilterClass(@Param("page") Page<MarkTask> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("userId") Long userId, @Param("questionId") Long questionId, @Param("classNames") List<String> classNames);
-    List<MarkTask> findAiUnMarked(@Param("page") Page<MarkTask> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId);
-
-    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("userId") Long userId, @Param("className") String className);
-
-    int countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("classNames") List<String> classNames, @Param("statusList") MarkTaskStatus[] statusList);
-    int countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("userId") Long userId, @Param("classNames") List<String> classNames, @Param("questionIds") List<Long> questionIds, @Param("statusList") MarkTaskStatus[] statusList, @Param("questionSize") Integer questionSize);
-
-    IPage<MarkTaskDto> pageMarkTask(@Param("page") Page<Object> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("loginName") String loginName, @Param("status") MarkTaskStatus status, @Param("studentCode") String studentCode, @Param("secretNumber") String secretNumber, @Param("teachClassName") String teachClassName, @Param("subScore") Double subScore);
-
-    MarkManageDto selectMarkedCountAndTotalCount(@Param("examId") Long examId, @Param("paperNumber") String paperNumber);
-
-    Long minQuestionIdByExamIdAndPaperNumber(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("statusList") MarkTaskStatus[] statusList);
-
-    List<MarkTask> listByStudentIdAndUserId(@Param("studentId") Long studentId, @Param("userId") Long userId);
-}
+package com.qmth.teachcloud.mark.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
+import com.qmth.teachcloud.mark.dto.mark.manage.MarkAiExport;
+import com.qmth.teachcloud.mark.dto.mark.manage.MarkManageDto;
+import com.qmth.teachcloud.mark.dto.mark.manage.MarkTaskDto;
+import com.qmth.teachcloud.mark.dto.mark.manage.MarkerInfoDto;
+import com.qmth.teachcloud.mark.entity.MarkTask;
+import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 评卷任务表 Mapper 接口
+ * </p>
+ *
+ * @author xf
+ * @since 2023-09-22
+ */
+public interface MarkTaskMapper extends BaseMapper<MarkTask> {
+
+    IPage<MarkManageDto> listPaperManage(@Param("page") Page<MarkManageDto> page, @Param("examId") Long examId, @Param("openCollegeId") Long openCollegeId, @Param("courseId") Long courseId, @Param("paperNumber") String paperNumber, @Param("progressStatus") Boolean progressStatus, @Param("dpr") DataPermissionRule dpr);
+
+    List<MarkerInfoDto> listUserMarkedCount(@Param("examId") Long examId, @Param("courseId") Long courseId, @Param("paperNumber") String paperNumber, @Param("dpr") DataPermissionRule dpr);
+
+    MarkTask getLastOneByUserIdAndStatus(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("userId") Long userId, @Param("status") String status);
+
+    IPage<MarkTask> listPageHistory(@Param("page") Page<Long> page, @Param("userId") Long userId, @Param("examId") Long examId, @Param("paperNumber") String paperNumber,
+                                    @Param("secretNumber") String secretNumber, @Param("markerScore") Double markerScore, @Param("statuses") MarkTaskStatus... statuses);
+
+    List<MarkTask> findUnMarked(@Param("page") Page<MarkTask> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber,
+                                @Param("userId") Long userId);
+
+    List<MarkTask> findUnMarkedFilterClass(@Param("page") Page<MarkTask> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("userId") Long userId, @Param("questionId") Long questionId, @Param("classNames") List<String> classNames);
+
+    List<MarkTask> findAiUnMarked(@Param("page") Page<MarkTask> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId);
+
+    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("userId") Long userId, @Param("className") String className);
+
+    int countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("classNames") List<String> classNames, @Param("statusList") MarkTaskStatus[] statusList);
+
+    int countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("userId") Long userId, @Param("classNames") List<String> classNames, @Param("questionIds") List<Long> questionIds, @Param("statusList") MarkTaskStatus[] statusList, @Param("questionSize") Integer questionSize);
+
+    IPage<MarkTaskDto> pageMarkTask(@Param("page") Page<Object> page, @Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("questionId") Long questionId, @Param("loginName") String loginName, @Param("status") MarkTaskStatus status, @Param("studentCode") String studentCode, @Param("secretNumber") String secretNumber, @Param("teachClassName") String teachClassName, @Param("subScore") Double subScore);
+
+    MarkManageDto selectMarkedCountAndTotalCount(@Param("examId") Long examId, @Param("paperNumber") String paperNumber);
+
+    Long minQuestionIdByExamIdAndPaperNumber(@Param("examId") Long examId, @Param("paperNumber") String paperNumber, @Param("statusList") MarkTaskStatus[] statusList);
+
+    List<MarkTask> listByStudentIdAndUserId(@Param("studentId") Long studentId, @Param("userId") Long userId);
+
+    /**
+     * AI智能评卷导出
+     *
+     * @param semesterId
+     * @param examId
+     * @param courseId
+     * @param paperNumber
+     * @param dpr
+     * @return
+     */
+    List<MarkAiExport> exportAiMark(@Param("semesterId") Long semesterId, @Param("examId") Long examId, @Param("courseId") Long courseId, @Param("paperNumber") String paperNumber, @Param("dpr") DataPermissionRule dpr);
+}

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

@@ -1,109 +1,121 @@
-package com.qmth.teachcloud.mark.service;
-
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.qmth.teachcloud.mark.dto.mark.SpecialTagDTO;
-import com.qmth.teachcloud.mark.dto.mark.manage.MarkManageDto;
-import com.qmth.teachcloud.mark.dto.mark.manage.MarkTaskDto;
-import com.qmth.teachcloud.mark.dto.mark.manage.Task;
-import com.qmth.teachcloud.mark.dto.mark.manage.TrackDTO;
-import com.qmth.teachcloud.mark.entity.MarkTask;
-import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
-import com.qmth.teachcloud.mark.params.MarkResultQuestion;
-
-import javax.servlet.http.HttpServletResponse;
-import java.util.List;
-
-/**
- * <p>
- * 评卷任务表 服务类
- * </p>
- *
- * @author xf
- * @since 2023-09-22
- */
-public interface MarkTaskService extends IService<MarkTask> {
-
-    IPage<MarkManageDto> listPaperManage(Long examId, Long openCollegeId, Long courseId, String paperNumber, Boolean progressStatus, Integer pageNumber, Integer pageSize);
-
-    void exportMarker(Long examId, Long courseId, String paperNumber, HttpServletResponse response);
-
-    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(Long examId, String paperNumber, Long questionId, Long userId, String className);
-
-    Integer markedCount(List<MarkTask> groupNumberMarkTask);
-
-    int countByExamIdAndPaperNumberAndQuestionIdAndUserId(Long examId, String paperNumber, Long questionId, Long userId);
-
-    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndStatusNotIn(Long examId, String paperNumber, Long questionId, Long userId, List<MarkTaskStatus> statusList);
-
-    boolean resetById(Long markTaskId, Long userId, String rejectReason, Long rejectId, Long date, MarkTaskStatus newStatus);
-
-    int countByExamIdAndPaperNumberAndQuestionIdAndStatusIn(Long examId, String paperNumber, Long questionId, List<MarkTaskStatus> statusList);
-
-    int countByExamIdAndPaperNumberAndStatusIn(Long examId, String paperNumber, List<MarkTaskStatus> statusList);
-
-    MarkTask getLastOneByUserIdAndStatus(Long examId, String paperNumber, Long questionId, Long userId, MarkTaskStatus status);
-
-    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndStudentId(Long examId, String paperNumber, Long questionId, Long studentId);
-
-    List<MarkTask> listByStudentIdAndQuestionId(Long studentId, Long questionId);
-
-    void updateHeaderResult(Long examId, String paperNumber, Long questionId, Long studentId, Long updateUserId, Double markerScore, TrackDTO[] tracks, SpecialTagDTO[] specialTags, Long updateTime, MarkTaskStatus arbitrated);
-
-    void deleteByStudentId(Long studentId);
-
-    void deleteByExamIdAndPaperNumberAndQuestionId(Long examId, String paperNumber, Long questionId);
-
-    int countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(Long examId, String paperNumber, Long questionId, List<String> className, MarkTaskStatus... status);
-
-    int countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(Long examId, String paperNumber,Long userId, List<String> className, List<Long> questionIds, MarkTaskStatus... status);
-
-    IPage<MarkTask> listPageHistory(Page<Long> page, Long userId, Long examId, String paperNumber, String secretNumber, Double markerScore, MarkTaskStatus...statuses);
-
-    List<MarkTask> findUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long userId);
-
-    List<MarkTask> findUnMarkedFilterClass(Page<MarkTask> page, Long examId, String paperNumber, Long userId, Long questionId, List<String> classNames);
-    List<MarkTask> findAiUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long questionId);
-
-    int countByIdAndStatus(Long studentId, MarkTaskStatus status);
-
-    int countByStatusAndIdIn(List<Long> ids, MarkTaskStatus... status);
-
-    List<MarkTask> findByStudentIdAndQuestionIdAndStatus(Long studentId, Long questionId, MarkTaskStatus... status);
-
-    boolean updateProblemResult(Long taskId, Long userId, Long now, int spent);
-
-    boolean updateStatusByStudentIdAndQuestionId(Long studentId, Long questionId, MarkTaskStatus status);
-
-    int countByStudentIdAndMarkerIdAndIdNotEqual(Long studentId, Long examId, String paperNumber, Long questionId, Long userId, Long taskId);
-
-    boolean updateMarkerResult(Long taskId, MarkTaskStatus marked, Long userId, MarkResultQuestion result,
-                               Long now, MarkTaskStatus... inStatus);
-
-    int countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(Long examId, String paperNumber, Long questionId, int taskNumber);
-
-    int countByStudentId(Long studentId);
-
-    int countByExamIdAndPaperNumber(Long examId, String paperNumber);
-
-    IPage<MarkTaskDto> pageMarkTask(Long examId, String paperNumber, Long questionId, String loginName, MarkTaskStatus status, String studentCode, String secretNumber, String teachClassName, Double subScore, Integer pageNumber, Integer pageSize);
-
-    void rejectMarkTask(Long id, String rejectReason);
-
-    Task getMarkTrack(Long id);
-
-    MarkManageDto selectMarkedCountAndTotalCount(Long examId, String paperNumber);
-
-    List<MarkTask> listByStudentId(Long studentId);
-
-    Long minQuestionIdByExamIdAndPaperNumber(Long examId, String paperNumber, MarkTaskStatus... statuses);
-
-    List<MarkTask> listByStudentIdAndUserId(Long studentId, Long userId);
-
-    List<MarkTask> listByStudentIdAndMarkerId(Long studentId, Long markerId, List<MarkTaskStatus> markTaskStatuses);
-
-    void resetHeaderByStudentIdAndQuestionId(Long studentId, Long questionId);
-
-    void resetArbitrateStatusByStudentIdAndQuestionIdAndTaskNumber(Long studentId, Long questionId, Integer taskNumber);
-}
+package com.qmth.teachcloud.mark.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.teachcloud.mark.dto.mark.SpecialTagDTO;
+import com.qmth.teachcloud.mark.dto.mark.manage.MarkManageDto;
+import com.qmth.teachcloud.mark.dto.mark.manage.MarkTaskDto;
+import com.qmth.teachcloud.mark.dto.mark.manage.Task;
+import com.qmth.teachcloud.mark.dto.mark.manage.TrackDTO;
+import com.qmth.teachcloud.mark.entity.MarkTask;
+import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
+import com.qmth.teachcloud.mark.params.MarkResultQuestion;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * <p>
+ * 评卷任务表 服务类
+ * </p>
+ *
+ * @author xf
+ * @since 2023-09-22
+ */
+public interface MarkTaskService extends IService<MarkTask> {
+
+    IPage<MarkManageDto> listPaperManage(Long examId, Long openCollegeId, Long courseId, String paperNumber, Boolean progressStatus, Integer pageNumber, Integer pageSize);
+
+    void exportMarker(Long examId, Long courseId, String paperNumber, HttpServletResponse response);
+
+    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(Long examId, String paperNumber, Long questionId, Long userId, String className);
+
+    Integer markedCount(List<MarkTask> groupNumberMarkTask);
+
+    int countByExamIdAndPaperNumberAndQuestionIdAndUserId(Long examId, String paperNumber, Long questionId, Long userId);
+
+    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndStatusNotIn(Long examId, String paperNumber, Long questionId, Long userId, List<MarkTaskStatus> statusList);
+
+    boolean resetById(Long markTaskId, Long userId, String rejectReason, Long rejectId, Long date, MarkTaskStatus newStatus);
+
+    int countByExamIdAndPaperNumberAndQuestionIdAndStatusIn(Long examId, String paperNumber, Long questionId, List<MarkTaskStatus> statusList);
+
+    int countByExamIdAndPaperNumberAndStatusIn(Long examId, String paperNumber, List<MarkTaskStatus> statusList);
+
+    MarkTask getLastOneByUserIdAndStatus(Long examId, String paperNumber, Long questionId, Long userId, MarkTaskStatus status);
+
+    List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndStudentId(Long examId, String paperNumber, Long questionId, Long studentId);
+
+    List<MarkTask> listByStudentIdAndQuestionId(Long studentId, Long questionId);
+
+    void updateHeaderResult(Long examId, String paperNumber, Long questionId, Long studentId, Long updateUserId, Double markerScore, TrackDTO[] tracks, SpecialTagDTO[] specialTags, Long updateTime, MarkTaskStatus arbitrated);
+
+    void deleteByStudentId(Long studentId);
+
+    void deleteByExamIdAndPaperNumberAndQuestionId(Long examId, String paperNumber, Long questionId);
+
+    int countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(Long examId, String paperNumber, Long questionId, List<String> className, MarkTaskStatus... status);
+
+    int countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(Long examId, String paperNumber, Long userId, List<String> className, List<Long> questionIds, MarkTaskStatus... status);
+
+    IPage<MarkTask> listPageHistory(Page<Long> page, Long userId, Long examId, String paperNumber, String secretNumber, Double markerScore, MarkTaskStatus... statuses);
+
+    List<MarkTask> findUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long userId);
+
+    List<MarkTask> findUnMarkedFilterClass(Page<MarkTask> page, Long examId, String paperNumber, Long userId, Long questionId, List<String> classNames);
+
+    List<MarkTask> findAiUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long questionId);
+
+    int countByIdAndStatus(Long studentId, MarkTaskStatus status);
+
+    int countByStatusAndIdIn(List<Long> ids, MarkTaskStatus... status);
+
+    List<MarkTask> findByStudentIdAndQuestionIdAndStatus(Long studentId, Long questionId, MarkTaskStatus... status);
+
+    boolean updateProblemResult(Long taskId, Long userId, Long now, int spent);
+
+    boolean updateStatusByStudentIdAndQuestionId(Long studentId, Long questionId, MarkTaskStatus status);
+
+    int countByStudentIdAndMarkerIdAndIdNotEqual(Long studentId, Long examId, String paperNumber, Long questionId, Long userId, Long taskId);
+
+    boolean updateMarkerResult(Long taskId, MarkTaskStatus marked, Long userId, MarkResultQuestion result,
+                               Long now, MarkTaskStatus... inStatus);
+
+    int countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(Long examId, String paperNumber, Long questionId, int taskNumber);
+
+    int countByStudentId(Long studentId);
+
+    int countByExamIdAndPaperNumber(Long examId, String paperNumber);
+
+    IPage<MarkTaskDto> pageMarkTask(Long examId, String paperNumber, Long questionId, String loginName, MarkTaskStatus status, String studentCode, String secretNumber, String teachClassName, Double subScore, Integer pageNumber, Integer pageSize);
+
+    void rejectMarkTask(Long id, String rejectReason);
+
+    Task getMarkTrack(Long id);
+
+    MarkManageDto selectMarkedCountAndTotalCount(Long examId, String paperNumber);
+
+    List<MarkTask> listByStudentId(Long studentId);
+
+    Long minQuestionIdByExamIdAndPaperNumber(Long examId, String paperNumber, MarkTaskStatus... statuses);
+
+    List<MarkTask> listByStudentIdAndUserId(Long studentId, Long userId);
+
+    List<MarkTask> listByStudentIdAndMarkerId(Long studentId, Long markerId, List<MarkTaskStatus> markTaskStatuses);
+
+    void resetHeaderByStudentIdAndQuestionId(Long studentId, Long questionId);
+
+    void resetArbitrateStatusByStudentIdAndQuestionIdAndTaskNumber(Long studentId, Long questionId, Integer taskNumber);
+
+    /**
+     * AI智能评卷导出
+     *
+     * @param semesterId
+     * @param examId
+     * @param courseId
+     * @param paperNumber
+     * @throws Exception
+     */
+    void exportAiMark(Long semesterId, Long examId, Long courseId, String paperNumber) throws Exception;
+}

+ 513 - 495
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkTaskServiceImpl.java

@@ -1,495 +1,513 @@
-package com.qmth.teachcloud.mark.service.impl;
-
-import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
-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.mark.MarkProblemStatus;
-import com.qmth.teachcloud.common.service.BasicRoleDataPermissionService;
-import com.qmth.teachcloud.common.util.ExcelUtil;
-import com.qmth.teachcloud.common.util.ServletUtil;
-import com.qmth.teachcloud.mark.dto.mark.SpecialTagDTO;
-import com.qmth.teachcloud.mark.dto.mark.manage.*;
-import com.qmth.teachcloud.mark.entity.MarkTask;
-import com.qmth.teachcloud.mark.enums.LockType;
-import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
-import com.qmth.teachcloud.mark.lock.LockService;
-import com.qmth.teachcloud.mark.mapper.MarkTaskMapper;
-import com.qmth.teachcloud.mark.params.MarkResultQuestion;
-import com.qmth.teachcloud.mark.service.*;
-import com.qmth.teachcloud.mark.utils.Calculator;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletResponse;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * <p>
- * 评卷任务表 服务实现类
- * </p>
- *
- * @author xf
- * @since 2023-09-22
- */
-@Service
-public class MarkTaskServiceImpl extends ServiceImpl<MarkTaskMapper, MarkTask> implements MarkTaskService {
-
-    @Resource
-    private MarkQuestionService markQuestionService;
-    @Resource
-    private MarkProblemHistoryService markProblemHistoryService;
-    @Resource
-    private MarkService markService;
-    @Resource
-    private LockService lockService;
-    @Resource
-    private TaskService taskService;
-    @Resource
-    private BasicRoleDataPermissionService basicRoleDataPermissionService;
-
-    @Override
-    public IPage<MarkManageDto> listPaperManage(Long examId, Long openCollegeId, Long courseId, String paperNumber, Boolean progressStatus, Integer pageNumber, Integer pageSize) {
-        Page<MarkManageDto> page = new Page<>(pageNumber, pageSize);
-        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
-        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(sysUser.getSchoolId(), sysUser.getId(), ServletUtil.getRequest().getServletPath());
-        IPage<MarkManageDto> markManageDtoIPage = this.baseMapper.listPaperManage(page, examId, openCollegeId, courseId,
-                paperNumber, progressStatus, dpr);
-        for (MarkManageDto record : markManageDtoIPage.getRecords()) {
-            record.setStatusDisplay(record.getStatus() == null ? null : record.getStatus().getName());
-            MarkManageDto markManageDto = this.selectMarkedCountAndTotalCount(record.getExamId(), record.getPaperNumber());
-            if (markManageDto != null) {
-                record.setMarkedCount(markManageDto.getMarkedCount());
-                record.setTotalCount(markManageDto.getTotalCount());
-                record.setPercent(markManageDto.getTotalCount() != null && markManageDto.getTotalCount() != 0 ? Calculator.divide2String(Calculator.multiply(markManageDto.getMarkedCount(), 100), Double.valueOf(markManageDto.getTotalCount()), 2) : "0");
-            } else {
-                record.setMarkedCount(0);
-                record.setTotalCount(0);
-                record.setPercent("0");
-            }
-            // 问题卷数量
-            record.setProblemCount(markProblemHistoryService.countByExamIdAndPaperNumberAndStatusAndClassNameIn(examId, record.getPaperNumber(), MarkProblemStatus.WAITING, null));
-            // 正在评卷数量
-            record.setCurrentCount(markQuestionService.countCurrentCountByExamIdAndPaperNumber(examId, record.getPaperNumber()));
-        }
-        return markManageDtoIPage;
-    }
-
-    @Override
-    public void exportMarker(Long examId, Long courseId, String paperNumber, HttpServletResponse response) {
-        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
-        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(sysUser.getSchoolId(), sysUser.getId(), ServletUtil.getRequest().getServletPath());
-        List<MarkerInfoDto> markerInfoDtoList = this.baseMapper.listUserMarkedCount(examId, courseId, paperNumber, dpr);
-        for (MarkerInfoDto markerInfoDto : markerInfoDtoList) {
-            if (markerInfoDto.getQuestionId() != null) {
-                MarkQuestion markQuestion = markQuestionService.getById(markerInfoDto.getQuestionId());
-                markerInfoDto.setQuestionName(markQuestion.getMainTitle());
-                markerInfoDto.setMainNumber(markQuestion.getMainNumber());
-                markerInfoDto.setSubNumber(markQuestion.getSubNumber());
-                markerInfoDto.setTotalScore(markQuestion.getTotalScore());
-            }
-        }
-        try {
-            ExcelUtil.excelExport("评卷员工作量", MarkerInfoDto.class, markerInfoDtoList, response);
-        } catch (Exception e) {
-            throw ExceptionResultEnum.ERROR.exception("导出评卷员工作量失败");
-        }
-    }
-
-    @Override
-    public List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(Long examId, String paperNumber, Long questionId, Long userId, String className) {
-        return this.baseMapper.listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(examId, paperNumber, questionId, userId, className);
-    }
-
-    @Override
-    public Integer markedCount(List<MarkTask> markTask) {
-        if (CollectionUtils.isEmpty(markTask)) {
-            return 0;
-        }
-        long count = markTask.stream().filter(m -> m.getStatus().equals(MarkTaskStatus.MARKED) || m.getStatus().equals(MarkTaskStatus.ARBITRATED) || m.getStatus().equals(MarkTaskStatus.WAIT_ARBITRATE)).count();
-        return (int) count;
-    }
-
-    @Override
-    public int countByExamIdAndPaperNumberAndQuestionIdAndUserId(Long examId, String paperNumber, Long questionId, Long userId) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        LambdaQueryWrapper<MarkTask> lambdaQueryWrapper = queryWrapper.lambda();
-        lambdaQueryWrapper.eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId);
-        if (userId != null) {
-            lambdaQueryWrapper.eq(MarkTask::getUserId, userId);
-        }
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndStatusNotIn(Long examId, String paperNumber, Long questionId, Long userId, List<MarkTaskStatus> statusList) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId)
-                .eq(MarkTask::getUserId, userId)
-                .notIn(MarkTask::getStatus, statusList);
-        return this.list(queryWrapper);
-    }
-
-    @Override
-    public boolean resetById(Long markTaskId, Long userId, String rejectReason, Long rejectId, Long date, MarkTaskStatus newStatus) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().set(MarkTask::getStatus, newStatus)
-                .set(MarkTask::getUserId, userId)
-                .set(MarkTask::getMarkerTime, null)
-                .set(MarkTask::getMarkerScore, null)
-                .set(MarkTask::getMarkerTrackList, null)
-                .set(MarkTask::getMarkerTagList, null)
-                .set(MarkTask::getMarkerSpent, null)
-                .set(MarkTask::getHeaderId, rejectId)
-                .set(MarkTask::getHeaderTime, date)
-                .set(MarkTask::getHeaderScore, null)
-                .set(MarkTask::getHeaderTrackList, null)
-                .set(MarkTask::getHeaderTagList, null)
-                .set(MarkTask::getRejectReason, rejectReason)
-                .eq(MarkTask::getId, markTaskId);
-        return this.update(updateWrapper);
-    }
-
-    @Override
-    public int countByExamIdAndPaperNumberAndQuestionIdAndStatusIn(Long examId, String paperNumber, Long questionId, List<MarkTaskStatus> statusList) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId)
-                .in(MarkTask::getStatus, statusList);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public int countByExamIdAndPaperNumberAndStatusIn(Long examId, String paperNumber, List<MarkTaskStatus> statusList) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .in(MarkTask::getStatus, statusList);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public MarkTask getLastOneByUserIdAndStatus(Long examId, String paperNumber, Long questionId, Long userId, MarkTaskStatus status) {
-        return this.baseMapper.getLastOneByUserIdAndStatus(examId, paperNumber, questionId, userId, status.name());
-    }
-
-    @Override
-    public List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndStudentId(Long examId, String paperNumber, Long questionId, Long studentId) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId)
-                .eq(MarkTask::getStudentId, studentId);
-        return this.list(queryWrapper);
-    }
-
-    @Override
-    public List<MarkTask> listByStudentIdAndQuestionId(Long studentId, Long questionId) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getQuestionId, questionId);
-        return this.list(queryWrapper);
-    }
-
-    @Override
-    public void updateHeaderResult(Long examId, String paperNumber, Long questionId, Long studentId, Long updateUserId, Double markerScore, TrackDTO[] tracks, SpecialTagDTO[] specialTags, Long updateTime, MarkTaskStatus arbitrated) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().set(MarkTask::getHeaderId, updateUserId)
-                .set(MarkTask::getHeaderScore, markerScore)
-                .set(MarkTask::getHeaderTrackList, JSON.toJSONString(tracks))
-                .set(MarkTask::getHeaderTagList, JSON.toJSONString(specialTags))
-                .set(MarkTask::getHeaderTime, updateTime)
-                .set(MarkTask::getStatus, arbitrated)
-                .eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId)
-                .eq(MarkTask::getStudentId, studentId);
-        this.update(updateWrapper);
-    }
-
-    @Override
-    public void deleteByStudentId(Long studentId) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().eq(MarkTask::getStudentId, studentId);
-        this.remove(updateWrapper);
-    }
-
-    @Override
-    public void deleteByExamIdAndPaperNumberAndQuestionId(Long examId, String paperNumber, Long questionId) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId);
-        this.remove(updateWrapper);
-    }
-
-    @Override
-    public int countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(Long examId, String paperNumber, Long questionId, List<String> className, MarkTaskStatus... status) {
-        return this.baseMapper.countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(examId, paperNumber, questionId, className, status);
-    }
-
-    @Override
-    public int countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(Long examId, String paperNumber, Long userId, List<String> className, List<Long> questionIds, MarkTaskStatus... status) {
-        return this.baseMapper.countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(examId, paperNumber, userId, className, questionIds, status, CollectionUtils.size(questionIds));
-    }
-
-    @Override
-    public IPage<MarkTask> listPageHistory(Page<Long> page, Long userId, Long examId, String paperNumber, String secretNumber, Double markerScore, MarkTaskStatus...statuses) {
-        return this.baseMapper.listPageHistory(page, userId, examId, paperNumber, secretNumber, markerScore, statuses);
-    }
-
-    @Override
-    public List<MarkTask> findUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long userId) {
-        return this.baseMapper.findUnMarked(page, examId, paperNumber, userId);
-    }
-
-    @Override
-    public List<MarkTask> findUnMarkedFilterClass(Page<MarkTask> page, Long examId, String paperNumber, Long userId, Long questionId, List<String> classNames) {
-        return this.baseMapper.findUnMarkedFilterClass(page, examId, paperNumber, userId, questionId, classNames);
-    }
-
-    @Override
-    public List<MarkTask> findAiUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long questionId) {
-        return this.baseMapper.findAiUnMarked(page, examId, paperNumber, questionId);
-    }
-
-    @Override
-    public int countByIdAndStatus(Long studentId, MarkTaskStatus status) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getStatus, status);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public int countByStatusAndIdIn(List<Long> ids, MarkTaskStatus... status) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().in(MarkTask::getId, ids)
-                .in(MarkTask::getStatus, status)
-                .isNull(MarkTask::getUserId);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public List<MarkTask> findByStudentIdAndQuestionIdAndStatus(Long studentId, Long questionId, MarkTaskStatus... status) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getQuestionId, questionId)
-                .in(MarkTask::getStatus, Arrays.asList(status));
-        return this.list(queryWrapper);
-    }
-
-    @Override
-    public boolean updateProblemResult(Long taskId, Long userId, Long now, int spent) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().eq(MarkTask::getId, taskId)
-                .in(MarkTask::getStatus, Arrays.asList(MarkTaskStatus.WAITING, MarkTaskStatus.REJECTED, MarkTaskStatus.MARKED, MarkTaskStatus.PROBLEM))
-                .set(MarkTask::getStatus, MarkTaskStatus.PROBLEM)
-                .set(MarkTask::getUserId, userId)
-                .set(MarkTask::getMarkerScore, null)
-                .set(MarkTask::getMarkerTrackList, null)
-                .set(MarkTask::getMarkerTagList, null)
-                .set(MarkTask::getHeaderTime, null)
-                .set(MarkTask::getHeaderId, null)
-                .set(MarkTask::getHeaderScore, null)
-                .set(MarkTask::getHeaderTrackList, null)
-                .set(MarkTask::getHeaderTagList, null)
-                .set(MarkTask::getMarkerTime, now)
-                .set(MarkTask::getMarkerSpent, spent);
-        return this.update(updateWrapper);
-    }
-
-    @Override
-    public boolean updateStatusByStudentIdAndQuestionId(Long studentId, Long questionId, MarkTaskStatus status) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getQuestionId, questionId)
-                .set(MarkTask::getStatus, status);
-        return this.update(updateWrapper);
-    }
-
-    @Override
-    public int countByStudentIdAndMarkerIdAndIdNotEqual(Long studentId, Long examId, String paperNumber, Long questionId, Long userId, Long taskId) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId)
-                .eq(MarkTask::getUserId, userId)
-                .ne(MarkTask::getId, taskId);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public boolean updateMarkerResult(Long taskId, MarkTaskStatus status, Long userId,
-                                      MarkResultQuestion result, Long now, MarkTaskStatus... inStatus) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().eq(MarkTask::getId, taskId)
-                .in(MarkTask::getStatus, Arrays.asList(inStatus))
-                .set(MarkTask::getStatus, status)
-                .set(MarkTask::getUserId, userId)
-                .set(MarkTask::getMarkerScore, result.getMarkerScore())
-                .set(MarkTask::getMarkerTrackList, JSON.toJSONString(result.getMarkerTrackList()))
-                .set(MarkTask::getMarkerTagList, JSON.toJSONString(result.getMarkerTagList()))
-                .set(MarkTask::getMarkerTime, now)
-                .set(MarkTask::getMarkerSpent, result.getSpent())
-                .set(MarkTask::getHeaderId, null)
-                .set(MarkTask::getHeaderTime, null)
-                .set(MarkTask::getRejectReason, null);
-        return this.update(updateWrapper);
-    }
-
-    @Override
-    public int countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(Long examId, String paperNumber, Long questionId, int taskNumber) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber)
-                .eq(MarkTask::getQuestionId, questionId)
-                .eq(MarkTask::getTaskNumber, taskNumber);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public int countByStudentId(Long studentId) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public int countByExamIdAndPaperNumber(Long examId, String paperNumber) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
-                .eq(MarkTask::getPaperNumber, paperNumber);
-        return this.count(queryWrapper);
-    }
-
-    @Override
-    public IPage<MarkTaskDto> pageMarkTask(Long examId, String paperNumber, Long questionId, String loginName, MarkTaskStatus status, String studentCode, String secretNumber, String teachClassName, Double subScore, Integer pageNumber, Integer pageSize) {
-        IPage<MarkTaskDto> markTaskDtoIPage = this.baseMapper.pageMarkTask(new Page<>(pageNumber, pageSize), examId, paperNumber, questionId, loginName, status, studentCode, secretNumber, teachClassName, subScore);
-        for (MarkTaskDto record : markTaskDtoIPage.getRecords()) {
-            record.setStatusDisplay(record.getStatus().getName());
-            // 分组题目
-            MarkQuestion markQuestion = markQuestionService.getById(record.getQuestionId());
-            record.setQuestionNumber(markQuestion.getQuestionNumber());
-            record.setQuestionScore(markQuestion.getTotalScore());
-        }
-        return markTaskDtoIPage;
-    }
-
-    @Override
-    public void rejectMarkTask(Long id, String rejectReason) {
-        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
-        MarkTask markTask = this.getById(id);
-        if (markTask == null) {
-            throw ExceptionResultEnum.ERROR.exception("任务不存在");
-        }
-        try {
-            lockService.watch(LockType.EXAM_SUBJECT, markTask.getExamId(), markTask.getPaperNumber());
-            lockService.watch(LockType.QUESTION, markTask.getQuestionId());
-            lockService.waitlock(LockType.STUDENT, markTask.getStudentId());
-            if (!markTask.getStatus().equals(MarkTaskStatus.MARKED)) {
-                throw ExceptionResultEnum.ERROR.exception("当前任务不允许打回");
-            }
-            markService.rejectMarkTask(markTask, sysUser.getId(), rejectReason);
-        } catch (Exception e) {
-            log.error("打回失败", e);
-        } finally {
-            lockService.unlock(LockType.STUDENT, markTask.getStudentId());
-            lockService.unwatch(LockType.QUESTION, markTask.getQuestionId());
-            lockService.unwatch(LockType.EXAM_SUBJECT, markTask.getExamId(), markTask.getPaperNumber());
-        }
-    }
-
-    @Override
-    public Task getMarkTrack(Long id) {
-        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
-        MarkTask markTask = this.getById(id);
-        if (markTask == null) {
-            throw ExceptionResultEnum.ERROR.exception("任务不存在,请刷新列表");
-        }
-        if (!MarkTaskStatus.MARKED.equals(markTask.getStatus())) {
-            throw ExceptionResultEnum.ERROR.exception("已评任务才允许打回");
-        }
-        Task dto = taskService.build(sysUser.getId(), Arrays.asList(markTask));
-        return dto;
-    }
-
-    @Override
-    public MarkManageDto selectMarkedCountAndTotalCount(Long examId, String paperNumber) {
-        return this.baseMapper.selectMarkedCountAndTotalCount(examId, paperNumber);
-    }
-
-    @Override
-    public List<MarkTask> listByStudentId(Long studentId) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
-                .orderByAsc(MarkTask::getMainNumber)
-                .orderByAsc(MarkTask::getSubNumber);
-        return this.list(queryWrapper);
-    }
-
-    @Override
-    public Long minQuestionIdByExamIdAndPaperNumber(Long examId, String paperNumber, MarkTaskStatus... statusList) {
-        return this.baseMapper.minQuestionIdByExamIdAndPaperNumber(examId, paperNumber, statusList);
-    }
-
-    @Override
-    public List<MarkTask> listByStudentIdAndUserId(Long studentId, Long userId) {
-        return baseMapper.listByStudentIdAndUserId(studentId, userId);
-    }
-
-    @Override
-    public List<MarkTask> listByStudentIdAndMarkerId(Long studentId, Long markerId, List<MarkTaskStatus> markTaskStatuses) {
-        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
-        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getUserId, markerId)
-                .in(MarkTask::getStatus, markTaskStatuses)
-                .orderByAsc(MarkTask::getMainNumber)
-                .orderByAsc(MarkTask::getSubNumber);
-        return this.list(queryWrapper);
-    }
-
-    @Override
-    public void resetHeaderByStudentIdAndQuestionId(Long studentId, Long questionId) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().set(MarkTask::getHeaderId, null)
-                .set(MarkTask::getHeaderTime, null)
-                .set(MarkTask::getHeaderScore, null)
-                .set(MarkTask::getHeaderTrackList, null)
-                .set(MarkTask::getHeaderTagList, null)
-                .eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getQuestionId, questionId);
-        this.update(updateWrapper);
-    }
-
-    @Override
-    public void resetArbitrateStatusByStudentIdAndQuestionIdAndTaskNumber(Long studentId, Long questionId, Integer taskNumber) {
-        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().set(MarkTask::getStatus, MarkTaskStatus.MARKED)
-                .set(MarkTask::getHeaderId, null)
-                .set(MarkTask::getHeaderTime, null)
-                .set(MarkTask::getHeaderScore, null)
-                .set(MarkTask::getHeaderTrackList, null)
-                .set(MarkTask::getHeaderTagList, null)
-                .eq(MarkTask::getStudentId, studentId)
-                .eq(MarkTask::getQuestionId, questionId)
-                .ne(MarkTask::getTaskNumber, taskNumber)
-                .in(MarkTask::getStatus, MarkTaskStatus.WAIT_ARBITRATE, MarkTaskStatus.ARBITRATED);
-        this.update(updateWrapper);
-    }
-}
+package com.qmth.teachcloud.mark.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
+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.mark.MarkProblemStatus;
+import com.qmth.teachcloud.common.service.BasicRoleDataPermissionService;
+import com.qmth.teachcloud.common.util.ExcelUtil;
+import com.qmth.teachcloud.common.util.ServletUtil;
+import com.qmth.teachcloud.mark.dto.mark.SpecialTagDTO;
+import com.qmth.teachcloud.mark.dto.mark.manage.*;
+import com.qmth.teachcloud.mark.entity.MarkTask;
+import com.qmth.teachcloud.mark.enums.LockType;
+import com.qmth.teachcloud.mark.enums.MarkTaskStatus;
+import com.qmth.teachcloud.mark.lock.LockService;
+import com.qmth.teachcloud.mark.mapper.MarkTaskMapper;
+import com.qmth.teachcloud.mark.params.MarkResultQuestion;
+import com.qmth.teachcloud.mark.service.*;
+import com.qmth.teachcloud.mark.utils.Calculator;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <p>
+ * 评卷任务表 服务实现类
+ * </p>
+ *
+ * @author xf
+ * @since 2023-09-22
+ */
+@Service
+public class MarkTaskServiceImpl extends ServiceImpl<MarkTaskMapper, MarkTask> implements MarkTaskService {
+
+    @Resource
+    private MarkQuestionService markQuestionService;
+    @Resource
+    private MarkProblemHistoryService markProblemHistoryService;
+    @Resource
+    private MarkService markService;
+    @Resource
+    private LockService lockService;
+    @Resource
+    private TaskService taskService;
+    @Resource
+    private BasicRoleDataPermissionService basicRoleDataPermissionService;
+
+    @Override
+    public IPage<MarkManageDto> listPaperManage(Long examId, Long openCollegeId, Long courseId, String paperNumber, Boolean progressStatus, Integer pageNumber, Integer pageSize) {
+        Page<MarkManageDto> page = new Page<>(pageNumber, pageSize);
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(sysUser.getSchoolId(), sysUser.getId(), ServletUtil.getRequest().getServletPath());
+        IPage<MarkManageDto> markManageDtoIPage = this.baseMapper.listPaperManage(page, examId, openCollegeId, courseId,
+                paperNumber, progressStatus, dpr);
+        for (MarkManageDto record : markManageDtoIPage.getRecords()) {
+            record.setStatusDisplay(record.getStatus() == null ? null : record.getStatus().getName());
+            MarkManageDto markManageDto = this.selectMarkedCountAndTotalCount(record.getExamId(), record.getPaperNumber());
+            if (markManageDto != null) {
+                record.setMarkedCount(markManageDto.getMarkedCount());
+                record.setTotalCount(markManageDto.getTotalCount());
+                record.setPercent(markManageDto.getTotalCount() != null && markManageDto.getTotalCount() != 0 ? Calculator.divide2String(Calculator.multiply(markManageDto.getMarkedCount(), 100), Double.valueOf(markManageDto.getTotalCount()), 2) : "0");
+            } else {
+                record.setMarkedCount(0);
+                record.setTotalCount(0);
+                record.setPercent("0");
+            }
+            // 问题卷数量
+            record.setProblemCount(markProblemHistoryService.countByExamIdAndPaperNumberAndStatusAndClassNameIn(examId, record.getPaperNumber(), MarkProblemStatus.WAITING, null));
+            // 正在评卷数量
+            record.setCurrentCount(markQuestionService.countCurrentCountByExamIdAndPaperNumber(examId, record.getPaperNumber()));
+        }
+        return markManageDtoIPage;
+    }
+
+    @Override
+    public void exportMarker(Long examId, Long courseId, String paperNumber, HttpServletResponse response) {
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(sysUser.getSchoolId(), sysUser.getId(), ServletUtil.getRequest().getServletPath());
+        List<MarkerInfoDto> markerInfoDtoList = this.baseMapper.listUserMarkedCount(examId, courseId, paperNumber, dpr);
+        for (MarkerInfoDto markerInfoDto : markerInfoDtoList) {
+            if (markerInfoDto.getQuestionId() != null) {
+                MarkQuestion markQuestion = markQuestionService.getById(markerInfoDto.getQuestionId());
+                markerInfoDto.setQuestionName(markQuestion.getMainTitle());
+                markerInfoDto.setMainNumber(markQuestion.getMainNumber());
+                markerInfoDto.setSubNumber(markQuestion.getSubNumber());
+                markerInfoDto.setTotalScore(markQuestion.getTotalScore());
+            }
+        }
+        try {
+            ExcelUtil.excelExport("评卷员工作量", MarkerInfoDto.class, markerInfoDtoList, response);
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception("导出评卷员工作量失败");
+        }
+    }
+
+    @Override
+    public List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(Long examId, String paperNumber, Long questionId, Long userId, String className) {
+        return this.baseMapper.listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndClassName(examId, paperNumber, questionId, userId, className);
+    }
+
+    @Override
+    public Integer markedCount(List<MarkTask> markTask) {
+        if (CollectionUtils.isEmpty(markTask)) {
+            return 0;
+        }
+        long count = markTask.stream().filter(m -> m.getStatus().equals(MarkTaskStatus.MARKED) || m.getStatus().equals(MarkTaskStatus.ARBITRATED) || m.getStatus().equals(MarkTaskStatus.WAIT_ARBITRATE)).count();
+        return (int) count;
+    }
+
+    @Override
+    public int countByExamIdAndPaperNumberAndQuestionIdAndUserId(Long examId, String paperNumber, Long questionId, Long userId) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<MarkTask> lambdaQueryWrapper = queryWrapper.lambda();
+        lambdaQueryWrapper.eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId);
+        if (userId != null) {
+            lambdaQueryWrapper.eq(MarkTask::getUserId, userId);
+        }
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndUserIdAndStatusNotIn(Long examId, String paperNumber, Long questionId, Long userId, List<MarkTaskStatus> statusList) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId)
+                .eq(MarkTask::getUserId, userId)
+                .notIn(MarkTask::getStatus, statusList);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public boolean resetById(Long markTaskId, Long userId, String rejectReason, Long rejectId, Long date, MarkTaskStatus newStatus) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkTask::getStatus, newStatus)
+                .set(MarkTask::getUserId, userId)
+                .set(MarkTask::getMarkerTime, null)
+                .set(MarkTask::getMarkerScore, null)
+                .set(MarkTask::getMarkerTrackList, null)
+                .set(MarkTask::getMarkerTagList, null)
+                .set(MarkTask::getMarkerSpent, null)
+                .set(MarkTask::getHeaderId, rejectId)
+                .set(MarkTask::getHeaderTime, date)
+                .set(MarkTask::getHeaderScore, null)
+                .set(MarkTask::getHeaderTrackList, null)
+                .set(MarkTask::getHeaderTagList, null)
+                .set(MarkTask::getRejectReason, rejectReason)
+                .eq(MarkTask::getId, markTaskId);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public int countByExamIdAndPaperNumberAndQuestionIdAndStatusIn(Long examId, String paperNumber, Long questionId, List<MarkTaskStatus> statusList) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId)
+                .in(MarkTask::getStatus, statusList);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public int countByExamIdAndPaperNumberAndStatusIn(Long examId, String paperNumber, List<MarkTaskStatus> statusList) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .in(MarkTask::getStatus, statusList);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public MarkTask getLastOneByUserIdAndStatus(Long examId, String paperNumber, Long questionId, Long userId, MarkTaskStatus status) {
+        return this.baseMapper.getLastOneByUserIdAndStatus(examId, paperNumber, questionId, userId, status.name());
+    }
+
+    @Override
+    public List<MarkTask> listByExamIdAndPaperNumberAndQuestionIdAndStudentId(Long examId, String paperNumber, Long questionId, Long studentId) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId)
+                .eq(MarkTask::getStudentId, studentId);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public List<MarkTask> listByStudentIdAndQuestionId(Long studentId, Long questionId) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getQuestionId, questionId);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public void updateHeaderResult(Long examId, String paperNumber, Long questionId, Long studentId, Long updateUserId, Double markerScore, TrackDTO[] tracks, SpecialTagDTO[] specialTags, Long updateTime, MarkTaskStatus arbitrated) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkTask::getHeaderId, updateUserId)
+                .set(MarkTask::getHeaderScore, markerScore)
+                .set(MarkTask::getHeaderTrackList, JSON.toJSONString(tracks))
+                .set(MarkTask::getHeaderTagList, JSON.toJSONString(specialTags))
+                .set(MarkTask::getHeaderTime, updateTime)
+                .set(MarkTask::getStatus, arbitrated)
+                .eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId)
+                .eq(MarkTask::getStudentId, studentId);
+        this.update(updateWrapper);
+    }
+
+    @Override
+    public void deleteByStudentId(Long studentId) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(MarkTask::getStudentId, studentId);
+        this.remove(updateWrapper);
+    }
+
+    @Override
+    public void deleteByExamIdAndPaperNumberAndQuestionId(Long examId, String paperNumber, Long questionId) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId);
+        this.remove(updateWrapper);
+    }
+
+    @Override
+    public int countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(Long examId, String paperNumber, Long questionId, List<String> className, MarkTaskStatus... status) {
+        return this.baseMapper.countByExamIdAndPaperNumberAndQuestionIdAndAndClassNameStatusIn(examId, paperNumber, questionId, className, status);
+    }
+
+    @Override
+    public int countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(Long examId, String paperNumber, Long userId, List<String> className, List<Long> questionIds, MarkTaskStatus... status) {
+        return this.baseMapper.countByExamIdAndPaperNumberAndUserIdAndAndClassNameAndQuestionIdIn(examId, paperNumber, userId, className, questionIds, status, CollectionUtils.size(questionIds));
+    }
+
+    @Override
+    public IPage<MarkTask> listPageHistory(Page<Long> page, Long userId, Long examId, String paperNumber, String secretNumber, Double markerScore, MarkTaskStatus... statuses) {
+        return this.baseMapper.listPageHistory(page, userId, examId, paperNumber, secretNumber, markerScore, statuses);
+    }
+
+    @Override
+    public List<MarkTask> findUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long userId) {
+        return this.baseMapper.findUnMarked(page, examId, paperNumber, userId);
+    }
+
+    @Override
+    public List<MarkTask> findUnMarkedFilterClass(Page<MarkTask> page, Long examId, String paperNumber, Long userId, Long questionId, List<String> classNames) {
+        return this.baseMapper.findUnMarkedFilterClass(page, examId, paperNumber, userId, questionId, classNames);
+    }
+
+    @Override
+    public List<MarkTask> findAiUnMarked(Page<MarkTask> page, Long examId, String paperNumber, Long questionId) {
+        return this.baseMapper.findAiUnMarked(page, examId, paperNumber, questionId);
+    }
+
+    @Override
+    public int countByIdAndStatus(Long studentId, MarkTaskStatus status) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getStatus, status);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public int countByStatusAndIdIn(List<Long> ids, MarkTaskStatus... status) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().in(MarkTask::getId, ids)
+                .in(MarkTask::getStatus, status)
+                .isNull(MarkTask::getUserId);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public List<MarkTask> findByStudentIdAndQuestionIdAndStatus(Long studentId, Long questionId, MarkTaskStatus... status) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getQuestionId, questionId)
+                .in(MarkTask::getStatus, Arrays.asList(status));
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public boolean updateProblemResult(Long taskId, Long userId, Long now, int spent) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(MarkTask::getId, taskId)
+                .in(MarkTask::getStatus, Arrays.asList(MarkTaskStatus.WAITING, MarkTaskStatus.REJECTED, MarkTaskStatus.MARKED, MarkTaskStatus.PROBLEM))
+                .set(MarkTask::getStatus, MarkTaskStatus.PROBLEM)
+                .set(MarkTask::getUserId, userId)
+                .set(MarkTask::getMarkerScore, null)
+                .set(MarkTask::getMarkerTrackList, null)
+                .set(MarkTask::getMarkerTagList, null)
+                .set(MarkTask::getHeaderTime, null)
+                .set(MarkTask::getHeaderId, null)
+                .set(MarkTask::getHeaderScore, null)
+                .set(MarkTask::getHeaderTrackList, null)
+                .set(MarkTask::getHeaderTagList, null)
+                .set(MarkTask::getMarkerTime, now)
+                .set(MarkTask::getMarkerSpent, spent);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean updateStatusByStudentIdAndQuestionId(Long studentId, Long questionId, MarkTaskStatus status) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getQuestionId, questionId)
+                .set(MarkTask::getStatus, status);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public int countByStudentIdAndMarkerIdAndIdNotEqual(Long studentId, Long examId, String paperNumber, Long questionId, Long userId, Long taskId) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId)
+                .eq(MarkTask::getUserId, userId)
+                .ne(MarkTask::getId, taskId);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public boolean updateMarkerResult(Long taskId, MarkTaskStatus status, Long userId,
+                                      MarkResultQuestion result, Long now, MarkTaskStatus... inStatus) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().eq(MarkTask::getId, taskId)
+                .in(MarkTask::getStatus, Arrays.asList(inStatus))
+                .set(MarkTask::getStatus, status)
+                .set(MarkTask::getUserId, userId)
+                .set(MarkTask::getMarkerScore, result.getMarkerScore())
+                .set(MarkTask::getMarkerTrackList, JSON.toJSONString(result.getMarkerTrackList()))
+                .set(MarkTask::getMarkerTagList, JSON.toJSONString(result.getMarkerTagList()))
+                .set(MarkTask::getMarkerTime, now)
+                .set(MarkTask::getMarkerSpent, result.getSpent())
+                .set(MarkTask::getHeaderId, null)
+                .set(MarkTask::getHeaderTime, null)
+                .set(MarkTask::getRejectReason, null);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public int countByExamIdAndPaperNumberAndQuestionIdAndTaskNumber(Long examId, String paperNumber, Long questionId, int taskNumber) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber)
+                .eq(MarkTask::getQuestionId, questionId)
+                .eq(MarkTask::getTaskNumber, taskNumber);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public int countByStudentId(Long studentId) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public int countByExamIdAndPaperNumber(Long examId, String paperNumber) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getExamId, examId)
+                .eq(MarkTask::getPaperNumber, paperNumber);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public IPage<MarkTaskDto> pageMarkTask(Long examId, String paperNumber, Long questionId, String loginName, MarkTaskStatus status, String studentCode, String secretNumber, String teachClassName, Double subScore, Integer pageNumber, Integer pageSize) {
+        IPage<MarkTaskDto> markTaskDtoIPage = this.baseMapper.pageMarkTask(new Page<>(pageNumber, pageSize), examId, paperNumber, questionId, loginName, status, studentCode, secretNumber, teachClassName, subScore);
+        for (MarkTaskDto record : markTaskDtoIPage.getRecords()) {
+            record.setStatusDisplay(record.getStatus().getName());
+            // 分组题目
+            MarkQuestion markQuestion = markQuestionService.getById(record.getQuestionId());
+            record.setQuestionNumber(markQuestion.getQuestionNumber());
+            record.setQuestionScore(markQuestion.getTotalScore());
+        }
+        return markTaskDtoIPage;
+    }
+
+    @Override
+    public void rejectMarkTask(Long id, String rejectReason) {
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        MarkTask markTask = this.getById(id);
+        if (markTask == null) {
+            throw ExceptionResultEnum.ERROR.exception("任务不存在");
+        }
+        try {
+            lockService.watch(LockType.EXAM_SUBJECT, markTask.getExamId(), markTask.getPaperNumber());
+            lockService.watch(LockType.QUESTION, markTask.getQuestionId());
+            lockService.waitlock(LockType.STUDENT, markTask.getStudentId());
+            if (!markTask.getStatus().equals(MarkTaskStatus.MARKED)) {
+                throw ExceptionResultEnum.ERROR.exception("当前任务不允许打回");
+            }
+            markService.rejectMarkTask(markTask, sysUser.getId(), rejectReason);
+        } catch (Exception e) {
+            log.error("打回失败", e);
+        } finally {
+            lockService.unlock(LockType.STUDENT, markTask.getStudentId());
+            lockService.unwatch(LockType.QUESTION, markTask.getQuestionId());
+            lockService.unwatch(LockType.EXAM_SUBJECT, markTask.getExamId(), markTask.getPaperNumber());
+        }
+    }
+
+    @Override
+    public Task getMarkTrack(Long id) {
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        MarkTask markTask = this.getById(id);
+        if (markTask == null) {
+            throw ExceptionResultEnum.ERROR.exception("任务不存在,请刷新列表");
+        }
+        if (!MarkTaskStatus.MARKED.equals(markTask.getStatus())) {
+            throw ExceptionResultEnum.ERROR.exception("已评任务才允许打回");
+        }
+        Task dto = taskService.build(sysUser.getId(), Arrays.asList(markTask));
+        return dto;
+    }
+
+    @Override
+    public MarkManageDto selectMarkedCountAndTotalCount(Long examId, String paperNumber) {
+        return this.baseMapper.selectMarkedCountAndTotalCount(examId, paperNumber);
+    }
+
+    @Override
+    public List<MarkTask> listByStudentId(Long studentId) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
+                .orderByAsc(MarkTask::getMainNumber)
+                .orderByAsc(MarkTask::getSubNumber);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public Long minQuestionIdByExamIdAndPaperNumber(Long examId, String paperNumber, MarkTaskStatus... statusList) {
+        return this.baseMapper.minQuestionIdByExamIdAndPaperNumber(examId, paperNumber, statusList);
+    }
+
+    @Override
+    public List<MarkTask> listByStudentIdAndUserId(Long studentId, Long userId) {
+        return baseMapper.listByStudentIdAndUserId(studentId, userId);
+    }
+
+    @Override
+    public List<MarkTask> listByStudentIdAndMarkerId(Long studentId, Long markerId, List<MarkTaskStatus> markTaskStatuses) {
+        QueryWrapper<MarkTask> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getUserId, markerId)
+                .in(MarkTask::getStatus, markTaskStatuses)
+                .orderByAsc(MarkTask::getMainNumber)
+                .orderByAsc(MarkTask::getSubNumber);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public void resetHeaderByStudentIdAndQuestionId(Long studentId, Long questionId) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkTask::getHeaderId, null)
+                .set(MarkTask::getHeaderTime, null)
+                .set(MarkTask::getHeaderScore, null)
+                .set(MarkTask::getHeaderTrackList, null)
+                .set(MarkTask::getHeaderTagList, null)
+                .eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getQuestionId, questionId);
+        this.update(updateWrapper);
+    }
+
+    @Override
+    public void resetArbitrateStatusByStudentIdAndQuestionIdAndTaskNumber(Long studentId, Long questionId, Integer taskNumber) {
+        UpdateWrapper<MarkTask> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkTask::getStatus, MarkTaskStatus.MARKED)
+                .set(MarkTask::getHeaderId, null)
+                .set(MarkTask::getHeaderTime, null)
+                .set(MarkTask::getHeaderScore, null)
+                .set(MarkTask::getHeaderTrackList, null)
+                .set(MarkTask::getHeaderTagList, null)
+                .eq(MarkTask::getStudentId, studentId)
+                .eq(MarkTask::getQuestionId, questionId)
+                .ne(MarkTask::getTaskNumber, taskNumber)
+                .in(MarkTask::getStatus, MarkTaskStatus.WAIT_ARBITRATE, MarkTaskStatus.ARBITRATED);
+        this.update(updateWrapper);
+    }
+
+    /**
+     * AI智能评卷导出
+     *
+     * @param semesterId
+     * @param examId
+     * @param courseId
+     * @param paperNumber
+     * @throws Exception
+     */
+    @Override
+    public void exportAiMark(Long semesterId, Long examId, Long courseId, String paperNumber) throws Exception {
+        HttpServletResponse response = ServletUtil.getResponse();
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        DataPermissionRule dpr = basicRoleDataPermissionService.findDataPermission(sysUser.getSchoolId(), sysUser.getId(), ServletUtil.getRequest().getServletPath());
+        List<MarkAiExport> markAiExportList = this.baseMapper.exportAiMark(semesterId, examId, courseId, paperNumber, dpr);
+        ExcelUtil.excelExport("AI智能评卷导出", MarkAiExport.class, markAiExportList, response);
+    }
+}

+ 46 - 0
teachcloud-mark/src/main/resources/mapper/MarkTaskMapper.xml

@@ -449,4 +449,50 @@
             order by t.main_number, t.sub_number
             order by t.main_number, t.sub_number
         </where>
         </where>
     </select>
     </select>
+
+    <select id="exportAiMark" resultType="com.qmth.teachcloud.mark.dto.mark.manage.MarkAiExport">
+        select
+            bc.name as courseName,
+            bc.code as courseCode,
+            maqp.paper_number as paperNumber,
+            count(distinct maqp.question_id) as questionNum,
+            count(mt.question_id) as taskNum
+        from mark_ai_question_param maqp
+            left join basic_exam be on be.id = maqp.exam_id
+            left join basic_semester bs on bs.id = be.semester_id
+            left join basic_course bc on bc.id = maqp.course_id
+            left join mark_paper mp on mp.paper_number = maqp.paper_number
+            left join mark_task mt on mt.exam_id = maqp.exam_id and mt.course_id = maqp.course_id and mt.paper_number = maqp.paper_number
+        <where>
+            <if test="semesterId != null and semesterId != ''">
+                and bs.id = #{semesterId}
+            </if>
+            <if test="examId != null and examId != ''">
+                and be.id = #{examId}
+            </if>
+            <if test="courseId != null and courseId != ''">
+                and bc.id = #{courseId}
+            </if>
+            <if test="paperNumber != null and paperNumber != ''">
+                and mp.paper_number = #{paperNumber}
+            </if>
+            and mt.ai_marked = true
+            <if test="dpr != null">
+                <if test="dpr.requestUserId != null">
+                    and mp.user_id = #{dpr.requestUserId}
+                </if>
+                <if test="dpr.courseUserId != null">
+                    AND EXISTS( SELECT 1 FROM (select course_id from teach_course where exam_id = #{examId} and user_id = #{dpr.courseUserId}) tc WHERE tc.course_id = mp.course_id)
+                </if>
+                <if test="dpr.orgIdSet != null">
+                    AND bc.teaching_room_id IN
+                    <foreach collection="dpr.orgIdSet" item="item" index="index" open="(" separator="," close=")">
+                        #{item}
+                    </foreach>
+                </if>
+            </if>
+        </where>
+        group by bc.name,bc.code,maqp.question_id
+        order by bc.name
+    </select>
 </mapper>
 </mapper>