xiatian 2 месяцев назад
Родитель
Сommit
f3a3c69d2d

+ 5 - 1
db/am_db.sql

@@ -22,7 +22,8 @@ CREATE TABLE `am_question` (
   `full_score` double NOT NULL,
   `full_score` double NOT NULL,
   `answer` longtext COLLATE utf8mb4_bin DEFAULT NULL,
   `answer` longtext COLLATE utf8mb4_bin DEFAULT NULL,
   `content` longtext COLLATE utf8mb4_bin NOT NULL,
   `content` longtext COLLATE utf8mb4_bin NOT NULL,
-  `image_slice` varchar(1000) COLLATE utf8mb4_bin NOT NULL,
+  `answer_range_type` varchar(1000) COLLATE utf8mb4_bin NOT NULL,
+  `image_slice` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL,
   `prompt_template` varchar(255) NOT NULL,
   `prompt_template` varchar(255) NOT NULL,
   `status` varchar(255) NOT NULL,
   `status` varchar(255) NOT NULL,
   PRIMARY KEY (`id`),
   PRIMARY KEY (`id`),
@@ -36,6 +37,7 @@ CREATE TABLE `am_student_score` (
   `create_time` datetime DEFAULT NULL,
   `create_time` datetime DEFAULT NULL,
   `update_time` datetime DEFAULT NULL,
   `update_time` datetime DEFAULT NULL,
   `question_id` bigint NOT NULL,
   `question_id` bigint NOT NULL,
+   `student_id` bigint NOT NULL,
   `exam_number` varchar(255) COLLATE utf8mb4_bin NOT NULL,
   `exam_number` varchar(255) COLLATE utf8mb4_bin NOT NULL,
   `answer_status` varchar(255) NOT NULL,
   `answer_status` varchar(255) NOT NULL,
   `score_status` varchar(255) NOT NULL,
   `score_status` varchar(255) NOT NULL,
@@ -47,6 +49,8 @@ CREATE TABLE `am_student_score` (
   `step_score` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL,
   `step_score` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL,
   `sheet` varchar(1000) DEFAULT NULL,
   `sheet` varchar(1000) DEFAULT NULL,
   `slice` varchar(500) DEFAULT NULL,
   `slice` varchar(500) DEFAULT NULL,
+  `image_position` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL,
+  `sheet_count` int DEFAULT NULL,
   PRIMARY KEY (`id`),
   PRIMARY KEY (`id`),
   UNIQUE KEY `IDX_STUDENT_SCORE_01` (`question_id`,`exam_number`)
   UNIQUE KEY `IDX_STUDENT_SCORE_01` (`question_id`,`exam_number`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

BIN
file/question-import.xlsx


+ 25 - 0
src/main/java/cn/com/qmth/am/bean/ImagePosition.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.am.bean;
+
+public class ImagePosition {
+
+    private Boolean left;
+
+    private Integer pageIndex;
+
+    public Boolean getLeft() {
+        return left;
+    }
+
+    public void setLeft(Boolean left) {
+        this.left = left;
+    }
+
+    public Integer getPageIndex() {
+        return pageIndex;
+    }
+
+    public void setPageIndex(Integer pageIndex) {
+        this.pageIndex = pageIndex;
+    }
+
+}

+ 31 - 0
src/main/java/cn/com/qmth/am/bean/ImageSize.java

@@ -0,0 +1,31 @@
+package cn.com.qmth.am.bean;
+
+public class ImageSize {
+
+    private int width;
+
+    private int height;
+
+    public ImageSize(int width, int height) {
+        super();
+        this.width = width;
+        this.height = height;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+}

+ 22 - 0
src/main/java/cn/com/qmth/am/bean/StudentScoreVo.java

@@ -1,11 +1,15 @@
 package cn.com.qmth.am.bean;
 package cn.com.qmth.am.bean;
 
 
+import java.util.List;
+
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.DataStatus;
 
 
 public class StudentScoreVo {
 public class StudentScoreVo {
 
 
     private Long id;
     private Long id;
 
 
+    private Long studentId;
+
     private Long examId;
     private Long examId;
 
 
     private String subjectCode;
     private String subjectCode;
@@ -29,6 +33,8 @@ public class StudentScoreVo {
     // 机评返回null
     // 机评返回null
     private Boolean scoreNone;
     private Boolean scoreNone;
 
 
+    private List<ImagePosition> ips;
+
     public Double getAiScore() {
     public Double getAiScore() {
         return aiScore;
         return aiScore;
     }
     }
@@ -109,4 +115,20 @@ public class StudentScoreVo {
         this.subjectCode = subjectCode;
         this.subjectCode = subjectCode;
     }
     }
 
 
+    public List<ImagePosition> getIps() {
+        return ips;
+    }
+
+    public void setIps(List<ImagePosition> ips) {
+        this.ips = ips;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
 }
 }

+ 35 - 0
src/main/java/cn/com/qmth/am/bean/StudentVo.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.am.bean;
+
+public class StudentVo {
+
+    private Long studentId;
+
+    private String examNumber;
+
+    private Integer sheetCount;
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public Integer getSheetCount() {
+        return sheetCount;
+    }
+
+    public void setSheetCount(Integer sheetCount) {
+        this.sheetCount = sheetCount;
+    }
+
+}

+ 25 - 0
src/main/java/cn/com/qmth/am/bean/TrackPosition.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.am.bean;
+
+public class TrackPosition {
+
+    private Double positionX;
+
+    private Integer offsetIndex;
+
+    public Double getPositionX() {
+        return positionX;
+    }
+
+    public void setPositionX(Double positionX) {
+        this.positionX = positionX;
+    }
+
+    public Integer getOffsetIndex() {
+        return offsetIndex;
+    }
+
+    public void setOffsetIndex(Integer offsetIndex) {
+        this.offsetIndex = offsetIndex;
+    }
+
+}

+ 7 - 1
src/main/java/cn/com/qmth/am/dao/stmms/StmmsDao.java

@@ -6,6 +6,9 @@ import org.apache.ibatis.annotations.Param;
 
 
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.dynamic.datasource.annotation.DS;
 
 
+import cn.com.qmth.am.bean.StudentVo;
+import cn.com.qmth.am.bean.TrackPosition;
+
 @DS("data-source-stmms")
 @DS("data-source-stmms")
 public interface StmmsDao {
 public interface StmmsDao {
 
 
@@ -13,7 +16,10 @@ public interface StmmsDao {
             @Param("mainNumber") Integer mainNumber, @Param("subNumber") String subNumber,
             @Param("mainNumber") Integer mainNumber, @Param("subNumber") String subNumber,
             @Param("examNumber") String examNumber);
             @Param("examNumber") String examNumber);
 
 
-    List<String> getUploadStudent(@Param("examId") Long examId, @Param("subjectCode") String subjectCode,
+    List<StudentVo> getUploadStudent(@Param("examId") Long examId, @Param("subjectCode") String subjectCode,
             @Param("limitCount") Integer limitCount);
             @Param("limitCount") Integer limitCount);
 
 
+    List<TrackPosition> getTrackPosition(@Param("studentId") Long studentId,
+            @Param("questionNumber") String questionNumber);
+
 }
 }

+ 12 - 0
src/main/java/cn/com/qmth/am/entity/QuestionEntity.java

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import cn.com.qmth.am.bean.ImageSlice;
 import cn.com.qmth.am.bean.ImageSlice;
 import cn.com.qmth.am.bean.ds.StandardAnswer;
 import cn.com.qmth.am.bean.ds.StandardAnswer;
 import cn.com.qmth.am.entity.base.IdEntity;
 import cn.com.qmth.am.entity.base.IdEntity;
+import cn.com.qmth.am.enums.AnswerRangeType;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.PromptTemplate;
 import cn.com.qmth.am.enums.PromptTemplate;
 import cn.com.qmth.am.handle.ImageSliceListTypeHandler;
 import cn.com.qmth.am.handle.ImageSliceListTypeHandler;
@@ -47,6 +48,9 @@ public class QuestionEntity extends IdEntity {
     @TableField(value = "answer", typeHandler = StandardAnswerListTypeHandler.class)
     @TableField(value = "answer", typeHandler = StandardAnswerListTypeHandler.class)
     private List<StandardAnswer> answer;
     private List<StandardAnswer> answer;
 
 
+    // 答题区域类型
+    private AnswerRangeType answerRangeType;
+
     @TableField(value = "image_slice", typeHandler = ImageSliceListTypeHandler.class)
     @TableField(value = "image_slice", typeHandler = ImageSliceListTypeHandler.class)
     private List<ImageSlice> imageSlice;
     private List<ImageSlice> imageSlice;
 
 
@@ -146,4 +150,12 @@ public class QuestionEntity extends IdEntity {
         this.promptTemplate = promptTemplate;
         this.promptTemplate = promptTemplate;
     }
     }
 
 
+    public AnswerRangeType getAnswerRangeType() {
+        return answerRangeType;
+    }
+
+    public void setAnswerRangeType(AnswerRangeType answerRangeType) {
+        this.answerRangeType = answerRangeType;
+    }
+
 }
 }

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

@@ -7,8 +7,10 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 
 
+import cn.com.qmth.am.bean.ImagePosition;
 import cn.com.qmth.am.entity.base.IdEntity;
 import cn.com.qmth.am.entity.base.IdEntity;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.DataStatus;
+import cn.com.qmth.am.handle.ImagePositionListTypeHandler;
 
 
 @TableName(value = "am_student_score", autoResultMap = true)
 @TableName(value = "am_student_score", autoResultMap = true)
 public class StudentScoreEntity extends IdEntity {
 public class StudentScoreEntity extends IdEntity {
@@ -17,6 +19,9 @@ public class StudentScoreEntity extends IdEntity {
 
 
     private Long questionId;
     private Long questionId;
 
 
+    // 云阅卷学生id
+    private Long studentId;
+
     private String examNumber;
     private String examNumber;
 
 
     // 机评分
     // 机评分
@@ -49,6 +54,11 @@ public class StudentScoreEntity extends IdEntity {
 
 
     private String slice;
     private String slice;
 
 
+    @TableField(value = "image_position", typeHandler = ImagePositionListTypeHandler.class)
+    private List<ImagePosition> imagePosition;
+
+    private Integer sheetCount;
+
     public Double getAiScore() {
     public Double getAiScore() {
         return aiScore;
         return aiScore;
     }
     }
@@ -145,4 +155,28 @@ public class StudentScoreEntity extends IdEntity {
         this.slice = slice;
         this.slice = slice;
     }
     }
 
 
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public List<ImagePosition> getImagePosition() {
+        return imagePosition;
+    }
+
+    public void setImagePosition(List<ImagePosition> imagePosition) {
+        this.imagePosition = imagePosition;
+    }
+
+    public Integer getSheetCount() {
+        return sheetCount;
+    }
+
+    public void setSheetCount(Integer sheetCount) {
+        this.sheetCount = sheetCount;
+    }
+
 }
 }

+ 30 - 0
src/main/java/cn/com/qmth/am/enums/AnswerRangeType.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.am.enums;
+
+/**
+ * 答题区域类型
+ *
+ */
+public enum AnswerRangeType {
+
+    FIXED("固定区域"), TRACK("轨迹查找"), ALL("全卷"),;
+
+    private AnswerRangeType(String name) {
+        this.name = name;
+    }
+
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public static AnswerRangeType getByName(String name) {
+        for (AnswerRangeType r : AnswerRangeType.values()) {
+            if (r.getName().equals(name)) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+}

+ 16 - 0
src/main/java/cn/com/qmth/am/handle/ImagePositionListTypeHandler.java

@@ -0,0 +1,16 @@
+package cn.com.qmth.am.handle;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+import cn.com.qmth.am.bean.ImagePosition;
+
+public class ImagePositionListTypeHandler extends ListTypeHandler<ImagePosition> {
+
+    @Override
+    protected JavaType specificType() {
+        return super.mapper.getTypeFactory().constructParametricType(List.class, ImagePosition.class);
+    }
+
+}

+ 6 - 1
src/main/java/cn/com/qmth/am/service/StmmsService.java

@@ -2,6 +2,9 @@ package cn.com.qmth.am.service;
 
 
 import java.util.List;
 import java.util.List;
 
 
+import cn.com.qmth.am.bean.StudentVo;
+import cn.com.qmth.am.bean.TrackPosition;
+
 /**
 /**
  * 类注释
  * 类注释
  */
  */
@@ -10,5 +13,7 @@ public interface StmmsService {
     List<Double> getMarkStudent(Long examId, String subjectCode, Integer mainNumber, String subNumber,
     List<Double> getMarkStudent(Long examId, String subjectCode, Integer mainNumber, String subNumber,
             String examNumber);
             String examNumber);
 
 
-    List<String> getUploadStudent(Long examId, String subjectCode, Integer limitCount);
+    List<StudentVo> getUploadStudent(Long examId, String subjectCode, Integer limitCount);
+
+    List<TrackPosition> getTrackPosition(Long studentId, String questionNumber);
 }
 }

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

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import cn.com.qmth.am.bean.OcrDto;
 import cn.com.qmth.am.bean.OcrDto;
 import cn.com.qmth.am.bean.StudentScoreImageDto;
 import cn.com.qmth.am.bean.StudentScoreImageDto;
 import cn.com.qmth.am.bean.StudentScoreVo;
 import cn.com.qmth.am.bean.StudentScoreVo;
+import cn.com.qmth.am.bean.StudentVo;
 import cn.com.qmth.am.entity.QuestionEntity;
 import cn.com.qmth.am.entity.QuestionEntity;
 import cn.com.qmth.am.entity.StudentScoreEntity;
 import cn.com.qmth.am.entity.StudentScoreEntity;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.DataStatus;
@@ -38,7 +39,7 @@ public interface StudentScoreService extends IService<StudentScoreEntity> {
 
 
     List<StudentScoreEntity> findAllToOcr();
     List<StudentScoreEntity> findAllToOcr();
 
 
-    void saveByQuestion(List<QuestionEntity> qlist, List<String> students);
+    void saveByQuestion(List<QuestionEntity> qlist, List<StudentVo> students);
 
 
     StudentScoreEntity findUpdateMarkingScore();
     StudentScoreEntity findUpdateMarkingScore();
 
 
@@ -48,4 +49,6 @@ public interface StudentScoreService extends IService<StudentScoreEntity> {
 
 
     List<StudentScoreVo> getAllInfoForUpdateScore();
     List<StudentScoreVo> getAllInfoForUpdateScore();
 
 
+    void updateMarkingScoreAndTrack(StudentScoreVo score);
+
 }
 }

+ 28 - 12
src/main/java/cn/com/qmth/am/service/impl/QuestionServiceImpl.java

@@ -40,6 +40,7 @@ import cn.com.qmth.am.bean.ds.StandardAnswer;
 import cn.com.qmth.am.config.SysProperty;
 import cn.com.qmth.am.config.SysProperty;
 import cn.com.qmth.am.dao.local.QuestionDao;
 import cn.com.qmth.am.dao.local.QuestionDao;
 import cn.com.qmth.am.entity.QuestionEntity;
 import cn.com.qmth.am.entity.QuestionEntity;
+import cn.com.qmth.am.enums.AnswerRangeType;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.ImportFileName;
 import cn.com.qmth.am.enums.ImportFileName;
 import cn.com.qmth.am.enums.PromptTemplate;
 import cn.com.qmth.am.enums.PromptTemplate;
@@ -53,7 +54,7 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionDao, QuestionEntity
     private Pattern scoreRex = Pattern.compile("\\[\\[([0-9][0-9]*(.[0-9]+){0,1})分\\]\\]");
     private Pattern scoreRex = Pattern.compile("\\[\\[([0-9][0-9]*(.[0-9]+){0,1})分\\]\\]");
 
 
     private static final String[] EXCEL_HEADER = new String[] { "考试ID", "科目代码", "科目名称", "题目名称", "大题号", "小题号", "满分",
     private static final String[] EXCEL_HEADER = new String[] { "考试ID", "科目代码", "科目名称", "题目名称", "大题号", "小题号", "满分",
-            "试题内容", "试题答案", "作答坐标", "模版文件" };
+            "试题内容", "试题答案", "作答区域类型", "作答坐标", "模版文件" };
 
 
     @Autowired
     @Autowired
     private SysProperty sysProperty;
     private SysProperty sysProperty;
@@ -276,22 +277,37 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionDao, QuestionEntity
             // }
             // }
             imp.setAnswer(getStandardAnswer(answer));
             imp.setAnswer(getStandardAnswer(answer));
 
 
-            String imageSlice = trimAndNullIfBlank(line.get(EXCEL_HEADER[9]));
-            if (StringUtils.isBlank(imageSlice)) {
-                msg.append("  作答坐标不能为空");
-            } else if (imageSlice.length() > 1000) {
-                msg.append("  作答坐标不能超过1000个字符");
+            String rangeType = trimAndNullIfBlank(line.get(EXCEL_HEADER[9]));
+            AnswerRangeType answerRangeType = null;
+            if (StringUtils.isBlank(rangeType)) {
+                msg.append("  作答区域类型不能为空");
             } else {
             } else {
-                List<ImageSlice> val = getImageSlice(imageSlice);
-                if (val == null) {
-                    msg.append("  作答坐标格式有误");
+                answerRangeType = AnswerRangeType.getByName(rangeType);
+                if (answerRangeType == null) {
+                    msg.append("  作答区域类型有误");
                 } else {
                 } else {
-                    imp.setImageSlice(val);
+                    imp.setAnswerRangeType(answerRangeType);
+                }
+            }
+
+            if (AnswerRangeType.FIXED.equals(answerRangeType)) {
+                String imageSlice = trimAndNullIfBlank(line.get(EXCEL_HEADER[10]));
+                if (StringUtils.isBlank(imageSlice)) {
+                    msg.append("  作答坐标不能为空");
+                } else if (imageSlice.length() > 1000) {
+                    msg.append("  作答坐标不能超过1000个字符");
+                } else {
+                    List<ImageSlice> val = getImageSlice(imageSlice);
+                    if (val == null) {
+                        msg.append("  作答坐标格式有误");
+                    } else {
+                        imp.setImageSlice(val);
+                    }
                 }
                 }
             }
             }
 
 
-            String template = trimAndNullIfBlank(line.get(EXCEL_HEADER[10]));
-            if (StringUtils.isBlank(imageSlice)) {
+            String template = trimAndNullIfBlank(line.get(EXCEL_HEADER[11]));
+            if (StringUtils.isBlank(template)) {
                 msg.append("  模版文件不能为空");
                 msg.append("  模版文件不能为空");
             } else {
             } else {
                 PromptTemplate val = PromptTemplate.getByCode(template);
                 PromptTemplate val = PromptTemplate.getByCode(template);

+ 8 - 1
src/main/java/cn/com/qmth/am/service/impl/StmmsServiceImpl.java

@@ -7,6 +7,8 @@ import org.springframework.stereotype.Service;
 
 
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.dynamic.datasource.annotation.DS;
 
 
+import cn.com.qmth.am.bean.StudentVo;
+import cn.com.qmth.am.bean.TrackPosition;
 import cn.com.qmth.am.dao.stmms.StmmsDao;
 import cn.com.qmth.am.dao.stmms.StmmsDao;
 import cn.com.qmth.am.service.StmmsService;
 import cn.com.qmth.am.service.StmmsService;
 
 
@@ -24,7 +26,12 @@ public class StmmsServiceImpl implements StmmsService {
     }
     }
 
 
     @Override
     @Override
-    public List<String> getUploadStudent(Long examId, String subjectCode, Integer limitCount) {
+    public List<StudentVo> getUploadStudent(Long examId, String subjectCode, Integer limitCount) {
         return stmmsDao.getUploadStudent(examId, subjectCode, limitCount);
         return stmmsDao.getUploadStudent(examId, subjectCode, limitCount);
     }
     }
+
+    @Override
+    public List<TrackPosition> getTrackPosition(Long studentId, String questionNumber) {
+        return stmmsDao.getTrackPosition(studentId, questionNumber);
+    }
 }
 }

+ 109 - 7
src/main/java/cn/com/qmth/am/service/impl/StudentScoreServiceImpl.java

@@ -1,8 +1,11 @@
 package cn.com.qmth.am.service.impl;
 package cn.com.qmth.am.service.impl;
 
 
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Comparator;
@@ -13,6 +16,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
+import javax.imageio.ImageIO;
+
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
@@ -35,17 +40,21 @@ import com.qmth.boot.tools.models.ByteArray;
 import cn.com.qmth.am.bean.AiMarkingDto;
 import cn.com.qmth.am.bean.AiMarkingDto;
 import cn.com.qmth.am.bean.AnswerImageDto;
 import cn.com.qmth.am.bean.AnswerImageDto;
 import cn.com.qmth.am.bean.AutoScoreEnRequest;
 import cn.com.qmth.am.bean.AutoScoreEnRequest;
+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.ImageSlice;
 import cn.com.qmth.am.bean.ModelSpeed;
 import cn.com.qmth.am.bean.ModelSpeed;
 import cn.com.qmth.am.bean.OcrDto;
 import cn.com.qmth.am.bean.OcrDto;
 import cn.com.qmth.am.bean.StudentScoreImageDto;
 import cn.com.qmth.am.bean.StudentScoreImageDto;
 import cn.com.qmth.am.bean.StudentScoreInfo;
 import cn.com.qmth.am.bean.StudentScoreInfo;
 import cn.com.qmth.am.bean.StudentScoreVo;
 import cn.com.qmth.am.bean.StudentScoreVo;
+import cn.com.qmth.am.bean.StudentVo;
 import cn.com.qmth.am.bean.ds.AutoScoreResult;
 import cn.com.qmth.am.bean.ds.AutoScoreResult;
 import cn.com.qmth.am.config.SysProperty;
 import cn.com.qmth.am.config.SysProperty;
 import cn.com.qmth.am.dao.local.StudentScoreDao;
 import cn.com.qmth.am.dao.local.StudentScoreDao;
 import cn.com.qmth.am.entity.QuestionEntity;
 import cn.com.qmth.am.entity.QuestionEntity;
 import cn.com.qmth.am.entity.StudentScoreEntity;
 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.DataStatus;
 import cn.com.qmth.am.service.DsMarkingService;
 import cn.com.qmth.am.service.DsMarkingService;
 import cn.com.qmth.am.service.QuestionService;
 import cn.com.qmth.am.service.QuestionService;
@@ -77,20 +86,20 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
 
 
     @Transactional
     @Transactional
     @Override
     @Override
-    public void saveByQuestion(List<QuestionEntity> qlist, List<String> examNumbers) {
+    public void saveByQuestion(List<QuestionEntity> qlist, List<StudentVo> stuVo) {
         if (CollectionUtils.isEmpty(qlist)) {
         if (CollectionUtils.isEmpty(qlist)) {
             throw new StatusException("试题信息为空");
             throw new StatusException("试题信息为空");
         }
         }
         Set<String> allStudent = getAllStudent();
         Set<String> allStudent = getAllStudent();
 
 
-        BatchSetDataUtil<String> bs = new BatchSetDataUtil<String>() {
+        BatchSetDataUtil<StudentVo> bs = new BatchSetDataUtil<StudentVo>() {
 
 
             @Override
             @Override
-            protected void setData(List<String> dataList) {
+            protected void setData(List<StudentVo> dataList) {
                 List<StudentScoreEntity> adds = new ArrayList<>();
                 List<StudentScoreEntity> adds = new ArrayList<>();
-                for (String examNumber : dataList) {
+                for (StudentVo vo : dataList) {
                     for (QuestionEntity q : qlist) {
                     for (QuestionEntity q : qlist) {
-                        String scorekey = q.getId() + "-" + examNumber;
+                        String scorekey = q.getId() + "-" + vo.getExamNumber();
                         if (!allStudent.contains(scorekey)) {
                         if (!allStudent.contains(scorekey)) {
                             StudentScoreEntity stu = new StudentScoreEntity();
                             StudentScoreEntity stu = new StudentScoreEntity();
                             adds.add(stu);
                             adds.add(stu);
@@ -98,7 +107,11 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
                             stu.setQuestionId(q.getId());
                             stu.setQuestionId(q.getId());
                             stu.setAnswerStatus(DataStatus.WAITING);
                             stu.setAnswerStatus(DataStatus.WAITING);
                             stu.setScoreStatus(DataStatus.WAITING);
                             stu.setScoreStatus(DataStatus.WAITING);
-                            stu.setExamNumber(examNumber);
+                            if (vo.getSheetCount() != null && vo.getSheetCount() != 0) {
+                                stu.setSheetCount(vo.getSheetCount());
+                            }
+                            stu.setExamNumber(vo.getExamNumber());
+                            stu.setStudentId(vo.getStudentId());
                         }
                         }
                     }
                     }
                 }
                 }
@@ -107,7 +120,7 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
                 }
                 }
             }
             }
         };
         };
-        bs.setDataForBatch(examNumbers, 500);
+        bs.setDataForBatch(stuVo, 500);
 
 
     }
     }
 
 
@@ -157,6 +170,16 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
     }
     }
 
 
     private void getSlice(StudentScoreEntity score, QuestionEntity q, StudentScoreImageDto dto) {
     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<>();
         List<byte[]> ret = new ArrayList<>();
         String suff = null;
         String suff = null;
         Map<Integer, AnswerImageDto> answerImages = new LinkedHashMap<>();
         Map<Integer, AnswerImageDto> answerImages = new LinkedHashMap<>();
@@ -177,6 +200,75 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
         }
         }
     }
     }
 
 
+    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 = 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) {
+        List<byte[]> ret = new ArrayList<>();
+        String suff = null;
+        Map<Integer, AnswerImageDto> answerImages = new LinkedHashMap<>();
+        for (int i = 1; i <= score.getSheetCount(); i++) {
+            AnswerImageDto sheet = getSheet(score, q, i, answerImages);
+            suff = sheet.getSuff();
+            ret.add(sheet.getImage());
+        }
+        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 ImageSize getSize(byte[] iamge) {
+        InputStream is = null;
+        try {
+            byte[] bytes = Arrays.copyOf(iamge, iamge.length);
+            is = new ByteArrayInputStream(bytes);
+            BufferedImage image = ImageIO.read(is);
+            int width = image.getWidth();
+            int height = image.getHeight();
+            return new ImageSize(width, height);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
     private AnswerImageDto getSheet(StudentScoreEntity score, QuestionEntity q, Integer pageIndex,
     private AnswerImageDto getSheet(StudentScoreEntity score, QuestionEntity q, Integer pageIndex,
             Map<Integer, AnswerImageDto> answerImages) {
             Map<Integer, AnswerImageDto> answerImages) {
         AnswerImageDto ret = answerImages.get(pageIndex);
         AnswerImageDto ret = answerImages.get(pageIndex);
@@ -676,4 +768,14 @@ public class StudentScoreServiceImpl extends ServiceImpl<StudentScoreDao, Studen
     public List<StudentScoreVo> getAllInfoForUpdateScore() {
     public List<StudentScoreVo> getAllInfoForUpdateScore() {
         return this.baseMapper.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);
+    }
 }
 }

+ 37 - 2
src/main/java/cn/com/qmth/am/service/impl/StudentServiceImpl.java

@@ -1,9 +1,11 @@
 package cn.com.qmth.am.service.impl;
 package cn.com.qmth.am.service.impl;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
@@ -14,10 +16,14 @@ import org.springframework.transaction.annotation.Transactional;
 
 
 import com.qmth.boot.core.exception.StatusException;
 import com.qmth.boot.core.exception.StatusException;
 
 
+import cn.com.qmth.am.bean.ImagePosition;
 import cn.com.qmth.am.bean.OcrDto;
 import cn.com.qmth.am.bean.OcrDto;
 import cn.com.qmth.am.bean.StudentScoreVo;
 import cn.com.qmth.am.bean.StudentScoreVo;
+import cn.com.qmth.am.bean.StudentVo;
+import cn.com.qmth.am.bean.TrackPosition;
 import cn.com.qmth.am.config.SysProperty;
 import cn.com.qmth.am.config.SysProperty;
 import cn.com.qmth.am.entity.QuestionEntity;
 import cn.com.qmth.am.entity.QuestionEntity;
+import cn.com.qmth.am.enums.AnswerRangeType;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.enums.DataStatus;
 import cn.com.qmth.am.service.QuestionService;
 import cn.com.qmth.am.service.QuestionService;
 import cn.com.qmth.am.service.StmmsService;
 import cn.com.qmth.am.service.StmmsService;
@@ -61,7 +67,7 @@ public class StudentServiceImpl implements StudentService {
             tem.add(q);
             tem.add(q);
         }
         }
         for (List<QuestionEntity> qlist : qmap.values()) {
         for (List<QuestionEntity> qlist : qmap.values()) {
-            List<String> students = stmmsService.getUploadStudent(qlist.get(0).getExamId(),
+            List<StudentVo> students = stmmsService.getUploadStudent(qlist.get(0).getExamId(),
                     qlist.get(0).getSubjectCode(), sysProperty.getStudentCount());
                     qlist.get(0).getSubjectCode(), sysProperty.getStudentCount());
             if (CollectionUtils.isEmpty(students)) {
             if (CollectionUtils.isEmpty(students)) {
                 continue;
                 continue;
@@ -87,6 +93,13 @@ public class StudentServiceImpl implements StudentService {
     public void buildImage(OcrDto ocrDto) {
     public void buildImage(OcrDto ocrDto) {
         if (DataStatus.WAITING.equals(ocrDto.getScore().getAnswerStatus())
         if (DataStatus.WAITING.equals(ocrDto.getScore().getAnswerStatus())
                 || DataStatus.FAILED.equals(ocrDto.getScore().getAnswerStatus())) {
                 || DataStatus.FAILED.equals(ocrDto.getScore().getAnswerStatus())) {
+            if (AnswerRangeType.TRACK.equals(ocrDto.getQuetion().getAnswerRangeType())
+                    && CollectionUtils.isEmpty(ocrDto.getScore().getImagePosition())) {
+                return;
+            } else if (AnswerRangeType.ALL.equals(ocrDto.getQuetion().getAnswerRangeType())
+                    && ocrDto.getScore().getSheetCount() == null) {
+                return;
+            }
             studentService.createSlice(ocrDto);
             studentService.createSlice(ocrDto);
         }
         }
     }
     }
@@ -130,6 +143,28 @@ public class StudentServiceImpl implements StudentService {
             return;
             return;
         }
         }
         score.setMarkingScore(list.get(0));
         score.setMarkingScore(list.get(0));
-        studentScoreService.updateMarkingScore(score);
+        if (AnswerRangeType.FIXED.equals(q.getAnswerRangeType())) {
+            studentScoreService.updateMarkingScore(score);
+        } else if (AnswerRangeType.TRACK.equals(q.getAnswerRangeType())) {
+            score.setIps(getImagePosition(score.getStudentId(), q.getMainNumber() + "." + q.getSubNumber()));
+            studentScoreService.updateMarkingScoreAndTrack(score);
+        }
+    }
+
+    private List<ImagePosition> getImagePosition(Long studentId, String questionNumber) {
+        List<ImagePosition> ret = new ArrayList<>();
+        List<TrackPosition> tps = stmmsService.getTrackPosition(studentId, questionNumber);
+        Set<String> set = new HashSet<>();
+        for (TrackPosition tp : tps) {
+            Boolean left = tp.getPositionX() < 0.5;
+            String key = left.toString() + tp.getOffsetIndex();
+            if (!set.contains(key)) {
+                ImagePosition ip = new ImagePosition();
+                ip.setLeft(left);
+                ip.setPageIndex(tp.getOffsetIndex());
+                ret.add(ip);
+            }
+        }
+        return ret;
     }
     }
 }
 }

+ 11 - 2
src/main/resources/mapper/StmmsMapper.xml

@@ -11,11 +11,20 @@
 		and s.is_upload=1 and s.subjective_status!='UNMARK' and ss.main_number=#{mainNumber} and ss.sub_number=#{subNumber}
 		and s.is_upload=1 and s.subjective_status!='UNMARK' and ss.main_number=#{mainNumber} and ss.sub_number=#{subNumber}
 	</select>
 	</select>
 	<select id="getUploadStudent"
 	<select id="getUploadStudent"
-		resultType="string">
-		select s.exam_number
+		resultType="cn.com.qmth.am.bean.StudentVo">
+		select s.exam_number,s.id student_id,s.sheet_count
 		from  eb_exam_student s 
 		from  eb_exam_student s 
 		where
 		where
 		s.exam_id=#{examId} and s.subject_code =#{subjectCode}
 		s.exam_id=#{examId} and s.subject_code =#{subjectCode}
 		and s.is_upload=1 and s.is_absent=0 limit #{limitCount}
 		and s.is_upload=1 and s.is_absent=0 limit #{limitCount}
 	</select>
 	</select>
+	<select id="getTrackPosition"
+		resultType="cn.com.qmth.am.bean.TrackPosition">
+		select t.position_x,t.offset_index
+		from  m_track t 
+		where
+		t.student_id=#{studentId} and t.question_number =#{questionNumber}
+		order by t.offset_index
+	</select>
+	
 </mapper>
 </mapper>

+ 2 - 1
src/main/resources/mapper/StudentScoreMapper.xml

@@ -34,7 +34,8 @@
 		select
 		select
 		f.id,
 		f.id,
 		f.exam_number,
 		f.exam_number,
-		f.question_id
+		f.question_id,
+		f.student_id
 		from am_student_score
 		from am_student_score
 		f
 		f
 		where f.marking_score is null
 		where f.marking_score is null