xiaofei 1 سال پیش
والد
کامیت
7a6fc42d79

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

@@ -94,6 +94,9 @@ public class MarkStudent implements Serializable {
     @ApiModelProperty(value = "原图数量")
     private Integer sheetCount;
 
+    @ApiModelProperty(value = "原图地址url")
+    private String sheetPath;
+
     @ApiModelProperty(value = "客观题识别结果")
     private String answers;
 
@@ -316,6 +319,14 @@ public class MarkStudent implements Serializable {
         this.sheetCount = sheetCount;
     }
 
+    public String getSheetPath() {
+        return sheetPath;
+    }
+
+    public void setSheetPath(String sheetPath) {
+        this.sheetPath = sheetPath;
+    }
+
     public String getAnswers() {
         return answers;
     }

+ 887 - 863
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkStudentServiceImpl.java

@@ -13,6 +13,8 @@ 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 org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -111,870 +113,892 @@ 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);
-		} 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());
-		}
-	}
-
-	@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(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;
-	}
-	
-	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(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;
+    }
+
+    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);
+    }
 }

+ 38 - 37
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanPaperPageServiceImpl.java

@@ -23,46 +23,47 @@ import com.qmth.teachcloud.mark.service.ScanPaperPageService;
  */
 @Service
 public class ScanPaperPageServiceImpl extends ServiceImpl<ScanPaperPageMapper, ScanPaperPage>
-		implements ScanPaperPageService {
+        implements ScanPaperPageService {
 
-	@Override
-	public ScanPaperPage findPaperIdAndIndex(Long paperId, Integer index) {
-		if (paperId == null) {
-			throw new ParameterException("paperId不能为空");
-		}
-		if (index == null) {
-			throw new ParameterException("index不能为空");
-		}
-		QueryWrapper<ScanPaperPage> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<ScanPaperPage> lw = wrapper.lambda();
-		lw.eq(ScanPaperPage::getPaperId, paperId);
-		lw.eq(ScanPaperPage::getPageIndex, index);
-		return baseMapper.selectOne(wrapper);
-	}
+    @Override
+    public ScanPaperPage findPaperIdAndIndex(Long paperId, Integer index) {
+        if (paperId == null) {
+            throw new ParameterException("paperId不能为空");
+        }
+        if (index == null) {
+            throw new ParameterException("index不能为空");
+        }
+        QueryWrapper<ScanPaperPage> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<ScanPaperPage> lw = wrapper.lambda();
+        lw.eq(ScanPaperPage::getPaperId, paperId);
+        lw.eq(ScanPaperPage::getPageIndex, index);
+        return baseMapper.selectOne(wrapper);
+    }
 
-	@Override
-	public List<ScanPaperPage> listByPaperId(Long paperId) {
-		if (paperId == null) {
-			throw new ParameterException("paperId不能为空");
-		}
-		QueryWrapper<ScanPaperPage> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<ScanPaperPage> lw = wrapper.lambda();
-		lw.eq(ScanPaperPage::getPaperId, paperId);
-		return baseMapper.selectList(wrapper);
-	}
+    @Override
+    public List<ScanPaperPage> listByPaperId(Long paperId) {
+        if (paperId == null) {
+            throw new ParameterException("paperId不能为空");
+        }
+        QueryWrapper<ScanPaperPage> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<ScanPaperPage> lw = wrapper.lambda();
+        lw.eq(ScanPaperPage::getPaperId, paperId);
+        lw.orderByAsc(ScanPaperPage::getPageIndex);
+        return baseMapper.selectList(wrapper);
+    }
 
-	@Override
-	public List<ScanPaperPage> listByPaperList(List<Long> paperIds) {
-		QueryWrapper<ScanPaperPage> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<ScanPaperPage> lw = wrapper.lambda();
-		lw.in(ScanPaperPage::getPaperId, paperIds);
-		wrapper.orderByAsc("paper_id", "page_index");
-		return this.list(wrapper);
-	}
+    @Override
+    public List<ScanPaperPage> listByPaperList(List<Long> paperIds) {
+        QueryWrapper<ScanPaperPage> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<ScanPaperPage> lw = wrapper.lambda();
+        lw.in(ScanPaperPage::getPaperId, paperIds);
+        wrapper.orderByAsc("paper_id", "page_index");
+        return this.list(wrapper);
+    }
 
-	@Override
-	public List<ScanPaperPageVo> listByStudentIds(List<Long> studentIds) {
-		return baseMapper.listByStudentIds(studentIds);
-	}
+    @Override
+    public List<ScanPaperPageVo> listByStudentIds(List<Long> studentIds) {
+        return baseMapper.listByStudentIds(studentIds);
+    }
 
 }