xiatian 4 жил өмнө
parent
commit
ef012c43aa
20 өөрчлөгдсөн 422 нэмэгдсэн , 72 устгасан
  1. 28 15
      themis-backend/src/main/java/com/qmth/themis/backend/api/TEOpenController.java
  2. 13 0
      themis-business/src/main/java/com/qmth/themis/business/bean/backend/OpenRecordAnswerBean.java
  3. 34 8
      themis-business/src/main/java/com/qmth/themis/business/bean/backend/OpenRecordNeedMarkBean.java
  4. 24 2
      themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamPaperCacheBean.java
  5. 3 0
      themis-business/src/main/java/com/qmth/themis/business/dao/TEExamStudentMapper.java
  6. 1 2
      themis-business/src/main/java/com/qmth/themis/business/dao/TOeExamRecordMapper.java
  7. 31 1
      themis-business/src/main/java/com/qmth/themis/business/entity/TEExamPaper.java
  8. 9 0
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java
  9. 2 0
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamStudentService.java
  10. 5 4
      themis-business/src/main/java/com/qmth/themis/business/service/TEOpenService.java
  11. 1 2
      themis-business/src/main/java/com/qmth/themis/business/service/TOeExamRecordService.java
  12. 71 5
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java
  13. 5 0
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamStudentServiceImpl.java
  14. 140 19
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEOpenServiceImpl.java
  15. 2 3
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java
  16. 29 1
      themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java
  17. 2 0
      themis-business/src/main/resources/db/init.sql
  18. 10 0
      themis-business/src/main/resources/mapper/TEExamStudentMapper.xml
  19. 3 2
      themis-business/src/main/resources/mapper/TOeExamAnswerMapper.xml
  20. 9 8
      themis-business/src/main/resources/mapper/TOeExamRecordMapper.xml

+ 28 - 15
themis-backend/src/main/java/com/qmth/themis/backend/api/TEOpenController.java

@@ -41,11 +41,18 @@ public class TEOpenController {
     @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
     public Result examQueryPage(@ApiParam(value = "考试id", required = false) @RequestParam(required = false) Long examId,
                                      @ApiParam(value = "考试code", required = false) @RequestParam(required = false) String code,
-                                     @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber,
-                                     @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
-    	if(pageSize>20) {
+                                     @ApiParam(value = "分页页码", required = false) @RequestParam(required = false) Integer pageNumber,
+                                     @ApiParam(value = "分页数", required = false) @RequestParam(required = false) Integer pageSize) {
+    	
+    	if(pageSize!=null&&pageSize>20) {
     		throw new BusinessException("每页最大条数为20");
     	}
+    	if(pageNumber==null) {
+    		pageNumber=1;
+    	}
+    	if(pageSize==null) {
+    		pageSize=20;
+    	}
         return ResultUtil.ok(openService.examQueryPage(examId, code, pageNumber, pageSize));
     }
     
@@ -55,11 +62,17 @@ public class TEOpenController {
     public Result examCourseQueryPage(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                      @ApiParam(value = "课程code", required = false) @RequestParam(required = false) String courseCode,
                                      @ApiParam(value = "是否有试卷", required = false) @RequestParam(required = false) Boolean hasPaper,
-                                     @ApiParam(value = "分页页码", required = true) @RequestParam int pageNumber,
-                                     @ApiParam(value = "分页数", required = true) @RequestParam int pageSize) {
-    	if(pageSize>100) {
+                                     @ApiParam(value = "分页页码", required = false) @RequestParam(required = false) Integer pageNumber,
+                                     @ApiParam(value = "分页数", required = false) @RequestParam(required = false) Integer pageSize) {
+    	if(pageSize!=null&&pageSize>100) {
     		throw new BusinessException("每页最大条数为100");
     	}
+    	if(pageNumber==null) {
+    		pageNumber=1;
+    	}
+    	if(pageSize==null) {
+    		pageSize=100;
+    	}
     	if(examId==null) {
     		throw new BusinessException("考试id不能为空");
     	}
@@ -82,21 +95,21 @@ public class TEOpenController {
     @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
     public Result examRecordNeedMark(@ApiParam(value = "考试id", required = true) @RequestParam Long examId,
                                      @ApiParam(value = "内容过滤卷", required = false) @RequestParam(required = false) String courseCode,
-                                     @ApiParam(value = "考试记录ID大于此参数", required = true) @RequestParam Integer idGt,
-                                     @ApiParam(value = "数量(最大20)", required = true) @RequestParam Integer count) {
+                                     @ApiParam(value = "考生ID大于此参数", required = false) @RequestParam(required = false) Long examStudentIdGt,
+                                     @ApiParam(value = "数量(最大20)", required = false) @RequestParam(required = false) Integer count) {
     	if(examId==null) {
     		throw new BusinessException("考试id不能为空");
     	}
-    	if(idGt==null) {
-    		throw new BusinessException("idGt不能为空");
+    	if(examStudentIdGt==null) {
+    		examStudentIdGt=0L;
     	}
-    	if(count==null) {
-    		throw new BusinessException("count不能为空");
-    	}
-    	if(count>20) {
+    	if(count!=null&&count>20) {
     		throw new BusinessException("count最大为20");
     	}
-        return ResultUtil.ok(openService.examRecordNeedMark(examId, courseCode,idGt,count));
+    	if(count==null) {
+    		count=20;
+    	}
+        return ResultUtil.ok(openService.examRecordNeedMark(examId, courseCode,examStudentIdGt,count));
     }
 
 }

+ 13 - 0
themis-business/src/main/java/com/qmth/themis/business/bean/backend/OpenRecordAnswerBean.java

@@ -9,6 +9,9 @@ public class OpenRecordAnswerBean {
 	@ApiModelProperty("考试记录id")
 	private Long recordId;
 	
+	@ApiModelProperty("试卷id")
+	private Long paperId;
+	
 	@ApiModelProperty("大题号")
 	private Integer mainNumber;
 
@@ -60,4 +63,14 @@ public class OpenRecordAnswerBean {
 	public void setAnswer(String answer) {
 		this.answer = answer;
 	}
+
+	public Long getPaperId() {
+		return paperId;
+	}
+
+	public void setPaperId(Long paperId) {
+		this.paperId = paperId;
+	}
+	
+	
 }

+ 34 - 8
themis-business/src/main/java/com/qmth/themis/business/bean/backend/OpenRecordNeedMarkBean.java

@@ -8,8 +8,12 @@ import io.swagger.annotations.ApiModelProperty;
 @ApiModel("开放接口-待评卷考试记录")
 public class OpenRecordNeedMarkBean {
 
+	
+	@ApiModelProperty(name = "考生id")
+    private Long examStudentId;
+	
 	@ApiModelProperty(name = "考试记录id")
-    private Long id;
+    private Long examRecordId;
 
 	@ApiModelProperty(value = "证件号")
     private String identity;
@@ -29,16 +33,13 @@ public class OpenRecordNeedMarkBean {
     @ApiModelProperty(value = "客观得分")
     private Double objectiveScore;
     
+    @ApiModelProperty(value = "交卷时间")
+    private Long finishTime;
+    
+    
     @ApiModelProperty(value = "作答结果")
     private List<OpenRecordAnswerBean> answers;
 
-	public Long getId() {
-		return id;
-	}
-
-	public void setId(Long id) {
-		this.id = id;
-	}
 
 	public String getIdentity() {
 		return identity;
@@ -95,5 +96,30 @@ public class OpenRecordNeedMarkBean {
 	public void setAnswers(List<OpenRecordAnswerBean> answers) {
 		this.answers = answers;
 	}
+
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+
+	public Long getExamRecordId() {
+		return examRecordId;
+	}
+
+	public void setExamRecordId(Long examRecordId) {
+		this.examRecordId = examRecordId;
+	}
+
+	public Long getFinishTime() {
+		return finishTime;
+	}
+
+	public void setFinishTime(Long finishTime) {
+		this.finishTime = finishTime;
+	}
     
+	
 }

+ 24 - 2
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamPaperCacheBean.java

@@ -1,11 +1,11 @@
 package com.qmth.themis.business.cache.bean;
 
+import java.io.Serializable;
+
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.qmth.themis.business.enums.EncryptModeEnum;
 
-import java.io.Serializable;
-
 public class ExamPaperCacheBean implements Serializable {
 
 	/**
@@ -30,6 +30,12 @@ public class ExamPaperCacheBean implements Serializable {
 
 	// 试卷总分
 	private Double totalScore;
+	
+    //试卷客观题总分
+    private Double totalObjectiveScore;
+    
+    //试卷主观题总分
+    private Double totalSubjectiveScore;
 
 	// 题干路径
 	private String paperPath;
@@ -202,6 +208,22 @@ public class ExamPaperCacheBean implements Serializable {
 		this.paperViewPath = paperViewPath;
 	}
 
+	public Double getTotalObjectiveScore() {
+		return totalObjectiveScore;
+	}
+
+	public void setTotalObjectiveScore(Double totalObjectiveScore) {
+		this.totalObjectiveScore = totalObjectiveScore;
+	}
+
+	public Double getTotalSubjectiveScore() {
+		return totalSubjectiveScore;
+	}
+
+	public void setTotalSubjectiveScore(Double totalSubjectiveScore) {
+		this.totalSubjectiveScore = totalSubjectiveScore;
+	}
+
 	
 
 }

+ 3 - 0
themis-business/src/main/java/com/qmth/themis/business/dao/TEExamStudentMapper.java

@@ -106,4 +106,7 @@ public interface TEExamStudentMapper extends BaseMapper<TEExamStudent> {
      * @return
      */
     public List<MarkResultSimpleExportDto> markResultQueryExport(@Param("examId") Long examId, @Param("activityId") Long activityId, @Param("identity") String identity, @Param("name") String name, @Param("courseCode") String courseCode);
+
+	public List<Long> findExamStudentIdsNeedMark(@Param("examId") Long examId, @Param("courseCode") String courseCode,
+            @Param("idGt") Long idGt, @Param("count") Integer count);
 }

+ 1 - 2
themis-business/src/main/java/com/qmth/themis/business/dao/TOeExamRecordMapper.java

@@ -348,6 +348,5 @@ public interface TOeExamRecordMapper extends BaseMapper<TOeExamRecord> {
 
     public void updateHasAnswerFile(@Param("recordId") Long recordId, @Param("hasAnswerFile") Integer hasAnswerFile);
 
-    public List<OpenRecordNeedMarkBean> findExamRecordNeedMark(@Param("examId") Long examId, @Param("courseCode") String courseCode,
-                                                               @Param("idGt") Integer idGt, @Param("count") Integer count);
+    public List<OpenRecordNeedMarkBean> findExamRecordNeedMark(@Param("examId") Long examId,@Param("examStudentIds") List<Long> examStudentIds);
 }

+ 31 - 1
themis-business/src/main/java/com/qmth/themis/business/entity/TEExamPaper.java

@@ -18,13 +18,26 @@ import io.swagger.annotations.ApiModelProperty;
 @ApiModel(value = "t_e_exam_paper", description = "考试试卷")
 public class TEExamPaper extends BaseEntity {
 
-    @ApiModelProperty(value = "试卷名称")
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = -3182356952124087406L;
+
+	@ApiModelProperty(value = "试卷名称")
     @TableField(value = "name")
     private String name;
 
     @ApiModelProperty(value = "试卷总分")
     @TableField(value = "total_score")
     private Double totalScore;
+    
+    @ApiModelProperty(value = "试卷客观题总分")
+    @TableField(value = "total_objective_score")
+    private Double totalObjectiveScore;
+    
+    @ApiModelProperty(value = "试卷主观题总分")
+    @TableField(value = "total_subjective_score")
+    private Double totalSubjectiveScore;
 
     @ApiModelProperty(value = "题干路径")
     @TableField(value = "paper_path")
@@ -210,6 +223,23 @@ public class TEExamPaper extends BaseEntity {
 	public void setPaperViewPath(String paperViewPath) {
 		this.paperViewPath = paperViewPath;
 	}
+
+	public Double getTotalObjectiveScore() {
+		return totalObjectiveScore;
+	}
+
+	public void setTotalObjectiveScore(Double totalObjectiveScore) {
+		this.totalObjectiveScore = totalObjectiveScore;
+	}
+
+	public Double getTotalSubjectiveScore() {
+		return totalSubjectiveScore;
+	}
+
+	public void setTotalSubjectiveScore(Double totalSubjectiveScore) {
+		this.totalSubjectiveScore = totalSubjectiveScore;
+	}
     
+	
     
 }

+ 9 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java

@@ -49,4 +49,13 @@ public interface TEExamPaperService extends IService<TEExamPaper> {
 	 */
 	void deleteObjectiveAnswerCacheBean(Long paperId);
 
+	/**
+	 * 删除试卷结构缓存
+	 *
+	 * @param paperId
+	 */
+	void deletePaperStructCacheBean(Long paperId);
+
+	Map<String, Integer> getPaperStructCacheBean(Long paperId);
+
 }

+ 2 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TEExamStudentService.java

@@ -91,4 +91,6 @@ public interface TEExamStudentService extends IService<TEExamStudent> {
      * @return
      */
     public List<MarkResultSimpleExportDto> markResultQueryExport(Long examId, Long activityId, String identity, String name, String courseCode);
+
+	public List<Long> findExamStudentIdsNeedMark(Long examId, String courseCode, Long idGt, Integer count);
 }

+ 5 - 4
themis-business/src/main/java/com/qmth/themis/business/service/TEOpenService.java

@@ -1,19 +1,20 @@
 package com.qmth.themis.business.service;
 
+import java.util.List;
+
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.qmth.themis.business.bean.backend.OpenExamBean;
 import com.qmth.themis.business.bean.backend.OpenExamCourseBean;
 
 public interface TEOpenService {
 
-	public IPage<OpenExamBean> examQueryPage(Long examId, String examCode, int pageNumber, int pageSize);
+	public List<OpenExamBean> examQueryPage(Long examId, String examCode, int pageNumber, int pageSize);
 
-	public IPage<OpenExamCourseBean> examCourseQueryPage(Long examId, String courseCode, Boolean hasPaper, int pageNumber, int pageSize);
+	public List<OpenExamCourseBean> examCourseQueryPage(Long examId, String courseCode, Boolean hasPaper, int pageNumber, int pageSize);
 
 	public JSONObject examPaperDetail(Long paperId, String filter);
 
-	public JSONArray examRecordNeedMark(Long examId, String courseCode, Integer idGt, Integer count);
+	public JSONArray examRecordNeedMark(Long examId, String courseCode, Long idGt, Integer count);
 
 }

+ 1 - 2
themis-business/src/main/java/com/qmth/themis/business/service/TOeExamRecordService.java

@@ -386,6 +386,5 @@ public interface TOeExamRecordService extends IService<TOeExamRecord> {
 
     TOeExamRecord findOneByExamId(Long examId, ExamRecordStatusEnum status);
 
-    public List<OpenRecordNeedMarkBean> findExamRecordNeedMark(Long examId, String courseCode, Integer idGt,
-                                                               Integer count);
+    public List<OpenRecordNeedMarkBean> findExamRecordNeedMark(Long examId,  List<Long> examStudentIds);
 }

+ 71 - 5
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java

@@ -18,6 +18,7 @@ import org.springframework.transaction.annotation.Transactional;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
 import com.qmth.themis.business.cache.bean.ObjectiveAnswerCacheBean;
 import com.qmth.themis.business.config.SystemConfig;
@@ -39,7 +40,6 @@ import com.qmth.themis.common.util.FileUtil;
  */
 @Service
 public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExamPaper> implements TEExamPaperService {
-    private static String underLine = "_";
     @Resource
     SystemConfig systemConfig;
     @Resource
@@ -107,6 +107,8 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
         ret.setCode(ep.getCode());
         ret.setName(ep.getName());
         ret.setTotalScore(ep.getTotalScore());
+        ret.setTotalObjectiveScore(ep.getTotalObjectiveScore());
+        ret.setTotalSubjectiveScore(ep.getTotalSubjectiveScore());
         ret.setPaperPath(ep.getPaperPath());
         ret.setStructPath(ep.getStructPath());
         ret.setAnswerPath(ep.getAnswerPath());
@@ -158,14 +160,14 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
 //            FileUtil.decryptFile(answerEncryptFile, answerFile, ep.getDecryptSecret(), ep.getDecryptVector());
             String structJson = FileUtil.readFileContent(structFile);
             String answerjson = FileUtil.readFileContent(answerFile);
-            return buildCache(structJson, answerjson);
+            return buildObjectiveAnswerCache(structJson, answerjson);
         } finally {
             FileUtil.deleteFolder(dir);
         }
     }
 
 
-    private Map<String, ObjectiveAnswerCacheBean> buildCache(String structStr, String answerStr) {
+    private Map<String, ObjectiveAnswerCacheBean> buildObjectiveAnswerCache(String structStr, String answerStr) {
         Map<String, ObjectiveAnswerCacheBean> map = new HashMap<String, ObjectiveAnswerCacheBean>();
         JSONObject answerJson = JSONObject.parseObject(answerStr);
         JSONArray answerdetails = answerJson.getJSONArray("details");
@@ -196,7 +198,7 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
                         Boolean answer = answerquestion.getBoolean("answer");
                         bean.setBoolAnswer(answer);
                     }
-                    map.put(mainNum + underLine + subNum, bean);
+                    map.put(RedisKeyHelper.examAnswerHashKey(mainNum, subNum, null), bean);
                 }
                 if (structType.intValue() == 6) {
                     JSONArray answersubQuestions = answerquestion.getJSONArray("subQuestions");
@@ -223,7 +225,7 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
                                 Boolean answer = answersubquestion.getBoolean("answer");
                                 bean.setBoolAnswer(answer);
                             }
-                            map.put(mainNum + underLine + subNum + underLine + subIndex, bean);
+                            map.put(RedisKeyHelper.examAnswerHashKey(mainNum, subNum, subIndex), bean);
                         }
                     }
                 }
@@ -249,6 +251,70 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
             teExamPaperMapper.updateWeight(id, map.get(id));
             examPaperService.deleteExamPaperCacheBean(id);
             examPaperService.deleteObjectiveAnswerCacheBean(id);
+            examPaperService.deletePaperStructCacheBean(id);
         }
     }
+    
+    /**
+     * 删除试卷结构缓存
+     *
+     * @param paperId
+     */
+    @Override
+    @CacheEvict(value = "paper_struct", key = "#paperId")
+    public void deletePaperStructCacheBean(Long paperId) {
+
+    }
+    @Cacheable(value = "paper_struct", key = "#paperId", unless = "#result == null")
+    @Override
+    public Map<String, Integer> getPaperStructCacheBean(Long paperId) {
+        Map<String, Integer> ret = null;
+        ExamPaperCacheBean ep = getExamPaperCacheBean(paperId);
+        if (ep == null) {
+            return ret;
+        }
+        if(StringUtils.isBlank(ep.getStructPath())) {//没有试卷结构文件
+        	return ret;
+        }
+        ret = new HashMap<String, Integer>();
+        String structUrl = OssUtil.getUrlForPrivateBucket(systemConfig.getOssEnv(3), ep.getStructPath());
+        String tempDir = SystemConstant.TEMP_FILES_DIR;
+        String dir = tempDir + "/" + uuid() + "/";
+        File dfile = new File(dir);
+        try {
+            dfile.mkdirs();
+            File structFile = new File(dir + "struct.json");
+            FileUtil.saveUrlAsFile(structUrl, structFile);
+            String structJson = FileUtil.readFileContent(structFile);
+            return buildPaperStructCache(structJson);
+        } finally {
+            FileUtil.deleteFolder(dir);
+        }
+    }
+    
+    private Map<String, Integer> buildPaperStructCache(String structStr) {
+        Map<String, Integer> map = new HashMap<String, Integer>();
+        JSONObject structJson = JSONObject.parseObject(structStr);
+        JSONArray structdetails = structJson.getJSONArray("details");
+        for (int i = 0; i < structdetails.size(); i++) {
+            Integer mainNum = structdetails.getJSONObject(i).getInteger("number");
+            JSONArray structdetailquestions = structdetails.getJSONObject(i).getJSONArray("questions");
+            for (int j = 0; j < structdetailquestions.size(); j++) {
+                JSONObject structquestion = structdetailquestions.getJSONObject(j);
+                Integer subNum = structquestion.getInteger("number");
+                Integer structType = structquestion.getInteger("structType");
+                map.put(RedisKeyHelper.examAnswerHashKey(mainNum, subNum, null), structType);
+                if (structType.intValue() == 6) {
+                    JSONArray structsubQuestions = structquestion.getJSONArray("subQuestions");
+                    for (int k = 0; k < structsubQuestions.size(); k++) {
+                        JSONObject structsubquestion = structsubQuestions.getJSONObject(k);
+                        Integer subIndex = structsubquestion.getInteger("number");
+                        Integer subStructType = structsubquestion.getInteger("structType");
+                        map.put(RedisKeyHelper.examAnswerHashKey(mainNum, subNum, subIndex), subStructType);
+                    }
+                }
+            }
+        }
+        return map;
+    }
 }

+ 5 - 0
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamStudentServiceImpl.java

@@ -175,4 +175,9 @@ public class TEExamStudentServiceImpl extends ServiceImpl<TEExamStudentMapper, T
     public List<MarkResultSimpleExportDto> markResultQueryExport(Long examId, Long activityId, String identity, String name, String courseCode) {
         return teExamStudentMapper.markResultQueryExport(examId, activityId, identity, name, courseCode);
     }
+
+	@Override
+	public List<Long> findExamStudentIdsNeedMark(Long examId, String courseCode, Long idGt, Integer count) {
+		return teExamStudentMapper.findExamStudentIdsNeedMark( examId, courseCode,  idGt, count);
+	}
 }

+ 140 - 19
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEOpenServiceImpl.java

@@ -2,6 +2,8 @@ package com.qmth.themis.business.service.impl;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -22,6 +24,7 @@ import com.qmth.themis.business.bean.backend.OpenExamBean;
 import com.qmth.themis.business.bean.backend.OpenExamCourseBean;
 import com.qmth.themis.business.bean.backend.OpenRecordAnswerBean;
 import com.qmth.themis.business.bean.backend.OpenRecordNeedMarkBean;
+import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
 import com.qmth.themis.business.config.SystemConfig;
 import com.qmth.themis.business.constant.SpringContextHolder;
@@ -29,9 +32,11 @@ import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.entity.TEExam;
 import com.qmth.themis.business.entity.TEExamCourse;
 import com.qmth.themis.business.enums.InvigilateMonitorStatusEnum;
+import com.qmth.themis.business.enums.RecordSelectStrategyEnum;
 import com.qmth.themis.business.service.TEExamCourseService;
 import com.qmth.themis.business.service.TEExamPaperService;
 import com.qmth.themis.business.service.TEExamService;
+import com.qmth.themis.business.service.TEExamStudentService;
 import com.qmth.themis.business.service.TEOpenService;
 import com.qmth.themis.business.service.TOeExamAnswerService;
 import com.qmth.themis.business.service.TOeExamRecordService;
@@ -55,22 +60,27 @@ public class TEOpenServiceImpl implements TEOpenService {
     
     @Resource
     TOeExamAnswerService examAnswerService;
+    
+    @Resource
+    TEExamStudentService examStudentService;
 	
 	
 	@Override
-	public IPage<OpenExamBean> examQueryPage(Long examId, String examCode, int pageNumber, int pageSize) {
+	public List<OpenExamBean> examQueryPage(Long examId, String examCode, int pageNumber, int pageSize) {
 		Page<OpenExamBean> ipage = new Page<>(pageNumber, pageSize);
         ipage.addOrder(OrderItem.desc("t.id"));
-		return examService.examQueryForOpen(ipage, examId, examCode);
+        IPage<OpenExamBean> ret=examService.examQueryForOpen(ipage, examId, examCode);
+		return ret.getRecords();
 	}
 
 
 	@Override
-	public IPage<OpenExamCourseBean> examCourseQueryPage(Long examId, String courseCode, Boolean hasPaper,
+	public List<OpenExamCourseBean> examCourseQueryPage(Long examId, String courseCode, Boolean hasPaper,
 			int pageNumber, int pageSize) {
 		Page<OpenExamCourseBean> ipage = new Page<>(pageNumber, pageSize);
         ipage.addOrder(OrderItem.desc("t.id"));
-		return examCourseService.examCourseQueryForOpen(ipage, examId,courseCode, hasPaper);
+        IPage<OpenExamCourseBean> ret=examCourseService.examCourseQueryForOpen(ipage, examId,courseCode, hasPaper);
+		return ret.getRecords();
 	}
 
 
@@ -224,27 +234,49 @@ public class TEOpenServiceImpl implements TEOpenService {
 
 
 	@Override
-	public JSONArray examRecordNeedMark(Long examId, String courseCode, Integer idGt,
+	public JSONArray examRecordNeedMark(Long examId, String courseCode, Long idGt,
 			Integer count) {
 		checkExam(examId);
-		List<OpenRecordNeedMarkBean> list=examRecordService.findExamRecordNeedMark(examId,courseCode,idGt,count);
-		if(list==null||list.size()==0) {
-			return null;
+		List<OpenRecordNeedMarkBean> list;
+		for(;;) {
+			List<Long> examStudentIds=examStudentService.findExamStudentIdsNeedMark(examId,courseCode,idGt,count);
+			if(examStudentIds==null||examStudentIds.size()==0) {
+				return null;
+			}
+			list=findExamRecordNeedMark(examId,examStudentIds);
+			if(list==null||list.size()==0) {
+				idGt=examStudentIds.get(examStudentIds.size()-1);
+			}else {
+				break;
+			}
 		}
-		List<Long> ids = list.stream().map(dto -> dto.getId()).collect(Collectors.toList());
+		List<Long> ids = list.stream().map(dto -> dto.getExamRecordId()).collect(Collectors.toList());
 		List<OpenRecordAnswerBean> answers=examAnswerService.findByRecordIds(ids);
-		Map<Long,Integer> map=new HashMap<>();
-		for(int i=0;i<list.size();i++) {
-			map.put(list.get(i).getId(), i);
+		if(answers!=null&&answers.size()>0) {
+			for(int i=0;i<answers.size();i++) {//剔除客观题答案
+				OpenRecordAnswerBean answer=answers.get(i);
+				Map<String, Integer> struct=examPaperService.getPaperStructCacheBean(answer.getPaperId());
+				Integer type=struct.get(RedisKeyHelper.examAnswerHashKey(answer.getMainNumber(), answer.getSubNumber(), answer.getSubIndex()));
+				if(type==1||type==2||type==3) {
+					answers.remove(i);
+					i--;
+				}
+			}
 		}
-		for(OpenRecordAnswerBean anwser:answers) {
-			OpenRecordNeedMarkBean mark=list.get(map.get(anwser.getRecordId()));
-			List<OpenRecordAnswerBean> ans=mark.getAnswers();
-			if(ans==null) {
-				ans=new ArrayList<OpenRecordAnswerBean>();
-				mark.setAnswers(ans);
+		if(answers!=null&&answers.size()>0) {
+			Map<Long,Integer> map=new HashMap<>();
+			for(int i=0;i<list.size();i++) {
+				map.put(list.get(i).getExamRecordId(), i);
+			}
+			for(OpenRecordAnswerBean anwser:answers) {
+				OpenRecordNeedMarkBean mark=list.get(map.get(anwser.getRecordId()));
+				List<OpenRecordAnswerBean> ans=mark.getAnswers();
+				if(ans==null) {
+					ans=new ArrayList<OpenRecordAnswerBean>();
+					mark.setAnswers(ans);
+				}
+				ans.add(anwser);
 			}
-			ans.add(anwser);
 		}
 		JSONArray  ja = JSONArray.parseArray(JSONArray.toJSONString(list));
 		for(int i=0;i<ja.size();i++) {
@@ -254,6 +286,7 @@ public class TEOpenServiceImpl implements TEOpenService {
 				for(int j=0;j<answersJa.size();j++) {
 					JSONObject answersJob=answersJa.getJSONObject(j);
 					answersJob.remove("recordId");
+					answersJob.remove("paperId");
 					String answerStr=answersJob.getString("answer");
 					if(StringUtils.isNotBlank(answerStr)) {
 						if(answerStr.toLowerCase().startsWith("true")||answerStr.toLowerCase().startsWith("false")) {
@@ -267,4 +300,92 @@ public class TEOpenServiceImpl implements TEOpenService {
 		}
 		return ja;
 	}
+	
+	private List<OpenRecordNeedMarkBean> findExamRecordNeedMark(Long examId, List<Long> examStudentIds){
+		List<OpenRecordNeedMarkBean> list=examRecordService.findExamRecordNeedMark(examId,examStudentIds);
+		if(list==null||list.size()==0) {
+			return null;
+		}
+		for(int i=0;i<list.size();i++) {//剔除没有主观题的试卷考试记录
+			ExamPaperCacheBean paper=examPaperService.getExamPaperCacheBean(list.get(i).getPaperId());
+			if(paper.getTotalSubjectiveScore()==0) {
+				list.remove(i);
+				i--;
+			}
+		}
+		if(list==null||list.size()==0) {
+			return null;
+		}
+		TEExam exam=examService.getById(examId);
+		if(RecordSelectStrategyEnum.HIGHEST_TOTAL_SCORE.equals(exam.getRecordSelectStrategy())) {//全阅
+			return list;
+		}else if(RecordSelectStrategyEnum.HIGHEST_OBJECTIVE_SCORE.equals(exam.getRecordSelectStrategy())) {//客观分最高,相同则都阅
+			Map<Long,List<OpenRecordNeedMarkBean>> map=new HashMap<>();
+			for(OpenRecordNeedMarkBean bean:list) {
+				List<OpenRecordNeedMarkBean> temList=map.get(bean.getExamStudentId());
+				if(temList==null) {
+					temList=new ArrayList<OpenRecordNeedMarkBean>();
+					temList.add(bean);
+					map.put(bean.getExamStudentId(), temList);
+				}else {
+					if(temList.get(0).getObjectiveScore()==bean.getObjectiveScore()) {
+						temList.add(bean);
+					}else if(temList.get(0).getObjectiveScore()<bean.getObjectiveScore()) {
+						temList=new ArrayList<OpenRecordNeedMarkBean>();
+						temList.add(bean);
+						map.put(bean.getExamStudentId(), temList);
+					}
+				}
+			}
+			List<OpenRecordNeedMarkBean> ret= new ArrayList<OpenRecordNeedMarkBean>();
+			for(List<OpenRecordNeedMarkBean> tem:map.values()) {
+				for(OpenRecordNeedMarkBean bean:tem) {
+					ret.add(bean);
+				}
+			}
+			Collections.sort(ret, new Comparator<OpenRecordNeedMarkBean>() {
+				@Override
+				public int compare(OpenRecordNeedMarkBean o1, OpenRecordNeedMarkBean o2) {
+					Long c1 = o1.getExamStudentId();
+					Long c2 = o2.getExamStudentId();
+					if (c1 > c2) {
+						return 1;
+					} else if (c1 < c2) {
+						return -1;
+					} else {
+						return 0;
+					}
+				}
+
+			});
+			return ret;
+		}else if(RecordSelectStrategyEnum.LATEST.equals(exam.getRecordSelectStrategy())) {//最后一次提交
+			Map<Long,OpenRecordNeedMarkBean> map=new HashMap<>();
+			for(OpenRecordNeedMarkBean bean:list) {
+				OpenRecordNeedMarkBean old=map.get(bean.getExamStudentId());
+				if(old==null||old.getFinishTime()<bean.getFinishTime()) {
+					map.put(bean.getExamStudentId(), bean);
+				}
+			}
+			List<OpenRecordNeedMarkBean> ret= map.values().stream().collect(Collectors.toList());
+			Collections.sort(ret, new Comparator<OpenRecordNeedMarkBean>() {
+				@Override
+				public int compare(OpenRecordNeedMarkBean o1, OpenRecordNeedMarkBean o2) {
+					Long c1 = o1.getExamStudentId();
+					Long c2 = o2.getExamStudentId();
+					if (c1 > c2) {
+						return 1;
+					} else if (c1 < c2) {
+						return -1;
+					} else {
+						return 0;
+					}
+				}
+
+			});
+			return ret;
+		}else {
+			throw new BusinessException("考试批次阅卷类型未定义");
+		}
+	}
 }

+ 2 - 3
themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java

@@ -817,8 +817,7 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
     }
 
     @Override
-    public List<OpenRecordNeedMarkBean> findExamRecordNeedMark(Long examId, String courseCode, Integer idGt,
-                                                               Integer count) {
-        return tOeExamRecordMapper.findExamRecordNeedMark(examId, courseCode, idGt, count);
+    public List<OpenRecordNeedMarkBean> findExamRecordNeedMark(Long examId, List<Long> examStudentIds) {
+        return tOeExamRecordMapper.findExamRecordNeedMark(examId,examStudentIds);
     }
 }

+ 29 - 1
themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java

@@ -404,6 +404,8 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
 	}
 
 	private void disposeStruct(String rootDir, TEExamPaper paper, File paperFile) {
+		BigDecimal obTotal = new BigDecimal(0);
+		BigDecimal subTotal = new BigDecimal(0);
 		if (paperFile == null) {
 			return;
 		}
@@ -415,12 +417,36 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
 				JSONObject question = questions.getJSONObject(j);
 				question.remove("body");
 				question.remove("options");
-				if (question.getInteger("structType").intValue() == 6) {
+				Double score = question.getDouble("score");
+				if(score==null) {
+					score=0.0;
+				}
+				Integer structType = question.getInteger("structType");
+				if (structType.intValue() != 1
+						&& structType.intValue() != 2
+								&& structType.intValue() != 3&&structType.intValue() != 6) {//主观题
+					subTotal=subTotal.add(new BigDecimal(score));
+				}else {//客观题
+					obTotal=obTotal.add(new BigDecimal(score));
+				}
+				if (structType.intValue() == 6) {
 					JSONArray subQuestions = question.getJSONArray("subQuestions");
 					for (int k = 0; k < subQuestions.size(); k++) {
 						JSONObject subquestion = subQuestions.getJSONObject(k);
 						subquestion.remove("body");
 						subquestion.remove("options");
+						Double subScore = subquestion.getDouble("score");
+						if(subScore==null) {
+							subScore=0.0;
+						}
+						Integer subStructType = subquestion.getInteger("structType");
+						if (subStructType.intValue() != 1
+								&& subStructType.intValue() != 2
+										&& subStructType.intValue() != 3) {//主观题
+							subTotal=subTotal.add(new BigDecimal(subScore));
+						}else {//客观题
+							obTotal=obTotal.add(new BigDecimal(subScore));
+						}
 					}
 				}
 			}
@@ -430,6 +456,8 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
 		FileUtil.saveAsFile(file.getAbsolutePath(), paperJson.toJSONString());
 		String filePath = sdf.format(new Date()) + "/" + uuid() + ".json";
 		paper.setStructPath(filePath);
+		paper.setTotalObjectiveScore(obTotal.doubleValue());
+		paper.setTotalSubjectiveScore(subTotal.doubleValue());
 		SystemConfig systemConfig = SpringContextHolder.getBean(SystemConfig.class);
 		OssUtil.ossUpload(systemConfig.getOssEnv(3), filePath, file);
 	}

+ 2 - 0
themis-business/src/main/resources/db/init.sql

@@ -1029,6 +1029,8 @@ CREATE TABLE `t_e_exam_paper` (
   `id` bigint NOT NULL COMMENT '主键',
   `name` varchar(100) NOT NULL COMMENT '试卷名称',
   `total_score` double NOT NULL COMMENT '试卷总分',
+  `total_objective_score` double NOT NULL COMMENT '试卷客观题总分',
+  `total_subjective_score` double NOT NULL COMMENT '试卷主观题总分',
   `paper_path` varchar(100) DEFAULT NULL COMMENT '题干路径',
   `decrypt_secret` varchar(50) DEFAULT NULL COMMENT '解密密钥',
   `encrypt_mode` varchar(30) DEFAULT NULL COMMENT 'auto:自动,hand:手动',

+ 10 - 0
themis-business/src/main/resources/mapper/TEExamStudentMapper.xml

@@ -370,4 +370,14 @@
 	<select id="markResultQueryExport" resultType="com.qmth.themis.business.dto.MarkResultSimpleExportDto">
 		<include refid="markResultCommon" />
 	</select>
+	
+	<select id="findExamStudentIdsNeedMark" resultType="java.lang.Long">
+		select t.id from t_e_exam_student t
+		where t.exam_id=#{examId} and t.id&gt;#{idGt}
+		<if test="courseCode != null and courseCode != ''">
+			and t.course_code=#{courseCode}
+		</if>
+		order by t.id
+		limit #{count}
+	</select>
 </mapper>

+ 3 - 2
themis-business/src/main/resources/mapper/TOeExamAnswerMapper.xml

@@ -11,9 +11,10 @@
 		t.sub_number
 		subNumber,
 		t.sub_index subIndex,
-		t.answer
+		t.answer,
+		f.paper_id paperId
 		FROM
-		t_oe_exam_answer t
+		t_oe_exam_answer t left join t_oe_exam_record f on t.exam_record_id=f.id
 		WHERE t.exam_record_id in
 		<foreach collection="ids" item="rid"
 			index="index" open="(" close=")" separator=",">

+ 9 - 8
themis-business/src/main/resources/mapper/TOeExamRecordMapper.xml

@@ -918,23 +918,24 @@
 
 	<select id="findExamRecordNeedMark" resultType="com.qmth.themis.business.bean.backend.OpenRecordNeedMarkBean">
 		SELECT
-		t.id,
+		t.id examRecordId,
+		t.exam_student_id examStudentId,
 		f.identity,
 		f.NAME,
 		g.CODE activityCode,
 		f.course_code courseCode,
 		f.course_name courseName,
 		t.paper_id paperId,
-		t.objective_score objectiveScore
+		t.objective_score objectiveScore,
+		t.finish_time finishTime
 		FROM
 		t_oe_exam_record t
 		LEFT JOIN t_e_exam_student f ON t.exam_student_id = f.id
 		LEFT JOIN t_e_exam_activity g ON t.exam_activity_id = g.id
-		where t.exam_id=#{examId} and t.id&gt;#{idGt}
-		<if test="courseCode != null and courseCode != ''">
-			and f.course_code=#{courseCode}
-		</if>
-		order by t.id
-		limit #{count}
+		where t.exam_id=#{examId} and t.breach_status!=1 and t.exam_student_id in
+		<foreach collection="examStudentIds" item="examStudentId"
+			index="index" open="(" close=")" separator=",">
+			#{examStudentId}
+		</foreach>
 	</select>
 </mapper>