package com.qmth.exam.reserve.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.qmth.boot.core.collection.PageResult; import com.qmth.boot.core.concurrent.service.ConcurrentService; import com.qmth.boot.core.exception.StatusException; import com.qmth.boot.tools.excel.ExcelReader; import com.qmth.boot.tools.excel.enums.ExcelType; import com.qmth.boot.tools.excel.model.DataMap; import com.qmth.boot.tools.io.ZipWriter; import com.qmth.exam.reserve.bean.Constants; import com.qmth.exam.reserve.bean.apply.ApplyRecordCacheBean; import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO; import com.qmth.exam.reserve.bean.category.CategoryCacheBean; import com.qmth.exam.reserve.bean.examsite.ExamSiteCacheBean; import com.qmth.exam.reserve.bean.login.LoginUser; import com.qmth.exam.reserve.bean.org.OrgInfo; import com.qmth.exam.reserve.bean.stdapply.*; import com.qmth.exam.reserve.bean.timeperiod.TimePeriodExamSiteBean; import com.qmth.exam.reserve.cache.CacheConstants; import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService; import com.qmth.exam.reserve.cache.impl.CategoryCacheService; import com.qmth.exam.reserve.cache.impl.ExamSiteCacheService; import com.qmth.exam.reserve.cache.impl.OrgCacheService; import com.qmth.exam.reserve.dao.StudentApplyDao; import com.qmth.exam.reserve.entity.*; import com.qmth.exam.reserve.entity.base.BaseEntity; import com.qmth.exam.reserve.enums.AsyncTaskType; import com.qmth.exam.reserve.enums.CategoryLevel; import com.qmth.exam.reserve.enums.EventType; import com.qmth.exam.reserve.enums.Role; import com.qmth.exam.reserve.service.*; import com.qmth.exam.reserve.template.execute.AutoAssignStudentService; import com.qmth.exam.reserve.template.execute.StudentApplyDetailExportService; import com.qmth.exam.reserve.template.execute.StudentApplyNoFinishExportService; import com.qmth.exam.reserve.util.DateUtil; import com.qmth.exam.reserve.util.JsonHelper; import com.qmth.exam.reserve.util.PageUtil; import com.qmth.exam.reserve.util.UnionUtil; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.redisson.api.RLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.time.LocalDate; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; import static org.apache.commons.lang3.time.DateUtils.isSameDay; /** * @Description 管理端-预约 */ @Service public class StudentApplyServiceImpl extends ServiceImpl implements StudentApplyService { private static final Logger log = LoggerFactory.getLogger(StudentApplyServiceImpl.class); private static final String[] EXCEL_HEADER = new String[]{"学号", "姓名", "证件号", "所属教学点", "预约考点1", "预约时段1", "预约考点2", "预约时段2", "预约考点3", "预约时段3", "预约考点4", "预约时段4"}; private static final int maxApplyNum = 3; @Autowired private ApplyTaskService applyTaskService; @Autowired private ExamSiteService examSiteService; @Autowired private TimePeriodService timePeriodService; @Autowired private ConcurrentService concurrentService; @Autowired private ApplyTaskCacheService cacheService; @Autowired private OperateLogService operateLogService; @Autowired private CategoryService categoryService; @Autowired private StudentService studentService; @Autowired private ExamSiteCacheService examSiteCacheService; @Autowired private CategoryCacheService categoryCacheService; @Autowired private MaterialGenerateService materialGenerateService; @Autowired private ExamRoomService examRoomService; @Autowired private AsyncTaskService asyncTaskService; @Autowired private StudentApplyNoFinishExportService studentApplyNoFinishExportService; @Autowired private StudentApplyDetailExportService studentApplyDetailExportService; @Autowired private AutoAssignStudentService autoAssignStudentService; @Autowired private OrgCacheService orgCacheService; @Autowired private TimePeriodExamRoomService timePeriodExamRoomService; @Override public PageResult page(StudentApplyReq req) { ApplyTaskEntity task = getApplyTask(); req.setTaskId(task.getId()); if (req.getTeachingId() != null) { List examSiteList = examSiteService.listExamSite(req.getTeachingId(), Boolean.FALSE); if (examSiteList == null || examSiteList.isEmpty()) { return new PageResult<>(); } List examSiteIds = examSiteList.stream().map(CategoryVO::getId).collect(Collectors.toList()); req.setExamSiteIds(examSiteIds.isEmpty() ? null : examSiteIds); } IPage iPage = baseMapper.page(new Page<>(req.getPageNumber(), req.getPageSize()), req); List records = iPage.getRecords(); for (StudentApplyVO record : records) { AgentAndRoomVO agentRoom = baseMapper.getAgentAndRoom(record.getId()); record.setAgentName(agentRoom.getAgentName()); record.setTeachingName(agentRoom.getTeachingName()); record.setApplyTeachingName(agentRoom.getApplyTeachingName()); record.setRoomName(agentRoom.getRoomName()); record.setUserName(agentRoom.getUserName()); // 是否可取消 TimePeriodEntity timePeriod = timePeriodService.getById(record.getTimePeriodId()); Date applyDate = DateUtils.truncate(new Date(timePeriod.getStartTime()), Calendar.DATE); Date canCancelDay = DateUtil.addValues(applyDate, Calendar.DAY_OF_MONTH, -task.getAllowApplyCancelDays()); if (new Date().after(canCancelDay)) { record.setCanCancelFlag(Boolean.FALSE); } else { record.setCanCancelFlag(Boolean.TRUE); } } return PageUtil.of(iPage); } private ApplyTaskEntity getApplyTask() { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ApplyTaskEntity::getEnable, Boolean.TRUE); ApplyTaskEntity task = applyTaskService.getOne(wrapper); if (task == null) { throw new StatusException("未开启预约任务"); } return task; } @Transactional @Override public void cancel(LoginUser user, Long id) { StudentApplyEntity studentApplyEntity = getById(id); if (studentApplyEntity == null) { log.warn("[cancel] 考生预约信息不存在, id:{} userAccount:{}", id, user.getAccount()); throw new StatusException("没有预约信息"); } if (studentApplyEntity.getTimePeriodId() == null) { log.warn("[cancel] 考生预约的时段为空, id:{} userAccount:{}", id, user.getAccount()); throw new StatusException("没有预约信息"); } TimePeriodEntity timePeriod = timePeriodService.getById(studentApplyEntity.getTimePeriodId()); if (timePeriod == null) { log.warn("[cancel] 考生预约的时段不存在, timePeriodId:{} userAccount:{}", studentApplyEntity.getTimePeriodId(), user.getAccount()); throw new StatusException("考试时段不存在"); } ApplyTaskEntity task = getApplyTask(); Date applyDate = DateUtils.truncate(new Date(timePeriod.getStartTime()), Calendar.DATE); Date canCancelDay = DateUtil.addValues(applyDate, Calendar.DAY_OF_MONTH, -task.getAllowApplyCancelDays()); if (new Date().after(canCancelDay)) { throw new StatusException("[cancel] 可取消时间已过,无法在取消"); } StudentEntity student = studentService.getById(studentApplyEntity.getStudentId()); if(student == null) { log.warn("[cancel] 考生不存在, studentId:{} ", studentApplyEntity.getStudentId()); throw new StatusException("考生不存在"); } if (concurrentService.isLocked(CacheConstants.LOCK_AUTO_APPLY)) { // 系统自动预约“任务执行期间”不允许取消预约 log.warn("[cancel] 系统自动预约中,不允许考生操作预约!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY); throw new StatusException(Constants.SYSTEM_BUSY); } // 教学点管理员,只能取消本教学点的考生 if (user.getRole().equals(Role.TEACHING) && !user.getCategoryId().equals(student.getCategoryId())) { log.warn("[cancel] 教学点管理员,只能取消本教学点的考生, userId:{}, studentId:{}", user.getId(), studentApplyEntity.getStudentId()); throw new StatusException("只能取消本教学点的考生"); } // 考生是否已经取消判断 ApplyRecordCacheBean studentApplyRecord = cacheService.getStudentApplyRecord(studentApplyEntity.getStudentId(), studentApplyEntity.getExamSiteId(), studentApplyEntity.getTimePeriodId()); if (studentApplyRecord != null && studentApplyRecord.getCancel()) { log.warn("[cancel] 考生已经取消,请稍后查看, studentId:{}", studentApplyEntity.getStudentId()); throw new StatusException("考生已经取消,请稍后查看"); } String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, studentApplyEntity.getStudentId()); RLock studentApplyLock = (RLock) concurrentService.getLock(studentApplyLockKey); try { if (!studentApplyLock.tryLock()) { log.warn("[cancel] 获取锁失败,同一个考生不允许同时操作取消预约, lockKey:{}", studentApplyLockKey); throw new StatusException(Constants.SYSTEM_BUSY); } else { //更新预约取消标志 studentApplyEntity.setCancel(Boolean.TRUE); updateById(studentApplyEntity); ApplyRecordCacheBean bean = new ApplyRecordCacheBean(); bean.setStudentId(studentApplyEntity.getStudentId()); bean.setExamSiteId(studentApplyEntity.getExamSiteId()); bean.setTimePeriodId(studentApplyEntity.getTimePeriodId()); bean.setCancel(Boolean.TRUE); bean.setOperateId(user.getId()); bean.setOperateTime(System.currentTimeMillis()); bean.setBizId(cacheService.increaseBizId()); // 推送至预约队列 /*boolean pushSuccess = cacheService.pushStudentApplyRecordQueue(bean); if (!pushSuccess) { log.error("[cancel] 预约消息推送失败,id:{}", id); throw new RuntimeException("取消失败,请稍后再试!"); }*/ // 保存至预约缓存 cacheService.saveStudentApplyRecord(bean); // 某考点某时段的“剩余可约数量”(归还1个被占数量) cacheService.increaseApplyAvailableCount(studentApplyEntity.getExamSiteId(), studentApplyEntity.getTimePeriodId()); } } catch (Exception e) { log.error("[cancel] 取消预约失败, msg:{}", e.getMessage()); throw new StatusException("取消预约失败,请稍后再试"); } finally { try { if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) { studentApplyLock.unlock(); log.info("[cancel] 解锁成功,lockKey:{}", studentApplyLock); } } catch (Exception e) { log.warn(e.getMessage()); } } //写入日志 operateLogService.insertOperateLog(user.getId(), EventType.CANCEL_APPLY, JsonHelper.toJson(studentApplyEntity)); } @Transactional @Override public List> importPreExam(LoginUser user, Long teachingId, Integer level, InputStream inputStream) { checkOpenTime(user.getOrgId()); List lineList; ExcelReader reader = ExcelReader.create(ExcelType.XLSX, inputStream, 0); try { lineList = reader.getDataMapList(); } catch (Exception e) { log.warn("[importPreExam] 读取预考数据失败,msg:{}", e.getMessage()); throw new StatusException("Excel解析失败,请检查文件格式"); } if (!Arrays.equals(EXCEL_HEADER, reader.getColumnNames())) { throw new StatusException("Excel表头错误,请不要修改模版中的表头"); } if (CollectionUtils.isEmpty(lineList)) { throw new StatusException("Excel中没有任何考生预约数据"); } List> failRecords = new ArrayList<>(); RLock lock = (RLock) concurrentService.getLock(CacheConstants.LOCK_AUTO_APPLY); try { if (!lock.tryLock()) { log.warn("[importPreExam] 其他教学点正在执行预约,获取锁失败!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY); throw new StatusException("系统繁忙,请稍后重试!"); } log.warn("[importPreExam] 获取锁成功!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY); CurrentApplyTaskVO task = cacheService.currentApplyTask(user.getOrgId()); // 开放式预约开始时间 Long openSelfStartTime = task.getOpenApplyStartTime(); // 开放式预约结束时间 Long openSelfEndTime = task.getOpenApplyEndTime(); long now = System.currentTimeMillis(); List applyList = new ArrayList<>(); //教学点缓存 Map teachingCache = getTeachingCache(level); //考点缓存 Map examSiteCache = getExamSiteCache(); //考试时段缓存 Map timePeriodCache = getTimePeriodCache(); //考点时段缓存 Map> timePeriodExamSiteCache = getTimePeriodExamSiteCache(task.getTaskId()); //考点所属教学点缓存 Map examSiteCategoryCache = getExamSiteCategoryCache(); for (int i = 0; i < lineList.size(); i++) { DataMap line = lineList.get(i); List agentTimeList = new ArrayList<>(); AgentAndTimeVO agentTime = new AgentAndTimeVO(); StudentImportVO apply = new StudentImportVO(); StringBuilder msg = new StringBuilder(); String studentCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[0])); if (StringUtils.isBlank(studentCode)) { msg.append(" 学号不能为空"); } String name = trimAndNullIfBlank(line.get(EXCEL_HEADER[1])); if (StringUtils.isBlank(name)) { msg.append(" 姓名不能为空"); } String identityNumber = trimAndNullIfBlank(line.get(EXCEL_HEADER[2])); if (StringUtils.isBlank(identityNumber)) { msg.append(" 证件号码不能为空"); } //有空行跳出循序-结束执行 if (StringUtils.isBlank(studentCode) && StringUtils.isBlank(name) && StringUtils.isBlank(identityNumber)) { failRecords.add(newError(i + 1, "该行数据为空,请检查")); break; } //判断考生是否存在 StudentEntity studentEntity = checkStudent(studentCode, name, identityNumber, task.getTaskId()); if (studentEntity == null) { msg.append(" 考生信息填写错误"); failRecords.add(newError(i + 1, msg.toString())); continue; } else { apply.setStudentId(studentEntity.getId()); apply.setApplyNumber(studentEntity.getApplyNumber()); } String teachingName = trimAndNullIfBlank(line.get(EXCEL_HEADER[3])); if (StringUtils.isBlank(teachingName)) { msg.append(" 考生所属教学点不能为空"); } Long categoryId = teachingCache.get(teachingName); if (categoryId == null) { msg.append(" 考生所属教学点不存在"); failRecords.add(newError(i + 1, msg.toString())); continue; } if (!studentEntity.getCategoryId().equals(categoryId)) { msg.append(" 导入考生的所属教学点和系统中的考生教学点不匹配"); } if (user.getRole().equals(Role.TEACHING) && !categoryId.equals(teachingId)) { msg.append(" 非本教学点考生"); } String examSiteName = trimAndNullIfBlank(line.get(EXCEL_HEADER[4])); if (StringUtils.isBlank(examSiteName)) { msg.append(" 预约考点1不能为空"); } //考点所属教学点 Long applyTeachingId; Long examSiteId = examSiteCache.get(examSiteName); if (examSiteId == null) { msg.append(" 预约考点1不存在"); } else { applyTeachingId = examSiteCategoryCache.get(examSiteId); if ((now < openSelfStartTime || now > openSelfEndTime) && !categoryId.equals(applyTeachingId)) { msg.append(" 未到自由预约时间,不能预约其他教学点下的考点"); } //教学点管理员导入-不能预约其他教学点下的考点 if (user.getRole().equals(Role.TEACHING) && !categoryId.equals(applyTeachingId)) { msg.append(" 不能预约其他教学点下的考点"); } } //预约的时段 String timePeriodStr = trimAndNullIfBlank(line.get(EXCEL_HEADER[5])); if (StringUtils.isBlank(timePeriodStr)) { msg.append(" 预约时段1不能为空"); } TimePeriodEntity timePeriodEntity; Map examSiteTimePeriodMap; try { //考点下的预约时段 examSiteTimePeriodMap = timePeriodExamSiteCache.get(examSiteId); if (examSiteTimePeriodMap != null) { timePeriodCache = examSiteTimePeriodMap; } timePeriodEntity = checkTimePeriod(timePeriodStr, timePeriodCache); agentTime.setAgentId(examSiteId); agentTime.setTimePeriodId(timePeriodEntity.getId()); agentTime.setStartTime(timePeriodEntity.getStartTime()); agentTimeList.add(agentTime); } catch (StatusException e) { msg.append(" ").append(e.getMessage()); } int index = 0; for (int num = 1; num <= maxApplyNum; num++) { String agentName = trimAndNullIfBlank(line.get(EXCEL_HEADER[num + 5 + index])); String timePeriodName = trimAndNullIfBlank(line.get(EXCEL_HEADER[num + 6 + index])); if (StringUtils.isBlank(agentName) && StringUtils.isBlank(timePeriodName)) { apply.setAgentTimeList(agentTimeList); applyList.add(apply); if (msg.length() > 0) { failRecords.add(newError(i + 1, msg.toString())); } break; } else { examSiteId = examSiteCache.get(agentName); if (examSiteId == null) { msg.append(" 预约考点").append(num + 1).append("不存在"); } else { applyTeachingId = examSiteCategoryCache.get(examSiteId); if ((now < openSelfStartTime || now > openSelfEndTime) && !applyTeachingId.equals(categoryId)) { msg.append(" 未到自由预约时间,不允许预约其他教学点的考点"); } //教学点管理员导入-不能预约其他教学点下的考点 if (user.getRole().equals(Role.TEACHING) && !categoryId.equals(applyTeachingId)) { msg.append(" 不能预约其他教学点下的考点"); } } try { //考点下的预约时段 examSiteTimePeriodMap = timePeriodExamSiteCache.get(examSiteId); if (examSiteTimePeriodMap != null) { timePeriodCache = examSiteTimePeriodMap; } timePeriodEntity = checkTimePeriod(timePeriodName, timePeriodCache); agentTime = new AgentAndTimeVO(); agentTime.setAgentId(examSiteId); agentTime.setTimePeriodId(timePeriodEntity.getId()); agentTime.setStartTime(timePeriodEntity.getStartTime()); agentTimeList.add(agentTime); } catch (StatusException e) { msg.append(" ").append(e.getMessage()); } } ++index; } // 处理导入预考模版中填满的情况 if(apply.getAgentTimeList() == null || apply.getAgentTimeList().isEmpty()) { apply.setAgentTimeList(agentTimeList); applyList.add(apply); //导入预考模版中填满的情况 如果有错误,加入到错误记录中(bug解决) if(msg.length() > 0) { failRecords.add(newError(i + 1, msg.toString())); } } //检测考生是否已经完成预约和超出预约的最大次数 checkStudentTimePeriod(apply, i, task.getAllowApplyCancelDays(), failRecords); } //考点容量判断 checkExamSiteTimeCapacity(applyList, failRecords); if (CollectionUtils.isNotEmpty(failRecords)) { return failRecords; } //保存数据 for (int i = 0; i < applyList.size(); i++) { StudentImportVO vo = applyList.get(i); try { List agentTimeList = vo.getAgentTimeList(); String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, vo.getStudentId()); RLock studentApplyLock = (RLock) concurrentService.getLock(studentApplyLockKey); try { if (!studentApplyLock.tryLock()) { log.warn("[importPreExam] 获取锁失败,考生在同时操作预约,lockKey:{}", studentApplyLockKey); } else { log.warn("[importPreExam] 获取锁成功,lockKey:{}", studentApplyLockKey); for (AgentAndTimeVO agentTimeVO : agentTimeList) { StudentApplyEntity entity = new StudentApplyEntity(); entity.setStudentId(vo.getStudentId()); entity.setExamSiteId(agentTimeVO.getAgentId()); entity.setTimePeriodId(agentTimeVO.getTimePeriodId()); entity.setCancel(Boolean.FALSE); entity.setOperateId(user.getId()); //保存考生预约 boolean successFlag = saveOrUpdateStudentApply(entity); if (successFlag) { // 队列bean ApplyRecordCacheBean bean = new ApplyRecordCacheBean(); bean.setStudentId(vo.getStudentId()); bean.setExamSiteId(agentTimeVO.getAgentId()); bean.setTimePeriodId(agentTimeVO.getTimePeriodId()); bean.setCancel(Boolean.FALSE); bean.setOperateId(user.getId()); bean.setOperateTime(System.currentTimeMillis()); bean.setBizId(cacheService.increaseBizId()); // 某考点某时段的“剩余可约数量”(抢占1个数量) boolean takeSuccess = cacheService.decreaseApplyAvailableCount(bean.getExamSiteId(), bean.getTimePeriodId()); if (!takeSuccess) { log.warn("[importPreExam] 预约失败,当前预约时段已约满!examSiteId:{} timePeriodId:{} studentId:{}", bean.getExamSiteId(), bean.getTimePeriodId(), bean.getStudentId()); //考点 ExamSiteCacheBean examSiteBean = examSiteCacheService.getExamSiteById(bean.getExamSiteId()); //时段 TimePeriodEntity timePeriod = timePeriodService.getById(bean.getTimePeriodId()); String message = MessageFormat.format("第{0}行,预约考点:{1},预约时段:{2}", i + 1, examSiteBean.getExamSiteName(), DateUtil.getStartAndEndTime(timePeriod.getStartTime(), timePeriod.getEndTime())); throw new StatusException(message); } // 保存至预约缓存 cacheService.saveStudentApplyRecord(bean); } } } } catch (Exception e) { log.error("[importPreExam] 导入预约失败,msg:{}", e.getMessage()); throw new StatusException("导入预考失败,错误原因:" + e.getMessage()); } finally { try { if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) { studentApplyLock.unlock(); log.info("[importPreExam] 解锁成功!lockKey:{}", studentApplyLockKey); } } catch (Exception e) { log.warn(e.getMessage()); } } } catch (StatusException e) { failRecords.add(newError(i + 1, "系统异常")); log.error("[importPreExam] 导入异常", e); } } } catch (StatusException e) { log.error("[importPreExam] 导入预考失败,msg:{}", e.getMessage()); throw new StatusException(e.getMessage()); } finally { try { // 解锁前检查当前线程是否持有该锁 if (lock.isLocked() && lock.isHeldByCurrentThread()) { lock.unlock(); log.info("解锁成功!lockKey:{}", CacheConstants.LOCK_AUTO_APPLY); } } catch (Exception e) { log.warn(e.getMessage()); } } return failRecords; } private Map> getTimePeriodExamSiteCache(Long taskId) { //学校管理员设置的所有预约时段 List timePeriodList = timePeriodService.listTimePeriodByTask(taskId); Map> timePeriodExamSiteCache = new HashMap<>(); List timePeriodExamSiteBeans = timePeriodService.listTimePeriodByExamSiteId(taskId, null); /*// 过滤开启的预约时段 timePeriodExamSiteBeans = timePeriodExamSiteBeans.stream() .filter(TimePeriodExamSiteBean::getEnable) .collect(Collectors.toList());*/ // 按照考点分组 Map> timePeriodMap = timePeriodExamSiteBeans.stream() .collect(Collectors.groupingBy(TimePeriodExamSiteBean::getExamSiteId)); Set>> entries = timePeriodMap.entrySet(); for (Map.Entry> entry : entries) { Long examSiteId = entry.getKey(); List timePeriodExamSiteBeansList = entry.getValue(); //取并集 timePeriodExamSiteBeansList = UnionUtil.unionByAttribute(timePeriodExamSiteBeansList, timePeriodList, TimePeriodExamSiteBean::getTimePeriodId); // 过滤开启的预约时段 timePeriodExamSiteBeansList = timePeriodExamSiteBeansList.stream() .filter(TimePeriodExamSiteBean::getEnable) .collect(Collectors.toList()); // 时段转换为Map key: start_time + end_time Map map = getStringTimePeriodEntityMap(timePeriodExamSiteBeansList); timePeriodExamSiteCache.put(examSiteId, map); } return timePeriodExamSiteCache; } private Map getStringTimePeriodEntityMap(List timePeriodExamSiteBeansList) { Map map = new HashMap<>(timePeriodExamSiteBeansList.size()); for (TimePeriodExamSiteBean bean : timePeriodExamSiteBeansList) { TimePeriodEntity entity = new TimePeriodEntity(); entity.setId(bean.getTimePeriodId()); entity.setStartTime(bean.getStartTime()); map.put(bean.getStartTime() + "-" + bean.getEndTime(), entity); } return map; } private StudentApplyEntity findStudentApply(StudentApplyEntity studentApply) { LambdaQueryWrapper lm = new LambdaQueryWrapper<>(); lm.eq(StudentApplyEntity::getExamSiteId, studentApply.getExamSiteId()); lm.eq(StudentApplyEntity::getTimePeriodId, studentApply.getTimePeriodId()); lm.eq(StudentApplyEntity::getStudentId, studentApply.getStudentId()); return baseMapper.selectOne(lm); } private void checkStudentTimePeriod(StudentImportVO studentImportVO, int row, int allowCancelDays, List> failRecords) { List agentTimeList = studentImportVO.getAgentTimeList(); //是否重复预约时段 boolean isDuplicate = agentTimeList.stream() .collect(Collectors.groupingBy(AgentAndTimeVO::getTimePeriodId, Collectors.counting())) .entrySet() .stream() .anyMatch(entry -> entry.getValue() > 1); if (isDuplicate) { failRecords.add(newError(row + 1, "同一个考生预约的时间不能相同")); } //预约的日期,是否在可取消日期之后 LocalDate day = LocalDate.now().plusDays(allowCancelDays); long epochMilli = day.plusDays(1).atStartOfDay(ZoneId.systemDefault()).minusSeconds(1).toInstant().toEpochMilli(); for (AgentAndTimeVO time : agentTimeList) { if (time.getStartTime() <= epochMilli) { failRecords.add(newError(row + 1, "预约的考试时段,只能为" + allowCancelDays + "天之后的时段")); } } /* 数据库的方式 List haveApplyList = listStudentHaveApply(studentImportVO.getStudentId()); int studentApplyFinishCount = haveApplyList.size();*/ // 考生已经预约的次数 int studentApplyFinishCount = cacheService.getStudentApplyFinishCount(studentImportVO.getStudentId()); List toBeApplyTimeList = new ArrayList<>(); //考生已完成预约 if (studentApplyFinishCount >= studentImportVO.getApplyNumber()) { studentImportVO.setAgentTimeList(toBeApplyTimeList); failRecords.add(newError(row + 1, "考生已完成预约,请先取消考生已预约的信息")); } //判断是否超过可预约次数 if (studentImportVO.getApplyNumber() < studentApplyFinishCount + agentTimeList.size()) { failRecords.add(newError(row + 1, "超过考生可预约的最大数量:" + studentImportVO.getApplyNumber())); } } private void checkExamSiteTimeCapacity(List applyList, List> failRecords) { try { //导入的所有时段 List availableTimeList = new ArrayList<>(); applyList.forEach(item -> availableTimeList.addAll(item.getAgentTimeList())); //按照考点+时段 分组 Map>> agentTimeMap = availableTimeList.stream() .collect(Collectors.groupingBy(AgentAndTimeVO::getAgentId, Collectors.groupingBy(AgentAndTimeVO::getTimePeriodId))); for (Map.Entry>> examSiteEntry : agentTimeMap.entrySet()) { Long examSiteId = examSiteEntry.getKey(); Map> timePeriodMap = examSiteEntry.getValue(); for (Map.Entry> timePeriodEntry : timePeriodMap.entrySet()) { Long timePeriodKey = timePeriodEntry.getKey(); //考点下某个时段可预约的容量 int availableCount = cacheService.getApplyAvailableCount(examSiteId, timePeriodKey); // 导入的某个考点,某个时段的数量 int impTimePeriodNum = timePeriodEntry.getValue().size(); if (impTimePeriodNum > availableCount) { ExamSiteCacheBean examSiteBean = examSiteCacheService.getExamSiteById(examSiteId); if (examSiteBean != null) { String examSiteName = examSiteBean.getExamSiteName(); TimePeriodEntity timePeriod = timePeriodService.getById(timePeriodKey); String startAndEndTime = DateUtil.getStartAndEndTime(timePeriod.getStartTime(), timePeriod.getEndTime()); log.warn("[importPreExam],{} {} 剩余容量为:{},而导入的数量为:{}", examSiteName, startAndEndTime, availableCount, impTimePeriodNum); failRecords.add(newError(0, examSiteName + " " + startAndEndTime + " 剩余容量为:" + (availableCount) + ",而导入的数量为:" + impTimePeriodNum)); } } } } } catch (Exception e) { log.error("[importPreExam],导入出错,msg:{}", e.getMessage()); failRecords.add(newError(0, "导入失败")); } } private TimePeriodEntity checkTimePeriod(String timePeriodStr, Map timeCache) { if (timePeriodStr == null || timePeriodStr.split(" ").length != 2) { throw new StatusException(" 预约时段格式不正确"); } String[] arr = timePeriodStr.split("-"); String startTime = arr[0] + ":00"; String endTime = startTime.substring(0, startTime.indexOf("日") + 1) + " " + arr[1] + ":00"; Long startTimeLong = DateUtil.getLongTimeByZHDate(startTime); Long endTimeLong = DateUtil.getLongTimeByZHDate(endTime); if (timeCache.get(startTimeLong + "-" + endTimeLong) == null) { throw new StatusException(" 预约时段不存在"); } return timeCache.get(startTimeLong + "-" + endTimeLong); } private Map newError(int lineNum, String msg) { Map map = new HashMap<>(); map.put("lineNum", lineNum); map.put("msg", msg); return map; } private StudentEntity checkStudent(String studentCode, String studentName, String identityNumber, Long taskId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentEntity::getStudentCode, studentCode); wrapper.eq(StudentEntity::getName, studentName); wrapper.eq(StudentEntity::getIdentityNumber, identityNumber); wrapper.eq(StudentEntity::getApplyTaskId, taskId); return studentService.getOne(wrapper); } private String trimAndNullIfBlank(String s) { if (StringUtils.isBlank(s)) { return null; } return s.trim(); } private Map getExamSiteCategoryCache() { Map cache = new HashMap<>(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ExamSiteEntity::getEnable, Boolean.TRUE); List examSiteList = examSiteService.list(wrapper); examSiteList.forEach(site -> cache.put(site.getId(), site.getCategoryId())); return cache; } private Map getTimePeriodCache() { Map map = new HashMap<>(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(TimePeriodEntity::getApplyTaskId, getApplyTask().getId()); List timeList = timePeriodService.list(wrapper); for (TimePeriodEntity time : timeList) { map.put(time.getStartTime() + "-" + time.getEndTime(), time); } return map; } private Map getExamSiteCache() { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ExamSiteEntity::getEnable, Boolean.TRUE); List categoryList = examSiteService.list(wrapper); return categoryList.stream().collect(Collectors.toMap(ExamSiteEntity::getName, ExamSiteEntity::getId)); } private Map getTeachingCache(Integer level) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(CategoryEntity::getEnable, Boolean.TRUE); wrapper.eq(CategoryEntity::getLevel, level == null ? CategoryLevel.TEACHING.getValue() : level); List categoryList = categoryService.list(wrapper); return categoryList.stream().collect(Collectors.toMap(CategoryEntity::getName, CategoryEntity::getId)); } private void checkOpenTime(Long orgId) { CurrentApplyTaskVO task = cacheService.currentApplyTask(orgId); if(task == null) { throw new StatusException("未有开启的任务"); } Date startTime = new Date(task.getSelfApplyStartTime()); Date endTime = new Date(task.getOpenApplyEndTime()); Date now = new Date(); if (now.before(startTime) || now.after(endTime)) { throw new StatusException("未到开放时间!"); } } @Override public void autoAssign(Long taskId, Long operateId) { RLock lock = (RLock) concurrentService.getLock(CacheConstants.LOCK_AUTO_APPLY); if (lock.isLocked()) { log.warn("[autoAssign] 获取锁失败, taskId:{}, lockKey:{}", taskId, CacheConstants.LOCK_AUTO_APPLY); throw new StatusException("自动分配执行未结束,请不要重复执行"); } // 自动分配须在第一阶段之后和第三阶段开始之前 CurrentApplyTaskVO curApplyTask = applyTaskService.currentApplyTask(null); Date selfEndTime = new Date(curApplyTask.getSelfApplyEndTime()); Date openStartTime = new Date(curApplyTask.getOpenApplyStartTime()); if (!DateUtil.isBetwwen(selfEndTime, openStartTime)) { throw new StatusException("自动分配,时间必须要在第一阶段结束之后,第三阶段开始之前"); } //是否有可预约的时段 List timePeriodList = listTimePeriod(taskId); timePeriodList = listNoCancelTimePeriod(timePeriodList, taskId); if (CollectionUtils.isEmpty(timePeriodList)) { log.warn("[autoAssign] 当前时间没有可预约的时段, taskId:{}", taskId); throw new StatusException("当前时间没有可预约的时段"); } //写入异步任务 Map taskMap = asyncTaskService.saveAsyncTask(AsyncTaskType.AUTO_ASSIGN, operateId); taskMap.computeIfAbsent("taskId", v -> taskId); taskMap.computeIfAbsent("operateId", v -> operateId); try { autoAssignStudentService.assignTask(taskMap); } catch (Exception e) { throw new StatusException(e.getMessage()); } } private List listExamSite(Long teachingId, Long examSiteId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ExamSiteEntity::getCategoryId, teachingId); wrapper.eq(examSiteId != null, ExamSiteEntity::getId, examSiteId); wrapper.eq(ExamSiteEntity::getEnable, Boolean.TRUE); return examSiteService.list(wrapper); } private List listNoCancelTimePeriod(List timePeriodList, Long taskId) { ApplyTaskEntity task = applyTaskService.getById(taskId); Long longToday = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(new Date()) + " 00:00:00"); Date today = new Date(longToday); Date otherDay = DateUtil.addValues(today, Calendar.DAY_OF_MONTH, task.getAllowApplyCancelDays()); Long longOtherDay = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(otherDay) + " 23:59:59"); return timePeriodList.stream().filter(time -> time.getStartTime() > longOtherDay).collect(Collectors.toList()); } private List listTimePeriod(Long taskId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(TimePeriodEntity::getApplyTaskId, taskId); wrapper.orderByAsc(TimePeriodEntity::getStartTime); List timeList = timePeriodService.list(wrapper); if (timeList.isEmpty()) { log.warn("当前任务未配置可用的考试时段,taskId:{}", taskId); throw new StatusException("考试时段未设置"); } return timeList; } @Transactional @Override public void autoLayout(Long teachingId) { OrgInfo org = orgCacheService.currentOrg(); CurrentApplyTaskVO applyTask = cacheService.currentApplyTask(org.getOrgId()); if(applyTask == null) { log.warn("[autoLayout] 未有开启的任务"); return; } String autoLayoutLockKey = String.format(CacheConstants.LOCK_ARRANGE_EXAM, DateUtil.formatShortDateString(new Date())); RLock autoLayoutLock = (RLock) concurrentService.getLock(autoLayoutLockKey); try { if (!autoLayoutLock.tryLock()) { log.warn("获取锁失败,已有线程在执行排考!lockKey:{}", autoLayoutLock); return; } log.warn("获取锁成功!lockKey:{}", autoLayoutLockKey); // 1.根据当前日期,查询不能取消的时段 List timePeriodList = listTimePeriod(applyTask.getTaskId()); List noCancelTimePeroidList = listNoCancelApplyTimePeriod(timePeriodList, applyTask.getAllowApplyCancelDays()); if (noCancelTimePeroidList.isEmpty()) { log.warn("[autoLayout] 没有待排考的时段"); return; } // 2.查询待排考的考生 List timePeriodIds = noCancelTimePeroidList.stream() .map(BaseEntity::getId) .collect(Collectors.toList()); List toBeLayoutStudentList = baseMapper.listTimePeriod(timePeriodIds, Boolean.FALSE); if (CollectionUtils.isEmpty(toBeLayoutStudentList)) { log.warn("[autoLayout] 没有待排考的考生"); return; } // 3.开始排考 Map> toBeLayoutStudentMap = toBeLayoutStudentList.stream() .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId)); for (Long examSiteId : toBeLayoutStudentMap.keySet()) { List studentApplyList= toBeLayoutStudentMap.get(examSiteId); Map> timeLayoutStudentMap = studentApplyList.stream() .collect(Collectors.groupingBy(StudentApplyEntity::getTimePeriodId)); /*List roomList = listExamRoom(examSiteId); if (roomList.isEmpty()) { log.warn("{}:未设置考场", examSiteId); return; }*/ ExamSiteEntity examSite = examSiteService.getById(examSiteId); layoutStudentByTimePeriod(applyTask.getTaskId(), examSite, timeLayoutStudentMap); } } catch (StatusException e) { log.error(e.getMessage()); } finally { try { // 解锁前检查当前线程是否持有该锁 if (autoLayoutLock.isLocked() && autoLayoutLock.isHeldByCurrentThread()) { autoLayoutLock.unlock(); log.info("解锁成功!lockKey:{}", autoLayoutLockKey); } } catch (Exception e) { log.warn(e.getMessage()); } } } private void layoutStudentByTimePeriod(Long taskId, ExamSiteEntity examSite,Map> timeLayoutStudentMap) { List roomList; for (Long timePeriodId : timeLayoutStudentMap.keySet()) { // 根据考点和时段查询可用考场 roomList = listAvailableExamRoom(examSite.getId(), timePeriodId); if (CollectionUtils.isEmpty(roomList)) { log.error("[autoLayout]考点{}:时段{}:未有可用考场", examSite.getId(), timePeriodId); continue; } List studentApplyList = timeLayoutStudentMap.get(timePeriodId); TimePeriodEntity timePeriod = timePeriodService.getById(timePeriodId); layoutStudentToRoom(taskId, examSite, roomList, studentApplyList, timePeriod); } } /* 查询可用考场列表 */ private List listAvailableExamRoom(Long examSiteId, Long timePeriodId) { // 获取所有考场列表 List examRoomList = listExamRoom(examSiteId); if (examRoomList == null || examRoomList.isEmpty()) { return Collections.emptyList(); } // 获取禁用的考场列表 List disableExamRoomList = timePeriodExamRoomService.listExamRoom(examSiteId, timePeriodId, false); if (CollectionUtils.isEmpty(disableExamRoomList)) { return examRoomList; } // 提取禁用考场的 ID 到 Set中 Set disableRoomIdSet = disableExamRoomList.stream() .filter(Objects::nonNull) .map(ExamRoomEntity::getId) .filter(Objects::nonNull) .collect(Collectors.toSet()); // 过滤掉禁用的考场 return examRoomList.stream() .filter(room -> room != null && room.getId() != null && !disableRoomIdSet.contains(room.getId())) .collect(Collectors.toList()); } private void layoutStudentToRoom(Long taskId, ExamSiteEntity examSite, List roomList, List studentApplyList, TimePeriodEntity timePeriod) { Integer timePeriodOrder = getTimePeriodOrder(taskId, timePeriod); for (ExamRoomEntity room : roomList) { int num = 0; for (Iterator iterator = studentApplyList.iterator(); iterator.hasNext(); ) { StudentApplyEntity student = iterator.next(); if (num >= room.getCapacity()) { break; } //座位号 String seatNumber = StringUtils.leftPad(String.valueOf(++num), 3, '0'); student.setExamRoomId(room.getId()); student.setSeatNumber(seatNumber); // 准考证号 String ticketNum = DateUtil.formatShortDateString(new Date()) + timePeriodOrder + examSite.getCode() + room.getCode() + seatNumber; student.setTicketNumber(ticketNum); baseMapper.updateById(student); iterator.remove(); } } } private Integer getTimePeriodOrder(Long taskId, TimePeriodEntity timePeriod) { List timeList = listTimePeriod(taskId); List sameDayTimeList = listSameDayTimePeriod(timeList, timePeriod.getStartTime()); for (int i = 0; i < sameDayTimeList.size(); i++) { TimePeriodEntity time = sameDayTimeList.get(i); if (time.getStartTime().equals(timePeriod.getStartTime())) return i + 1; } return 0; } private List listNoCancelApplyTimePeriod(List list, Integer allowApplyCancelDays) { // 不能修改预约的日期 Date noCancelDate = DateUtil.addValues(Calendar.DAY_OF_MONTH, allowApplyCancelDays); return list.stream() .filter( item -> isSameDay(noCancelDate, new Date(item.getStartTime()))) .collect(Collectors.toList()); /* List noCancelTimePeroidList; for (TimePeriodEntity time : list) { // 预约的时段 和 不能修改预约的日期 if (isSameDay(noCancelDate, new Date(time.getStartTime()))) noCancelTimePeroidList.add(time); }*/ } @Override public File downloadSignIn(Long teachingId, Long agentId, Long examDate) { ApplyTaskEntity task = getApplyTask(); List timePeriodList = listTimePeriod(task.getId()); List sameDayTimePeriodList = listSameDayTimePeriod(timePeriodList, examDate); if (sameDayTimePeriodList.isEmpty()) { log.warn("[downloadSignIn] 没有查询到当天的时段,examDate:{}", examDate); throw new StatusException("没有可下载的签到表"); } List examSiteList = listExamSite(teachingId, agentId); if (examSiteList == null || examSiteList.isEmpty()) { log.warn("[downloadSignIn] teachingId:{},agentId:{} 没有可用的考点", teachingId, agentId); throw new StatusException("没有可用的考点"); } File tempDir = new File("temp"); if (!tempDir.exists()) { try { boolean success = tempDir.mkdirs(); if (!success) { log.warn("[downloadSignIn] 临时目录创建失败!"); } } catch (Exception e) { log.error("[downloadSignIn] 临时目录创建失败,msg:{}", e.getMessage()); } } CategoryCacheBean category = categoryCacheService.getCategoryById(teachingId); File zipFile = new File(tempDir, category.getName() + "签到表.zip"); ZipWriter writer; try { writer = ZipWriter.create(zipFile); List fileList = new ArrayList<>(); for (ExamSiteEntity examSite : examSiteList) { fileList.addAll(downloadByExamSite(writer, task.getName(), examSite, sameDayTimePeriodList)); } if (fileList.isEmpty()) { throw new StatusException("暂未排考,请等待排考后下载"); } } catch (IOException e) { log.error("[downloadSignIn] 创建文件异常,msg:{}", e.getMessage()); throw new StatusException("文件写入异常"); } writer.close(); return zipFile; } public List downloadByExamSite(ZipWriter writer, String taskName, ExamSiteEntity site, List timeList) throws IOException { List fileList = new ArrayList<>(); List roomList = listExamRoom(site.getId()); MaterialTitleInfo title = new MaterialTitleInfo(); title.setTaskName(taskName); title.setSiteName(site.getName()); for (TimePeriodEntity time : timeList) { title.setTimePeriod(DateUtil.getStartAndEndTime(time.getStartTime(), time.getEndTime())); for (ExamRoomEntity room : roomList) { title.setAddress(room.getAddress()); title.setRoomCode(room.getCode()); title.setRoomName(room.getName()); List studentList = baseMapper.listStudentApply(time.getId(), room.getId()); if (!studentList.isEmpty()) { File file = materialGenerateService.generateSignInForm(title, studentList); fileList.add(file); writer.write(file, title.getSiteName(), title.getTimePeriod() + " 第【" + title.getRoomCode() + "】考场" + ".pdf"); } } } return fileList; } public List listExamRoom(Long examSiteId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ExamRoomEntity::getExamSiteId, examSiteId); wrapper.eq(ExamRoomEntity::getEnable, Boolean.TRUE); wrapper.orderByAsc(ExamRoomEntity::getCode); return examRoomService.list(wrapper); } private List listSameDayTimePeriod(List timePeriodList, Long startTime) { Date day = new Date(startTime); List resultList = new ArrayList<>(); for (TimePeriodEntity time : timePeriodList) { if (isSameDay(day, new Date(time.getStartTime()))) resultList.add(time); } return resultList.stream().sorted(Comparator.comparing(TimePeriodEntity::getStartTime)).collect(Collectors.toList()); } @Override public int countApplyFinishForExamSiteAndTimePeriod(Long examSiteId, Long timePeriodId) { Integer value = baseMapper.countApplyFinishForExamSiteAndTimePeriod(examSiteId, timePeriodId); return value != null ? value : 0; } @Override public List listSignInDate(Long taskId) { ApplyTaskEntity task; if (taskId != null) { task = applyTaskService.getById(taskId); } else { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ApplyTaskEntity::getEnable, Boolean.TRUE); task = applyTaskService.getOne(wrapper); } if (task == null) { log.warn("[listSignDate] 未开启预约任务"); return Collections.emptyList(); } List timePeriodList = listTimePeriod(task.getId()); if (timePeriodList.isEmpty()) { log.warn("[listSignDate] 未配置考试时段"); return Collections.emptyList(); } List signInVoList = new ArrayList<>(); Date today = DateUtils.truncate(new Date(), Calendar.DATE); if (isInTimePeriod(today, timePeriodList)) { SignInVO signInVO = new SignInVO(); signInVO.setExamDate(today.getTime()); signInVoList.add(signInVO); } for (int i = 1; i <= task.getAllowApplyCancelDays(); i++) { Date otherDay = DateUtils.addDays(today, i); if (isInTimePeriod(otherDay, timePeriodList)) { SignInVO signInVO = new SignInVO(); signInVO.setExamDate(otherDay.getTime()); signInVoList.add(signInVO); } } signInVoList.sort(Comparator.comparing(SignInVO::getExamDate)); return signInVoList; } private boolean isInTimePeriod(Date date, List timePeriodList) { for (TimePeriodEntity timePeriod : timePeriodList) { Date day = new Date(timePeriod.getStartTime()); if (DateUtils.isSameDay(day, date)) { return true; } } return false; } @Override public List listExamSiteAvailable(List examSiteList) { List availableList = new ArrayList<>(); ApplyTaskEntity task = getApplyTask(); List timePeriodList = listTimePeriod(task.getId()); for (TimePeriodEntity timePeriod : timePeriodList) { String[] availableArr = getAvailableArr(examSiteList, timePeriod); availableList.add(availableArr); } return availableList; } private String[] getAvailableArr(List examSiteList, TimePeriodEntity timePeriod) { String[] availableArr = new String[examSiteList.size() + 1]; availableArr[0] = DateUtil.getStartAndEndTime(timePeriod.getStartTime(), timePeriod.getEndTime()); for (int i = 0; i < examSiteList.size(); i++) { CategoryVO categoryVO = examSiteList.get(i); int applyFinishCount = countApplyFinishForExamSiteAndTimePeriod(categoryVO.getId(), timePeriod.getId()); //剩余的容量 availableArr[i + 1] = String.valueOf(categoryVO.getCapacity() - applyFinishCount); } return availableArr; } @Override public List listStudentApplyDetail(StudentApplyReq req) { OrgInfo org = orgCacheService.currentOrg(); CurrentApplyTaskVO curApplyTask = cacheService.currentApplyTask(org.getOrgId()); if (curApplyTask == null) { return Collections.emptyList(); } return baseMapper.listStudentApplyDetail(req, curApplyTask.getTaskId()); } @Override public List listStudentNoApply(Long teachingId) { return Collections.emptyList(); } @Override public void exportNoApplyStudent(Long teachingId, Long operateId) { //写入异步任务 Map taskMap = asyncTaskService.saveAsyncTask(AsyncTaskType.NO_FINISH_APPLY_STUDENT, operateId); taskMap.computeIfAbsent("teachingId", v -> teachingId); taskMap.computeIfAbsent("operateId", v -> operateId); try { studentApplyNoFinishExportService.exportTask(taskMap); } catch (Exception e) { throw new StatusException(e.getMessage()); } } @Override public void exportStudentApplyDetail(StudentApplyReq req, Long operateId) { //写入异步任务 Map taskMap = asyncTaskService.saveAsyncTask(AsyncTaskType.STUDENT_APPLY_EXPORT, operateId); taskMap.computeIfAbsent("operateId", v -> operateId); taskMap.computeIfAbsent("condition", v -> req); try { studentApplyDetailExportService.exportTask(taskMap); } catch (Exception e) { throw new StatusException(e.getMessage()); } } @Transactional @Override public boolean saveOrUpdateStudentApply(StudentApplyEntity studentApply) { // 缓存中考生是否已经预约 ApplyRecordCacheBean studentApplyRecord = cacheService.getStudentApplyRecord(studentApply.getStudentId(), studentApply.getExamSiteId(), studentApply.getTimePeriodId()); //数据库中考生是否已经预约 StudentApplyEntity existStudentApply = findStudentApply(studentApply); // 缓存中考生未预约 if (studentApplyRecord == null) { // 数据库中不存在 if (existStudentApply == null) { baseMapper.insert(studentApply); } else if (existStudentApply.getCancel()) { //数据库中存在,但已取消 existStudentApply.setCancel(Boolean.FALSE); existStudentApply.setOperateId(studentApply.getOperateId()); baseMapper.updateById(existStudentApply); } else { // 数据库中已存在预约信息 return false; } return true; } // 考生先预约,然后做了取消操作 if (studentApplyRecord.getCancel()) { if (existStudentApply != null) { existStudentApply.setCancel(Boolean.FALSE); existStudentApply.setOperateId(studentApply.getOperateId()); baseMapper.updateById(existStudentApply); } else { baseMapper.insert(studentApply); } return true; } else { //考生已经预约 return false; } /* // 预约是否已经存在 StudentApplyEntity existStudentApply = findStudentApply(studentApply); if (studentApplyRecord != null) { existStudentApply.setCancel(Boolean.FALSE); baseMapper.updateById(existStudentApply); } else { baseMapper.insert(studentApply); }*/ } }