PaperServiceImpl.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. package cn.com.qmth.scancentral.service.impl;
  2. import java.io.IOException;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.stream.Collectors;
  6. import javax.validation.constraints.NotNull;
  7. import org.apache.commons.collections4.CollectionUtils;
  8. import org.apache.commons.lang3.StringUtils;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import org.springframework.web.multipart.MultipartFile;
  15. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  16. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  17. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  18. import com.baomidou.mybatisplus.core.metadata.IPage;
  19. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  20. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  21. import com.qmth.boot.core.collection.PageResult;
  22. import com.qmth.boot.core.concurrent.service.ConcurrentService;
  23. import com.qmth.boot.core.exception.ParameterException;
  24. import com.qmth.boot.core.exception.StatusException;
  25. import com.qmth.boot.core.fss.store.FileStore;
  26. import cn.com.qmth.scancentral.bean.MismatchQueryDomain;
  27. import cn.com.qmth.scancentral.bean.MismatchToggleDomain;
  28. import cn.com.qmth.scancentral.bean.User;
  29. import cn.com.qmth.scancentral.bean.papermigrate.PaperMigrateDomain;
  30. import cn.com.qmth.scancentral.bean.papermigrate.PaperMigratePage;
  31. import cn.com.qmth.scancentral.dao.PaperDao;
  32. import cn.com.qmth.scancentral.entity.*;
  33. import cn.com.qmth.scancentral.enums.LockType;
  34. import cn.com.qmth.scancentral.enums.UploadStatus;
  35. import cn.com.qmth.scancentral.service.*;
  36. import cn.com.qmth.scancentral.util.PageUtil;
  37. import cn.com.qmth.scancentral.vo.MismatchToggleVo;
  38. import cn.com.qmth.scancentral.vo.PaperMigrateVo;
  39. import cn.com.qmth.scancentral.vo.UriVo;
  40. import cn.com.qmth.scancentral.vo.answerquery.StudentPaperVo;
  41. import cn.com.qmth.scancentral.vo.mismatchquery.MismatchQueryVo;
  42. import cn.com.qmth.scancentral.vo.mismatchquery.PaperPageDO;
  43. import cn.com.qmth.scancentral.vo.paper.PaperCetVo;
  44. import cn.com.qmth.scancentral.vo.paper.PaperVo;
  45. @Service
  46. public class PaperServiceImpl extends ServiceImpl<PaperDao, PaperEntity> implements PaperService {
  47. protected static Logger log = LoggerFactory.getLogger(PaperServiceImpl.class);
  48. @Autowired
  49. private ExamService examService;
  50. @Autowired
  51. private StudentPaperService studentPaperService;
  52. @Autowired
  53. private BatchPaperService batchPaperService;
  54. @Autowired
  55. private AnswerCardService answerCardService;
  56. @Autowired
  57. private StudentService studentService;
  58. @Autowired
  59. private ConcurrentService concurrentService;
  60. @Autowired
  61. private FileStore fileStore;
  62. @Autowired
  63. private FileService fileService;
  64. @Autowired
  65. private FilePropertyService filePropertyService;
  66. @Autowired
  67. private PaperPageService paperPageService;
  68. @Autowired
  69. private SubjectService subjectService;
  70. @Autowired
  71. private AnswerCardSubjectService answerCardSubjectService;
  72. /**
  73. * 全量更新paper和所属的page集合,并刷新paper相关属性
  74. *
  75. * @param paper
  76. * @param pages
  77. */
  78. @Override
  79. @Transactional
  80. public void savePaperAndPages(@NotNull PaperEntity paper, @NotNull List<PaperPageEntity> pages) {
  81. // 与卡格式校验
  82. AnswerCardEntity answerCard = answerCardService.findByExamAndNumber(paper.getExamId(), paper.getCardNumber());
  83. if (answerCard == null) {
  84. throw new ParameterException("卡格式不存在, cardNumber=" + paper.getCardNumber());
  85. }
  86. if (pages.size() != (answerCard.getSinglePage() ? 1 : 2)) {
  87. throw new ParameterException("paper.page数量与卡格式不符, cardNumber=" + paper.getCardNumber());
  88. }
  89. // 更新paper属性
  90. paper.setPageCount(pages.size());
  91. paper.setQuestionFilled(false);
  92. // 判断是否客观题已填涂
  93. for (PaperPageEntity page : pages) {
  94. paper.setQuestionFilled(paper.getQuestionFilled() || page.isQuestionFilled());
  95. }
  96. // 保存paper数据
  97. saveOrUpdate(paper);
  98. for (PaperPageEntity page : pages) {
  99. // 验证原图与裁切图已上传
  100. try {
  101. if (!fileStore.exist(page.getSheetPath())) {
  102. throw new ParameterException("原图不存在:" + page.getSheetPath());
  103. }
  104. } catch (IOException e) {
  105. throw new ParameterException("图片访问失败:" + page.getSheetPath());
  106. }
  107. if (CollectionUtils.isNotEmpty(page.getSlicePath())) {
  108. for (String spath : page.getSlicePath()) {
  109. try {
  110. if (!fileStore.exist(spath)) {
  111. throw new ParameterException("裁切图不存在:" + spath);
  112. }
  113. } catch (IOException e) {
  114. throw new ParameterException("图片访问失败:" + spath);
  115. }
  116. }
  117. }
  118. page.setPaperId(paper.getId());
  119. }
  120. // 保存page数据
  121. paperPageService.saveOrUpdateBatchByMultiId(pages);
  122. }
  123. @Override
  124. public List<PaperVo> findStudentPaper(Long studentId) {
  125. return baseMapper.findStudentPaper(studentId);
  126. }
  127. @Override
  128. public int getMismatchCountByExam(Long examId) {
  129. QueryWrapper<PaperEntity> wrapper = new QueryWrapper<>();
  130. LambdaQueryWrapper<PaperEntity> lw = wrapper.lambda();
  131. lw.eq(PaperEntity::getExamId, examId);
  132. lw.eq(PaperEntity::getMismatch, true);
  133. return this.count(wrapper);
  134. }
  135. @Transactional
  136. @Override
  137. public MismatchToggleVo mismatchToggle(MismatchToggleDomain domain) {
  138. PaperEntity paper = this.getById(domain.getPaperId());
  139. if (paper == null) {
  140. throw new ParameterException("未找到paper信息");
  141. }
  142. updateMismatch(domain.getPaperId(), domain.getEnable());
  143. return MismatchToggleVo.create(paper.getId(), domain.getEnable());
  144. }
  145. @Override
  146. public PageResult<MismatchQueryVo> mismatchQuery(MismatchQueryDomain query) {
  147. IPage<MismatchQueryVo> iPage = baseMapper
  148. .mismatchQueryPage(new Page<>(query.getPageNumber(), query.getPageSize()), query);
  149. for (MismatchQueryVo vo : iPage.getRecords()) {
  150. List<PaperPageDO> paperPageList = baseMapper.findMismatchByBatchIdAndStudentId(vo.getBatchId(),
  151. vo.getStudentId());
  152. for (PaperPageDO paperPage : paperPageList) {
  153. vo.addPaperPage(paperPage);
  154. }
  155. }
  156. return PageUtil.of(iPage);
  157. }
  158. @Override
  159. public List<StudentPaperVo> listByStudentIds(List<Long> paramList) {
  160. return baseMapper.listByStudentIds(paramList);
  161. }
  162. @Transactional
  163. @Override
  164. public UriVo adminSliceUpload(Long paperId, Integer pageIndex, Integer index, MultipartFile multipartFile,
  165. String md5) {
  166. PaperEntity paper = this.getById(paperId);
  167. if (paper == null) {
  168. throw new ParameterException("paper信息未找到");
  169. }
  170. if (pageIndex < 1 || pageIndex > paper.getPageCount()) {
  171. throw new ParameterException("pageIndex需要大于0小于" + paper.getPageCount());
  172. }
  173. if (index <= 0) {
  174. throw new ParameterException("page.index必须大于0");
  175. }
  176. BatchPaperEntity bp = batchPaperService.findByPaperId(paperId);
  177. if (bp == null) {
  178. throw new ParameterException("paper未找到所属批次");
  179. }
  180. StudentEntity student = studentService.getById(bp.getStudentId());
  181. try {
  182. String path = fileService.adminUploadSlice(multipartFile.getInputStream(), md5, paper.getExamId(),
  183. student.getExamNumber(), paper.getNumber(), pageIndex, index);
  184. filePropertyService.save(paper.getExamId(), path, md5, multipartFile.getSize());
  185. return UriVo.create(path);
  186. } catch (Exception e) {
  187. log.error("裁切图上传失败,paperId=" + paperId + ", pageIndex=" + pageIndex + ", index=" + index, e);
  188. throw new StatusException("裁切图上传失败", e);
  189. }
  190. }
  191. @Transactional
  192. @Override
  193. public UriVo adminSliceUpdate(Long paperId, Integer pageIndex, Integer index, MultipartFile multipartFile,
  194. String md5) {
  195. PaperEntity paper = this.getById(paperId);
  196. if (paper == null) {
  197. throw new ParameterException("paper信息未找到");
  198. }
  199. PaperPageEntity page = paperPageService.findPaperIdAndIndex(paperId, pageIndex);
  200. if (page == null) {
  201. throw new ParameterException("page信息未找到");
  202. }
  203. if (index <= 0) {
  204. throw new ParameterException("page.index必须大于0");
  205. }
  206. if (CollectionUtils.isEmpty(page.getSlicePath()) || page.getSlicePath().size() < index
  207. || StringUtils.isBlank(page.getSlicePath().get(index - 1))) {
  208. throw new ParameterException("未找到对应的裁切图");
  209. }
  210. StudentPaperEntity sp = studentPaperService.findByPaperId(paperId);
  211. if (sp == null) {
  212. throw new ParameterException("paper未绑定考生");
  213. }
  214. StudentEntity student = studentService.getById(sp.getStudentId());
  215. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().lock();
  216. try {
  217. String path = fileService.adminUploadSlice(multipartFile.getInputStream(), md5, paper.getExamId(),
  218. student.getExamNumber(), paper.getNumber(), pageIndex, index);
  219. filePropertyService.save(paper.getExamId(), path, md5, multipartFile.getSize());
  220. page.getSlicePath().set(index - 1, path);
  221. paperPageService.updateByMultiId(page);
  222. // 更新考生文件上传状态
  223. studentService.updateFileUploadStatus(sp.getStudentId(), UploadStatus.WAITING_UPLOAD);
  224. return UriVo.create(path);
  225. } catch (Exception e) {
  226. log.error("裁切图更新失败,paperId=" + paperId + ", pageIndex=" + pageIndex + ", index=" + index, e);
  227. throw new StatusException("裁切图更新失败", e);
  228. } finally {
  229. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().unlock();
  230. }
  231. }
  232. @Transactional
  233. @Override
  234. public PaperMigrateVo paperMigrate(User user, PaperMigrateDomain domain) {
  235. // 查找修改paper
  236. PaperEntity paper = getById(domain.getPaperId());
  237. if (paper == null) {
  238. throw new ParameterException("题卡信息未找到, paperId=" + domain.getPaperId());
  239. }
  240. // 校验卡格式
  241. AnswerCardEntity answerCard = answerCardService.findByExamAndNumber(domain.getExamId(), domain.getCardNumber());
  242. if (answerCard == null) {
  243. throw new ParameterException("卡格式信息未找到");
  244. }
  245. // 查找目标考生
  246. StudentEntity student = studentService.findByExamAndSubjectCodeAndExamNumber(domain.getExamId(),
  247. domain.getSubjectCode(), domain.getExamNumber());
  248. if (student == null) {
  249. throw new ParameterException("考生信息未找到, 准考证号=" + domain.getExamNumber());
  250. }
  251. // 目标考生已扫描时,输入卡格式需要保持一致
  252. if (student.getCardNumber() != null && !student.getCardNumber().equals(domain.getCardNumber())) {
  253. throw new ParameterException("卡格式与目标考生不一致");
  254. }
  255. // 验证卡格式所属科目
  256. boolean allowSubject = answerCardSubjectService.checkSubject(answerCard.getExamId(), answerCard.getNumber(),
  257. student.getSubjectCode());
  258. if (!allowSubject) {
  259. throw new ParameterException("卡格式与考生科目不一致");
  260. }
  261. // cardNumber或paperNumber有变化,都需要重新识别
  262. if ((!paper.getNumber().equals(domain.getPaperNumber())
  263. || !paper.getCardNumber().equals(domain.getCardNumber()))
  264. && CollectionUtils.isEmpty(domain.getPages())) {
  265. throw new ParameterException("卡格式有变化需要重新识别, pages信息不能为空");
  266. }
  267. ExamEntity exam = examService.getById(paper.getExamId());
  268. // 指定paper现有绑定考生
  269. StudentEntity previousStudent = null;
  270. StudentPaperEntity previousStudentPaper = studentPaperService.findByPaperId(domain.getPaperId());
  271. if (previousStudentPaper != null) {
  272. // 考生没变,张数也没变
  273. if (previousStudentPaper.getStudentId().equals(student.getId())
  274. && previousStudentPaper.getPaperNumber().equals(domain.getPaperNumber())) {
  275. throw new ParameterException("该题卡已绑定该考生");
  276. }
  277. // 考生ID变了才需要获取原考生
  278. if (!previousStudentPaper.getStudentId().equals(student.getId())) {
  279. previousStudent = studentService.getById(previousStudentPaper.getStudentId());
  280. }
  281. }
  282. if (previousStudent != null) {
  283. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + previousStudent.getId()).writeLock().lock();
  284. }
  285. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().lock();
  286. try {
  287. if (previousStudentPaper != null) {
  288. // 解绑paper和原有考生
  289. if (previousStudent != null) {
  290. List<StudentPaperEntity> studentPaperList = studentPaperService
  291. .findByStudentId(previousStudent.getId()).stream()
  292. .filter(entity -> !entity.getPaperId().equals(previousStudentPaper.getPaperId()))
  293. .collect(Collectors.toList());
  294. studentService.updateStudentAndPaper(user, previousStudent.getId(), studentPaperList);
  295. studentService.updateAssignedCheckCount(previousStudent.getId(), true);
  296. }
  297. // 考生没变,直接解绑paper
  298. else {
  299. studentPaperService.deleteByMultiId(previousStudentPaper);
  300. }
  301. }
  302. // 更新paper属性
  303. paper.setCardNumber(domain.getCardNumber());
  304. paper.setNumber(domain.getPaperNumber());
  305. // 修改的paper强制置为人工绑定
  306. paper.setAssigned(true);
  307. // 原有mismatch标记需要清除
  308. paper.setMismatch(false);
  309. paper.setUpdaterId(user.getId());
  310. paper.setUpdateTime(System.currentTimeMillis());
  311. if (CollectionUtils.isNotEmpty(domain.getPages())) {
  312. List<String> paperTypeBarcodeContents = exam.getPaperTypeBarcodeContent();
  313. SubjectEntity subject = subjectService.findByExamIdAndCode(student.getExamId(),
  314. student.getSubjectCode());
  315. if (subject.getPaperTypeBarcodeContent() != null && !subject.getPaperTypeBarcodeContent().isEmpty()) {
  316. paperTypeBarcodeContents = subject.getPaperTypeBarcodeContent();
  317. }
  318. // 重新识别时更新所有page
  319. List<PaperPageEntity> pages = new ArrayList<>();
  320. for (PaperMigratePage page : domain.getPages()) {
  321. pages.add(page.update(paperPageService.findPaperIdAndIndex(paper.getId(), page.getIndex()),
  322. paperTypeBarcodeContents));
  323. }
  324. savePaperAndPages(paper, pages);
  325. } else {
  326. saveOrUpdate(paper);
  327. }
  328. // 查找目标考生现有绑定paper
  329. boolean exist = false;
  330. List<StudentPaperEntity> currentStudentPaperList = studentPaperService.findByStudentId(student.getId());
  331. for (StudentPaperEntity current : currentStudentPaperList) {
  332. if (current.getPaperNumber().equals(paper.getNumber())) {
  333. exist = true;
  334. updateMismatch(current.getPaperId(), true);
  335. current.setPaperId(paper.getId());
  336. }
  337. }
  338. if (!exist) {
  339. currentStudentPaperList.add(new StudentPaperEntity(student.getId(), paper.getNumber(), paper.getId()));
  340. }
  341. // 更新目标考生状态
  342. studentService.updateStudentAndPaper(user, student.getId(), currentStudentPaperList);
  343. studentService.updateAssignedCheckCount(student.getId(), true);
  344. // 返回数据结果
  345. return PaperMigrateVo.create(domain.getPaperId());
  346. } finally {
  347. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().unlock();
  348. if (previousStudent != null) {
  349. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + previousStudent.getId()).writeLock()
  350. .unlock();
  351. }
  352. }
  353. }
  354. @Override
  355. public int getCountByExamAndCardNumber(Long examId, Integer number) {
  356. LambdaQueryWrapper<PaperEntity> lw = new LambdaQueryWrapper<>();
  357. lw.eq(PaperEntity::getExamId, examId);
  358. lw.eq(PaperEntity::getCardNumber, number);
  359. return this.count(lw);
  360. }
  361. @Override
  362. @Transactional
  363. public boolean updateMismatch(Long id, boolean mismatch) {
  364. LambdaUpdateWrapper<PaperEntity> lw = new LambdaUpdateWrapper<>();
  365. lw.set(PaperEntity::getMismatch, mismatch);
  366. lw.eq(PaperEntity::getId, id);
  367. return this.update(lw);
  368. }
  369. @Transactional
  370. @Override
  371. public UriVo adminSheetUpdate(Long paperId, Integer pageIndex, MultipartFile multipartFile, String md5) {
  372. PaperEntity paper = this.getById(paperId);
  373. if (paper == null) {
  374. throw new ParameterException("paper信息未找到");
  375. }
  376. PaperPageEntity page = paperPageService.findPaperIdAndIndex(paperId, pageIndex);
  377. if (page == null) {
  378. throw new ParameterException("page信息未找到");
  379. }
  380. if (StringUtils.isBlank(page.getSheetPath())) {
  381. throw new ParameterException("未找到对应的原图");
  382. }
  383. StudentPaperEntity sp = studentPaperService.findByPaperId(paperId);
  384. if (sp == null) {
  385. throw new ParameterException("paper未绑定考生");
  386. }
  387. StudentEntity student = studentService.getById(sp.getStudentId());
  388. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().lock();
  389. try {
  390. String path = fileService.adminUploadSheet(multipartFile.getInputStream(), md5, paper.getExamId(),
  391. student.getExamNumber(), paper.getNumber(), pageIndex);
  392. filePropertyService.save(paper.getExamId(), path, md5, multipartFile.getSize());
  393. page.setSheetPath(path);
  394. paperPageService.updateByMultiId(page);
  395. // 更新考生文件上传状态
  396. studentService.updateFileUploadStatus(sp.getStudentId(), UploadStatus.WAITING_UPLOAD);
  397. return UriVo.create(path);
  398. } catch (Exception e) {
  399. log.error("原图更新失败,paperId=" + paperId + ", pageIndex=" + pageIndex, e);
  400. throw new StatusException("原图更新失败", e);
  401. } finally {
  402. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + sp.getStudentId()).writeLock().unlock();
  403. }
  404. }
  405. @Override
  406. public List<PaperCetVo> findByStudentIds(List<Long> studentIds) {
  407. return this.baseMapper.findByStudentIds(studentIds);
  408. }
  409. // @Override
  410. // @Transactional
  411. // public void updatePaperAssignedSuspect(Long id, Integer number, Boolean assignedSuspect) {
  412. // StudentPaperEntity sp = studentPaperService.findByStudentIdAndPaperNumber(id, number);
  413. // if (sp == null) {
  414. // throw new ParameterException("未找到绑定扫描结果");
  415. // }
  416. // LambdaUpdateWrapper<PaperEntity> lw = new LambdaUpdateWrapper<>();
  417. // lw.set(PaperEntity::getAssignedSuspect, assignedSuspect);
  418. // lw.eq(PaperEntity::getId, sp.getPaperId());
  419. // this.update(lw);
  420. // }
  421. @Override
  422. public List<StudentPaperVo> listByBatchIdAndStudentId(Long batchId, Long studentId) {
  423. return this.baseMapper.listByBatchIdAndStudentId(batchId, studentId);
  424. }
  425. }