package cn.com.qmth.scancentral.service.impl; import cn.com.qmth.scancentral.bean.BatchCreateDomain; import cn.com.qmth.scancentral.bean.BatchQueryDomain; import cn.com.qmth.scancentral.bean.User; import cn.com.qmth.scancentral.bean.WorkloadDomain; import cn.com.qmth.scancentral.bean.answersave.AnswerDomain; import cn.com.qmth.scancentral.bean.answersave.AnswerPaper; import cn.com.qmth.scancentral.dao.BatchDao; import cn.com.qmth.scancentral.entity.*; import cn.com.qmth.scancentral.enums.*; import cn.com.qmth.scancentral.exception.NotFoundExceptions; import cn.com.qmth.scancentral.exception.ParameterExceptions; import cn.com.qmth.scancentral.service.*; import cn.com.qmth.scancentral.support.TaskLock; import cn.com.qmth.scancentral.support.TaskLockUtil; import cn.com.qmth.scancentral.util.PageUtil; import cn.com.qmth.scancentral.vo.*; import cn.com.qmth.scancentral.vo.batch.*; import cn.com.qmth.scancentral.vo.batchdetail.BatchDetailDO; import cn.com.qmth.scancentral.vo.batchdetail.BatchDetailVo; import cn.com.qmth.scancentral.vo.batchdetail.BatchStudentVo; import cn.com.qmth.scancentral.vo.checkimage.CheckImageSubmitVo; import cn.com.qmth.scancentral.vo.device.DeviceVo; import cn.com.qmth.scancentral.vo.paper.PaperVo; import cn.com.qmth.scancentral.vo.task.TaskStatusVo; import cn.com.qmth.scancentral.vo.verify.VerifyStudentVo; import cn.com.qmth.scancentral.vo.verify.VerifyTaskVo; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qmth.boot.core.collection.PageResult; import com.qmth.boot.core.concurrent.service.ConcurrentService; import com.qmth.boot.core.exception.ParameterException; import com.qmth.boot.core.exception.StatusException; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.math.RandomUtils; 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 org.springframework.web.multipart.MultipartFile; import java.util.*; @Service public class BatchServiceImpl extends ServiceImpl implements BatchService { @Autowired private ExamService examService; @Autowired private StudentService studentService; @Autowired private PaperService paperService; @Autowired private PaperPageService paperPageService; @Autowired private BatchPaperService batchPaperService; @Autowired private AnswerCardService answerCardService; @Autowired private AnswerCardSubjectService answerCardSubjectService; @Autowired private ConcurrentService concurrentService; @Autowired private FileService fileService; @Autowired private FilePropertyService filePropertyService; @Autowired private SubjectService subjectService; @Autowired private AssignedCheckHistoryService assignedCheckHistoryService; @Override public BatchEntity checkBatchStatus(BatchEntity b) { if (b == null) { throw new ParameterException("批次未找到"); } if (BatchStatus.FINISH.equals(b.getStatus())) { throw new ParameterException("批次已经完成"); } if (VerifyStatus.WAITING.equals(b.getVerifyStatus())) { throw new ParameterException("批次正在审核中"); } if (VerifyStatus.REJECT.equals(b.getVerifyStatus())) { throw new ParameterException("批次审核不通过"); } return b; } @Transactional @Override public BatchCreateVo batchCreate(BatchCreateDomain domain, User user) { ExamEntity exam = examService.getById(domain.getExamId()); if (exam == null) { throw ParameterExceptions.EXAM_NOT_FOUND; } List rescan = new ArrayList<>(); String packageCode = null; for (String examNumber : domain.getExamNumbers()) { StudentEntity student = studentService.findByExamAndSubjectCodeAndExamNumber(domain.getExamId(), domain.getSubjectCode(), examNumber); if (student == null) { throw new ParameterException("未找到考生信息:" + examNumber); } if (exam.getScanByPackage() != null && exam.getScanByPackage()) { if (packageCode == null) { packageCode = student.getPackageCode(); } else { if (!packageCode.equals(student.getPackageCode())) { throw new ParameterException("考生[" + examNumber + "]卷袋编号与其它考生不一致"); } } if (domain.getSubjectCode() != null && !domain.getSubjectCode().equals(student.getSubjectCode())) { throw new ParameterException("考生[" + examNumber + "]科目代码与[" + domain.getSubjectCode() + "]不一致"); } } Rescan rc = getRescan(student); if (rc != null) { rescan.add(rc); } } if (exam.getScanByPackage() != null && exam.getScanByPackage()) { if (StringUtils.isBlank(domain.getSubjectCode())) { throw new ParameterException("整袋模式下科目代码不能为空"); } List ss = studentService.findByExamAndPackage(domain.getExamId(), packageCode, domain.getSubjectCode()); if (CollectionUtils.isEmpty(ss) || ss.size() != domain.getExamNumbers().size()) { throw new ParameterException("该批次考生数量与卷袋考生数量不一致"); } } BatchEntity b = new BatchEntity(); b.setExamId(domain.getExamId()); b.setDevice(user.getAccount()); b.setPackageCode(packageCode); b.setSubjectCode(domain.getSubjectCode()); b.setAssignedCount(0); b.setScanCount(0); b.setStatus(BatchStatus.INIT); this.save(b); BatchCreateVo vo = new BatchCreateVo(); vo.setRescan(rescan); vo.setId(b.getId()); vo.setCreateTime(b.getCreateTime()); vo.setEnableSyncVerify(exam.getEnableSyncVerify()); return vo; } private Rescan getRescan(StudentEntity student) { Rescan res = new Rescan(); res.setExamNumber(student.getExamNumber()); List papers = paperService.findStudentPaper(student.getId()); List resPapers = new ArrayList<>(); res.setPapers(resPapers); if (CollectionUtils.isEmpty(papers)) { return null; } PaperVo first = papers.get(0); res.setBatchId(first.getBatchId()); res.setDevice(first.getDevice()); res.setCreateTime(first.getCreateTime()); for (PaperVo paper : papers) { Paper p = new Paper(); p.setId(paper.getId()); p.setNumber(paper.getNumber()); p.setMismatch(paper.getMismatch()); List pages = new ArrayList<>(); p.setPages(pages); List paperPages = paperPageService.listByPaperId(paper.getId()); if (CollectionUtils.isNotEmpty(paperPages)) { for (PaperPageEntity pp : paperPages) { pages.add(pp.getSheetPath()); } } resPapers.add(p); } return res; } @Override public int getCountByExam(Long examId) { QueryWrapper wrapper = new QueryWrapper<>(); LambdaQueryWrapper lw = wrapper.lambda(); lw.eq(BatchEntity::getExamId, examId); return this.count(wrapper); } @Override public int getValidCountByExam(Long examId) { QueryWrapper wrapper = new QueryWrapper<>(); LambdaQueryWrapper lw = wrapper.lambda(); lw.and(param -> { param.or().isNull(BatchEntity::getVerifyStatus); param.or().ne(BatchEntity::getVerifyStatus, VerifyStatus.REJECT); }); lw.eq(BatchEntity::getExamId, examId); lw.gt(BatchEntity::getScanCount, 0); return this.count(wrapper); } @Override public int getVerifyCount(Long examId) { QueryWrapper wrapper = new QueryWrapper<>(); LambdaQueryWrapper lw = wrapper.lambda(); lw.eq(BatchEntity::getExamId, examId); lw.eq(BatchEntity::getVerifyStatus, VerifyStatus.WAITING); return this.count(wrapper); } @Transactional @Override public SheetUploadVo sheetUpload(Long batchId, String examNumber, Integer paperNumber, Integer pageIndex, MultipartFile multipartFile, String md5) { BatchEntity batch = checkBatchStatus(getById(batchId)); try { String path = fileService.uploadSheet(multipartFile.getInputStream(), md5, batch.getExamId(), batchId, examNumber, paperNumber, pageIndex); filePropertyService.save(batch.getExamId(), path, md5, multipartFile.getSize()); return SheetUploadVo.create(path); } catch (Exception e) { log.error("原图上传失败,batchId=" + batch + ", examNumber=" + examNumber + ", paperNumber=" + paperNumber + ", pageIndex=" + pageIndex, e); throw new StatusException("原图上传失败", e); } } @Transactional @Override public SliceUploadVo sliceUpload(Long batchId, String examNumber, Integer paperNumber, Integer pageIndex, Integer index, MultipartFile multipartFile, String md5) { BatchEntity batch = checkBatchStatus(getById(batchId)); try { String path = fileService.uploadSlice(multipartFile.getInputStream(), md5, batch.getExamId(), batchId, examNumber, paperNumber, pageIndex, index); filePropertyService.save(batch.getExamId(), path, md5, multipartFile.getSize()); return SliceUploadVo.create(path); } catch (Exception e) { log.error("裁切图上传失败,batchId=" + batch + ", examNumber=" + examNumber + ", paperNumber=" + paperNumber + ", pageIndex=" + pageIndex + ", index=" + index, e); throw new StatusException("裁切图上传失败", e); } } @Transactional @Override public AnswerSaveVo batchSave(AnswerDomain domain, User user) { BatchEntity batch = checkBatchStatus(getById(domain.getBatchId())); ExamEntity exam = examService.getById(batch.getExamId()); if (exam == null) { throw ParameterExceptions.EXAM_NOT_FOUND; } String examNumber = domain.getExamNumber(); StudentEntity student = studentService.findByExamAndSubjectCodeAndExamNumber(batch.getExamId(), domain.getSubjectCode(), examNumber); if (student == null) { throw new ParameterException("考生信息未找到"); } AnswerCardEntity answerCard = answerCardService.findByExamAndNumber(batch.getExamId(), domain.getCardNumber()); if (answerCard == null) { throw new ParameterException("卡格式信息未找到"); } // 整袋扫描验证packageCode if (exam.getScanByPackage() != null && exam.getScanByPackage()) { if (!StringUtils.equals(batch.getPackageCode(), student.getPackageCode())) { throw new ParameterException("与批次卷袋号不一致"); } } // 验证卡格式所属科目 boolean allowSubject = answerCardSubjectService.checkSubject(answerCard.getExamId(), answerCard.getNumber(), student.getSubjectCode()); if (!allowSubject) { throw new ParameterException("卡格式与考生科目不一致"); } // 不允许缺页时,保存张数需要与卡格式定义一致 if (!exam.getAllowUnexistPaper() && domain.getPapers().size() != answerCard.getPaperCount()) { throw new ParameterException("卡格式张数不一致"); } List paperTypeBarcodeContents = exam.getPaperTypeBarcodeContent(); SubjectEntity subject = subjectService.findByExamIdAndCode(student.getExamId(), student.getSubjectCode()); if (subject.getPaperTypeBarcodeContent() != null && !subject.getPaperTypeBarcodeContent().isEmpty()) { paperTypeBarcodeContents = subject.getPaperTypeBarcodeContent(); } boolean studentAssigned = false; List studentPaperList = new ArrayList<>(); for (AnswerPaper answerPaper : domain.getPapers()) { // 验证page数量 answerPaper.answerCardValidate(answerCard); Integer paperNumber = answerPaper.getNumber(); PaperEntity paper = findOrCreatePaper(batch, student, paperNumber, user); // 设置paper属性 paper.setExamId(batch.getExamId()); paper.setCardNumber(answerCard.getNumber()); paper.setNumber(paperNumber); paper.setMismatch(false); paper.setQuestionFilled(false); paper.setAssignedSuspect(false); paper.setSubjectiveFilled(answerPaper.getSubjectiveFilled()); paper.setOmrExamNumber(answerPaper.getOmrExamNumber()); paper.setAssigned(answerPaper.getAssigned()); // 保存paper与page到数据库 paperService.savePaperAndPages(paper, answerPaper.buildPageList(paperTypeBarcodeContents)); // 记录paper与batch关联关系 batchPaperService.update(batch, paper, student.getId(), paperNumber, false); // 创建student与paper的关联关系 studentPaperList.add(new StudentPaperEntity(student.getId(), paperNumber, paper.getId())); studentAssigned = studentAssigned || paper.getAssigned(); } // 更新批次统计数量,改到finish的时候更新 // updateAssignedCount(batch.getId()); // updateScanCount(batch.getId()); // 不开启实时审核,或者没有人工绑定的情况下,或者当前批次无需审核,直接更新考生扫描状态 if (VerifyStatus.CANCEL.equals(batch.getVerifyStatus()) || exam.getEnableSyncVerify() == null || !exam.getEnableSyncVerify() || !studentAssigned) { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().lock(); try { studentService.updateStudentAndPaper(user, student.getId(), studentPaperList); studentService.updateAssignedCheckCount(student.getId(), true); } finally { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().unlock(); } } return AnswerSaveVo.create(findStudentCountByBatch(batch.getId())); } private PaperEntity findOrCreatePaper(BatchEntity batch, StudentEntity student, Integer paperNumber, User user) { PaperEntity paper = null; // 尝试重用已有paper对象 BatchPaperEntity batchPaper = batchPaperService.findByBatchIdAndStudentIdAndPaperNumber(batch.getId(), student.getId(), paperNumber); if (batchPaper != null) { paper = paperService.getById(batchPaper.getPaperId()); } // 构造全新paper对象 if (paper == null) { paper = new PaperEntity(); paper.setCreateTime(System.currentTimeMillis()); paper.setCreatorId(user.getId()); } return paper; } private void updateBatchAssignedStudentData(User user, Long batchId, Long examId) { List batchPaperList = batchPaperService.findByBatchId(batchId); Map> studentMap = new HashMap<>(); for (BatchPaperEntity batchPaper : batchPaperList) { studentMap.computeIfAbsent(batchPaper.getStudentId(), key -> new ArrayList<>()).add(new StudentPaperEntity( batchPaper.getStudentId(), batchPaper.getPaperNumber(), batchPaper.getPaperId())); } for (Map.Entry> entry : studentMap.entrySet()) { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().lock(); try { studentService.updateStudentAndPaper(user, entry.getKey(), entry.getValue()); assignedCheckHistoryService.save(user.getId(), entry.getKey(), examId); studentService.updateAssignedCheckCount(entry.getKey(), false); } finally { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().unlock(); } } } @Transactional @Override public void verify(Long batchId, Boolean comfirm, User user) { BatchEntity entity = this.getById(batchId); if (entity == null) { throw new ParameterException("批次不存在"); } if (!VerifyStatus.WAITING.equals(entity.getVerifyStatus())) { throw new ParameterException("批次已被处理"); } if (comfirm) { updateBatchAssignedStudentData(user, batchId, entity.getExamId()); entity.setVerifyStatus(VerifyStatus.CONFIRM); } else { entity.setVerifyStatus(VerifyStatus.REJECT); } entity.setUpdateTime(System.currentTimeMillis()); this.saveOrUpdate(entity); } @Transactional @Override public BatchVerifyVo batchVerify(Long id) { BatchEntity entity = this.getById(id); if (entity == null) { throw new ParameterException("批次不存在"); } Integer count = findStudentCountByBatch(id); if (count == null || count == 0) { throw new ParameterException("该批次未上传图片"); } if (entity.getVerifyStatus() == null) { ExamEntity exam = examService.getById(entity.getExamId()); if (exam.getEnableSyncVerify() == null || !exam.getEnableSyncVerify()) { throw new ParameterException("考试未启用实时审核"); } entity.setVerifyStatus(VerifyStatus.WAITING); this.saveOrUpdate(entity); } BatchVerifyVo vo = new BatchVerifyVo(); vo.setStatus(entity.getVerifyStatus()); vo.setUpdateTime(entity.getUpdateTime()); return vo; } @Transactional @Override public BatchFinishVo batchFinish(Long id) { BatchEntity entity = this.getById(id); if (entity == null) { throw new ParameterException("批次不存在"); } checkBatchStatus(entity); ExamEntity exam = examService.getById(entity.getExamId()); if (exam.getScanByPackage() != null && exam.getScanByPackage()) { checkStudentCount(entity); } List saveList = new ArrayList(); if (exam.getImageCheckRatio() == 1) { List papers = batchPaperService.findByBatchId(id); for (BatchPaperEntity batchPaperEntity : papers) { batchPaperEntity.setNeedCheck(true); saveList.add(batchPaperEntity); } } else { List studentIds = batchPaperService.findStudentIdByBatchId(id); int checkCount = (int) (studentIds.size() * exam.getImageCheckRatio()); if (checkCount > 0) { Set checkIds = new HashSet(); while (checkIds.size() < checkCount) { Long studentId = studentIds.get(RandomUtils.nextInt(studentIds.size())); checkIds.add(studentId); } List papers = batchPaperService.findByBatchIdAndStudentIds(id, checkIds); for (BatchPaperEntity batchPaperEntity : papers) { batchPaperEntity.setNeedCheck(true); saveList.add(batchPaperEntity); } } } if (!saveList.isEmpty()) { batchPaperService.saveOrUpdateBatchByMultiId(saveList); entity.setCheckStatus(CheckStatus.WAITING); } entity.setStatus(BatchStatus.FINISH); this.saveOrUpdate(entity); updateAssignedCount(entity.getId()); updateScanCount(entity.getId()); BatchFinishVo vo = new BatchFinishVo(); vo.setStatus(entity.getStatus()); vo.setUpdateTime(System.currentTimeMillis()); return vo; } private void checkStudentCount(BatchEntity entity) { List papers = batchPaperService.findByBatchId(entity.getId()); List students = studentService.findByExamAndPackage(entity.getExamId(), entity.getPackageCode(), entity.getSubjectCode()); if (CollectionUtils.isEmpty(papers) && CollectionUtils.isEmpty(students)) { return; } if (CollectionUtils.isEmpty(papers) || CollectionUtils.isEmpty(students)) { throw new ParameterException("卷袋考生有未扫描"); } Set ps = new HashSet<>(); Set ss = new HashSet<>(); for (BatchPaperEntity p : papers) { ps.add(p.getStudentId()); } for (StudentEntity s : students) { ss.add(s.getId()); } if (ps.size() != ss.size()) { throw new ParameterException("卷袋考生有未扫描"); } for (Long p : ps) { if (!ss.contains(p)) { throw new ParameterException("卷袋考生有未扫描"); } } } @Transactional @Override public void updateScanCount(Long id) { baseMapper.updateScanCount(id); } @Transactional @Override public void updateAssignedCount(Long id) { baseMapper.updateAssignedCount(id); } @Override public PageResult batchQuery(BatchQueryDomain query) { IPage iPage = baseMapper.batchQueryPage(new Page<>(query.getPageNumber(), query.getPageSize()), query); // TODO 需要后续补充incomplete逻辑 return PageUtil.of(iPage); } @Override public List batchSummary(BatchQueryDomain query) { // TODO 需要后续补充incomplete逻辑 return baseMapper.batchSummary(query); } @Override public BatchDetailVo batchDetail(Long id) { BatchEntity batchEntity = this.getById(id); BatchDetailVo vo = new BatchDetailVo(); vo.update(batchEntity); Map map = new LinkedHashMap<>(); List list = baseMapper.batchDetailList(id); for (BatchDetailDO item : list) { BatchStudentVo detail = map.computeIfAbsent(item.getId(), key -> BatchStudentVo.build(item)); // 判断是否人工绑定 detail.setAssigned(detail.getAssigned() || item.getAssigned()); // 判断是否识别缺考 if (item.getAbsent() != null && item.getAbsent().getResult()) { detail.setOmrAbsent(true); } // 创建paper与page对象 detail.findOrCreatePaper(item).addPage(item); } vo.setStudents(new ArrayList<>(map.values())); return vo; } @Override public VerifyTaskVo getVerifyTask(Long examId, User user) { int retry = 1; VerifyTaskVo task = null; while (task == null) { List list = this.findUnVerify(examId, retry, 20, VerifyStatus.WAITING).getRecords(); if (list.isEmpty()) { break; } for (BatchEntity t : list) { if (this.applyVerifyTask(t, user.getAccount())) { task = toVerifyTaskVo(t); break; } } if (task == null) { retry++; } } if (task == null) { throw NotFoundExceptions.NO_SYNC_VERIFY_TASK; } return task; } private VerifyTaskVo toVerifyTaskVo(BatchEntity batch) { VerifyTaskVo vo = new VerifyTaskVo(); vo.setBatchId(batch.getId()); vo.setCreateTime(batch.getCreateTime()); vo.setDevice(batch.getDevice()); vo.setPackageCode(batch.getPackageCode()); Map studentMap = new LinkedHashMap<>(); List list = baseMapper.batchDetailList(batch.getId()); for (BatchDetailDO item : list) { VerifyStudentVo student = studentMap.computeIfAbsent(item.getId(), key -> VerifyStudentVo.build(item)); student.findOrCreatePaper(item).getPages().add(item.getSheetPath()); } vo.setStudents(new ArrayList<>(studentMap.values())); return vo; } @Override public boolean applyVerifyTask(BatchEntity t, String user) { TaskLock taskLock = TaskLockUtil.getVerifyTask(t.getExamId().toString()); boolean lock = taskLock.add(t.getId(), user); // 上锁失败直接返回 if (!lock) { return false; } // 重复校验任务状态 if (VerifyStatus.WAITING.equals(t.getVerifyStatus())) { return true; } else { taskLock.remove(t.getId().toString()); return false; } } @Override public void releaseVerifyTask(Long examId, String account) { TaskLock taskLock = TaskLockUtil.getVerifyTask(examId.toString()); taskLock.clear(account); } @Override public void releaseVerifyTask(BatchEntity t) { TaskLock taskLock = TaskLockUtil.getVerifyTask(t.getExamId().toString()); taskLock.remove(t.getId().toString()); } @Override public boolean hasAppliedVerifyTask(BatchEntity t, String user) { TaskLock taskLock = TaskLockUtil.getVerifyTask(t.getExamId().toString()); return taskLock.exist(t.getId(), user); } @Override public IPage findUnVerify(Long examId, int pageNumber, int pageSize, VerifyStatus status) { return this.baseMapper.findUnVerify(new Page<>(pageNumber, pageSize), examId, status); } @Override public List batchScanner(Long examId) { return baseMapper.batchScanner(examId); } @Override public List workload(WorkloadDomain domain) { Long examId = domain.getExamId(); Long startTime = domain.getStartTime(); Long endTime = domain.getEndTime(); if (examId == null) { throw new ParameterException("examId不能为空"); } return baseMapper.workload(examId, startTime, endTime); } @Override public Integer findStudentCountByBatch(Long batchId) { return baseMapper.findStudentCountByBatch(batchId); } @Override public BatchEntity findByPaperId(Long paperId) { return baseMapper.selectById(batchPaperService.findByPaperId(paperId).getBatchId()); } @Override public TaskStatusVo getCheckImageStatus(Long examId, User user) { TaskStatusVo vo = new TaskStatusVo(); vo.setFinishCount(this.getCheckCountByExamId(examId, CheckStatus.FINISH, user)); vo.setTodoCount(this.getCheckCountByExamId(examId, CheckStatus.WAITING, user)); return vo; } @Override public VerifyTaskVo getCheckImageTask(Long examId, User user) { int retry = 1; VerifyTaskVo task = null; while (task == null) { List list = this.findUnCheck(examId, retry, 20, BatchStatus.FINISH, CheckStatus.WAITING) .getRecords(); if (list.isEmpty()) { break; } for (BatchEntity t : list) { if (this.applyCheckImageTask(t, user.getAccount())) { task = toCheckTaskVo(t); break; } } if (task == null) { retry++; } } if (task == null) { throw NotFoundExceptions.NO_SYNC_VERIFY_TASK; } return task; } private VerifyTaskVo toCheckTaskVo(BatchEntity batch) { VerifyTaskVo vo = new VerifyTaskVo(); vo.setBatchId(batch.getId()); vo.setCreateTime(batch.getCreateTime()); vo.setDevice(batch.getDevice()); vo.setPackageCode(batch.getPackageCode()); Map studentMap = new LinkedHashMap<>(); List list = baseMapper.batchDetailListToCheck(batch.getId(), true); for (BatchDetailDO item : list) { VerifyStudentVo student = studentMap.computeIfAbsent(item.getId(), key -> VerifyStudentVo.build(item)); student.findOrCreatePaper(item).getPages().add(item.getSheetPath()); } vo.setStudents(new ArrayList<>(studentMap.values())); return vo; } @Override public VerifyTaskVo getHistoryCheckImageTask(Long examId, Long batchId, User user) { if (examId == null) { throw new ParameterException("examId不能为空"); } if (user.getId() == null) { throw new ParameterException("userId不能为空"); } BatchEntity batchEntity = this.baseMapper.getHistory(examId, batchId, user.getId(), CheckStatus.FINISH); if (batchEntity == null) { throw NotFoundExceptions.NO_CHECK_IMAGE_TASK; } VerifyTaskVo task = toCheckTaskVo(batchEntity); return task; } @Transactional @Override public CheckImageSubmitVo submitCheckImageTask(Long examId, Long batchId, User user) { BatchEntity entity = this.getById(batchId); if (entity == null) { throw new ParameterException("批次不存在"); } if (entity.getCheckStatus() == CheckStatus.FINISH) { throw new ParameterException("该批次已经被审核"); } entity.setCheckStatus(CheckStatus.FINISH); entity.setCheckImageTime(System.currentTimeMillis()); entity.setCheckImageUserId(user.getId()); this.saveOrUpdate(entity); this.releaseCheckImageTask(entity); CheckImageSubmitVo vo = new CheckImageSubmitVo(); vo.setBatchId(batchId); vo.setUpdateTime(entity.getUpdateTime()); vo.getStatus().setFinishCount(this.getCheckCountByExamId(examId, CheckStatus.FINISH, user)); vo.getStatus().setTodoCount(this.getCheckCountByExamId(examId, CheckStatus.WAITING, user)); return vo; } @Override public void releaseCheckImageTask(Long examId, User user) { TaskLock taskLock = TaskLockUtil.getCheckTask(examId.toString()); taskLock.clear(user.getAccount()); } @Override public int getCheckCountByExamId(Long examId, CheckStatus status, User user) { QueryWrapper wrapper = new QueryWrapper<>(); LambdaQueryWrapper lw = wrapper.lambda(); lw.eq(BatchEntity::getExamId, examId); lw.eq(BatchEntity::getCheckStatus, status); lw.eq(BatchEntity::getStatus, BatchStatus.FINISH); // 审核员只统计自身已完成的数量 if (status == CheckStatus.FINISH && user.getRole() == Role.AUDITOR) { lw.eq(BatchEntity::getCheckImageUserId, user.getId()); } return this.count(wrapper); } @Override public void releaseCheckImageTask(BatchEntity t) { TaskLock taskLock = TaskLockUtil.getCheckTask(t.getExamId().toString()); taskLock.remove(t.getId().toString()); } @Override public IPage findUnCheck(Long examId, int pageNumber, int pageSize, BatchStatus batchStatus, CheckStatus checkStatus) { return this.baseMapper.findUnCheck(new Page<>(pageNumber, pageSize), examId, batchStatus, checkStatus); } @Override public boolean applyCheckImageTask(BatchEntity t, String user) { TaskLock taskLock = TaskLockUtil.getCheckTask(t.getExamId().toString()); boolean lock = taskLock.add(t.getId(), user); // 上锁失败直接返回 if (!lock) { return false; } // 重复校验任务状态 if (CheckStatus.WAITING.equals(t.getCheckStatus())) { return true; } else { taskLock.remove(t.getId().toString()); return false; } } @Override @Transactional public void batchVerifyCancel(User user, Long examId) { QueryWrapper wrapper = new QueryWrapper<>(); LambdaQueryWrapper lw = wrapper.lambda(); lw.eq(BatchEntity::getExamId, examId); lw.eq(BatchEntity::getVerifyStatus, VerifyStatus.WAITING); List list = this.list(lw); for (BatchEntity batch : list) { List batchPaperList = batchPaperService.findByBatchId(batch.getId()); Map> studentMap = new HashMap<>(); for (BatchPaperEntity batchPaper : batchPaperList) { studentMap.computeIfAbsent(batchPaper.getStudentId(), key -> new ArrayList<>()) .add(new StudentPaperEntity(batchPaper.getStudentId(), batchPaper.getPaperNumber(), batchPaper.getPaperId())); } for (Map.Entry> entry : studentMap.entrySet()) { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().lock(); try { studentService.updateStudentAndPaper(user, entry.getKey(), entry.getValue()); studentService.updateAssignedCheckCount(entry.getKey(), true); } finally { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().unlock(); } } batch.setVerifyStatus(VerifyStatus.CANCEL); } this.updateBatchById(list); } @Override @Transactional public void updateBatchCount() { QueryWrapper wrapper = new QueryWrapper<>(); LambdaQueryWrapper lw = wrapper.lambda(); lw.eq(BatchEntity::getStatus, BatchStatus.INIT); List list = this.list(lw); for (BatchEntity batch : list) { updateAssignedCount(batch.getId()); updateScanCount(batch.getId()); } } @Override public List subjectList(BatchSubjectQuery req) { if (req.getExamId() == null) { throw new ParameterException("考试id不能为空"); } return this.baseMapper.subjectList(req); } @Override public List deviceList(BatchDeviceQuery req) { if (req.getExamId() == null) { throw new ParameterException("考试id不能为空"); } if (StringUtils.isBlank(req.getSubjectCode())) { throw new ParameterException("科目代码不能为空"); } return this.baseMapper.deviceList(req); } @Override public List batchList(BatchQuery req) { if (req.getExamId() == null) { throw new ParameterException("考试id不能为空"); } if (StringUtils.isBlank(req.getSubjectCode())) { throw new ParameterException("科目代码不能为空"); } if (StringUtils.isBlank(req.getDevice())) { throw new ParameterException("设备不能为空"); } return this.baseMapper.batchList(req); } @Override public List studentList(Long batchId) { if (batchId == null) { throw new ParameterException("批次id不能为空"); } return this.baseMapper.studentList(batchId); } }