xiatian 2 年之前
父節點
當前提交
8b5ccf24fd

+ 6 - 1
src/main/java/cn/com/qmth/scancloud/tools/enums/TaskType.java

@@ -20,7 +20,12 @@ public enum TaskType {
 
     OBJECTIVE_QUESTION_EXPORT("考生客观题作答阅卷数据导出", ObjectiveQuestionExportTask.class),
 
-    EXCEL_TO_TXT("Excel转为txt文件", ExcelToTxtTask.class);
+    EXCEL_TO_TXT("Excel转为txt文件", ExcelToTxtTask.class),
+    
+    ABSENT_IMPORT("缺考导入", AbsentImportTask.class),
+    
+    
+    ;
 
     private String title;
 

+ 38 - 53
src/main/java/cn/com/qmth/scancloud/tools/model/ExamStudent.java

@@ -10,214 +10,199 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth;
 public class ExamStudent {
 
     @ExcelIgnore
-    private Long id;//考生ID
+    private Long id;// 考生ID
 
     @ExcelIgnore
-    private Long examId;//考试ID
+    private Long examId;// 考试ID
 
     @ExcelProperty("科目代码")
-    private String subjectCode;//科目代码
+    private String subjectCode;// 科目代码
 
     @ExcelProperty("科目名称")
-    private String subjectName;//科目名称
+    private String subjectName;// 科目名称
 
     @ExcelProperty("准考证号")
-    private String examNumber;//准考证号
+    private String examNumber;// 准考证号
 
     @ExcelProperty("考生姓名")
-    private String name;//考生姓名
+    private String name;// 考生姓名
 
     @ExcelProperty("考生编号")
-    private String studentCode;//考生编号
+    private String studentCode;// 考生编号
 
     @ExcelProperty("卷袋号")
-    private String packageCode;//卷袋号
+    private String packageCode;// 卷袋号
 
     @ExcelProperty("座位号")
-    private String seatNumber;//座位号
+    private String seatNumber;// 座位号
 
     private String campusCode;
-    
+
     @ExcelProperty("学习中心")
-    private String campusName;//学习中心
+    private String campusName;// 学习中心
 
     @ExcelProperty("考场")
-    private String examSite;//考场
-    
+    private String examSite;// 考场
+
     private String examSiteName;
 
     @ExcelProperty("考点")
-    private String examRoom;//考点
+    private String examRoom;// 考点
 
     @ExcelIgnore
-    private String paperType;//卷型
+    private String paperType;// 卷型
 
     @ExcelIgnore
-    private List<String> answer;//客观题作答阅卷数据
+    private List<String> answer;// 客观题作答阅卷数据
+
+    @ExcelIgnore
+    private Boolean absent;// 缺考
+
+    @ExcelIgnore
+    private String violation;// 违纪
 
-    
     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 getSubjectName() {
         return subjectName;
     }
 
-    
     public void setSubjectName(String subjectName) {
         this.subjectName = subjectName;
     }
 
-    
     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 getPackageCode() {
         return packageCode;
     }
 
-    
     public void setPackageCode(String packageCode) {
         this.packageCode = packageCode;
     }
 
-    
     public String getSeatNumber() {
         return seatNumber;
     }
 
-    
     public void setSeatNumber(String seatNumber) {
         this.seatNumber = seatNumber;
     }
 
-    
     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 getExamRoom() {
         return examRoom;
     }
 
-    
     public void setExamRoom(String examRoom) {
         this.examRoom = examRoom;
     }
 
-    
     public String getPaperType() {
         return paperType;
     }
 
-    
     public void setPaperType(String paperType) {
         this.paperType = paperType;
     }
 
-    
     public List<String> getAnswer() {
         return answer;
     }
 
-    
     public void setAnswer(List<String> answer) {
         this.answer = answer;
     }
 
-
-    
     public String getExamSiteName() {
         return examSiteName;
     }
 
-
-    
     public void setExamSiteName(String examSiteName) {
         this.examSiteName = examSiteName;
     }
 
-
-    
     public String getCampusCode() {
         return campusCode;
     }
 
-
-    
     public void setCampusCode(String campusCode) {
         this.campusCode = campusCode;
     }
 
-    
+    public Boolean getAbsent() {
+        return absent;
+    }
+
+    public void setAbsent(Boolean absent) {
+        this.absent = absent;
+    }
+
+    public String getViolation() {
+        return violation;
+    }
+
+    public void setViolation(String violation) {
+        this.violation = violation;
+    }
+
 }

+ 183 - 0
src/main/java/cn/com/qmth/scancloud/tools/service/impl/AbsentImportTask.java

@@ -0,0 +1,183 @@
+package cn.com.qmth.scancloud.tools.service.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.LineIterator;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.com.qmth.scancloud.tools.config.SysProperty;
+import cn.com.qmth.scancloud.tools.enums.TaskType;
+import cn.com.qmth.scancloud.tools.model.ExamStudent;
+import cn.com.qmth.scancloud.tools.service.AbstractTask;
+import cn.com.qmth.scancloud.tools.service.CommonService;
+import cn.com.qmth.scancloud.tools.utils.FileHelper;
+import cn.com.qmth.scancloud.tools.utils.HttpHelper;
+import cn.com.qmth.scancloud.tools.utils.JsonHelper;
+import cn.com.qmth.scancloud.tools.utils.StatusException;
+
+/**
+ * 缺考导入
+ */
+public class AbsentImportTask extends AbstractTask {
+
+    private static final Logger log = LoggerFactory.getLogger(AbsentImportTask.class);
+    private static Pattern examNumberRex = Pattern.compile("^[0-9]{15}$");
+    @Override
+    protected String getTaskName() {
+        return TaskType.EXAM_STUDENT_IMPORT.getTitle();
+    }
+
+    @Override
+    protected void execute() {
+        Long examId = SysProperty.EXAM_ID;
+        if (examId == null) {
+            throw new StatusException("【scan.tool.examId】未配置!");
+        }
+
+        if (CommonService.findExam(examId) == null) {
+            throw new StatusException("当前考试不存在!examId = " + examId);
+        }
+
+        if (!SysProperty.DATA_FILE.endsWith(".txt")) {
+            throw new StatusException("导入模板目前仅支持后缀名为“.txt”的文件!");
+        }
+
+        int total = 0;
+        List<ExamStudent> list = new ArrayList<>();
+        Set<String> subjectCodes = new HashSet<>();
+        Set<String> examNumbers = new HashSet<>();
+
+        File file = new File(SysProperty.DATA_FILE);
+        try (LineIterator lines = FileHelper.readLines(file);) {
+            while (lines.hasNext()) {
+                String line = lines.nextLine();
+
+                ExamStudent data = this.parseValues(total, line, examNumbers);
+                if (data != null) {
+                    data.setExamId(examId);
+                    list.add(data);
+
+                    subjectCodes.add(data.getSubjectCode());
+
+                    // 同个考试下考生的“准考证号”值唯一
+                    examNumbers.add(data.getExamNumber());
+                }
+
+                total++;
+            }
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+
+        log.info("导入文件全部行数共 {} 行,实际有效数据共 {} 条!", total, list.size());
+        if (CollectionUtils.isEmpty(list)) {
+            return;
+        }
+
+        // 校验所有科目代码
+        boolean allExist = true;
+        for (String subjectCode : subjectCodes) {
+            if (CommonService.findCourse(examId, subjectCode) == null) {
+                log.warn("科目不存在! examId={} subjectCode={}", examId, subjectCode);
+                allExist = false;
+            }
+        }
+        if (!allExist) {
+            throw new StatusException("科目信息有误!");
+        }
+
+        // 分批保存
+        int batchSize = 5000;
+        List<ExamStudent> batchList = new ArrayList<>();
+        String url = SysProperty.SCAN_SERVER_URL + "/api/tool/import/cet/absent";
+        for (int n = 0; n < list.size(); n++) {
+            batchList.add(list.get(n));
+
+            if (batchList.size() % batchSize == 0) {
+                String json = JsonHelper.toJson(batchList);
+                String result = HttpHelper.post(url, json);
+                batchList.clear();
+
+                float rate = (n + 1) * 100f / list.size();
+                log.info("已处理数:{} 进度:{}% {}", n + 1, rate, result);
+            }
+        }
+
+        if (CollectionUtils.isNotEmpty(batchList)) {
+            String json = JsonHelper.toJson(batchList);
+            String result = HttpHelper.post(url, json);
+            log.info("已处理数:{} 进度:100% {}", list.size(), result);
+        }
+    }
+
+    private ExamStudent parseValues(int index, String line, Set<String> examNumbers) {
+        if (StringUtils.isBlank(line)) {
+            return null;
+        }
+
+        line = StringUtils.deleteWhitespace(line);
+        String[] values = StringUtils.splitByWholeSeparatorPreserveAllTokens(line, SysProperty.TEMPLATE_SEPARATOR);
+
+        try {
+
+            // 默认模板:准考证号,考生姓名,考点名称
+            String examNumber = values[0].trim();
+            String absent = values[2].trim();
+            String violation = values[3].trim();
+            if (StringUtils.isEmpty(examNumber)) {
+                throw new StatusException(String.format("【第%s行】“准考证号”字段不能为空! %s", index + 1, line));
+            }
+            if (examNumbers.contains(examNumber)) {
+                throw new StatusException(String.format("【第%s行】考生信息存在重复! %s", index + 1, line));
+            }
+            if (StringUtils.isEmpty(absent)) {
+                throw new StatusException(String.format("【第%s行】“缺考”字段不能为空! %s", index + 1, line));
+            }
+            if (StringUtils.isEmpty(violation)) {
+                throw new StatusException(String.format("【第%s行】“违纪”字段不能为空! %s", index + 1, line));
+            }
+            try {
+                checkExamNumber(examNumber);
+            } catch (StatusException e) {
+                throw new StatusException(String.format("【第%s行】%s! %s", index + 1,e.getMessage(), line));
+            }
+
+            ExamStudent data = new ExamStudent();
+            data.setExamNumber(examNumber);
+            data.setAbsent("1".equals(absent)?true:false);
+            data.setViolation(violation);
+            return data;
+        } catch (ArrayIndexOutOfBoundsException e) {
+            log.error("【第{}行】内容格式错误! {} {}", index + 1, line, e.toString());
+            throw new StatusException("数据内容有误!");
+        }
+    }
+    
+    private void checkExamNumber(String examNumber) {
+        if(!examNumberRex.matcher(examNumber).find()) {
+            throw new StatusException("准考证号不是15位数字");
+        }
+        String year=examNumber.substring(6, 8);
+        if(!year.equals(SysProperty.YEAR)) {
+            throw new StatusException("年度不正确");
+        }
+        String yearHalf=examNumber.substring(8, 9);
+        if(!yearHalf.equals(SysProperty.YEAR_HALF)) {
+            throw new StatusException("考次不正确");
+        }
+        int subjectCode=Integer.valueOf(examNumber.substring(9, 10));
+        if(subjectCode<1||subjectCode>6) {
+            throw new StatusException("语种不正确");
+        }
+    }
+
+}

+ 2 - 2
src/main/java/cn/com/qmth/scancloud/tools/service/impl/ExamStudentImportTask.java

@@ -129,7 +129,7 @@ public class ExamStudentImportTask extends AbstractTask {
 
         try {
 
-            // 默认模板:科目代码|科目名称|准考证号|考生姓名|考生编号|卷袋号|座位号|学习中心|考场|考点
+            // 默认模板:准考证号,考生姓名,考点名称
             String examNumber = values[0].trim();
             String name = values[1].trim();
             String examSiteName = values[2].trim();
@@ -185,7 +185,7 @@ public class ExamStudentImportTask extends AbstractTask {
             throw new StatusException("考次不正确");
         }
         int subjectCode=Integer.valueOf(examNumber.substring(9, 10));
-        if(subjectCode<1||subjectCode>6) {
+        if(subjectCode<1||subjectCode>9) {
             throw new StatusException("语种不正确");
         }
     }

+ 1 - 1
src/main/java/cn/com/qmth/scancloud/tools/service/impl/ObjectiveQuestionExportTask.java

@@ -21,7 +21,7 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * 考生客观题作答阅卷数据导出
+ * 考生打包文件和扫描结果导出
  */
 public class ObjectiveQuestionExportTask extends AbstractTask {