package com.qmth.exam.reserve.service.impl; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; 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.core.toolkit.StringUtils; 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.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.exam.reserve.bean.login.LoginUser; import com.qmth.exam.reserve.bean.stdapply.AgentAndTimeVO; 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.dao.ApplyTaskDao; import com.qmth.exam.reserve.dao.OperateLogDao; import com.qmth.exam.reserve.dao.StudentApplyDao; import com.qmth.exam.reserve.dao.TimePeriodDao; import com.qmth.exam.reserve.entity.ApplyTaskEntity; import com.qmth.exam.reserve.entity.CategoryEntity; import com.qmth.exam.reserve.entity.ExamSiteEntity; import com.qmth.exam.reserve.entity.OperateLogEntity; 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.service.CategoryService; import com.qmth.exam.reserve.service.ExamSiteService; import com.qmth.exam.reserve.service.StudentApplyService; import com.qmth.exam.reserve.service.StudentService; import com.qmth.exam.reserve.util.DateUtil; import com.qmth.exam.reserve.util.JsonHelper; import com.qmth.exam.reserve.util.PageUtil; @Service public class StudentApplyServiceImpl extends ServiceImpl implements StudentApplyService { private static final String[] EXCEL_HEADER = new String[] { "学号", "姓名", "证件号", "所属教学点", "预约考点1", "预约时段1", "预约考点2", "预约时段2", "预约考点3", "预约时段3", "预约考点4", "预约时段4" }; @Autowired private TimePeriodDao timePeriodDao; @Autowired private ApplyTaskDao applyTaskDao; @Autowired private OperateLogDao operateLogDao; @Autowired private CategoryService categoryService; @Autowired private StudentService studentService; @Autowired private ExamSiteService examSiteService; @Override public PageResult page(StudentApplyReq req) { IPage iPage = this.baseMapper .page(new Page(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 timePeroid = this.timePeriodDao.selectById(studentApply.getTimePeriodId()); if (timePeroid == null) throw new StatusException("考试时段不存在,请检查考试时段数据!"); LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(ApplyTaskEntity::getEnable, Boolean.TRUE); ApplyTaskEntity task = applyTaskDao.selectOne(wrapper); Date applyDate = DateUtil.parse(DateUtil.getShortDateByLongTime(timePeroid.getStartTime()), "yyyy-MM-dd"); Date canCancelDay = DateUtil.addValues(applyDate, 5, -task.getAllowApplyCancelDays()); if (new Date().after(canCancelDay)) throw new StatusException("可取消时间已过,无法取消!"); studentApply.setCancel(Boolean.TRUE); this.baseMapper.updateById(studentApply); // TODO redis更新:该时段redis已预约的数量减1 // 写入日志 OperateLogEntity log = new OperateLogEntity(); log.setCreateTime(System.currentTimeMillis()); log.setUpdateTime(System.currentTimeMillis()); log.setOperateId(user.getId()); log.setEventType(EventType.CANCEL.toString()); log.setContent(JsonHelper.toJson(studentApply)); operateLogDao.insert(log); } @Transactional @Override public List> importPreExam(LoginUser user, Long teachingId, InputStream inputStream) { checkInTime(); List lineList = null; 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无内容"); } List> failRecords = new ArrayList>(); Map teachingCache = getTeachingCache(); Map agentCache = getAgentCache(teachingId); Map timeCache = getTimePeriodCache(); List applyList = new ArrayList<>(); AgentAndTimeVO agentTime = new AgentAndTimeVO(); for (int i = 0; i < lineList.size(); i++) { List 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); apply.setStudentId(student.getId()); } 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 (!student.getCategoryId().equals(categoryId)) { 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); if (agentId == null) { msg.append(" 预约考点1不存在"); } String timePeriod1 = trimAndNullIfBlank(line.get(EXCEL_HEADER[5])); if (StringUtils.isBlank(timePeriod1)) { msg.append(" 预约时段1不能为空"); } Long timePeriodId = null; try { timePeriodId = checkTimePeriod(timePeriod1, timeCache); agentTime.setAgentId(agentId); agentTime.setTimePeriodId(timePeriodId); 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不存在"); try { timePeriodId = checkTimePeriod(timePeriod2, timeCache); agentTime = new AgentAndTimeVO(); agentTime.setAgentId(agentId); agentTime.setTimePeriodId(timePeriodId); 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不存在"); try { timePeriodId = checkTimePeriod(timePeriod3, timeCache); agentTime = new AgentAndTimeVO(); agentTime.setAgentId(agentId); agentTime.setTimePeriodId(timePeriodId); 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不存在"); try { timePeriodId = checkTimePeriod(timePeriod4, timeCache); agentTime = new AgentAndTimeVO(); agentTime.setAgentId(agentId); agentTime.setTimePeriodId(timePeriodId); agentTimeList.add(agentTime); apply.setAgentTimeList(agentTimeList); applyList.add(apply); } catch (StatusException e) { msg.append(" " + e.getMessage()); } } } if (CollectionUtils.isNotEmpty(failRecords)) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return failRecords; } for (int i = 0; i < applyList.size(); i++) { StudentImportVO vo = applyList.get(i); try { saveStdApply(vo); } catch (StatusException e) { failRecords.add(newError(i + 1, e.getMessage())); } catch (Exception e) { failRecords.add(newError(i + 1, " 系统异常")); log.error("导入异常", e); } } if (CollectionUtils.isNotEmpty(failRecords)) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } // TODO 更新redis return failRecords; } private void checkInTime() { LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(ApplyTaskEntity::getEnable, Boolean.TRUE); ApplyTaskEntity task = applyTaskDao.selectOne(wrapper); Date start = DateUtil.parse(DateUtil.getLongDateByLongTime(task.getOpenApplyStartTime()), null); // DateUtil.isBetwwen(start, end) } private void saveStdApply(StudentImportVO vo) { List agentTimeList = vo.getAgentTimeList(); LambdaQueryWrapper lm = new LambdaQueryWrapper<>(); lm.eq(StudentApplyEntity::getStudentId, vo.getStudentId()); this.baseMapper.delete(lm); for (AgentAndTimeVO agentTime : agentTimeList) { StudentApplyEntity entity = new StudentApplyEntity(); entity.setStudentId(vo.getStudentId()); entity.setCreateTime(System.currentTimeMillis()); entity.setUpdateTime(System.currentTimeMillis()); entity.setExamSiteId(agentTime.getAgentId()); entity.setTimePeriodId(agentTime.getTimePeriodId()); entity.setCancel(Boolean.FALSE); this.baseMapper.insert(entity); } } private Long checkTimePeriod(String timePeriod, Map timeCache) { if (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 getTimePeriodCache() { Map map = new HashMap<>(); LambdaQueryWrapper lm = new LambdaQueryWrapper<>(); List timeList = timePeriodDao.selectList(lm); for (TimePeriodEntity time : timeList) { map.put(time.getStartTime() + "-" + time.getEndTime(), time.getId()); } return map; } private Map getTeachingCache() { LambdaQueryWrapper lm = new LambdaQueryWrapper<>(); lm.eq(CategoryEntity::getEnable, Boolean.TRUE); lm.eq(CategoryEntity::getLevel, CategoryLevel.TEACHING.getValue()); List categoryList = categoryService.list(lm); return categoryList.stream().collect(Collectors.toMap(CategoryEntity::getName, CategoryEntity::getId)); } private Map getAgentCache(Long categoryId) { LambdaQueryWrapper lm = new LambdaQueryWrapper<>(); lm.eq(ExamSiteEntity::getEnable, Boolean.TRUE); lm.eq(ExamSiteEntity::getCategoryId, categoryId); List categoryList = examSiteService.list(lm); return categoryList.stream().collect(Collectors.toMap(ExamSiteEntity::getName, ExamSiteEntity::getId)); } private StudentEntity checkStd(String studentCode, String name, String identityNumber) { LambdaQueryWrapper lm = new LambdaQueryWrapper<>(); lm.eq(StudentEntity::getStudentCode, studentCode); lm.eq(StudentEntity::getName, name); lm.eq(StudentEntity::getIdentityNumber, identityNumber); StudentEntity student = studentService.getOne(lm); if (student == null) { throw new StatusException(" 考生信息填写错误"); } return student; } private Map newError(int lineNum, String msg) { Map 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(); } }