123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020 |
- package com.qmth.exam.reserve.service.impl;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.time.LocalDate;
- import java.time.ZoneId;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Calendar;
- import java.util.Comparator;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.stream.Collectors;
- import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
- import com.qmth.exam.reserve.entity.base.BaseEntity;
- import com.qmth.exam.reserve.enums.Role;
- import com.qmth.exam.reserve.service.*;
- 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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
- 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.login.LoginUser;
- import com.qmth.exam.reserve.bean.stdapply.AgentAndTimeVO;
- import com.qmth.exam.reserve.bean.stdapply.CategoryVO;
- import com.qmth.exam.reserve.bean.stdapply.MaterialTitleInfo;
- import com.qmth.exam.reserve.bean.stdapply.SignInVO;
- import com.qmth.exam.reserve.bean.stdapply.StudentApplyReq;
- import com.qmth.exam.reserve.bean.stdapply.StudentApplyVO;
- import com.qmth.exam.reserve.bean.stdapply.StudentImportVO;
- import com.qmth.exam.reserve.cache.CacheConstants;
- import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService;
- import com.qmth.exam.reserve.dao.StudentApplyDao;
- import com.qmth.exam.reserve.entity.ApplyTaskEntity;
- import com.qmth.exam.reserve.entity.CategoryEntity;
- import com.qmth.exam.reserve.entity.ExamRoomEntity;
- import com.qmth.exam.reserve.entity.ExamSiteEntity;
- import com.qmth.exam.reserve.entity.StudentApplyEntity;
- import com.qmth.exam.reserve.entity.StudentEntity;
- import com.qmth.exam.reserve.entity.TimePeriodEntity;
- import com.qmth.exam.reserve.enums.CategoryLevel;
- import com.qmth.exam.reserve.enums.EventType;
- import com.qmth.exam.reserve.util.DateUtil;
- import com.qmth.exam.reserve.util.JsonHelper;
- import com.qmth.exam.reserve.util.PageUtil;
- import static org.apache.commons.lang3.time.DateUtils.isSameDay;
- @Service
- public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, StudentApplyEntity>
- implements StudentApplyService {
- private final static Logger log = LoggerFactory.getLogger(StudentApplyServiceImpl.class);
- private static final String[] EXCEL_HEADER = new String[]{"学号", "姓名", "证件号", "所属教学点", "预约考点1", "预约时段1", "预约考点2",
- "预约时段2", "预约考点3", "预约时段3", "预约考点4", "预约时段4"};
- @Autowired
- private TimePeriodService timePeriodService;
- @Autowired
- private ApplyTaskService applyTaskService;
- @Autowired
- private CategoryService categoryService;
- @Autowired
- private StudentService studentService;
- @Autowired
- private ExamSiteService examSiteService;
- @Autowired
- private OperateLogService operateLogService;
- @Autowired
- private ExamRoomService examRoomService;
- @Autowired
- private MaterialGenerateService materialService;
- @Autowired
- private ConcurrentService concurrentService;
- @Autowired
- private ApplyTaskCacheService cacheService;
- @Autowired
- private StudentAutoAssignService studentAutoAssignService;
- @Override
- public PageResult<StudentApplyVO> page(StudentApplyReq req) {
- if (req.getTaskId() == null) {
- ApplyTaskEntity task = getApplyTask();
- req.setTaskId(task.getId());
- }
- if (req.getTeachingId() != null) {
- List<CategoryVO> listExamSite = examSiteService.listExamSite(req.getTeachingId());
- List<Long> examSiteIds = listExamSite.stream().map(CategoryVO::getId).collect(Collectors.toList());
- if (!examSiteIds.isEmpty()) {
- req.setExamSiteIds(examSiteIds);
- }
- }
- IPage<StudentApplyVO> iPage = this.baseMapper
- .page(new Page<StudentApplyVO>(req.getPageNumber(), req.getPageSize()), req);
- return PageUtil.of(iPage);
- }
- @Transactional
- @Override
- public void cancel(LoginUser user, Long id) {
- // 时间判断
- StudentApplyEntity studentApply = this.baseMapper.selectById(id);
- if (studentApply == null || studentApply.getTimePeriodId() == null) {
- throw new StatusException("考生没有预约,无法取消!");
- }
- TimePeriodEntity timePeriod = timePeriodService.getById(studentApply.getTimePeriodId());
- if (timePeriod == null) {
- 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("可取消时间已过,无法取消!");
- }
- String studentApplyLockKey = String.format(CacheConstants.LOCK_STUDENT_APPLY, studentApply.getStudentId());
- RLock studentApplyLock = (RLock) concurrentService.getLock(studentApplyLockKey);
- try {
- if (!studentApplyLock.tryLock()) {
- log.warn("获取锁失败,同一个考生不允许同时操作预约!lockKey:{}", studentApplyLockKey);
- throw new StatusException(Constants.SYSTEM_BUSY);
- } else {
- log.warn("获取锁成功!lockKey:{}", studentApplyLockKey);
- studentApply.setCancel(Boolean.TRUE);
- this.baseMapper.updateById(studentApply);
- ApplyRecordCacheBean bean = new ApplyRecordCacheBean();
- bean.setStudentId(studentApply.getStudentId());
- bean.setExamSiteId(studentApply.getExamSiteId());
- bean.setTimePeriodId(studentApply.getTimePeriodId());
- bean.setCancel(Boolean.TRUE);
- bean.setOperateId(user.getId());
- bean.setOperateTime(System.currentTimeMillis());
- // 先推送至预约队列
- cacheService.pushStudentApplyRecordQueue(bean);
- cacheService.saveStudentApplyRecord(bean);
- cacheService.decreaseApplyFinishCount(studentApply.getExamSiteId(), studentApply.getTimePeriodId());
- }
- } catch (Exception e) {
- throw new StatusException("取消预约失败,请稍后再试!", e);
- } finally {
- try {
- // 解锁前检查当前线程是否持有该锁
- if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) {
- studentApplyLock.unlock();
- log.info("解锁成功!lockKey:{}", studentApplyLockKey);
- }
- } catch (Exception e) {
- log.warn(e.getMessage());
- }
- }
- operateLogService.insertOperateLog(user.getId(), EventType.CANCEL_APPLY, JsonHelper.toJson(studentApply));
- }
- @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) {
- throw new StatusException("Excel 解析失败");
- }
- if (!Arrays.equals(EXCEL_HEADER, reader.getColumnNames())) {
- throw new StatusException("Excel表头错误");
- }
- if (CollectionUtils.isEmpty(lineList)) {
- throw new StatusException("Excel无内容");
- }
- ApplyTaskEntity task = getApplyTask();
- Date openStartTime = new Date(task.getOpenApplyStartTime());
- Date now = new Date();
- List<Map<String, Object>> failRecords = new ArrayList<Map<String, Object>>();
- Map<String, Long> teachingCache = getTeachingCache(level);
- Map<String, Long> agentCache = getAgentCache();
- Map<String, TimePeriodEntity> timeCache = getTimePeriodCache();
- Map<Long, Long> examSiteCategoryCache = getExamSiteCategoryCache();
- List<StudentImportVO> applyList = new ArrayList<>();
- AgentAndTimeVO agentTime;
- for (int i = 0; i < lineList.size(); i++) {
- List<AgentAndTimeVO> agentTimeList = new ArrayList<>();
- DataMap line = lineList.get(i);
- 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(" 证件号不能为空");
- }
- StudentEntity student = null;
- try {
- student = checkStd(studentCode, name, identityNumber, task);
- apply.setStudentId(student.getId());
- apply.setApplyNumber(student.getApplyNumber());
- } catch (StatusException e) {
- msg.append(" " + e.getMessage());
- failRecords.add(newError(i + 1, msg.toString()));
- continue;
- }
- String teachingName = trimAndNullIfBlank(line.get(EXCEL_HEADER[3]));
- if (StringUtils.isBlank(teachingName)) {
- msg.append(" 所属教学点不能为空");
- }
- Long categoryId = teachingCache.get(teachingName);
- if (categoryId == null) {
- msg.append(" 所属教学点不存在");
- }
- if (categoryId != null && !student.getCategoryId().equals(categoryId)) {
- msg.append(" 导入的考生所属教学点和系统中的考生教学点不匹配");
- }
- if (user.getRole().equals(Role.TEACHING) && categoryId != null && !categoryId.equals(teachingId)) {
- msg.append(" 不是本教学点的考生");
- }
- String agentName1 = trimAndNullIfBlank(line.get(EXCEL_HEADER[4]));
- if (StringUtils.isBlank(agentName1)) {
- msg.append(" 预约考点1不能为空");
- }
- agentTime = new AgentAndTimeVO();
- Long agentId = agentCache.get(agentName1);
- Long applyCategoryId = null;
- if (agentId == null) {
- msg.append(" 预约考点1不存在");
- } else {
- applyCategoryId = examSiteCategoryCache.get(agentId);
- if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
- msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
- }
- }
- String timePeriod1 = trimAndNullIfBlank(line.get(EXCEL_HEADER[5]));
- if (StringUtils.isBlank(timePeriod1)) {
- msg.append(" 预约时段1不能为空");
- }
- TimePeriodEntity timePeriod = null;
- try {
- timePeriod = checkTimePeriod(timePeriod1, timeCache);
- agentTime.setAgentId(agentId);
- agentTime.setTimePeriodId(timePeriod.getId());
- agentTime.setStartTime(timePeriod.getStartTime());
- agentTimeList.add(agentTime);
- } catch (StatusException e) {
- msg.append(" " + e.getMessage());
- }
- String agentName2 = trimAndNullIfBlank(line.get(EXCEL_HEADER[6]));
- String timePeriod2 = trimAndNullIfBlank(line.get(EXCEL_HEADER[7]));
- if (StringUtils.isBlank(agentName2) && StringUtils.isBlank(timePeriod2)) {
- apply.setAgentTimeList(agentTimeList);
- applyList.add(apply);
- if (msg.length() > 0)
- failRecords.add(newError(i + 1, msg.toString()));
- continue;
- } else {
- agentId = agentCache.get(agentName2);
- if (agentId == null) {
- msg.append(" 预约考点2不存在");
- } else {
- applyCategoryId = examSiteCategoryCache.get(agentId);
- if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
- msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
- }
- }
- try {
- timePeriod = checkTimePeriod(timePeriod2, timeCache);
- agentTime = new AgentAndTimeVO();
- agentTime.setAgentId(agentId);
- agentTime.setTimePeriodId(timePeriod.getId());
- agentTime.setStartTime(timePeriod.getStartTime());
- agentTimeList.add(agentTime);
- } catch (StatusException e) {
- msg.append(" " + e.getMessage());
- }
- }
- String agentName3 = trimAndNullIfBlank(line.get(EXCEL_HEADER[8]));
- String timePeriod3 = trimAndNullIfBlank(line.get(EXCEL_HEADER[9]));
- if (StringUtils.isBlank(agentName3) && StringUtils.isBlank(timePeriod3)) {
- apply.setAgentTimeList(agentTimeList);
- applyList.add(apply);
- if (msg.length() > 0)
- failRecords.add(newError(i + 1, msg.toString()));
- continue;
- } else {
- agentId = agentCache.get(agentName3);
- if (agentId == null) {
- msg.append(" 预约考点3不存在");
- } else {
- applyCategoryId = examSiteCategoryCache.get(agentId);
- if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
- msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
- }
- }
- try {
- timePeriod = checkTimePeriod(timePeriod3, timeCache);
- agentTime = new AgentAndTimeVO();
- agentTime.setAgentId(agentId);
- agentTime.setTimePeriodId(timePeriod.getId());
- agentTime.setStartTime(timePeriod.getStartTime());
- agentTimeList.add(agentTime);
- } catch (StatusException e) {
- msg.append(" " + e.getMessage());
- }
- }
- String agentName4 = trimAndNullIfBlank(line.get(EXCEL_HEADER[10]));
- String timePeriod4 = trimAndNullIfBlank(line.get(EXCEL_HEADER[11]));
- if (StringUtils.isBlank(agentName4) && StringUtils.isBlank(timePeriod4)) {
- apply.setAgentTimeList(agentTimeList);
- applyList.add(apply);
- if (msg.length() > 0)
- failRecords.add(newError(i + 1, msg.toString()));
- continue;
- } else {
- agentId = agentCache.get(agentName4);
- if (agentId == null) {
- msg.append(" 预约考点4不存在");
- } else {
- applyCategoryId = examSiteCategoryCache.get(agentId);
- if (now.before(openStartTime) && categoryId != null && !applyCategoryId.equals(categoryId)) {
- msg.append(" 未到自由预约时间,不允许预约其他教学点的考点");
- }
- }
- try {
- timePeriod = checkTimePeriod(timePeriod4, timeCache);
- agentTime = new AgentAndTimeVO();
- agentTime.setAgentId(agentId);
- agentTime.setTimePeriodId(timePeriod.getId());
- agentTime.setStartTime(timePeriod.getStartTime());
- agentTimeList.add(agentTime);
- apply.setAgentTimeList(agentTimeList);
- applyList.add(apply);
- } catch (StatusException e) {
- msg.append(" " + e.getMessage());
- }
- }
- }
- checkStudentApplyTime(applyList, getApplyTask().getAllowApplyCancelDays(), failRecords);
- if (CollectionUtils.isNotEmpty(failRecords)) {
- // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
- return failRecords;
- }
- checkAvailableTimePeriod(applyList);
- for (int i = 0; i < applyList.size(); i++) {
- StudentImportVO vo = applyList.get(i);
- try {
- saveStdApply(i, vo, user.getId(), failRecords);
- } catch (StatusException e) {
- failRecords.add(newError(i + 1, " 系统异常"));
- log.error("导入异常", e);
- }
- }
- if (CollectionUtils.isNotEmpty(failRecords)) {
- // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
- return failRecords;
- }
- return failRecords;
- }
- private void checkOpenTime(Long orgId) {
- CurrentApplyTaskVO task = cacheService.currentApplyTask(orgId);
- 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("未到开放时间!");
- }
- }
- private Map<Long, Long> getExamSiteCategoryCache() {
- Map<Long, Long> cache = new HashMap<>();
- LambdaQueryWrapper<ExamSiteEntity> lm = new LambdaQueryWrapper<>();
- lm.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
- List<ExamSiteEntity> examSiteList = examSiteService.list(lm);
- examSiteList.forEach(site -> {
- cache.put(site.getId(), site.getCategoryId());
- });
- return cache;
- }
- private void checkAvailableTimePeriod(List<StudentImportVO> applyList) {
- for (StudentImportVO vo : applyList) {
- List<AgentAndTimeVO> agentTimeList = vo.getAgentTimeList();
- List<StudentApplyEntity> haveApplyList = listStudentApply(vo.getStudentId());
- int studentApplyFinishCount = cacheService.getStudentApplyFinishCount(vo.getStudentId());
- if (studentApplyFinishCount == 0) {
- studentApplyFinishCount = haveApplyList.size();
- }
- agentTimeList.sort(Comparator.comparing(AgentAndTimeVO::getStartTime));
- List<AgentAndTimeVO> tobeInsertTimeList = new ArrayList<>();
- // 考生已经完成预约-不做处理
- if (studentApplyFinishCount >= vo.getApplyNumber()) {
- vo.setAgentTimeList(tobeInsertTimeList);
- continue;
- }
- // 只预约了部分
- if (studentApplyFinishCount > 0 && studentApplyFinishCount < vo.getApplyNumber()) {
- List<AgentAndTimeVO> availableList = listAvailableTime(haveApplyList, agentTimeList);
- // 需要填充的次数
- tobeInsertTimeList.addAll(availableList);
- }
- // 未预约
- if (studentApplyFinishCount == 0) {
- tobeInsertTimeList = agentTimeList;
- }
- vo.setAgentTimeList(tobeInsertTimeList);
- }
- }
- private List<AgentAndTimeVO> listAvailableTime(List<StudentApplyEntity> haveApplyList,
- List<AgentAndTimeVO> agentTimeList) {
- List<AgentAndTimeVO> availableList = new ArrayList<>();
- for (AgentAndTimeVO time : agentTimeList) {
- boolean flag = false;
- for (StudentApplyEntity apply : haveApplyList) {
- if (time.getTimePeriodId().equals(apply.getTimePeriodId())) {
- flag = true;
- break;
- }
- }
- if (!flag) {
- availableList.add(time);
- }
- }
- return availableList;
- }
- private void checkStudentApplyTime(List<StudentImportVO> applyList, Integer days,
- List<Map<String, Object>> failRecords) {
- for (int i = 0; i < applyList.size(); i++) {
- StudentImportVO vo = applyList.get(i);
- List<AgentAndTimeVO> agentTimeList = vo.getAgentTimeList();
- if (agentTimeList.size() > vo.getApplyNumber()) {
- failRecords.add(newError(i + 1, " 导入考生预约数量大于考生可预约的数量"));
- }
- boolean isDuplicate = agentTimeList.stream()
- .collect(Collectors.groupingBy(AgentAndTimeVO::getTimePeriodId, Collectors.counting())).entrySet()
- .stream().anyMatch(entry -> entry.getValue() > 1);
- if (isDuplicate) {
- failRecords.add(newError(i + 1, " 预约的时间不能相同"));
- }
- LocalDate day = LocalDate.now().plusDays(days);
- long epochMilli = day.plusDays(1).atStartOfDay(ZoneId.systemDefault()).minusSeconds(1).toInstant()
- .toEpochMilli();
- for (AgentAndTimeVO time : agentTimeList) {
- if (time.getStartTime() <= epochMilli) {
- failRecords.add(newError(i + 1, " 预约的考试时间,必须在可取消的时间之后"));
- }
- }
- }
- }
- private ApplyTaskEntity getApplyTask() {
- LambdaQueryWrapper<ApplyTaskEntity> wrapper = new LambdaQueryWrapper<ApplyTaskEntity>()
- .eq(ApplyTaskEntity::getEnable, Boolean.TRUE);
- ApplyTaskEntity task = applyTaskService.getOne(wrapper);
- if (task == null)
- throw new StatusException("未开启预约任务");
- return task;
- }
- private List<StudentApplyEntity> saveStdApply(int row, StudentImportVO vo, Long userId,
- List<Map<String, Object>> failRecords) {
- List<StudentApplyEntity> ApplyList = new ArrayList<>();
- 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("获取锁失败,考生在同时操作预约!lockKey:{}", studentApplyLockKey);
- } else {
- log.warn("获取锁成功!lockKey:{}", studentApplyLockKey);
- for (AgentAndTimeVO agentTime : agentTimeList) {
- // 已经预约的容量和考点的容量从redis中获取
- int haveApplyCount = cacheService.getApplyFinishCount(agentTime.getAgentId(),
- agentTime.getTimePeriodId());
- int examSiteCount = cacheService.getApplyTotalCount(agentTime.getAgentId());
- if (haveApplyCount >= examSiteCount) {
- ExamSiteEntity examSite = examSiteService.getById(agentTime.getAgentId());
- failRecords.add(newError(row + 1, " 考点【" + examSite.getName() + "】的容量已满"));
- } else {
- StudentApplyEntity entity = new StudentApplyEntity();
- entity.setStudentId(vo.getStudentId());
- entity.setExamSiteId(agentTime.getAgentId());
- entity.setTimePeriodId(agentTime.getTimePeriodId());
- entity.setCancel(Boolean.FALSE);
- entity.setOperateId(userId);
- StudentApplyEntity existStudentApply = findStudentApply(entity);
- if (existStudentApply != null) {
- existStudentApply.setCancel(Boolean.FALSE);
- baseMapper.updateById(existStudentApply);
- } else {
- baseMapper.insert(entity);
- }
- ApplyRecordCacheBean bean = new ApplyRecordCacheBean();
- bean.setStudentId(vo.getStudentId());
- bean.setExamSiteId(agentTime.getAgentId());
- bean.setTimePeriodId(agentTime.getTimePeriodId());
- bean.setCancel(Boolean.FALSE);
- bean.setOperateId(userId);
- bean.setOperateTime(System.currentTimeMillis());
- // 先推送至预约队列
- cacheService.pushStudentApplyRecordQueue(bean);
- cacheService.saveStudentApplyRecord(bean);
- cacheService.increaseApplyFinishCount(agentTime.getAgentId(), agentTime.getTimePeriodId());
- }
- }
- }
- } catch (Exception e) {
- log.error("导入预考失败,错误原因:", e.getMessage());
- throw new StatusException("导入预考失败,请稍后再试!", e);
- } finally {
- try {
- if (studentApplyLock.isLocked() && studentApplyLock.isHeldByCurrentThread()) {
- studentApplyLock.unlock();
- log.info("解锁成功!lockKey:{}", studentApplyLockKey);
- }
- } catch (Exception e) {
- log.warn(e.getMessage());
- }
- }
- return ApplyList;
- }
- private TimePeriodEntity checkTimePeriod(String timePeriod, Map<String, TimePeriodEntity> timeCache) {
- if (timePeriod == null || timePeriod.split(" ").length != 2) {
- throw new StatusException(" 预约时段格式不正确");
- }
- String[] arr = timePeriod.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, TimePeriodEntity> getTimePeriodCache() {
- Map<String, TimePeriodEntity> map = new HashMap<>();
- LambdaQueryWrapper<TimePeriodEntity> lm = new LambdaQueryWrapper<>();
- lm.eq(TimePeriodEntity::getApplyTaskId, getApplyTask().getId());
- List<TimePeriodEntity> timeList = timePeriodService.list(lm);
- for (TimePeriodEntity time : timeList) {
- map.put(time.getStartTime() + "-" + time.getEndTime(), time);
- }
- return map;
- }
- private Map<String, Long> getTeachingCache(Integer level) {
- LambdaQueryWrapper<CategoryEntity> lm = new LambdaQueryWrapper<>();
- lm.eq(CategoryEntity::getEnable, Boolean.TRUE);
- lm.eq(CategoryEntity::getLevel, level == null ? CategoryLevel.TEACHING.getValue() : level);
- List<CategoryEntity> categoryList = categoryService.list(lm);
- return categoryList.stream().collect(Collectors.toMap(CategoryEntity::getName, CategoryEntity::getId));
- }
- private Map<String, Long> getAgentCache() {
- LambdaQueryWrapper<ExamSiteEntity> lm = new LambdaQueryWrapper<>();
- lm.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
- List<ExamSiteEntity> categoryList = examSiteService.list(lm);
- return categoryList.stream().collect(Collectors.toMap(ExamSiteEntity::getName, ExamSiteEntity::getId));
- }
- private StudentEntity checkStd(String studentCode, String name, String identityNumber, ApplyTaskEntity task) {
- LambdaQueryWrapper<StudentEntity> lm = new LambdaQueryWrapper<>();
- lm.eq(StudentEntity::getStudentCode, studentCode);
- lm.eq(StudentEntity::getName, name);
- lm.eq(StudentEntity::getIdentityNumber, identityNumber);
- lm.eq(StudentEntity::getApplyTaskId, task.getId());
- StudentEntity student = studentService.getOne(lm);
- if (student == null) {
- throw new StatusException(" 考生信息填写错误");
- }
- return student;
- }
- 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 String trimAndNullIfBlank(String s) {
- if (StringUtils.isBlank(s)) {
- return null;
- }
- return s.trim();
- }
- @Override
- public void autoAssign(Long taskId, Long userId) {
- checkAfterOpenTime();
- // 未完成预约的考生
- List<StudentEntity> studentList = studentService.listNoFinishStudent(taskId, Boolean.FALSE);
- Map<Long, List<StudentEntity>> noFinishApplyMap = studentList.stream().collect(Collectors.groupingBy(StudentEntity::getCategoryId));
- // 考位是否充足
- List<TimePeriodEntity> timeList = listTimePeriod(taskId);
- timeList = listNoCancelExamTimePeriod(timeList, taskId);
- if (timeList == null || timeList.isEmpty()) {
- throw new StatusException("可以取消预约的考试时段为0");
- }
- checkTeachingCapacity(noFinishApplyMap, timeList);
- RLock lock = (RLock) concurrentService.getLock(CacheConstants.LOCK_AUTO_APPLY);
- if (lock.isLocked()) {
- throw new StatusException("其他老师正在执行自动分配,请不要重复执行!");
- }
- //异步执行
- studentAutoAssignService.autoAssign(taskId, userId, timeList);
- }
- private void checkTeachingCapacity(Map<Long, List<StudentEntity>> map, List<TimePeriodEntity> timeList) {
- for (Long key : map.keySet()) {
- List<ExamSiteEntity> siteList = listExamSite(key, null);
- // 总考位数量
- Integer total = siteList.stream().mapToInt(ExamSiteEntity::getCapacity).sum() * timeList.size();
- // 已经预约的数量
- List<Long> examSiteIds = siteList.stream().map(BaseEntity::getId).collect(Collectors.toList());
- List<Long> timePeriodIds = timeList.stream().map(BaseEntity::getId).collect(Collectors.toList());
- Integer haveApplyNum = baseMapper.getHaveApplyCount(examSiteIds, timePeriodIds, Boolean.FALSE);
- // 未预约的数量
- Integer noApplyNum = getNoApplyNum(map.get(key));
- if (noApplyNum > total - haveApplyNum) {
- CategoryEntity category = categoryService.getById(key);
- throw new StatusException("【" + category.getName() + "】教学点考位不足!剩余的考位数量:【" + (total - haveApplyNum) + "】,实际需要的考位数量:【" + noApplyNum + "】");
- }
- }
- }
- private Integer getNoApplyNum(List<StudentEntity> list) {
- int noApplyNum = 0;
- for (StudentEntity student : list) {
- if (student.getApplyNumber() == 1) {
- noApplyNum++;
- } else if (student.getApplyNumber() > 1) {
- int haveApplyNum = cacheService.getStudentApplyFinishCount(student.getId());
- noApplyNum = noApplyNum + (student.getApplyNumber() - haveApplyNum);
- }
- }
- return noApplyNum;
- }
- private List<TimePeriodEntity> listNoCancelExamTimePeriod(List<TimePeriodEntity> timeList, 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 timeList.stream().filter(time -> time.getStartTime() > longOtherDay).collect(Collectors.toList());
- }
- 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 checkAfterOpenTime() {
- ApplyTaskEntity task = getApplyTask();
- Date selfEndTime = new Date(task.getSelfApplyEndTime());
- Date openStartTime = new Date(task.getOpenApplyStartTime());
- if (!DateUtil.isBetwwen(selfEndTime, openStartTime)) {
- throw new StatusException("自动分配,时间必须要在第一阶段结束之后,第三阶段开始之前");
- }
- }
- private List<StudentApplyEntity> listStudentApply(Long stdId) {
- LambdaQueryWrapper<StudentApplyEntity> lm = new LambdaQueryWrapper<>();
- lm.eq(StudentApplyEntity::getStudentId, stdId);
- lm.eq(StudentApplyEntity::getCancel, Boolean.FALSE);
- return this.baseMapper.selectList(lm);
- }
- 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()) {
- throw new StatusException("考试时段未设置");
- }
- return timeList;
- }
- private List<ExamSiteEntity> listExamSite(Long categoryId, Long examSiteId) {
- LambdaQueryWrapper<ExamSiteEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(ExamSiteEntity::getCategoryId, categoryId);
- wrapper.eq(examSiteId != null, ExamSiteEntity::getId, examSiteId);
- wrapper.eq(ExamSiteEntity::getEnable, Boolean.TRUE);
- return examSiteService.list(wrapper);
- }
- @Transactional
- @Override
- public void autoLayout(Long teachingId) {
- ApplyTaskEntity applyTask = getApplyTask();
- 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.getId());
- List<TimePeriodEntity> noCancelTimePeroidList = listNoCancelApplyTimePeriod(timePeriodList,
- applyTask.getAllowApplyCancelDays());
- if (noCancelTimePeroidList.isEmpty()) {
- log.warn("当前时间不在取消预约范围内");
- return;
- }
- // 2.查询考试日期的待排考的考生
- List<StudentApplyEntity> toBeLayoutStudentList = this.baseMapper.listTimePeriod(
- noCancelTimePeroidList.stream().map(item -> item.getId()).collect(Collectors.toList()),
- Boolean.FALSE);
- if (toBeLayoutStudentList == null || toBeLayoutStudentList.isEmpty()) {
- log.warn("没有待排考的考生");
- return;
- }
- // 3.开始排考
- Map<Long, List<StudentApplyEntity>> toBeLayoutStudentMap = toBeLayoutStudentList.stream()
- .collect(Collectors.groupingBy(StudentApplyEntity::getExamSiteId));
- for (Long examSiteId : toBeLayoutStudentMap.keySet()) {
- Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap = toBeLayoutStudentMap.get(examSiteId).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.getId(), examSite, roomList, timeLayoutStudentMap);
- }
- } catch (StatusException e) {
- log.error(e.getMessage());
- e.printStackTrace();
- } 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, List<ExamRoomEntity> roomList,
- Map<Long, List<StudentApplyEntity>> timeLayoutStudentMap) {
- for (Long timePeriodId : timeLayoutStudentMap.keySet()) {
- List<StudentApplyEntity> studentApplyList = timeLayoutStudentMap.get(timePeriodId);
- layoutStudentToRoom(taskId, examSite, roomList, studentApplyList, timePeriodService.getById(timePeriodId));
- }
- }
- 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);
- student.setTicketNumber(
- generateTicketNumber(timePeriodOrder, examSite.getCode(), room.getCode(), seatNumber));
- this.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> listSameDayTimePeriod(List<TimePeriodEntity> timeList, Long startTime) {
- Date day = new Date(startTime);
- List<TimePeriodEntity> resultList = new ArrayList<>();
- for (TimePeriodEntity time : timeList) {
- if (isSameDay(day, new Date(time.getStartTime())))
- resultList.add(time);
- }
- return resultList.stream().sorted(Comparator.comparing(TimePeriodEntity::getStartTime))
- .collect(Collectors.toList());
- }
- private String generateTicketNumber(Integer timePeriodOrder, String examSiteCode, String roomCode,
- String seatNumber) {
- return DateUtil.formatShortDateString(new Date()) + timePeriodOrder + examSiteCode + roomCode + seatNumber;
- }
- public List<ExamRoomEntity> listExamRoom(Long examSiteId) {
- LambdaQueryWrapper<ExamRoomEntity> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(ExamRoomEntity::getExamSiteId, examSiteId);
- wrapper.orderByAsc(ExamRoomEntity::getCode);
- return examRoomService.list(wrapper);
- }
- private List<TimePeriodEntity> listNoCancelApplyTimePeriod(List<TimePeriodEntity> list,
- Integer allowApplyCancelDays) {
- Date noCancelDate = getNoCancelApplyDate(allowApplyCancelDays);
- List<TimePeriodEntity> noCancelTimePeroidList = new ArrayList<>();
- for (TimePeriodEntity time : list) {
- if (isSameDay(noCancelDate, new Date(time.getStartTime())))
- noCancelTimePeroidList.add(time);
- }
- return noCancelTimePeroidList;
- }
- private Date getNoCancelApplyDate(Integer allowApplyCancelDays) {
- return DateUtil.addValues(Calendar.DAY_OF_MONTH, allowApplyCancelDays);
- }
- @Override
- public File downloadSignIn(Long teachingId, Long agentId, Long examDate) {
- ApplyTaskEntity applyTask = getApplyTask();
- List<TimePeriodEntity> timePeriodList = listTimePeriod(applyTask.getId());
- List<TimePeriodEntity> noCancelTimePeroidList = listSameDayTimePeriod(timePeriodList, examDate);
- if (noCancelTimePeroidList.isEmpty()) {
- throw new StatusException("当前时间没有可下载的签到表");
- }
- List<ExamSiteEntity> siteList = listExamSite(teachingId, agentId);
- if (siteList.isEmpty())
- throw new StatusException("当前教学点下没有可用的考点");
- CategoryEntity category = categoryService.getById(teachingId);
- File tempFolder = new File("temp");
- if (!tempFolder.exists()) {
- tempFolder.mkdirs();
- }
- ZipWriter writer = null;
- File zipFile = new File(tempFolder, category.getName() + "签到表.zip");
- try {
- writer = ZipWriter.create(zipFile);
- List<File> fileList = new ArrayList<>();
- for (ExamSiteEntity site : siteList) {
- fileList.addAll(downloadByExamSite(writer, applyTask.getName(), site, noCancelTimePeroidList));
- }
- if (fileList.isEmpty()) {
- throw new StatusException("暂未排考,请等待排考后下载");
- }
- } catch (IOException e) {
- e.printStackTrace();
- 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());
- List<StudentApplyVO> studentList = baseMapper.listStudentApply(time.getId(), room.getId());
- if (!studentList.isEmpty()) {
- File file = materialService.generateSignInForm(title, studentList);
- fileList.add(file);
- writer.write(file, title.getSiteName(),
- title.getTimePeriod() + " 第【" + title.getRoomCode() + "】考场" + ".pdf");
- }
- }
- }
- return fileList;
- }
- /**
- * 获取某考点某时段的“已预约数量”
- */
- @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) {
- List<SignInVO> signInList = new ArrayList<>();
- ApplyTaskEntity task = null;
- if (taskId != null) {
- task = applyTaskService.getById(taskId);
- } else {
- LambdaQueryWrapper<ApplyTaskEntity> wrapper = new LambdaQueryWrapper<ApplyTaskEntity>()
- .eq(ApplyTaskEntity::getEnable, Boolean.TRUE);
- task = applyTaskService.getOne(wrapper);
- }
- if (task == null) {
- log.warn("当前没有开启的任务");
- return signInList;
- }
- List<TimePeriodEntity> timePeriodList = listTimePeriod(task.getId());
- if (timePeriodList.isEmpty()) {
- log.warn("未配置考试时段");
- return signInList;
- }
- String todayStr = DateUtil.formatShortSplitDateString(new Date());
- Long longToday = DateUtil.getLongTimeByDate(todayStr + " 00:00:00");
- Date today = new Date(longToday);
- if (isInTimePeriod(today, timePeriodList)) {
- SignInVO vo = new SignInVO();
- vo.setExamDate(longToday);
- signInList.add(vo);
- }
- for (int i = 1; i <= task.getAllowApplyCancelDays(); i++) {
- Date otherDay = DateUtil.addValues(today, Calendar.DAY_OF_MONTH, i);
- Long longOtherDay = DateUtil.getLongTimeByDate(DateUtil.formatShortSplitDateString(otherDay) + " 00:00:00");
- if (isInTimePeriod(otherDay, timePeriodList)) {
- SignInVO vo = new SignInVO();
- vo.setExamDate(longOtherDay);
- signInList.add(vo);
- }
- }
- signInList.sort(Comparator.comparing(SignInVO::getExamDate));
- return signInList;
- }
- 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;
- }
- }
|