package cn.com.qmth.scancentral.service.impl; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import javax.validation.constraints.NotNull; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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 com.qmth.boot.core.fss.store.FileStore; import cn.com.qmth.scancentral.bean.MismatchQueryDomain; import cn.com.qmth.scancentral.bean.MismatchToggleDomain; import cn.com.qmth.scancentral.bean.User; import cn.com.qmth.scancentral.bean.papermigrate.PaperMigrateDomain; import cn.com.qmth.scancentral.bean.papermigrate.PaperMigratePage; import cn.com.qmth.scancentral.dao.PaperDao; import cn.com.qmth.scancentral.entity.*; import cn.com.qmth.scancentral.enums.LockType; import cn.com.qmth.scancentral.enums.UploadStatus; import cn.com.qmth.scancentral.service.*; import cn.com.qmth.scancentral.util.PageUtil; import cn.com.qmth.scancentral.vo.MismatchToggleVo; import cn.com.qmth.scancentral.vo.PaperMigrateVo; import cn.com.qmth.scancentral.vo.UriVo; import cn.com.qmth.scancentral.vo.answerquery.StudentPaperVo; import cn.com.qmth.scancentral.vo.mismatchquery.MismatchQueryVo; import cn.com.qmth.scancentral.vo.mismatchquery.PaperPageDO; import cn.com.qmth.scancentral.vo.paper.PaperCetVo; import cn.com.qmth.scancentral.vo.paper.PaperVo; @Service public class PaperServiceImpl extends ServiceImpl implements PaperService { protected static Logger log = LoggerFactory.getLogger(PaperServiceImpl.class); @Autowired private ExamService examService; @Autowired private StudentPaperService studentPaperService; @Autowired private BatchPaperService batchPaperService; @Autowired private AnswerCardService answerCardService; @Autowired private StudentService studentService; @Autowired private ConcurrentService concurrentService; @Autowired private FileStore fileStore; @Autowired private FileService fileService; @Autowired private FilePropertyService filePropertyService; @Autowired private PaperPageService paperPageService; @Autowired private SubjectService subjectService; @Autowired private AnswerCardSubjectService answerCardSubjectService; /** * 全量更新paper和所属的page集合,并刷新paper相关属性 * * @param paper * @param pages */ @Override @Transactional public void savePaperAndPages(@NotNull PaperEntity paper, @NotNull List pages) { // 与卡格式校验 AnswerCardEntity answerCard = answerCardService.findByExamAndNumber(paper.getExamId(), paper.getCardNumber()); if (answerCard == null) { throw new ParameterException("卡格式不存在, cardNumber=" + paper.getCardNumber()); } if (pages.size() != (answerCard.getSinglePage() ? 1 : 2)) { throw new ParameterException("paper.page数量与卡格式不符, cardNumber=" + paper.getCardNumber()); } // 更新paper属性 paper.setPageCount(pages.size()); paper.setQuestionFilled(false); // 判断是否客观题已填涂 for (PaperPageEntity page : pages) { paper.setQuestionFilled(paper.getQuestionFilled() || page.isQuestionFilled()); } // 保存paper数据 saveOrUpdate(paper); for (PaperPageEntity page : pages) { // 验证原图与裁切图已上传 try { if (!fileStore.exist(page.getSheetPath())) { throw new ParameterException("原图不存在:" + page.getSheetPath()); } } catch (IOException e) { throw new ParameterException("图片访问失败:" + page.getSheetPath()); } if (CollectionUtils.isNotEmpty(page.getSlicePath())) { for (String spath : page.getSlicePath()) { try { if (!fileStore.exist(spath)) { throw new ParameterException("裁切图不存在:" + spath); } } catch (IOException e) { throw new ParameterException("图片访问失败:" + spath); } } } page.setPaperId(paper.getId()); } // 保存page数据 paperPageService.saveOrUpdateBatchByMultiId(pages); } @Override public List findStudentPaper(Long studentId) { return baseMapper.findStudentPaper(studentId); } @Override public int getMismatchCountByExam(Long examId) { QueryWrapper wrapper = new QueryWrapper<>(); LambdaQueryWrapper lw = wrapper.lambda(); lw.eq(PaperEntity::getExamId, examId); lw.eq(PaperEntity::getMismatch, true); return this.count(wrapper); } @Transactional @Override public MismatchToggleVo mismatchToggle(MismatchToggleDomain domain) { PaperEntity paper = this.getById(domain.getPaperId()); if (paper == null) { throw new ParameterException("未找到paper信息"); } updateMismatch(domain.getPaperId(), domain.getEnable()); return MismatchToggleVo.create(paper.getId(), domain.getEnable()); } @Override public PageResult mismatchQuery(MismatchQueryDomain query) { IPage iPage = baseMapper .mismatchQueryPage(new Page<>(query.getPageNumber(), query.getPageSize()), query); for (MismatchQueryVo vo : iPage.getRecords()) { List paperPageList = baseMapper.findMismatchByBatchIdAndStudentId(vo.getBatchId(), vo.getStudentId()); for (PaperPageDO paperPage : paperPageList) { vo.addPaperPage(paperPage); } } return PageUtil.of(iPage); } @Override public List listByStudentIds(List paramList) { return baseMapper.listByStudentIds(paramList); } @Transactional @Override public UriVo adminSliceUpload(Long paperId, Integer pageIndex, Integer index, MultipartFile multipartFile, String md5) { PaperEntity paper = this.getById(paperId); if (paper == null) { throw new ParameterException("paper信息未找到"); } if (pageIndex < 1 || pageIndex > paper.getPageCount()) { throw new ParameterException("pageIndex需要大于0小于" + paper.getPageCount()); } if (index <= 0) { throw new ParameterException("page.index必须大于0"); } BatchPaperEntity bp = batchPaperService.findByPaperId(paperId); if (bp == null) { throw new ParameterException("paper未找到所属批次"); } StudentEntity student = studentService.getById(bp.getStudentId()); try { String path = fileService.adminUploadSlice(multipartFile.getInputStream(), md5, paper.getExamId(), student.getExamNumber(), paper.getNumber(), pageIndex, index); filePropertyService.save(paper.getExamId(), path, md5, multipartFile.getSize()); return UriVo.create(path); } catch (Exception e) { log.error("裁切图上传失败,paperId=" + paperId + ", pageIndex=" + pageIndex + ", index=" + index, e); throw new StatusException("裁切图上传失败", e); } } @Transactional @Override public UriVo adminSliceUpdate(Long paperId, Integer pageIndex, Integer index, MultipartFile multipartFile, String md5) { PaperEntity paper = this.getById(paperId); if (paper == null) { throw new ParameterException("paper信息未找到"); } PaperPageEntity page = paperPageService.findPaperIdAndIndex(paperId, pageIndex); if (page == null) { throw new ParameterException("page信息未找到"); } if (index <= 0) { throw new ParameterException("page.index必须大于0"); } if (CollectionUtils.isEmpty(page.getSlicePath()) || page.getSlicePath().size() < index || StringUtils.isBlank(page.getSlicePath().get(index - 1))) { throw new ParameterException("未找到对应的裁切图"); } StudentPaperEntity sp = studentPaperService.findByPaperId(paperId); if (sp == null) { throw new ParameterException("paper未绑定考生"); } StudentEntity student = studentService.getById(sp.getStudentId()); concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().lock(); try { String path = fileService.adminUploadSlice(multipartFile.getInputStream(), md5, paper.getExamId(), student.getExamNumber(), paper.getNumber(), pageIndex, index); filePropertyService.save(paper.getExamId(), path, md5, multipartFile.getSize()); page.getSlicePath().set(index - 1, path); paperPageService.updateByMultiId(page); // 更新考生文件上传状态 studentService.updateFileUploadStatus(sp.getStudentId(), UploadStatus.WAITING_UPLOAD); return UriVo.create(path); } catch (Exception e) { log.error("裁切图更新失败,paperId=" + paperId + ", pageIndex=" + pageIndex + ", index=" + index, e); throw new StatusException("裁切图更新失败", e); } finally { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().unlock(); } } @Transactional @Override public PaperMigrateVo paperMigrate(User user, PaperMigrateDomain domain) { // 查找修改paper PaperEntity paper = getById(domain.getPaperId()); if (paper == null) { throw new ParameterException("题卡信息未找到, paperId=" + domain.getPaperId()); } // 校验卡格式 AnswerCardEntity answerCard = answerCardService.findByExamAndNumber(domain.getExamId(), domain.getCardNumber()); if (answerCard == null) { throw new ParameterException("卡格式信息未找到"); } // 查找目标考生 StudentEntity student = studentService.findByExamAndSubjectCodeAndExamNumber(domain.getExamId(), domain.getSubjectCode(), domain.getExamNumber()); if (student == null) { throw new ParameterException("考生信息未找到, 准考证号=" + domain.getExamNumber()); } // 目标考生已扫描时,输入卡格式需要保持一致 if (student.getCardNumber() != null && !student.getCardNumber().equals(domain.getCardNumber())) { throw new ParameterException("卡格式与目标考生不一致"); } // 验证卡格式所属科目 boolean allowSubject = answerCardSubjectService.checkSubject(answerCard.getExamId(), answerCard.getNumber(), student.getSubjectCode()); if (!allowSubject) { throw new ParameterException("卡格式与考生科目不一致"); } // cardNumber或paperNumber有变化,都需要重新识别 if ((!paper.getNumber().equals(domain.getPaperNumber()) || !paper.getCardNumber().equals(domain.getCardNumber())) && CollectionUtils.isEmpty(domain.getPages())) { throw new ParameterException("卡格式有变化需要重新识别, pages信息不能为空"); } ExamEntity exam = examService.getById(paper.getExamId()); // 指定paper现有绑定考生 StudentEntity previousStudent = null; StudentPaperEntity previousStudentPaper = studentPaperService.findByPaperId(domain.getPaperId()); if (previousStudentPaper != null) { // 考生没变,张数也没变 if (previousStudentPaper.getStudentId().equals(student.getId()) && previousStudentPaper.getPaperNumber().equals(domain.getPaperNumber())) { throw new ParameterException("该题卡已绑定该考生"); } // 考生ID变了才需要获取原考生 if (!previousStudentPaper.getStudentId().equals(student.getId())) { previousStudent = studentService.getById(previousStudentPaper.getStudentId()); } } if (previousStudent != null) { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + previousStudent.getId()).writeLock().lock(); } concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().lock(); try { if (previousStudentPaper != null) { // 解绑paper和原有考生 if (previousStudent != null) { List studentPaperList = studentPaperService .findByStudentId(previousStudent.getId()).stream() .filter(entity -> !entity.getPaperId().equals(previousStudentPaper.getPaperId())) .collect(Collectors.toList()); studentService.updateStudentAndPaper(user, previousStudent.getId(), studentPaperList); studentService.updateAssignedCheckCount(previousStudent.getId(), true); } // 考生没变,直接解绑paper else { studentPaperService.deleteByMultiId(previousStudentPaper); } } // 更新paper属性 paper.setCardNumber(domain.getCardNumber()); paper.setNumber(domain.getPaperNumber()); // 修改的paper强制置为人工绑定 paper.setAssigned(true); // 原有mismatch标记需要清除 paper.setMismatch(false); paper.setUpdaterId(user.getId()); paper.setUpdateTime(System.currentTimeMillis()); if (CollectionUtils.isNotEmpty(domain.getPages())) { List paperTypeBarcodeContents = exam.getPaperTypeBarcodeContent(); SubjectEntity subject = subjectService.findByExamIdAndCode(student.getExamId(), student.getSubjectCode()); if (subject.getPaperTypeBarcodeContent() != null && !subject.getPaperTypeBarcodeContent().isEmpty()) { paperTypeBarcodeContents = subject.getPaperTypeBarcodeContent(); } // 重新识别时更新所有page List pages = new ArrayList<>(); for (PaperMigratePage page : domain.getPages()) { pages.add(page.update(paperPageService.findPaperIdAndIndex(paper.getId(), page.getIndex()), paperTypeBarcodeContents)); } savePaperAndPages(paper, pages); } else { saveOrUpdate(paper); } // 查找目标考生现有绑定paper boolean exist = false; List currentStudentPaperList = studentPaperService.findByStudentId(student.getId()); for (StudentPaperEntity current : currentStudentPaperList) { if (current.getPaperNumber().equals(paper.getNumber())) { exist = true; updateMismatch(current.getPaperId(), true); current.setPaperId(paper.getId()); } } if (!exist) { currentStudentPaperList.add(new StudentPaperEntity(student.getId(), paper.getNumber(), paper.getId())); } // 更新目标考生状态 studentService.updateStudentAndPaper(user, student.getId(), currentStudentPaperList); studentService.updateAssignedCheckCount(student.getId(), true); // 返回数据结果 return PaperMigrateVo.create(domain.getPaperId()); } finally { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().unlock(); if (previousStudent != null) { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + previousStudent.getId()).writeLock() .unlock(); } } } @Override public int getCountByExamAndCardNumber(Long examId, Integer number) { LambdaQueryWrapper lw = new LambdaQueryWrapper<>(); lw.eq(PaperEntity::getExamId, examId); lw.eq(PaperEntity::getCardNumber, number); return this.count(lw); } @Override @Transactional public boolean updateMismatch(Long id, boolean mismatch) { LambdaUpdateWrapper lw = new LambdaUpdateWrapper<>(); lw.set(PaperEntity::getMismatch, mismatch); lw.eq(PaperEntity::getId, id); return this.update(lw); } @Transactional @Override public UriVo adminSheetUpdate(Long paperId, Integer pageIndex, MultipartFile multipartFile, String md5) { PaperEntity paper = this.getById(paperId); if (paper == null) { throw new ParameterException("paper信息未找到"); } PaperPageEntity page = paperPageService.findPaperIdAndIndex(paperId, pageIndex); if (page == null) { throw new ParameterException("page信息未找到"); } if (StringUtils.isBlank(page.getSheetPath())) { throw new ParameterException("未找到对应的原图"); } StudentPaperEntity sp = studentPaperService.findByPaperId(paperId); if (sp == null) { throw new ParameterException("paper未绑定考生"); } StudentEntity student = studentService.getById(sp.getStudentId()); concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().lock(); try { String path = fileService.adminUploadSheet(multipartFile.getInputStream(), md5, paper.getExamId(), student.getExamNumber(), paper.getNumber(), pageIndex); filePropertyService.save(paper.getExamId(), path, md5, multipartFile.getSize()); page.setSheetPath(path); paperPageService.updateByMultiId(page); // 更新考生文件上传状态 studentService.updateFileUploadStatus(sp.getStudentId(), UploadStatus.WAITING_UPLOAD); return UriVo.create(path); } catch (Exception e) { log.error("原图更新失败,paperId=" + paperId + ", pageIndex=" + pageIndex, e); throw new StatusException("原图更新失败", e); } finally { concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().unlock(); } } @Override public List findByStudentIds(List studentIds) { return this.baseMapper.findByStudentIds(studentIds); } // @Override // @Transactional // public void updatePaperAssignedSuspect(Long id, Integer number, Boolean assignedSuspect) { // StudentPaperEntity sp = studentPaperService.findByStudentIdAndPaperNumber(id, number); // if (sp == null) { // throw new ParameterException("未找到绑定扫描结果"); // } // LambdaUpdateWrapper lw = new LambdaUpdateWrapper<>(); // lw.set(PaperEntity::getAssignedSuspect, assignedSuspect); // lw.eq(PaperEntity::getId, sp.getPaperId()); // this.update(lw); // } @Override public List listByBatchIdAndStudentId(Long batchId, Long studentId) { return this.baseMapper.listByBatchIdAndStudentId(batchId, studentId); } }