deason hace 9 meses
padre
commit
450328fba0

+ 2 - 2
src/main/java/cn/com/qmth/scancentral/controller/admin/StudentImportController.java

@@ -38,7 +38,7 @@ public class StudentImportController extends BaseController {
     @ApiOperation(value = "考生导入")
     @PostMapping("/import")
     public Map<String, Object> studentImport(@RequestParam Long examId, @RequestParam MultipartFile file) {
-        Long taskId = studentImportService.studentImport(examId, file);
+        String taskId = studentImportService.studentImport(examId, file);
 
         Map<String, Object> result = new HashMap<>();
         result.put("taskId", taskId);
@@ -47,7 +47,7 @@ public class StudentImportController extends BaseController {
 
     @ApiOperation(value = "考生导入进度查询")
     @PostMapping("/import/progress")
-    public Map<String, Object> studentImportProgress(@RequestParam Long taskId) {
+    public Map<String, Object> studentImportProgress(@RequestParam String taskId) {
         return studentImportService.studentImportProgress(taskId);
     }
 

+ 2 - 2
src/main/java/cn/com/qmth/scancentral/service/StudentImportService.java

@@ -6,8 +6,8 @@ import java.util.Map;
 
 public interface StudentImportService {
 
-    Long studentImport(Long examId, MultipartFile file);
+    String studentImport(Long examId, MultipartFile file);
 
-    Map<String, Object> studentImportProgress(Long taskId);
+    Map<String, Object> studentImportProgress(String taskId);
 
 }

+ 184 - 7
src/main/java/cn/com/qmth/scancentral/service/impl/StudentImportServiceImpl.java

@@ -1,30 +1,207 @@
 package cn.com.qmth.scancentral.service.impl;
 
+import cn.com.qmth.scancentral.entity.ExamEntity;
+import cn.com.qmth.scancentral.service.ExamService;
 import cn.com.qmth.scancentral.service.StudentImportService;
+import cn.com.qmth.scancentral.service.SubjectService;
+import cn.com.qmth.scancentral.util.FileUtil;
+import cn.com.qmth.scancentral.vo.studentimport.StudentImportInfo;
+import com.qmth.boot.tools.uuid.FastUUID;
+import org.apache.commons.io.FileUtils;
+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.Component;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.regex.Pattern;
 
 @Component
 public class StudentImportServiceImpl implements StudentImportService {
 
     private static final Logger log = LoggerFactory.getLogger(StudentImportServiceImpl.class);
 
-    @Override
-    public Long studentImport(Long examId, MultipartFile file) {
-        return 1L;
-    }
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private SubjectService subjectService;
 
     @Override
-    public Map<String, Object> studentImportProgress(Long taskId) {
+    public Map<String, Object> studentImportProgress(String taskId) {
         Map<String, Object> result = new HashMap<>();
         result.put("progress", 100);
         result.put("errFileUrl", "");
         return result;
     }
 
+    @Override
+    public String studentImport(Long examId, MultipartFile file) {
+        final String taskId = FastUUID.get();
+        final String tempDir = "temp/import_data/";
+        FileUtil.makeDirs(tempDir);
+        File logFile = new File(tempDir + taskId + ".log");
+
+        if (examId == null) {
+            this.writeLogFile(logFile, "【错误】考试ID不能为空");
+            return taskId;
+        }
+
+        ExamEntity exam = examService.getById(examId);
+        if (exam == null) {
+            this.writeLogFile(logFile, "【错误】当前考试不存在");
+            return taskId;
+        }
+
+        if (exam.getYear() == null || exam.getYearHalf() == null) {
+            this.writeLogFile(logFile, "【错误】未设置考试年度或考次");
+            return taskId;
+        }
+
+        String fileSuffix = FileUtil.getFileSuffix(file.getOriginalFilename());
+        if (!".txt".equals(fileSuffix)) {
+            this.writeLogFile(logFile, "【错误】导入模板目前仅支持后缀名为“.txt”的文件");
+            return taskId;
+        }
+
+        List<String> lines;
+        try {
+            File dataFile = new File(tempDir + taskId + fileSuffix);
+            FileUtils.writeByteArrayToFile(dataFile, file.getBytes());
+            lines = FileUtil.readAllLines(dataFile);
+            log.warn("读取考生数据文件共{}行! taskId:{}", lines.size(), taskId);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            this.writeLogFile(logFile, "【错误】读取导入数据文件失败");
+            return taskId;
+        }
+
+        List<StudentImportInfo> list = new ArrayList<>();
+        List<String> errs = this.parseValues(lines, list, examId, exam.getYear(), exam.getYearHalf());
+        log.warn("解析考生数据共{}条! errCount:{} taskId:{}", list.size(), errs.size(), taskId);
+        if (!errs.isEmpty()) {
+            this.writeLogFile(logFile, StringUtils.join(errs, "\n"));
+            return taskId;
+        }
+
+        // todo insert
+        log.info("开始写入数据库...");
+
+        this.writeLogFile(logFile, "本次成功导入0条,失败0条");
+        return taskId;
+    }
+
+    private List<String> parseValues(List<String> lines, List<StudentImportInfo> list, Long examId, Integer yearConfig, Integer yearHalfConfig) {
+        Set<String> subjectCodes = new HashSet<>();
+        Set<String> examNumbers = new HashSet<>();
+        List<String> errs = new ArrayList<>();
+        Pattern examNumberPattern = Pattern.compile("^[0-9]{15}$");
+
+        for (int n = 1; n < lines.size(); n++) {
+            String line = lines.get(n);
+            if (StringUtils.isBlank(line)) {
+                continue;
+            }
+
+            line = StringUtils.deleteWhitespace(line);
+            String[] values = StringUtils.splitByWholeSeparatorPreserveAllTokens(line, ",");
+            if (values.length != 3) {
+                errs.add(String.format("【第%s行】数据格式不正确", n + 1));
+                continue;
+            }
+
+            boolean okData = true;
+            // 默认模板:准考证号,姓名,考点名称(标题行,数据从第二行读取)
+            String examNumber = values[0].trim();
+            String name = values[1].trim();
+            String examSiteName = values[2].trim();
+
+            if (StringUtils.isEmpty(examNumber)) {
+                okData = false;
+                errs.add(String.format("【第%s行】准考证号不能为空", n + 1));
+            }
+            if (StringUtils.isEmpty(name)) {
+                okData = false;
+                errs.add(String.format("【第%s行】姓名不能为空", n + 1));
+            }
+            if (StringUtils.isEmpty(examSiteName)) {
+                okData = false;
+                errs.add(String.format("【第%s行】考点名称不能为空", n + 1));
+            }
+            if (examNumbers.contains(examNumber)) {
+                okData = false;
+                errs.add(String.format("【第%s行】考生信息存在重复", n + 1));
+            }
+            examNumbers.add(examNumber);
+
+            if (!examNumberPattern.matcher(examNumber).find()) {
+                errs.add(String.format("【第%s行】准考证号不是15位数字", n + 1));
+                continue;
+            }
+
+            String year = examNumber.substring(6, 8);
+            if (!yearConfig.toString().equals(year)) {
+                okData = false;
+                errs.add(String.format("【第%s行】考试年度不正确", n + 1));
+            }
+
+            String yearHalf = examNumber.substring(8, 9);
+            if (!yearHalfConfig.toString().equals(yearHalf)) {
+                okData = false;
+                errs.add(String.format("【第%s行】考次不正确", n + 1));
+            }
+
+            if (!okData) {
+                continue;
+            }
+
+            String subjectCode = examNumber.substring(9, 10);
+            String examSite = examNumber.substring(0, 5);
+            String campusCode = examNumber.substring(0, 6);
+            String examRoom = examNumber.substring(10, 13);
+            String seatNumber = examNumber.substring(13, 15);
+            subjectCodes.add(subjectCode);
+
+            StudentImportInfo data = new StudentImportInfo();
+            data.setExamId(examId);
+            data.setSubjectCode(subjectCode);
+            data.setExamNumber(examNumber);
+            data.setName(name);
+            data.setStudentCode(examNumber);
+            data.setExamSite(examSite);
+            data.setExamSiteName(examSiteName);
+            data.setCampusCode(campusCode);
+            data.setCampusName(examSiteName + examNumber.substring(5, 6));
+            data.setExamRoom(campusCode + examRoom);
+            data.setPackageCode(campusCode + examRoom);
+            data.setSeatNumber(seatNumber);
+            list.add(data);
+        }
+
+        // 校验所有科目代码
+        for (String subjectCode : subjectCodes) {
+            if (subjectService.findByExamIdAndCode(examId, subjectCode) == null) {
+                log.warn("科目不存在!examId:{} subjectCode:{}", examId, subjectCode);
+                errs.add(String.format("【错误】科目代码不存在!subjectCode:%s", subjectCode));
+            }
+        }
+
+        return errs;
+    }
+
+    private void writeLogFile(File logFile, String content) {
+        if (StringUtils.isEmpty(content)) {
+            return;
+        }
+        try {
+            FileUtils.writeStringToFile(logFile, "\n" + content, "UTF-8", true);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+    }
+
 }

+ 48 - 1
src/main/java/cn/com/qmth/scancentral/util/FileUtil.java

@@ -1,11 +1,58 @@
 package cn.com.qmth.scancentral.util;
 
+import com.google.common.io.Files;
 import com.qmth.boot.core.exception.StatusException;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
 
 public class FileUtil {
 
+    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
+
+    /**
+     * 创建文件目录
+     */
+    public static void makeDirs(String dirPath) {
+        if (StringUtils.isBlank(dirPath)) {
+            return;
+        }
+        File dir = new File(dirPath);
+        if (!dir.exists()) {
+            dir.mkdirs();
+        }
+    }
+
+    /**
+     * 获取文件后缀名,包括"."
+     */
+    public static String getFileSuffix(String fileName) {
+        if (StringUtils.isNotEmpty(fileName)) {
+            int index = fileName.lastIndexOf(".");
+            if (index > 0) {
+                return fileName.substring(index).toLowerCase();
+            }
+        }
+
+        return "";
+    }
+
+    /**
+     * 读取文件内容(一次性读取)
+     */
+    public static List<String> readAllLines(File file) {
+        try {
+            return Files.readLines(file, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new RuntimeException("读取文件失败!");
+        }
+    }
+
     public static String readFileContent(InputStream in) {
         StringBuilder content = new StringBuilder();
         InputStreamReader streamReader = null;
@@ -45,7 +92,7 @@ public class FileUtil {
 
     /**
      * 获得文件的byte数组
-     * 
+     *
      * @param filePath
      * @return
      * @throws IOException

+ 135 - 0
src/main/java/cn/com/qmth/scancentral/vo/studentimport/StudentImportInfo.java

@@ -0,0 +1,135 @@
+package cn.com.qmth.scancentral.vo.studentimport;
+
+public class StudentImportInfo {
+
+    private Long id;// 考生ID
+
+    private Long examId;// 考试ID
+
+    private String subjectCode;// 科目代码
+
+    private String examNumber;// 准考证号
+
+    private String name;// 考生姓名
+
+    private String studentCode;// 考生编号
+
+    private String campusCode;
+
+    private String campusName;// 学习中心
+
+    private String examSite;// 考场
+
+    private String examSiteName;
+
+    private String examRoom;// 考点
+
+    private String packageCode;// 卷袋号
+
+    private String seatNumber;// 座位号
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getCampusCode() {
+        return campusCode;
+    }
+
+    public void setCampusCode(String campusCode) {
+        this.campusCode = campusCode;
+    }
+
+    public String getCampusName() {
+        return campusName;
+    }
+
+    public void setCampusName(String campusName) {
+        this.campusName = campusName;
+    }
+
+    public String getExamSite() {
+        return examSite;
+    }
+
+    public void setExamSite(String examSite) {
+        this.examSite = examSite;
+    }
+
+    public String getExamSiteName() {
+        return examSiteName;
+    }
+
+    public void setExamSiteName(String examSiteName) {
+        this.examSiteName = examSiteName;
+    }
+
+    public String getExamRoom() {
+        return examRoom;
+    }
+
+    public void setExamRoom(String examRoom) {
+        this.examRoom = examRoom;
+    }
+
+    public String getPackageCode() {
+        return packageCode;
+    }
+
+    public void setPackageCode(String packageCode) {
+        this.packageCode = packageCode;
+    }
+
+    public String getSeatNumber() {
+        return seatNumber;
+    }
+
+    public void setSeatNumber(String seatNumber) {
+        this.seatNumber = seatNumber;
+    }
+
+}