package com.qmth.exam.reserve.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.exception.StatusException; import com.qmth.boot.core.fss.store.FileStore; import com.qmth.exam.reserve.bean.Constants; import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO; import com.qmth.exam.reserve.bean.login.LoginUser; import com.qmth.exam.reserve.bean.org.OrgInfo; import com.qmth.exam.reserve.bean.stdapply.CategoryVO; import com.qmth.exam.reserve.bean.student.StudentInfo; import com.qmth.exam.reserve.bean.student.StudentReq; import com.qmth.exam.reserve.bean.student.StudentVO; import com.qmth.exam.reserve.bean.student.WechatBindReq; import com.qmth.exam.reserve.cache.impl.ApplyTaskCacheService; import com.qmth.exam.reserve.cache.impl.CategoryCacheService; import com.qmth.exam.reserve.cache.impl.OrgCacheService; import com.qmth.exam.reserve.config.SysProperty; import com.qmth.exam.reserve.dao.StudentDao; import com.qmth.exam.reserve.entity.ApplyTaskEntity; import com.qmth.exam.reserve.entity.StudentApplyEntity; import com.qmth.exam.reserve.entity.StudentCourseEntity; import com.qmth.exam.reserve.entity.StudentEntity; import com.qmth.exam.reserve.enums.AsyncTaskType; import com.qmth.exam.reserve.enums.EventType; import com.qmth.exam.reserve.enums.FileUploadType; import com.qmth.exam.reserve.enums.Role; import com.qmth.exam.reserve.service.*; import com.qmth.exam.reserve.template.execute.StudentPhotoUploadService; import com.qmth.exam.reserve.util.FileUtil; import com.qmth.exam.reserve.util.PageUtil; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.compress.utils.FileNameUtils; import org.apache.commons.lang3.StringUtils; 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 org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.StringJoiner; import java.util.stream.Collectors; @Service public class StudentServiceImpl extends ServiceImpl implements StudentService { private static final Logger log = LoggerFactory.getLogger(StudentServiceImpl.class); @Autowired private CategoryCacheService categoryCacheService; @Autowired private StudentApplyService studentApplyService; @Autowired private OperateLogService operateLogService; @Autowired private ApplyTaskService applyTaskService; @Autowired private FileStore fileStore; @Autowired private AsyncTaskService asyncTaskService; @Autowired private StudentPhotoUploadService studentPhotoUploadService; @Autowired private ApplyTaskCacheService applyTaskCacheService; @Autowired private SysProperty sysProperty; @Autowired private StudentCourseService studentCourseService; @Autowired private OrgCacheService orgCacheService; @Override public StudentEntity findByStudentCode(Long applyTaskId, String studentCode) { if (StringUtils.isEmpty(studentCode)) { throw new StatusException("学号不能为空"); } if (applyTaskId == null) { throw new StatusException("预约任务ID不能为空"); } LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentEntity::getStudentCode, studentCode); wrapper.eq(StudentEntity::getApplyTaskId, applyTaskId); return baseMapper.selectOne(wrapper); } @Override public StudentEntity findByOpenIdAndUid(String openId, String uid) { if (StringUtils.isEmpty(openId)) { throw new StatusException("微信OID不能为空"); } LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentEntity::getOpenId, openId); if (StringUtils.isNotEmpty(uid)) { wrapper.eq(StudentEntity::getUid, uid); } return baseMapper.selectOne(wrapper); } @Override public int findStudentApplyNumberById(Long studentId) { // 只查询applyNumber等少量字段 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.select(StudentEntity::getApplyNumber); wrapper.eq(StudentEntity::getId, studentId); StudentEntity stu = this.getOne(wrapper); if (stu == null) { log.warn("学生信息不存在!studentId:{}", studentId); throw new StatusException("学生信息不存在"); } return stu.getApplyNumber(); } @Override public StudentEntity findLessInfoByStudentId(Long studentId) { // 只查询id、name等少量字段 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.select(StudentEntity::getId, StudentEntity::getName); wrapper.eq(StudentEntity::getId, studentId); return this.getOne(wrapper); } @Override public StudentInfo findInfoByStudentId(Long studentId) { StudentInfo info = baseMapper.findInfoById(studentId); if (info == null) { throw new StatusException("学生信息不存在"); } return info; } @Transactional @Override public void bindingWechat(WechatBindReq req) { StudentEntity stu = baseMapper.selectById(req.getStudentId()); if (stu == null) { throw new StatusException("学生信息不存在"); } if (StringUtils.isNotEmpty(stu.getOpenId())) { throw new StatusException("微信账号已绑定"); } if (StringUtils.isEmpty(req.getOpenId())) { throw new StatusException("微信OID不能为空"); } LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.set(StudentEntity::getOpenId, req.getOpenId()); updateWrapper.set(StudentEntity::getUid, req.getUid()); updateWrapper.eq(StudentEntity::getId, req.getStudentId()); this.update(updateWrapper); log.info("[WECHAT_BINDING] studentId:{} openId:{} uid:{}", req.getStudentId(), req.getOpenId(), req.getUid()); } @Transactional @Override public void unbindWechatByStudentId(Long studentId) { StudentEntity stu = baseMapper.selectById(studentId); if (stu == null) { throw new StatusException("学生信息不存在"); } if (StringUtils.isEmpty(stu.getOpenId())) { return; } LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.set(StudentEntity::getOpenId, null); updateWrapper.set(StudentEntity::getUid, null); updateWrapper.eq(StudentEntity::getId, studentId); this.update(updateWrapper); log.info("[WECHAT_UNBIND] studentId:{} openId:{} uid:{}", stu.getId(), stu.getOpenId(), stu.getUid()); } @Override public List listNoFinishStudent(Long taskId, Boolean cancel) { return this.baseMapper.listNoFinishStudent(taskId, cancel); } @Override public List listNoFinishCategory(Long taskId, Boolean cancel) { return baseMapper.listNoFinishCategory(taskId, cancel); } @Override public PageResult pageStudent(StudentReq req) { if (req.getTaskId() == null) { OrgInfo org = orgCacheService.currentOrg(); CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(org.getOrgId()); if (curApplyTask == null) { return new PageResult<>(); } else { req.setTaskId(curApplyTask.getTaskId()); } } IPage iPage = baseMapper.pageStudent(new Page<>(req.getPageNumber(), req.getPageSize()), req); List recordList = iPage.getRecords(); for (StudentVO student : recordList) { //添加图片访问前缀 student.setPhotoPath(sysProperty.getServer() + File.separator + student.getPhotoPath()); } return PageUtil.of(iPage); } @Override @Transactional public void delete(Long[] studentCourseIds, LoginUser loginUser) { // 考生科目ID for(Long studentCourseId : studentCourseIds) { StudentCourseEntity studentCourseEntity = studentCourseService.getById(studentCourseId); if(studentCourseEntity == null) { throw new StatusException("考生科目不存在"); } Long studentId = studentCourseEntity.getStudentId(); List studentApplyList = getStudentApplyList(studentId); if (CollectionUtils.isNotEmpty(studentApplyList)) { log.warn("[考生删除] 删除失败,存在预约信息,student_id:{}", studentId); throw new StatusException("考生存在预约信息,无法删除"); } StudentEntity studentEntity = this.getById(studentId); if (studentEntity == null) { log.warn("[考生删除] 考生不存在,student_id:{}", studentId); throw new StatusException("考生不存在,无法删除"); } //教学点管理员 if (loginUser.getRole().equals(Role.TEACHING)) { Long studentCategoryId = studentEntity.getCategoryId(); Long loginUserCategoryId = loginUser.getCategoryId(); if (!studentCategoryId.equals(loginUserCategoryId)) { log.warn("[考生删除] student_id:{}, 所在教学点{},登录用户的教学点:{} ", studentId, studentCategoryId, loginUserCategoryId); throw new StatusException("不能删除其他教学点的考生科目信息"); } } // 删除科目信息 studentCourseService.removeById(studentCourseId); // 考生是否还有科目,没有科目,删除考生 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentCourseEntity::getStudentId, studentId); List studentCourseList = studentCourseService.list(wrapper); if (CollectionUtils.isEmpty(studentCourseList)) { //删除考生信息 this.removeById(studentId); } else { //更新考生的预约次数 this.updateStudentApplyNumber(studentId); } // 清空缓存 applyTaskCacheService.clearStudentApplyNumberCache(studentId); //写入日志 operateLogService.insertOperateLog(loginUser.getId(), EventType.DELETE_STUDENT, String.join("-", studentId.toString(), studentCourseId.toString())); } } private List getStudentApplyList(Long studentId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StudentApplyEntity::getStudentId, studentId); wrapper.eq(StudentApplyEntity::getCancel, Boolean.FALSE); return studentApplyService.list(wrapper); } @Override public void modifyApplyNumber(Long studentId, Long operateId, Integer applyNumber) { if (applyNumber == null || applyNumber <= 0) { throw new StatusException("考生的预约次数必须大于0"); } List studentApplyList = getStudentApplyList(studentId); if (CollectionUtils.isNotEmpty(studentApplyList) && applyNumber < studentApplyList.size()) { throw new StatusException("考生已预约了" + studentApplyList.size() + "次,不能小于已经预约的次数"); } LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(StudentEntity::getId, studentId); updateWrapper.set(StudentEntity::getApplyNumber, applyNumber); this.update(updateWrapper); //写入日志 operateLogService.insertOperateLog(operateId, EventType.MODIFY_APPLY_NUMBER, studentId + "->" + applyNumber); } @Override public void uploadStudentPhotoAsync(Long operateId, MultipartFile file) throws IOException { ApplyTaskEntity applyTask = applyTaskService.findApplyTask(); if (applyTask == null) { throw new StatusException("未开启预约任务,无法导入"); } File tempFile = FileUtil.getFileTemp(".zip"); if (tempFile != null) { file.transferTo(tempFile); } //写入异步任务 Map taskMap = asyncTaskService.saveAsyncTask(AsyncTaskType.UPLOAD_STUDENT_PHOTO, operateId); taskMap.computeIfAbsent("taskId", v -> applyTask.getId()); taskMap.computeIfAbsent("operateId", v -> operateId); try { studentPhotoUploadService.uploadTask(taskMap, tempFile); } catch (Exception e) { throw new StatusException(e.getMessage()); } } @Override public void updateStudentApplyNumber(Long studentId) { baseMapper.updateStudentApplyNumber(studentId); } @Override public void uploadStudentPhoto(Long operateId, MultipartFile file) { //文件大小限制 if (file.getSize() > Constants.FILE_SIZE_LIMIT) { throw new StatusException("上传的考生照片大小不能超过500KB"); } OrgInfo org = orgCacheService.currentOrg(); CurrentApplyTaskVO curApplyTask = applyTaskCacheService.currentApplyTask(org.getOrgId()); if (curApplyTask == null) { throw new StatusException("未开启预约任务,无法导入"); } //照片必须以考生的身份证命名 String identityNumber = FileNameUtils.getBaseName(file.getOriginalFilename()); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(StudentEntity::getIdentityNumber, identityNumber); queryWrapper.eq(StudentEntity::getApplyTaskId, curApplyTask.getTaskId()); List studentList = this.list(queryWrapper); if (CollectionUtils.isEmpty(studentList)) { throw new StatusException("考生照片必须以考生的身份证命名"); } StringJoiner stringJoiner = FileUtil.getDirName(false); String path = stringJoiner.toString().replaceAll("\\\\", "/"); try { fileStore.write(path + file.getOriginalFilename(), file.getInputStream(), DigestUtils.md5Hex(file.getInputStream())); } catch (Exception e) { log.error("[上传考生头像],上传失败:{}", e.getMessage()); throw new StatusException("考生头像上传失败,请稍后重试"); } //更新考生照片路径 List studentIds = studentList.stream().map(StudentEntity::getId).collect(Collectors.toList()); LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.set(StudentEntity::getPhotoPath, path + file.getOriginalFilename()); updateWrapper.in(StudentEntity::getId, studentIds); this.update(null, updateWrapper); } }