haogh 1 éve
szülő
commit
c0f8602be1

+ 5 - 0
src/main/java/com/qmth/exam/reserve/cache/CacheConstants.java

@@ -30,4 +30,9 @@ public interface CacheConstants {
      */
     String LOCK_ARRANGE_EXAM = "LOCK:ARRANGE:EXAM:";
 
+    /**
+     * 自动安排时间锁
+     */
+    String LOCK_AUTO_APPLY = "LOCK:AUTO:APPLY";
+
 }

+ 4 - 4
src/main/java/com/qmth/exam/reserve/controller/admin/StudentApplyController.java

@@ -135,10 +135,10 @@ public class StudentApplyController extends BaseController {
     @ApiOperation(value = "自动排考")
     @PostMapping(value = "/std/auto/layout")
     public void autoLayout(@ApiParam("教学点ID") @RequestParam Long teachingId) {
-        /*
-         * LoginUser user = this.curLoginUser(); if (!Role.ADMIN.equals(user.getRole()))
-         * { throw new StatusException("没有权限"); }
-         */
+        LoginUser user = this.curLoginUser();
+        if (!Role.ADMIN.equals(user.getRole())) {
+            throw new StatusException("没有权限");
+        }
         studentApplyService.autoLayout(teachingId);
     }
 

+ 32 - 1
src/main/java/com/qmth/exam/reserve/controller/admin/StudentImportTaskController.java

@@ -1,15 +1,27 @@
 package com.qmth.exam.reserve.controller.admin;
 
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.qmth.boot.api.annotation.Aac;
 import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.boot.core.exception.StatusException;
+import com.qmth.exam.reserve.bean.login.LoginUser;
 import com.qmth.exam.reserve.controller.BaseController;
+import com.qmth.exam.reserve.entity.StudentImportTaskEntity;
+import com.qmth.exam.reserve.enums.ImportStatus;
+import com.qmth.exam.reserve.enums.Role;
 import com.qmth.exam.reserve.service.StudentImportTaskService;
+import com.qmth.exam.reserve.util.ResourceUtil;
 
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -23,9 +35,28 @@ public class StudentImportTaskController extends BaseController {
     @Autowired
     private StudentImportTaskService studentImportService;
 
+    @ApiOperation(value = "考生信息模版下载")
+    @GetMapping(value = "/import/template")
+    public void downloadTemplate(HttpServletResponse response) {
+        exportFile("考生信息导入模板.xlsx", ResourceUtil.getStream("templates/studentImport.xlsx"));
+    }
+
     @ApiOperation(value = "考生信息导入")
     @PostMapping(value = "/upload")
     public void upload(MultipartFile file) {
-        // TODO
+        LoginUser user = this.curLoginUser();
+        if (!Role.ADMIN.equals(user.getRole())) {
+            throw new StatusException("没有权限");
+        }
+        LambdaQueryWrapper<StudentImportTaskEntity> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StudentImportTaskEntity::getStatus, ImportStatus.RUNNING.toString());
+        StudentImportTaskEntity exists = studentImportService.getOne(wrapper);
+        if (exists != null)
+            throw new StatusException("正在导入,请稍后再试");
+        try {
+            studentImportService.importStudent(user.getId(), file.getOriginalFilename(), file.getInputStream());
+        } catch (IOException e) {
+            throw new StatusException("文件读取出错", e);
+        }
     }
 }

+ 0 - 36
src/main/java/com/qmth/exam/reserve/enums/DateField.java

@@ -1,36 +0,0 @@
-package com.qmth.exam.reserve.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-@Getter
-@AllArgsConstructor
-public enum DateField {
-
-    YEAR(1, "年份"),
-
-    MONTH(2, "月份"),
-
-    WEEK(3, "星期"),
-
-    DAY(5, "日期"),
-
-    HOUR(11, "小时"),
-
-    MINUTE(12, "分钟"),
-
-    SECOND(13, "秒");
-
-    private int value;
-    private String name;
-
-    public static DateField getByName(String name) {
-        for (DateField r : DateField.values()) {
-            if (r.getName().equals(name)) {
-                return r;
-            }
-        }
-        return null;
-    }
-
-}

+ 26 - 0
src/main/java/com/qmth/exam/reserve/enums/ImportStatus.java

@@ -0,0 +1,26 @@
+package com.qmth.exam.reserve.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum ImportStatus {
+
+    RUNNING("正在导入"),
+
+    SUCCESS("导入成功"),
+
+    FAILURE("导入失败");
+
+    private String name;
+
+    public static ImportStatus getByName(String name) {
+        for (ImportStatus r : ImportStatus.values()) {
+            if (r.getName().equals(name)) {
+                return r;
+            }
+        }
+        return null;
+    }
+}

+ 23 - 0
src/main/java/com/qmth/exam/reserve/enums/ImportType.java

@@ -0,0 +1,23 @@
+package com.qmth.exam.reserve.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum ImportType {
+
+    STUDENT("考生导入");
+
+    private String name;
+
+    public static ImportType getByName(String name) {
+        for (ImportType r : ImportType.values()) {
+            if (r.getName().equals(name)) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+}

+ 2 - 0
src/main/java/com/qmth/exam/reserve/service/ApplyTaskService.java

@@ -23,4 +23,6 @@ public interface ApplyTaskService extends IService<ApplyTaskEntity> {
 
     CurrentApplyTaskVO currentApplyTask();
 
+    ApplyTaskEntity findApplyTask();
+
 }

+ 11 - 0
src/main/java/com/qmth/exam/reserve/service/StudentImportAsyncService.java

@@ -0,0 +1,11 @@
+package com.qmth.exam.reserve.service;
+
+import java.util.List;
+
+import com.qmth.boot.tools.excel.model.DataMap;
+import com.qmth.exam.reserve.entity.StudentImportTaskEntity;
+
+public interface StudentImportAsyncService {
+
+    void asyncImportStudent(StudentImportTaskEntity task, Long orgId, List<DataMap> lineList);
+}

+ 4 - 0
src/main/java/com/qmth/exam/reserve/service/StudentImportTaskService.java

@@ -1,8 +1,12 @@
 package com.qmth.exam.reserve.service;
 
+import java.io.InputStream;
+
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.qmth.exam.reserve.entity.StudentImportTaskEntity;
 
 public interface StudentImportTaskService extends IService<StudentImportTaskEntity> {
 
+    void importStudent(Long operateId, String filePath, InputStream inputStream);
+
 }

+ 13 - 5
src/main/java/com/qmth/exam/reserve/service/impl/ApplyTaskServiceImpl.java

@@ -1,5 +1,11 @@
 package com.qmth.exam.reserve.service.impl;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -17,11 +23,6 @@ import com.qmth.exam.reserve.dao.ApplyTaskDao;
 import com.qmth.exam.reserve.entity.ApplyTaskEntity;
 import com.qmth.exam.reserve.service.ApplyTaskService;
 import com.qmth.exam.reserve.util.PageUtil;
-import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
-
-import java.util.ArrayList;
-import java.util.List;
 
 @Service
 public class ApplyTaskServiceImpl extends ServiceImpl<ApplyTaskDao, ApplyTaskEntity> implements ApplyTaskService {
@@ -101,4 +102,11 @@ public class ApplyTaskServiceImpl extends ServiceImpl<ApplyTaskDao, ApplyTaskEnt
         return baseMapper.currentApplyTask();
     }
 
+    @Override
+    public ApplyTaskEntity findApplyTask() {
+        LambdaQueryWrapper<ApplyTaskEntity> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(ApplyTaskEntity::getEnable, Boolean.TRUE);
+        return baseMapper.selectOne(wrapper);
+    }
+
 }

+ 10 - 4
src/main/java/com/qmth/exam/reserve/service/impl/StudentApplyServiceImpl.java

@@ -3,6 +3,7 @@ package com.qmth.exam.reserve.service.impl;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
@@ -47,7 +48,6 @@ 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.DateField;
 import com.qmth.exam.reserve.enums.EventType;
 import com.qmth.exam.reserve.service.ApplyTaskService;
 import com.qmth.exam.reserve.service.CategoryService;
@@ -115,7 +115,7 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
             throw new StatusException("考试时段不存在,请检查考试时段数据!");
         ApplyTaskEntity task = getApplyTask();
         Date applyDate = DateUtil.parse(DateUtil.getShortDateByLongTime(timePeroid.getStartTime()), "yyyy-MM-dd");
-        Date canCancelDay = DateUtil.addValues(applyDate, DateField.DAY.getValue(), -task.getAllowApplyCancelDays());
+        Date canCancelDay = DateUtil.addValues(applyDate, Calendar.DAY_OF_MONTH, -task.getAllowApplyCancelDays());
         if (new Date().after(canCancelDay))
             throw new StatusException("可取消时间已过,无法取消!");
         studentApply.setCancel(Boolean.TRUE);
@@ -124,6 +124,10 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
         operateLogService.insertOperateLog(user.getId(), EventType.CANCEL_APPLY, JsonHelper.toJson(studentApply));
     }
 
+    public static void main(String[] args) {
+        System.out.println(Calendar.DAY_OF_MONTH);
+    }
+
     @Transactional
     @Override
     public List<Map<String, Object>> importPreExam(LoginUser user, Long teachingId, InputStream inputStream) {
@@ -439,6 +443,7 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
     public void autoAssign(Long taskId) {
         checkAfterOpenTime();
         try {
+            redisClient.set(CacheConstants.LOCK_AUTO_APPLY, UUID.randomUUID(), TIMEOUT, TimeUnit.MINUTES);
             // 1、未完成预约的考生
             LambdaQueryWrapper<StudentEntity> lm = new LambdaQueryWrapper<>();
             lm.eq(StudentEntity::getApplyFinished, Boolean.FALSE);
@@ -469,7 +474,7 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
             log.error(e.getMessage());
             throw new StatusException(e.getMessage());
         } finally {
-
+            redisClient.delete(CacheConstants.LOCK_AUTO_APPLY);
         }
 
     }
@@ -587,6 +592,7 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
         return examSiteService.list(wrapper);
     }
 
+    @Transactional
     @Override
     public void autoLayout(Long teachingId) {
         ApplyTaskEntity applyTask = getApplyTask();
@@ -708,7 +714,7 @@ public class StudentApplyServiceImpl extends ServiceImpl<StudentApplyDao, Studen
     }
 
     private String getNoCancelApplyDate(Integer allowApplyCancelDays) {
-        return DateUtil.formatShortDateString(DateUtil.addValues(DateField.DAY.getValue(), allowApplyCancelDays));
+        return DateUtil.formatShortDateString(DateUtil.addValues(Calendar.DAY_OF_MONTH, allowApplyCancelDays));
     }
 
 }

+ 189 - 0
src/main/java/com/qmth/exam/reserve/service/impl/StudentImportAsyncServiceImpl.java

@@ -0,0 +1,189 @@
+package com.qmth.exam.reserve.service.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.qmth.boot.core.exception.StatusException;
+import com.qmth.boot.tools.excel.model.DataMap;
+import com.qmth.exam.reserve.entity.CategoryEntity;
+import com.qmth.exam.reserve.entity.StudentEntity;
+import com.qmth.exam.reserve.entity.StudentImportTaskEntity;
+import com.qmth.exam.reserve.enums.CategoryLevel;
+import com.qmth.exam.reserve.enums.ImportStatus;
+import com.qmth.exam.reserve.service.CategoryService;
+import com.qmth.exam.reserve.service.StudentImportAsyncService;
+import com.qmth.exam.reserve.service.StudentImportTaskService;
+import com.qmth.exam.reserve.service.StudentService;
+
+@Service
+public class StudentImportAsyncServiceImpl implements StudentImportAsyncService {
+
+    private final static Logger log = LoggerFactory.getLogger(StudentImportAsyncServiceImpl.class);
+
+    private static final String[] EXCEL_HEADER = new String[] { "学号", "姓名", "证件号", "所属教学点代码", "所属教学点名称", "可约时段数" };
+
+    @Autowired
+    private CategoryService categoryService;
+
+    @Autowired
+    private StudentService studentService;
+
+    @Autowired
+    private StudentImportTaskService importTaskService;
+
+    @Async
+    @Transactional
+    @Override
+    public void asyncImportStudent(StudentImportTaskEntity task, Long orgId, List<DataMap> lineList) {
+        Map<String, CategoryEntity> teachingCache = getTeachingCache();
+        List<StudentEntity> studentList = new ArrayList<StudentEntity>();
+        List<Map<String, Object>> failRecords = new ArrayList<Map<String, Object>>();
+        for (int i = 0; i < lineList.size(); i++) {
+            DataMap line = lineList.get(i);
+            StringBuilder msg = new StringBuilder();
+            StudentEntity student = new StudentEntity();
+            String studentCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[0]));
+            if (StringUtils.isBlank(studentCode)) {
+                msg.append(" 学号不能为空");
+            } else {
+                student.setStudentCode(studentCode);
+            }
+            String name = trimAndNullIfBlank(line.get(EXCEL_HEADER[1]));
+            if (StringUtils.isBlank(name)) {
+                msg.append(" 姓名不能为空");
+            } else {
+                student.setName(name);
+            }
+
+            String identityNumber = trimAndNullIfBlank(line.get(EXCEL_HEADER[2]));
+            if (StringUtils.isBlank(identityNumber)) {
+                msg.append(" 证件号不能为空");
+            } else if (identityNumber.length() < 6) {
+                msg.append(" 证件号不正确");
+            } else {
+                student.setIdentityNumber(identityNumber);
+                String password = identityNumber.substring(identityNumber.length() - 6);
+                student.setPassword(DigestUtils.sha256Hex(password).toUpperCase());
+            }
+
+            String teachingCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[3]));
+            if (StringUtils.isBlank(teachingCode)) {
+                msg.append(" 教学点代码不能为空");
+            }
+            CategoryEntity category = teachingCache.get(teachingCode);
+            if (category == null) {
+                msg.append(" 教学点不存在");
+            }
+
+            String teachingName = trimAndNullIfBlank(line.get(EXCEL_HEADER[4]));
+            if (StringUtils.isBlank(teachingName)) {
+                msg.append(" 教学点名称不能为空");
+            }
+            if (category != null && !category.getName().equals(teachingName)) {
+                msg.append(" 教学点名称错误");
+            }
+            if (category != null && category.getName().equals(teachingName)) {
+                student.setCategoryId(category.getId());
+                student.setOrgId(orgId);
+                student.setApplyTaskId(task.getApplyTaskId());
+            }
+            try {
+                String applyNumber = trimAndNullIfBlank(line.get(EXCEL_HEADER[5]));
+                if (StringUtils.isBlank(applyNumber)) {
+                    msg.append(" 可约时段数不能为空");
+                } else {
+                    student.setApplyNumber(Integer.parseInt(applyNumber));
+                }
+            } catch (Exception e) {
+                msg.append(" 可约时段只能为数字");
+            }
+            if (msg.length() > 0) {
+                failRecords.add(newError(i + 1, msg.toString()));
+            } else {
+                studentList.add(student);
+            }
+        }
+
+        if (CollectionUtils.isNotEmpty(failRecords)) {
+            updateStudentImportTask(task, ImportStatus.FAILURE.toString(), failRecords);
+            return;
+        }
+        for (int i = 0; i < studentList.size(); i++) {
+            StudentEntity studentEntity = studentList.get(i);
+            try {
+                saveStudent(studentEntity);
+            } catch (StatusException e) {
+                failRecords.add(newError(i + 1, e.getMessage()));
+            } catch (Exception e) {
+                failRecords.add(newError(i + 1, " 系统异常"));
+                log.error("导入异常", e);
+                updateStudentImportTask(task, ImportStatus.FAILURE.toString(), failRecords);
+                return;
+            }
+        }
+
+        updateStudentImportTask(task, ImportStatus.SUCCESS.toString(), failRecords);
+    }
+
+    private void updateStudentImportTask(StudentImportTaskEntity task, String status,
+            List<Map<String, Object>> failRecords) {
+        task.setMessage(failRecords.size() == 0 ? null : failRecords.toString());
+        task.setStatus(status);
+        importTaskService.saveOrUpdate(task);
+    }
+
+    private void saveStudent(StudentEntity studentEntity) {
+        LambdaQueryWrapper<StudentEntity> wrapper = new LambdaQueryWrapper<StudentEntity>()
+                .eq(StudentEntity::getStudentCode, studentEntity.getStudentCode());
+        StudentEntity existStudent = studentService.getOne(wrapper);
+        if (existStudent != null) {
+            existStudent.setName(studentEntity.getName());
+            existStudent.setIdentityNumber(studentEntity.getIdentityNumber());
+            existStudent.setCategoryId(studentEntity.getCategoryId());
+            existStudent.setApplyNumber(studentEntity.getApplyNumber());
+            existStudent.setPassword(studentEntity.getPassword());
+            studentService.updateById(existStudent);
+        } else {
+            studentEntity.setApplyFinished(Boolean.FALSE);
+            studentService.save(studentEntity);
+        }
+    }
+
+    private String trimAndNullIfBlank(String s) {
+        if (StringUtils.isBlank(s)) {
+            return null;
+        }
+        return s.trim();
+    }
+
+    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 Map<String, CategoryEntity> getTeachingCache() {
+        LambdaQueryWrapper<CategoryEntity> lm = new LambdaQueryWrapper<>();
+        lm.eq(CategoryEntity::getEnable, Boolean.TRUE);
+        lm.eq(CategoryEntity::getLevel, CategoryLevel.TEACHING.getValue());
+        List<CategoryEntity> categoryList = categoryService.list(lm);
+        return categoryList.stream().collect(Collectors.toMap(CategoryEntity::getCode, Function.identity()));
+    }
+
+}

+ 49 - 0
src/main/java/com/qmth/exam/reserve/service/impl/StudentImportTaskServiceImpl.java

@@ -1,14 +1,63 @@
 package com.qmth.exam.reserve.service.impl;
 
+import java.io.InputStream;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+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.dao.StudentImportTaskDao;
+import com.qmth.exam.reserve.entity.ApplyTaskEntity;
 import com.qmth.exam.reserve.entity.StudentImportTaskEntity;
+import com.qmth.exam.reserve.enums.ImportStatus;
+import com.qmth.exam.reserve.enums.ImportType;
+import com.qmth.exam.reserve.service.ApplyTaskService;
+import com.qmth.exam.reserve.service.StudentImportAsyncService;
 import com.qmth.exam.reserve.service.StudentImportTaskService;
 
 @Service
 public class StudentImportTaskServiceImpl extends ServiceImpl<StudentImportTaskDao, StudentImportTaskEntity>
         implements StudentImportTaskService {
 
+    @Autowired
+    private StudentImportAsyncService asyncImportService;
+
+    @Autowired
+    private StudentImportTaskService studentTaskService;
+
+    @Autowired
+    private ApplyTaskService applyTaskService;
+
+    @Override
+    public void importStudent(Long operateId, String filePath, InputStream inputStream) {
+        List<DataMap> lineList = null;
+        ExcelReader reader = ExcelReader.create(ExcelType.XLSX, inputStream, 0);
+        try {
+            lineList = reader.getDataMapList();
+        } catch (Exception e) {
+            throw new StatusException("Excel 解析失败");
+        }
+        if (CollectionUtils.isEmpty(lineList)) {
+            throw new StatusException("Excel无内容");
+        }
+        StudentImportTaskEntity studentTask = new StudentImportTaskEntity();
+        ApplyTaskEntity task = applyTaskService.findApplyTask();
+        if (task == null) {
+            throw new StatusException("没有开启的任务");
+        }
+        studentTask.setOperateId(operateId);
+        studentTask.setApplyTaskId(task.getId());
+        studentTask.setType(ImportType.STUDENT.toString());
+        studentTask.setStatus(ImportStatus.RUNNING.toString());
+        studentTask.setFilePath(filePath);
+        studentTaskService.save(studentTask);
+        asyncImportService.asyncImportStudent(studentTask, task.getOrgId(), lineList);
+    }
+
 }

BIN
src/main/resources/templates/studentImport.xlsx