ting.yin il y a 1 an
Parent
commit
3ce8223354

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

@@ -1,6 +1,5 @@
 package com.qmth.distributed.print.api.mark;
 
-import com.qmth.boot.api.annotation.Aac;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.RequestBody;

+ 9 - 12
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/bean/scananswer/AnswerPageVo.java

@@ -2,16 +2,13 @@ package com.qmth.teachcloud.mark.bean.scananswer;
 
 import java.util.List;
 
-import com.qmth.teachcloud.mark.bean.ArrayResult;
-import com.qmth.teachcloud.mark.bean.BoolResult;
-
 public class AnswerPageVo {
 	private Integer index;
     private String sheetUri;
     private List<String> sliceUri;
-	private BoolResult absent;
-	private BoolResult breach;
-	private ArrayResult question;
+	private Boolean absent;
+	private Boolean breach;
+	private List<String> question;
 	private String recogData;
 	public Integer getIndex() {
 		return index;
@@ -19,22 +16,22 @@ public class AnswerPageVo {
 	public void setIndex(Integer index) {
 		this.index = index;
 	}
-	public BoolResult getAbsent() {
+	public Boolean getAbsent() {
 		return absent;
 	}
-	public void setAbsent(BoolResult absent) {
+	public void setAbsent(Boolean absent) {
 		this.absent = absent;
 	}
-	public BoolResult getBreach() {
+	public Boolean getBreach() {
 		return breach;
 	}
-	public void setBreach(BoolResult breach) {
+	public void setBreach(Boolean breach) {
 		this.breach = breach;
 	}
-	public ArrayResult getQuestion() {
+	public List<String> getQuestion() {
 		return question;
 	}
-	public void setQuestion(ArrayResult question) {
+	public void setQuestion(List<String> question) {
 		this.question = question;
 	}
 	public String getSheetUri() {

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

@@ -737,8 +737,8 @@ public class MarkServiceImpl implements MarkService {
 		dto.setEnableAllZore(false);
 		dto.setFileServer(null);
 		dto.setUserName(user.getRealName());
-		dto.getSubject().setAnswerUrl(teachcloudCommonService.filePreview(markPaper.getAnswerFilePath()));
-		dto.getSubject().setPaperUrl(teachcloudCommonService.filePreview(markPaper.getPaperFilePath()));
+		dto.getSubject().setAnswerUrl(markPaper.getAnswerFilePath());
+		dto.getSubject().setPaperUrl(markPaper.getPaperFilePath());
 		dto.getSubject().setCode(markPaper.getPaperNumber());
 		dto.getSubject().setName(markPaper.getCourseName());
 		dto.setForceSpecialTag(true);

+ 898 - 899
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkStudentServiceImpl.java

@@ -13,16 +13,13 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.constraints.NotNull;
 
-import com.alibaba.fastjson.JSON;
-import com.qmth.teachcloud.mark.bean.FilePathVo;
-import com.qmth.teachcloud.mark.dto.UnexistStudentDto;
-import com.qmth.teachcloud.mark.dto.mark.manage.MarkerInfoDto;
 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;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -43,6 +40,7 @@ import com.qmth.teachcloud.common.enums.scan.ConditionType;
 import com.qmth.teachcloud.common.service.TeachcloudCommonService;
 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;
@@ -66,6 +64,7 @@ import com.qmth.teachcloud.mark.bean.scanexaminfo.ScanExamInfoVo;
 import com.qmth.teachcloud.mark.bean.student.AbsentManualUpdateVo;
 import com.qmth.teachcloud.mark.bean.student.StudentQuery;
 import com.qmth.teachcloud.mark.bean.student.StudentVo;
+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;
@@ -115,902 +114,902 @@ 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(0d);
-            student.setObjectiveScoreList(null);
-        }
-        if (!old.getAbsent() && student.getAbsent()) {// 正考转缺考
-            student.setSubjectiveScore(0d);
-            student.setSubjectiveScoreList(null);
-            this.updateById(student);
-            this.updateSubjectiveStatusAndScore(student.getId(), SubjectiveStatus.UNMARK, 0D, null);
-        }
-        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());
-                            pageVo.setBreach(p.getBreach());
-                            pageVo.setQuestion(p.getQuestion());
-                            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, String coursePaperId, HttpServletResponse response) {
-        try {
-            List<UnexistStudentDto> unexistStudentDtoList = this.baseMapper.listUnexistStudentByExamIdAndCoursePaperId(examId, coursePaperId);
-            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());
+			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(0d);
+			student.setObjectiveScoreList(null);
+		}
+		if (!old.getAbsent() && student.getAbsent()) {// 正考转缺考
+			student.setSubjectiveScore(0d);
+			student.setSubjectiveScoreList(null);
+			this.updateById(student);
+			this.updateSubjectiveStatusAndScore(student.getId(), SubjectiveStatus.UNMARK, 0D, null);
+		}
+		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, String coursePaperId, HttpServletResponse response) {
+		try {
+			List<UnexistStudentDto> unexistStudentDtoList = this.baseMapper
+					.listUnexistStudentByExamIdAndCoursePaperId(examId, coursePaperId);
+			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);
+	}
 }