Browse Source

3.3.0 fix

xiaofei 1 year ago
parent
commit
5684dcc353

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

@@ -242,6 +242,7 @@ public class SysController {
         }
         SysUser sysUser = userList.get(0);
         LoginResult loginResult = teachcloudCommonService.login(login.getPassword(), sysUser, AppSourceEnum.SYSTEM);
+        loginResult.setUiSetting(sysUser.getUiSetting());
         try {
             basicOperationLogService.saveOperateLogWithNoAuth(sysUser.getSchoolId(), sysUser.getOrgId(), sysUser.getId(), "用户登录", CustomizedOperationTypeEnum.LOGIN, SystemEnum.DISTRIBUTED_PRINT);
         } catch (Exception e) {

+ 2 - 0
distributed-print/src/main/java/com/qmth/distributed/print/api/mark/MarkInspectedController.java

@@ -1,6 +1,7 @@
 package com.qmth.distributed.print.api.mark;
 
 
+import com.qmth.boot.api.annotation.Aac;
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.util.Result;
@@ -71,6 +72,7 @@ public class MarkInspectedController {
     /**
      * 主观题检查任务获取
      */
+    @Aac(auth = false)
     @ApiOperation(value = "主观题检查任务获取")
     @RequestMapping(value = "/subjective/getTask", method = RequestMethod.POST)
     public Result getSubjectiveInspectedTask(@ApiParam(value = "考生ID", required = true) @RequestParam Long studentId) {

+ 11 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/bean/result/LoginResult.java

@@ -71,6 +71,9 @@ public class LoginResult implements Serializable {
     @ApiModelProperty(name = "版本号")
     String version;
 
+    @ApiModelProperty(name = "评卷设置")
+    String uiSetting;
+
     public String getVersion() {
         return version;
     }
@@ -228,6 +231,14 @@ public class LoginResult implements Serializable {
         this.schoolInfo = schoolInfo;
     }
 
+    public String getUiSetting() {
+        return uiSetting;
+    }
+
+    public void setUiSetting(String uiSetting) {
+        this.uiSetting = uiSetting;
+    }
+
     public class SchoolNativeBean implements Serializable {
 
         @ApiModelProperty(value = "id")

+ 24 - 20
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/MarkStudent.java

@@ -105,16 +105,20 @@ public class MarkStudent implements Serializable {
     private String answers;
 
     @ApiModelProperty(value = "是否已上传")
-    private Boolean isUpload;
+    @TableField(value = "is_upload")
+    private Boolean upload;
 
     @ApiModelProperty(value = "是否缺考")
-    private Boolean isAbsent;
+    @TableField(value = "is_absent")
+    private Boolean absent;
 
     @ApiModelProperty(value = "是否人工指定缺考")
-    private Boolean isManualAbsent;
+    @TableField(value = "is_manual_absent")
+    private Boolean manualAbsent;
 
     @ApiModelProperty(value = "是否违纪")
-    private Boolean isBreach;
+    @TableField(value = "is_breach")
+    private Boolean breach;
 
     @ApiModelProperty(value = "上传时间")
     private Long uploadTime;
@@ -197,10 +201,10 @@ public class MarkStudent implements Serializable {
         this.college = college;
         this.className = className;
         this.sheetCount = 0;
-        this.isUpload = false;
-        this.isAbsent = false;
-        this.isManualAbsent = false;
-        this.isBreach = false;
+        this.upload = false;
+        this.absent = false;
+        this.manualAbsent = false;
+        this.breach = false;
         this.absentSuspect = false;
         this.assignConfirmed = false;
         this.subjectiveStatus = SubjectiveStatus.UNMARK;
@@ -344,35 +348,35 @@ public class MarkStudent implements Serializable {
     }
 
     public Boolean getUpload() {
-        return isUpload;
+        return upload;
     }
 
     public void setUpload(Boolean upload) {
-        isUpload = upload;
+        this.upload = upload;
     }
 
     public Boolean getAbsent() {
-        return isAbsent;
+        return absent;
     }
 
     public void setAbsent(Boolean absent) {
-        isAbsent = absent;
+        this.absent = absent;
     }
 
     public Boolean getManualAbsent() {
-        return isManualAbsent;
+        return manualAbsent;
     }
 
     public void setManualAbsent(Boolean manualAbsent) {
-        isManualAbsent = manualAbsent;
+        this.manualAbsent = manualAbsent;
     }
 
     public Boolean getBreach() {
-        return isBreach;
+        return breach;
     }
 
     public void setBreach(Boolean breach) {
-        isBreach = breach;
+        this.breach = breach;
     }
 
     public Long getUploadTime() {
@@ -667,10 +671,10 @@ public class MarkStudent implements Serializable {
                 ", batchCode=" + batchCode +
                 ", sheetCount=" + sheetCount +
                 ", answers=" + answers +
-                ", isUpload=" + isUpload +
-                ", isAbsent=" + isAbsent +
-                ", isManualAbsent=" + isManualAbsent +
-                ", isBreach=" + isBreach +
+                ", isUpload=" + upload +
+                ", isAbsent=" + absent +
+                ", isManualAbsent=" + manualAbsent +
+                ", isBreach=" + breach +
                 ", uploadTime=" + uploadTime +
                 ", checkTime=" + checkTime +
                 ", checkUserId=" + checkUserId +

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

@@ -111,4 +111,6 @@ public interface MarkStudentService extends IService<MarkStudent> {
 	ScoreReportVo scoreReport(Long examId, String paperNumber);
 
     void exportUnexist(Long examId, HttpServletResponse response);
+
+    long countUnmarkByExamIdAndPaperNumber(Long examId, String paperNumber);
 }

+ 65 - 27
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkPaperServiceImpl.java

@@ -1,15 +1,5 @@
 package com.qmth.teachcloud.mark.service.impl;
 
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import javax.annotation.Resource;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.stereotype.Service;
-
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
@@ -18,6 +8,7 @@ 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.bean.dto.mark.MarkSettingDto;
+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.MarkPaperStatus;
@@ -30,12 +21,22 @@ import com.qmth.teachcloud.mark.dto.mark.score.CheckScoreListDto;
 import com.qmth.teachcloud.mark.dto.mark.score.MarkPaperPackageDto;
 import com.qmth.teachcloud.mark.dto.mark.score.SettingDto;
 import com.qmth.teachcloud.mark.entity.MarkPaper;
+import com.qmth.teachcloud.mark.entity.MarkStudent;
 import com.qmth.teachcloud.mark.entity.ScanPackage;
 import com.qmth.teachcloud.mark.mapper.MarkPaperMapper;
 import com.qmth.teachcloud.mark.service.MarkPaperService;
+import com.qmth.teachcloud.mark.service.MarkQuestionService;
 import com.qmth.teachcloud.mark.service.MarkStudentService;
 import com.qmth.teachcloud.mark.service.ScanPackageService;
 import com.qmth.teachcloud.mark.utils.Calculator;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -55,6 +56,8 @@ public class MarkPaperServiceImpl extends ServiceImpl<MarkPaperMapper, MarkPaper
     @Resource
     private BasicRoleDataPermissionService basicRoleDataPermissionService;
     @Resource
+    private MarkQuestionService markQuestionService;
+    @Resource
     private MarkStudentService markStudentService;
 
 
@@ -103,11 +106,46 @@ public class MarkPaperServiceImpl extends ServiceImpl<MarkPaperMapper, MarkPaper
 
     @Override
     public Boolean finishPaper(Long examId, List<String> paperNumbers, MarkPaperStatus status) {
-        UpdateWrapper<MarkPaper> updateWrapper = new UpdateWrapper<>();
-        updateWrapper.lambda().set(MarkPaper::getStatus, status)
-                .eq(MarkPaper::getExamId, examId)
-                .in(MarkPaper::getPaperNumber, paperNumbers);
-        return this.update(updateWrapper);
+        for (String paperNumber : paperNumbers) {
+            MarkPaper markPaper = this.getByExamIdAndPaperNumber(examId, paperNumber);
+            String courseInfo = String.format("%s[%s],试卷编号%s,", markPaper.getCourseName(), markPaper.getCourseCode(), markPaper.getPaperNumber());
+            // 主观题是否全部分组
+            List<MarkQuestion> markQuestionObjectiveList = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, null, true);
+            if (CollectionUtils.isNotEmpty(markQuestionObjectiveList)) {
+                if (markQuestionObjectiveList.stream().filter(m -> StringUtils.isBlank(m.getAnswer())).count() > 0) {
+                    throw ExceptionResultEnum.ERROR.exception(courseInfo + "客观题标答未设置,无法结束评卷");
+                }
+            }
+
+            // 主观题是否全部分组
+            List<MarkQuestion> markQuestionSubjectiveList = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, null, false);
+            // 没有主观题,不校验考生评卷
+            if (CollectionUtils.isNotEmpty(markQuestionSubjectiveList)) {
+                if (markQuestionSubjectiveList.stream().filter(m -> m.getGroupNumber() == null).count() > 0) {
+                    throw ExceptionResultEnum.ERROR.exception(courseInfo + "主观题未全部分组,无法结束评卷");
+                }
+                // 未缺考、未违约且已上传图片的考生全部评完
+                if (markStudentService.countUnmarkByExamIdAndPaperNumber(examId, paperNumber) > 0) {
+                    throw ExceptionResultEnum.ERROR.exception(courseInfo + "考生未全部评完,无法结束评卷");
+                }
+            }
+
+            UpdateWrapper<MarkPaper> updateWrapper = new UpdateWrapper<>();
+            updateWrapper.lambda().set(MarkPaper::getStatus, status)
+                    .eq(MarkPaper::getExamId, examId)
+                    .eq(MarkPaper::getPaperNumber, paperNumber);
+            this.update(updateWrapper);
+
+            // 结束评卷时,客观题统分
+            if (MarkPaperStatus.FINISH.equals(status)) {
+                List<MarkStudent> markStudentList = markStudentService.listByExamIdAndCoursePaperId(examId, markPaper.getCoursePaperId());
+                for (MarkStudent markStudent : markStudentList) {
+                    markStudentService.saveUploadStudent(markStudent);
+                }
+            }
+        }
+
+        return true;
     }
 
     @Override
@@ -230,18 +268,18 @@ public class MarkPaperServiceImpl extends ServiceImpl<MarkPaperMapper, MarkPaper
         return settingDto;
     }
 
-	@Override
-	public IPage<ArchiveScoreVo> scoreList(ArchiveScoreQuery query) {
-		Page<ArchiveScoreVo> page = new Page<>(query.getPageNumber(), query.getPageSize());
-		IPage<ArchiveScoreVo> ret=this.baseMapper.scoreList(page, query);
-		if(CollectionUtils.isNotEmpty(ret.getRecords())) {
-			for(ArchiveScoreVo vo :ret.getRecords()) {
-				double total=vo.getStudentCount()-vo.getAbsentCount();
-				vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100),total, 2)+"%");
-				vo.setExcellentRate(Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100),total, 2)+"%");
-			}
-		}
+    @Override
+    public IPage<ArchiveScoreVo> scoreList(ArchiveScoreQuery query) {
+        Page<ArchiveScoreVo> page = new Page<>(query.getPageNumber(), query.getPageSize());
+        IPage<ArchiveScoreVo> ret = this.baseMapper.scoreList(page, query);
+        if (CollectionUtils.isNotEmpty(ret.getRecords())) {
+            for (ArchiveScoreVo vo : ret.getRecords()) {
+                double total = vo.getStudentCount() - vo.getAbsentCount();
+                vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100), total, 2) + "%");
+                vo.setExcellentRate(Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100), total, 2) + "%");
+            }
+        }
         return ret;
-	}
+    }
 
 }

+ 926 - 953
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkStudentServiceImpl.java

@@ -1,24 +1,5 @@
 package com.qmth.teachcloud.mark.service.impl;
 
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletResponse;
-import javax.validation.constraints.NotNull;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -42,22 +23,10 @@ import com.qmth.teachcloud.common.util.ExcelUtil;
 import com.qmth.teachcloud.common.util.ServletUtil;
 import com.qmth.teachcloud.mark.bean.FilePathVo;
 import com.qmth.teachcloud.mark.bean.UpdateTimeVo;
-import com.qmth.teachcloud.mark.bean.archivescore.ArchiveStudentQuery;
-import com.qmth.teachcloud.mark.bean.archivescore.ArchiveStudentVo;
-import com.qmth.teachcloud.mark.bean.archivescore.ClassVo;
-import com.qmth.teachcloud.mark.bean.archivescore.CollegeVo;
-import com.qmth.teachcloud.mark.bean.archivescore.QuestionVo;
-import com.qmth.teachcloud.mark.bean.archivescore.ScanPaperPageVo;
-import com.qmth.teachcloud.mark.bean.archivescore.ScoreRangeVo;
-import com.qmth.teachcloud.mark.bean.archivescore.ScoreReportVo;
-import com.qmth.teachcloud.mark.bean.archivescore.TeacherVo;
+import com.qmth.teachcloud.mark.bean.archivescore.*;
 import com.qmth.teachcloud.mark.bean.omredit.OmrEditDomain;
 import com.qmth.teachcloud.mark.bean.omredit.OmrEditPaper;
-import com.qmth.teachcloud.mark.bean.scananswer.AnswerPageVo;
-import com.qmth.teachcloud.mark.bean.scananswer.AnswerPaperVo;
-import com.qmth.teachcloud.mark.bean.scananswer.AnswerQueryDomain;
-import com.qmth.teachcloud.mark.bean.scananswer.AnswerQueryVo;
-import com.qmth.teachcloud.mark.bean.scananswer.StudentPaperVo;
+import com.qmth.teachcloud.mark.bean.scananswer.*;
 import com.qmth.teachcloud.mark.bean.scanexaminfo.CheckTask;
 import com.qmth.teachcloud.mark.bean.scanexaminfo.ScanExamCheckInfoVo;
 import com.qmth.teachcloud.mark.bean.scanexaminfo.ScanExamInfoVo;
@@ -68,18 +37,8 @@ import com.qmth.teachcloud.mark.dto.UnexistStudentDto;
 import com.qmth.teachcloud.mark.dto.mark.ScoreInfo;
 import com.qmth.teachcloud.mark.dto.mark.ScoreItem;
 import com.qmth.teachcloud.mark.dto.mark.manage.Task;
-import com.qmth.teachcloud.mark.dto.mark.score.SheetUrlDto;
-import com.qmth.teachcloud.mark.dto.mark.score.StudentObjectiveAnswerDto;
-import com.qmth.teachcloud.mark.dto.mark.score.StudentObjectiveDetailDto;
-import com.qmth.teachcloud.mark.dto.mark.score.StudentPaperDetailDto;
-import com.qmth.teachcloud.mark.dto.mark.score.StudentScoreDetailDto;
-import com.qmth.teachcloud.mark.entity.MarkPaper;
-import com.qmth.teachcloud.mark.entity.MarkStudent;
-import com.qmth.teachcloud.mark.entity.MarkUserGroup;
-import com.qmth.teachcloud.mark.entity.ScanAnswerCard;
-import com.qmth.teachcloud.mark.entity.ScanPaper;
-import com.qmth.teachcloud.mark.entity.ScanPaperPage;
-import com.qmth.teachcloud.mark.entity.ScanStudentPaper;
+import com.qmth.teachcloud.mark.dto.mark.score.*;
+import com.qmth.teachcloud.mark.entity.*;
 import com.qmth.teachcloud.mark.enums.ExamStatus;
 import com.qmth.teachcloud.mark.enums.LockType;
 import com.qmth.teachcloud.mark.enums.OmrTaskStatus;
@@ -87,22 +46,22 @@ import com.qmth.teachcloud.mark.lock.LockService;
 import com.qmth.teachcloud.mark.mapper.MarkStudentMapper;
 import com.qmth.teachcloud.mark.params.MarkHeaderGroupResult;
 import com.qmth.teachcloud.mark.params.MarkHeaderResult;
-import com.qmth.teachcloud.mark.service.MarkPaperService;
-import com.qmth.teachcloud.mark.service.MarkQuestionService;
-import com.qmth.teachcloud.mark.service.MarkService;
-import com.qmth.teachcloud.mark.service.MarkStudentService;
-import com.qmth.teachcloud.mark.service.MarkSubjectiveScoreService;
-import com.qmth.teachcloud.mark.service.MarkUserGroupService;
-import com.qmth.teachcloud.mark.service.ScanAnswerCardService;
-import com.qmth.teachcloud.mark.service.ScanOmrTaskService;
-import com.qmth.teachcloud.mark.service.ScanPackageService;
-import com.qmth.teachcloud.mark.service.ScanPaperPageService;
-import com.qmth.teachcloud.mark.service.ScanPaperService;
-import com.qmth.teachcloud.mark.service.ScanStudentPaperService;
-import com.qmth.teachcloud.mark.service.TaskService;
+import com.qmth.teachcloud.mark.service.*;
 import com.qmth.teachcloud.mark.utils.BatchGetDataUtil;
 import com.qmth.teachcloud.mark.utils.Calculator;
 import com.qmth.teachcloud.mark.utils.ScoreCalculateUtil;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotNull;
+import java.text.DecimalFormat;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -114,902 +73,916 @@ import com.qmth.teachcloud.mark.utils.ScoreCalculateUtil;
  */
 @Service
 public class MarkStudentServiceImpl extends ServiceImpl<MarkStudentMapper, MarkStudent> implements MarkStudentService {
-	@Autowired
-	private MarkPaperService markPaperService;
-	@Autowired
-	private ScanPackageService scanPackageService;
-	@Autowired
-	private ScanPaperService scanPaperService;
-	@Autowired
-	private ScanPaperPageService scanPaperPageService;
-	@Autowired
-	private ScanOmrTaskService scanOmrTaskService;
-	@Autowired
-	private ScanAnswerCardService answerCardService;
-	@Autowired
-	private ScanStudentPaperService studentPaperService;
-	@Resource
-	private MarkQuestionService markQuestionService;
-	@Resource
-	private TeachcloudCommonService teachcloudCommonService;
-	@Autowired
-	private ConcurrentService concurrentService;
-	@Resource
-	private MarkService markService;
-	@Resource
-	private LockService lockService;
-	@Resource
-	private TaskService taskService;
-	@Resource
-	private MarkUserGroupService markUserGroupService;
-
-	@Autowired
-	private MarkSubjectiveScoreService markSubjectiveScoreService;
-
-	@Override
-	public List<String> listClassByExamIdAndCourseCode(Long examId, String paperNumber) {
-		QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
-		queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getPaperNumber, paperNumber);
-		List<MarkStudent> markStudentList = this.list(queryWrapper);
-
-		List<String> classNameList = new ArrayList<>();
-		if (CollectionUtils.isNotEmpty(markStudentList)) {
-			classNameList = markStudentList.stream().filter(m -> StringUtils.isNotBlank(m.getClassName()))
-					.map(MarkStudent::getClassName).distinct().collect(Collectors.toList());
-		}
-		return classNameList;
-	}
-
-	@Override
-	public void updateSubjectiveStatusAndScore(Long studentId, SubjectiveStatus status, Double score,
-			String scoreList) {
-		UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
-		updateWrapper.lambda().set(MarkStudent::getSubjectiveStatus, status)
-				.set(MarkStudent::getSubjectiveScore, score)
-				.set(MarkStudent::getSubjectiveScoreList, scoreList).eq(MarkStudent::getId, studentId);
-		this.update(updateWrapper);
-	}
-
-	@Override
-	public void updateSubjectiveStatusAndScore(Long examId, String paperNumber, SubjectiveStatus status, double score,
-			String scoreList) {
-		UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
-		updateWrapper.lambda().set(MarkStudent::getSubjectiveStatus, status).set(MarkStudent::getSubjectiveScore, score)
-				.set(MarkStudent::getSubjectiveScoreList, scoreList).eq(MarkStudent::getExamId, examId)
-				.eq(MarkStudent::getPaperNumber, paperNumber);
-		this.update(updateWrapper);
-	}
-
-	@Override
-	public ScanExamInfoVo getScanExamInfo(BasicExam exam) {
-		ScanExamInfoVo vo = new ScanExamInfoVo();
-		vo.setId(exam.getId());
-		vo.setName(exam.getName());
-		vo.getAnswerScan().setCourseCount(markPaperService.getCountByExam(exam.getId()));
-		vo.getAnswerScan().setTotalCount(getCount(exam.getId(), null));
-		vo.getAnswerScan().setScannedCount(getCount(exam.getId(), ScanStatus.SCANNED));
-		vo.getPackageScan().setScannedCount(scanPackageService.getCount(exam.getId()));
-		return vo;
-	}
-
-	@Override
-	public IPage<StudentScoreDetailDto> pageStudentScore(Long examId, String paperNumber, String college,
-			String className, String teacher, Integer filter, Boolean absent, Boolean breach, Double startScore,
-			Double endScore, Double subScore, Integer objectiveScoreRateLt, String studentName, String studentCode,
-			Integer pageNumber, Integer pageSize) {
-		if (startScore != null && endScore == null) {
-			throw ExceptionResultEnum.ERROR.exception("请输入结束分数值");
-		}
-		Page<StudentScoreDetailDto> page = new Page<>(pageNumber, pageSize);
-		MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
-		Double objectiveScoreLt = objectiveScoreRateLt == null ? null
-				: Double.parseDouble(new DecimalFormat("####.###")
-						.format(markPaper.getObjectiveScore() * objectiveScoreRateLt / 100));
-		IPage<StudentScoreDetailDto> studentScoreDetailDtoIPage = this.baseMapper.pageStudentScore(page, examId,
-				paperNumber, college, className, teacher, filter, absent, breach, startScore, endScore, subScore,
-				objectiveScoreLt, studentName, studentCode);
-		for (StudentScoreDetailDto scoreDetailDto : studentScoreDetailDtoIPage.getRecords()) {
-			// 原图
-			scoreDetailDto.setSheetUrls(buildSheetUrls(scoreDetailDto.getStudentId()));
-
-		}
-		return studentScoreDetailDtoIPage;
-	}
-
-	@Override
-	public List<SheetUrlDto> buildSheetUrls(Long studentId) {
-		// 原图
-		List<SheetUrlDto> sheetUrls = new ArrayList<>();
-		List<StudentPaperDetailDto> studentPaperDetailDtoList = scanPaperService.listStudentPaperDetail(studentId);
-		for (int i = 0; i < studentPaperDetailDtoList.size(); i++) {
-			StudentPaperDetailDto studentPaperDetailDto = studentPaperDetailDtoList.get(i);
-			sheetUrls.add(new SheetUrlDto(
-					2 * (studentPaperDetailDto.getPaperIndex() - 1) + studentPaperDetailDto.getPageIndex(),
-					teachcloudCommonService.filePreview(studentPaperDetailDto.getSheetPath())));
-		}
-		return sheetUrls;
-	}
-
-	private int getCount(Long examId, ScanStatus status) {
-		QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
-		lw.eq(MarkStudent::getExamId, examId);
-		if (status != null) {
-			lw.eq(MarkStudent::getScanStatus, status);
-		}
-		return baseMapper.selectCount(wrapper);
-	}
-
-	private int getOmrAbsentCount(Long examId, Boolean checked) {
-		QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
-		lw.eq(MarkStudent::getExamId, examId);
-		lw.eq(MarkStudent::getOmrAbsent, true);
-		if (checked != null) {
-			lw.eq(MarkStudent::getOmrAbsentChecked, checked);
-		}
-		return baseMapper.selectCount(wrapper);
-	}
-
-	private int getIncompleteCount(Long examId) {
-		QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
-		lw.eq(MarkStudent::getExamId, examId);
-		lw.eq(MarkStudent::getIncomplete, true);
-		return baseMapper.selectCount(wrapper);
-	}
-
-	@Override
-	public ScanExamCheckInfoVo checkInfo(BasicExam exam) {
-		Long examId = exam.getId();
-		ScanExamCheckInfoVo vo = new ScanExamCheckInfoVo();
-		vo.setId(exam.getId());
-		vo.setName(exam.getName());
-		CheckTask ct = vo.getCheckTask();
-		ct.setUnexistCount(getCount(examId, ScanStatus.UNEXIST));
-		ct.setUnexistCheckedCount(getCount(examId, ScanStatus.MANUAL_ABSENT));
-		ct.setAssignedCount(scanPaperService.getAssignedCount(examId, false));
-		ct.setAssignedCheckedCount(scanPaperService.getAssignedCount(examId, true));
-		ct.setAbsentCheckCount(getOmrAbsentCount(examId, false));
-		ct.setAbsentCheckedCount(getOmrAbsentCount(examId, true));
-		ct.setObjectiveCheckCount(scanOmrTaskService.getCount(examId, OmrTaskStatus.WAITING));
-		ct.setObjectiveCheckedCount(scanOmrTaskService.getCount(examId, OmrTaskStatus.PROCESSED));
-		ct.setIncompleteCount(getIncompleteCount(examId));
-		return vo;
-	}
-
-	/**
-	 * 根据考生当前绑定的paper刷新考生状态,需要在外部调用处对考生上锁
-	 */
-	@Override
-	@Transactional
-	public void updateStudentByPaper(@NotNull Long userId, @NotNull Long studentId, @NotNull boolean updateOmrTask) {
-		MarkStudent student = this.getById(studentId);
-		if (student == null) {
-			throw new ParameterException("找不到对应的考生");
-		}
-		// 重置状态
-		student.setIncomplete(false);
-		student.setAssigned(false);
-		student.setQuestionFilled(false);
-		student.setOmrAbsent(false);
-		int paperCount = 0;
-		List<ScanStudentPaper> studentPaperList = studentPaperService.findByStudentId(studentId);
-		for (ScanStudentPaper studentPaper : studentPaperList) {
-			paperCount++;
-			// 获取paper详情更新考生状态
-			ScanPaper paper = scanPaperService.getById(studentPaper.getPaperId());
-			student.setAssigned(student.getAssigned() || paper.getAssigned());
-			student.setQuestionFilled(student.getQuestionFilled() || paper.getQuestionFilled());
-			student.setCardNumber(paper.getCardNumber());
-			// 单独判断首张纸正面的识别结果
-			if (studentPaper.getPaperIndex() == 1) {
-				// 根据识别结果更新考生属性
-				ScanPaperPage page = scanPaperPageService.findPaperIdAndIndex(paper.getId(), 1);
-				student.setOmrAbsent(page.getAbsent() == null ? false : page.getAbsent().getResult());
+    @Autowired
+    private MarkPaperService markPaperService;
+    @Autowired
+    private ScanPackageService scanPackageService;
+    @Autowired
+    private ScanPaperService scanPaperService;
+    @Autowired
+    private ScanPaperPageService scanPaperPageService;
+    @Autowired
+    private ScanOmrTaskService scanOmrTaskService;
+    @Autowired
+    private ScanAnswerCardService answerCardService;
+    @Autowired
+    private ScanStudentPaperService studentPaperService;
+    @Resource
+    private MarkQuestionService markQuestionService;
+    @Resource
+    private TeachcloudCommonService teachcloudCommonService;
+    @Autowired
+    private ConcurrentService concurrentService;
+    @Resource
+    private MarkService markService;
+    @Resource
+    private LockService lockService;
+    @Resource
+    private TaskService taskService;
+    @Resource
+    private MarkUserGroupService markUserGroupService;
+
+    @Autowired
+    private MarkSubjectiveScoreService markSubjectiveScoreService;
+
+    @Override
+    public List<String> listClassByExamIdAndCourseCode(Long examId, String paperNumber) {
+        QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getPaperNumber, paperNumber);
+        List<MarkStudent> markStudentList = this.list(queryWrapper);
+
+        List<String> classNameList = new ArrayList<>();
+        if (CollectionUtils.isNotEmpty(markStudentList)) {
+            classNameList = markStudentList.stream().filter(m -> StringUtils.isNotBlank(m.getClassName()))
+                    .map(MarkStudent::getClassName).distinct().collect(Collectors.toList());
+        }
+        return classNameList;
+    }
+
+    @Override
+    public void updateSubjectiveStatusAndScore(Long studentId, SubjectiveStatus status, Double score,
+                                               String scoreList) {
+        UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkStudent::getSubjectiveStatus, status)
+                .set(MarkStudent::getSubjectiveScore, score)
+                .set(MarkStudent::getSubjectiveScoreList, scoreList).eq(MarkStudent::getId, studentId);
+        this.update(updateWrapper);
+    }
+
+    @Override
+    public void updateSubjectiveStatusAndScore(Long examId, String paperNumber, SubjectiveStatus status, double score,
+                                               String scoreList) {
+        UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkStudent::getSubjectiveStatus, status).set(MarkStudent::getSubjectiveScore, score)
+                .set(MarkStudent::getSubjectiveScoreList, scoreList).eq(MarkStudent::getExamId, examId)
+                .eq(MarkStudent::getPaperNumber, paperNumber);
+        this.update(updateWrapper);
+    }
+
+    @Override
+    public ScanExamInfoVo getScanExamInfo(BasicExam exam) {
+        ScanExamInfoVo vo = new ScanExamInfoVo();
+        vo.setId(exam.getId());
+        vo.setName(exam.getName());
+        vo.getAnswerScan().setCourseCount(markPaperService.getCountByExam(exam.getId()));
+        vo.getAnswerScan().setTotalCount(getCount(exam.getId(), null));
+        vo.getAnswerScan().setScannedCount(getCount(exam.getId(), ScanStatus.SCANNED));
+        vo.getPackageScan().setScannedCount(scanPackageService.getCount(exam.getId()));
+        return vo;
+    }
+
+    @Override
+    public IPage<StudentScoreDetailDto> pageStudentScore(Long examId, String paperNumber, String college,
+                                                         String className, String teacher, Integer filter, Boolean absent, Boolean breach, Double startScore,
+                                                         Double endScore, Double subScore, Integer objectiveScoreRateLt, String studentName, String studentCode,
+                                                         Integer pageNumber, Integer pageSize) {
+        if (startScore != null && endScore == null) {
+            throw ExceptionResultEnum.ERROR.exception("请输入结束分数值");
+        }
+        Page<StudentScoreDetailDto> page = new Page<>(pageNumber, pageSize);
+        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
+        Double objectiveScoreLt = objectiveScoreRateLt == null ? null
+                : Double.parseDouble(new DecimalFormat("####.###")
+                .format(markPaper.getObjectiveScore() * objectiveScoreRateLt / 100));
+        IPage<StudentScoreDetailDto> studentScoreDetailDtoIPage = this.baseMapper.pageStudentScore(page, examId,
+                paperNumber, college, className, teacher, filter, absent, breach, startScore, endScore, subScore,
+                objectiveScoreLt, studentName, studentCode);
+        for (StudentScoreDetailDto scoreDetailDto : studentScoreDetailDtoIPage.getRecords()) {
+            // 原图
+            scoreDetailDto.setSheetUrls(buildSheetUrls(scoreDetailDto.getStudentId()));
+
+        }
+        return studentScoreDetailDtoIPage;
+    }
+
+    @Override
+    public List<SheetUrlDto> buildSheetUrls(Long studentId) {
+        // 原图
+        List<SheetUrlDto> sheetUrls = new ArrayList<>();
+        List<StudentPaperDetailDto> studentPaperDetailDtoList = scanPaperService.listStudentPaperDetail(studentId);
+        for (int i = 0; i < studentPaperDetailDtoList.size(); i++) {
+            StudentPaperDetailDto studentPaperDetailDto = studentPaperDetailDtoList.get(i);
+            sheetUrls.add(new SheetUrlDto(
+                    2 * (studentPaperDetailDto.getPaperIndex() - 1) + studentPaperDetailDto.getPageIndex(),
+                    teachcloudCommonService.filePreview(studentPaperDetailDto.getSheetPath())));
+        }
+        return sheetUrls;
+    }
+
+    private int getCount(Long examId, ScanStatus status) {
+        QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
+        lw.eq(MarkStudent::getExamId, examId);
+        if (status != null) {
+            lw.eq(MarkStudent::getScanStatus, status);
+        }
+        return baseMapper.selectCount(wrapper);
+    }
+
+    private int getOmrAbsentCount(Long examId, Boolean checked) {
+        QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
+        lw.eq(MarkStudent::getExamId, examId);
+        lw.eq(MarkStudent::getOmrAbsent, true);
+        if (checked != null) {
+            lw.eq(MarkStudent::getOmrAbsentChecked, checked);
+        }
+        return baseMapper.selectCount(wrapper);
+    }
+
+    private int getIncompleteCount(Long examId) {
+        QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
+        lw.eq(MarkStudent::getExamId, examId);
+        lw.eq(MarkStudent::getIncomplete, true);
+        return baseMapper.selectCount(wrapper);
+    }
+
+    @Override
+    public ScanExamCheckInfoVo checkInfo(BasicExam exam) {
+        Long examId = exam.getId();
+        ScanExamCheckInfoVo vo = new ScanExamCheckInfoVo();
+        vo.setId(exam.getId());
+        vo.setName(exam.getName());
+        CheckTask ct = vo.getCheckTask();
+        ct.setUnexistCount(getCount(examId, ScanStatus.UNEXIST));
+        ct.setUnexistCheckedCount(getCount(examId, ScanStatus.MANUAL_ABSENT));
+        ct.setAssignedCount(scanPaperService.getAssignedCount(examId, false));
+        ct.setAssignedCheckedCount(scanPaperService.getAssignedCount(examId, true));
+        ct.setAbsentCheckCount(getOmrAbsentCount(examId, false));
+        ct.setAbsentCheckedCount(getOmrAbsentCount(examId, true));
+        ct.setObjectiveCheckCount(scanOmrTaskService.getCount(examId, OmrTaskStatus.WAITING));
+        ct.setObjectiveCheckedCount(scanOmrTaskService.getCount(examId, OmrTaskStatus.PROCESSED));
+        ct.setIncompleteCount(getIncompleteCount(examId));
+        return vo;
+    }
+
+    /**
+     * 根据考生当前绑定的paper刷新考生状态,需要在外部调用处对考生上锁
+     */
+    @Override
+    @Transactional
+    public void updateStudentByPaper(@NotNull Long userId, @NotNull Long studentId, @NotNull boolean updateOmrTask) {
+        MarkStudent student = this.getById(studentId);
+        if (student == null) {
+            throw new ParameterException("找不到对应的考生");
+        }
+        // 重置状态
+        student.setIncomplete(false);
+        student.setAssigned(false);
+        student.setQuestionFilled(false);
+        student.setOmrAbsent(false);
+        int paperCount = 0;
+        List<ScanStudentPaper> studentPaperList = studentPaperService.findByStudentId(studentId);
+        for (ScanStudentPaper studentPaper : studentPaperList) {
+            paperCount++;
+            // 获取paper详情更新考生状态
+            ScanPaper paper = scanPaperService.getById(studentPaper.getPaperId());
+            student.setAssigned(student.getAssigned() || paper.getAssigned());
+            student.setQuestionFilled(student.getQuestionFilled() || paper.getQuestionFilled());
+            student.setCardNumber(paper.getCardNumber());
+            // 单独判断首张纸正面的识别结果
+            if (studentPaper.getPaperIndex() == 1) {
+                // 根据识别结果更新考生属性
+                ScanPaperPage page = scanPaperPageService.findPaperIdAndIndex(paper.getId(), 1);
+                student.setOmrAbsent(page.getAbsent() == null ? false : page.getAbsent().getResult());
 //                student.setDevice(batchService.findByPaperId(paper.getId()).getDevice());
-			}
-		}
-		// 更新考生状态
-		if (paperCount > 0) {
-			ScanAnswerCard answerCard = answerCardService.findByExamAndNumber(student.getExamId(),
-					student.getCardNumber());
-			student.setIncomplete(paperCount != answerCard.getPaperCount());
-			student.setScanStatus(ScanStatus.SCANNED);
-			// 更新图片数量和图片地址
-			updateStudentSheetInfo(studentId, studentPaperList);
-		} else {
-			student.setScanStatus(ScanStatus.UNEXIST);
-		}
+            }
+        }
+        // 更新考生状态
+        if (paperCount > 0) {
+            ScanAnswerCard answerCard = answerCardService.findByExamAndNumber(student.getExamId(),
+                    student.getCardNumber());
+            student.setIncomplete(paperCount != answerCard.getPaperCount());
+            student.setScanStatus(ScanStatus.SCANNED);
+            // 更新图片数量和图片地址
+            updateStudentSheetInfo(studentId, studentPaperList);
+        } else {
+            student.setScanStatus(ScanStatus.UNEXIST);
+        }
 //        student.setUpdaterId(userId);
 //        student.setUpdateTime(System.currentTimeMillis());
-		this.saveOrUpdate(student);
-		if (updateOmrTask) {
-			// 清除识别对照任务
-			scanOmrTaskService.deleteByStudentId(student.getExamId(), student.getId());
-			// 重新生成识别对照任务
-			scanOmrTaskService.buildTask(ConditionType.FILL_SUSPECT, student.getId());
-		}
-	}
-
-	private void updateStudentSheetInfo(Long studentId, List<ScanStudentPaper> studentPaperList) {
-		List<FilePathVo> filePathVoList = new ArrayList<>();
-		for (ScanStudentPaper scanStudentPaper : studentPaperList) {
-			List<ScanPaperPage> scanPaperPages = scanPaperPageService.listByPaperId(scanStudentPaper.getPaperId());
-			for (ScanPaperPage scanPaperPage : scanPaperPages) {
-				String sheetPath = scanPaperPage.getSheetPath();
-				if (StringUtils.isNotBlank(sheetPath)) {
-					filePathVoList.add(JSON.parseObject(sheetPath, FilePathVo.class));
-				}
-			}
-		}
-		UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
-		updateWrapper.lambda().set(MarkStudent::getSheetCount, filePathVoList.size())
-				.set(MarkStudent::getSheetPath, JSON.toJSONString(filePathVoList)).eq(MarkStudent::getId, studentId);
-		this.update(updateWrapper);
-	}
-
-	@Override
-	public MarkStudent findByExamIdAndCoursePaperIdAndStudentCode(Long examId, String coursePaperId,
-			String studentCode) {
-		QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
-		lw.eq(MarkStudent::getExamId, examId);
-		lw.eq(MarkStudent::getCoursePaperId, coursePaperId);
-		lw.eq(MarkStudent::getStudentCode, studentCode);
-		return baseMapper.selectOne(wrapper);
-	}
-
-	@Override
-	public StudentObjectiveDetailDto getObjectiveInspectedTask(Long studentId) {
-		MarkStudent markStudent = this.getById(studentId);
-		StudentObjectiveDetailDto studentObjectiveDetailDto = new StudentObjectiveDetailDto();
-		if (markStudent != null) {
-			studentObjectiveDetailDto.setStudentId(markStudent.getId());
-			studentObjectiveDetailDto.setStudentName(markStudent.getStudentName());
-			studentObjectiveDetailDto.setStudentCode(markStudent.getStudentCode());
-			studentObjectiveDetailDto.setExamPlace(markStudent.getExamPlace());
-			studentObjectiveDetailDto.setExamRoom(markStudent.getExamRoom());
-			studentObjectiveDetailDto.setExamId(markStudent.getExamId());
-			studentObjectiveDetailDto.setCourseCode(markStudent.getCourseCode());
-			studentObjectiveDetailDto.setCourseName(markStudent.getCourseName());
-			studentObjectiveDetailDto.setPaperNumber(markStudent.getPaperNumber());
-			studentObjectiveDetailDto
-					.setObjectiveScore(markStudent.getObjectiveScore() != null ? markStudent.getObjectiveScore() : 0);
-			studentObjectiveDetailDto.setSubjectiveScore(
-					markStudent.getSubjectiveScore() != null ? markStudent.getSubjectiveScore() : 0);
-			studentObjectiveDetailDto.setUpload(markStudent.getUpload());
-			studentObjectiveDetailDto.setAbsent(markStudent.getAbsent());
-			studentObjectiveDetailDto.setSheetUrls(this.buildSheetUrls(studentId));
-
-			List<MarkQuestion> questions = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(
-					markStudent.getExamId(), markStudent.getPaperNumber(), null, true);
-			List<String> answers = markStudent.getAnswerList();
-			int questionCount = questions.size();
-			int answerCount = answers.size();
-
-			List<StudentObjectiveAnswerDto> answerDtoList = new ArrayList<>();
-			Map<Integer, String> titles = new HashMap<>();
-			// 已设置客观题
-			int maxCount = Math.max(questionCount, answerCount);
-			for (int i = 0; i < maxCount; i++) {
-				MarkQuestion q = questionCount > i ? questions.get(i) : null;
-				String answer = answerCount > i ? answers.get(i) : "#";
-				StudentObjectiveAnswerDto studentObjectiveAnswerDto = new StudentObjectiveAnswerDto();
-				studentObjectiveAnswerDto.setMainNumber(q != null ? q.getMainNumber() : 0);
-				studentObjectiveAnswerDto.setSubNumber(q != null ? q.getSubNumber() : 0);
-				studentObjectiveAnswerDto.setAnswer(answer);
-				studentObjectiveAnswerDto.setExist(q != null && q.getTotalScore() > 0);
-				studentObjectiveAnswerDto.setQuestionType(q.getQuestionType());
-				answerDtoList.add(studentObjectiveAnswerDto);
-
-				if (q != null) {
-					titles.put(q.getMainNumber(), q.getMainTitle());
-				}
-			}
-			studentObjectiveDetailDto.setAnswers(answerDtoList);
-			studentObjectiveDetailDto.setTitles(titles);
-		}
-
-		return studentObjectiveDetailDto;
-	}
-
-	@Override
-	public Boolean saveObjectiveInspectedTask(Long studentId, String answers) {
-		MarkStudent student = this.getById(studentId);
-		answers = StringUtils.trimToEmpty(answers);
-		if (student != null) {
-			student.setAnswers(answers.toUpperCase());
-			this.updateById(student);
-			return saveUploadStudent(student);
-		} else {
-			return false;
-		}
-	}
-
-	@Override
-	public int countUploadedByExamIdAndPaperNumber(Long examId, String paperNumber) {
-		QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
-		queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getPaperNumber, paperNumber)
-				.eq(MarkStudent::getUpload, true).eq(MarkStudent::getAbsent, false).eq(MarkStudent::getBreach, false);
-		return this.count(queryWrapper);
-	}
-
-	@Override
-	public boolean updateScanInfo(MarkStudent student) {
-		UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
-		updateWrapper.lambda().set(MarkStudent::getSheetCount, student.getSheetCount())
-				.set(MarkStudent::getAnswers, student.getAnswers())
-				.set(MarkStudent::getBatchCode, student.getBatchCode()).set(MarkStudent::getAbsent, student.getAbsent())
-				.set(MarkStudent::getUpload, true).set(MarkStudent::getUploadTime, System.currentTimeMillis())
-				.set(MarkStudent::getObjectiveScore, student.getObjectiveScore())
-				.set(MarkStudent::getObjectiveScoreList, student.getObjectiveScoreList())
-				.set(MarkStudent::getCardNumber, student.getCardNumber()).eq(MarkStudent::getId, student.getId());
-		return this.update(updateWrapper);
-
-	}
-
-	@Override
-	public List<MarkStudent> listAbsentOrBreachMarkTaskStudent(Long examId, String paperNumber) {
-		return this.baseMapper.listAbsentOrBreachMarkTaskStudent(examId, paperNumber);
-	}
-
-	@Override
-	public List<MarkStudent> listUnMarkTaskStudent(Long examId, String paperNumber, Integer groupNumber, int pageSize) {
-		Page<MarkStudent> page = new Page<>(1, pageSize);
-		IPage<MarkStudent> markStudentIPage = this.baseMapper.listUnMarkTaskStudent(page, examId, paperNumber,
-				groupNumber);
-		return markStudentIPage.getRecords();
-	}
-
-	/**
-	 * 客观题统分 统分场景: 1.后台-成绩检查-客观题检查 2.扫描端-考生图片上传 3.扫描端-客观题二次识别
-	 *
-	 * @param student
-	 * @return
-	 */
-	@Override
-	public boolean saveUploadStudent(MarkStudent student) {
-		MarkStudent old = this.getById(student.getId());
-		if (!student.getAbsent()) {// 正考
-			MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(student.getExamId(),
-					student.getPaperNumber());
-			if (markPaper.getStatus().equals(MarkPaperStatus.FINISH)) {
-				markPaperService.updateStatus(markPaper.getExamId(), markPaper.getPaperNumber(), MarkPaperStatus.FORMAL,
-						MarkPaperStatus.FINISH);
-			}
-		}
-		calculateObjectiveScore(student);
-		if (student.getAbsent()) {// 转缺考
-			student.setObjectiveScore(null);
-			student.setObjectiveScoreList(null);
-		}
-		if (!old.getAbsent() && student.getAbsent()) {// 正考转缺考
-			student.setSubjectiveScore(null);
-			student.setSubjectiveScoreList(null);
-			student.setSubjectiveStatus(SubjectiveStatus.UNMARK);
-			this.updateById(student);
-		}
-		boolean success = this.updateScanInfo(student);
-		if (success) {
-			markPaperService.updateUploadCount(student.getExamId(), student.getPaperNumber(),
-					this.countUploadedByExamIdAndPaperNumber(student.getExamId(), student.getPaperNumber()));
-		}
-		return success;
-	}
-
-	private void calculateObjectiveScore(MarkStudent student) {
-		ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
-
-		ScoreInfo info = util.calculate(markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(
-				student.getExamId(), student.getPaperNumber(), null, true), null);
-		student.setObjectiveScore(info.getObjectiveScore());
-		student.setScoreList(info.getScoreList(), true);
-	}
-
-	@Override
-	@Transactional
-	public void updateStudentAndPaper(@NotNull SysUser user, @NotNull Long id,
-			@NotNull List<ScanStudentPaper> studentPaperList) {
-		for (ScanStudentPaper studentPaper : studentPaperList) {
-			studentPaper.setStudentId(id);
-		}
-		// 清空原有绑定关系
-		studentPaperService.removeByStudentId(id);
-		// 保存绑定关系
-		studentPaperService.saveOrUpdateBatch(studentPaperList);
-		// 更新考生状态
-		updateStudentByPaper(user.getId(), id, true);
-	}
-
-	@Override
-	public StudentVo findOne(StudentQuery query) {
-		return baseMapper.findOne(query);
-	}
-
-	@Override
-	public int countByExamIdAndSecretNumber(Long examId, String secretNumber) {
-		QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
-		queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getSecretNumber, secretNumber);
-		return this.count(queryWrapper);
-	}
-
-	@Override
-	public List<MarkStudent> listByExamIdAndCoursePaperId(Long examId, String coursePaperId) {
-		QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
-		queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getCoursePaperId, coursePaperId);
-		return this.list(queryWrapper);
-	}
-
-	@Override
-	public IPage<AnswerQueryVo> query(AnswerQueryDomain query) {
-		// 查询考生分页信息
-		IPage<AnswerQueryVo> iPage = baseMapper.queryPage(new Page<>(query.getPageNumber(), query.getPageSize()),
-				query);
-		if (CollectionUtils.isNotEmpty(iPage.getRecords())) {
-			for (AnswerQueryVo vo : iPage.getRecords()) {
-				if (vo.getIsAbsent() != null && vo.getIsAbsent()) {
-					vo.setExamStatus(ExamStatus.ABSENT);
-				} else {
-					vo.setExamStatus(ExamStatus.OK);
-				}
-			}
-		}
-		if (CollectionUtils.isNotEmpty(iPage.getRecords()) && (query.getWithPaper() != null && query.getWithPaper())) {
-			Map<Long, AnswerQueryVo> map = new HashMap<>();
-
-			for (AnswerQueryVo vo : iPage.getRecords()) {
-				List<AnswerPaperVo> papers = new ArrayList<>();
-				vo.setPapers(papers);
-				if (vo.getCardPaperCount() != null) {
-					for (int i = 1; i <= vo.getCardPaperCount(); i++) {
-						AnswerPaperVo pv = new AnswerPaperVo();
-						pv.setNumber(i);
-						papers.add(pv);
-					}
-				}
-				map.put(vo.getId(), vo);
-			}
-			// 根据考生id查找绑定paper
-			List<Long> studentIds = iPage.getRecords().stream().map(p -> p.getId()).collect(Collectors.toList());
-			List<StudentPaperVo> paperList = new BatchGetDataUtil<StudentPaperVo, Long>() {
-
-				@Override
-				public List<StudentPaperVo> getData(List<Long> paramList) {
-					return scanPaperService.listByStudentIds(paramList);
-				}
-			}.getDataForBatch(studentIds, 200);
-
-			if (CollectionUtils.isNotEmpty(paperList)) {
-				Map<Long, AnswerPaperVo> paperMap = new HashMap<>();
-				for (StudentPaperVo p : paperList) {
-					AnswerQueryVo vo = map.get(p.getStudentId());
-					if (vo == null) {
-						continue;
-					}
-					List<AnswerPaperVo> papers = vo.getPapers();
-					if (papers == null) {
-						continue;
-					}
-					if (papers.size() < p.getNumber()) {
-						continue;
-					}
-					AnswerPaperVo pvo = papers.get(p.getNumber() - 1);
-					pvo.setId(p.getPaperId());
-					pvo.setNumber(p.getNumber());
-					pvo.setAssigned(p.getAssigned());
-					paperMap.put(p.getPaperId(), pvo);
-				}
-				// 查找page
-				List<Long> paperIds = paperList.stream().map(p -> p.getPaperId()).collect(Collectors.toList());
-				List<ScanPaperPage> paperPageList = new BatchGetDataUtil<ScanPaperPage, Long>() {
-
-					@Override
-					public List<ScanPaperPage> getData(List<Long> paramList) {
-						return scanPaperPageService.listByPaperList(paramList);
-					}
-				}.getDataForBatch(paperIds, 200);
-
-				if (CollectionUtils.isNotEmpty(paperPageList)) {
-					for (ScanPaperPage p : paperPageList) {
-						AnswerPaperVo pvo = paperMap.get(p.getPaperId());
-						if (pvo == null) {
-							continue;
-						}
-						List<AnswerPageVo> pages = pvo.getPages();
-						if (pages == null) {
-							pages = new ArrayList<>();
-							pvo.setPages(pages);
-						}
-						AnswerPageVo pageVo = new AnswerPageVo();
-						pageVo.setIndex(p.getPageIndex());
-						pageVo.setSheetUri(teachcloudCommonService.filePreview(p.getSheetPath()));
-						if (query.getWithOmrDetail() != null && query.getWithOmrDetail()) {
-							pageVo.setAbsent(p.getAbsent().getResult());
-							pageVo.setBreach(p.getBreach().getResult());
-							pageVo.setQuestion(p.getQuestion().getResult());
-							pageVo.setRecogData(p.getRecogData());
-						}
-						pages.add(pageVo);
-					}
-				}
-			}
-		}
-		return iPage;
-	}
-
-	@Override
-	public List<String> summary(AnswerQueryDomain query) {
-		// 不分页查询考生准考证号
-		return baseMapper.querySummary(query);
-	}
-
-	@Transactional
-	@Override
-	public UpdateTimeVo omrEdit(Long userId, OmrEditDomain domain) {
-		MarkStudent student = findByExamIdAndStudentCode(domain.getExamId(), domain.getStudentCode());
-		if (student == null) {
-			throw new ParameterException("考生信息未找到");
-		}
-		concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().lock();
-		try {
-			for (OmrEditPaper paperEdit : domain.getPapers()) {
-				ScanStudentPaper sp = studentPaperService.findByStudentIdAndPaperNumber(student.getId(),
-						paperEdit.getNumber());
-				if (sp == null) {
-					throw new ParameterException("未找到绑定扫描结果");
-				}
-				ScanPaper paperEntity = scanPaperService.getById(sp.getPaperId());
-				if (paperEntity == null) {
-					throw new ParameterException("未找到paper信息结果");
-				}
-				paperEntity.setUpdaterId(userId);
-				paperEntity.setUpdateTime(System.currentTimeMillis());
-				List<ScanPaperPage> pages = scanPaperPageService.listByPaperId(paperEntity.getId());
-				for (ScanPaperPage pageEntity : pages) {
-					paperEdit.updatePage(pageEntity);
-				}
-				scanPaperService.savePaperAndPages(paperEntity, pages);
-			}
-			updateStudentByPaper(userId, student.getId(), false);
-			return UpdateTimeVo.create();
-		} finally {
-			concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().unlock();
-		}
-	}
-
-	@Override
-	public MarkStudent findByExamIdAndStudentCode(Long examId, String studentCode) {
-		if (examId == null) {
-			throw new ParameterException("examId 不能为空");
-		}
-		if (StringUtils.isBlank(studentCode)) {
-			throw new ParameterException("studentCode不能为空");
-		}
-		QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
-		queryWrapper.lambda().eq(MarkStudent::getExamId, examId);
-		queryWrapper.lambda().eq(MarkStudent::getStudentCode, studentCode);
-		return baseMapper.selectOne(queryWrapper);
-	}
-
-	@Override
-	public long countStudentCountByExamIdAndPaperNumber(Long examId, String paperNumber, String paperType) {
-		QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
-		queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getPaperNumber, paperNumber)
-				.eq(MarkStudent::getPaperType, paperType);
-		return this.count(queryWrapper);
-	}
-
-	@Transactional
-	@Override
-	public AbsentManualUpdateVo absentManualUpdate(Long examId, String coursePaperId, String studentCode) {
-		MarkStudent student = findByExamIdAndCoursePaperIdAndStudentCode(examId, coursePaperId, studentCode);
-		if (student == null) {
-			throw new ParameterException("考生未找到");
-		}
-		if (student.getScanStatus() != ScanStatus.UNEXIST) {
-			throw new ParameterException("考生不是未扫描状态");
-		}
-		LambdaUpdateWrapper<MarkStudent> lw = new LambdaUpdateWrapper<>();
-		lw.set(MarkStudent::getScanStatus, ScanStatus.MANUAL_ABSENT);
-		lw.eq(MarkStudent::getId, student.getId());
-		lw.eq(MarkStudent::getScanStatus, ScanStatus.UNEXIST);
-		update(lw);
-		return AbsentManualUpdateVo.create(ScanStatus.MANUAL_ABSENT);
-	}
-
-	@Transactional
-	@Override
-	public UpdateTimeVo confirm(Long examId, String coursePaperId, String studentCode, Boolean omrAbsent) {
-		LambdaUpdateWrapper<MarkStudent> lw = new LambdaUpdateWrapper<>();
-		lw.set(MarkStudent::getOmrAbsentChecked, true);
-		lw.eq(MarkStudent::getExamId, examId);
-		lw.eq(MarkStudent::getCoursePaperId, coursePaperId);
-		lw.eq(MarkStudent::getStudentCode, studentCode);
-		if (omrAbsent != null) {
-			lw.eq(MarkStudent::getOmrAbsent, omrAbsent);
-		}
-		if (!update(lw)) {
-			throw new ParameterException("考生未找到");
-		}
-		return UpdateTimeVo.create();
-	}
-
-	@Override
-	public List<Long> findIdByExamIdAndPaperNumberAndSubjectiveStatus(Long examId, String paperNumber,
-			SubjectiveStatus unmark, SubjectiveStatus marked) {
-		return this.baseMapper.findIdByExamIdAndPaperNumberAndSubjectiveStatus(examId, paperNumber, unmark.name(),
-				marked.name());
-	}
-
-	@Override
-	public Task getSubjectiveInspectedTask(Long studentId) {
-		Task task = null;
-		if (studentId != null) {
-			SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
-			MarkStudent markStudent = this.getById(studentId);
-			releaseStudent(markStudent);
-			if (markService.applyStudent(markStudent, sysUser.getId())) {
-				task = taskService.build(studentId);
-			}
-		}
-		return task;
-	}
-
-	@Override
-	public void saveSubjectiveInspectedTask(MarkHeaderResult markResult) {
-		SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
-		for (MarkHeaderGroupResult groupResult : markResult.getGroups()) {
-			MarkUserGroup markUserGroup = markUserGroupService.getByExamIdAndPaperNumberAndGroupNumberAndUserId(
-					markResult.getExamId(), markResult.getPaperNumber(), groupResult.getGroupNumber(), sysUser.getId());
-			try {
-				lockService.watch(LockType.EXAM_SUBJECT, markUserGroup.getExamId(), markUserGroup.getPaperNumber());
-				lockService.watch(LockType.GROUP, markUserGroup.getExamId(), markUserGroup.getPaperNumber(),
-						markUserGroup.getGroupNumber());
-				lockService.watch(LockType.MARK_USER_GROUP, markUserGroup.getId());
-				lockService.waitlock(LockType.STUDENT, groupResult.getStudentId());
-				markService.submitHeaderTask(groupResult, markUserGroup);
-			} catch (Exception e) {
-				log.error("save task error", e);
-			} finally {
-				lockService.unlock(LockType.STUDENT, groupResult.getStudentId());
-				lockService.unwatch(LockType.MARK_USER_GROUP, markUserGroup.getId());
-				lockService.unwatch(LockType.GROUP, markUserGroup.getExamId(), markUserGroup.getPaperNumber(),
-						markUserGroup.getGroupNumber());
-				lockService.unwatch(LockType.EXAM_SUBJECT, markUserGroup.getExamId(), markUserGroup.getPaperNumber());
-			}
-		}
-	}
-
-	private void releaseStudent(MarkStudent markStudent) {
-		try {
-			lockService.waitlock(LockType.STUDENT, markStudent.getId());
-			markService.releaseByStudent(markStudent);
-		} catch (Exception e) {
-			log.error("release user error", e);
-		} finally {
-			lockService.unlock(LockType.STUDENT, markStudent.getId());
-		}
-	}
-
-	@Override
-	public List<ArchiveStudentVo> studentList(ArchiveStudentQuery query) {
-		List<ArchiveStudentVo> ret = baseMapper.studentList(query);
-		if (CollectionUtils.isNotEmpty(ret)) {
-			fillSheetUrls(ret);
-		}
-		return ret;
-	}
-
-	private void fillSheetUrls(List<ArchiveStudentVo> ret) {
-		List<Long> studentIds = ret.stream().map(p -> p.getId()).collect(Collectors.toList());
-		// 查找page
-		List<ScanPaperPageVo> paperPageList = new BatchGetDataUtil<ScanPaperPageVo, Long>() {
-
-			@Override
-			public List<ScanPaperPageVo> getData(List<Long> studentIds) {
-				return scanPaperPageService.listByStudentIds(studentIds);
-			}
-		}.getDataForBatch(studentIds, 200);
-
-		Map<Long, List<String>> map = new HashMap<>();
-		if (CollectionUtils.isNotEmpty(paperPageList)) {
-			for (ScanPaperPageVo p : paperPageList) {
-				List<String> tem = map.get(p.getStudentId());
-				if (tem == null) {
-					tem = new ArrayList<>();
-					map.put(p.getStudentId(), tem);
-				}
-				tem.add(p.getSheetPath());
-			}
-		}
-		for (ArchiveStudentVo vo : ret) {
-			vo.setSheetUrls(map.get(vo.getId()));
-		}
-	}
-
-	@Override
-	public void scoreExport(ArchiveStudentQuery query, HttpServletResponse response) {
-		List<ArchiveStudentVo> ret = baseMapper.studentList(query);
-		try {
-			ExcelUtil.excelExport("成绩导出", ArchiveStudentVo.class, ret, response);
-		} catch (Exception e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	@Override
-	public ScoreReportVo scoreReport(Long examId, String paperNumber) {
-		ScoreReportVo ret = new ScoreReportVo();
-		ret.setOverview(baseMapper.overview(examId, paperNumber));
-		if (ret.getOverview() != null) {
-			double total = ret.getOverview().getStudentCount() - ret.getOverview().getAbsentCount();
-			ret.getOverview().setPassRate(
-					Calculator.divide2String(Calculator.multiply(ret.getOverview().getPassCount(), 100), total, 2)
-							+ "%");
-			ret.getOverview().setExcellentRate(
-					Calculator.divide2String(Calculator.multiply(ret.getOverview().getExcellentCount(), 100), total, 2)
-							+ "%");
-		}
-
-		fillScoreRange(ret, examId, paperNumber);
-
-		ret.setCollege(baseMapper.college(examId, paperNumber));
-		if (CollectionUtils.isNotEmpty(ret.getCollege())) {
-			for (CollegeVo vo : ret.getCollege()) {
-				double total = vo.getStudentCount() - vo.getAbsentCount();
-				vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100), total, 2) + "%");
-				vo.setExcellentRate(
-						Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100), total, 2) + "%");
-			}
-		}
-
-		ret.setClassData(baseMapper.classData(examId, paperNumber));
-		if (CollectionUtils.isNotEmpty(ret.getClassData())) {
-			for (ClassVo vo : ret.getClassData()) {
-				double total = vo.getStudentCount() - vo.getAbsentCount();
-				vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100), total, 2) + "%");
-				vo.setExcellentRate(
-						Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100), total, 2) + "%");
-			}
-		}
-
-		ret.setTeacher(baseMapper.teacher(examId, paperNumber));
-		if (CollectionUtils.isNotEmpty(ret.getTeacher())) {
-			for (TeacherVo vo : ret.getTeacher()) {
-				double total = vo.getStudentCount() - vo.getAbsentCount();
-				vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100), total, 2) + "%");
-				vo.setExcellentRate(
-						Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100), total, 2) + "%");
-			}
-		}
-
-		fillObjective(ret, examId, paperNumber);
-
-		ret.setSubjective(markSubjectiveScoreService.getSubjectiveVo(examId, paperNumber));
-		if (CollectionUtils.isNotEmpty(ret.getSubjective())) {
-			for (QuestionVo vo : ret.getSubjective()) {
-				double total = vo.getStudentCount();
-				vo.setScoreRate(Calculator.divide(vo.getScoreCount(), total, 2));
-				vo.setFullScoreRate(Calculator.divide(vo.getFullScoreCount(), total, 2));
-			}
-		}
-		return ret;
-	}
-
-	@Override
-	public void exportUnexist(Long examId, HttpServletResponse response) {
-		try {
-			List<UnexistStudentDto> unexistStudentDtoList = this.baseMapper.listUnexistStudentByExamIdAndCoursePaperId(examId);
-			ExcelUtil.excelExport("评卷员工作量", UnexistStudentDto.class, unexistStudentDtoList, response);
-		} catch (Exception e) {
-			throw ExceptionResultEnum.ERROR.exception("导出评卷员工作量失败");
-		}
-	}
-
-	private void fillObjective(ScoreReportVo ret, Long examId, String paperNumber) {
-		List<MarkQuestion> qs = markQuestionService.listQuestionByExamIdAndPaperNumber(examId, paperNumber);
-		List<MarkStudent> students = listByExamIdAndPaperNumberAndAbsent(examId, paperNumber, false);
-		Map<String, QuestionVo> map = new HashMap<>();
-		for (MarkStudent s : students) {
-			List<ScoreItem> sis = s.getScoreList(true, qs);
-			if (CollectionUtils.isNotEmpty(sis)) {
-				for (ScoreItem si : sis) {
-					String key = si.getMainNumber() + "-" + si.getSubNumber();
-					QuestionVo vo = map.get(key);
-					if (vo == null) {
-						vo = new QuestionVo();
-						vo.setScoreCount(0);
-						vo.setFullScoreCount(0);
-						vo.setStudentCount(0);
-						vo.setScoreSum(0.0);
-						vo.setTitle(si.getTitle());
-						vo.setScore(si.getTotalScore());
-						map.put(key, vo);
-					}
-					vo.setStudentCount(vo.getStudentCount() + 1);
-					vo.setScoreSum(vo.getScoreSum() + si.getScore());
-					if (si.getScore() == si.getTotalScore()) {
-						vo.setFullScoreCount(vo.getFullScoreCount() + 1);
-					}
-					if (si.getScore() > 0) {
-						vo.setScoreCount(vo.getScoreCount() + 1);
-					}
-				}
-			}
-		}
-		if (map.isEmpty()) {
-			return;
-		}
-		List<QuestionVo> list = new ArrayList<>(map.values());
-		Collections.sort(list, new Comparator<QuestionVo>() {
-
-			@Override
-			public int compare(QuestionVo o1, QuestionVo o2) {
-				if (o1.getMainNumber() > o2.getMainNumber()) {
-					return 1;
-				} else if (o1.getSubNumber() < o2.getSubNumber()) {
-					return -1;
-				} else {
-					return 0;
-				}
-			}
-
-		});
-		for (QuestionVo vo : list) {
-			double total = vo.getStudentCount();
-			vo.setScoreRate(Calculator.divide(vo.getScoreCount(), total, 2));
-			vo.setFullScoreRate(Calculator.divide(vo.getFullScoreCount(), total, 2));
-		}
-		ret.setObjective(list);
-	}
-
-	private List<MarkStudent> listByExamIdAndPaperNumberAndAbsent(Long examId, String paperNumber, Boolean absent) {
-		QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
-		lw.eq(MarkStudent::getExamId, examId);
-		lw.eq(MarkStudent::getPaperNumber, paperNumber);
-		if (absent != null) {
-			lw.eq(MarkStudent::getAbsent, absent);
-		}
-		return this.list(wrapper);
-	}
-
-	private void fillScoreRange(ScoreReportVo ret, Long examId, String paperNumber) {
-		int toltal = getCountByPaperNumber(examId, paperNumber);
-		List<ScoreRangeVo> scoreRange = new ArrayList<>();
-		ret.setScoreRange(scoreRange);
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 1.0, 9.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 10.0, 19.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 20.0, 29.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 30.0, 39.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 40.0, 49.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 50.0, 59.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 60.0, 69.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 70.0, 79.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 80.0, 89.5));
-		scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 90.0, 100.0));
-	}
-
-	private ScoreRangeVo getScoreRangeVo(int toltal, Long examId, String paperNumber, Double start, Double end) {
-		int count = getCount(examId, paperNumber, start, end);
-		Double rate = null;
-		if (toltal != 0) {
-			rate = Calculator.multiply(count, toltal, 2);
-		}
-		ScoreRangeVo vo = new ScoreRangeVo(count, start, end, rate);
-		return vo;
-	}
-
-	private int getCountByPaperNumber(Long examId, String paperNumber) {
-		QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
-		lw.eq(MarkStudent::getExamId, examId);
-		lw.eq(MarkStudent::getPaperNumber, paperNumber);
-		return this.count(wrapper);
-	}
-
-	private int getCount(Long examId, String paperNumber, Double start, Double end) {
-		return baseMapper.getCountByScoreRange(examId, paperNumber, start, end);
-	}
+        this.saveOrUpdate(student);
+        if (updateOmrTask) {
+            // 清除识别对照任务
+            scanOmrTaskService.deleteByStudentId(student.getExamId(), student.getId());
+            // 重新生成识别对照任务
+            scanOmrTaskService.buildTask(ConditionType.FILL_SUSPECT, student.getId());
+        }
+    }
+
+    private void updateStudentSheetInfo(Long studentId, List<ScanStudentPaper> studentPaperList) {
+        List<FilePathVo> filePathVoList = new ArrayList<>();
+        for (ScanStudentPaper scanStudentPaper : studentPaperList) {
+            List<ScanPaperPage> scanPaperPages = scanPaperPageService.listByPaperId(scanStudentPaper.getPaperId());
+            for (ScanPaperPage scanPaperPage : scanPaperPages) {
+                String sheetPath = scanPaperPage.getSheetPath();
+                if (StringUtils.isNotBlank(sheetPath)) {
+                    filePathVoList.add(JSON.parseObject(sheetPath, FilePathVo.class));
+                }
+            }
+        }
+        UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkStudent::getSheetCount, filePathVoList.size())
+                .set(MarkStudent::getSheetPath, JSON.toJSONString(filePathVoList)).eq(MarkStudent::getId, studentId);
+        this.update(updateWrapper);
+    }
+
+    @Override
+    public MarkStudent findByExamIdAndCoursePaperIdAndStudentCode(Long examId, String coursePaperId,
+                                                                  String studentCode) {
+        QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
+        lw.eq(MarkStudent::getExamId, examId);
+        lw.eq(MarkStudent::getCoursePaperId, coursePaperId);
+        lw.eq(MarkStudent::getStudentCode, studentCode);
+        return baseMapper.selectOne(wrapper);
+    }
+
+    @Override
+    public StudentObjectiveDetailDto getObjectiveInspectedTask(Long studentId) {
+        MarkStudent markStudent = this.getById(studentId);
+        StudentObjectiveDetailDto studentObjectiveDetailDto = new StudentObjectiveDetailDto();
+        if (markStudent != null) {
+            studentObjectiveDetailDto.setStudentId(markStudent.getId());
+            studentObjectiveDetailDto.setStudentName(markStudent.getStudentName());
+            studentObjectiveDetailDto.setStudentCode(markStudent.getStudentCode());
+            studentObjectiveDetailDto.setExamPlace(markStudent.getExamPlace());
+            studentObjectiveDetailDto.setExamRoom(markStudent.getExamRoom());
+            studentObjectiveDetailDto.setExamId(markStudent.getExamId());
+            studentObjectiveDetailDto.setCourseCode(markStudent.getCourseCode());
+            studentObjectiveDetailDto.setCourseName(markStudent.getCourseName());
+            studentObjectiveDetailDto.setPaperNumber(markStudent.getPaperNumber());
+            studentObjectiveDetailDto
+                    .setObjectiveScore(markStudent.getObjectiveScore() != null ? markStudent.getObjectiveScore() : 0);
+            studentObjectiveDetailDto.setSubjectiveScore(
+                    markStudent.getSubjectiveScore() != null ? markStudent.getSubjectiveScore() : 0);
+            studentObjectiveDetailDto.setUpload(markStudent.getUpload());
+            studentObjectiveDetailDto.setAbsent(markStudent.getAbsent());
+            studentObjectiveDetailDto.setSheetUrls(this.buildSheetUrls(studentId));
+
+            List<MarkQuestion> questions = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(
+                    markStudent.getExamId(), markStudent.getPaperNumber(), null, true);
+            List<String> answers = markStudent.getAnswerList();
+            int questionCount = questions.size();
+            int answerCount = answers.size();
+
+            List<StudentObjectiveAnswerDto> answerDtoList = new ArrayList<>();
+            Map<Integer, String> titles = new HashMap<>();
+            // 已设置客观题
+            int maxCount = Math.max(questionCount, answerCount);
+            for (int i = 0; i < maxCount; i++) {
+                MarkQuestion q = questionCount > i ? questions.get(i) : null;
+                String answer = answerCount > i ? answers.get(i) : "#";
+                StudentObjectiveAnswerDto studentObjectiveAnswerDto = new StudentObjectiveAnswerDto();
+                studentObjectiveAnswerDto.setMainNumber(q != null ? q.getMainNumber() : 0);
+                studentObjectiveAnswerDto.setSubNumber(q != null ? q.getSubNumber() : 0);
+                studentObjectiveAnswerDto.setAnswer(answer);
+                studentObjectiveAnswerDto.setExist(q != null && q.getTotalScore() > 0);
+                studentObjectiveAnswerDto.setQuestionType(q.getQuestionType());
+                answerDtoList.add(studentObjectiveAnswerDto);
+
+                if (q != null) {
+                    titles.put(q.getMainNumber(), q.getMainTitle());
+                }
+            }
+            studentObjectiveDetailDto.setAnswers(answerDtoList);
+            studentObjectiveDetailDto.setTitles(titles);
+        }
+
+        return studentObjectiveDetailDto;
+    }
+
+    @Override
+    public Boolean saveObjectiveInspectedTask(Long studentId, String answers) {
+        MarkStudent student = this.getById(studentId);
+        answers = StringUtils.trimToEmpty(answers);
+        if (student != null) {
+            student.setAnswers(answers.toUpperCase());
+            return saveUploadStudent(student);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int countUploadedByExamIdAndPaperNumber(Long examId, String paperNumber) {
+        QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getPaperNumber, paperNumber)
+                .eq(MarkStudent::getUpload, true).eq(MarkStudent::getAbsent, false).eq(MarkStudent::getBreach, false);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public boolean updateScanInfo(MarkStudent student) {
+        UpdateWrapper<MarkStudent> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(MarkStudent::getSheetCount, student.getSheetCount())
+                .set(MarkStudent::getAnswers, student.getAnswers())
+                .set(MarkStudent::getBatchCode, student.getBatchCode())
+                .set(MarkStudent::getAbsent, student.getAbsent())
+                .set(MarkStudent::getUpload, true)
+                .set(MarkStudent::getUploadTime, System.currentTimeMillis())
+                .set(MarkStudent::getObjectiveScore, student.getObjectiveScore())
+                .set(MarkStudent::getObjectiveScoreList, student.getObjectiveScoreList())
+                .set(MarkStudent::getCardNumber, student.getCardNumber())
+                .eq(MarkStudent::getId, student.getId());
+        return this.update(updateWrapper);
+
+    }
+
+    @Override
+    public List<MarkStudent> listAbsentOrBreachMarkTaskStudent(Long examId, String paperNumber) {
+        return this.baseMapper.listAbsentOrBreachMarkTaskStudent(examId, paperNumber);
+    }
+
+    @Override
+    public List<MarkStudent> listUnMarkTaskStudent(Long examId, String paperNumber, Integer groupNumber, int pageSize) {
+        Page<MarkStudent> page = new Page<>(1, pageSize);
+        IPage<MarkStudent> markStudentIPage = this.baseMapper.listUnMarkTaskStudent(page, examId, paperNumber,
+                groupNumber);
+        return markStudentIPage.getRecords();
+    }
+
+    /**
+     * 客观题统分 统分场景: 1.后台-成绩检查-客观题检查 2.扫描端-考生图片上传 3.扫描端-客观题二次识别
+     *
+     * @param student
+     * @return
+     */
+    @Override
+    public boolean saveUploadStudent(MarkStudent student) {
+        MarkStudent old = this.getById(student.getId());
+        if (!student.getAbsent()) {// 正考
+            MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(student.getExamId(),
+                    student.getPaperNumber());
+            if (markPaper.getStatus().equals(MarkPaperStatus.FINISH)) {
+                markPaperService.updateStatus(markPaper.getExamId(), markPaper.getPaperNumber(), MarkPaperStatus.FORMAL,
+                        MarkPaperStatus.FINISH);
+            }
+        }
+        calculateObjectiveScore(student);
+        if (student.getAbsent()) {// 转缺考
+            student.setObjectiveScore(null);
+            student.setObjectiveScoreList(null);
+        }
+        if (!old.getAbsent() && student.getAbsent()) {// 正考转缺考
+            student.setSubjectiveScore(null);
+            student.setSubjectiveScoreList(null);
+            student.setSubjectiveStatus(SubjectiveStatus.UNMARK);
+            this.updateById(student);
+        }
+        boolean success = this.updateScanInfo(student);
+        if (success) {
+            markPaperService.updateUploadCount(student.getExamId(), student.getPaperNumber(),
+                    this.countUploadedByExamIdAndPaperNumber(student.getExamId(), student.getPaperNumber()));
+        }
+        return success;
+    }
+
+    private void calculateObjectiveScore(MarkStudent student) {
+        ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
+
+        ScoreInfo info = util.calculate(markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(
+                student.getExamId(), student.getPaperNumber(), null, true), null);
+        student.setObjectiveScore(info.getObjectiveScore());
+        student.setScoreList(info.getScoreList(), true);
+    }
+
+    @Override
+    @Transactional
+    public void updateStudentAndPaper(@NotNull SysUser user, @NotNull Long id,
+                                      @NotNull List<ScanStudentPaper> studentPaperList) {
+        for (ScanStudentPaper studentPaper : studentPaperList) {
+            studentPaper.setStudentId(id);
+        }
+        // 清空原有绑定关系
+        studentPaperService.removeByStudentId(id);
+        // 保存绑定关系
+        studentPaperService.saveOrUpdateBatch(studentPaperList);
+        // 更新考生状态
+        updateStudentByPaper(user.getId(), id, true);
+    }
+
+    @Override
+    public StudentVo findOne(StudentQuery query) {
+        return baseMapper.findOne(query);
+    }
+
+    @Override
+    public int countByExamIdAndSecretNumber(Long examId, String secretNumber) {
+        QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getSecretNumber, secretNumber);
+        return this.count(queryWrapper);
+    }
+
+    @Override
+    public List<MarkStudent> listByExamIdAndCoursePaperId(Long examId, String coursePaperId) {
+        QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getCoursePaperId, coursePaperId);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public IPage<AnswerQueryVo> query(AnswerQueryDomain query) {
+        // 查询考生分页信息
+        IPage<AnswerQueryVo> iPage = baseMapper.queryPage(new Page<>(query.getPageNumber(), query.getPageSize()),
+                query);
+        if (CollectionUtils.isNotEmpty(iPage.getRecords())) {
+            for (AnswerQueryVo vo : iPage.getRecords()) {
+                if (vo.getIsAbsent() != null && vo.getIsAbsent()) {
+                    vo.setExamStatus(ExamStatus.ABSENT);
+                } else {
+                    vo.setExamStatus(ExamStatus.OK);
+                }
+            }
+        }
+        if (CollectionUtils.isNotEmpty(iPage.getRecords()) && (query.getWithPaper() != null && query.getWithPaper())) {
+            Map<Long, AnswerQueryVo> map = new HashMap<>();
+
+            for (AnswerQueryVo vo : iPage.getRecords()) {
+                List<AnswerPaperVo> papers = new ArrayList<>();
+                vo.setPapers(papers);
+                if (vo.getCardPaperCount() != null) {
+                    for (int i = 1; i <= vo.getCardPaperCount(); i++) {
+                        AnswerPaperVo pv = new AnswerPaperVo();
+                        pv.setNumber(i);
+                        papers.add(pv);
+                    }
+                }
+                map.put(vo.getId(), vo);
+            }
+            // 根据考生id查找绑定paper
+            List<Long> studentIds = iPage.getRecords().stream().map(p -> p.getId()).collect(Collectors.toList());
+            List<StudentPaperVo> paperList = new BatchGetDataUtil<StudentPaperVo, Long>() {
+
+                @Override
+                public List<StudentPaperVo> getData(List<Long> paramList) {
+                    return scanPaperService.listByStudentIds(paramList);
+                }
+            }.getDataForBatch(studentIds, 200);
+
+            if (CollectionUtils.isNotEmpty(paperList)) {
+                Map<Long, AnswerPaperVo> paperMap = new HashMap<>();
+                for (StudentPaperVo p : paperList) {
+                    AnswerQueryVo vo = map.get(p.getStudentId());
+                    if (vo == null) {
+                        continue;
+                    }
+                    List<AnswerPaperVo> papers = vo.getPapers();
+                    if (papers == null) {
+                        continue;
+                    }
+                    if (papers.size() < p.getNumber()) {
+                        continue;
+                    }
+                    AnswerPaperVo pvo = papers.get(p.getNumber() - 1);
+                    pvo.setId(p.getPaperId());
+                    pvo.setNumber(p.getNumber());
+                    pvo.setAssigned(p.getAssigned());
+                    paperMap.put(p.getPaperId(), pvo);
+                }
+                // 查找page
+                List<Long> paperIds = paperList.stream().map(p -> p.getPaperId()).collect(Collectors.toList());
+                List<ScanPaperPage> paperPageList = new BatchGetDataUtil<ScanPaperPage, Long>() {
+
+                    @Override
+                    public List<ScanPaperPage> getData(List<Long> paramList) {
+                        return scanPaperPageService.listByPaperList(paramList);
+                    }
+                }.getDataForBatch(paperIds, 200);
+
+                if (CollectionUtils.isNotEmpty(paperPageList)) {
+                    for (ScanPaperPage p : paperPageList) {
+                        AnswerPaperVo pvo = paperMap.get(p.getPaperId());
+                        if (pvo == null) {
+                            continue;
+                        }
+                        List<AnswerPageVo> pages = pvo.getPages();
+                        if (pages == null) {
+                            pages = new ArrayList<>();
+                            pvo.setPages(pages);
+                        }
+                        AnswerPageVo pageVo = new AnswerPageVo();
+                        pageVo.setIndex(p.getPageIndex());
+                        pageVo.setSheetUri(teachcloudCommonService.filePreview(p.getSheetPath()));
+                        if (query.getWithOmrDetail() != null && query.getWithOmrDetail()) {
+                            pageVo.setAbsent(p.getAbsent().getResult());
+                            pageVo.setBreach(p.getBreach().getResult());
+                            pageVo.setQuestion(p.getQuestion().getResult());
+                            pageVo.setRecogData(p.getRecogData());
+                        }
+                        pages.add(pageVo);
+                    }
+                }
+            }
+        }
+        return iPage;
+    }
+
+    @Override
+    public List<String> summary(AnswerQueryDomain query) {
+        // 不分页查询考生准考证号
+        return baseMapper.querySummary(query);
+    }
+
+    @Transactional
+    @Override
+    public UpdateTimeVo omrEdit(Long userId, OmrEditDomain domain) {
+        MarkStudent student = findByExamIdAndStudentCode(domain.getExamId(), domain.getStudentCode());
+        if (student == null) {
+            throw new ParameterException("考生信息未找到");
+        }
+        concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().lock();
+        try {
+            for (OmrEditPaper paperEdit : domain.getPapers()) {
+                ScanStudentPaper sp = studentPaperService.findByStudentIdAndPaperNumber(student.getId(),
+                        paperEdit.getNumber());
+                if (sp == null) {
+                    throw new ParameterException("未找到绑定扫描结果");
+                }
+                ScanPaper paperEntity = scanPaperService.getById(sp.getPaperId());
+                if (paperEntity == null) {
+                    throw new ParameterException("未找到paper信息结果");
+                }
+                paperEntity.setUpdaterId(userId);
+                paperEntity.setUpdateTime(System.currentTimeMillis());
+                List<ScanPaperPage> pages = scanPaperPageService.listByPaperId(paperEntity.getId());
+                for (ScanPaperPage pageEntity : pages) {
+                    paperEdit.updatePage(pageEntity);
+                }
+                scanPaperService.savePaperAndPages(paperEntity, pages);
+            }
+            updateStudentByPaper(userId, student.getId(), false);
+            return UpdateTimeVo.create();
+        } finally {
+            concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().unlock();
+        }
+    }
+
+    @Override
+    public MarkStudent findByExamIdAndStudentCode(Long examId, String studentCode) {
+        if (examId == null) {
+            throw new ParameterException("examId 不能为空");
+        }
+        if (StringUtils.isBlank(studentCode)) {
+            throw new ParameterException("studentCode不能为空");
+        }
+        QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkStudent::getExamId, examId);
+        queryWrapper.lambda().eq(MarkStudent::getStudentCode, studentCode);
+        return baseMapper.selectOne(queryWrapper);
+    }
+
+    @Override
+    public long countStudentCountByExamIdAndPaperNumber(Long examId, String paperNumber, String paperType) {
+        QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkStudent::getExamId, examId).eq(MarkStudent::getPaperNumber, paperNumber)
+                .eq(MarkStudent::getPaperType, paperType);
+        return this.count(queryWrapper);
+    }
+
+    @Transactional
+    @Override
+    public AbsentManualUpdateVo absentManualUpdate(Long examId, String coursePaperId, String studentCode) {
+        MarkStudent student = findByExamIdAndCoursePaperIdAndStudentCode(examId, coursePaperId, studentCode);
+        if (student == null) {
+            throw new ParameterException("考生未找到");
+        }
+        if (student.getScanStatus() != ScanStatus.UNEXIST) {
+            throw new ParameterException("考生不是未扫描状态");
+        }
+        LambdaUpdateWrapper<MarkStudent> lw = new LambdaUpdateWrapper<>();
+        lw.set(MarkStudent::getScanStatus, ScanStatus.MANUAL_ABSENT);
+        lw.eq(MarkStudent::getId, student.getId());
+        lw.eq(MarkStudent::getScanStatus, ScanStatus.UNEXIST);
+        update(lw);
+        return AbsentManualUpdateVo.create(ScanStatus.MANUAL_ABSENT);
+    }
+
+    @Transactional
+    @Override
+    public UpdateTimeVo confirm(Long examId, String coursePaperId, String studentCode, Boolean omrAbsent) {
+        LambdaUpdateWrapper<MarkStudent> lw = new LambdaUpdateWrapper<>();
+        lw.set(MarkStudent::getOmrAbsentChecked, true);
+        lw.eq(MarkStudent::getExamId, examId);
+        lw.eq(MarkStudent::getCoursePaperId, coursePaperId);
+        lw.eq(MarkStudent::getStudentCode, studentCode);
+        if (omrAbsent != null) {
+            lw.eq(MarkStudent::getOmrAbsent, omrAbsent);
+        }
+        if (!update(lw)) {
+            throw new ParameterException("考生未找到");
+        }
+        return UpdateTimeVo.create();
+    }
+
+    @Override
+    public List<Long> findIdByExamIdAndPaperNumberAndSubjectiveStatus(Long examId, String paperNumber,
+                                                                      SubjectiveStatus unmark, SubjectiveStatus marked) {
+        return this.baseMapper.findIdByExamIdAndPaperNumberAndSubjectiveStatus(examId, paperNumber, unmark.name(),
+                marked.name());
+    }
+
+    @Override
+    public Task getSubjectiveInspectedTask(Long studentId) {
+        Task task = null;
+        if (studentId != null) {
+            SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+            MarkStudent markStudent = this.getById(studentId);
+            releaseStudent(markStudent);
+            if (markService.applyStudent(markStudent, sysUser.getId())) {
+                task = taskService.build(studentId);
+            }
+        }
+        return task;
+    }
+
+    @Override
+    public void saveSubjectiveInspectedTask(MarkHeaderResult markResult) {
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        for (MarkHeaderGroupResult groupResult : markResult.getGroups()) {
+            MarkUserGroup markUserGroup = markUserGroupService.getByExamIdAndPaperNumberAndGroupNumberAndUserId(
+                    markResult.getExamId(), markResult.getPaperNumber(), groupResult.getGroupNumber(), sysUser.getId());
+            try {
+                lockService.watch(LockType.EXAM_SUBJECT, markUserGroup.getExamId(), markUserGroup.getPaperNumber());
+                lockService.watch(LockType.GROUP, markUserGroup.getExamId(), markUserGroup.getPaperNumber(),
+                        markUserGroup.getGroupNumber());
+                lockService.watch(LockType.MARK_USER_GROUP, markUserGroup.getId());
+                lockService.waitlock(LockType.STUDENT, groupResult.getStudentId());
+                markService.submitHeaderTask(groupResult, markUserGroup);
+            } catch (Exception e) {
+                log.error("save task error", e);
+            } finally {
+                lockService.unlock(LockType.STUDENT, groupResult.getStudentId());
+                lockService.unwatch(LockType.MARK_USER_GROUP, markUserGroup.getId());
+                lockService.unwatch(LockType.GROUP, markUserGroup.getExamId(), markUserGroup.getPaperNumber(),
+                        markUserGroup.getGroupNumber());
+                lockService.unwatch(LockType.EXAM_SUBJECT, markUserGroup.getExamId(), markUserGroup.getPaperNumber());
+            }
+        }
+    }
+
+    private void releaseStudent(MarkStudent markStudent) {
+        try {
+            lockService.waitlock(LockType.STUDENT, markStudent.getId());
+            markService.releaseByStudent(markStudent);
+        } catch (Exception e) {
+            log.error("release user error", e);
+        } finally {
+            lockService.unlock(LockType.STUDENT, markStudent.getId());
+        }
+    }
+
+    @Override
+    public List<ArchiveStudentVo> studentList(ArchiveStudentQuery query) {
+        List<ArchiveStudentVo> ret = baseMapper.studentList(query);
+        if (CollectionUtils.isNotEmpty(ret)) {
+            fillSheetUrls(ret);
+        }
+        return ret;
+    }
+
+    private void fillSheetUrls(List<ArchiveStudentVo> ret) {
+        List<Long> studentIds = ret.stream().map(p -> p.getId()).collect(Collectors.toList());
+        // 查找page
+        List<ScanPaperPageVo> paperPageList = new BatchGetDataUtil<ScanPaperPageVo, Long>() {
+
+            @Override
+            public List<ScanPaperPageVo> getData(List<Long> studentIds) {
+                return scanPaperPageService.listByStudentIds(studentIds);
+            }
+        }.getDataForBatch(studentIds, 200);
+
+        Map<Long, List<String>> map = new HashMap<>();
+        if (CollectionUtils.isNotEmpty(paperPageList)) {
+            for (ScanPaperPageVo p : paperPageList) {
+                List<String> tem = map.get(p.getStudentId());
+                if (tem == null) {
+                    tem = new ArrayList<>();
+                    map.put(p.getStudentId(), tem);
+                }
+                tem.add(p.getSheetPath());
+            }
+        }
+        for (ArchiveStudentVo vo : ret) {
+            vo.setSheetUrls(map.get(vo.getId()));
+        }
+    }
+
+    @Override
+    public void scoreExport(ArchiveStudentQuery query, HttpServletResponse response) {
+        List<ArchiveStudentVo> ret = baseMapper.studentList(query);
+        try {
+            ExcelUtil.excelExport("成绩导出", ArchiveStudentVo.class, ret, response);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public ScoreReportVo scoreReport(Long examId, String paperNumber) {
+        ScoreReportVo ret = new ScoreReportVo();
+        ret.setOverview(baseMapper.overview(examId, paperNumber));
+        if (ret.getOverview() != null) {
+            double total = ret.getOverview().getStudentCount() - ret.getOverview().getAbsentCount();
+            ret.getOverview().setPassRate(
+                    Calculator.divide2String(Calculator.multiply(ret.getOverview().getPassCount(), 100), total, 2)
+                            + "%");
+            ret.getOverview().setExcellentRate(
+                    Calculator.divide2String(Calculator.multiply(ret.getOverview().getExcellentCount(), 100), total, 2)
+                            + "%");
+        }
+
+        fillScoreRange(ret, examId, paperNumber);
+
+        ret.setCollege(baseMapper.college(examId, paperNumber));
+        if (CollectionUtils.isNotEmpty(ret.getCollege())) {
+            for (CollegeVo vo : ret.getCollege()) {
+                double total = vo.getStudentCount() - vo.getAbsentCount();
+                vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100), total, 2) + "%");
+                vo.setExcellentRate(
+                        Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100), total, 2) + "%");
+            }
+        }
+
+        ret.setClassData(baseMapper.classData(examId, paperNumber));
+        if (CollectionUtils.isNotEmpty(ret.getClassData())) {
+            for (ClassVo vo : ret.getClassData()) {
+                double total = vo.getStudentCount() - vo.getAbsentCount();
+                vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100), total, 2) + "%");
+                vo.setExcellentRate(
+                        Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100), total, 2) + "%");
+            }
+        }
+
+        ret.setTeacher(baseMapper.teacher(examId, paperNumber));
+        if (CollectionUtils.isNotEmpty(ret.getTeacher())) {
+            for (TeacherVo vo : ret.getTeacher()) {
+                double total = vo.getStudentCount() - vo.getAbsentCount();
+                vo.setPassRate(Calculator.divide2String(Calculator.multiply(vo.getPassCount(), 100), total, 2) + "%");
+                vo.setExcellentRate(
+                        Calculator.divide2String(Calculator.multiply(vo.getExcellentCount(), 100), total, 2) + "%");
+            }
+        }
+
+        fillObjective(ret, examId, paperNumber);
+
+        ret.setSubjective(markSubjectiveScoreService.getSubjectiveVo(examId, paperNumber));
+        if (CollectionUtils.isNotEmpty(ret.getSubjective())) {
+            for (QuestionVo vo : ret.getSubjective()) {
+                double total = vo.getStudentCount();
+                vo.setScoreRate(Calculator.divide(vo.getScoreCount(), total, 2));
+                vo.setFullScoreRate(Calculator.divide(vo.getFullScoreCount(), total, 2));
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    public void exportUnexist(Long examId, HttpServletResponse response) {
+        try {
+            List<UnexistStudentDto> unexistStudentDtoList = this.baseMapper.listUnexistStudentByExamIdAndCoursePaperId(examId);
+            ExcelUtil.excelExport("评卷员工作量", UnexistStudentDto.class, unexistStudentDtoList, response);
+        } catch (Exception e) {
+            throw ExceptionResultEnum.ERROR.exception("导出评卷员工作量失败");
+        }
+    }
+
+    @Override
+    public long countUnmarkByExamIdAndPaperNumber(Long examId, String paperNumber) {
+        QueryWrapper<MarkStudent> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(MarkStudent::getExamId, examId)
+                .eq(MarkStudent::getPaperNumber, paperNumber)
+                .eq(MarkStudent::getSubjectiveStatus, SubjectiveStatus.UNMARK)
+                .eq(MarkStudent::getUpload, true)
+                .eq(MarkStudent::getAbsent, false)
+                .eq(MarkStudent::getBreach, false);
+        return this.count(queryWrapper);
+    }
+
+    private void fillObjective(ScoreReportVo ret, Long examId, String paperNumber) {
+        List<MarkQuestion> qs = markQuestionService.listQuestionByExamIdAndPaperNumber(examId, paperNumber);
+        List<MarkStudent> students = listByExamIdAndPaperNumberAndAbsent(examId, paperNumber, false);
+        Map<String, QuestionVo> map = new HashMap<>();
+        for (MarkStudent s : students) {
+            List<ScoreItem> sis = s.getScoreList(true, qs);
+            if (CollectionUtils.isNotEmpty(sis)) {
+                for (ScoreItem si : sis) {
+                    String key = si.getMainNumber() + "-" + si.getSubNumber();
+                    QuestionVo vo = map.get(key);
+                    if (vo == null) {
+                        vo = new QuestionVo();
+                        vo.setScoreCount(0);
+                        vo.setFullScoreCount(0);
+                        vo.setStudentCount(0);
+                        vo.setScoreSum(0.0);
+                        vo.setTitle(si.getTitle());
+                        vo.setScore(si.getTotalScore());
+                        map.put(key, vo);
+                    }
+                    vo.setStudentCount(vo.getStudentCount() + 1);
+                    vo.setScoreSum(vo.getScoreSum() + si.getScore());
+                    if (si.getScore() == si.getTotalScore()) {
+                        vo.setFullScoreCount(vo.getFullScoreCount() + 1);
+                    }
+                    if (si.getScore() > 0) {
+                        vo.setScoreCount(vo.getScoreCount() + 1);
+                    }
+                }
+            }
+        }
+        if (map.isEmpty()) {
+            return;
+        }
+        List<QuestionVo> list = new ArrayList<>(map.values());
+        Collections.sort(list, new Comparator<QuestionVo>() {
+
+            @Override
+            public int compare(QuestionVo o1, QuestionVo o2) {
+                if (o1.getMainNumber() > o2.getMainNumber()) {
+                    return 1;
+                } else if (o1.getSubNumber() < o2.getSubNumber()) {
+                    return -1;
+                } else {
+                    return 0;
+                }
+            }
+
+        });
+        for (QuestionVo vo : list) {
+            double total = vo.getStudentCount();
+            vo.setScoreRate(Calculator.divide(vo.getScoreCount(), total, 2));
+            vo.setFullScoreRate(Calculator.divide(vo.getFullScoreCount(), total, 2));
+        }
+        ret.setObjective(list);
+    }
+
+    private List<MarkStudent> listByExamIdAndPaperNumberAndAbsent(Long examId, String paperNumber, Boolean absent) {
+        QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
+        lw.eq(MarkStudent::getExamId, examId);
+        lw.eq(MarkStudent::getPaperNumber, paperNumber);
+        if (absent != null) {
+            lw.eq(MarkStudent::getAbsent, absent);
+        }
+        return this.list(wrapper);
+    }
+
+    private void fillScoreRange(ScoreReportVo ret, Long examId, String paperNumber) {
+        int toltal = getCountByPaperNumber(examId, paperNumber);
+        List<ScoreRangeVo> scoreRange = new ArrayList<>();
+        ret.setScoreRange(scoreRange);
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 1.0, 9.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 10.0, 19.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 20.0, 29.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 30.0, 39.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 40.0, 49.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 50.0, 59.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 60.0, 69.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 70.0, 79.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 80.0, 89.5));
+        scoreRange.add(getScoreRangeVo(toltal, examId, paperNumber, 90.0, 100.0));
+    }
+
+    private ScoreRangeVo getScoreRangeVo(int toltal, Long examId, String paperNumber, Double start, Double end) {
+        int count = getCount(examId, paperNumber, start, end);
+        Double rate = null;
+        if (toltal != 0) {
+            rate = Calculator.multiply(count, toltal, 2);
+        }
+        ScoreRangeVo vo = new ScoreRangeVo(count, start, end, rate);
+        return vo;
+    }
+
+    private int getCountByPaperNumber(Long examId, String paperNumber) {
+        QueryWrapper<MarkStudent> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<MarkStudent> lw = wrapper.lambda();
+        lw.eq(MarkStudent::getExamId, examId);
+        lw.eq(MarkStudent::getPaperNumber, paperNumber);
+        return this.count(wrapper);
+    }
+
+    private int getCount(Long examId, String paperNumber, Double start, Double end) {
+        return baseMapper.getCountByScoreRange(examId, paperNumber, start, end);
+    }
 }

+ 4 - 4
teachcloud-mark/src/main/resources/mapper/MarkStudentMapper.xml

@@ -20,10 +20,10 @@
         <result column="batch_code" property="batchCode" />
         <result column="sheet_count" property="sheetCount" />
         <result column="answers" property="answers" />
-        <result column="is_upload" property="isUpload" />
-        <result column="is_absent" property="isAbsent" />
-        <result column="is_manual_absent" property="isManualAbsent" />
-        <result column="is_breach" property="isBreach" />
+        <result column="is_upload" property="upload" />
+        <result column="is_absent" property="absent" />
+        <result column="is_manual_absent" property="manualAbsent" />
+        <result column="is_breach" property="breach" />
         <result column="upload_time" property="uploadTime" />
         <result column="check_time" property="checkTime" />
         <result column="check_user_id" property="checkUserId" />