|
@@ -0,0 +1,745 @@
|
|
|
|
+package cn.com.qmth.am.service.impl;
|
|
|
|
+
|
|
|
|
+import java.io.File;
|
|
|
|
+import java.io.FileOutputStream;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.Arrays;
|
|
|
|
+import java.util.Comparator;
|
|
|
|
+import java.util.HashSet;
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.Set;
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
+
|
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.stereotype.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;
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
|
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
|
|
|
|
+import com.qmth.boot.core.exception.StatusException;
|
|
|
|
+import com.qmth.boot.core.retrofit.exception.RetrofitResponseError;
|
|
|
|
+import com.qmth.boot.tools.models.ByteArray;
|
|
|
|
+
|
|
|
|
+import cn.com.qmth.am.bean.AiMarkingDto;
|
|
|
|
+import cn.com.qmth.am.bean.AnswerImageDto;
|
|
|
|
+import cn.com.qmth.am.bean.ImagePosition;
|
|
|
|
+import cn.com.qmth.am.bean.ImageSize;
|
|
|
|
+import cn.com.qmth.am.bean.ImageSlice;
|
|
|
|
+import cn.com.qmth.am.bean.ModelSpeed;
|
|
|
|
+import cn.com.qmth.am.bean.OcrDto;
|
|
|
|
+import cn.com.qmth.am.bean.StudentScoreImageDto;
|
|
|
|
+import cn.com.qmth.am.bean.StudentScoreInfo;
|
|
|
|
+import cn.com.qmth.am.bean.StudentScoreVo;
|
|
|
|
+import cn.com.qmth.am.bean.StudentVo;
|
|
|
|
+import cn.com.qmth.am.bean.ds.AutoScoreRequest;
|
|
|
|
+import cn.com.qmth.am.bean.ds.AutoScoreResult;
|
|
|
|
+import cn.com.qmth.am.config.SysProperty;
|
|
|
|
+import cn.com.qmth.am.dao.local.StudentScoreDao;
|
|
|
|
+import cn.com.qmth.am.entity.QuestionEntity;
|
|
|
|
+import cn.com.qmth.am.entity.StudentScoreEntity;
|
|
|
|
+import cn.com.qmth.am.enums.AnswerRangeType;
|
|
|
|
+import cn.com.qmth.am.enums.DataStatus;
|
|
|
|
+import cn.com.qmth.am.enums.FileType;
|
|
|
|
+import cn.com.qmth.am.service.DsMarkingService;
|
|
|
|
+import cn.com.qmth.am.service.QuestionService;
|
|
|
|
+import cn.com.qmth.am.service.StudentScoreService;
|
|
|
|
+import cn.com.qmth.am.utils.BatchSetDataUtil;
|
|
|
|
+import cn.com.qmth.am.utils.FileUtil;
|
|
|
|
+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);
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ConcurrentService concurrentService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private SysProperty sysProperty;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private DsMarkingService dsMarkingService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private QuestionService questionService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ModelSpeed modelSpeed;
|
|
|
|
+
|
|
|
|
+ @Transactional
|
|
|
|
+ @Override
|
|
|
|
+ public void saveByQuestion(List<QuestionEntity> qlist, List<StudentVo> stuVo) {
|
|
|
|
+ if (CollectionUtils.isEmpty(qlist)) {
|
|
|
|
+ throw new StatusException("试题信息为空");
|
|
|
|
+ }
|
|
|
|
+ Set<String> allStudent = getAllStudent();
|
|
|
|
+
|
|
|
|
+ BatchSetDataUtil<StudentVo> bs = new BatchSetDataUtil<StudentVo>() {
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ protected void setData(List<StudentVo> dataList) {
|
|
|
|
+ List<StudentScoreEntity> adds = new ArrayList<>();
|
|
|
|
+ for (StudentVo vo : dataList) {
|
|
|
|
+ for (QuestionEntity q : qlist) {
|
|
|
|
+ String scorekey = q.getId() + "-" + vo.getExamNumber();
|
|
|
|
+ if (!allStudent.contains(scorekey)) {
|
|
|
|
+ StudentScoreEntity stu = new StudentScoreEntity();
|
|
|
|
+ adds.add(stu);
|
|
|
|
+ allStudent.add(scorekey);
|
|
|
|
+ stu.setQuestionId(q.getId());
|
|
|
|
+ stu.setAnswerStatus(DataStatus.WAITING);
|
|
|
|
+ stu.setScoreStatus(DataStatus.WAITING);
|
|
|
|
+ if (vo.getSheetCount() != null && vo.getSheetCount() != 0) {
|
|
|
|
+ stu.setSheetCount(vo.getSheetCount());
|
|
|
|
+ }
|
|
|
|
+ stu.setExamNumber(vo.getExamNumber());
|
|
|
|
+ stu.setStudentId(vo.getStudentId());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (CollectionUtils.isNotEmpty(adds)) {
|
|
|
|
+ saveBatch(adds);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ bs.setDataForBatch(stuVo, 500);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Set<String> getAllStudent() {
|
|
|
|
+ Set<String> ret = new HashSet<>();
|
|
|
|
+ List<StudentScoreInfo> list = this.baseMapper.getAllList();
|
|
|
|
+ if (CollectionUtils.isEmpty(list)) {
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ for (StudentScoreInfo s : list) {
|
|
|
|
+ ret.add(s.getQuestionId() + "-" + s.getExamNUmber());
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @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);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void updateMarkingErr(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);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Transactional
|
|
|
|
+ @Override
|
|
|
|
+ public void createSlice(OcrDto ocrDto) {
|
|
|
|
+ StudentScoreImageDto dto = new StudentScoreImageDto();
|
|
|
|
+ dto.setStudentScoreId(ocrDto.getScore().getId());
|
|
|
|
+ dto.setOcrServer(ocrDto.getOcrServer());
|
|
|
|
+ getSlice(ocrDto.getScore(), ocrDto.getQuetion(), dto);
|
|
|
|
+ ocr(dto);
|
|
|
|
+ if (sysProperty.getSaveImage()) {
|
|
|
|
+ saveSliceImage(ocrDto.getQuetion(), ocrDto.getScore(), dto.getImage());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void getSlice(StudentScoreEntity score, QuestionEntity q, StudentScoreImageDto dto) {
|
|
|
|
+ if (AnswerRangeType.FIXED.equals(q.getAnswerRangeType())) {
|
|
|
|
+ getFixedSlice(score, q, dto);
|
|
|
|
+ } else if (AnswerRangeType.TRACK.equals(q.getAnswerRangeType())) {
|
|
|
|
+ getTrackSlice(score, q, dto);
|
|
|
|
+ } else if (AnswerRangeType.ALL.equals(q.getAnswerRangeType())) {
|
|
|
|
+ getAllSheet(score, q, dto);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void getFixedSlice(StudentScoreEntity score, QuestionEntity q, StudentScoreImageDto dto) {
|
|
|
|
+ List<byte[]> ret = new ArrayList<>();
|
|
|
|
+ String suff = null;
|
|
|
|
+ Map<Integer, AnswerImageDto> answerImages = new LinkedHashMap<>();
|
|
|
|
+ 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 (sysProperty.getSaveImage()) {
|
|
|
|
+ saveSheetImage(q, score, answerImages);
|
|
|
|
+ }
|
|
|
|
+ dto.setSuff(suff);
|
|
|
|
+ if (ret.size() > 1) {
|
|
|
|
+ dto.setImage(ImageUtil.joinImages(ret, suff));
|
|
|
|
+ } else {
|
|
|
|
+ dto.setImage(ret.get(0));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void getTrackSlice(StudentScoreEntity score, QuestionEntity q, StudentScoreImageDto dto) {
|
|
|
|
+ List<byte[]> ret = new ArrayList<>();
|
|
|
|
+ String suff = null;
|
|
|
|
+ Map<Integer, AnswerImageDto> answerImages = new LinkedHashMap<>();
|
|
|
|
+ for (ImagePosition s : score.getImagePosition()) {
|
|
|
|
+ AnswerImageDto sheet = getSheet(score, q, s.getPageIndex(), answerImages);
|
|
|
|
+ ImageSize is = ImageUtil.getSize(sheet.getImage());
|
|
|
|
+ suff = sheet.getSuff();
|
|
|
|
+ int x = 0;
|
|
|
|
+ int y = 0;
|
|
|
|
+ int w = is.getWidth() / 2;
|
|
|
|
+ int h = is.getHeight();
|
|
|
|
+ if (!s.getLeft()) {
|
|
|
|
+ x = w;
|
|
|
|
+ }
|
|
|
|
+ ret.add(ImageUtil.cutImage(sheet.getImage(), sheet.getSuff(), x, y, w, h));
|
|
|
|
+ }
|
|
|
|
+ if (sysProperty.getSaveImage()) {
|
|
|
|
+ saveSheetImage(q, score, answerImages);
|
|
|
|
+ }
|
|
|
|
+ dto.setSuff(suff);
|
|
|
|
+ if (ret.size() > 1) {
|
|
|
|
+ dto.setImage(ImageUtil.joinImages(ret, suff));
|
|
|
|
+ } else {
|
|
|
|
+ dto.setImage(ret.get(0));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void getAllSheet(StudentScoreEntity score, QuestionEntity q, StudentScoreImageDto dto) {
|
|
|
|
+ String suff = null;
|
|
|
|
+ Map<Integer, AnswerImageDto> answerImages = new LinkedHashMap<>();
|
|
|
|
+ AnswerImageDto sheet = getSheet(score, q, 1, answerImages);
|
|
|
|
+ suff = sheet.getSuff();
|
|
|
|
+ if (sysProperty.getSaveImage()) {
|
|
|
|
+ saveSheetImage(q, score, answerImages);
|
|
|
|
+ }
|
|
|
|
+ dto.setSuff(suff);
|
|
|
|
+ dto.setImage(sheet.getImage());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private AnswerImageDto getSheet(StudentScoreEntity score, QuestionEntity q, Integer pageIndex,
|
|
|
|
+ Map<Integer, AnswerImageDto> answerImages) {
|
|
|
|
+ AnswerImageDto ret = answerImages.get(pageIndex);
|
|
|
|
+ if (ret != null) {
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ ret = new AnswerImageDto();
|
|
|
|
+ String url = getImageUrl(score, q);
|
|
|
|
+ try {
|
|
|
|
+ String tem = url.split("\\?")[0];
|
|
|
|
+ String suff = tem.substring(tem.lastIndexOf(".") + 1).toLowerCase();
|
|
|
|
+ ret.setImage(ByteArray.fromUrl(url).value());
|
|
|
|
+ ret.setPageIndex(pageIndex);
|
|
|
|
+ ret.setSuff(suff);
|
|
|
|
+ answerImages.put(pageIndex, ret);
|
|
|
|
+ return ret;
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ throw new RuntimeException(e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void saveSheetImage(QuestionEntity q, StudentScoreEntity s, Map<Integer, AnswerImageDto> answerImages) {
|
|
|
|
+ List<String> ss = new ArrayList<>();
|
|
|
|
+ for (AnswerImageDto ims : answerImages.values()) {
|
|
|
|
+ String path = "sheet/" + q.getExamId() + "/" + q.getSubjectCode() + "/" + q.getMainNumber() + "/"
|
|
|
|
+ + q.getSubNumber() + "/" + s.getExamNumber() + "-" + ims.getPageIndex() + ".jpg";
|
|
|
|
+ ss.add(path);
|
|
|
|
+ File image = new File(sysProperty.getDataDir() + "/" + path);
|
|
|
|
+ if (image.exists()) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ boolean lock = concurrentService.getReadWriteLock(path).writeLock().tryLock();
|
|
|
|
+ if (lock) {
|
|
|
|
+ FileOutputStream out = null;
|
|
|
|
+ try {
|
|
|
|
+ if (image.exists()) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ if (!image.getParentFile().exists()) {
|
|
|
|
+ image.getParentFile().mkdirs();
|
|
|
|
+ }
|
|
|
|
+ byte[] bs = Arrays.copyOf(ims.getImage(), ims.getImage().length);
|
|
|
|
+ out = new FileOutputStream(image);
|
|
|
|
+ out.write(bs, 0, bs.length);
|
|
|
|
+ out.flush();
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ throw new RuntimeException(e);
|
|
|
|
+ } finally {
|
|
|
|
+ concurrentService.getReadWriteLock(path).writeLock().unlock();
|
|
|
|
+ if (out != null) {
|
|
|
|
+ try {
|
|
|
|
+ out.close();
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ this.baseMapper.saveSheetPath(s.getId(), JSONArray.toJSONString(ss));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void saveSliceImage(QuestionEntity q, StudentScoreEntity s, byte[] imageByte) {
|
|
|
|
+ String path = "slice/" + q.getExamId() + "/" + q.getSubjectCode() + "/" + q.getMainNumber() + "/"
|
|
|
|
+ + q.getSubNumber() + "/" + s.getExamNumber() + ".jpg";
|
|
|
|
+ updateSlice(s.getId(), path);
|
|
|
|
+ File image = new File(sysProperty.getDataDir() + "/" + path);
|
|
|
|
+ if (image.exists()) {
|
|
|
|
+ image.delete();
|
|
|
|
+ } else {
|
|
|
|
+ if (!image.getParentFile().exists()) {
|
|
|
|
+ image.getParentFile().mkdirs();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ FileOutputStream out = null;
|
|
|
|
+ try {
|
|
|
|
+ byte[] bs = Arrays.copyOf(imageByte, imageByte.length);
|
|
|
|
+ out = new FileOutputStream(image);
|
|
|
|
+ out.write(bs, 0, bs.length);
|
|
|
|
+ out.flush();
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ throw new RuntimeException(e);
|
|
|
|
+ } finally {
|
|
|
|
+ if (out != null) {
|
|
|
|
+ try {
|
|
|
|
+ out.close();
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void updateSlice(Long id, String path) {
|
|
|
|
+ UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
|
|
|
|
+ LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ lw.set(StudentScoreEntity::getSlice, path);
|
|
|
|
+ lw.eq(StudentScoreEntity::getId, id);
|
|
|
|
+ this.update(wrapper);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String getImageUrl(StudentScoreEntity score, QuestionEntity q) {
|
|
|
|
+ String examNumber = score.getExamNumber();
|
|
|
|
+ return sysProperty.getImageServer() + "/"
|
|
|
|
+ + FileType.SLICE.getPath(q.getExamId(), q.getSubjectCode(), examNumber.substring(0, 5),
|
|
|
|
+ examNumber.substring(5, 6), examNumber.substring(10, 13), q.getMainNumber(), examNumber,
|
|
|
|
+ q.getMainNumber(), "jpg");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static String getSuffix(String input) {
|
|
|
|
+ return StringUtils.trimToEmpty(input).substring(Math.max(0, input.length() - 3));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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 = "2019301151012";
|
|
|
|
+ String s = "https://file.markingcloud.com/" + getMarkingCloudPath(725, getSuffix(code), code, 2, "jpg");
|
|
|
|
+ System.out.println(s);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // @Override
|
|
|
|
+ // public StudentScoreImageDto pollStudentScoreImage() {
|
|
|
|
+ // try {
|
|
|
|
+ // return queue.take();
|
|
|
|
+ // } catch (InterruptedException e) {
|
|
|
|
+ // throw new RuntimeException(e);
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void ocr(StudentScoreImageDto dto) {
|
|
|
|
+ try {
|
|
|
|
+ String ret = ocrDispose(dto);
|
|
|
|
+ if (ret != null) {
|
|
|
|
+ updateAnswer(dto.getStudentScoreId(), ret);
|
|
|
|
+ modelSpeed.addOcrCOunt();
|
|
|
|
+ } 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) {
|
|
|
|
+ try {
|
|
|
|
+ String base64 = FileUtil.byteToBase64(dto.getImage(), dto.getSuff());
|
|
|
|
+ String ret = dsMarkingService.ocr(dto.getOcrServer(), base64);
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ } 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);
|
|
|
|
+ } else {
|
|
|
|
+ throw new StatusException("重试次数过多");
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ throw e;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ throw e;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void ocrErr(StudentScoreImageDto dto, String err) {
|
|
|
|
+ updateAnswerErr(dto.getStudentScoreId(), err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<StudentScoreEntity> findAllToOcr() {
|
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ lw.in(StudentScoreEntity::getAnswerStatus, DataStatus.WAITING, DataStatus.FAILED);
|
|
|
|
+ return this.list(wrapper);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @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);
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void aiMarking(StudentScoreEntity score) {
|
|
|
|
+ AiMarkingDto dto = new AiMarkingDto();
|
|
|
|
+ dto.setScoreInfo(score);
|
|
|
|
+ try {
|
|
|
|
+ QuestionEntity q = questionService.getById(score.getQuestionId());
|
|
|
|
+ if (q == null) {
|
|
|
|
+ throw new StatusException("未找到试题信息");
|
|
|
|
+ }
|
|
|
|
+ dto.setQuestion(q);
|
|
|
|
+ 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.1);
|
|
|
|
+ req.setQuestionTitle(q.getTitle());
|
|
|
|
+ req.setExt(q.getExt());
|
|
|
|
+ req.setScoreGrades(q.getScoreGrades());
|
|
|
|
+ AutoScoreResult ret = aiMarkingDispose(dto, req);
|
|
|
|
+ if (ret != null) {
|
|
|
|
+ List<Double> stepScore = new ArrayList<>();
|
|
|
|
+ for (Double d : ret.getStepScore()) {
|
|
|
|
+ stepScore.add(d);
|
|
|
|
+ }
|
|
|
|
+ updateScore(score.getId(), ret.getTotalScore(), stepScore);
|
|
|
|
+ modelSpeed.addMarkingCount();
|
|
|
|
+ } else {
|
|
|
|
+ updateMarkingErr(score.getId(), "making失败,返回null");
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ aiScoreErr(dto, e.getMessage());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private AutoScoreResult aiMarkingDispose(AiMarkingDto dto, AutoScoreRequest req) {
|
|
|
|
+ try {
|
|
|
|
+ return dsMarkingService.autoScore(req, dto.getQuestion());
|
|
|
|
+ } 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, 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);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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) {
|
|
|
|
+ List<QuestionEntity> qs = questionService.findByExamIdAndSubject(examId, null);
|
|
|
|
+ if (CollectionUtils.isEmpty(qs)) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ List<Long> qids = qs.stream().map(e -> e.getId()).collect(Collectors.toList());
|
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ if (status != null) {
|
|
|
|
+ lw.eq(StudentScoreEntity::getScoreStatus, status);
|
|
|
|
+ }
|
|
|
|
+ lw.in(StudentScoreEntity::getQuestionId, qids);
|
|
|
|
+ return this.count(wrapper);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Transactional
|
|
|
|
+ @Override
|
|
|
|
+ public void removeBy(Long examId, String subjectCode) {
|
|
|
|
+ List<QuestionEntity> qs = questionService.findByExamIdAndSubject(examId, subjectCode);
|
|
|
|
+ if (CollectionUtils.isEmpty(qs)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ List<Long> qids = qs.stream().map(e -> e.getId()).collect(Collectors.toList());
|
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ lw.in(StudentScoreEntity::getQuestionId, qids);
|
|
|
|
+ this.remove(wrapper);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int countOcrBy(Long examId, DataStatus status) {
|
|
|
|
+ List<QuestionEntity> qs = questionService.findByExamIdAndSubject(examId, null);
|
|
|
|
+ if (CollectionUtils.isEmpty(qs)) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ List<Long> qids = qs.stream().map(e -> e.getId()).collect(Collectors.toList());
|
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ if (status != null) {
|
|
|
|
+ lw.eq(StudentScoreEntity::getAnswerStatus, status);
|
|
|
|
+ }
|
|
|
|
+ lw.in(StudentScoreEntity::getQuestionId, qids);
|
|
|
|
+ return this.count(wrapper);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<StudentScoreEntity> findBy(Long questionId, Boolean exZero, Integer count, Integer score) {
|
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ lw.eq(StudentScoreEntity::getQuestionId, questionId);
|
|
|
|
+ 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.getExamNumber();
|
|
|
|
+ String c2 = o2.getExamNumber();
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public StudentScoreEntity findUpdateMarkingScore() {
|
|
|
|
+ QueryWrapper<StudentScoreEntity> wrapper = new QueryWrapper<>();
|
|
|
|
+ LambdaQueryWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ lw.isNull(StudentScoreEntity::getMarkingScore);
|
|
|
|
+ wrapper.last("LIMIT 1");
|
|
|
|
+ return this.getOne(wrapper);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Transactional
|
|
|
|
+ @Override
|
|
|
|
+ public void updateMarkingScore(StudentScoreVo score) {
|
|
|
|
+ UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
|
|
|
|
+ LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ lw.set(StudentScoreEntity::getMarkingScore, score.getMarkingScore());
|
|
|
|
+ lw.eq(StudentScoreEntity::getId, score.getId());
|
|
|
|
+ this.update(wrapper);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<StudentScoreVo> getInfoByExam(Long examId) {
|
|
|
|
+ return this.baseMapper.getInfoByExam(examId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<StudentScoreVo> getAllInfoForUpdateScore() {
|
|
|
|
+ return this.baseMapper.getAllInfoForUpdateScore();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void updateMarkingScoreAndTrack(StudentScoreVo score) {
|
|
|
|
+ UpdateWrapper<StudentScoreEntity> wrapper = new UpdateWrapper<>();
|
|
|
|
+ LambdaUpdateWrapper<StudentScoreEntity> lw = wrapper.lambda();
|
|
|
|
+ lw.set(StudentScoreEntity::getMarkingScore, score.getMarkingScore());
|
|
|
|
+ lw.set(StudentScoreEntity::getImagePosition, JSONArray.toJSONString(score.getIps()));
|
|
|
|
+ lw.eq(StudentScoreEntity::getId, score.getId());
|
|
|
|
+ this.update(wrapper);
|
|
|
|
+ }
|
|
|
|
+}
|