瀏覽代碼

重构试评功能;扫描端新增二次识别修改客观题,上传缺考名单

ting.yin 1 年之前
父節點
當前提交
142d8ef394
共有 20 個文件被更改,包括 598 次插入412 次删除
  1. 23 17
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamStudentDao.java
  2. 49 35
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamStudent.java
  3. 64 52
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/query/ExamStudentSearchQuery.java
  4. 10 9
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamStudentService.java
  5. 177 170
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java
  6. 100 79
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java
  7. 0 24
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  8. 7 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TrialServiceImpl.java
  9. 0 8
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  10. 2 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/TrialService.java
  11. 2 2
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/SubjectiveStatus.java
  12. 33 2
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java
  13. 2 2
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java
  14. 15 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java
  15. 36 0
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamStudentController.java
  16. 44 0
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ScanController.java
  17. 1 11
      stmms-web/src/main/java/cn/com/qmth/stmms/api/dto/MenualAbsentDTO.java
  18. 24 0
      stmms-web/src/main/java/cn/com/qmth/stmms/api/dto/RefixStudent.java
  19. 1 0
      stmms-web/src/main/webapp/sql/stmms_ft.sql
  20. 8 0
      stmms-web/src/main/webapp/sql/upgrade/1.3.14.sql

+ 23 - 17
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamStudentDao.java

@@ -2,6 +2,7 @@ package cn.com.qmth.stmms.biz.exam.dao;
 
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Modifying;
@@ -13,8 +14,8 @@ import java.util.Date;
 import java.util.List;
 import java.util.Set;
 
-public interface ExamStudentDao
-        extends PagingAndSortingRepository<ExamStudent, Integer>, JpaSpecificationExecutor<ExamStudent> {
+public interface ExamStudentDao extends PagingAndSortingRepository<ExamStudent, Integer>,
+        JpaSpecificationExecutor<ExamStudent> {
 
     public List<ExamStudent> findByExamId(int examId, Pageable pageable);
 
@@ -72,14 +73,17 @@ public interface ExamStudentDao
 
     @Modifying
     @Query("update ExamStudent s set s.subjectiveStatus=?3, s.subjectiveScore=?4, s.subjectiveScoreList=?5,s.inspectTime=?6,s.inspectorId=?7 where s.examId=?1 and s.subjectCode=?2")
-    public void updateSubjectiveStatusAndScore(Integer examId, String subjetCode, SubjectiveStatus status, double score,
-            String scoreList, Date inspectTime, Integer inspectorId);
-
-    @Modifying
-    @Query("update ExamStudent s set s.subjectiveStatus=?3, s.subjectiveScore=?4, s.subjectiveScoreList=?5, s.inspectTime=?6, s.inspectorId=?7 "
-            + " where s.examId=?1 and s.subjectCode=?2 and s.subjectiveStatus not in (?8)")
-    public void updateSubjectiveStatusAndScoreWithoutStatus(Integer examId, String subjetCode, SubjectiveStatus status,
-            double score, String scoreList, Date inspectTime, Integer inspectorId, SubjectiveStatus... notInStatus);
+    public void updateSubjectiveStatusAndScore(Integer examId, String subjetCode, SubjectiveStatus status,
+            double score, String scoreList, Date inspectTime, Integer inspectorId);
+
+    // @Modifying
+    // @Query("update ExamStudent s set s.subjectiveStatus=?3, s.subjectiveScore=?4, s.subjectiveScoreList=?5, s.inspectTime=?6, s.inspectorId=?7 "
+    // +
+    // " where s.examId=?1 and s.subjectCode=?2 and s.subjectiveStatus not in (?8)")
+    // public void updateSubjectiveStatusAndScoreWithoutStatus(Integer examId,
+    // String subjetCode, SubjectiveStatus status,
+    // double score, String scoreList, Date inspectTime, Integer inspectorId,
+    // SubjectiveStatus... notInStatus);
 
     @Modifying
     @Query("update ExamStudent s set s.subjectiveStatus=?3, s.subjectiveScore=?4, s.subjectiveScoreList=?5 "
@@ -159,13 +163,12 @@ public interface ExamStudentDao
 
     @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.upload=true and s.absent=false and s.breach=false "
             + " and not exists (select l.id from MarkLibrary l where l.studentId=s.id and l.groupNumber=?3)")
-    public List<ExamStudent> findUnLibraryStudent(Integer examId, String subjectCode, Integer groupNumber,
-            Pageable page);
+    public List<ExamStudent> findUnLibraryStudent(Integer examId, String subjectCode, Integer groupNumber, Pageable page);
 
     @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.upload=true and s.absent=false and s.breach=false "
-            + "and s.subjectiveStatus=?4 and not exists (select l.id from TrialLibrary l where l.studentId=s.id and l.groupNumber=?3)")
-    public List<ExamStudent> findUnTrialStudent(Integer examId, String subjectCode, Integer groupNumber,
-            SubjectiveStatus status, Pageable page);
+            + "and s.trial=?4 and not exists (select l.id from TrialLibrary l where l.studentId=s.id and l.groupNumber=?3)")
+    public List<ExamStudent> findUnTrialStudent(Integer examId, String subjectCode, Integer groupNumber, boolean trial,
+            Pageable page);
 
     @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.upload=true and (s.absent=true or s.breach=true) and "
             + "exists (select l.id from MarkLibrary l where l.studentId=s.id)")
@@ -206,8 +209,7 @@ public interface ExamStudentDao
     public void updateSubjectiveStatusAndTimeAndInspectorId(Integer studentId, SubjectiveStatus status,
             Date inspectTime, Integer inspectorId);
 
-    public List<ExamStudent> findByExamIdAndStudentCodeAndSubjectCode(int examId, String studentCode,
-            String subjectCode);
+    public List<ExamStudent> findByExamIdAndStudentCodeAndSubjectCode(int examId, String studentCode, String subjectCode);
 
     @Query("select sum(s.sheetCount) from ExamStudent s where s.examId=?1 ")
     public Long sumSheetCountByExamId(Integer examId);
@@ -252,4 +254,8 @@ public interface ExamStudentDao
     @Query("update ExamStudent s set s.inspected=0, s.inspectTime=null ,s.inspectorId=null where s.id=?1 ")
     public void cancelInspect(Integer studentId);
 
+    @Modifying
+    @Query("update ExamStudent s set s.trial=?2 where s.id=?1")
+    public int updateTrial(Integer studentId, boolean trial);
+
 }

+ 49 - 35
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamStudent.java

@@ -196,6 +196,12 @@ public class ExamStudent implements Serializable {
     @Column(name = "is_exception", nullable = false)
     private boolean exception;
 
+    /**
+     * 是否数试评
+     */
+    @Column(name = "is_trial", nullable = false)
+    private boolean trial;
+
     /**
      * 上传时间
      */
@@ -284,32 +290,31 @@ public class ExamStudent implements Serializable {
 
     @Column(name = "inspect_count", nullable = true)
     private Integer inspectCount;
-    
+
     /**
      * 当前轮次是否已复核
      */
     @Column(name = "inspected", nullable = false)
     private boolean inspected;
-    
+
     /**
      * 成绩校验人ID
      */
     @Column(name = "score_verify_user")
     private Integer scoreVerifyUser;
-    
+
     /**
      * 成绩校验时间
      */
     @Temporal(TemporalType.TIMESTAMP)
     @Column(name = "score_verify_time")
     private Date scoreVerifyTime;
-    
+
     /**
      * 成绩校验标记结果
      */
     @Column(name = "score_verify_flagged")
     private Boolean scoreVerifyFlagged;
-    
 
     /**
      * 科目备注信息
@@ -918,7 +923,7 @@ public class ExamStudent implements Serializable {
         this.cardNumber = cardNumber;
     }
 
-	public Integer getInspectCount() {
+    public Integer getInspectCount() {
         return inspectCount;
     }
 
@@ -926,41 +931,50 @@ public class ExamStudent implements Serializable {
         this.inspectCount = inspectCount;
     }
 
-	public Integer getScoreVerifyUser() {
-		return scoreVerifyUser;
-	}
+    public Integer getScoreVerifyUser() {
+        return scoreVerifyUser;
+    }
 
-	public void setScoreVerifyUser(Integer scoreVerifyUser) {
-		this.scoreVerifyUser = scoreVerifyUser;
-	}
+    public void setScoreVerifyUser(Integer scoreVerifyUser) {
+        this.scoreVerifyUser = scoreVerifyUser;
+    }
 
-	public Date getScoreVerifyTime() {
-		return scoreVerifyTime;
-	}
+    public Date getScoreVerifyTime() {
+        return scoreVerifyTime;
+    }
 
-	public void setScoreVerifyTime(Date scoreVerifyTime) {
-		this.scoreVerifyTime = scoreVerifyTime;
-	}
+    public void setScoreVerifyTime(Date scoreVerifyTime) {
+        this.scoreVerifyTime = scoreVerifyTime;
+    }
 
-	public Boolean getScoreVerifyFlagged() {
-		return scoreVerifyFlagged;
-	}
+    public Boolean getScoreVerifyFlagged() {
+        return scoreVerifyFlagged;
+    }
+
+    public void setScoreVerifyFlagged(Boolean scoreVerifyFlagged) {
+        this.scoreVerifyFlagged = scoreVerifyFlagged;
+    }
+
+    public boolean getInspected() {
+        return inspected;
+    }
 
-	public void setScoreVerifyFlagged(Boolean scoreVerifyFlagged) {
-		this.scoreVerifyFlagged = scoreVerifyFlagged;
-	}
+    public void setInspected(boolean inspected) {
+        this.inspected = inspected;
+    }
 
-	public boolean getInspected() {
-		return inspected;
-	}
+    public static ExamStudent of(ExamStudentVo vo) {
+        ExamStudent e = new ExamStudent();
+        BeanUtils.copyProperties(vo, e);
+        return e;
+    }
 
-	public void setInspected(boolean inspected) {
-		this.inspected = inspected;
-	}
+    public boolean isTrial() {
+        return trial;
+    }
+
+    public void setTrial(boolean trial) {
+        this.trial = trial;
+    }
 
-	public static ExamStudent of(ExamStudentVo vo) {
-		ExamStudent e=new ExamStudent();
-		BeanUtils.copyProperties(vo, e);
-		return e;
-	}
 }

+ 64 - 52
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/query/ExamStudentSearchQuery.java

@@ -14,8 +14,11 @@ import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
 
 public class ExamStudentSearchQuery extends BaseQuery<ExamStudent> {
-	private Integer userId;
-	private Role role;
+
+    private Integer userId;
+
+    private Role role;
+
     private Integer examId;
 
     private Integer schoolId;
@@ -94,6 +97,8 @@ public class ExamStudentSearchQuery extends BaseQuery<ExamStudent> {
 
     private Boolean breach;
 
+    private Boolean trial;
+
     private String college;
 
     private String className;
@@ -105,19 +110,19 @@ public class ExamStudentSearchQuery extends BaseQuery<ExamStudent> {
     private List<Integer> studentIds;
 
     private Integer inspectorId;
-    
+
     private String inspectorName;
 
     private Integer sheetCount;
-    
-    //是否已复核
+
+    // 是否已复核
     private Boolean inspected;
-    
+
     private Double questionScore;
-    
+
     private Boolean questionScoreEmpty;
-    
-    //不可重复
+
+    // 不可重复
     private Boolean inspectUnrepeated;
 
     public ExamStudentSearchQuery() {
@@ -509,61 +514,68 @@ public class ExamStudentSearchQuery extends BaseQuery<ExamStudent> {
         this.sheetCount = sheetCount;
     }
 
-	public Boolean getInspected() {
-		return inspected;
-	}
+    public Boolean getInspected() {
+        return inspected;
+    }
 
-	public void setInspected(Boolean inspected) {
-		this.inspected = inspected;
-	}
+    public void setInspected(Boolean inspected) {
+        this.inspected = inspected;
+    }
 
-	public String getInspectorName() {
-		return inspectorName;
-	}
+    public String getInspectorName() {
+        return inspectorName;
+    }
 
-	public void setInspectorName(String inspectorName) {
-		this.inspectorName = inspectorName;
-	}
+    public void setInspectorName(String inspectorName) {
+        this.inspectorName = inspectorName;
+    }
 
-	public Double getQuestionScore() {
-		return questionScore;
-	}
+    public Double getQuestionScore() {
+        return questionScore;
+    }
 
-	public void setQuestionScore(Double questionScore) {
-		this.questionScore = questionScore;
-	}
+    public void setQuestionScore(Double questionScore) {
+        this.questionScore = questionScore;
+    }
 
-	public Boolean getQuestionScoreEmpty() {
-		return questionScoreEmpty;
-	}
+    public Boolean getQuestionScoreEmpty() {
+        return questionScoreEmpty;
+    }
 
-	public void setQuestionScoreEmpty(Boolean questionScoreEmpty) {
-		this.questionScoreEmpty = questionScoreEmpty;
-	}
+    public void setQuestionScoreEmpty(Boolean questionScoreEmpty) {
+        this.questionScoreEmpty = questionScoreEmpty;
+    }
 
-	public Boolean getInspectUnrepeated() {
-		return inspectUnrepeated;
-	}
+    public Boolean getInspectUnrepeated() {
+        return inspectUnrepeated;
+    }
 
-	public void setInspectUnrepeated(Boolean inspectUnrepeated) {
-		this.inspectUnrepeated = inspectUnrepeated;
-	}
+    public void setInspectUnrepeated(Boolean inspectUnrepeated) {
+        this.inspectUnrepeated = inspectUnrepeated;
+    }
 
-	public Role getRole() {
-		return role;
-	}
+    public Role getRole() {
+        return role;
+    }
 
-	public void setRole(Role role) {
-		this.role = role;
-	}
+    public void setRole(Role role) {
+        this.role = role;
+    }
 
-	public Integer getUserId() {
-		return userId;
-	}
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
 
-	public void setUserId(Integer userId) {
-		this.userId = userId;
-	}
+    public Boolean getTrial() {
+        return trial;
+    }
+
+    public void setTrial(Boolean trial) {
+        this.trial = trial;
+    }
 
-	
 }

+ 10 - 9
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamStudentService.java

@@ -65,7 +65,8 @@ public interface ExamStudentService {
 
     void updateSubjectInfo(ExamSubject subject);
 
-    void resetSubjectiveStatusAndScoreWithoutTrial(Integer examId, String subjectCode);
+    // void resetSubjectiveStatusAndScoreWithoutTrial(Integer examId, String
+    // subjectCode);
 
     void updateSubjectiveStatusAndScoreAndInspectorId(Integer examId, String subjectCode, SubjectiveStatus status,
             double score, String scoreList, Date inspectTime, Integer inspectorId);
@@ -107,8 +108,7 @@ public interface ExamStudentService {
     public Long countByExamIdAndSubjectCodeAndCampus(Integer examId, String code, String campusName, boolean upload,
             boolean absent);
 
-    public long countByNoAbsentAndBreach(int examId, String subjectCode, boolean upload, boolean absent,
-            boolean breach);
+    public long countByNoAbsentAndBreach(int examId, String subjectCode, boolean upload, boolean absent, boolean breach);
 
     public long countByAbsentAndBreach(int examId, String subjectCode, Boolean absent, Boolean breach);
 
@@ -138,7 +138,7 @@ public interface ExamStudentService {
 
     long countUploadedByExamId(Integer examId);
 
-    ExamStudent randomUnTrialStudent(Integer examId, String subjectCode, Integer groupNumber);
+    List<ExamStudent> findUnTrialStudent(Integer examId, String subjectCode, Integer groupNumber);
 
     long countByExamIdAndManualAbsent(int examId, boolean manualAbsent);
 
@@ -153,10 +153,7 @@ public interface ExamStudentService {
 
     public void inspect(Integer studentId, Date inspectTime, Integer inspectorId);
 
-    public List<ExamStudent> findByExamIdAndStudentCodeAndSubjectCode(int examId, String studentCode,
-            String subjectCode);
-
-    long countByExamIdAndSubjectCodeAndStatus(int examId, String subjectCode, SubjectiveStatus... status);
+    public List<ExamStudent> findByExamIdAndStudentCodeAndSubjectCode(int examId, String studentCode, String subjectCode);
 
     public ExamStudent randomStudent(Integer examId, String code);
 
@@ -187,7 +184,11 @@ public interface ExamStudentService {
     void scoreVerifyTag(Integer userId, Integer studentId, Boolean tag);
 
     void cancelInspect(Integer studentId);
-    
+
     public ExamStudentSearchQuery findByQueryPlus(ExamStudentSearchQuery query);
 
+    public long countByExamIdAndSubjectCodeAndTrial(int examId, String code, boolean trial);
+
+    public boolean updateTrial(Integer studentId, boolean trial);
+
 }

+ 177 - 170
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java

@@ -15,6 +15,7 @@ import cn.com.qmth.stmms.biz.utils.PictureTag;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.math.RandomUtils;
 import org.hibernate.SQLQuery;
@@ -31,6 +32,7 @@ import javax.persistence.EntityManager;
 import javax.persistence.PersistenceContext;
 import javax.persistence.Query;
 import javax.persistence.criteria.*;
+
 import java.math.BigDecimal;
 import java.text.DecimalFormat;
 import java.util.*;
@@ -171,8 +173,7 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
             if (student.getSecretNumber() == null) {
                 student.randomSecretNumber();
                 while (secretNumberSet.contains(student.getSecretNumber())
-                        || studentDao.countByExamIdAndSecretNumber(student.getExamId(), student.getSecretNumber())
-                        > 0) {
+                        || studentDao.countByExamIdAndSecretNumber(student.getExamId(), student.getSecretNumber()) > 0) {
                     student.randomSecretNumber();
                 }
             }
@@ -401,10 +402,10 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     @Transactional
     public boolean updateScanInfo(ExamStudent student) {
         student.setUploadTime(new Date());
-        return studentDao
-                .updateScanInfo(student.getId(), student.getSheetCount(), student.getSliceCount(), student.getAnswers(),
-                        student.getBatchCode(), student.getPaperType(), student.isAbsent(), student.getUploadTime(),
-                        student.getObjectiveScore(), student.getObjectiveScoreList(), student.getCardNumber()) > 0;
+        return studentDao.updateScanInfo(student.getId(), student.getSheetCount(), student.getSliceCount(),
+                student.getAnswers(), student.getBatchCode(), student.getPaperType(), student.isAbsent(),
+                student.getUploadTime(), student.getObjectiveScore(), student.getObjectiveScoreList(),
+                student.getCardNumber()) > 0;
     }
 
     @Override
@@ -460,12 +461,14 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         studentDao.updateSubjectiveStatusAndScore(id, status, score, scoreList, null, null);
     }
 
-    @Override
-    @Transactional
-    public void resetSubjectiveStatusAndScoreWithoutTrial(Integer examId, String subjectCode) {
-        studentDao.updateSubjectiveStatusAndScoreWithoutStatus(examId, subjectCode, SubjectiveStatus.UNMARK, 0, null,
-                null, null, SubjectiveStatus.TRIAL);
-    }
+    // @Override
+    // @Transactional
+    // public void resetSubjectiveStatusAndScoreWithoutTrial(Integer examId,
+    // String subjectCode) {
+    // studentDao.updateSubjectiveStatusAndScoreWithoutStatus(examId,
+    // subjectCode, SubjectiveStatus.UNMARK, 0, null,
+    // null, null, SubjectiveStatus.TRIAL);
+    // }
 
     @Override
     @Transactional
@@ -492,14 +495,14 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
             @Override
             public Predicate toPredicate(Root<ExamStudent> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
                 List<Predicate> predicates = new LinkedList<Predicate>();
-                Expression<Double> evaluationItemSum = cb
-                        .sum(root.get("objectiveScore").as(Double.class), root.get("subjectiveScore").as(Double.class));
+                Expression<Double> evaluationItemSum = cb.sum(root.get("objectiveScore").as(Double.class),
+                        root.get("subjectiveScore").as(Double.class));
                 if (query.getStartScore() != null) {
                     Predicate predicate1 = cb.ge(evaluationItemSum, query.getStartScore());
                     Predicate predicate2 = cb.le(evaluationItemSum, query.getEndScore());
                     if (query.getStartScore() == 0) {
-                        Predicate predicate = cb
-                                .or(cb.equal(root.get("absent"), true), cb.equal(root.get("breach"), true));
+                        Predicate predicate = cb.or(cb.equal(root.get("absent"), true),
+                                cb.equal(root.get("breach"), true));
                         Predicate predicate3 = cb.and(predicate1, predicate2);
                         predicates.add(cb.or(predicate, predicate3));
                     } else {
@@ -614,6 +617,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
                 if (query.getException() != null) {
                     predicates.add(cb.equal(root.get("exception"), query.getException()));
                 }
+                if (query.getTrial() != null) {
+                    predicates.add(cb.equal(root.get("trial"), query.getTrial()));
+                }
                 if (query.getManualAbsent() != null) {
                     predicates.add(cb.equal(root.get("manualAbsent"), query.getManualAbsent()));
                 }
@@ -710,9 +716,8 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
                 if (query.getSheetCount() != null) {
                     predicates.add(cb.equal(root.get("sheetCount"), query.getSheetCount()));
                 }
-                return predicates.isEmpty() ?
-                        cb.conjunction() :
-                        cb.and(predicates.toArray(new Predicate[predicates.size()]));
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
+                        .size()]));
             }
         };
     }
@@ -731,8 +736,8 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     @Override
     public ExamStudent findBySchoolIdAndSubjectCodeAndStudentCodeAndRemark(Integer schoolId, String subjectCode,
             String studentCode, String examSeqCode) {
-        return studentDao
-                .findBySchoolIdAndSubjectCodeAndStudentCodeAndRemark(schoolId, subjectCode, studentCode, examSeqCode);
+        return studentDao.findBySchoolIdAndSubjectCodeAndStudentCodeAndRemark(schoolId, subjectCode, studentCode,
+                examSeqCode);
     }
 
     @Override
@@ -774,14 +779,13 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     }
 
     @Override
-    public ExamStudent randomUnTrialStudent(Integer examId, String subjectCode, Integer groupNumber) {
+    public List<ExamStudent> findUnTrialStudent(Integer examId, String subjectCode, Integer groupNumber) {
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
         query.setPageNumber(1);
-        query.setPageSize(1);
+        query.setPageSize(Integer.MAX_VALUE);
         query.setSort(new Sort(Direction.ASC, "uploadTime", "id"));
-        List<ExamStudent> list = studentDao
-                .findUnTrialStudent(examId, subjectCode, groupNumber, SubjectiveStatus.TRIAL, query);
-        return list.isEmpty() ? null : list.get(0);
+        List<ExamStudent> list = studentDao.findUnTrialStudent(examId, subjectCode, groupNumber, true, query);
+        return list;
     }
 
     @Override
@@ -806,8 +810,7 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         return countByQuery(query);
     }
 
-    public long countByNoAbsentAndBreach(int examId, String subjectCode, boolean upload, boolean absent,
-            boolean breach) {
+    public long countByNoAbsentAndBreach(int examId, String subjectCode, boolean upload, boolean absent, boolean breach) {
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
         query.setExamId(examId);
         query.setSubjectCode(subjectCode);
@@ -860,8 +863,8 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
             sliceConfig = exam.getSliceConfigList();
         }
         if (!sliceConfig.isEmpty()) {
-            List<PictureTag> tags = PictureConfigTransform
-                    .process(sliceConfig, getSliceTags(student, withGroupScore, sliceConfig)).get(index);
+            List<PictureTag> tags = PictureConfigTransform.process(sliceConfig,
+                    getSliceTags(student, withGroupScore, sliceConfig)).get(index);
             if (tags != null) {
                 list.addAll(tags);
             }
@@ -911,8 +914,8 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     public Map<MarkGroup, List<OriginTag>> getSliceTags(ExamStudent student, boolean withGroupScore,
             List<PictureConfigItem> sliceConfig) {
         Map<MarkGroup, List<OriginTag>> tagMap = new HashMap<MarkGroup, List<OriginTag>>();
-        List<ExamQuestion> questions = questionService
-                .findByExamAndSubjectAndObjective(student.getExamId(), student.getSubjectCode(), false);
+        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
+                student.getSubjectCode(), false);
         List<ScoreItem> scoreList = student.getScoreList(false);
         List<MarkGroup> markGroups = groupService.findByExamAndSubject(student.getExamId(), student.getSubjectCode());
         for (MarkGroup group : markGroups) {
@@ -934,14 +937,13 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         List<String> lines = new LinkedList<>();
         lines.add("成绩明细");
         // 总分得分明细
-        lines.add("总分 (客观+主观) | " + format.format(student.getTotalScore()) + "=" + format
-                .format(student.getObjectiveScore() != null ? student.getObjectiveScore() : 0) + "+" + format
-                .format(student.getSubjectiveScore() != null ? student.getSubjectiveScore() : 0));
+        lines.add("总分 (客观+主观) | " + format.format(student.getTotalScore()) + "="
+                + format.format(student.getObjectiveScore() != null ? student.getObjectiveScore() : 0) + "+"
+                + format.format(student.getSubjectiveScore() != null ? student.getSubjectiveScore() : 0));
         // 客观题得分明细
         List<String> objectives = new LinkedList<>();
-        List<ExamQuestion> questions = questionService
-                .findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(), student.getSubjectCode(), true,
-                        student.getPaperType());
+        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndPaperType(
+                student.getExamId(), student.getSubjectCode(), true, student.getPaperType());
         List<ScoreItem> scoreList = student.getScoreList(true);
         List<String> details = new ArrayList<>();
         int i = 0;
@@ -1024,9 +1026,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
                 if (config.getX() <= 1 && config.getY() <= 1 && config.getI() <= sliceConfig.size()
                         && sliceConfig.get(config.getI() - 1).getW() > 0
                         && sliceConfig.get(config.getI() - 1).getH() > 0) {
-                    tag = new OriginTag(0, group.getNumber(), format.format(score), config.getI(),
-                            config.getX() * sliceConfig.get(config.getI() - 1).getW(),
-                            config.getY() * sliceConfig.get(config.getI() - 1).getH());
+                    tag = new OriginTag(0, group.getNumber(), format.format(score), config.getI(), config.getX()
+                            * sliceConfig.get(config.getI() - 1).getW(), config.getY()
+                            * sliceConfig.get(config.getI() - 1).getH());
                 }
                 originTags.add(tag);
             }
@@ -1068,16 +1070,16 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
                 List<MarkTrack> tracks = trackService.findByLibraryId(library.getId());
                 for (MarkTrack markTrack : tracks) {
                     // 未作答时只显示汉字"空"
-                    originTags.add(new OriginTag(library.getMarkerId(), library.getGroupNumber(),
-                            markTrack.isUnanswered() ? "空" : format.format(markTrack.getScore()),
-                            markTrack.getOffsetIndex(), markTrack.getOffsetX(), markTrack.getOffsetY()));
+                    originTags.add(new OriginTag(library.getMarkerId(), library.getGroupNumber(), markTrack
+                            .isUnanswered() ? "空" : format.format(markTrack.getScore()), markTrack.getOffsetIndex(),
+                            markTrack.getOffsetX(), markTrack.getOffsetY()));
                 }
                 // 添加特殊标记
                 List<MarkSpecialTag> specialTags = specialTagService.findByLibraryId(library.getId());
                 for (MarkSpecialTag markSpecialTag : specialTags) {
-                    originTags.add(new OriginTag(library.getMarkerId(), library.getGroupNumber(),
-                            markSpecialTag.getTagName(), markSpecialTag.getOffsetIndex(), markSpecialTag.getOffsetX(),
-                            markSpecialTag.getOffsetY()));
+                    originTags.add(new OriginTag(library.getMarkerId(), library.getGroupNumber(), markSpecialTag
+                            .getTagName(), markSpecialTag.getOffsetIndex(), markSpecialTag.getOffsetX(), markSpecialTag
+                            .getOffsetY()));
                 }
             }
 
@@ -1126,19 +1128,16 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     }
 
     @Override
-    public List<ExamStudent> findByExamIdAndStudentCodeAndSubjectCode(int examId, String studentCode,
-            String subjectCode) {
+    public List<ExamStudent> findByExamIdAndStudentCodeAndSubjectCode(int examId, String studentCode, String subjectCode) {
         return studentDao.findByExamIdAndStudentCodeAndSubjectCode(examId, studentCode, subjectCode);
     }
 
     @Override
-    public long countByExamIdAndSubjectCodeAndStatus(int examId, String subjectCode, SubjectiveStatus... status) {
+    public long countByExamIdAndSubjectCodeAndTrial(int examId, String subjectCode, boolean trial) {
         ExamStudentSearchQuery query = new ExamStudentSearchQuery();
         query.setExamId(examId);
         query.setSubjectCode(subjectCode);
-        for (SubjectiveStatus subjectiveStatus : status) {
-            query.addStatus(subjectiveStatus);
-        }
+        query.setTrial(trial);
         return countByQuery(query);
     }
 
@@ -1279,9 +1278,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     public int batchUpdate(List<ExamStudent> updateList) {
         int i = 0;
         for (ExamStudent student : updateList) {
-            i = i + this.studentDao
-                    .updateInfo(student.getId(), student.getCollege(), student.getClassName(), student.getTeacher(),
-                            student.getExamRoom(), student.getExamSite(), student.getRemark());
+            i = i
+                    + this.studentDao.updateInfo(student.getId(), student.getCollege(), student.getClassName(),
+                            student.getTeacher(), student.getExamRoom(), student.getExamSite(), student.getRemark());
         }
         return i;
     }
@@ -1303,202 +1302,210 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     public void cancelInspect(Integer studentId) {
         studentDao.cancelInspect(studentId);
     }
-    
 
-	@Override
-	public ExamStudentSearchQuery findByQueryPlus(ExamStudentSearchQuery query) {
-		checkQuery(query);
+    @Override
+    public ExamStudentSearchQuery findByQueryPlus(ExamStudentSearchQuery query) {
+        checkQuery(query);
         if (query.getSheetCount() != null) {
             query.setSheetCount(query.getSheetCount() * 2);
         }
         Integer totalCount = countByQueryPlus(query);
-		if(totalCount>0) {
-			List<ExamStudent> list = findByQuerySql(query);
-			query.setResult(list);
-			query.setTotalCount(totalCount);
-		}else {
-			query.setResult(new ArrayList<>());
-			query.setTotalCount(0);
-		}
+        if (totalCount > 0) {
+            List<ExamStudent> list = findByQuerySql(query);
+            query.setResult(list);
+            query.setTotalCount(totalCount);
+        } else {
+            query.setResult(new ArrayList<>());
+            query.setTotalCount(0);
+        }
         if (query.getSheetCount() != null) {
             query.setSheetCount(query.getSheetCount() / 2);
         }
         return query;
-	}
-	
-	private Integer countByQueryPlus(ExamStudentSearchQuery req) {
-		StringBuilder sql = new StringBuilder();
-		sql.append("select count(1) ");
-		sql.append("from eb_exam_student s ");
-		sql.append(getWhereSql(req));
-		Query query = entityManager.createNativeQuery(sql.toString());
-		return Integer.valueOf(query.getResultList().get(0).toString());
-	}
-	
-	@SuppressWarnings("unchecked")
-	private List<ExamStudent> findByQuerySql(ExamStudentSearchQuery req) {
-		int offset = (req.getPageNumber() - 1) * req.getPageSize();
-		StringBuilder sql = new StringBuilder();
-		sql.append(
-				" select s.sheet_count sheetCount,s.exam_number examNumber,s.exam_id examId,s.id,s.student_code studentCode,s.name");
-		sql.append(
-				" ,s.subject_code subjectCode,s.subject_name subjectName,s.is_upload upload,s.is_breach breach,s.is_absent absent,s.subjective_score subjectiveScore ");
-		sql.append(
-				" ,s.score_verify_time scoreVerifyTime,s.subjective_score_list subjectiveScoreList,s.objective_score objectiveScore ");
-		sql.append(" ,s.subject_level subjectLevel,s.subject_category subjectCategory,s.college,s.class_name className,s.teacher ");
-		sql.append(" ,s.exam_site examSite,s.exam_room examRoom,s.package_code packageCode");
-		sql.append(" from eb_exam_student s ");
+    }
+
+    private Integer countByQueryPlus(ExamStudentSearchQuery req) {
+        StringBuilder sql = new StringBuilder();
+        sql.append("select count(1) ");
+        sql.append("from eb_exam_student s ");
         sql.append(getWhereSql(req));
-		sql.append(" limit " + offset + "," + req.getPageSize());
-		Query query = entityManager.createNativeQuery(sql.toString());
-		query.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(ExamStudent.class));
-		List<ExamStudent> ret = query.getResultList();
-		return ret;
-	}
-
-	private String getWhereSql(ExamStudentSearchQuery query) {
-		StringBuilder sql = new StringBuilder(" WHERE 1=1 ");
-		if (query.getStartScore() != null) {
-			if (query.getStartScore() == 0) {
-				sql.append(" and (s.is_absent=1 or s.is_breach=1 or (s.subjective_score+s.objective_score>="
-			+query.getStartScore()+" and s.subjective_score+s.objective_score<="+query.getEndScore()+") ");
-			}else{
-				sql.append(" and s.is_absent=0 and s.is_breach=0 and s.subjective_score+s.objective_score>="
-						+query.getStartScore()+" and s.subjective_score+s.objective_score<="+query.getEndScore());
-			}
-		}
-		if (query.getExamId() != null && query.getExamId() > 0) {
-			sql.append(" and s.exam_id="+query.getExamId());
-		}
-		if (query.getSchoolId() != null) {
-			sql.append(" and s.school_id="+query.getSchoolId());
-		}
-//		if (StringUtils.isNotBlank(query.getIds())) {
-//            sql.append(" and s.id in("+query.getIds()+")");
-//        }
-//		if (query.getStudentIds() != null && query.getStudentIds().size() > 0) {
-//			sql.append(" and s.id in("+StringUtils.join(query.getStudentIds(),",")+")");
-//		}
-		if (StringUtils.isNotBlank(query.getExamNumber())) {
-            sql.append(" and s.exam_number='"+query.getExamNumber()+"'");
-        }
-		if (StringUtils.isNotEmpty(query.getSecretNumber())) {
-            sql.append(" and s.secret_number='"+query.getSecretNumber()+"'");
+        Query query = entityManager.createNativeQuery(sql.toString());
+        return Integer.valueOf(query.getResultList().get(0).toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<ExamStudent> findByQuerySql(ExamStudentSearchQuery req) {
+        int offset = (req.getPageNumber() - 1) * req.getPageSize();
+        StringBuilder sql = new StringBuilder();
+        sql.append(" select s.sheet_count sheetCount,s.exam_number examNumber,s.exam_id examId,s.id,s.student_code studentCode,s.name");
+        sql.append(" ,s.subject_code subjectCode,s.subject_name subjectName,s.is_upload upload,s.is_breach breach,s.is_absent absent,s.subjective_score subjectiveScore ");
+        sql.append(" ,s.score_verify_time scoreVerifyTime,s.subjective_score_list subjectiveScoreList,s.objective_score objectiveScore ");
+        sql.append(" ,s.subject_level subjectLevel,s.subject_category subjectCategory,s.college,s.class_name className,s.teacher ");
+        sql.append(" ,s.exam_site examSite,s.exam_room examRoom,s.package_code packageCode");
+        sql.append(" from eb_exam_student s ");
+        sql.append(getWhereSql(req));
+        sql.append(" limit " + offset + "," + req.getPageSize());
+        Query query = entityManager.createNativeQuery(sql.toString());
+        query.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(ExamStudent.class));
+        List<ExamStudent> ret = query.getResultList();
+        return ret;
+    }
+
+    private String getWhereSql(ExamStudentSearchQuery query) {
+        StringBuilder sql = new StringBuilder(" WHERE 1=1 ");
+        if (query.getStartScore() != null) {
+            if (query.getStartScore() == 0) {
+                sql.append(" and (s.is_absent=1 or s.is_breach=1 or (s.subjective_score+s.objective_score>="
+                        + query.getStartScore() + " and s.subjective_score+s.objective_score<=" + query.getEndScore()
+                        + ") ");
+            } else {
+                sql.append(" and s.is_absent=0 and s.is_breach=0 and s.subjective_score+s.objective_score>="
+                        + query.getStartScore() + " and s.subjective_score+s.objective_score<=" + query.getEndScore());
+            }
+        }
+        if (query.getExamId() != null && query.getExamId() > 0) {
+            sql.append(" and s.exam_id=" + query.getExamId());
+        }
+        if (query.getSchoolId() != null) {
+            sql.append(" and s.school_id=" + query.getSchoolId());
+        }
+        // if (StringUtils.isNotBlank(query.getIds())) {
+        // sql.append(" and s.id in("+query.getIds()+")");
+        // }
+        // if (query.getStudentIds() != null && query.getStudentIds().size() >
+        // 0) {
+        // sql.append(" and s.id in("+StringUtils.join(query.getStudentIds(),",")+")");
+        // }
+        if (StringUtils.isNotBlank(query.getExamNumber())) {
+            sql.append(" and s.exam_number='" + query.getExamNumber() + "'");
+        }
+        if (StringUtils.isNotEmpty(query.getSecretNumber())) {
+            sql.append(" and s.secret_number='" + query.getSecretNumber() + "'");
         }
         if (StringUtils.isNotEmpty(query.getStudentCode())) {
-            sql.append(" and s.student_code='"+query.getStudentCode()+"'");
+            sql.append(" and s.student_code='" + query.getStudentCode() + "'");
         }
         if (StringUtils.isNotBlank(query.getSubjectCode())) {
-            sql.append(" and s.subject_code='"+query.getSubjectCode()+"'");
-            
+            sql.append(" and s.subject_code='" + query.getSubjectCode() + "'");
+
         }
         if (StringUtils.isNotBlank(query.getCampusName())) {
-            sql.append(" and s.campus_name='"+query.getCampusName()+"'");
+            sql.append(" and s.campus_name='" + query.getCampusName() + "'");
         }
         if (StringUtils.isNotBlank(query.getName())) {
-            sql.append(" and s.name like '"+query.getName()+"%'");
+            sql.append(" and s.name like '" + query.getName() + "%'");
         }
         if (StringUtils.isNotBlank(query.getBatchCode())) {
-            sql.append(" and s.batch_code='"+query.getBatchCode()+"'");
+            sql.append(" and s.batch_code='" + query.getBatchCode() + "'");
         }
         if (StringUtils.isNotBlank(query.getPackageCode())) {
-            sql.append(" and s.package_code='"+query.getPackageCode()+"'");
+            sql.append(" and s.package_code='" + query.getPackageCode() + "'");
         }
         if (StringUtils.isNotBlank(query.getSubjectLevel())) {
-            sql.append(" and s.subject_level='"+query.getSubjectLevel()+"'");
+            sql.append(" and s.subject_level='" + query.getSubjectLevel() + "'");
         }
         if (StringUtils.isNotBlank(query.getSubjectCategory())) {
-            sql.append(" and s.subject_category='"+query.getSubjectCategory()+"'");
+            sql.append(" and s.subject_category='" + query.getSubjectCategory() + "'");
         }
         if (query.getObjectiveScore() != null) {
-            sql.append(" and s.objective_score="+query.getObjectiveScore());
+            sql.append(" and s.objective_score=" + query.getObjectiveScore());
         } else if (query.getObjectiveScoreGt() != null) {
-            sql.append(" and s.objective_score>"+query.getObjectiveScoreGt());
+            sql.append(" and s.objective_score>" + query.getObjectiveScoreGt());
         } else if (query.getObjectiveScoreLt() != null) {
-            sql.append(" and s.objective_score<"+query.getObjectiveScoreLt());
+            sql.append(" and s.objective_score<" + query.getObjectiveScoreLt());
         }
         if (query.getStatusSet() != null && query.getStatusSet().size() > 0) {
-            List<String> codes = query.getStatusSet().stream().map(e->e.name()).collect(Collectors.toList());
-            sql.append(" and s.subjective_status in('"+StringUtils.join(codes,"','")+"')");
+            List<String> codes = query.getStatusSet().stream().map(e -> e.name()).collect(Collectors.toList());
+            sql.append(" and s.subjective_status in('" + StringUtils.join(codes, "','") + "')");
         }
         if (query.getSubjectiveScore() != null) {
-            sql.append(" and s.subjective_score="+query.getSubjectiveScore());
+            sql.append(" and s.subjective_score=" + query.getSubjectiveScore());
         } else if (query.getSubjectiveScoreGt() != null) {
-        	sql.append(" and s.subjective_score>"+query.getSubjectiveScoreGt());
+            sql.append(" and s.subjective_score>" + query.getSubjectiveScoreGt());
         } else if (query.getSubjectiveScoreLt() != null) {
-        	sql.append(" and s.subjective_score<"+query.getSubjectiveScoreLt());
+            sql.append(" and s.subjective_score<" + query.getSubjectiveScoreLt());
         }
         if (query.getUpload() != null) {
-            sql.append(" and s.is_upload="+(query.getUpload()?1:0));
+            sql.append(" and s.is_upload=" + (query.getUpload() ? 1 : 0));
         }
         if (query.getInspected() != null) {
-            sql.append(" and s.inspected="+(query.getInspected()?1:0));
+            sql.append(" and s.inspected=" + (query.getInspected() ? 1 : 0));
         }
         if (query.getAbsent() != null) {
             if (query.getAbsent()) {// 缺考=缺考+ 未上传
-            	sql.append(" and (s.is_absent=1 or s.is_upload=0)");
+                sql.append(" and (s.is_absent=1 or s.is_upload=0)");
             } else {
-            	sql.append(" and s.is_absent="+(query.getAbsent()?1:0));
+                sql.append(" and s.is_absent=" + (query.getAbsent() ? 1 : 0));
                 sql.append(" and s.is_upload=1");
             }
         }
         if (query.getBreach() != null) {
-            sql.append(" and s.is_breach="+(query.getBreach()?1:0));
+            sql.append(" and s.is_breach=" + (query.getBreach() ? 1 : 0));
         }
         if (query.getException() != null) {
-        	sql.append(" and s.is_exception="+(query.getException()?1:0));
+            sql.append(" and s.is_exception=" + (query.getException() ? 1 : 0));
         }
         if (query.getManualAbsent() != null) {
-        	sql.append(" and s.is_manual_absent="+(query.getManualAbsent()?1:0));
+            sql.append(" and s.is_manual_absent=" + (query.getManualAbsent() ? 1 : 0));
         }
         if (query.getUploadTimeNotNull() != null) {
-        	sql.append(" and s.upload_time is not null");
+            sql.append(" and s.upload_time is not null");
         }
         if (StringUtils.isNotBlank(query.getSubjectCodeIn())) {
-            sql.append(" and s.subject_code in('"+StringUtils.join(query.getSubjectCodeIn().split(","),"','")+"')");
+            sql.append(" and s.subject_code in('" + StringUtils.join(query.getSubjectCodeIn().split(","), "','") + "')");
         }
         if (StringUtils.isNotBlank(query.getCampusNameIn())) {
-            sql.append(" and s.campus_name in('"+StringUtils.join(query.getCampusNameIn().split(","),"','")+"')");
+            sql.append(" and s.campus_name in('" + StringUtils.join(query.getCampusNameIn().split(","), "','") + "')");
         }
         if (StringUtils.isNotBlank(query.getExamSiteIn())) {
-            sql.append(" and s.exam_site in('"+StringUtils.join(query.getExamSiteIn().split(","),"','")+"')");
+            sql.append(" and s.exam_site in('" + StringUtils.join(query.getExamSiteIn().split(","), "','") + "')");
         }
         if (StringUtils.isNotBlank(query.getSubjectCodeNotIn())) {
-            sql.append(" and s.subject_code not in('"+StringUtils.join(query.getSubjectCodeNotIn().split(","),"','")+"')");
+            sql.append(" and s.subject_code not in('" + StringUtils.join(query.getSubjectCodeNotIn().split(","), "','")
+                    + "')");
         }
         if (StringUtils.isNotBlank(query.getCampusNameNotIn())) {
-        	sql.append(" and s.campus_name not in('"+StringUtils.join(query.getCampusNameNotIn().split(","),"','")+"')");
+            sql.append(" and s.campus_name not in('" + StringUtils.join(query.getCampusNameNotIn().split(","), "','")
+                    + "')");
         }
         if (StringUtils.isNotBlank(query.getExamSiteNotIn())) {
-            sql.append(" and s.exam_site not in('"+StringUtils.join(query.getExamSiteNotIn().split(","),"','")+"')");
+            sql.append(" and s.exam_site not in('" + StringUtils.join(query.getExamSiteNotIn().split(","), "','")
+                    + "')");
         }
         if (StringUtils.isNotBlank(query.getPaperType())) {
-            sql.append(" and s.paper_type='"+query.getPaperType()+"'");
+            sql.append(" and s.paper_type='" + query.getPaperType() + "'");
         }
         if (StringUtils.isNotBlank(query.getCollege())) {
-            sql.append(" and s.college='"+query.getCollege()+"'");
+            sql.append(" and s.college='" + query.getCollege() + "'");
         }
         if (StringUtils.isNotBlank(query.getClassName())) {
-            sql.append(" and s.class_name='"+query.getClassName()+"'");
+            sql.append(" and s.class_name='" + query.getClassName() + "'");
         }
         if (StringUtils.isNotBlank(query.getTeacher())) {
-            sql.append(" and s.teacher='"+query.getTeacher()+"'");
+            sql.append(" and s.teacher='" + query.getTeacher() + "'");
         }
         if (query.getInspectorId() != null) {
-            sql.append(" and s.inspector_id="+query.getTeacher());
+            sql.append(" and s.inspector_id=" + query.getTeacher());
         }
         if (StringUtils.isNotBlank(query.getExamSite())) {
-            sql.append(" and s.exam_site='"+query.getExamSite()+"'");
+            sql.append(" and s.exam_site='" + query.getExamSite() + "'");
         }
         if (StringUtils.isNotBlank(query.getExamRoom())) {
-            sql.append(" and s.exam_room='"+query.getExamRoom()+"'");
+            sql.append(" and s.exam_room='" + query.getExamRoom() + "'");
         }
         if (query.getSheetCount() != null) {
-            sql.append(" and s.sheet_count="+query.getSheetCount());
+            sql.append(" and s.sheet_count=" + query.getSheetCount());
         }
-        if(Role.SCHOOL_VIEWER.equals(query.getRole())) {
-        	sql.append(" and exists (select 1 from eb_user_student us where us.user_id="+query.getUserId()+" and s.exam_number=us.exam_number)");
+        if (Role.SCHOOL_VIEWER.equals(query.getRole())) {
+            sql.append(" and exists (select 1 from eb_user_student us where us.user_id=" + query.getUserId()
+                    + " and s.exam_number=us.exam_number)");
         }
-		return sql.toString();
-	}
+        return sql.toString();
+    }
+
+    @Transactional
+    @Override
+    public boolean updateTrial(Integer studentId, boolean trial) {
+        return studentDao.updateTrial(studentId, trial) > 0;
+    }
 }

+ 100 - 79
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java

@@ -1,27 +1,44 @@
 package cn.com.qmth.stmms.biz.mark.service.Impl;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
 import cn.com.qmth.stmms.biz.config.service.SystemConfigService;
 import cn.com.qmth.stmms.biz.config.service.impl.SystemCache;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.Marker;
-import cn.com.qmth.stmms.biz.exam.service.*;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.biz.mark.service.TrialService;
 import cn.com.qmth.stmms.biz.report.service.ReportService;
 import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
-import cn.com.qmth.stmms.common.enums.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.util.*;
+import cn.com.qmth.stmms.common.enums.ConfigType;
+import cn.com.qmth.stmms.common.enums.ExamStatus;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
 
 /**
  * 与评卷相关的所有定时任务
@@ -45,6 +62,9 @@ public class MarkCronService {
     @Autowired
     private MarkLibraryService libraryService;
 
+    @Autowired
+    private TrialService trialService;
+
     @Autowired
     private MarkService markService;
 
@@ -101,8 +121,8 @@ public class MarkCronService {
     public void buildLibrary() {
         log.info("start auto-create library");
         try {
-            List<Integer> examIds = groupService
-                    .findExamIdByStatus(ExamStatus.START, MarkStatus.TRIAL, MarkStatus.FORMAL);
+            List<Integer> examIds = groupService.findExamIdByStatus(ExamStatus.START, MarkStatus.TRIAL,
+                    MarkStatus.FORMAL);
             for (Integer examId : examIds) {
                 buildLibraryByExam(examId);
             }
@@ -135,11 +155,11 @@ public class MarkCronService {
         // 获取主观题总分大于0的科目
         List<ExamSubject> subjects = subjectService.list(examId, 0);
         for (ExamSubject subject : subjects) {
-            List<MarkGroup> groups = groupService
-                    .findByExamAndSubjectAndStatus(examId, subject.getCode(), MarkStatus.FORMAL);
+            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subject.getCode(),
+                    MarkStatus.FORMAL);
             for (MarkGroup markGroup : groups) {
-                List<Marker> markers = markerService
-                        .findByExamAndSubjectAndGroup(examId, subject.getCode(), markGroup.getNumber());
+                List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subject.getCode(),
+                        markGroup.getNumber());
                 for (Marker marker : markers) {
                     try {
                         if (markService.needUpdateQuality(marker, markerActiveExpireMinute)) {
@@ -160,11 +180,11 @@ public class MarkCronService {
         for (ExamSubject subject : subjects) {
             try {
                 lockService.watch(LockType.EXAM_SUBJECT, subject.getExamId(), subject.getCode());
-                log.info("start create library for examId=" + subject.getExamId() + ", subjectCode=" + subject
-                        .getCode());
+                log.info("start create library for examId=" + subject.getExamId() + ", subjectCode="
+                        + subject.getCode());
                 // 清除缺考考生和违纪考生
-                List<ExamStudent> list = studentService
-                        .findAbsentOrBreachLibraryStudent(subject.getExamId(), subject.getCode());
+                List<ExamStudent> list = studentService.findAbsentOrBreachLibraryStudent(subject.getExamId(),
+                        subject.getCode());
                 if (list != null) {
                     for (ExamStudent student : list) {
                         try {
@@ -179,12 +199,10 @@ public class MarkCronService {
                     }
                 }
                 // 处理正常考生
-                // 生成试评任务
-                List<MarkGroup> groups = groupService
-                        .findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(), MarkStatus.TRIAL);
-                long trialCount = studentService
-                        .countByExamIdAndSubjectCodeAndStatus(examId, subject.getCode(), SubjectiveStatus.TRIAL,
-                                SubjectiveStatus.MARKED);
+                // 生成试评考生
+                List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(),
+                        subject.getCode(), MarkStatus.TRIAL);
+                long trialCount = studentService.countByExamIdAndSubjectCodeAndTrial(examId, subject.getCode(), true);
                 if (!groups.isEmpty() && trialCount < subject.getTrialCount()) {
                     while (trialCount < subject.getTrialCount()) {
                         // 取一个的考生
@@ -192,21 +210,16 @@ public class MarkCronService {
                         if (student == null) {
                             break;
                         }
-                        studentService
-                                .updateSubjectiveStatusAndTimeAndInspectorId(student.getId(), SubjectiveStatus.TRIAL,
-                                        null, null);
+                        studentService.updateTrial(student.getId(), true);
                         trialCount++;
                     }
                 }
+                groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
+                        MarkStatus.FORMAL, MarkStatus.TRIAL);
                 for (MarkGroup group : groups) {
-                    buildTrialLibrary(trialCount, group);
-                }
-                // 生成正评任务
-                // 试评状态的分组也提前生成
-                groups = groupService
-                        .findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(), MarkStatus.FORMAL,
-                                MarkStatus.TRIAL);
-                for (MarkGroup group : groups) {
+                    // 试评状态的分组也提前生成
+                    buildTrialLibrary(group);
+                    // 生成正评任务
                     buildFormalLibrary(group);
                 }
             } finally {
@@ -222,14 +235,13 @@ public class MarkCronService {
                     + ", groupNumber=" + group.getNumber());
             lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
             // 上锁后重复验证分组状态
-            if (!groupService
-                    .validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(), MarkStatus.FORMAL,
-                            MarkStatus.TRIAL)) {
+            if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                    MarkStatus.FORMAL, MarkStatus.TRIAL)) {
                 return;
             }
             int count = 0;
-            List<ExamStudent> studentList = studentService
-                    .findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(), pageSize);
+            List<ExamStudent> studentList = studentService.findUnLibraryStudent(group.getExamId(),
+                    group.getSubjectCode(), group.getNumber(), pageSize);
             while (!studentList.isEmpty()) {
                 List<MarkLibrary> libraryList = new ArrayList<>();
                 int doubleCount = 0;
@@ -255,9 +267,9 @@ public class MarkCronService {
                             int expectCount = (int) (studentCount * group.getDoubleRate());
                             // 随机数判断加入当前已经生成双评任务的比例加权
                             // 实际双评任务数小于理论生成数 &&(剩余未生成双评的考生数量小于剩余应生成的数量||随机比例)
-                            needDouble = doubleCount < expectCount && (
-                                    (studentCount - libraryCount + doubleCount) <= (expectCount - doubleCount)
-                                            || Math.random() < group.getDoubleRate() + 0.1);
+                            needDouble = doubleCount < expectCount
+                                    && ((studentCount - libraryCount + doubleCount) <= (expectCount - doubleCount) || Math
+                                            .random() < group.getDoubleRate() + 0.1);
                         }
                         if (needDouble) {
                             library = new MarkLibrary();
@@ -277,59 +289,68 @@ public class MarkCronService {
                 // 有新任务创建
                 if (!libraryList.isEmpty()) {
                     count += libraryList.size();
-                    //任务乱序
+                    // 任务乱序
                     Collections.shuffle(libraryList);
-                    //批量保存
+                    // 批量保存
                     libraryService.save(libraryList);
-                    //正评状态才需要更新任务数量
+                    // 正评状态才需要更新任务数量
                     if (group.getStatus() == MarkStatus.FORMAL) {
                         markService.updateLibraryCount(group);
                         markService.updateMarkedCount(group);
                     }
                 }
-                studentList = studentService
-                        .findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(), pageSize);
+                studentList = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(),
+                        group.getNumber(), pageSize);
             }
-            log.info("finish create " + count + " library for examId=" + group.getExamId() + ", subjectCode=" + group
-                    .getSubjectCode() + ", groupNumber=" + group.getNumber());
+            log.info("finish create " + count + " library for examId=" + group.getExamId() + ", subjectCode="
+                    + group.getSubjectCode() + ", groupNumber=" + group.getNumber());
         } catch (Exception e) {
-            log.error("build formal library error for examId=" + group.getExamId() + ", subjectCode=" + group
-                    .getSubjectCode() + ", groupNumber=" + group.getNumber(), e);
+            log.error(
+                    "build formal library error for examId=" + group.getExamId() + ", subjectCode="
+                            + group.getSubjectCode() + ", groupNumber=" + group.getNumber(), e);
         } finally {
             lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
         }
     }
 
-    private void buildTrialLibrary(long trialCount, MarkGroup group) {
-        while (trialCount > group.getLibraryCount()) {
-            // 取一个未生成试评任务的考生
-            ExamStudent student = studentService
-                    .randomUnTrialStudent(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            if (student == null) {
+    private void buildTrialLibrary(MarkGroup group) {
+        List<ExamStudent> studentList = studentService.findUnTrialStudent(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        if (studentList.isEmpty()) {
+            return;
+        }
+        // 尝试构造试评任务
+        try {
+            lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+            // 上锁后重新验证分组状态
+            if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                    MarkStatus.TRIAL)) {
                 return;
             }
-            // 尝试构造试评任务
-            try {
-                lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
-                lockService.watch(LockType.STUDENT, student.getId());
-                // 上锁后重新验证分组状态
-                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
-                        MarkStatus.TRIAL)) {
-                    break;
-                }
-                // 上锁后重复验证考生状态
+            List<TrialLibrary> trialList = new ArrayList<TrialLibrary>();
+            for (ExamStudent student : studentList) {
                 if (studentService.validateStatus(student.getId())) {
-                    markService.buildTrialLibrary(student, group);
-                    markService.updateLibraryCount(group);
-                    markService.updateMarkedCount(group);
+                    TrialLibrary library = new TrialLibrary();
+                    library.setExamId(student.getExamId());
+                    library.setSubjectCode(student.getSubjectCode());
+                    library.setGroupNumber(group.getNumber());
+                    library.setStudentId(student.getId());
+                    library.setExamNumber(student.getExamNumber());
+                    library.setSecretNumber(student.getSecretNumber());
+                    trialList.add(library);
                 }
-            } catch (Exception e) {
-                log.error("build trial library error for studentId=" + student.getId() + ", groupNumber=" + group
-                        .getNumber(), e);
-            } finally {
-                lockService.unwatch(LockType.STUDENT, student.getId());
-                lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
             }
+            if (group.getStatus() == MarkStatus.TRIAL && !trialList.isEmpty()) {
+                trialService.save(trialList);
+                markService.updateLibraryCount(group);
+                markService.updateMarkedCount(group);
+            }
+        } catch (Exception e) {
+            log.error(
+                    "build trial library error for examId=" + group.getExamId() + ", subjectCode="
+                            + group.getSubjectCode() + ", groupNumber=" + group.getNumber(), e);
+        } finally {
+            lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
         }
     }
 

+ 0 - 24
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java

@@ -1318,30 +1318,6 @@ public class MarkServiceImpl implements MarkService {
         }
     }
 
-    /**
-     * 根据考生、学习中心、评卷分组构造试评评卷任务
-     *
-     * @param student
-     *            - 考生
-     * @param group
-     *            - 评卷分组
-     */
-    @Override
-    @Transactional
-    public void buildTrialLibrary(ExamStudent student, MarkGroup group) {
-        // 查询是否已创建评卷任务
-        if (trialLibraryDao.countByStudentIdAndGroupNumber(student.getId(), group.getNumber()) == 0) {
-            TrialLibrary library = new TrialLibrary();
-            library.setExamId(student.getExamId());
-            library.setSubjectCode(student.getSubjectCode());
-            library.setGroupNumber(group.getNumber());
-            library.setStudentId(student.getId());
-            library.setExamNumber(student.getExamNumber());
-            library.setSecretNumber(student.getSecretNumber());
-            trialLibraryDao.save(library);
-        }
-    }
-
     private String getGroupKey(MarkGroup group) {
         return group.getExamId() + "_" + group.getSubjectCode() + "_" + group.getNumber();
     }

+ 7 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TrialServiceImpl.java

@@ -15,6 +15,7 @@ import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import cn.com.qmth.stmms.biz.common.BaseQuery;
 import cn.com.qmth.stmms.biz.common.BaseQueryService;
@@ -161,4 +162,10 @@ public class TrialServiceImpl extends BaseQueryService<TrialLibrary> implements
     public long countMarkedLibrary(Integer examId, String subjectCode, Integer groupNumber) {
         return libraryDao.countMarked(examId, subjectCode, groupNumber);
     }
+
+    @Transactional
+    @Override
+    public void save(List<TrialLibrary> trialList) {
+        libraryDao.save(trialList);
+    }
 }

+ 0 - 8
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java

@@ -168,14 +168,6 @@ public interface MarkService {
      */
     int applyCount(Marker marker);
 
-    /**
-     * 根据考生、大题构造试评评卷任务
-     *
-     * @param student
-     * @param group
-     */
-    void buildTrialLibrary(ExamStudent student, MarkGroup group);
-
     /**
      * 评卷员提交评卷任务
      *

+ 2 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/TrialService.java

@@ -38,4 +38,6 @@ public interface TrialService {
 
     long countMarkedLibrary(Integer examId, String subjectCode, Integer groupNumber);
 
+    void save(List<TrialLibrary> trialList);
+
 }

+ 2 - 2
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/SubjectiveStatus.java

@@ -5,7 +5,7 @@ import java.util.List;
 
 public enum SubjectiveStatus {
 
-    UNMARK("未评完"), TRIAL("试评"), MARKED("已评完");
+    UNMARK("未评完"), MARKED("已评完");
 
     private String name;
 
@@ -32,7 +32,7 @@ public enum SubjectiveStatus {
         if (options == null) {
             options = new LinkedList<>();
             for (SubjectiveStatus status : SubjectiveStatus.values()) {
-                if (status != UNMARK && status != TRIAL) {
+                if (status != UNMARK) {
                     options.add(status);
                 }
             }

+ 33 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java

@@ -56,6 +56,7 @@ import cn.com.qmth.stmms.common.enums.HistoryStatus;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.enums.MarkMode;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 
@@ -106,6 +107,9 @@ public class ArbitrateController extends BaseExamController {
     @Value("${slice.split.config}")
     private String splitConfig;
 
+    @Value("${marker.forceMode}")
+    private String forceMarkMode;
+
     // 并发处理互斥锁
     private Map<Integer, Integer> currentTaskMap = new HashMap<Integer, Integer>();
 
@@ -153,15 +157,16 @@ public class ArbitrateController extends BaseExamController {
         WebUser wu = RequestUtils.getWebUser(request);
         releaseByUser(wu.getUser().getId());
         JSONObject setting = new JSONObject();
+        MarkGroup group = null;
 
         if (historyId != null) {
             ArbitrateHistory history = arbitrateService.findById(historyId);
             subjectCode = history.getSubjectCode();
-            MarkGroup group = groupService.findOne(examId, subjectCode, history.getGroupNumber());
+            group = groupService.findOne(examId, subjectCode, history.getGroupNumber());
             setting.accumulate("selective", group.isSelective());
         }
         if (subjectCode != null && groupNumber != null) {
-            MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
+            group = groupService.findOne(examId, subjectCode, groupNumber);
             setting.accumulate("selective", group.isSelective());
         }
         ExamSubject examSubject = subjectService.find(examId, subjectCode);
@@ -179,9 +184,35 @@ public class ArbitrateController extends BaseExamController {
         setting.accumulate("splitConfig", getSplitConfig());
         setting.accumulate("enableSplit",
                 examSubject.getEnableSplit() == null ? exam.isEnableSplit() : examSubject.getEnableSplit());
+        setMode(setting, group, exam);
         return setting;
     }
 
+    private void setMode(JSONObject setting, MarkGroup group, Exam exam) {
+        MarkMode mode = null;
+        boolean forceMode = false;
+        if (MarkMode.findByName(forceMarkMode) != null) {
+            // 全局配置的强制评卷模式
+            mode = MarkMode.findByName(forceMarkMode);
+            forceMode = true;
+        } else {
+            // 没有全局配置,优先从考试取强制评卷模式
+            if (exam != null && exam.getMarkMode() != null) {
+                mode = exam.getMarkMode();
+                forceMode = true;
+            } else if (group != null && group.getMarkMode() != null) {
+                // 没有全局配置,优先从大题配置取强制评卷模式
+                mode = group.getMarkMode();
+                forceMode = true;
+            }
+            if (mode == null) {
+                mode = MarkMode.COMMON;
+            }
+        }
+        setting.accumulate("mode", mode);
+        setting.accumulate("forceMode", forceMode);
+    }
+
     private double[] getSplitConfig() {
         String strs[] = splitConfig.split(",");
         double[] config = new double[strs.length];

+ 2 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java

@@ -459,7 +459,7 @@ public class MarkGroupController extends BaseExamController {
                 if (questionIds != null && reset.booleanValue()) {
                     // advance update
                     if (!checkSelective(examId, subjectCode, questionIds)) {
-                        addMessage(redirectAttributes, "选做题不合法!选做题和非选做题不能在一组,选做题不能跨组或区设置");
+                        addMessage(redirectAttributes, "选做题不合法!选做题和非选做题不能在一组,选做题不能跨组或区设置");
                         redirectAttributes.addAttribute("subjectCode", subjectCode);
                         redirectAttributes.addAttribute("number", number);
                         return "redirect:/admin/exam/group/edit-simple";
@@ -601,7 +601,7 @@ public class MarkGroupController extends BaseExamController {
             redirectAttributes.addAttribute("subjectCode", subjectCode);
             return "redirect:/admin/exam/group/add";
         } else if (!checkSelective(examId, subjectCode, questionIds)) {
-            addMessage(redirectAttributes, "选做题不合法!选做题和非选做题不能在一组,选做题不能跨组或区设置");
+            addMessage(redirectAttributes, "选做题不合法!选做题和非选做题不能在一组,选做题不能跨组或区设置");
             redirectAttributes.addAttribute("subjectCode", subjectCode);
             return "redirect:/admin/exam/group/add";
         } else {

+ 15 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java

@@ -751,9 +751,20 @@ public class PaperController extends BaseExamController {
     public String delete(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam Integer[] ids, ExamSubjectSearchQuery query, @RequestParam(required = false) Boolean upload) {
         List<ExamQuestion> list = new ArrayList<ExamQuestion>();
+        List<ExamQuestion> error = new ArrayList<ExamQuestion>();
         for (Integer id : ids) {
             ExamQuestion question = questionService.findById(id);
             if (question != null) {
+                SelectiveGroup selectiveGroup = selectiveGroupService.findOne(question.getExamId(),
+                        question.getSubjectCode(), question.getMainNumber());
+                if (selectiveGroup != null) {
+                    error.add(question);
+                    continue;
+                }
+                if (!question.isObjective() && question.getGroupNumber() != null) {
+                    error.add(question);
+                    continue;
+                }
                 if (lockService.trylock(LockType.QUESTION_DELETE, question.getExamId(), question.getSubjectCode(),
                         question.getMainNumber(), question.getSubNumber())) {
                     list.add(question);
@@ -765,6 +776,9 @@ public class PaperController extends BaseExamController {
         if (!list.isEmpty()) {
             taskExecutor.submit(new QuestionDeleteThread(list, markService, lockService));
         }
+        if (!error.isEmpty()) {
+            addMessage(redirectAttributes, "部分题目已有分组或选做题存在选做题分组,无法被删除");
+        }
         String u = upload == null ? "" : upload.toString();
         String t = query.getTotalScoreNotEqual() == null ? "" : query.getTotalScoreNotEqual().toString();
         return "redirect:/admin/exam/paper/detail?subjectCode=" + query.getCode() + "&pageNumber="
@@ -903,7 +917,7 @@ public class PaperController extends BaseExamController {
                 }
             }
             target.delete();
-        } catch (IOException e) {
+        } catch (Exception e) {
             log.error("parse question excel error", e);
             error.add("zip文件解析失败");
         }

+ 36 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ExamStudentController.java

@@ -1,5 +1,7 @@
 package cn.com.qmth.stmms.api.controller;
 
+import cn.com.qmth.stmms.api.dto.MenualAbsentDTO;
+import cn.com.qmth.stmms.api.exception.ApiException;
 import cn.com.qmth.stmms.api.utils.AESUtil;
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
@@ -16,11 +18,16 @@ import cn.com.qmth.stmms.biz.user.service.UserService;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.ExamStatus;
+import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
+
 import com.google.common.base.Strings;
+
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
+
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,6 +36,7 @@ import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+
 import java.text.DecimalFormat;
 import java.util.LinkedList;
 import java.util.List;
@@ -450,4 +458,32 @@ public class ExamStudentController extends BaseApiController {
         }
         return array;
     }
+
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCAN_ADMIN, Role.SCANNER })
+    @RequestMapping(value = "/student/manualAbsent/{examId}", method = RequestMethod.POST)
+    @ResponseBody
+    public Object updateManualAbsent(HttpServletRequest request, @PathVariable Integer examId,
+            @RequestBody MenualAbsentDTO[] datas) {
+        ApiUser user = RequestUtils.getApiUser(request);
+        Exam exam = examService.findById(examId);
+        JSONArray array = new JSONArray();
+        // 判断上传权限
+        if (exam == null || !exam.getSchoolId().equals(user.getSchoolId())
+                || !ExamStatus.START.equals(exam.getStatus()) || !ExamType.SCAN_IMAGE.equals(exam.getType())) {
+            throw ApiException.EXAM_NOT_ACCESSIBLED;
+        }
+        studentService.clearManualAbsent(examId);
+        if (datas != null && datas.length > 0) {
+            for (MenualAbsentDTO dto : datas) {
+                ExamStudent student = studentService.findByExamIdAndExamNumber(examId, dto.getExamNumber());
+                if (student != null) {
+                    studentService.updateManualAbsent(examId, student.getExamNumber(), true);
+                    JSONObject obj = new JSONObject();
+                    obj.accumulate("examNumber", student.getExamNumber());
+                    array.add(obj);
+                }
+            }
+        }
+        return array;
+    }
 }

+ 44 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/ScanController.java

@@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
+import cn.com.qmth.stmms.api.dto.RefixStudent;
 import cn.com.qmth.stmms.api.exception.ApiException;
 import cn.com.qmth.stmms.api.utils.ScanStudentParameter;
 import cn.com.qmth.stmms.biz.exam.model.CheckStudent;
@@ -160,4 +161,47 @@ public class ScanController extends BaseApiController {
         }
         return obj;
     }
+
+    /**
+     * 上传考生识别结果
+     *
+     * @param request
+     * @param examId
+     * @param scStudentParameter
+     * @return
+     */
+    @Logging(menu = "扫描端-考生二次识别客观题上传", type = LogType.UPDATE)
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCANNER, Role.SCAN_ADMIN })
+    @RequestMapping(value = "/student/answer/{examId}", method = RequestMethod.POST)
+    @ResponseBody
+    public JSONArray updateStudentAnswer(HttpServletRequest request, @PathVariable Integer examId,
+            @RequestBody RefixStudent[] refixStudents) {
+        ApiUser user = RequestUtils.getApiUser(request);
+        Exam exam = examService.findById(examId);
+        JSONArray array = new JSONArray();
+        // 判断上传权限
+        if (exam == null || !exam.getSchoolId().equals(user.getSchoolId())
+                || !ExamStatus.START.equals(exam.getStatus()) || !ExamType.SCAN_IMAGE.equals(exam.getType())) {
+            throw ApiException.EXAM_NOT_ACCESSIBLED;
+        }
+        if (refixStudents != null && refixStudents.length > 0) {
+            for (RefixStudent sc : refixStudents) {
+                ExamStudent student = studentService.findByExamIdAndExamNumber(examId, sc.getExamNumber());
+                if (student != null && student.isUpload()) {
+                    // 同步更新评卷任务,返回失败的考生
+                    student.setAnswers(sc.getAnswer());
+                    if (!saveUploadStudent(student)) {
+                        JSONObject obj = new JSONObject();
+                        obj.accumulate("examNumber", student.getExamNumber());
+                        array.add(obj);
+                    }
+                } else {
+                    JSONObject obj = new JSONObject();
+                    obj.accumulate("examNumber", student.getExamNumber());
+                    array.add(obj);
+                }
+            }
+        }
+        return array;
+    }
 }

+ 1 - 11
stmms-web/src/main/java/cn/com/qmth/stmms/api/utils/MenualAbsentDTO.java → stmms-web/src/main/java/cn/com/qmth/stmms/api/dto/MenualAbsentDTO.java

@@ -1,19 +1,9 @@
-package cn.com.qmth.stmms.api.utils;
+package cn.com.qmth.stmms.api.dto;
 
 public class MenualAbsentDTO {
 
     private String examNumber;
 
-    private boolean absent;
-
-    public boolean isAbsent() {
-        return absent;
-    }
-
-    public void setAbsent(boolean absent) {
-        this.absent = absent;
-    }
-
     public String getExamNumber() {
         return examNumber;
     }

+ 24 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/dto/RefixStudent.java

@@ -0,0 +1,24 @@
+package cn.com.qmth.stmms.api.dto;
+
+public class RefixStudent {
+
+    private String examNumber;
+
+    private String answer;
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+}

+ 1 - 0
stmms-web/src/main/webapp/sql/stmms_ft.sql

@@ -315,6 +315,7 @@ CREATE TABLE `eb_exam_student`
     `is_manual_absent`      tinyint(1)  NOT NULL COMMENT '是否人工指定缺考',
     `is_breach`             tinyint(1)  NOT NULL COMMENT '是否违纪',
     `is_exception`          tinyint(1)  NOT NULL COMMENT '是否数据异常',
+    `is_trial`              tinyint(1)  NOT NULL COMMENT '是否试评',
     `upload_time`           datetime     DEFAULT NULL COMMENT '上传时间',
     `inspect_time`          datetime     DEFAULT NULL COMMENT '复核时间',
     `inspector_id`          int(11)      DEFAULT NULL COMMENT '复核人ID',

+ 8 - 0
stmms-web/src/main/webapp/sql/upgrade/1.3.14.sql

@@ -156,6 +156,14 @@ CREATE TABLE `m_header_track`
     KEY `index3` (`exam_id`, `subject_code`, `group_number`)
 ) ENGINE = InnoDB
   DEFAULT CHARSET = utf8mb4 COMMENT ='组长轨迹给分表';
+
+-- 试评 
+ALTER TABLE eb_exam_student ADD COLUMN  `is_trial`      tinyint(1)  NOT NULL DEFAULT 0 COMMENT '是否试评';
+-- 更新试评数据
+UPDATE eb_exam_student set is_trial=1 where subjective_status='TRIAL';
+UPDATE eb_exam_student set subjective_status='UNMARK' where subjective_status='TRIAL';
+UPDATE eb_exam_student o,(SELECT DISTINCT s.student_id FROM m_trial_library s) n
+SET o.is_trial = 1 WHERE o.id = n.student_id;
 	
 DROP TABLE IF EXISTS `eb_user_student`;
 CREATE TABLE `eb_user_student`