BatchServiceImpl.java 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. package cn.com.qmth.scancentral.service.impl;
  2. import cn.com.qmth.scancentral.bean.BatchCreateDomain;
  3. import cn.com.qmth.scancentral.bean.BatchQueryDomain;
  4. import cn.com.qmth.scancentral.bean.User;
  5. import cn.com.qmth.scancentral.bean.WorkloadDomain;
  6. import cn.com.qmth.scancentral.bean.answersave.AnswerDomain;
  7. import cn.com.qmth.scancentral.bean.answersave.AnswerPaper;
  8. import cn.com.qmth.scancentral.dao.BatchDao;
  9. import cn.com.qmth.scancentral.entity.*;
  10. import cn.com.qmth.scancentral.enums.*;
  11. import cn.com.qmth.scancentral.exception.NotFoundExceptions;
  12. import cn.com.qmth.scancentral.exception.ParameterExceptions;
  13. import cn.com.qmth.scancentral.service.*;
  14. import cn.com.qmth.scancentral.support.TaskLock;
  15. import cn.com.qmth.scancentral.support.TaskLockUtil;
  16. import cn.com.qmth.scancentral.util.PageUtil;
  17. import cn.com.qmth.scancentral.vo.*;
  18. import cn.com.qmth.scancentral.vo.batch.*;
  19. import cn.com.qmth.scancentral.vo.batchdetail.BatchDetailDO;
  20. import cn.com.qmth.scancentral.vo.batchdetail.BatchDetailVo;
  21. import cn.com.qmth.scancentral.vo.batchdetail.BatchStudentVo;
  22. import cn.com.qmth.scancentral.vo.checkimage.CheckImageSubmitVo;
  23. import cn.com.qmth.scancentral.vo.device.DeviceVo;
  24. import cn.com.qmth.scancentral.vo.paper.PaperVo;
  25. import cn.com.qmth.scancentral.vo.task.TaskStatusVo;
  26. import cn.com.qmth.scancentral.vo.verify.VerifyStudentVo;
  27. import cn.com.qmth.scancentral.vo.verify.VerifyTaskVo;
  28. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  29. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  30. import com.baomidou.mybatisplus.core.metadata.IPage;
  31. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  32. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  33. import com.qmth.boot.core.collection.PageResult;
  34. import com.qmth.boot.core.concurrent.service.ConcurrentService;
  35. import com.qmth.boot.core.exception.ParameterException;
  36. import com.qmth.boot.core.exception.StatusException;
  37. import org.apache.commons.collections4.CollectionUtils;
  38. import org.apache.commons.lang.math.RandomUtils;
  39. import org.apache.commons.lang3.StringUtils;
  40. import org.springframework.beans.factory.annotation.Autowired;
  41. import org.springframework.stereotype.Service;
  42. import org.springframework.transaction.annotation.Transactional;
  43. import org.springframework.web.multipart.MultipartFile;
  44. import java.util.*;
  45. @Service
  46. public class BatchServiceImpl extends ServiceImpl<BatchDao, BatchEntity> implements BatchService {
  47. @Autowired
  48. private ExamService examService;
  49. @Autowired
  50. private StudentService studentService;
  51. @Autowired
  52. private PaperService paperService;
  53. @Autowired
  54. private PaperPageService paperPageService;
  55. @Autowired
  56. private BatchPaperService batchPaperService;
  57. @Autowired
  58. private AnswerCardService answerCardService;
  59. @Autowired
  60. private AnswerCardSubjectService answerCardSubjectService;
  61. @Autowired
  62. private ConcurrentService concurrentService;
  63. @Autowired
  64. private FileService fileService;
  65. @Autowired
  66. private FilePropertyService filePropertyService;
  67. @Autowired
  68. private SubjectService subjectService;
  69. @Autowired
  70. private AssignedCheckHistoryService assignedCheckHistoryService;
  71. @Override
  72. public BatchEntity checkBatchStatus(BatchEntity b) {
  73. if (b == null) {
  74. throw new ParameterException("批次未找到");
  75. }
  76. if (BatchStatus.FINISH.equals(b.getStatus())) {
  77. throw new ParameterException("批次已经完成");
  78. }
  79. if (VerifyStatus.WAITING.equals(b.getVerifyStatus())) {
  80. throw new ParameterException("批次正在审核中");
  81. }
  82. if (VerifyStatus.REJECT.equals(b.getVerifyStatus())) {
  83. throw new ParameterException("批次审核不通过");
  84. }
  85. return b;
  86. }
  87. @Transactional
  88. @Override
  89. public BatchCreateVo batchCreate(BatchCreateDomain domain, User user) {
  90. ExamEntity exam = examService.getById(domain.getExamId());
  91. if (exam == null) {
  92. throw ParameterExceptions.EXAM_NOT_FOUND;
  93. }
  94. List<Rescan> rescan = new ArrayList<>();
  95. String packageCode = null;
  96. for (String examNumber : domain.getExamNumbers()) {
  97. StudentEntity student = studentService.findByExamAndSubjectCodeAndExamNumber(domain.getExamId(),
  98. domain.getSubjectCode(), examNumber);
  99. if (student == null) {
  100. throw new ParameterException("未找到考生信息:" + examNumber);
  101. }
  102. if (exam.getScanByPackage() != null && exam.getScanByPackage()) {
  103. if (packageCode == null) {
  104. packageCode = student.getPackageCode();
  105. } else {
  106. if (!packageCode.equals(student.getPackageCode())) {
  107. throw new ParameterException("考生[" + examNumber + "]卷袋编号与其它考生不一致");
  108. }
  109. }
  110. if (domain.getSubjectCode() != null && !domain.getSubjectCode().equals(student.getSubjectCode())) {
  111. throw new ParameterException("考生[" + examNumber + "]科目代码与[" + domain.getSubjectCode() + "]不一致");
  112. }
  113. }
  114. Rescan rc = getRescan(student);
  115. if (rc != null) {
  116. rescan.add(rc);
  117. }
  118. }
  119. if (exam.getScanByPackage() != null && exam.getScanByPackage()) {
  120. if (StringUtils.isBlank(domain.getSubjectCode())) {
  121. throw new ParameterException("整袋模式下科目代码不能为空");
  122. }
  123. List<StudentEntity> ss = studentService.findByExamAndPackage(domain.getExamId(), packageCode,
  124. domain.getSubjectCode());
  125. if (CollectionUtils.isEmpty(ss) || ss.size() != domain.getExamNumbers().size()) {
  126. throw new ParameterException("该批次考生数量与卷袋考生数量不一致");
  127. }
  128. }
  129. BatchEntity b = new BatchEntity();
  130. b.setExamId(domain.getExamId());
  131. b.setDevice(user.getAccount());
  132. b.setPackageCode(packageCode);
  133. b.setSubjectCode(domain.getSubjectCode());
  134. b.setAssignedCount(0);
  135. b.setScanCount(0);
  136. b.setStatus(BatchStatus.INIT);
  137. this.save(b);
  138. BatchCreateVo vo = new BatchCreateVo();
  139. vo.setRescan(rescan);
  140. vo.setId(b.getId());
  141. vo.setCreateTime(b.getCreateTime());
  142. vo.setEnableSyncVerify(exam.getEnableSyncVerify());
  143. return vo;
  144. }
  145. private Rescan getRescan(StudentEntity student) {
  146. Rescan res = new Rescan();
  147. res.setExamNumber(student.getExamNumber());
  148. List<PaperVo> papers = paperService.findStudentPaper(student.getId());
  149. List<Paper> resPapers = new ArrayList<>();
  150. res.setPapers(resPapers);
  151. if (CollectionUtils.isEmpty(papers)) {
  152. return null;
  153. }
  154. PaperVo first = papers.get(0);
  155. res.setBatchId(first.getBatchId());
  156. res.setDevice(first.getDevice());
  157. res.setCreateTime(first.getCreateTime());
  158. for (PaperVo paper : papers) {
  159. Paper p = new Paper();
  160. p.setId(paper.getId());
  161. p.setNumber(paper.getNumber());
  162. p.setMismatch(paper.getMismatch());
  163. List<String> pages = new ArrayList<>();
  164. p.setPages(pages);
  165. List<PaperPageEntity> paperPages = paperPageService.listByPaperId(paper.getId());
  166. if (CollectionUtils.isNotEmpty(paperPages)) {
  167. for (PaperPageEntity pp : paperPages) {
  168. pages.add(pp.getSheetPath());
  169. }
  170. }
  171. resPapers.add(p);
  172. }
  173. return res;
  174. }
  175. @Override
  176. public int getCountByExam(Long examId) {
  177. QueryWrapper<BatchEntity> wrapper = new QueryWrapper<>();
  178. LambdaQueryWrapper<BatchEntity> lw = wrapper.lambda();
  179. lw.eq(BatchEntity::getExamId, examId);
  180. return this.count(wrapper);
  181. }
  182. @Override
  183. public int getValidCountByExam(Long examId) {
  184. QueryWrapper<BatchEntity> wrapper = new QueryWrapper<>();
  185. LambdaQueryWrapper<BatchEntity> lw = wrapper.lambda();
  186. lw.and(param -> {
  187. param.or().isNull(BatchEntity::getVerifyStatus);
  188. param.or().ne(BatchEntity::getVerifyStatus, VerifyStatus.REJECT);
  189. });
  190. lw.eq(BatchEntity::getExamId, examId);
  191. lw.gt(BatchEntity::getScanCount, 0);
  192. return this.count(wrapper);
  193. }
  194. @Override
  195. public int getVerifyCount(Long examId) {
  196. QueryWrapper<BatchEntity> wrapper = new QueryWrapper<>();
  197. LambdaQueryWrapper<BatchEntity> lw = wrapper.lambda();
  198. lw.eq(BatchEntity::getExamId, examId);
  199. lw.eq(BatchEntity::getVerifyStatus, VerifyStatus.WAITING);
  200. return this.count(wrapper);
  201. }
  202. @Transactional
  203. @Override
  204. public SheetUploadVo sheetUpload(Long batchId, String examNumber, Integer paperNumber, Integer pageIndex,
  205. MultipartFile multipartFile, String md5) {
  206. BatchEntity batch = checkBatchStatus(getById(batchId));
  207. try {
  208. String path = fileService.uploadSheet(multipartFile.getInputStream(), md5, batch.getExamId(), batchId,
  209. examNumber, paperNumber, pageIndex);
  210. filePropertyService.save(batch.getExamId(), path, md5, multipartFile.getSize());
  211. return SheetUploadVo.create(path);
  212. } catch (Exception e) {
  213. log.error("原图上传失败,batchId=" + batch + ", examNumber=" + examNumber + ", paperNumber=" + paperNumber
  214. + ", pageIndex=" + pageIndex, e);
  215. throw new StatusException("原图上传失败", e);
  216. }
  217. }
  218. @Transactional
  219. @Override
  220. public SliceUploadVo sliceUpload(Long batchId, String examNumber, Integer paperNumber, Integer pageIndex,
  221. Integer index, MultipartFile multipartFile, String md5) {
  222. BatchEntity batch = checkBatchStatus(getById(batchId));
  223. try {
  224. String path = fileService.uploadSlice(multipartFile.getInputStream(), md5, batch.getExamId(), batchId,
  225. examNumber, paperNumber, pageIndex, index);
  226. filePropertyService.save(batch.getExamId(), path, md5, multipartFile.getSize());
  227. return SliceUploadVo.create(path);
  228. } catch (Exception e) {
  229. log.error("裁切图上传失败,batchId=" + batch + ", examNumber=" + examNumber + ", paperNumber=" + paperNumber
  230. + ", pageIndex=" + pageIndex + ", index=" + index, e);
  231. throw new StatusException("裁切图上传失败", e);
  232. }
  233. }
  234. @Transactional
  235. @Override
  236. public AnswerSaveVo batchSave(AnswerDomain domain, User user) {
  237. BatchEntity batch = checkBatchStatus(getById(domain.getBatchId()));
  238. ExamEntity exam = examService.getById(batch.getExamId());
  239. if (exam == null) {
  240. throw ParameterExceptions.EXAM_NOT_FOUND;
  241. }
  242. String examNumber = domain.getExamNumber();
  243. StudentEntity student = studentService.findByExamAndSubjectCodeAndExamNumber(batch.getExamId(),
  244. domain.getSubjectCode(), examNumber);
  245. if (student == null) {
  246. throw new ParameterException("考生信息未找到");
  247. }
  248. AnswerCardEntity answerCard = answerCardService.findByExamAndNumber(batch.getExamId(), domain.getCardNumber());
  249. if (answerCard == null) {
  250. throw new ParameterException("卡格式信息未找到");
  251. }
  252. // 整袋扫描验证packageCode
  253. if (exam.getScanByPackage() != null && exam.getScanByPackage()) {
  254. if (!StringUtils.equals(batch.getPackageCode(), student.getPackageCode())) {
  255. throw new ParameterException("与批次卷袋号不一致");
  256. }
  257. }
  258. // 验证卡格式所属科目
  259. boolean allowSubject = answerCardSubjectService.checkSubject(answerCard.getExamId(), answerCard.getNumber(),
  260. student.getSubjectCode());
  261. if (!allowSubject) {
  262. throw new ParameterException("卡格式与考生科目不一致");
  263. }
  264. // 不允许缺页时,保存张数需要与卡格式定义一致
  265. if (!exam.getAllowUnexistPaper() && domain.getPapers().size() != answerCard.getPaperCount()) {
  266. throw new ParameterException("卡格式张数不一致");
  267. }
  268. List<String> paperTypeBarcodeContents = exam.getPaperTypeBarcodeContent();
  269. SubjectEntity subject = subjectService.findByExamIdAndCode(student.getExamId(), student.getSubjectCode());
  270. if (subject.getPaperTypeBarcodeContent() != null && !subject.getPaperTypeBarcodeContent().isEmpty()) {
  271. paperTypeBarcodeContents = subject.getPaperTypeBarcodeContent();
  272. }
  273. boolean studentAssigned = false;
  274. List<StudentPaperEntity> studentPaperList = new ArrayList<>();
  275. for (AnswerPaper answerPaper : domain.getPapers()) {
  276. // 验证page数量
  277. answerPaper.answerCardValidate(answerCard);
  278. Integer paperNumber = answerPaper.getNumber();
  279. PaperEntity paper = findOrCreatePaper(batch, student, paperNumber, user);
  280. // 设置paper属性
  281. paper.setExamId(batch.getExamId());
  282. paper.setCardNumber(answerCard.getNumber());
  283. paper.setNumber(paperNumber);
  284. paper.setMismatch(false);
  285. paper.setQuestionFilled(false);
  286. paper.setAssignedSuspect(false);
  287. paper.setSubjectiveFilled(answerPaper.getSubjectiveFilled());
  288. paper.setOmrExamNumber(answerPaper.getOmrExamNumber());
  289. paper.setAssigned(answerPaper.getAssigned());
  290. // 保存paper与page到数据库
  291. paperService.savePaperAndPages(paper, answerPaper.buildPageList(paperTypeBarcodeContents));
  292. // 记录paper与batch关联关系
  293. batchPaperService.update(batch, paper, student.getId(), paperNumber, false);
  294. // 创建student与paper的关联关系
  295. studentPaperList.add(new StudentPaperEntity(student.getId(), paperNumber, paper.getId()));
  296. studentAssigned = studentAssigned || paper.getAssigned();
  297. }
  298. // 更新批次统计数量,改到finish的时候更新
  299. // updateAssignedCount(batch.getId());
  300. // updateScanCount(batch.getId());
  301. // 不开启实时审核,或者没有人工绑定的情况下,或者当前批次无需审核,直接更新考生扫描状态
  302. if (VerifyStatus.CANCEL.equals(batch.getVerifyStatus()) || exam.getEnableSyncVerify() == null
  303. || !exam.getEnableSyncVerify() || !studentAssigned) {
  304. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().lock();
  305. try {
  306. studentService.updateStudentAndPaper(user, student.getId(), studentPaperList);
  307. studentService.updateAssignedCheckCount(student.getId(), true);
  308. } finally {
  309. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + student.getId()).writeLock().unlock();
  310. }
  311. }
  312. return AnswerSaveVo.create(findStudentCountByBatch(batch.getId()));
  313. }
  314. private PaperEntity findOrCreatePaper(BatchEntity batch, StudentEntity student, Integer paperNumber, User user) {
  315. PaperEntity paper = null;
  316. // 尝试重用已有paper对象
  317. BatchPaperEntity batchPaper = batchPaperService.findByBatchIdAndStudentIdAndPaperNumber(batch.getId(),
  318. student.getId(), paperNumber);
  319. if (batchPaper != null) {
  320. paper = paperService.getById(batchPaper.getPaperId());
  321. }
  322. // 构造全新paper对象
  323. if (paper == null) {
  324. paper = new PaperEntity();
  325. paper.setCreateTime(System.currentTimeMillis());
  326. paper.setCreatorId(user.getId());
  327. }
  328. return paper;
  329. }
  330. private void updateBatchAssignedStudentData(User user, Long batchId, Long examId) {
  331. List<BatchPaperEntity> batchPaperList = batchPaperService.findByBatchId(batchId);
  332. Map<Long, List<StudentPaperEntity>> studentMap = new HashMap<>();
  333. for (BatchPaperEntity batchPaper : batchPaperList) {
  334. studentMap.computeIfAbsent(batchPaper.getStudentId(), key -> new ArrayList<>()).add(new StudentPaperEntity(
  335. batchPaper.getStudentId(), batchPaper.getPaperNumber(), batchPaper.getPaperId()));
  336. }
  337. for (Map.Entry<Long, List<StudentPaperEntity>> entry : studentMap.entrySet()) {
  338. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().lock();
  339. try {
  340. studentService.updateStudentAndPaper(user, entry.getKey(), entry.getValue());
  341. assignedCheckHistoryService.save(user.getId(), entry.getKey(), examId);
  342. studentService.updateAssignedCheckCount(entry.getKey(), false);
  343. } finally {
  344. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().unlock();
  345. }
  346. }
  347. }
  348. @Transactional
  349. @Override
  350. public void verify(Long batchId, Boolean comfirm, User user) {
  351. BatchEntity entity = this.getById(batchId);
  352. if (entity == null) {
  353. throw new ParameterException("批次不存在");
  354. }
  355. if (!VerifyStatus.WAITING.equals(entity.getVerifyStatus())) {
  356. throw new ParameterException("批次已被处理");
  357. }
  358. if (comfirm) {
  359. updateBatchAssignedStudentData(user, batchId, entity.getExamId());
  360. entity.setVerifyStatus(VerifyStatus.CONFIRM);
  361. } else {
  362. entity.setVerifyStatus(VerifyStatus.REJECT);
  363. }
  364. entity.setUpdateTime(System.currentTimeMillis());
  365. this.saveOrUpdate(entity);
  366. }
  367. @Transactional
  368. @Override
  369. public BatchVerifyVo batchVerify(Long id) {
  370. BatchEntity entity = this.getById(id);
  371. if (entity == null) {
  372. throw new ParameterException("批次不存在");
  373. }
  374. Integer count = findStudentCountByBatch(id);
  375. if (count == null || count == 0) {
  376. throw new ParameterException("该批次未上传图片");
  377. }
  378. if (entity.getVerifyStatus() == null) {
  379. ExamEntity exam = examService.getById(entity.getExamId());
  380. if (exam.getEnableSyncVerify() == null || !exam.getEnableSyncVerify()) {
  381. throw new ParameterException("考试未启用实时审核");
  382. }
  383. entity.setVerifyStatus(VerifyStatus.WAITING);
  384. this.saveOrUpdate(entity);
  385. }
  386. BatchVerifyVo vo = new BatchVerifyVo();
  387. vo.setStatus(entity.getVerifyStatus());
  388. vo.setUpdateTime(entity.getUpdateTime());
  389. return vo;
  390. }
  391. @Transactional
  392. @Override
  393. public BatchFinishVo batchFinish(Long id) {
  394. BatchEntity entity = this.getById(id);
  395. if (entity == null) {
  396. throw new ParameterException("批次不存在");
  397. }
  398. checkBatchStatus(entity);
  399. ExamEntity exam = examService.getById(entity.getExamId());
  400. if (exam.getScanByPackage() != null && exam.getScanByPackage()) {
  401. checkStudentCount(entity);
  402. }
  403. List<BatchPaperEntity> saveList = new ArrayList<BatchPaperEntity>();
  404. if (exam.getImageCheckRatio() == 1) {
  405. List<BatchPaperEntity> papers = batchPaperService.findByBatchId(id);
  406. for (BatchPaperEntity batchPaperEntity : papers) {
  407. batchPaperEntity.setNeedCheck(true);
  408. saveList.add(batchPaperEntity);
  409. }
  410. } else {
  411. List<Long> studentIds = batchPaperService.findStudentIdByBatchId(id);
  412. int checkCount = (int) (studentIds.size() * exam.getImageCheckRatio());
  413. if (checkCount > 0) {
  414. Set<Long> checkIds = new HashSet<Long>();
  415. while (checkIds.size() < checkCount) {
  416. Long studentId = studentIds.get(RandomUtils.nextInt(studentIds.size()));
  417. checkIds.add(studentId);
  418. }
  419. List<BatchPaperEntity> papers = batchPaperService.findByBatchIdAndStudentIds(id, checkIds);
  420. for (BatchPaperEntity batchPaperEntity : papers) {
  421. batchPaperEntity.setNeedCheck(true);
  422. saveList.add(batchPaperEntity);
  423. }
  424. }
  425. }
  426. if (!saveList.isEmpty()) {
  427. batchPaperService.saveOrUpdateBatchByMultiId(saveList);
  428. entity.setCheckStatus(CheckStatus.WAITING);
  429. }
  430. entity.setStatus(BatchStatus.FINISH);
  431. this.saveOrUpdate(entity);
  432. updateAssignedCount(entity.getId());
  433. updateScanCount(entity.getId());
  434. BatchFinishVo vo = new BatchFinishVo();
  435. vo.setStatus(entity.getStatus());
  436. vo.setUpdateTime(System.currentTimeMillis());
  437. return vo;
  438. }
  439. private void checkStudentCount(BatchEntity entity) {
  440. List<BatchPaperEntity> papers = batchPaperService.findByBatchId(entity.getId());
  441. List<StudentEntity> students = studentService.findByExamAndPackage(entity.getExamId(), entity.getPackageCode(),
  442. entity.getSubjectCode());
  443. if (CollectionUtils.isEmpty(papers) && CollectionUtils.isEmpty(students)) {
  444. return;
  445. }
  446. if (CollectionUtils.isEmpty(papers) || CollectionUtils.isEmpty(students)) {
  447. throw new ParameterException("卷袋考生有未扫描");
  448. }
  449. Set<Long> ps = new HashSet<>();
  450. Set<Long> ss = new HashSet<>();
  451. for (BatchPaperEntity p : papers) {
  452. ps.add(p.getStudentId());
  453. }
  454. for (StudentEntity s : students) {
  455. ss.add(s.getId());
  456. }
  457. if (ps.size() != ss.size()) {
  458. throw new ParameterException("卷袋考生有未扫描");
  459. }
  460. for (Long p : ps) {
  461. if (!ss.contains(p)) {
  462. throw new ParameterException("卷袋考生有未扫描");
  463. }
  464. }
  465. }
  466. @Transactional
  467. @Override
  468. public void updateScanCount(Long id) {
  469. baseMapper.updateScanCount(id);
  470. }
  471. @Transactional
  472. @Override
  473. public void updateAssignedCount(Long id) {
  474. baseMapper.updateAssignedCount(id);
  475. }
  476. @Override
  477. public PageResult<BatchQueryVo> batchQuery(BatchQueryDomain query) {
  478. IPage<BatchQueryVo> iPage = baseMapper.batchQueryPage(new Page<>(query.getPageNumber(), query.getPageSize()),
  479. query);
  480. // TODO 需要后续补充incomplete逻辑
  481. return PageUtil.of(iPage);
  482. }
  483. @Override
  484. public List<Long> batchSummary(BatchQueryDomain query) {
  485. // TODO 需要后续补充incomplete逻辑
  486. return baseMapper.batchSummary(query);
  487. }
  488. @Override
  489. public BatchDetailVo batchDetail(Long id) {
  490. BatchEntity batchEntity = this.getById(id);
  491. BatchDetailVo vo = new BatchDetailVo();
  492. vo.update(batchEntity);
  493. Map<Long, BatchStudentVo> map = new LinkedHashMap<>();
  494. List<BatchDetailDO> list = baseMapper.batchDetailList(id);
  495. for (BatchDetailDO item : list) {
  496. BatchStudentVo detail = map.computeIfAbsent(item.getId(), key -> BatchStudentVo.build(item));
  497. // 判断是否人工绑定
  498. detail.setAssigned(detail.getAssigned() || item.getAssigned());
  499. // 判断是否识别缺考
  500. if (item.getAbsent() != null && item.getAbsent().getResult()) {
  501. detail.setOmrAbsent(true);
  502. }
  503. // 创建paper与page对象
  504. detail.findOrCreatePaper(item).addPage(item);
  505. }
  506. vo.setStudents(new ArrayList<>(map.values()));
  507. return vo;
  508. }
  509. @Override
  510. public VerifyTaskVo getVerifyTask(Long examId, User user) {
  511. int retry = 1;
  512. VerifyTaskVo task = null;
  513. while (task == null) {
  514. List<BatchEntity> list = this.findUnVerify(examId, retry, 20, VerifyStatus.WAITING).getRecords();
  515. if (list.isEmpty()) {
  516. break;
  517. }
  518. for (BatchEntity t : list) {
  519. if (this.applyVerifyTask(t, user.getAccount())) {
  520. task = toVerifyTaskVo(t);
  521. break;
  522. }
  523. }
  524. if (task == null) {
  525. retry++;
  526. }
  527. }
  528. if (task == null) {
  529. throw NotFoundExceptions.NO_SYNC_VERIFY_TASK;
  530. }
  531. return task;
  532. }
  533. private VerifyTaskVo toVerifyTaskVo(BatchEntity batch) {
  534. VerifyTaskVo vo = new VerifyTaskVo();
  535. vo.setBatchId(batch.getId());
  536. vo.setCreateTime(batch.getCreateTime());
  537. vo.setDevice(batch.getDevice());
  538. vo.setPackageCode(batch.getPackageCode());
  539. Map<Long, VerifyStudentVo> studentMap = new LinkedHashMap<>();
  540. List<BatchDetailDO> list = baseMapper.batchDetailList(batch.getId());
  541. for (BatchDetailDO item : list) {
  542. VerifyStudentVo student = studentMap.computeIfAbsent(item.getId(), key -> VerifyStudentVo.build(item));
  543. student.findOrCreatePaper(item).getPages().add(item.getSheetPath());
  544. }
  545. vo.setStudents(new ArrayList<>(studentMap.values()));
  546. return vo;
  547. }
  548. @Override
  549. public boolean applyVerifyTask(BatchEntity t, String user) {
  550. TaskLock taskLock = TaskLockUtil.getVerifyTask(t.getExamId().toString());
  551. boolean lock = taskLock.add(t.getId(), user);
  552. // 上锁失败直接返回
  553. if (!lock) {
  554. return false;
  555. }
  556. // 重复校验任务状态
  557. if (VerifyStatus.WAITING.equals(t.getVerifyStatus())) {
  558. return true;
  559. } else {
  560. taskLock.remove(t.getId().toString());
  561. return false;
  562. }
  563. }
  564. @Override
  565. public void releaseVerifyTask(Long examId, String account) {
  566. TaskLock taskLock = TaskLockUtil.getVerifyTask(examId.toString());
  567. taskLock.clear(account);
  568. }
  569. @Override
  570. public void releaseVerifyTask(BatchEntity t) {
  571. TaskLock taskLock = TaskLockUtil.getVerifyTask(t.getExamId().toString());
  572. taskLock.remove(t.getId().toString());
  573. }
  574. @Override
  575. public boolean hasAppliedVerifyTask(BatchEntity t, String user) {
  576. TaskLock taskLock = TaskLockUtil.getVerifyTask(t.getExamId().toString());
  577. return taskLock.exist(t.getId(), user);
  578. }
  579. @Override
  580. public IPage<BatchEntity> findUnVerify(Long examId, int pageNumber, int pageSize, VerifyStatus status) {
  581. return this.baseMapper.findUnVerify(new Page<>(pageNumber, pageSize), examId, status);
  582. }
  583. @Override
  584. public List<String> batchScanner(Long examId) {
  585. return baseMapper.batchScanner(examId);
  586. }
  587. @Override
  588. public List<ScannerWorkloadVo> workload(WorkloadDomain domain) {
  589. Long examId = domain.getExamId();
  590. Long startTime = domain.getStartTime();
  591. Long endTime = domain.getEndTime();
  592. if (examId == null) {
  593. throw new ParameterException("examId不能为空");
  594. }
  595. return baseMapper.workload(examId, startTime, endTime);
  596. }
  597. @Override
  598. public Integer findStudentCountByBatch(Long batchId) {
  599. return baseMapper.findStudentCountByBatch(batchId);
  600. }
  601. @Override
  602. public BatchEntity findByPaperId(Long paperId) {
  603. return baseMapper.selectById(batchPaperService.findByPaperId(paperId).getBatchId());
  604. }
  605. @Override
  606. public TaskStatusVo getCheckImageStatus(Long examId, User user) {
  607. TaskStatusVo vo = new TaskStatusVo();
  608. vo.setFinishCount(this.getCheckCountByExamId(examId, CheckStatus.FINISH, user));
  609. vo.setTodoCount(this.getCheckCountByExamId(examId, CheckStatus.WAITING, user));
  610. return vo;
  611. }
  612. @Override
  613. public VerifyTaskVo getCheckImageTask(Long examId, User user) {
  614. int retry = 1;
  615. VerifyTaskVo task = null;
  616. while (task == null) {
  617. List<BatchEntity> list = this.findUnCheck(examId, retry, 20, BatchStatus.FINISH, CheckStatus.WAITING)
  618. .getRecords();
  619. if (list.isEmpty()) {
  620. break;
  621. }
  622. for (BatchEntity t : list) {
  623. if (this.applyCheckImageTask(t, user.getAccount())) {
  624. task = toCheckTaskVo(t);
  625. break;
  626. }
  627. }
  628. if (task == null) {
  629. retry++;
  630. }
  631. }
  632. if (task == null) {
  633. throw NotFoundExceptions.NO_SYNC_VERIFY_TASK;
  634. }
  635. return task;
  636. }
  637. private VerifyTaskVo toCheckTaskVo(BatchEntity batch) {
  638. VerifyTaskVo vo = new VerifyTaskVo();
  639. vo.setBatchId(batch.getId());
  640. vo.setCreateTime(batch.getCreateTime());
  641. vo.setDevice(batch.getDevice());
  642. vo.setPackageCode(batch.getPackageCode());
  643. Map<Long, VerifyStudentVo> studentMap = new LinkedHashMap<>();
  644. List<BatchDetailDO> list = baseMapper.batchDetailListToCheck(batch.getId(), true);
  645. for (BatchDetailDO item : list) {
  646. VerifyStudentVo student = studentMap.computeIfAbsent(item.getId(), key -> VerifyStudentVo.build(item));
  647. student.findOrCreatePaper(item).getPages().add(item.getSheetPath());
  648. }
  649. vo.setStudents(new ArrayList<>(studentMap.values()));
  650. return vo;
  651. }
  652. @Override
  653. public VerifyTaskVo getHistoryCheckImageTask(Long examId, Long batchId, User user) {
  654. if (examId == null) {
  655. throw new ParameterException("examId不能为空");
  656. }
  657. if (user.getId() == null) {
  658. throw new ParameterException("userId不能为空");
  659. }
  660. BatchEntity batchEntity = this.baseMapper.getHistory(examId, batchId, user.getId(), CheckStatus.FINISH);
  661. if (batchEntity == null) {
  662. throw NotFoundExceptions.NO_CHECK_IMAGE_TASK;
  663. }
  664. VerifyTaskVo task = toCheckTaskVo(batchEntity);
  665. return task;
  666. }
  667. @Transactional
  668. @Override
  669. public CheckImageSubmitVo submitCheckImageTask(Long examId, Long batchId, User user) {
  670. BatchEntity entity = this.getById(batchId);
  671. if (entity == null) {
  672. throw new ParameterException("批次不存在");
  673. }
  674. if (entity.getCheckStatus() == CheckStatus.FINISH) {
  675. throw new ParameterException("该批次已经被审核");
  676. }
  677. entity.setCheckStatus(CheckStatus.FINISH);
  678. entity.setCheckImageTime(System.currentTimeMillis());
  679. entity.setCheckImageUserId(user.getId());
  680. this.saveOrUpdate(entity);
  681. this.releaseCheckImageTask(entity);
  682. CheckImageSubmitVo vo = new CheckImageSubmitVo();
  683. vo.setBatchId(batchId);
  684. vo.setUpdateTime(entity.getUpdateTime());
  685. vo.getStatus().setFinishCount(this.getCheckCountByExamId(examId, CheckStatus.FINISH, user));
  686. vo.getStatus().setTodoCount(this.getCheckCountByExamId(examId, CheckStatus.WAITING, user));
  687. return vo;
  688. }
  689. @Override
  690. public void releaseCheckImageTask(Long examId, User user) {
  691. TaskLock taskLock = TaskLockUtil.getCheckTask(examId.toString());
  692. taskLock.clear(user.getAccount());
  693. }
  694. @Override
  695. public int getCheckCountByExamId(Long examId, CheckStatus status, User user) {
  696. QueryWrapper<BatchEntity> wrapper = new QueryWrapper<>();
  697. LambdaQueryWrapper<BatchEntity> lw = wrapper.lambda();
  698. lw.eq(BatchEntity::getExamId, examId);
  699. lw.eq(BatchEntity::getCheckStatus, status);
  700. lw.eq(BatchEntity::getStatus, BatchStatus.FINISH);
  701. // 审核员只统计自身已完成的数量
  702. if (status == CheckStatus.FINISH && user.getRole() == Role.AUDITOR) {
  703. lw.eq(BatchEntity::getCheckImageUserId, user.getId());
  704. }
  705. return this.count(wrapper);
  706. }
  707. @Override
  708. public void releaseCheckImageTask(BatchEntity t) {
  709. TaskLock taskLock = TaskLockUtil.getCheckTask(t.getExamId().toString());
  710. taskLock.remove(t.getId().toString());
  711. }
  712. @Override
  713. public IPage<BatchEntity> findUnCheck(Long examId, int pageNumber, int pageSize, BatchStatus batchStatus,
  714. CheckStatus checkStatus) {
  715. return this.baseMapper.findUnCheck(new Page<>(pageNumber, pageSize), examId, batchStatus, checkStatus);
  716. }
  717. @Override
  718. public boolean applyCheckImageTask(BatchEntity t, String user) {
  719. TaskLock taskLock = TaskLockUtil.getCheckTask(t.getExamId().toString());
  720. boolean lock = taskLock.add(t.getId(), user);
  721. // 上锁失败直接返回
  722. if (!lock) {
  723. return false;
  724. }
  725. // 重复校验任务状态
  726. if (CheckStatus.WAITING.equals(t.getCheckStatus())) {
  727. return true;
  728. } else {
  729. taskLock.remove(t.getId().toString());
  730. return false;
  731. }
  732. }
  733. @Override
  734. @Transactional
  735. public void batchVerifyCancel(User user, Long examId) {
  736. QueryWrapper<BatchEntity> wrapper = new QueryWrapper<>();
  737. LambdaQueryWrapper<BatchEntity> lw = wrapper.lambda();
  738. lw.eq(BatchEntity::getExamId, examId);
  739. lw.eq(BatchEntity::getVerifyStatus, VerifyStatus.WAITING);
  740. List<BatchEntity> list = this.list(lw);
  741. for (BatchEntity batch : list) {
  742. List<BatchPaperEntity> batchPaperList = batchPaperService.findByBatchId(batch.getId());
  743. Map<Long, List<StudentPaperEntity>> studentMap = new HashMap<>();
  744. for (BatchPaperEntity batchPaper : batchPaperList) {
  745. studentMap.computeIfAbsent(batchPaper.getStudentId(), key -> new ArrayList<>())
  746. .add(new StudentPaperEntity(batchPaper.getStudentId(), batchPaper.getPaperNumber(),
  747. batchPaper.getPaperId()));
  748. }
  749. for (Map.Entry<Long, List<StudentPaperEntity>> entry : studentMap.entrySet()) {
  750. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().lock();
  751. try {
  752. studentService.updateStudentAndPaper(user, entry.getKey(), entry.getValue());
  753. studentService.updateAssignedCheckCount(entry.getKey(), true);
  754. } finally {
  755. concurrentService.getReadWriteLock(LockType.STUDENT + "-" + entry.getKey()).writeLock().unlock();
  756. }
  757. }
  758. batch.setVerifyStatus(VerifyStatus.CANCEL);
  759. }
  760. this.updateBatchById(list);
  761. }
  762. @Override
  763. @Transactional
  764. public void updateBatchCount() {
  765. QueryWrapper<BatchEntity> wrapper = new QueryWrapper<>();
  766. LambdaQueryWrapper<BatchEntity> lw = wrapper.lambda();
  767. lw.eq(BatchEntity::getStatus, BatchStatus.INIT);
  768. List<BatchEntity> list = this.list(lw);
  769. for (BatchEntity batch : list) {
  770. updateAssignedCount(batch.getId());
  771. updateScanCount(batch.getId());
  772. }
  773. }
  774. @Override
  775. public List<SubjectVo> subjectList(BatchSubjectQuery req) {
  776. if (req.getExamId() == null) {
  777. throw new ParameterException("考试id不能为空");
  778. }
  779. return this.baseMapper.subjectList(req);
  780. }
  781. @Override
  782. public List<DeviceVo> deviceList(BatchDeviceQuery req) {
  783. if (req.getExamId() == null) {
  784. throw new ParameterException("考试id不能为空");
  785. }
  786. if (StringUtils.isBlank(req.getSubjectCode())) {
  787. throw new ParameterException("科目代码不能为空");
  788. }
  789. return this.baseMapper.deviceList(req);
  790. }
  791. @Override
  792. public List<BatchVo> batchList(BatchQuery req) {
  793. if (req.getExamId() == null) {
  794. throw new ParameterException("考试id不能为空");
  795. }
  796. if (StringUtils.isBlank(req.getSubjectCode())) {
  797. throw new ParameterException("科目代码不能为空");
  798. }
  799. if (StringUtils.isBlank(req.getDevice())) {
  800. throw new ParameterException("设备不能为空");
  801. }
  802. return this.baseMapper.batchList(req);
  803. }
  804. @Override
  805. public List<BatchStudentSimpleVo> studentList(Long batchId) {
  806. if (batchId == null) {
  807. throw new ParameterException("批次id不能为空");
  808. }
  809. return this.baseMapper.studentList(batchId);
  810. }
  811. }