StudentApplyServiceImpl.java 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. package com.qmth.exam.reserve.service.impl;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.time.LocalDate;
  6. import java.time.ZoneId;
  7. import java.util.ArrayList;
  8. import java.util.Arrays;
  9. import java.util.Calendar;
  10. import java.util.Comparator;
  11. import java.util.Date;
  12. import java.util.HashMap;
  13. import java.util.Iterator;
  14. import java.util.List;
  15. import java.util.Map;
  16. import java.util.stream.Collectors;
  17. import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
  18. import com.qmth.exam.reserve.entity.base.BaseEntity;
  19. import com.qmth.exam.reserve.enums.Role;
  20. import com.qmth.exam.reserve.service.*;
  21. import org.apache.commons.lang3.StringUtils;
  22. import org.apache.commons.lang3.time.DateUtils;
  23. import org.redisson.api.RLock;
  24. import org.slf4j.Logger;
  25. import org.slf4j.LoggerFactory;
  26. import org.springframework.beans.factory.annotation.Autowired;
  27. import org.springframework.stereotype.Service;
  28. import org.springframework.transaction.annotation.Transactional;
  29. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  30. import com.baomidou.mybatisplus.core.metadata.IPage;
  31. import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
  32. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  33. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  34. import com.qmth.boot.core.collection.PageResult;
  35. import com.qmth.boot.core.concurrent.service.ConcurrentService;
  36. import com.qmth.boot.core.exception.StatusException;
  37. import com.qmth.boot.tools.excel.ExcelReader;
  38. import com.qmth.boot.tools.excel.enums.ExcelType;
  39. import com.qmth.boot.tools.excel.model.DataMap;
  40. import com.qmth.boot.tools.io.ZipWriter;
  41. import com.qmth.exam.reserve.bean.Constants;
  42. import com.qmth.exam.reserve.bean.apply.ApplyRecordCacheBean;
  43. import com.qmth.exam.reserve.bean.login.LoginUser;
  44. import com.qmth.exam.reserve.bean.stdapply.AgentAndTimeVO;
  45. import com.qmth.exam.reserve.bean.stdapply.CategoryVO;
  46. import com.qmth.exam.reserve.bean.stdapply.MaterialTitleInfo;
  47. import com.qmth.exam.reserve.bean.stdapply.SignInVO;
  48. import com.qmth.exam.reserve.bean.stdapply.StudentApplyReq;
  49. import com.qmth.exam.reserve.bean.stdapply.StudentApplyVO;
  50. import com.qmth.exam.reserve.bean.stdapply.StudentImportVO;
  51. import com.qmth.exam.reserve.cache.CacheConstants;
  52. import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService;
  53. import com.qmth.exam.reserve.dao.StudentApplyDao;
  54. import com.qmth.exam.reserve.entity.ApplyTaskEntity;
  55. import com.qmth.exam.reserve.entity.CategoryEntity;
  56. import com.qmth.exam.reserve.entity.ExamRoomEntity;
  57. import com.qmth.exam.reserve.entity.ExamSiteEntity;
  58. import com.qmth.exam.reserve.entity.StudentApplyEntity;
  59. import com.qmth.exam.reserve.entity.StudentEntity;
  60. import com.qmth.exam.reserve.entity.TimePeriodEntity;
  61. import com.qmth.exam.reserve.enums.CategoryLevel;
  62. import com.qmth.exam.reserve.enums.EventType;
  63. import com.qmth.exam.reserve.util.DateUtil;
  64. import com.qmth.exam.reserve.util.JsonHelper;
  65. import com.qmth.exam.reserve.util.PageUtil;
  66. import static org.apache.commons.lang3.time.DateUtils.isSameDay;
  67. @Service
  68. public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, StudentApplyEntity>
  69. implements StudentApplyService {
  70. private final static Logger log = LoggerFactory.getLogger(StudentApplyServiceImpl.class);
  71. private static final String[] EXCEL_HEADER = new String[]{"学号", "姓名", "证件号", "所属教学点", "预约考点1", "预约时段1", "预约考点2",
  72. "预约时段2", "预约考点3", "预约时段3", "预约考点4", "预约时段4"};
  73. @Autowired
  74. private TimePeriodService timePeriodService;
  75. @Autowired
  76. private ApplyTaskService applyTaskService;
  77. @Autowired
  78. private CategoryService categoryService;
  79. @Autowired
  80. private StudentService studentService;
  81. @Autowired
  82. private ExamSiteService examSiteService;
  83. @Autowired
  84. private OperateLogService operateLogService;
  85. @Autowired
  86. private ExamRoomService examRoomService;
  87. @Autowired
  88. private MaterialGenerateService materialService;
  89. @Autowired
  90. private ConcurrentService concurrentService;
  91. @Autowired
  92. private ApplyTaskCacheService cacheService;
  93. @Autowired
  94. private StudentAutoAssignService studentAutoAssignService;
  95. @Override
  96. public PageResult<StudentApplyVO> page(StudentApplyReq req) {
  97. if (req.getTaskId() == null) {
  98. ApplyTaskEntity task = getApplyTask();
  99. req.setTaskId(task.getId());
  100. }
  101. if (req.getTeachingId() != null) {
  102. List<CategoryVO> listExamSite = examSiteService.listExamSite(req.getTeachingId());
  103. List<Long> examSiteIds = listExamSite.stream().map(CategoryVO::getId).collect(Collectors.toList());
  104. if (!examSiteIds.isEmpty()) {
  105. req.setExamSiteIds(examSiteIds);
  106. }
  107. }
  108. IPage<StudentApplyVO> iPage = this.baseMapper
  109. .page(new Page<StudentApplyVO>(req.getPageNumber(), req.getPageSize()), req);
  110. return PageUtil.of(iPage);
  111. }
  112. @Transactional
  113. @Override
  114. public void cancel(LoginUser user, Long id) {
  115. // 时间判断
  116. StudentApplyEntity studentApply = this.baseMapper.selectById(id);
  117. if (studentApply == null || studentApply.getTimePeriodId() == null) {
  118. throw new StatusException("考生没有预约,无法取消!");
  119. }
  120. TimePeriodEntity timePeriod = timePeriodService.getById(studentApply.getTimePeriodId());
  121. if (timePeriod == null) {
  122. throw new StatusException("考试时段不存在,请检查考试时段数据!");
  123. }
  124. ApplyTaskEntity task = getApplyTask();
  125. Date applyDate = DateUtils.truncate(new Date(timePeriod.getStartTime()), Calendar.DATE);
  126. Date canCancelDay = DateUtil.addValues(applyDate, Calendar.DAY_OF_MONTH, -task.getAllowApplyCancelDays());
  127. if (new Date().after(canCancelDay)) {
  128. throw new StatusException("可取消时间已过,无法取消!");
  129. }
  130. String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, studentApply.getStudentId());
  131. RLock studentApplyLock = (RLock) concurrentService.getLock(studentApplyLockKey);
  132. try {
  133. if (!studentApplyLock.tryLock()) {
  134. log.warn("获取锁失败,同一个考生不允许同时操作预约!lockKey:{}", studentApplyLockKey);
  135. throw new StatusException(Constants.SYSTEM_BUSY);
  136. } else {
  137. log.warn("获取锁成功!lockKey:{}", studentApplyLockKey);
  138. studentApply.setCancel(Boolean.TRUE);
  139. this.baseMapper.updateById(studentApply);
  140. ApplyRecordCacheBean bean = new ApplyRecordCacheBean();
  141. bean.setStudentId(studentApply.getStudentId());
  142. bean.setExamSiteId(studentApply.getExamSiteId());
  143. bean.setTimePeriodId(studentApply.getTimePeriodId());
  144. bean.setCancel(Boolean.TRUE);
  145. bean.setOperateId(user.getId());
  146. bean.setOperateTime(System.currentTimeMillis());
  147. // 先推送至预约队列
  148. cacheService.pushStudentApplyRecordQueue(bean);
  149. cacheService.saveStudentApplyRecord(bean);
  150. cacheService.decreaseApplyFinishCount(studentApply.getExamSiteId(), studentApply.getTimePeriodId());
  151. }
  152. } catch (Exception e) {
  153. throw new StatusException("取消预约失败,请稍后再试!", e);
  154. } finally {
  155. try {
  156. // 解锁前检查当前线程是否持有该锁
  157. if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) {
  158. studentApplyLock.unlock();
  159. log.info("解锁成功!lockKey:{}", studentApplyLockKey);
  160. }
  161. } catch (Exception e) {
  162. log.warn(e.getMessage());
  163. }
  164. }
  165. operateLogService.insertOperateLog(user.getId(), EventType.CANCEL_APPLY, JsonHelper.toJson(studentApply));
  166. }
  167. @Transactional
  168. @Override
  169. public List<Map<String, Object>> importPreExam(LoginUser user, Long teachingId, Integer level,
  170. InputStream inputStream) {
  171. checkOpenTime(user.getOrgId());
  172. List<DataMap> lineList;
  173. ExcelReader reader = ExcelReader.create(ExcelType.XLSX, inputStream, 0);
  174. try {
  175. lineList = reader.getDataMapList();
  176. } catch (Exception e) {
  177. throw new StatusException("Excel 解析失败");
  178. }
  179. if (!Arrays.equals(EXCEL_HEADER, reader.getColumnNames())) {
  180. throw new StatusException("Excel表头错误");
  181. }
  182. if (CollectionUtils.isEmpty(lineList)) {
  183. throw new StatusException("Excel无内容");
  184. }
  185. ApplyTaskEntity task = getApplyTask();
  186. Date openStartTime = new Date(task.getOpenApplyStartTime());
  187. Date now = new Date();
  188. List<Map<String, Object>> failRecords = new ArrayList<Map<String, Object>>();
  189. Map<String, Long> teachingCache = getTeachingCache(level);
  190. Map<String, Long> agentCache = getAgentCache();
  191. Map<String, TimePeriodEntity> timeCache = getTimePeriodCache();
  192. Map<Long, Long> examSiteCategoryCache = getExamSiteCategoryCache();
  193. List<StudentImportVO> applyList = new ArrayList<>();
  194. AgentAndTimeVO agentTime;
  195. for (int i = 0; i < lineList.size(); i++) {
  196. List<AgentAndTimeVO> agentTimeList = new ArrayList<>();
  197. DataMap line = lineList.get(i);
  198. StudentImportVO apply = new StudentImportVO();
  199. StringBuilder msg = new StringBuilder();
  200. String studentCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[0]));
  201. if (StringUtils.isBlank(studentCode)) {
  202. msg.append(" 学号不能为空");
  203. }
  204. String name = trimAndNullIfBlank(line.get(EXCEL_HEADER[1]));
  205. if (StringUtils.isBlank(name)) {
  206. msg.append(" 姓名不能为空");
  207. }
  208. String identityNumber = trimAndNullIfBlank(line.get(EXCEL_HEADER[2]));
  209. if (StringUtils.isBlank(identityNumber)) {
  210. msg.append(" 证件号不能为空");
  211. }
  212. StudentEntity student = null;
  213. try {
  214. student = checkStd(studentCode, name, identityNumber, task);
  215. apply.setStudentId(student.getId());
  216. apply.setApplyNumber(student.getApplyNumber());
  217. } catch (StatusException e) {
  218. msg.append(" " + e.getMessage());
  219. failRecords.add(newError(i + 1, msg.toString()));
  220. continue;
  221. }
  222. String teachingName = trimAndNullIfBlank(line.get(EXCEL_HEADER[3]));
  223. if (StringUtils.isBlank(teachingName)) {
  224. msg.append(" 所属教学点不能为空");
  225. }
  226. Long categoryId = teachingCache.get(teachingName);
  227. if (categoryId == null) {
  228. msg.append(" 所属教学点不存在");
  229. }
  230. if (categoryId != null && !student.getCategoryId().equals(categoryId)) {
  231. msg.append(" 导入的考生所属教学点和系统中的考生教学点不匹配");
  232. }
  233. if (user.getRole().equals(Role.TEACHING) && categoryId != null && !categoryId.equals(teachingId)) {
  234. msg.append(" 不是本教学点的考生");
  235. }
  236. String agentName1 = trimAndNullIfBlank(line.get(EXCEL_HEADER[4]));
  237. if (StringUtils.isBlank(agentName1)) {
  238. msg.append(" 预约考点1不能为空");
  239. }
  240. agentTime = new AgentAndTimeVO();
  241. Long agentId = agentCache.get(agentName1);
  242. Long applyCategoryId = null;
  243. if (agentId == null) {
  244. msg.append(" 预约考点1不存在");
  245. } else {
  246. applyCategoryId = examSiteCategoryCache.get(agentId);
  247. if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
  248. msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
  249. }
  250. }
  251. String timePeriod1 = trimAndNullIfBlank(line.get(EXCEL_HEADER[5]));
  252. if (StringUtils.isBlank(timePeriod1)) {
  253. msg.append(" 预约时段1不能为空");
  254. }
  255. TimePeriodEntity timePeriod = null;
  256. try {
  257. timePeriod = checkTimePeriod(timePeriod1, timeCache);
  258. agentTime.setAgentId(agentId);
  259. agentTime.setTimePeriodId(timePeriod.getId());
  260. agentTime.setStartTime(timePeriod.getStartTime());
  261. agentTimeList.add(agentTime);
  262. } catch (StatusException e) {
  263. msg.append(" " + e.getMessage());
  264. }
  265. String agentName2 = trimAndNullIfBlank(line.get(EXCEL_HEADER[6]));
  266. String timePeriod2 = trimAndNullIfBlank(line.get(EXCEL_HEADER[7]));
  267. if (StringUtils.isBlank(agentName2) && StringUtils.isBlank(timePeriod2)) {
  268. apply.setAgentTimeList(agentTimeList);
  269. applyList.add(apply);
  270. if (msg.length() > 0)
  271. failRecords.add(newError(i + 1, msg.toString()));
  272. continue;
  273. } else {
  274. agentId = agentCache.get(agentName2);
  275. if (agentId == null) {
  276. msg.append(" 预约考点2不存在");
  277. } else {
  278. applyCategoryId = examSiteCategoryCache.get(agentId);
  279. if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
  280. msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
  281. }
  282. }
  283. try {
  284. timePeriod = checkTimePeriod(timePeriod2, timeCache);
  285. agentTime = new AgentAndTimeVO();
  286. agentTime.setAgentId(agentId);
  287. agentTime.setTimePeriodId(timePeriod.getId());
  288. agentTime.setStartTime(timePeriod.getStartTime());
  289. agentTimeList.add(agentTime);
  290. } catch (StatusException e) {
  291. msg.append(" " + e.getMessage());
  292. }
  293. }
  294. String agentName3 = trimAndNullIfBlank(line.get(EXCEL_HEADER[8]));
  295. String timePeriod3 = trimAndNullIfBlank(line.get(EXCEL_HEADER[9]));
  296. if (StringUtils.isBlank(agentName3) && StringUtils.isBlank(timePeriod3)) {
  297. apply.setAgentTimeList(agentTimeList);
  298. applyList.add(apply);
  299. if (msg.length() > 0)
  300. failRecords.add(newError(i + 1, msg.toString()));
  301. continue;
  302. } else {
  303. agentId = agentCache.get(agentName3);
  304. if (agentId == null) {
  305. msg.append(" 预约考点3不存在");
  306. } else {
  307. applyCategoryId = examSiteCategoryCache.get(agentId);
  308. if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
  309. msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
  310. }
  311. }
  312. try {
  313. timePeriod = checkTimePeriod(timePeriod3, timeCache);
  314. agentTime = new AgentAndTimeVO();
  315. agentTime.setAgentId(agentId);
  316. agentTime.setTimePeriodId(timePeriod.getId());
  317. agentTime.setStartTime(timePeriod.getStartTime());
  318. agentTimeList.add(agentTime);
  319. } catch (StatusException e) {
  320. msg.append(" " + e.getMessage());
  321. }
  322. }
  323. String agentName4 = trimAndNullIfBlank(line.get(EXCEL_HEADER[10]));
  324. String timePeriod4 = trimAndNullIfBlank(line.get(EXCEL_HEADER[11]));
  325. if (StringUtils.isBlank(agentName4) && StringUtils.isBlank(timePeriod4)) {
  326. apply.setAgentTimeList(agentTimeList);
  327. applyList.add(apply);
  328. if (msg.length() > 0)
  329. failRecords.add(newError(i + 1, msg.toString()));
  330. continue;
  331. } else {
  332. agentId = agentCache.get(agentName4);
  333. if (agentId == null) {
  334. msg.append(" 预约考点4不存在");
  335. } else {
  336. applyCategoryId = examSiteCategoryCache.get(agentId);
  337. if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
  338. msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
  339. }
  340. }
  341. try {
  342. timePeriod = checkTimePeriod(timePeriod4, timeCache);
  343. agentTime = new AgentAndTimeVO();
  344. agentTime.setAgentId(agentId);
  345. agentTime.setTimePeriodId(timePeriod.getId());
  346. agentTime.setStartTime(timePeriod.getStartTime());
  347. agentTimeList.add(agentTime);
  348. apply.setAgentTimeList(agentTimeList);
  349. applyList.add(apply);
  350. } catch (StatusException e) {
  351. msg.append(" " + e.getMessage());
  352. }
  353. }
  354. }
  355. checkStudentApplyTime(applyList, getApplyTask().getAllowApplyCancelDays(), failRecords);
  356. if (CollectionUtils.isNotEmpty(failRecords)) {
  357. // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  358. return failRecords;
  359. }
  360. checkAvailableTimePeriod(applyList);
  361. for (int i = 0; i < applyList.size(); i++) {
  362. StudentImportVO vo = applyList.get(i);
  363. try {
  364. saveStdApply(i, vo, user.getId(), failRecords);
  365. } catch (StatusException e) {
  366. failRecords.add(newError(i + 1, " 系统异常"));
  367. log.error("导入异常", e);
  368. }
  369. }
  370. if (CollectionUtils.isNotEmpty(failRecords)) {
  371. // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  372. return failRecords;
  373. }
  374. return failRecords;
  375. }
  376. private void checkOpenTime(Long orgId) {
  377. CurrentApplyTaskVO task = cacheService.currentApplyTask(orgId);
  378. Date startTime = new Date(task.getSelfApplyStartTime());
  379. Date endTime = new Date(task.getOpenApplyEndTime());
  380. Date now = new Date();
  381. if (now.before(startTime) || now.after(endTime)) {
  382. throw new StatusException("未到开放时间!");
  383. }
  384. }
  385. private Map<Long, Long> getExamSiteCategoryCache() {
  386. Map<Long, Long> cache = new HashMap<>();
  387. LambdaQueryWrapper<ExamSiteEntity> lm = new LambdaQueryWrapper<>();
  388. lm.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
  389. List<ExamSiteEntity> examSiteList = examSiteService.list(lm);
  390. examSiteList.forEach(site -> {
  391. cache.put(site.getId(), site.getCategoryId());
  392. });
  393. return cache;
  394. }
  395. private void checkAvailableTimePeriod(List<StudentImportVO> applyList) {
  396. for (StudentImportVO vo : applyList) {
  397. List<AgentAndTimeVO> agentTimeList = vo.getAgentTimeList();
  398. List<StudentApplyEntity> haveApplyList = listStudentApply(vo.getStudentId());
  399. int studentApplyFinishCount = cacheService.getStudentApplyFinishCount(vo.getStudentId());
  400. if (studentApplyFinishCount == 0) {
  401. studentApplyFinishCount = haveApplyList.size();
  402. }
  403. agentTimeList.sort(Comparator.comparing(AgentAndTimeVO::getStartTime));
  404. List<AgentAndTimeVO> tobeInsertTimeList = new ArrayList<>();
  405. // 考生已经完成预约-不做处理
  406. if (studentApplyFinishCount >= vo.getApplyNumber()) {
  407. vo.setAgentTimeList(tobeInsertTimeList);
  408. continue;
  409. }
  410. // 只预约了部分
  411. if (studentApplyFinishCount > 0 && studentApplyFinishCount < vo.getApplyNumber()) {
  412. List<AgentAndTimeVO> availableList = listAvailableTime(haveApplyList, agentTimeList);
  413. // 需要填充的次数
  414. tobeInsertTimeList.addAll(availableList);
  415. }
  416. // 未预约
  417. if (studentApplyFinishCount == 0) {
  418. tobeInsertTimeList = agentTimeList;
  419. }
  420. vo.setAgentTimeList(tobeInsertTimeList);
  421. }
  422. }
  423. private List<AgentAndTimeVO> listAvailableTime(List<StudentApplyEntity> haveApplyList,
  424. List<AgentAndTimeVO> agentTimeList) {
  425. List<AgentAndTimeVO> availableList = new ArrayList<>();
  426. for (AgentAndTimeVO time : agentTimeList) {
  427. boolean flag = false;
  428. for (StudentApplyEntity apply : haveApplyList) {
  429. if (time.getTimePeriodId().equals(apply.getTimePeriodId())) {
  430. flag = true;
  431. break;
  432. }
  433. }
  434. if (!flag) {
  435. availableList.add(time);
  436. }
  437. }
  438. return availableList;
  439. }
  440. private void checkStudentApplyTime(List<StudentImportVO> applyList, Integer days,
  441. List<Map<String, Object>> failRecords) {
  442. for (int i = 0; i < applyList.size(); i++) {
  443. StudentImportVO vo = applyList.get(i);
  444. List<AgentAndTimeVO> agentTimeList = vo.getAgentTimeList();
  445. if (agentTimeList.size() > vo.getApplyNumber()) {
  446. failRecords.add(newError(i + 1, " 导入考生预约数量大于考生可预约的数量"));
  447. }
  448. boolean isDuplicate = agentTimeList.stream()
  449. .collect(Collectors.groupingBy(AgentAndTimeVO::getTimePeriodId, Collectors.counting())).entrySet()
  450. .stream().anyMatch(entry -> entry.getValue() > 1);
  451. if (isDuplicate) {
  452. failRecords.add(newError(i + 1, " 预约的时间不能相同"));
  453. }
  454. LocalDate day = LocalDate.now().plusDays(days);
  455. long epochMilli = day.plusDays(1).atStartOfDay(ZoneId.systemDefault()).minusSeconds(1).toInstant()
  456. .toEpochMilli();
  457. for (AgentAndTimeVO time : agentTimeList) {
  458. if (time.getStartTime() <= epochMilli) {
  459. failRecords.add(newError(i + 1, " 预约的考试时间,必须在可取消的时间之后"));
  460. }
  461. }
  462. }
  463. }
  464. private ApplyTaskEntity getApplyTask() {
  465. LambdaQueryWrapper<ApplyTaskEntity> wrapper = new LambdaQueryWrapper<ApplyTaskEntity>()
  466. .eq(ApplyTaskEntity::getEnable, Boolean.TRUE);
  467. ApplyTaskEntity task = applyTaskService.getOne(wrapper);
  468. if (task == null)
  469. throw new StatusException("未开启预约任务");
  470. return task;
  471. }
  472. private List<StudentApplyEntity> saveStdApply(int row, StudentImportVO vo, Long userId,
  473. List<Map<String, Object>> failRecords) {
  474. List<StudentApplyEntity> ApplyList = new ArrayList<>();
  475. List<AgentAndTimeVO> agentTimeList = vo.getAgentTimeList();
  476. String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, vo.getStudentId());
  477. RLock studentApplyLock = (RLock) concurrentService.getLock(studentApplyLockKey);
  478. try {
  479. if (!studentApplyLock.tryLock()) {
  480. log.warn("获取锁失败,考生在同时操作预约!lockKey:{}", studentApplyLockKey);
  481. } else {
  482. log.warn("获取锁成功!lockKey:{}", studentApplyLockKey);
  483. for (AgentAndTimeVO agentTime : agentTimeList) {
  484. // 已经预约的容量和考点的容量从redis中获取
  485. int haveApplyCount = cacheService.getApplyFinishCount(agentTime.getAgentId(),
  486. agentTime.getTimePeriodId());
  487. int examSiteCount = cacheService.getApplyTotalCount(agentTime.getAgentId());
  488. if (haveApplyCount >= examSiteCount) {
  489. ExamSiteEntity examSite = examSiteService.getById(agentTime.getAgentId());
  490. failRecords.add(newError(row + 1, " 考点【" + examSite.getName() + "】的容量已满"));
  491. } else {
  492. StudentApplyEntity entity = new StudentApplyEntity();
  493. entity.setStudentId(vo.getStudentId());
  494. entity.setExamSiteId(agentTime.getAgentId());
  495. entity.setTimePeriodId(agentTime.getTimePeriodId());
  496. entity.setCancel(Boolean.FALSE);
  497. entity.setOperateId(userId);
  498. StudentApplyEntity existStudentApply = findStudentApply(entity);
  499. if (existStudentApply != null) {
  500. existStudentApply.setCancel(Boolean.FALSE);
  501. baseMapper.updateById(existStudentApply);
  502. } else {
  503. baseMapper.insert(entity);
  504. }
  505. ApplyRecordCacheBean bean = new ApplyRecordCacheBean();
  506. bean.setStudentId(vo.getStudentId());
  507. bean.setExamSiteId(agentTime.getAgentId());
  508. bean.setTimePeriodId(agentTime.getTimePeriodId());
  509. bean.setCancel(Boolean.FALSE);
  510. bean.setOperateId(userId);
  511. bean.setOperateTime(System.currentTimeMillis());
  512. // 先推送至预约队列
  513. cacheService.pushStudentApplyRecordQueue(bean);
  514. cacheService.saveStudentApplyRecord(bean);
  515. cacheService.increaseApplyFinishCount(agentTime.getAgentId(), agentTime.getTimePeriodId());
  516. }
  517. }
  518. }
  519. } catch (Exception e) {
  520. log.error("导入预考失败,错误原因:", e.getMessage());
  521. throw new StatusException("导入预考失败,请稍后再试!", e);
  522. } finally {
  523. try {
  524. if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) {
  525. studentApplyLock.unlock();
  526. log.info("解锁成功!lockKey:{}", studentApplyLockKey);
  527. }
  528. } catch (Exception e) {
  529. log.warn(e.getMessage());
  530. }
  531. }
  532. return ApplyList;
  533. }
  534. private TimePeriodEntity checkTimePeriod(String timePeriod, Map<String, TimePeriodEntity> timeCache) {
  535. if (timePeriod == null || timePeriod.split(" ").length != 2) {
  536. throw new StatusException(" 预约时段格式不正确");
  537. }
  538. String[] arr = timePeriod.split("-");
  539. String startTime = arr[0] + ":00";
  540. String endTime = startTime.substring(0, startTime.indexOf("日") + 1) + " " + arr[1] + ":00";
  541. Long startTimeLong = DateUtil.getLongTimeByZHDate(startTime);
  542. Long endTimeLong = DateUtil.getLongTimeByZHDate(endTime);
  543. if (timeCache.get(startTimeLong + "-" + endTimeLong) == null) {
  544. throw new StatusException(" 预约时段不存在");
  545. }
  546. return timeCache.get(startTimeLong + "-" + endTimeLong);
  547. }
  548. private Map<String, TimePeriodEntity> getTimePeriodCache() {
  549. Map<String, TimePeriodEntity> map = new HashMap<>();
  550. LambdaQueryWrapper<TimePeriodEntity> lm = new LambdaQueryWrapper<>();
  551. lm.eq(TimePeriodEntity::getApplyTaskId, getApplyTask().getId());
  552. List<TimePeriodEntity> timeList = timePeriodService.list(lm);
  553. for (TimePeriodEntity time : timeList) {
  554. map.put(time.getStartTime() + "-" + time.getEndTime(), time);
  555. }
  556. return map;
  557. }
  558. private Map<String, Long> getTeachingCache(Integer level) {
  559. LambdaQueryWrapper<CategoryEntity> lm = new LambdaQueryWrapper<>();
  560. lm.eq(CategoryEntity::getEnable, Boolean.TRUE);
  561. lm.eq(CategoryEntity::getLevel, level == null ? CategoryLevel.TEACHING.getValue() : level);
  562. List<CategoryEntity> categoryList = categoryService.list(lm);
  563. return categoryList.stream().collect(Collectors.toMap(CategoryEntity::getName, CategoryEntity::getId));
  564. }
  565. private Map<String, Long> getAgentCache() {
  566. LambdaQueryWrapper<ExamSiteEntity> lm = new LambdaQueryWrapper<>();
  567. lm.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
  568. List<ExamSiteEntity> categoryList = examSiteService.list(lm);
  569. return categoryList.stream().collect(Collectors.toMap(ExamSiteEntity::getName, ExamSiteEntity::getId));
  570. }
  571. private StudentEntity checkStd(String studentCode, String name, String identityNumber, ApplyTaskEntity task) {
  572. LambdaQueryWrapper<StudentEntity> lm = new LambdaQueryWrapper<>();
  573. lm.eq(StudentEntity::getStudentCode, studentCode);
  574. lm.eq(StudentEntity::getName, name);
  575. lm.eq(StudentEntity::getIdentityNumber, identityNumber);
  576. lm.eq(StudentEntity::getApplyTaskId, task.getId());
  577. StudentEntity student = studentService.getOne(lm);
  578. if (student == null) {
  579. throw new StatusException(" 考生信息填写错误");
  580. }
  581. return student;
  582. }
  583. private Map<String, Object> newError(int lineNum, String msg) {
  584. Map<String, Object> map = new HashMap<>();
  585. map.put("lineNum", lineNum);
  586. map.put("msg", msg);
  587. return map;
  588. }
  589. private String trimAndNullIfBlank(String s) {
  590. if (StringUtils.isBlank(s)) {
  591. return null;
  592. }
  593. return s.trim();
  594. }
  595. @Override
  596. public void autoAssign(Long taskId, Long userId) {
  597. checkAfterOpenTime();
  598. // 未完成预约的考生
  599. List<StudentEntity> studentList = studentService.listNoFinishStudent(taskId, Boolean.FALSE);
  600. Map<Long, List<StudentEntity>> noFinishApplyMap = studentList.stream().collect(Collectors.groupingBy(StudentEntity::getCategoryId));
  601. // 考位是否充足
  602. List<TimePeriodEntity> timeList = listTimePeriod(taskId);
  603. timeList = listNoCancelExamTimePeriod(timeList, taskId);
  604. if (timeList == null || timeList.isEmpty()) {
  605. throw new StatusException("可以取消预约的考试时段为0");
  606. }
  607. checkTeachingCapacity(noFinishApplyMap, timeList);
  608. RLock lock = (RLock) concurrentService.getLock(CacheConstants.LOCK_AUTO_APPLY);
  609. if (lock.isLocked()) {
  610. throw new StatusException("其他老师正在执行自动分配,请不要重复执行!");
  611. }
  612. //异步执行
  613. studentAutoAssignService.autoAssign(taskId, userId, timeList);
  614. }
  615. private void checkTeachingCapacity(Map<Long, List<StudentEntity>> map, List<TimePeriodEntity> timeList) {
  616. for (Long key : map.keySet()) {
  617. List<ExamSiteEntity> siteList = listExamSite(key, null);
  618. // 总考位数量
  619. Integer total = siteList.stream().mapToInt(ExamSiteEntity::getCapacity).sum() * timeList.size();
  620. // 已经预约的数量
  621. List<Long> examSiteIds = siteList.stream().map(BaseEntity::getId).collect(Collectors.toList());
  622. List<Long> timePeriodIds = timeList.stream().map(BaseEntity::getId).collect(Collectors.toList());
  623. Integer haveApplyNum = baseMapper.getHaveApplyCount(examSiteIds, timePeriodIds, Boolean.FALSE);
  624. // 未预约的数量
  625. Integer noApplyNum = getNoApplyNum(map.get(key));
  626. if (noApplyNum > total - haveApplyNum) {
  627. CategoryEntity category = categoryService.getById(key);
  628. throw new StatusException("【" + category.getName() + "】教学点考位不足!剩余的考位数量:【" + (total - haveApplyNum) + "】,实际需要的考位数量:【" + noApplyNum + "】");
  629. }
  630. }
  631. }
  632. private Integer getNoApplyNum(List<StudentEntity> list) {
  633. int noApplyNum = 0;
  634. for (StudentEntity student : list) {
  635. if (student.getApplyNumber() == 1) {
  636. noApplyNum++;
  637. } else if (student.getApplyNumber() > 1) {
  638. int haveApplyNum = cacheService.getStudentApplyFinishCount(student.getId());
  639. noApplyNum = noApplyNum + (student.getApplyNumber() - haveApplyNum);
  640. }
  641. }
  642. return noApplyNum;
  643. }
  644. private List<TimePeriodEntity> listNoCancelExamTimePeriod(List<TimePeriodEntity> timeList, Long taskId) {
  645. ApplyTaskEntity task = applyTaskService.getById(taskId);
  646. Long longToday = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(new Date()) + " 00:00:00");
  647. Date today = new Date(longToday);
  648. Date otherDay = DateUtil.addValues(today, Calendar.DAY_OF_MONTH, task.getAllowApplyCancelDays());
  649. Long longOtherDay = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(otherDay) + " 23:59:59");
  650. return timeList.stream().filter(time -> time.getStartTime() > longOtherDay).collect(Collectors.toList());
  651. }
  652. private StudentApplyEntity findStudentApply(StudentApplyEntity studentApply) {
  653. LambdaQueryWrapper<StudentApplyEntity> lm = new LambdaQueryWrapper<>();
  654. lm.eq(StudentApplyEntity::getExamSiteId, studentApply.getExamSiteId());
  655. lm.eq(StudentApplyEntity::getTimePeriodId, studentApply.getTimePeriodId());
  656. lm.eq(StudentApplyEntity::getStudentId, studentApply.getStudentId());
  657. return baseMapper.selectOne(lm);
  658. }
  659. private void checkAfterOpenTime() {
  660. ApplyTaskEntity task = getApplyTask();
  661. Date selfEndTime = new Date(task.getSelfApplyEndTime());
  662. Date openStartTime = new Date(task.getOpenApplyStartTime());
  663. if (!DateUtil.isBetwwen(selfEndTime, openStartTime)) {
  664. throw new StatusException("自动分配,时间必须要在第一阶段结束之后,第三阶段开始之前");
  665. }
  666. }
  667. private List<StudentApplyEntity> listStudentApply(Long stdId) {
  668. LambdaQueryWrapper<StudentApplyEntity> lm = new LambdaQueryWrapper<>();
  669. lm.eq(StudentApplyEntity::getStudentId, stdId);
  670. lm.eq(StudentApplyEntity::getCancel, Boolean.FALSE);
  671. return this.baseMapper.selectList(lm);
  672. }
  673. private List<TimePeriodEntity> listTimePeriod(Long taskId) {
  674. LambdaQueryWrapper<TimePeriodEntity> wrapper = new LambdaQueryWrapper<>();
  675. wrapper.eq(TimePeriodEntity::getApplyTaskId, taskId);
  676. wrapper.orderByAsc(TimePeriodEntity::getStartTime);
  677. List<TimePeriodEntity> timeList = timePeriodService.list(wrapper);
  678. if (timeList.isEmpty()) {
  679. throw new StatusException("考试时段未设置");
  680. }
  681. return timeList;
  682. }
  683. private List<ExamSiteEntity> listExamSite(Long categoryId, Long examSiteId) {
  684. LambdaQueryWrapper<ExamSiteEntity> wrapper = new LambdaQueryWrapper<>();
  685. wrapper.eq(ExamSiteEntity::getCategoryId, categoryId);
  686. wrapper.eq(examSiteId != null, ExamSiteEntity::getId, examSiteId);
  687. wrapper.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
  688. return examSiteService.list(wrapper);
  689. }
  690. @Transactional
  691. @Override
  692. public void autoLayout(Long teachingId) {
  693. ApplyTaskEntity applyTask = getApplyTask();
  694. String autoLayoutLockKey = String.format(CacheConstants.LOCK_ARRANGE_EXAM,
  695. DateUtil.formatShortDateString(new Date()));
  696. RLock autoLayoutLock = (RLock) concurrentService.getLock(autoLayoutLockKey);
  697. try {
  698. if (!autoLayoutLock.tryLock()) {
  699. log.warn("获取锁失败,已有线程在执行排考!lockKey:{}", autoLayoutLock);
  700. return;
  701. }
  702. log.warn("获取锁成功!lockKey:{}", autoLayoutLockKey);
  703. // 1.根据当前日期,查询不能取消的时段
  704. List<TimePeriodEntity> timePeriodList = listTimePeriod(applyTask.getId());
  705. List<TimePeriodEntity> noCancelTimePeroidList = listNoCancelApplyTimePeriod(timePeriodList,
  706. applyTask.getAllowApplyCancelDays());
  707. if (noCancelTimePeroidList.isEmpty()) {
  708. log.warn("当前时间不在取消预约范围内");
  709. return;
  710. }
  711. // 2.查询考试日期的待排考的考生
  712. List<StudentApplyEntity> toBeLayoutStudentList = this.baseMapper.listTimePeriod(
  713. noCancelTimePeroidList.stream().map(item -> item.getId()).collect(Collectors.toList()),
  714. Boolean.FALSE);
  715. if (toBeLayoutStudentList == null || toBeLayoutStudentList.isEmpty()) {
  716. log.warn("没有待排考的考生");
  717. return;
  718. }
  719. // 3.开始排考
  720. Map<Long, List<StudentApplyEntity>> toBeLayoutStudentMap = toBeLayoutStudentList.stream()
  721. .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId));
  722. for (Long examSiteId : toBeLayoutStudentMap.keySet()) {
  723. Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap = toBeLayoutStudentMap.get(examSiteId).stream()
  724. .collect(Collectors.groupingBy(StudentApplyEntity::getTimePeriodId));
  725. List<ExamRoomEntity> roomList = listExamRoom(examSiteId);
  726. if (roomList.isEmpty()) {
  727. log.warn("{}:未设置考场", examSiteId);
  728. return;
  729. }
  730. ExamSiteEntity examSite = examSiteService.getById(examSiteId);
  731. layoutStudentByTimePeriod(applyTask.getId(), examSite, roomList, timeLayoutStudentMap);
  732. }
  733. } catch (StatusException e) {
  734. log.error(e.getMessage());
  735. e.printStackTrace();
  736. } finally {
  737. try {
  738. // 解锁前检查当前线程是否持有该锁
  739. if (autoLayoutLock.isLocked() && autoLayoutLock.isHeldByCurrentThread()) {
  740. autoLayoutLock.unlock();
  741. log.info("解锁成功!lockKey:{}", autoLayoutLockKey);
  742. }
  743. } catch (Exception e) {
  744. log.warn(e.getMessage());
  745. }
  746. }
  747. }
  748. private void layoutStudentByTimePeriod(Long taskId, ExamSiteEntity examSite, List<ExamRoomEntity> roomList,
  749. Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap) {
  750. for (Long timePeriodId : timeLayoutStudentMap.keySet()) {
  751. List<StudentApplyEntity> studentApplyList = timeLayoutStudentMap.get(timePeriodId);
  752. layoutStudentToRoom(taskId, examSite, roomList, studentApplyList, timePeriodService.getById(timePeriodId));
  753. }
  754. }
  755. private void layoutStudentToRoom(Long taskId, ExamSiteEntity examSite, List<ExamRoomEntity> roomList,
  756. List<StudentApplyEntity> studentApplyList, TimePeriodEntity timePeriod) {
  757. Integer timePeriodOrder = getTimePeriodOrder(taskId, timePeriod);
  758. for (ExamRoomEntity room : roomList) {
  759. int num = 0;
  760. for (Iterator<StudentApplyEntity> iterator = studentApplyList.iterator(); iterator.hasNext(); ) {
  761. StudentApplyEntity student = iterator.next();
  762. if (num >= room.getCapacity())
  763. break;
  764. String seatNumber = StringUtils.leftPad(String.valueOf(++num), 3, '0');
  765. student.setExamRoomId(room.getId());
  766. student.setSeatNumber(seatNumber);
  767. student.setTicketNumber(
  768. generateTicketNumber(timePeriodOrder, examSite.getCode(), room.getCode(), seatNumber));
  769. this.baseMapper.updateById(student);
  770. iterator.remove();
  771. }
  772. }
  773. }
  774. private Integer getTimePeriodOrder(Long taskId, TimePeriodEntity timePeriod) {
  775. List<TimePeriodEntity> timeList = listTimePeriod(taskId);
  776. List<TimePeriodEntity> sameDayTimeList = listSameDayTimePeriod(timeList, timePeriod.getStartTime());
  777. for (int i = 0; i < sameDayTimeList.size(); i++) {
  778. TimePeriodEntity time = sameDayTimeList.get(i);
  779. if (time.getStartTime().equals(timePeriod.getStartTime()))
  780. return i + 1;
  781. }
  782. return 0;
  783. }
  784. private List<TimePeriodEntity> listSameDayTimePeriod(List<TimePeriodEntity> timeList, Long startTime) {
  785. Date day = new Date(startTime);
  786. List<TimePeriodEntity> resultList = new ArrayList<>();
  787. for (TimePeriodEntity time : timeList) {
  788. if (isSameDay(day, new Date(time.getStartTime())))
  789. resultList.add(time);
  790. }
  791. return resultList.stream().sorted(Comparator.comparing(TimePeriodEntity::getStartTime))
  792. .collect(Collectors.toList());
  793. }
  794. private String generateTicketNumber(Integer timePeriodOrder, String examSiteCode, String roomCode,
  795. String seatNumber) {
  796. return DateUtil.formatShortDateString(new Date()) + timePeriodOrder + examSiteCode + roomCode + seatNumber;
  797. }
  798. public List<ExamRoomEntity> listExamRoom(Long examSiteId) {
  799. LambdaQueryWrapper<ExamRoomEntity> wrapper = new LambdaQueryWrapper<>();
  800. wrapper.eq(ExamRoomEntity::getExamSiteId, examSiteId);
  801. wrapper.orderByAsc(ExamRoomEntity::getCode);
  802. return examRoomService.list(wrapper);
  803. }
  804. private List<TimePeriodEntity> listNoCancelApplyTimePeriod(List<TimePeriodEntity> list,
  805. Integer allowApplyCancelDays) {
  806. Date noCancelDate = getNoCancelApplyDate(allowApplyCancelDays);
  807. List<TimePeriodEntity> noCancelTimePeroidList = new ArrayList<>();
  808. for (TimePeriodEntity time : list) {
  809. if (isSameDay(noCancelDate, new Date(time.getStartTime())))
  810. noCancelTimePeroidList.add(time);
  811. }
  812. return noCancelTimePeroidList;
  813. }
  814. private Date getNoCancelApplyDate(Integer allowApplyCancelDays) {
  815. return DateUtil.addValues(Calendar.DAY_OF_MONTH, allowApplyCancelDays);
  816. }
  817. @Override
  818. public File downloadSignIn(Long teachingId, Long agentId, Long examDate) {
  819. ApplyTaskEntity applyTask = getApplyTask();
  820. List<TimePeriodEntity> timePeriodList = listTimePeriod(applyTask.getId());
  821. List<TimePeriodEntity> noCancelTimePeroidList = listSameDayTimePeriod(timePeriodList, examDate);
  822. if (noCancelTimePeroidList.isEmpty()) {
  823. throw new StatusException("当前时间没有可下载的签到表");
  824. }
  825. List<ExamSiteEntity> siteList = listExamSite(teachingId, agentId);
  826. if (siteList.isEmpty())
  827. throw new StatusException("当前教学点下没有可用的考点");
  828. CategoryEntity category = categoryService.getById(teachingId);
  829. File tempFolder = new File("temp");
  830. if (!tempFolder.exists()) {
  831. tempFolder.mkdirs();
  832. }
  833. ZipWriter writer = null;
  834. File zipFile = new File(tempFolder, category.getName() + "签到表.zip");
  835. try {
  836. writer = ZipWriter.create(zipFile);
  837. List<File> fileList = new ArrayList<>();
  838. for (ExamSiteEntity site : siteList) {
  839. fileList.addAll(downloadByExamSite(writer, applyTask.getName(), site, noCancelTimePeroidList));
  840. }
  841. if (fileList.isEmpty()) {
  842. throw new StatusException("暂未排考,请等待排考后下载");
  843. }
  844. } catch (IOException e) {
  845. e.printStackTrace();
  846. throw new StatusException("文件写入异常");
  847. }
  848. writer.close();
  849. return zipFile;
  850. }
  851. public List<File> downloadByExamSite(ZipWriter writer, String taskName, ExamSiteEntity site,
  852. List<TimePeriodEntity> timeList) throws IOException {
  853. List<File> fileList = new ArrayList<>();
  854. List<ExamRoomEntity> roomList = listExamRoom(site.getId());
  855. MaterialTitleInfo title = new MaterialTitleInfo();
  856. title.setTaskName(taskName);
  857. title.setSiteName(site.getName());
  858. for (TimePeriodEntity time : timeList) {
  859. title.setTimePeriod(DateUtil.getStartAndEndTime(time.getStartTime(), time.getEndTime()));
  860. for (ExamRoomEntity room : roomList) {
  861. title.setAddress(room.getAddress());
  862. title.setRoomCode(room.getCode());
  863. List<StudentApplyVO> studentList = baseMapper.listStudentApply(time.getId(), room.getId());
  864. if (!studentList.isEmpty()) {
  865. File file = materialService.generateSignInForm(title, studentList);
  866. fileList.add(file);
  867. writer.write(file, title.getSiteName(),
  868. title.getTimePeriod() + " 第【" + title.getRoomCode() + "】考场" + ".pdf");
  869. }
  870. }
  871. }
  872. return fileList;
  873. }
  874. /**
  875. * 获取某考点某时段的“已预约数量”
  876. */
  877. @Override
  878. public int countApplyFinishForExamSiteAndTimePeriod(Long examSiteId, Long timePeriodId) {
  879. Integer value = baseMapper.countApplyFinishForExamSiteAndTimePeriod(examSiteId, timePeriodId);
  880. return value != null ? value : 0;
  881. }
  882. @Override
  883. public List<SignInVO> listSignInDate(Long taskId) {
  884. List<SignInVO> signInList = new ArrayList<>();
  885. ApplyTaskEntity task = null;
  886. if (taskId != null) {
  887. task = applyTaskService.getById(taskId);
  888. } else {
  889. LambdaQueryWrapper<ApplyTaskEntity> wrapper = new LambdaQueryWrapper<ApplyTaskEntity>()
  890. .eq(ApplyTaskEntity::getEnable, Boolean.TRUE);
  891. task = applyTaskService.getOne(wrapper);
  892. }
  893. if (task == null) {
  894. log.warn("当前没有开启的任务");
  895. return signInList;
  896. }
  897. List<TimePeriodEntity> timePeriodList = listTimePeriod(task.getId());
  898. if (timePeriodList.isEmpty()) {
  899. log.warn("未配置考试时段");
  900. return signInList;
  901. }
  902. String todayStr = DateUtil.formatShortSplitDateString(new Date());
  903. Long longToday = DateUtil.getLongTimeByDate(todayStr + " 00:00:00");
  904. Date today = new Date(longToday);
  905. if (isInTimePeriod(today, timePeriodList)) {
  906. SignInVO vo = new SignInVO();
  907. vo.setExamDate(longToday);
  908. signInList.add(vo);
  909. }
  910. for (int i = 1; i <= task.getAllowApplyCancelDays(); i++) {
  911. Date otherDay = DateUtil.addValues(today, Calendar.DAY_OF_MONTH, i);
  912. Long longOtherDay = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(otherDay) + " 00:00:00");
  913. if (isInTimePeriod(otherDay, timePeriodList)) {
  914. SignInVO vo = new SignInVO();
  915. vo.setExamDate(longOtherDay);
  916. signInList.add(vo);
  917. }
  918. }
  919. signInList.sort(Comparator.comparing(SignInVO::getExamDate));
  920. return signInList;
  921. }
  922. private boolean isInTimePeriod(Date date, List<TimePeriodEntity> timePeriodList) {
  923. for (TimePeriodEntity timePeriod : timePeriodList) {
  924. Date day = new Date(timePeriod.getStartTime());
  925. if (DateUtils.isSameDay(day, date)) {
  926. return true;
  927. }
  928. }
  929. return false;
  930. }
  931. }