12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298 |
- 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<StudentApplyDao, StudentApplyEntity> 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<StudentApplyVO> page(StudentApplyReq req) {
- ApplyTaskEntity task = getApplyTask();
- req.setTaskId(task.getId());
- if (req.getTeachingId() != null) {
- List<CategoryVO> examSiteList = examSiteService.listExamSite(req.getTeachingId(), Boolean.FALSE);
- if (examSiteList == null || examSiteList.isEmpty()) {
- return new PageResult<>();
- }
- List<Long> examSiteIds = examSiteList.stream().map(CategoryVO::getId).collect(Collectors.toList());
- req.setExamSiteIds(examSiteIds.isEmpty() ? null : examSiteIds);
- }
- IPage<StudentApplyVO> iPage = baseMapper.page(new Page<>(req.getPageNumber(), req.getPageSize()), req);
- List<StudentApplyVO> 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<ApplyTaskEntity> 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<Map<String, Object>> importPreExam(LoginUser user, Long teachingId, Integer level, InputStream inputStream) {
- checkOpenTime(user.getOrgId());
- List<DataMap> 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<Map<String, Object>> 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<StudentImportVO> applyList = new ArrayList<>();
- //教学点缓存
- Map<String, Long> teachingCache = getTeachingCache(level);
- //考点缓存
- Map<String, Long> examSiteCache = getExamSiteCache();
- //考试时段缓存
- Map<String, TimePeriodEntity> timePeriodCache = getTimePeriodCache();
- //考点时段缓存
- Map<Long, Map<String, TimePeriodEntity>> timePeriodExamSiteCache = getTimePeriodExamSiteCache(task.getTaskId());
- //考点所属教学点缓存
- Map<Long, Long> examSiteCategoryCache = getExamSiteCategoryCache();
- for (int i = 0; i < lineList.size(); i++) {
- DataMap line = lineList.get(i);
- List<AgentAndTimeVO> 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<String, TimePeriodEntity> 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<AgentAndTimeVO> 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<Long, Map<String, TimePeriodEntity>> getTimePeriodExamSiteCache(Long taskId) {
- //学校管理员设置的所有预约时段
- List<TimePeriodExamSiteBean> timePeriodList = timePeriodService.listTimePeriodByTask(taskId);
- Map<Long, Map<String, TimePeriodEntity>> timePeriodExamSiteCache = new HashMap<>();
- List<TimePeriodExamSiteBean> timePeriodExamSiteBeans = timePeriodService.listTimePeriodByExamSiteId(taskId, null);
- /*// 过滤开启的预约时段
- timePeriodExamSiteBeans = timePeriodExamSiteBeans.stream()
- .filter(TimePeriodExamSiteBean::getEnable)
- .collect(Collectors.toList());*/
- // 按照考点分组
- Map<Long, List<TimePeriodExamSiteBean>> timePeriodMap = timePeriodExamSiteBeans.stream()
- .collect(Collectors.groupingBy(TimePeriodExamSiteBean::getExamSiteId));
- Set<Map.Entry<Long, List<TimePeriodExamSiteBean>>> entries = timePeriodMap.entrySet();
- for (Map.Entry<Long, List<TimePeriodExamSiteBean>> entry : entries) {
- Long examSiteId = entry.getKey();
- List<TimePeriodExamSiteBean> 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<String, TimePeriodEntity> map = getStringTimePeriodEntityMap(timePeriodExamSiteBeansList);
- timePeriodExamSiteCache.put(examSiteId, map);
- }
- return timePeriodExamSiteCache;
- }
- private Map<String, TimePeriodEntity> getStringTimePeriodEntityMap(List<TimePeriodExamSiteBean> timePeriodExamSiteBeansList) {
- Map<String, TimePeriodEntity> 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<StudentApplyEntity> 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<Map<String, Object>> failRecords) {
- List<AgentAndTimeVO> 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<StudentApplyEntity> haveApplyList = listStudentHaveApply(studentImportVO.getStudentId());
- int studentApplyFinishCount = haveApplyList.size();*/
- // 考生已经预约的次数
- int studentApplyFinishCount = cacheService.getStudentApplyFinishCount(studentImportVO.getStudentId());
- List<AgentAndTimeVO> 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<StudentImportVO> applyList, List<Map<String, Object>> failRecords) {
- try {
- //导入的所有时段
- List<AgentAndTimeVO> availableTimeList = new ArrayList<>();
- applyList.forEach(item -> availableTimeList.addAll(item.getAgentTimeList()));
- //按照考点+时段 分组
- Map<Long, Map<Long, List<AgentAndTimeVO>>> agentTimeMap = availableTimeList.stream()
- .collect(Collectors.groupingBy(AgentAndTimeVO::getAgentId, Collectors.groupingBy(AgentAndTimeVO::getTimePeriodId)));
- for (Map.Entry<Long, Map<Long, List<AgentAndTimeVO>>> examSiteEntry : agentTimeMap.entrySet()) {
- Long examSiteId = examSiteEntry.getKey();
- Map<Long, List<AgentAndTimeVO>> timePeriodMap = examSiteEntry.getValue();
- for (Map.Entry<Long, List<AgentAndTimeVO>> 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<String, TimePeriodEntity> 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<String, Object> newError(int lineNum, String msg) {
- Map<String, Object> 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<StudentEntity> 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<Long, Long> getExamSiteCategoryCache() {
- Map<Long, Long> cache = new HashMap<>();
- LambdaQueryWrapper<ExamSiteEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
- List<ExamSiteEntity> examSiteList = examSiteService.list(wrapper);
- examSiteList.forEach(site -> cache.put(site.getId(), site.getCategoryId()));
- return cache;
- }
- private Map<String, TimePeriodEntity> getTimePeriodCache() {
- Map<String, TimePeriodEntity> map = new HashMap<>();
- LambdaQueryWrapper<TimePeriodEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(TimePeriodEntity::getApplyTaskId, getApplyTask().getId());
- List<TimePeriodEntity> timeList = timePeriodService.list(wrapper);
- for (TimePeriodEntity time : timeList) {
- map.put(time.getStartTime() + "-" + time.getEndTime(), time);
- }
- return map;
- }
- private Map<String, Long> getExamSiteCache() {
- LambdaQueryWrapper<ExamSiteEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
- List<ExamSiteEntity> categoryList = examSiteService.list(wrapper);
- return categoryList.stream().collect(Collectors.toMap(ExamSiteEntity::getName, ExamSiteEntity::getId));
- }
- private Map<String, Long> getTeachingCache(Integer level) {
- LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(CategoryEntity::getEnable, Boolean.TRUE);
- wrapper.eq(CategoryEntity::getLevel, level == null ? CategoryLevel.TEACHING.getValue() : level);
- List<CategoryEntity> 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<TimePeriodEntity> timePeriodList = listTimePeriod(taskId);
- timePeriodList = listNoCancelTimePeriod(timePeriodList, taskId);
- if (CollectionUtils.isEmpty(timePeriodList)) {
- log.warn("[autoAssign] 当前时间没有可预约的时段, taskId:{}", taskId);
- throw new StatusException("当前时间没有可预约的时段");
- }
- //写入异步任务
- Map<String, Object> 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<ExamSiteEntity> listExamSite(Long teachingId, Long examSiteId) {
- LambdaQueryWrapper<ExamSiteEntity> 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<TimePeriodEntity> listNoCancelTimePeriod(List<TimePeriodEntity> 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<TimePeriodEntity> listTimePeriod(Long taskId) {
- LambdaQueryWrapper<TimePeriodEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(TimePeriodEntity::getApplyTaskId, taskId);
- wrapper.orderByAsc(TimePeriodEntity::getStartTime);
- List<TimePeriodEntity> 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<TimePeriodEntity> timePeriodList = listTimePeriod(applyTask.getTaskId());
- List<TimePeriodEntity> noCancelTimePeroidList = listNoCancelApplyTimePeriod(timePeriodList, applyTask.getAllowApplyCancelDays());
- if (noCancelTimePeroidList.isEmpty()) {
- log.warn("[autoLayout] 没有待排考的时段");
- return;
- }
- // 2.查询待排考的考生
- List<Long> timePeriodIds = noCancelTimePeroidList.stream()
- .map(BaseEntity::getId)
- .collect(Collectors.toList());
- List<StudentApplyEntity> toBeLayoutStudentList = baseMapper.listTimePeriod(timePeriodIds, Boolean.FALSE);
- if (CollectionUtils.isEmpty(toBeLayoutStudentList)) {
- log.warn("[autoLayout] 没有待排考的考生");
- return;
- }
- // 3.开始排考
- Map<Long, List<StudentApplyEntity>> toBeLayoutStudentMap = toBeLayoutStudentList.stream()
- .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId));
- for (Long examSiteId : toBeLayoutStudentMap.keySet()) {
- List<StudentApplyEntity> studentApplyList= toBeLayoutStudentMap.get(examSiteId);
- Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap = studentApplyList.stream()
- .collect(Collectors.groupingBy(StudentApplyEntity::getTimePeriodId));
- /*List<ExamRoomEntity> 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<Long, List<StudentApplyEntity>> timeLayoutStudentMap) {
- List<ExamRoomEntity> roomList;
- for (Long timePeriodId : timeLayoutStudentMap.keySet()) {
- // 根据考点和时段查询可用考场
- roomList = listAvailableExamRoom(examSite.getId(), timePeriodId);
- if (CollectionUtils.isEmpty(roomList)) {
- log.error("[autoLayout]考点{}:时段{}:未有可用考场", examSite.getId(), timePeriodId);
- continue;
- }
- List<StudentApplyEntity> studentApplyList = timeLayoutStudentMap.get(timePeriodId);
- TimePeriodEntity timePeriod = timePeriodService.getById(timePeriodId);
- layoutStudentToRoom(taskId, examSite, roomList, studentApplyList, timePeriod);
- }
- }
- /*
- 查询可用考场列表
- */
- private List<ExamRoomEntity> listAvailableExamRoom(Long examSiteId, Long timePeriodId) {
- // 获取所有考场列表
- List<ExamRoomEntity> examRoomList = listExamRoom(examSiteId);
- if (examRoomList == null || examRoomList.isEmpty()) {
- return Collections.emptyList();
- }
- // 获取禁用的考场列表
- List<ExamRoomEntity> disableExamRoomList = timePeriodExamRoomService.listExamRoom(examSiteId, timePeriodId, false);
- if (CollectionUtils.isEmpty(disableExamRoomList)) {
- return examRoomList;
- }
- // 提取禁用考场的 ID 到 Set中
- Set<Long> 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<ExamRoomEntity> roomList,
- List<StudentApplyEntity> studentApplyList, TimePeriodEntity timePeriod) {
- Integer timePeriodOrder = getTimePeriodOrder(taskId, timePeriod);
- for (ExamRoomEntity room : roomList) {
- int num = 0;
- for (Iterator<StudentApplyEntity> 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<TimePeriodEntity> timeList = listTimePeriod(taskId);
- List<TimePeriodEntity> 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<TimePeriodEntity> listNoCancelApplyTimePeriod(List<TimePeriodEntity> 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<TimePeriodEntity> 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<TimePeriodEntity> timePeriodList = listTimePeriod(task.getId());
- List<TimePeriodEntity> sameDayTimePeriodList = listSameDayTimePeriod(timePeriodList, examDate);
- if (sameDayTimePeriodList.isEmpty()) {
- log.warn("[downloadSignIn] 没有查询到当天的时段,examDate:{}", examDate);
- throw new StatusException("没有可下载的签到表");
- }
- List<ExamSiteEntity> 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<File> 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<File> downloadByExamSite(ZipWriter writer, String taskName, ExamSiteEntity site, List<TimePeriodEntity> timeList) throws IOException {
- List<File> fileList = new ArrayList<>();
- List<ExamRoomEntity> 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<StudentApplyVO> 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<ExamRoomEntity> listExamRoom(Long examSiteId) {
- LambdaQueryWrapper<ExamRoomEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(ExamRoomEntity::getExamSiteId, examSiteId);
- wrapper.eq(ExamRoomEntity::getEnable, Boolean.TRUE);
- wrapper.orderByAsc(ExamRoomEntity::getCode);
- return examRoomService.list(wrapper);
- }
- private List<TimePeriodEntity> listSameDayTimePeriod(List<TimePeriodEntity> timePeriodList, Long startTime) {
- Date day = new Date(startTime);
- List<TimePeriodEntity> 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<SignInVO> listSignInDate(Long taskId) {
- ApplyTaskEntity task;
- if (taskId != null) {
- task = applyTaskService.getById(taskId);
- } else {
- LambdaQueryWrapper<ApplyTaskEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(ApplyTaskEntity::getEnable, Boolean.TRUE);
- task = applyTaskService.getOne(wrapper);
- }
- if (task == null) {
- log.warn("[listSignDate] 未开启预约任务");
- return Collections.emptyList();
- }
- List<TimePeriodEntity> timePeriodList = listTimePeriod(task.getId());
- if (timePeriodList.isEmpty()) {
- log.warn("[listSignDate] 未配置考试时段");
- return Collections.emptyList();
- }
- List<SignInVO> 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<TimePeriodEntity> timePeriodList) {
- for (TimePeriodEntity timePeriod : timePeriodList) {
- Date day = new Date(timePeriod.getStartTime());
- if (DateUtils.isSameDay(day, date)) {
- return true;
- }
- }
- return false;
- }
- @Override
- public List<String[]> listExamSiteAvailable(List<CategoryVO> examSiteList) {
- List<String[]> availableList = new ArrayList<>();
- ApplyTaskEntity task = getApplyTask();
- List<TimePeriodEntity> timePeriodList = listTimePeriod(task.getId());
- for (TimePeriodEntity timePeriod : timePeriodList) {
- String[] availableArr = getAvailableArr(examSiteList, timePeriod);
- availableList.add(availableArr);
- }
- return availableList;
- }
- private String[] getAvailableArr(List<CategoryVO> 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<StudentApplyExport> 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<StudentExport> listStudentNoApply(Long teachingId) {
- return Collections.emptyList();
- }
- @Override
- public void exportNoApplyStudent(Long teachingId, Long operateId) {
- //写入异步任务
- Map<String, Object> 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<String, Object> 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);
- }*/
- }
- }
|