xiatian 1 năm trước cách đây
mục cha
commit
337a0a802a

+ 5 - 1
db/am_db.sql

@@ -40,6 +40,10 @@ CREATE TABLE `am_student_score` (
   `marking_score` double DEFAULT NULL,
   `marking_score` double DEFAULT NULL,
   `score_ratio` double DEFAULT NULL,
   `score_ratio` double DEFAULT NULL,
   `student_id` bigint NOT NULL,
   `student_id` bigint NOT NULL,
+  `exam_id` bigint NOT NULL,
+  `student_code` varchar(255) COLLATE utf8mb4_bin NOT NULL,
+  `subject_code` varchar(255) COLLATE utf8mb4_bin NOT NULL,
   `sub_number` int NOT NULL,
   `sub_number` int NOT NULL,
-  PRIMARY KEY (`id`)
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `IDX_STUDENT_SCORE_01` (`exam_id`, `subject_code`, `student_code`,`main_number`, `sub_number`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

+ 64 - 0
src/main/java/cn/com/qmth/am/bean/StudentScoreDto.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.am.bean;
+
+public class StudentScoreDto {
+
+	private Long examId;
+	private String subjectCode;
+	private String studentCode;
+	private Integer mainNumber;
+	private String subNumber;
+	
+	//人评分
+	private Double markingScore;
+
+	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 getStudentCode() {
+		return studentCode;
+	}
+
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
+
+	public Integer getMainNumber() {
+		return mainNumber;
+	}
+
+	public void setMainNumber(Integer mainNumber) {
+		this.mainNumber = mainNumber;
+	}
+
+	public String getSubNumber() {
+		return subNumber;
+	}
+
+	public void setSubNumber(String subNumber) {
+		this.subNumber = subNumber;
+	}
+
+	public Double getMarkingScore() {
+		return markingScore;
+	}
+
+	public void setMarkingScore(Double markingScore) {
+		this.markingScore = markingScore;
+	}
+
+
+
+}

+ 27 - 0
src/main/java/cn/com/qmth/am/entity/StudentScoreEntity.java

@@ -11,7 +11,10 @@ public class StudentScoreEntity extends IdEntity {
 	private static final long serialVersionUID = -6261302618070108336L;
 	private static final long serialVersionUID = -6261302618070108336L;
 
 
 	private Long studentId;
 	private Long studentId;
+	private Long examId;
 
 
+	private String subjectCode;
+	private String studentCode;
 	private Integer mainNumber;
 	private Integer mainNumber;
 	private String subNumber;
 	private String subNumber;
 	
 	
@@ -92,6 +95,30 @@ public class StudentScoreEntity extends IdEntity {
 		this.errMsg = errMsg;
 		this.errMsg = errMsg;
 	}
 	}
 
 
+	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 getStudentCode() {
+		return studentCode;
+	}
+
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
+
 
 
 
 
 }
 }

+ 1 - 0
src/main/java/cn/com/qmth/am/service/StudentScoreService.java

@@ -9,5 +9,6 @@ import cn.com.qmth.am.entity.StudentScoreEntity;
  */
  */
 public interface StudentScoreService  extends IService<StudentScoreEntity> {
 public interface StudentScoreService  extends IService<StudentScoreEntity> {
 
 
+	void importScore();
 
 
 }
 }

+ 256 - 0
src/main/java/cn/com/qmth/am/service/impl/StudentScoreServiceImpl.java

@@ -1,14 +1,270 @@
 package cn.com.qmth.am.service.impl;
 package cn.com.qmth.am.service.impl;
 
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 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 cn.com.qmth.am.bean.ImportResult;
+import cn.com.qmth.am.bean.StudentScoreDto;
+import cn.com.qmth.am.config.SysProperty;
 import cn.com.qmth.am.dao.StudentScoreDao;
 import cn.com.qmth.am.dao.StudentScoreDao;
 import cn.com.qmth.am.entity.StudentScoreEntity;
 import cn.com.qmth.am.entity.StudentScoreEntity;
 import cn.com.qmth.am.service.StudentScoreService;
 import cn.com.qmth.am.service.StudentScoreService;
 
 
 @Service
 @Service
 public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, StudentScoreEntity> implements StudentScoreService {
 public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, StudentScoreEntity> implements StudentScoreService {
+	private static final String[] EXCEL_HEADER = new String[] { "考试ID", "科目代码","考生编号", "大题号", "小题号", "评分"};
+	@Autowired
+	private SysProperty sysProperty;
+
+	@Override
+	public void importScore() {
+		File dir = new File(sysProperty.getDataDir() + "/marking-score-import");
+		File[] fs = dir.listFiles();
+		if (fs == null || fs.length == 0) {
+			return;
+		}
+		for (File file : fs) {
+			if (!file.isFile() || !file.getName().endsWith(".xlsx")) {
+				continue;
+			}
+			InputStream inputStream = null;
+			ImportResult ret = null;
+			try {
+				inputStream = new FileInputStream(file);
+				ret = disposeFile(inputStream);
+			} catch (Exception e) {
+				String errMsg;
+				if (e instanceof FileNotFoundException) {
+					errMsg = "未找到文件:" + file.getAbsolutePath();
+				} else {
+					errMsg = "系统错误:" + e.getMessage();
+				}
+				ret = new ImportResult(errMsg);
+			} finally {
+				if (inputStream != null) {
+					try {
+						inputStream.close();
+					} catch (IOException e) {
+					}
+				}
+			}
+			moveFile(dir, file, ret);
+		}
+	}
+
+	private void moveFile(File dir, File file, ImportResult ret) {
+		try {
+			boolean succss = CollectionUtils.isEmpty(ret.getErrMsg());
+			if (succss) {
+				File sucDir = new File(dir.getAbsoluteFile() + "/success/");
+				if (!sucDir.exists()) {
+					sucDir.mkdir();
+				}
+				File targetFile = new File(sucDir.getAbsoluteFile() + "/" + file.getName());
+				if (targetFile.exists()) {
+					targetFile.delete();
+				}
+				FileUtils.copyFile(file, targetFile);
+				file.delete();
+				String fname = file.getName().substring(0, file.getName().lastIndexOf("."));
+				File msgFile = new File(sucDir.getAbsoluteFile() + "/" + fname + "_info.txt");
+				if (msgFile.exists()) {
+					msgFile.delete();
+				}
+				FileUtils.write(msgFile, ret.getCountInfo(), "UFT-8");
+			} else {
+				File sucDir = new File(dir.getAbsoluteFile() + "/failed/");
+				if (!sucDir.exists()) {
+					sucDir.mkdir();
+				}
+				File targetFile = new File(sucDir.getAbsoluteFile() + "/" + file.getName());
+				if (targetFile.exists()) {
+					targetFile.delete();
+				}
+				FileUtils.copyFile(file, targetFile);
+				file.delete();
+				String fname = file.getName().substring(0, file.getName().lastIndexOf("."));
+				File msgFile = new File(sucDir.getAbsoluteFile() + "/" + fname + "_info.txt");
+				if (msgFile.exists()) {
+					msgFile.delete();
+				}
+				FileUtils.writeLines(msgFile, ret.getErrMsg(), "UFT-8");
+			}
+		} catch (IOException e) {
+			throw new StatusException("文件处理出错", e);
+		}
+
+	}
+
+	private String errorMsg(int lineNum, String msg) {
+		return "第" + lineNum + "行 " + msg;
+	}
+
+	private String trimAndNullIfBlank(String s) {
+		if (StringUtils.isBlank(s)) {
+			return null;
+		}
+		return s.trim();
+	}
+
+	private ImportResult disposeFile(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 (!Arrays.equals(EXCEL_HEADER, reader.getColumnNames())) {
+			throw new StatusException("Excel表头错误");
+		}
+		if (CollectionUtils.isEmpty(lineList)) {
+			throw new StatusException("Excel无内容");
+		}
+		if (100001 < lineList.size()) {
+			throw new StatusException("数据行数不能超过100000");
+		}
+		List<StudentScoreDto> ss = new ArrayList<>();
+		ImportResult ret = new ImportResult();
+		List<String> failRecords = new ArrayList<>();
+		ret.setErrMsg(failRecords);
+		for (int i = 0; i < lineList.size(); i++) {
+			DataMap line = lineList.get(i);
+
+			StringBuilder msg = new StringBuilder();
+
+			StudentScoreDto imp = new StudentScoreDto();
+			String examId = trimAndNullIfBlank(line.get(EXCEL_HEADER[0]));
+			if (StringUtils.isBlank(examId)) {
+				msg.append("  考试ID不能为空");
+			} else if (examId.length() > 20) {
+				msg.append("  考试ID不能超过20个字符");
+			} else {
+				try {
+					Long examIdVal = Long.parseLong(examId);
+					imp.setExamId(examIdVal);
+				} catch (NumberFormatException e) {
+					msg.append("  考试ID只能是数字");
+				}
+			}
+
+			String subjectCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[1]));
+			if (StringUtils.isBlank(subjectCode)) {
+				msg.append("  科目代码不能为空");
+			} else if (subjectCode.length() > 100) {
+				msg.append("  科目代码不能超过100个字符");
+			}
+			imp.setSubjectCode(subjectCode);
+			
+			String studentCode = trimAndNullIfBlank(line.get(EXCEL_HEADER[2]));
+			if (StringUtils.isBlank(studentCode)) {
+				msg.append("  考生编号不能为空");
+			} else if (studentCode.length() > 100) {
+				msg.append("  考生编号不能超过100个字符");
+			}
+			imp.setStudentCode(studentCode);
+
+			String mainNum = trimAndNullIfBlank(line.get(EXCEL_HEADER[3]));
+			if (StringUtils.isBlank(mainNum)) {
+				msg.append("  大题号不能为空");
+			} else if (mainNum.length() > 10) {
+				msg.append("  大题号不能超过10个字符");
+			} else {
+				try {
+					Integer mainNumVal = Integer.parseInt(mainNum);
+					if (mainNumVal <= 0) {
+						msg.append("  大题号必须大于0");
+					}
+					imp.setMainNumber(mainNumVal);
+				} catch (NumberFormatException e) {
+					msg.append("  大题号格式错误");
+				}
+			}
+
+			String subNum = trimAndNullIfBlank(line.get(EXCEL_HEADER[4]));
+			if (StringUtils.isBlank(subNum)) {
+				msg.append("  小题号不能为空");
+			} else if (subNum.length() > 10) {
+				msg.append("  小题号不能超过10个字符");
+			} 
+			imp.setSubNumber(subNum);
+
+			String score = trimAndNullIfBlank(line.get(EXCEL_HEADER[5]));
+			if (StringUtils.isBlank(score)) {
+				msg.append("  评分不能为空");
+			} else if (score.length() > 10) {
+				msg.append("  评分不能超过10个字符");
+			} else {
+				try {
+					Double scoreVal = Double.parseDouble(score);
+					imp.setMarkingScore(scoreVal);
+				} catch (NumberFormatException e) {
+					msg.append("  评分格式错误");
+				}
+			}
+
+
+			if (msg.length() > 0) {
+				failRecords.add(errorMsg(i + 1, msg.toString()));
+			} else {
+				ss.add(imp);
+			}
+
+		}
+
+		if (CollectionUtils.isNotEmpty(failRecords)) {
+			return ret;
+		}
+		try {
+			updateScoreBatch(ret,ss);
+		} catch (Exception e) {
+			failRecords.add("系统错误:" + e.getMessage());
+		}
+		return ret;
+	}
+
 
 
+	private void updateScoreBatch(ImportResult ret,List<StudentScoreDto> ss) {
+		if (CollectionUtils.isEmpty(ss)) {
+			ret.setCountInfo("更新数量:0");
+			return;
+		}
+		int count=0;
+		for (StudentScoreDto s : ss) {
+			count=count+updateScore(s);
+		}
+		ret.setCountInfo("更新数量:"+count);
+	}
+	
+	private int updateScore(StudentScoreDto dto) {
+		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+		lw.set(StudentScoreEntity::getMarkingScore, dto.getMarkingScore());
+		lw.eq(StudentScoreEntity::getExamId, dto.getExamId());
+		lw.eq(StudentScoreEntity::getSubjectCode, dto.getSubjectCode());
+		lw.eq(StudentScoreEntity::getStudentCode, dto.getStudentCode());
+		lw.eq(StudentScoreEntity::getMainNumber, dto.getMainNumber());
+		lw.eq(StudentScoreEntity::getSubNumber, dto.getSubNumber());
+		return this.update(wrapper)?1:0;
+	}
 }
 }

+ 36 - 0
src/main/java/cn/com/qmth/am/task/StudentScoreImportJob.java

@@ -0,0 +1,36 @@
+package cn.com.qmth.am.task;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
+
+import cn.com.qmth.am.enums.LockType;
+import cn.com.qmth.am.service.StudentScoreService;
+
+@Service
+public class StudentScoreImportJob {
+
+    @Autowired
+    private ConcurrentService concurrentService;
+    
+    @Autowired
+    private StudentScoreService studentScoreService;
+
+    @Scheduled(fixedDelay = 5 * 1000, initialDelay = 10 * 1000)
+    public void doJob() {
+        boolean lock = concurrentService.getReadWriteLock(LockType.MARKING_SCORE_IMPORT.name()).writeLock().tryLock();
+        try {
+            if (!lock) {
+                return;
+            }
+            studentScoreService.importScore();
+        } finally {
+            if (lock) {
+                concurrentService.getReadWriteLock(LockType.MARKING_SCORE_IMPORT.name()).writeLock().unlock();
+            }
+        }
+    }
+
+}