Răsfoiți Sursa

保存步骤分

xiatian 7 luni în urmă
părinte
comite
52df087283

+ 1 - 0
db/am_db.sql

@@ -62,6 +62,7 @@ CREATE TABLE `am_student_score` (
   `marking_score` double DEFAULT NULL,
   `err_msg` varchar(2000) COLLATE utf8mb4_bin DEFAULT NULL,
   `score_none` bit(1) DEFAULT NULL,
+  `step_score` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `IDX_STUDENT_SCORE_01` (`student_id`, `question_id`),
   UNIQUE KEY `IDX_STUDENT_SCORE_02` (`exam_id`, `subject_code`, `student_code`,`main_number`, `sub_number`)

+ 142 - 119
src/main/java/cn/com/qmth/am/entity/StudentScoreEntity.java

@@ -1,165 +1,188 @@
 package cn.com.qmth.am.entity;
 
+import java.util.List;
+
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 
 import cn.com.qmth.am.entity.base.IdEntity;
 import cn.com.qmth.am.enums.DataStatus;
 
-@TableName("am_student_score")
+@TableName(value = "am_student_score", autoResultMap = true)
 public class StudentScoreEntity extends IdEntity {
 
-	private static final long serialVersionUID = -6261302618070108336L;
-
-	private Long studentId;
-	private Long questionId;
-	private Long examId;
-
-	private String subjectCode;
-	private String studentCode;
-	private Integer mainNumber;
-	private String subNumber;
-
-	// 机评分
-	private Double aiScore;
-	// 人评分
-	private Double markingScore;
-	// 机评得分率
-	private Double scoreRatio;
-	//ocr状态
-	private DataStatus answerStatus;
-	//评分状态
-	private DataStatus scoreStatus;
-	//ocr结果
-	private String answer;
-
-	//错误信息
+    private static final long serialVersionUID = -6261302618070108336L;
+
+    private Long studentId;
+
+    private Long questionId;
+
+    private Long examId;
+
+    private String subjectCode;
+
+    private String studentCode;
+
+    private Integer mainNumber;
+
+    private String subNumber;
+
+    // 机评分
+    private Double aiScore;
+
+    // 人评分
+    private Double markingScore;
+
+    // 机评得分率
+    private Double scoreRatio;
+
+    // ocr状态
+    private DataStatus answerStatus;
+
+    // 评分状态
+    private DataStatus scoreStatus;
+
+    // ocr结果
+    private String answer;
+
+    // 错误信息
     @TableField(updateStrategy = FieldStrategy.IGNORED)
-	private String errMsg;
-    
-    //机评返回null
+    private String errMsg;
+
+    // 机评返回null
     private Boolean scoreNone;
 
-	public Long getStudentId() {
-		return studentId;
-	}
+    @TableField(updateStrategy = FieldStrategy.IGNORED, typeHandler = JacksonTypeHandler.class)
+    private List<Double> stepScore;
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
 
-	public void setStudentId(Long studentId) {
-		this.studentId = studentId;
-	}
+    public Integer getMainNumber() {
+        return mainNumber;
+    }
 
-	public Integer getMainNumber() {
-		return mainNumber;
-	}
+    public void setMainNumber(Integer mainNumber) {
+        this.mainNumber = mainNumber;
+    }
 
-	public void setMainNumber(Integer mainNumber) {
-		this.mainNumber = mainNumber;
-	}
+    public String getSubNumber() {
+        return subNumber;
+    }
 
-	public String getSubNumber() {
-		return subNumber;
-	}
+    public void setSubNumber(String subNumber) {
+        this.subNumber = subNumber;
+    }
 
-	public void setSubNumber(String subNumber) {
-		this.subNumber = subNumber;
-	}
+    public Double getAiScore() {
+        return aiScore;
+    }
 
-	public Double getAiScore() {
-		return aiScore;
-	}
+    public void setAiScore(Double aiScore) {
+        this.aiScore = aiScore;
+    }
 
-	public void setAiScore(Double aiScore) {
-		this.aiScore = aiScore;
-	}
+    public Double getMarkingScore() {
+        return markingScore;
+    }
 
-	public Double getMarkingScore() {
-		return markingScore;
-	}
+    public void setMarkingScore(Double markingScore) {
+        this.markingScore = markingScore;
+    }
 
-	public void setMarkingScore(Double markingScore) {
-		this.markingScore = markingScore;
-	}
+    public Double getScoreRatio() {
+        return scoreRatio;
+    }
 
-	public Double getScoreRatio() {
-		return scoreRatio;
-	}
+    public void setScoreRatio(Double scoreRatio) {
+        this.scoreRatio = scoreRatio;
+    }
 
-	public void setScoreRatio(Double scoreRatio) {
-		this.scoreRatio = scoreRatio;
-	}
+    public String getErrMsg() {
+        return errMsg;
+    }
 
+    public void setErrMsg(String errMsg) {
+        this.errMsg = errMsg;
+    }
 
-	public String getErrMsg() {
-		return errMsg;
-	}
+    public Long getExamId() {
+        return examId;
+    }
 
-	public void setErrMsg(String errMsg) {
-		this.errMsg = errMsg;
-	}
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
 
-	public Long getExamId() {
-		return examId;
-	}
+    public String getSubjectCode() {
+        return subjectCode;
+    }
 
-	public void setExamId(Long examId) {
-		this.examId = examId;
-	}
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
 
-	public String getSubjectCode() {
-		return subjectCode;
-	}
+    public String getStudentCode() {
+        return studentCode;
+    }
 
-	public void setSubjectCode(String subjectCode) {
-		this.subjectCode = subjectCode;
-	}
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
 
-	public String getStudentCode() {
-		return studentCode;
-	}
+    public DataStatus getAnswerStatus() {
+        return answerStatus;
+    }
 
-	public void setStudentCode(String studentCode) {
-		this.studentCode = studentCode;
-	}
+    public void setAnswerStatus(DataStatus answerStatus) {
+        this.answerStatus = answerStatus;
+    }
 
-	public DataStatus getAnswerStatus() {
-		return answerStatus;
-	}
+    public DataStatus getScoreStatus() {
+        return scoreStatus;
+    }
 
-	public void setAnswerStatus(DataStatus answerStatus) {
-		this.answerStatus = answerStatus;
-	}
+    public void setScoreStatus(DataStatus scoreStatus) {
+        this.scoreStatus = scoreStatus;
+    }
 
-	public DataStatus getScoreStatus() {
-		return scoreStatus;
-	}
+    public String getAnswer() {
+        return answer;
+    }
 
-	public void setScoreStatus(DataStatus scoreStatus) {
-		this.scoreStatus = scoreStatus;
-	}
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
 
-	public String getAnswer() {
-		return answer;
-	}
+    public Long getQuestionId() {
+        return questionId;
+    }
 
-	public void setAnswer(String answer) {
-		this.answer = answer;
-	}
+    public void setQuestionId(Long questionId) {
+        this.questionId = questionId;
+    }
 
-	public Long getQuestionId() {
-		return questionId;
-	}
+    public Boolean getScoreNone() {
+        return scoreNone;
+    }
 
-	public void setQuestionId(Long questionId) {
-		this.questionId = questionId;
-	}
+    public void setScoreNone(Boolean scoreNone) {
+        this.scoreNone = scoreNone;
+    }
 
-	public Boolean getScoreNone() {
-		return scoreNone;
-	}
+    public List<Double> getStepScore() {
+        return stepScore;
+    }
 
-	public void setScoreNone(Boolean scoreNone) {
-		this.scoreNone = scoreNone;
-	}
+    public void setStepScore(List<Double> stepScore) {
+        this.stepScore = stepScore;
+    }
 
 }

+ 813 - 791
src/main/java/cn/com/qmth/am/service/impl/StudentScoreServiceImpl.java

@@ -5,6 +5,7 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -23,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -65,795 +67,815 @@ import cn.com.qmth.am.utils.ImageUtil;
 
 @Service
 public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, StudentScoreEntity>
-		implements StudentScoreService {
-	private static final Logger log = LoggerFactory.getLogger(StudentScoreService.class);
-	private static BlockingQueue<StudentScoreImageDto> queue;
-	private static final String[] EXCEL_HEADER = new String[] { "考试ID", "科目代码", "考生编号", "大题号", "小题号", "评分" };
-	@Autowired
-	private SysProperty sysProperty;
-	@Autowired
-	private SolarService solarService;
-	@Autowired
-	private StudentService studentService;
-	@Autowired
-	private OcrApiClient ocrApiClient;
-	@Autowired
-	private AiService aiService;
-	@Autowired
-	private QuestionService questionService;
-	static {
-		int threadCount = Runtime.getRuntime().availableProcessors();
-		queue = new ArrayBlockingQueue<>(threadCount * 2);
-	}
-
-	@Override
-	public void importScore() {
-		File dir = new File(sysProperty.getDataDir());
-		File[] fs = dir.listFiles();
-		if (fs == null || fs.length == 0) {
-			return;
-		}
-		for (File file : fs) {
-			if (!file.isFile() || !file.getName().equals(ImportFileName.SCORE_IMPORT.getName())) {
-				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(), "utf-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, StandardCharsets.UTF_8.name(), ret.getErrMsg());
-			}
-		} 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 + 2, 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;
-	}
-
-	@Override
-	public List<StudentScoreEntity> getByStudentId(Long studentId) {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.eq(StudentScoreEntity::getStudentId, studentId);
-		return this.list(wrapper);
-	}
-
-	@Transactional
-	@Override
-	public void add(StudentEntity student, Map<Long, QuestionEntity> quetions,
-			List<StudentScoreEntity> oldscores) {
-		List<StudentScoreEntity> adds = new ArrayList<>();
-		for (QuestionEntity q : quetions.values()) {
-			if (!exists(q, oldscores)) {
-				StudentScoreEntity s = new StudentScoreEntity();
-				adds.add(s);
-				s.setQuestionId(q.getId());
-				s.setExamId(student.getExamId());
-				s.setAnswerStatus(DataStatus.WAITING);
-				s.setMainNumber(q.getMainNumber());
-				s.setScoreStatus(DataStatus.WAITING);
-				s.setStudentCode(student.getStudentCode());
-				s.setStudentId(student.getId());
-				s.setSubjectCode(student.getSubjectCode());
-				s.setSubNumber(q.getSubNumber());
-			}
-		}
-		if(CollectionUtils.isNotEmpty(adds)) {
-			this.saveBatch(adds);
-		}
-	}
-
-	private boolean exists(QuestionEntity q, List<StudentScoreEntity> oldscores) {
-		if(CollectionUtils.isEmpty(oldscores)) {
-			return false;
-		}
-		for (StudentScoreEntity s : oldscores) {
-			if (s.getExamId().equals(q.getExamId()) && s.getSubjectCode().equals(q.getSubjectCode())
-					&& s.getMainNumber().equals(q.getMainNumber()) && s.getSubNumber().equals(q.getSubNumber())) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	@Transactional
-	@Override
-	public void updateAnswerErr(Long id, String err) {
-		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
-		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.set(StudentScoreEntity::getAnswerStatus, DataStatus.FAILED);
-		lw.set(StudentScoreEntity::getErrMsg, err);
-		lw.eq(StudentScoreEntity::getId, id);
-		this.update(wrapper);
-	}
-
-	@Override
-	public void createSlice(StudentScoreEntity score, QuestionEntity q, Map<Integer, AnswerImageDto> answerImages) {
-		StudentScoreImageDto dto = new StudentScoreImageDto();
-		dto.setStudentId(score.getStudentId());
-		dto.setStudentScoreId(score.getId());
-		dto.setImage(getSlice(score, q, answerImages));
-//		saveSliceImage(score, dto.getImage());
-		try {
-			queue.put(dto);
-		} catch (InterruptedException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	private byte[] getSlice(StudentScoreEntity score, QuestionEntity q, Map<Integer, AnswerImageDto> answerImages) {
-		List<byte[]> ret = new ArrayList<>();
-		String suff = null;
-		for (ImageSlice s : q.getImageSlice()) {
-			AnswerImageDto sheet = getSheet(score, q, s.getI(), answerImages);
-			suff = sheet.getSuff();
-			ret.add(ImageUtil.cutImage(sheet.getImage(), sheet.getSuff(), s.getX().intValue(), s.getY().intValue(),
-					s.getW().intValue(), s.getH().intValue()));
-		}
-		if (ret.size() > 1) {
-			return ImageUtil.joinImages(ret, suff);
-		} else {
-			return ret.get(0);
-		}
-	}
-
-	private AnswerImageDto getSheet(StudentScoreEntity score, QuestionEntity q, Integer pageIndex,
-			Map<Integer, AnswerImageDto> answerImages) {
-		AnswerImageDto ret = answerImages.get(pageIndex);
-		if (ret != null) {
-			return ret;
-		}
-		try {
-			ret = new AnswerImageDto();
-			String url = getImageUrl(score, q, pageIndex);
-			String tem = url.split("\\?")[0];
-			String suff = tem.substring(tem.lastIndexOf(".") + 1).toLowerCase();
-			ret.setImage(ByteArray.fromUrl(url).value());
-//			saveSheetImage(score,pageIndex, ret.getImage());
-			ret.setPageIndex(pageIndex);
-			ret.setSuff(suff);
-			answerImages.put(pageIndex, ret);
-			return ret;
-		} catch (Exception e) {
-			throw new RuntimeException(e);
-		}
-	}
-//	private void saveSheetImage(StudentScoreEntity s,Integer page,byte[] bs) {
-//		File dir=new File(sysProperty.getDataDir()+"/"+"sheet");
-//		if(!dir.exists()) {
-//			dir.mkdir();
-//		}
-//		File image=new File(dir.getAbsolutePath()+"/"+s.getStudentCode()+"-"+page+".jpg");
-//		if(image.exists()) {
-//			image.delete();
-//		}
-//		FileOutputStream out=null;
-//		try {
-//			out = new FileOutputStream(image);
-//			out.write(bs, 0, bs.length);
-//			out.flush();
-//		} catch (Exception e) {
-//		}finally {
-//			if(out!=null) {
-//				try {
-//					out.close();
-//				} catch (IOException e) {
-//				}
-//			}
-//		}
-//	}
-//	private void saveSliceImage(StudentScoreEntity s,byte[] bs) {
-//		File dir=new File(sysProperty.getDataDir()+"/"+"slice");
-//		if(!dir.exists()) {
-//			dir.mkdir();
-//		}
-//		File image=new File(dir.getAbsolutePath()+"/"+s.getStudentCode()+"-"+s.getMainNumber()+"-"+s.getSubNumber()+".jpg");
-//		if(image.exists()) {
-//			image.delete();
-//		}
-//		FileOutputStream out=null;
-//		try {
-//			out = new FileOutputStream(image);
-//			out.write(bs, 0, bs.length);
-//			out.flush();
-//		} catch (Exception e) {
-//		}finally {
-//			if(out!=null) {
-//				try {
-//					out.close();
-//				} catch (IOException e) {
-//				}
-//			}
-//		}
-//	}
-
-	private String getImageUrl(StudentScoreEntity score, QuestionEntity q, Integer pageIndex) {
-		if (DataType.MARKING_CLOUD.equals(sysProperty.getDataType())) {
-			return getImageUrlFromMarkingCloud(score, pageIndex);
-		} else if (DataType.TEACH_CLOUD.equals(sysProperty.getDataType())) {
-			return getImageUrlFromTeachCloud(score, q, pageIndex);
-		} else {
-			throw new StatusException("数据类型错误");
-		}
-	}
-
-	private String getImageUrlFromMarkingCloud(StudentScoreEntity score, Integer pageIndex) {
-		return sysProperty.getImageServer() + "/" + getMarkingCloudPath(score.getExamId(),
-				getSuffix(score.getStudentCode()), score.getStudentCode(), pageIndex, "jpg");
-	}
-
-	private static String getSuffix(String input) {
-		return StringUtils.trimToEmpty(input).substring(Math.max(0, input.length() - 3));
-	}
-
-	private String getImageUrlFromTeachCloud(StudentScoreEntity score, QuestionEntity q, Integer pageIndex) {
-		int paperNum = (pageIndex + 1) / 2;
-		int page = 1;
-		if (pageIndex % 2 == 0) {
-			page = 2;
-		}
-		return sysProperty.getImageServer() + "/" + getTeachCloudPath(score.getExamId(), score.getSubjectCode(),
-				score.getStudentCode(), paperNum, page, "jpg");
-	}
-
-	private String getTeachCloudPath(Object... param) {
-		return String.format("sheet/%d/%s/%s/%d-%d.%s", param);
-	}
-
-	private static String getMarkingCloudPath(Object... param) {
-		return String.format("sheet/%d/%s/%s-%d.%s", param);
-	}
-
-	public static void main(String[] args) {
-//		Pattern pattern = Pattern.compile("\\[\\[([0-9](.[0-9]+){0,1})分\\]\\]");
-//		String d="[[1.1分]]中国共产党是中国特色社会主义事业的坚强领导核心。\n"
-//				+ "[[1.2分]]中国共产党的领导地位是在历史奋斗中形成的。\n"
-//				+ "[[1.3分]]中国共产党领导是人民当家作主的可靠保障。\n"
-//				+ "[[1.4分]]中国共产党领导关系中国特色社会主义的性质、方向和命运。\n"
-//				+ "[[2分]]中国共产党领导是实现中华民族伟大复兴的根本保证。";
-//		Matcher matcher = pattern.matcher(d);
-//		int start=0;
-//		double score=0.0;
-//		while (matcher.find()) {
-//			if(start!=0) {
-//				System.out.println(score+d.substring(start,matcher.start()));
-//			}
-//			score=Double.valueOf(matcher.group(1));
-//			start=matcher.end();
-//        }
-//		if(start<d.length()) {
-//			System.out.println(score+d.substring(start,d.length()));
-//		}
-//		String[] items = d.split("\\[\\[[0-9](.[0-9]+){0,1}分\\]\\]");
-		String code = "2021113801";
-		String s = "https://file.markingcloud.com/" + getMarkingCloudPath(1379, getSuffix(code), code, 1, "jpg");
-		System.out.println(s);
-	}
-
-	@Override
-	public StudentScoreImageDto pollStudentScoreImage() {
-		try {
-			return queue.take();
-		} catch (InterruptedException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	@Transactional
-	@Override
-	public void ocr(StudentScoreImageDto dto) {
-		try {
-			OrgInfo org = solarService.getOrgList().get(0);
-			String ret = ocrDispose(dto, org);
-			if (ret != null) {
-				updateAnswer(dto.getStudentScoreId(), ret);
-			} else {
-				ocrErr(dto, "ocr失败,返回null");
-			}
-		} catch (Exception e) {
-			ocrErr(dto, e.getMessage());
-		}
-	}
-
-	private void updateAnswer(Long id, String answer) {
-		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
-		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.set(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
-		lw.set(StudentScoreEntity::getErrMsg, null);
-		lw.set(StudentScoreEntity::getAnswer, answer);
-		lw.eq(StudentScoreEntity::getId, id);
-		this.update(wrapper);
-	}
-
-	private String ocrDispose(StudentScoreImageDto dto, OrgInfo org) {
-		SignatureInfo signature = SignatureInfo.secret(org.getAccessKey(), org.getAccessSecret());
-		try {
-			return ocrApiClient.forImage(signature, OcrType.HANDWRITING, UploadFile.build("image", "", dto.getImage()));
-		} catch (Exception e) {
-			log.error("ocr异常", e);
-			if (e instanceof RetrofitResponseError) {
-				RetrofitResponseError tem = (RetrofitResponseError) e;
-				if (tem.getCode() == 503) {
-					if (dto.getRetry() <= 3) {
-						try {
-							Thread.sleep(3000);
-						} catch (InterruptedException e1) {
-						}
-						dto.setRetry(dto.getRetry() + 1);
-						return ocrDispose(dto, org);
-					} else {
-						throw new StatusException("重试次数过多");
-					}
-				} else {
-					throw e;
-				}
-			} else {
-				throw e;
-			}
-		}
-	}
-
-	private void ocrErr(StudentScoreImageDto dto, String err) {
-		updateAnswerErr(dto.getStudentScoreId(), err);
-		studentService.updateStatus(dto.getStudentId(), DataStatus.FAILED);
-	}
-
-	@Override
-	public List<StudentScoreEntity> findAllToAiMarking() {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.eq(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
-		lw.in(StudentScoreEntity::getScoreStatus, DataStatus.WAITING, DataStatus.FAILED);
-		return this.list(wrapper);
-	}
-	
-	@Override
-	public List<StudentScoreEntity> findToAiMarking(Long studentId) {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.eq(StudentScoreEntity::getStudentId, studentId);
-		lw.eq(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
-		lw.in(StudentScoreEntity::getScoreStatus, DataStatus.WAITING, DataStatus.FAILED);
-		return this.list(wrapper);
-	}
-
-	@Transactional
-	@Override
-	public void aiMarking(StudentScoreEntity score) {
-		AiMarkingDto dto = new AiMarkingDto();
-		dto.setScoreInfo(score);
-		try {
-			OrgInfo org = solarService.getOrgList().get(0);
-			QuestionEntity q = questionService.getById(score.getQuestionId());
-			if (q == null) {
-				throw new StatusException("未找到试题信息");
-			}
-			if (CollectionUtils.isEmpty(q.getAnswer())) {
-				return;
-			}
-			AutoScoreRequest req = new AutoScoreRequest();
-			req.setQuestionBody(q.getContent());
-			req.setStandardAnswer(q.getAnswer());
-			req.setStudentAnswer(score.getAnswer());
-			req.setSubjectName(q.getSubjectName());
-			req.setTotalScore(q.getFullScore());
-			req.setIntervalScore(0.5);
-			AutoScoreResult ret = aiMarkingDispose(dto, org, req);
-			if (ret != null) {
-				updateScore(score.getId(), ret.getTotalScore(), null);
-			} else {
-				updateScoreNone(score.getId(), 0.0, 0.0);
-			}
-			if (allSuccess(score.getStudentId())) {
-				studentService.updateStatus(score.getStudentId(), DataStatus.SUCCESS);
-			}
-		} catch (Exception e) {
-			aiScoreErr(dto, e.getMessage());
-		}
-	}
-
-	private boolean allSuccess(Long studentId) {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.eq(StudentScoreEntity::getStudentId, studentId);
-		List<StudentScoreEntity> list = this.list(wrapper);
-		for (StudentScoreEntity s : list) {
-			if (!DataStatus.SUCCESS.equals(s.getScoreStatus())) {
-				return false;
-			}
-		}
-		return true;
-	}
-
-	private AutoScoreResult aiMarkingDispose(AiMarkingDto dto, OrgInfo org, AutoScoreRequest req) {
-		SignatureInfo signature = SignatureInfo.secret(org.getAccessKey(), org.getAccessSecret());
-		try {
-			return aiService.autoScore(req, signature);
-		} catch (Exception e) {
-			log.error("aiScore异常", e);
-			if (e instanceof RetrofitResponseError) {
-				RetrofitResponseError tem = (RetrofitResponseError) e;
-				if (tem.getCode() == 503) {
-					if (dto.getRetry() <= 3) {
-						try {
-							Thread.sleep(3000);
-						} catch (InterruptedException e1) {
-						}
-						dto.setRetry(dto.getRetry() + 1);
-						return aiMarkingDispose(dto, org, req);
-					} else {
-						throw new StatusException("重试次数过多");
-					}
-				} else {
-					throw e;
-				}
-			} else {
-				throw e;
-			}
-		}
-	}
-
-	private void updateScore(Long id, Double aiScore, Double scoreRatio) {
-		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
-		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.set(StudentScoreEntity::getScoreStatus, DataStatus.SUCCESS);
-		lw.set(StudentScoreEntity::getAiScore, aiScore);
-		lw.set(StudentScoreEntity::getScoreRatio, scoreRatio);
-		lw.set(StudentScoreEntity::getErrMsg, null);
-		lw.eq(StudentScoreEntity::getId, id);
-		this.update(wrapper);
-	}
-
-	private void updateScoreNone(Long id, Double aiScore, Double scoreRatio) {
-		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
-		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.set(StudentScoreEntity::getScoreStatus, DataStatus.SUCCESS);
-		lw.set(StudentScoreEntity::getAiScore, aiScore);
-		lw.set(StudentScoreEntity::getScoreRatio, scoreRatio);
-		lw.set(StudentScoreEntity::getErrMsg, null);
-		lw.set(StudentScoreEntity::getScoreNone, true);
-		lw.eq(StudentScoreEntity::getId, id);
-		this.update(wrapper);
-	}
-
-	private void aiScoreErr(AiMarkingDto dto, String err) {
-		updateScoreErr(dto.getScoreInfo().getId(), err);
-		studentService.updateStatus(dto.getScoreInfo().getStudentId(), DataStatus.FAILED);
-	}
-
-	private void updateScoreErr(Long id, String err) {
-		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
-		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.set(StudentScoreEntity::getScoreStatus, DataStatus.FAILED);
-		lw.set(StudentScoreEntity::getErrMsg, err);
-		lw.eq(StudentScoreEntity::getId, id);
-		this.update(wrapper);
-	}
-
-	private void resetAnswerStatus() {
-		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
-		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.set(StudentScoreEntity::getAnswerStatus, DataStatus.WAITING);
-		lw.eq(StudentScoreEntity::getAnswerStatus, DataStatus.PROCESSING);
-		this.update(wrapper);
-	}
-
-	private void resetScoreStatus() {
-		UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
-		LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.set(StudentScoreEntity::getScoreStatus, DataStatus.WAITING);
-		lw.eq(StudentScoreEntity::getScoreStatus, DataStatus.PROCESSING);
-		this.update(wrapper);
-	}
-
-	@Override
-	public void resetStatus() {
-		resetAnswerStatus();
-		resetScoreStatus();
-	}
-
-	@Override
-	public int countBy(Long examId, DataStatus status) {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		if (status != null) {
-			lw.eq(StudentScoreEntity::getScoreStatus, status);
-		}
-		lw.eq(StudentScoreEntity::getExamId, examId);
-		return this.count(wrapper);
-	}
-
-	@Transactional
-	@Override
-	public void removeBy(Long examId, String subjectCode) {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		if (subjectCode != null) {
-			lw.eq(StudentScoreEntity::getSubjectCode, subjectCode);
-		}
-		lw.eq(StudentScoreEntity::getExamId, examId);
-		this.remove(wrapper);
-	}
-
-	@Override
-	public int countOcrBy(Long examId, DataStatus status) {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		if (status != null) {
-			lw.eq(StudentScoreEntity::getAnswerStatus, status);
-		}
-		lw.eq(StudentScoreEntity::getExamId, examId);
-		return this.count(wrapper);
-	}
-
-	@Override
-	public List<StudentScoreEntity> findBy(Long examId, String subjectCode, Integer mainNumber, String subNumber,
-			Boolean exZero, Integer count, Integer score) {
-		QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
-		LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
-		lw.eq(StudentScoreEntity::getExamId, examId);
-		lw.eq(StudentScoreEntity::getSubjectCode, subjectCode);
-		lw.eq(StudentScoreEntity::getMainNumber, mainNumber);
-		lw.eq(StudentScoreEntity::getSubNumber, subNumber);
-		lw.isNotNull(StudentScoreEntity::getAiScore);
-		lw.isNotNull(StudentScoreEntity::getMarkingScore);
-		lw.and(wq -> {
-			wq.or(wq1 -> wq1.isNull(StudentScoreEntity::getScoreNone));
-			wq.or(wq2 -> wq2.eq(StudentScoreEntity::getScoreNone, false));
-		});
-		if (exZero != null && exZero) {
-			lw.and(wq -> {
-				wq.or(wq1 -> wq1.ne(StudentScoreEntity::getAiScore, 0));
-				wq.or(wq2 -> wq2.eq(StudentScoreEntity::getMarkingScore, 0));
-			});
-		}
-		List<StudentScoreEntity> ret = this.list(wrapper);
-		if (CollectionUtils.isEmpty(ret)) {
-			return ret;
-		}
-		if (score != null) {
-			List<StudentScoreEntity> tem = new ArrayList<>();
-			for (StudentScoreEntity s : ret) {
-				if (getSubtract(s.getAiScore(), s.getMarkingScore()) <= score) {
-					tem.add(s);
-				}
-			}
-			ret = tem;
-		}
-		if (CollectionUtils.isEmpty(ret)) {
-			return ret;
-		}
-		if (count != null) {
-			ret.sort(new Comparator<StudentScoreEntity>() {
-				@Override
-				public int compare(StudentScoreEntity o1, StudentScoreEntity o2) {
-					String c1 = o1.getStudentCode();
-					String c2 = o2.getStudentCode();
-					return c1.compareTo(c2);
-				}
-			});
-			if (ret.size() <= count) {
-				return ret;
-			}
-			return ret.subList(0, count);
-		}
-		return ret;
-	}
-
-	private Double getSubtract(Double d1, Double d2) {
-		if (d1 == null || d2 == null) {
-			return null;
-		}
-		Double r = d1 - d2;
-		if (r < 0) {
-			r = 0 - r;
-		}
-		return r;
-	}
+        implements StudentScoreService {
+
+    private static final Logger log = LoggerFactory.getLogger(StudentScoreService.class);
+
+    private static BlockingQueue<StudentScoreImageDto> queue;
+
+    private static final String[] EXCEL_HEADER = new String[] { "考试ID", "科目代码", "考生编号", "大题号", "小题号", "评分" };
+
+    @Autowired
+    private SysProperty sysProperty;
+
+    @Autowired
+    private SolarService solarService;
+
+    @Autowired
+    private StudentService studentService;
+
+    @Autowired
+    private OcrApiClient ocrApiClient;
+
+    @Autowired
+    private AiService aiService;
+
+    @Autowired
+    private QuestionService questionService;
+    static {
+        int threadCount = Runtime.getRuntime().availableProcessors();
+        queue = new ArrayBlockingQueue<>(threadCount * 2);
+    }
+
+    @Override
+    public void importScore() {
+        File dir = new File(sysProperty.getDataDir());
+        File[] fs = dir.listFiles();
+        if (fs == null || fs.length == 0) {
+            return;
+        }
+        for (File file : fs) {
+            if (!file.isFile() || !file.getName().equals(ImportFileName.SCORE_IMPORT.getName())) {
+                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(), "utf-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, StandardCharsets.UTF_8.name(), ret.getErrMsg());
+            }
+        } 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 + 2, 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;
+    }
+
+    @Override
+    public List<StudentScoreEntity> getByStudentId(Long studentId) {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.eq(StudentScoreEntity::getStudentId, studentId);
+        return this.list(wrapper);
+    }
+
+    @Transactional
+    @Override
+    public void add(StudentEntity student, Map<Long, QuestionEntity> quetions, List<StudentScoreEntity> oldscores) {
+        List<StudentScoreEntity> adds = new ArrayList<>();
+        for (QuestionEntity q : quetions.values()) {
+            if (!exists(q, oldscores)) {
+                StudentScoreEntity s = new StudentScoreEntity();
+                adds.add(s);
+                s.setQuestionId(q.getId());
+                s.setExamId(student.getExamId());
+                s.setAnswerStatus(DataStatus.WAITING);
+                s.setMainNumber(q.getMainNumber());
+                s.setScoreStatus(DataStatus.WAITING);
+                s.setStudentCode(student.getStudentCode());
+                s.setStudentId(student.getId());
+                s.setSubjectCode(student.getSubjectCode());
+                s.setSubNumber(q.getSubNumber());
+            }
+        }
+        if (CollectionUtils.isNotEmpty(adds)) {
+            this.saveBatch(adds);
+        }
+    }
+
+    private boolean exists(QuestionEntity q, List<StudentScoreEntity> oldscores) {
+        if (CollectionUtils.isEmpty(oldscores)) {
+            return false;
+        }
+        for (StudentScoreEntity s : oldscores) {
+            if (s.getExamId().equals(q.getExamId()) && s.getSubjectCode().equals(q.getSubjectCode())
+                    && s.getMainNumber().equals(q.getMainNumber()) && s.getSubNumber().equals(q.getSubNumber())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Transactional
+    @Override
+    public void updateAnswerErr(Long id, String err) {
+        UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+        LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.set(StudentScoreEntity::getAnswerStatus, DataStatus.FAILED);
+        lw.set(StudentScoreEntity::getErrMsg, err);
+        lw.eq(StudentScoreEntity::getId, id);
+        this.update(wrapper);
+    }
+
+    @Override
+    public void createSlice(StudentScoreEntity score, QuestionEntity q, Map<Integer, AnswerImageDto> answerImages) {
+        StudentScoreImageDto dto = new StudentScoreImageDto();
+        dto.setStudentId(score.getStudentId());
+        dto.setStudentScoreId(score.getId());
+        dto.setImage(getSlice(score, q, answerImages));
+        // saveSliceImage(score, dto.getImage());
+        try {
+            queue.put(dto);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private byte[] getSlice(StudentScoreEntity score, QuestionEntity q, Map<Integer, AnswerImageDto> answerImages) {
+        List<byte[]> ret = new ArrayList<>();
+        String suff = null;
+        for (ImageSlice s : q.getImageSlice()) {
+            AnswerImageDto sheet = getSheet(score, q, s.getI(), answerImages);
+            suff = sheet.getSuff();
+            ret.add(ImageUtil.cutImage(sheet.getImage(), sheet.getSuff(), s.getX().intValue(), s.getY().intValue(),
+                    s.getW().intValue(), s.getH().intValue()));
+        }
+        if (ret.size() > 1) {
+            return ImageUtil.joinImages(ret, suff);
+        } else {
+            return ret.get(0);
+        }
+    }
+
+    private AnswerImageDto getSheet(StudentScoreEntity score, QuestionEntity q, Integer pageIndex,
+            Map<Integer, AnswerImageDto> answerImages) {
+        AnswerImageDto ret = answerImages.get(pageIndex);
+        if (ret != null) {
+            return ret;
+        }
+        try {
+            ret = new AnswerImageDto();
+            String url = getImageUrl(score, q, pageIndex);
+            String tem = url.split("\\?")[0];
+            String suff = tem.substring(tem.lastIndexOf(".") + 1).toLowerCase();
+            ret.setImage(ByteArray.fromUrl(url).value());
+            // saveSheetImage(score,pageIndex, ret.getImage());
+            ret.setPageIndex(pageIndex);
+            ret.setSuff(suff);
+            answerImages.put(pageIndex, ret);
+            return ret;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    // private void saveSheetImage(StudentScoreEntity s,Integer page,byte[] bs)
+    // {
+    // File dir=new File(sysProperty.getDataDir()+"/"+"sheet");
+    // if(!dir.exists()) {
+    // dir.mkdir();
+    // }
+    // File image=new
+    // File(dir.getAbsolutePath()+"/"+s.getStudentCode()+"-"+page+".jpg");
+    // if(image.exists()) {
+    // image.delete();
+    // }
+    // FileOutputStream out=null;
+    // try {
+    // out = new FileOutputStream(image);
+    // out.write(bs, 0, bs.length);
+    // out.flush();
+    // } catch (Exception e) {
+    // }finally {
+    // if(out!=null) {
+    // try {
+    // out.close();
+    // } catch (IOException e) {
+    // }
+    // }
+    // }
+    // }
+    // private void saveSliceImage(StudentScoreEntity s,byte[] bs) {
+    // File dir=new File(sysProperty.getDataDir()+"/"+"slice");
+    // if(!dir.exists()) {
+    // dir.mkdir();
+    // }
+    // File image=new
+    // File(dir.getAbsolutePath()+"/"+s.getStudentCode()+"-"+s.getMainNumber()+"-"+s.getSubNumber()+".jpg");
+    // if(image.exists()) {
+    // image.delete();
+    // }
+    // FileOutputStream out=null;
+    // try {
+    // out = new FileOutputStream(image);
+    // out.write(bs, 0, bs.length);
+    // out.flush();
+    // } catch (Exception e) {
+    // }finally {
+    // if(out!=null) {
+    // try {
+    // out.close();
+    // } catch (IOException e) {
+    // }
+    // }
+    // }
+    // }
+
+    private String getImageUrl(StudentScoreEntity score, QuestionEntity q, Integer pageIndex) {
+        if (DataType.MARKING_CLOUD.equals(sysProperty.getDataType())) {
+            return getImageUrlFromMarkingCloud(score, pageIndex);
+        } else if (DataType.TEACH_CLOUD.equals(sysProperty.getDataType())) {
+            return getImageUrlFromTeachCloud(score, q, pageIndex);
+        } else {
+            throw new StatusException("数据类型错误");
+        }
+    }
+
+    private String getImageUrlFromMarkingCloud(StudentScoreEntity score, Integer pageIndex) {
+        return sysProperty.getImageServer() + "/" + getMarkingCloudPath(score.getExamId(),
+                getSuffix(score.getStudentCode()), score.getStudentCode(), pageIndex, "jpg");
+    }
+
+    private static String getSuffix(String input) {
+        return StringUtils.trimToEmpty(input).substring(Math.max(0, input.length() - 3));
+    }
+
+    private String getImageUrlFromTeachCloud(StudentScoreEntity score, QuestionEntity q, Integer pageIndex) {
+        int paperNum = (pageIndex + 1) / 2;
+        int page = 1;
+        if (pageIndex % 2 == 0) {
+            page = 2;
+        }
+        return sysProperty.getImageServer() + "/" + getTeachCloudPath(score.getExamId(), score.getSubjectCode(),
+                score.getStudentCode(), paperNum, page, "jpg");
+    }
+
+    private String getTeachCloudPath(Object... param) {
+        return String.format("sheet/%d/%s/%s/%d-%d.%s", param);
+    }
+
+    private static String getMarkingCloudPath(Object... param) {
+        return String.format("sheet/%d/%s/%s-%d.%s", param);
+    }
+
+    public static void main(String[] args) {
+        // Pattern pattern =
+        // Pattern.compile("\\[\\[([0-9](.[0-9]+){0,1})分\\]\\]");
+        // String d="[[1.1分]]中国共产党是中国特色社会主义事业的坚强领导核心。\n"
+        // + "[[1.2分]]中国共产党的领导地位是在历史奋斗中形成的。\n"
+        // + "[[1.3分]]中国共产党领导是人民当家作主的可靠保障。\n"
+        // + "[[1.4分]]中国共产党领导关系中国特色社会主义的性质、方向和命运。\n"
+        // + "[[2分]]中国共产党领导是实现中华民族伟大复兴的根本保证。";
+        // Matcher matcher = pattern.matcher(d);
+        // int start=0;
+        // double score=0.0;
+        // while (matcher.find()) {
+        // if(start!=0) {
+        // System.out.println(score+d.substring(start,matcher.start()));
+        // }
+        // score=Double.valueOf(matcher.group(1));
+        // start=matcher.end();
+        // }
+        // if(start<d.length()) {
+        // System.out.println(score+d.substring(start,d.length()));
+        // }
+        // String[] items = d.split("\\[\\[[0-9](.[0-9]+){0,1}分\\]\\]");
+        String code = "2021113801";
+        String s = "https://file.markingcloud.com/" + getMarkingCloudPath(1379, getSuffix(code), code, 1, "jpg");
+        System.out.println(s);
+    }
+
+    @Override
+    public StudentScoreImageDto pollStudentScoreImage() {
+        try {
+            return queue.take();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Transactional
+    @Override
+    public void ocr(StudentScoreImageDto dto) {
+        try {
+            OrgInfo org = solarService.getOrgList().get(0);
+            String ret = ocrDispose(dto, org);
+            if (ret != null) {
+                updateAnswer(dto.getStudentScoreId(), ret);
+            } else {
+                ocrErr(dto, "ocr失败,返回null");
+            }
+        } catch (Exception e) {
+            ocrErr(dto, e.getMessage());
+        }
+    }
+
+    private void updateAnswer(Long id, String answer) {
+        UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+        LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.set(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
+        lw.set(StudentScoreEntity::getErrMsg, null);
+        lw.set(StudentScoreEntity::getAnswer, answer);
+        lw.eq(StudentScoreEntity::getId, id);
+        this.update(wrapper);
+    }
+
+    private String ocrDispose(StudentScoreImageDto dto, OrgInfo org) {
+        SignatureInfo signature = SignatureInfo.secret(org.getAccessKey(), org.getAccessSecret());
+        try {
+            return ocrApiClient.forImage(signature, OcrType.HANDWRITING, UploadFile.build("image", "", dto.getImage()));
+        } catch (Exception e) {
+            log.error("ocr异常", e);
+            if (e instanceof RetrofitResponseError) {
+                RetrofitResponseError tem = (RetrofitResponseError) e;
+                if (tem.getCode() == 503) {
+                    if (dto.getRetry() <= 3) {
+                        try {
+                            Thread.sleep(3000);
+                        } catch (InterruptedException e1) {
+                        }
+                        dto.setRetry(dto.getRetry() + 1);
+                        return ocrDispose(dto, org);
+                    } else {
+                        throw new StatusException("重试次数过多");
+                    }
+                } else {
+                    throw e;
+                }
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    private void ocrErr(StudentScoreImageDto dto, String err) {
+        updateAnswerErr(dto.getStudentScoreId(), err);
+        studentService.updateStatus(dto.getStudentId(), DataStatus.FAILED);
+    }
+
+    @Override
+    public List<StudentScoreEntity> findAllToAiMarking() {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.eq(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
+        lw.in(StudentScoreEntity::getScoreStatus, DataStatus.WAITING, DataStatus.FAILED);
+        return this.list(wrapper);
+    }
+
+    @Override
+    public List<StudentScoreEntity> findToAiMarking(Long studentId) {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.eq(StudentScoreEntity::getStudentId, studentId);
+        lw.eq(StudentScoreEntity::getAnswerStatus, DataStatus.SUCCESS);
+        lw.in(StudentScoreEntity::getScoreStatus, DataStatus.WAITING, DataStatus.FAILED);
+        return this.list(wrapper);
+    }
+
+    @Transactional
+    @Override
+    public void aiMarking(StudentScoreEntity score) {
+        AiMarkingDto dto = new AiMarkingDto();
+        dto.setScoreInfo(score);
+        try {
+            OrgInfo org = solarService.getOrgList().get(0);
+            QuestionEntity q = questionService.getById(score.getQuestionId());
+            if (q == null) {
+                throw new StatusException("未找到试题信息");
+            }
+            if (CollectionUtils.isEmpty(q.getAnswer())) {
+                return;
+            }
+            AutoScoreRequest req = new AutoScoreRequest();
+            req.setQuestionBody(q.getContent());
+            req.setStandardAnswer(q.getAnswer());
+            req.setStudentAnswer(score.getAnswer());
+            req.setSubjectName(q.getSubjectName());
+            req.setTotalScore(q.getFullScore());
+            req.setIntervalScore(0.5);
+            AutoScoreResult ret = aiMarkingDispose(dto, org, req);
+            if (ret != null) {
+                List<Double> stepScore = new ArrayList<>();
+                for (Double d : ret.getStepScore()) {
+                    stepScore.add(d);
+                }
+                double total = Arrays.stream(ret.getStepScore()).mapToObj(BigDecimal::valueOf)
+                        .reduce(BigDecimal.ZERO, BigDecimal::add).doubleValue();
+                updateScore(score.getId(), total, stepScore);
+            } else {
+                updateScoreNone(score.getId(), 0.0);
+            }
+            if (allSuccess(score.getStudentId())) {
+                studentService.updateStatus(score.getStudentId(), DataStatus.SUCCESS);
+            }
+        } catch (Exception e) {
+            aiScoreErr(dto, e.getMessage());
+        }
+    }
+
+    private boolean allSuccess(Long studentId) {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.eq(StudentScoreEntity::getStudentId, studentId);
+        List<StudentScoreEntity> list = this.list(wrapper);
+        for (StudentScoreEntity s : list) {
+            if (!DataStatus.SUCCESS.equals(s.getScoreStatus())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private AutoScoreResult aiMarkingDispose(AiMarkingDto dto, OrgInfo org, AutoScoreRequest req) {
+        SignatureInfo signature = SignatureInfo.secret(org.getAccessKey(), org.getAccessSecret());
+        try {
+            return aiService.autoScore(req, signature);
+        } catch (Exception e) {
+            log.error("aiScore异常", e);
+            if (e instanceof RetrofitResponseError) {
+                RetrofitResponseError tem = (RetrofitResponseError) e;
+                if (tem.getCode() == 503) {
+                    if (dto.getRetry() <= 3) {
+                        try {
+                            Thread.sleep(3000);
+                        } catch (InterruptedException e1) {
+                        }
+                        dto.setRetry(dto.getRetry() + 1);
+                        return aiMarkingDispose(dto, org, req);
+                    } else {
+                        throw new StatusException("重试次数过多");
+                    }
+                } else {
+                    throw e;
+                }
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    private void updateScore(Long id, Double aiScore, List<Double> scoreStep) {
+        UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+        LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.set(StudentScoreEntity::getScoreStatus, DataStatus.SUCCESS);
+        lw.set(StudentScoreEntity::getAiScore, aiScore);
+        lw.set(StudentScoreEntity::getStepScore, JSONArray.toJSONString(scoreStep));
+        lw.set(StudentScoreEntity::getErrMsg, null);
+        lw.set(StudentScoreEntity::getScoreNone, false);
+        lw.eq(StudentScoreEntity::getId, id);
+        this.update(wrapper);
+    }
+
+    private void updateScoreNone(Long id, Double aiScore) {
+        UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+        LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.set(StudentScoreEntity::getScoreStatus, DataStatus.SUCCESS);
+        lw.set(StudentScoreEntity::getAiScore, aiScore);
+        lw.set(StudentScoreEntity::getStepScore, null);
+        lw.set(StudentScoreEntity::getErrMsg, null);
+        lw.set(StudentScoreEntity::getScoreNone, true);
+        lw.eq(StudentScoreEntity::getId, id);
+        this.update(wrapper);
+    }
+
+    private void aiScoreErr(AiMarkingDto dto, String err) {
+        updateScoreErr(dto.getScoreInfo().getId(), err);
+        studentService.updateStatus(dto.getScoreInfo().getStudentId(), DataStatus.FAILED);
+    }
+
+    private void updateScoreErr(Long id, String err) {
+        UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+        LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.set(StudentScoreEntity::getScoreStatus, DataStatus.FAILED);
+        lw.set(StudentScoreEntity::getErrMsg, err);
+        lw.eq(StudentScoreEntity::getId, id);
+        this.update(wrapper);
+    }
+
+    private void resetAnswerStatus() {
+        UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+        LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.set(StudentScoreEntity::getAnswerStatus, DataStatus.WAITING);
+        lw.eq(StudentScoreEntity::getAnswerStatus, DataStatus.PROCESSING);
+        this.update(wrapper);
+    }
+
+    private void resetScoreStatus() {
+        UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
+        LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.set(StudentScoreEntity::getScoreStatus, DataStatus.WAITING);
+        lw.eq(StudentScoreEntity::getScoreStatus, DataStatus.PROCESSING);
+        this.update(wrapper);
+    }
+
+    @Override
+    public void resetStatus() {
+        resetAnswerStatus();
+        resetScoreStatus();
+    }
+
+    @Override
+    public int countBy(Long examId, DataStatus status) {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        if (status != null) {
+            lw.eq(StudentScoreEntity::getScoreStatus, status);
+        }
+        lw.eq(StudentScoreEntity::getExamId, examId);
+        return this.count(wrapper);
+    }
+
+    @Transactional
+    @Override
+    public void removeBy(Long examId, String subjectCode) {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        if (subjectCode != null) {
+            lw.eq(StudentScoreEntity::getSubjectCode, subjectCode);
+        }
+        lw.eq(StudentScoreEntity::getExamId, examId);
+        this.remove(wrapper);
+    }
+
+    @Override
+    public int countOcrBy(Long examId, DataStatus status) {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        if (status != null) {
+            lw.eq(StudentScoreEntity::getAnswerStatus, status);
+        }
+        lw.eq(StudentScoreEntity::getExamId, examId);
+        return this.count(wrapper);
+    }
+
+    @Override
+    public List<StudentScoreEntity> findBy(Long examId, String subjectCode, Integer mainNumber, String subNumber,
+            Boolean exZero, Integer count, Integer score) {
+        QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
+        lw.eq(StudentScoreEntity::getExamId, examId);
+        lw.eq(StudentScoreEntity::getSubjectCode, subjectCode);
+        lw.eq(StudentScoreEntity::getMainNumber, mainNumber);
+        lw.eq(StudentScoreEntity::getSubNumber, subNumber);
+        lw.isNotNull(StudentScoreEntity::getAiScore);
+        lw.isNotNull(StudentScoreEntity::getMarkingScore);
+        lw.and(wq -> {
+            wq.or(wq1 -> wq1.isNull(StudentScoreEntity::getScoreNone));
+            wq.or(wq2 -> wq2.eq(StudentScoreEntity::getScoreNone, false));
+        });
+        if (exZero != null && exZero) {
+            lw.and(wq -> {
+                wq.or(wq1 -> wq1.ne(StudentScoreEntity::getAiScore, 0));
+                wq.or(wq2 -> wq2.eq(StudentScoreEntity::getMarkingScore, 0));
+            });
+        }
+        List<StudentScoreEntity> ret = this.list(wrapper);
+        if (CollectionUtils.isEmpty(ret)) {
+            return ret;
+        }
+        if (score != null) {
+            List<StudentScoreEntity> tem = new ArrayList<>();
+            for (StudentScoreEntity s : ret) {
+                if (getSubtract(s.getAiScore(), s.getMarkingScore()) <= score) {
+                    tem.add(s);
+                }
+            }
+            ret = tem;
+        }
+        if (CollectionUtils.isEmpty(ret)) {
+            return ret;
+        }
+        if (count != null) {
+            ret.sort(new Comparator<StudentScoreEntity>() {
+
+                @Override
+                public int compare(StudentScoreEntity o1, StudentScoreEntity o2) {
+                    String c1 = o1.getStudentCode();
+                    String c2 = o2.getStudentCode();
+                    return c1.compareTo(c2);
+                }
+            });
+            if (ret.size() <= count) {
+                return ret;
+            }
+            return ret.subList(0, count);
+        }
+        return ret;
+    }
+
+    private Double getSubtract(Double d1, Double d2) {
+        if (d1 == null || d2 == null) {
+            return null;
+        }
+        Double r = d1 - d2;
+        if (r < 0) {
+            r = 0 - r;
+        }
+        return r;
+    }
 }