Browse Source

将评卷分组改造,可以兼容包含多个大题的情况

luoshi 6 năm trước cách đây
mục cha
commit
c90061bff4

+ 1 - 0
.gitignore

@@ -1,6 +1,7 @@
 .project
 .settings
 .classpath
+.DS_Store
 target
 **/.classpath
 **/.project

+ 21 - 18
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamQuestionDao.java

@@ -12,27 +12,31 @@ import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 
 public interface ExamQuestionDao extends JpaRepository<ExamQuestion, Integer>, JpaSpecificationExecutor<ExamQuestion> {
 
-    @Query("select sum(q.totalScore) as totalScore from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.mainNumber=?3 and q.paperType=?4 ")
-    public Double queryFullScoreByExamIdAndSubjectCodeAndMainNumberAndPaperType(Integer examId, String subjectCode,
-            Integer mainNumberz,String paperType);
-
     @Query("select q from ExamQuestion q where q.examId=?1 and  q.subjectCode=?2 and q.objective=?3 "
             + "order by q.paperType, q.mainNumber, q.subNumber")
     public List<ExamQuestion> findByExamIdAndSubjectCodeAndObjective(Integer examId, String subjectCode,
             boolean objective);
 
+    @Query("select count(q) from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.mainNumber=?4")
+    public long countByExamIdAndSubjectCodeAndObjectiveAndMainNumber(Integer examId, String subjectCode,
+            boolean objective, Integer mainNumber);
+
     // @Query("select q from ExamQuestion q where q.examId=?1 and
     // q.subjectCode=?2 and q.objective=?3 "
     // + "and q.paperType=?4 order by q.mainNumber, q.subNumber")
     public List<ExamQuestion> findByExamIdAndSubjectCodeAndObjectiveAndPaperType(Integer examId, String subjectCode,
             boolean objective, String paperType, Sort sort);
 
-    @Query("select q from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 "
-            + "and q.mainNumber=?4 order by q.subNumber")
-
+    @Query("select q from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3"
+            + "  and q.mainNumber=?4 order by q.subNumber")
     public List<ExamQuestion> findByExamIdAndSubjectCodeAndObjectiveAndMainNumber(Integer examId, String subjectCode,
             boolean objective, Integer mainNumber);
 
+    @Query("select q from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3"
+            + "  and q.mainNumber=?4 order by q.mainNumber, q.subNumber")
+    public List<ExamQuestion> findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(Integer examId, String subjectCode,
+            boolean objective, Integer groupNumber);
+
     // @Query("select q from ExamQuestion q where q.examId=?1 and
     // q.subjectCode=?2 and q.objective=?3 "
     // + "and q.paperType=?4 and q.mainNumber=?5 order by q.subNumber")
@@ -40,23 +44,22 @@ public interface ExamQuestionDao extends JpaRepository<ExamQuestion, Integer>, J
             String subjectCode, boolean objective, String paperType, Integer mainNumber, Sort sort);
 
     @Modifying
-    @Query("delete from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3")
-
-    public void deleteByExamIdAndSubjectCodeAndObjective(Integer examId, String subjectCode, boolean objective);
+    @Query("update ExamQuestion q set q.mainTitle=?5 "
+            + "where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.mainNumber=?4")
+    public void updateMainTitle(Integer examId, String subjectCode, boolean objective, Integer mainNumber,
+            String mainTitle);
 
     @Modifying
-    @Query("delete from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.mainNumber=?4")
-    public void deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber);
+    @Query("delete from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3")
+    public void deleteByExamIdAndSubjectCodeAndObjective(Integer examId, String subjectCode, boolean objective);
 
     @Modifying
-    @Query("update ExamQuestion q set q.mainTitle=?5 where q.examId=?1 and q.subjectCode=?2 "
-            + "and q.objective=?3 and q.mainNumber=?4")
-    public void updateMainTitleByExamIdAndSubjectCodeAndObjectiveAndMainNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, String mainTitle);
+    @Query("delete from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.groupNumber=?4")
+    public void deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(Integer examId, String subjectCode,
+            boolean objective, Integer groupNumber);
 
     @Query("select DISTINCT q.paperType from ExamQuestion q where q.examId=?1 and  q.subjectCode=?2 and q.objective=?3 and q.paperType is not null "
             + "order by q.paperType")
-    public List<String> getPaperType(int examId, String subjectCode,boolean objective);
+    public List<String> getPaperType(int examId, String subjectCode, boolean objective);
 
 }

+ 11 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamQuestion.java

@@ -42,6 +42,9 @@ public class ExamQuestion implements Serializable {
     @Column(name = "main_title", nullable = false, length = 64)
     private String mainTitle;
 
+    @Column(name = "group_number", nullable = false)
+    private Integer groupNumber;
+
     @Column(name = "answer", nullable = true, length = 16)
     private String answer;
 
@@ -262,4 +265,12 @@ public class ExamQuestion implements Serializable {
         this.trialCount = trialCount;
     }
 
+    public Integer getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(Integer groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
 }

+ 38 - 17
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroup.java

@@ -3,7 +3,10 @@ package cn.com.qmth.stmms.biz.exam.model;
 import java.io.Serializable;
 import java.text.DecimalFormat;
 import java.util.Date;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import javax.persistence.Column;
 import javax.persistence.EmbeddedId;
@@ -32,9 +35,6 @@ public class MarkGroup implements Serializable {
     @EmbeddedId
     private MarkGroupPK pk;
 
-    @Column(name = "title", length = 128, nullable = false)
-    private String title;
-
     @Column(name = "pic_list", length = 255, nullable = false)
     private String picList;
 
@@ -102,11 +102,17 @@ public class MarkGroup implements Serializable {
     @Transient
     private int currentCount;
 
+    @Transient
+    private String title;
+
+    @Transient
+    private String mainNumber;
+
     @Transient
     private String scoreList;
 
     @Transient
-    private List<ExamQuestion> questionList;
+    private Map<Integer, List<ExamQuestion>> questionMap;
 
     @Transient
     private long markerCount;
@@ -124,14 +130,13 @@ public class MarkGroup implements Serializable {
         this.pk = new MarkGroupPK();
     }
 
-    public MarkGroup(Integer examId, String subjectCode, Integer number, String title,
-            List<PictureConfigItem> configList, Double totalScore, Double doubleRate, Double arbitrateThreshold,
-            Integer scorePolicy, String markMode, Integer trialCount, boolean sheetView) {
+    public MarkGroup(Integer examId, String subjectCode, Integer number, List<PictureConfigItem> configList,
+            Double totalScore, Double doubleRate, Double arbitrateThreshold, Integer scorePolicy, String markMode,
+            Integer trialCount, boolean sheetView) {
         this.pk = new MarkGroupPK();
         this.pk.setExamId(examId);
         this.pk.setNumber(number);
         this.pk.setSubjectCode(subjectCode);
-        this.title = title;
         this.picList = configList != null && configList.size() > 0
                 ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER)
                 : "";
@@ -148,6 +153,8 @@ public class MarkGroup implements Serializable {
         }
         if (scorePolicy != null) {
             this.scorePolicy = ScorePolicy.findByValue(scorePolicy);
+        } else {
+            this.scorePolicy = ScorePolicy.AVG;
         }
         if (markMode != null) {
             this.markMode = MarkMode.findByName(markMode);
@@ -243,15 +250,21 @@ public class MarkGroup implements Serializable {
         this.scoreList = scoreList;
     }
 
-    public void setScoreList(List<ExamQuestion> questionList) {
+    public void setQuestionList(List<ExamQuestion> questionList) {
         DecimalFormat format = new DecimalFormat("###.#");
+        Set<Integer> mainNumbers = new LinkedHashSet<>();
+        Set<String> mainTitles = new LinkedHashSet<>();
         StringBuilder score = new StringBuilder();
         for (ExamQuestion question : questionList) {
             if (score.length() > 0) {
                 score.append(",");
             }
             score.append(format.format(question.getTotalScore()));
+            mainNumbers.add(question.getMainNumber());
+            mainTitles.add(question.getMainTitle());
         }
+        this.mainNumber = StringUtils.join(mainNumbers, ",");
+        this.title = StringUtils.join(mainTitles, ",");
         this.scoreList = score.toString();
     }
 
@@ -279,14 +292,6 @@ public class MarkGroup implements Serializable {
         this.title = title;
     }
 
-    public List<ExamQuestion> getQuestionList() {
-        return questionList;
-    }
-
-    public void setQuestionList(List<ExamQuestion> questionList) {
-        this.questionList = questionList;
-    }
-
     public int getCurrentCount() {
         return currentCount;
     }
@@ -375,4 +380,20 @@ public class MarkGroup implements Serializable {
         this.sheetView = sheetView;
     }
 
+    public Map<Integer, List<ExamQuestion>> getQuestionMap() {
+        return questionMap;
+    }
+
+    public void setQuestionMap(Map<Integer, List<ExamQuestion>> questionMap) {
+        this.questionMap = questionMap;
+    }
+
+    public String getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(String mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
 }

+ 9 - 7
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamQuestionService.java

@@ -9,6 +9,8 @@ public interface ExamQuestionService {
 
     ExamQuestion save(ExamQuestion question);
 
+    void save(List<ExamQuestion> questionList);
+
     ExamQuestionSearchQuery findByQuery(ExamQuestionSearchQuery query);
 
     long countByQuery(ExamQuestionSearchQuery query);
@@ -17,20 +19,20 @@ public interface ExamQuestionService {
 
     void deleteByExamAndSubjectAndObjective(Integer examId, String subjectCode, boolean objective);
 
-    void deleteByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode, boolean objective,
-            Integer mainNumber);
+    void deleteByExamAndSubjectAndObjectiveAndGroupNumber(Integer examId, String subjectCode, boolean objective,
+            Integer groupNumber);
 
     List<ExamQuestion> findByExamAndSubjectAndObjective(Integer examId, String subjectCode, boolean objective);
 
     List<ExamQuestion> findByExamAndSubjectAndObjectiveAndPaperType(Integer examId, String subjectCode,
             boolean objective, String paperType);
 
-    List<ExamQuestion> findByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber);
+    List<String> getPaperType(int examId, String subjectCode);
 
-    List<ExamQuestion> findByExamAndSubjectAndObjectiveAndPaperTypeAndMainNumber(Integer examId, String subjectCode,
-            boolean objective, String paperType, Integer mainNumber);
+    long countByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode, boolean objective,
+            Integer mainNumber);
 
-    List<String> getPaperType(int examId, String subjectCode);
+    List<ExamQuestion> findByExamAndSubjectAndObjectiveAndGroupNumber(Integer examId, String subjectCode,
+            boolean objective, Integer groupNumber);
 
 }

+ 0 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java

@@ -26,8 +26,6 @@ public interface MarkGroupService {
 
     void updatePicList(int examId, String subjectCode, int number, List<PictureConfigItem> picList);
 
-    void updateTitle(int examId, String subjectCode, int number, String title);
-
     void updateBuildTime(int examId, String subjectCode, int number, Date time);
 
     void resetBuildTime(int examId, String subjectCode);

+ 22 - 17
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamQuestionServiceImpl.java

@@ -34,12 +34,17 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
         return questionDao.save(question);
     }
 
+    @Transactional
+    @Override
+    public void save(List<ExamQuestion> questionList) {
+        questionDao.save(questionList);
+    }
+
     @Transactional
     @Override
     public void updateMainTitle(Integer examId, String subjectCode, boolean objective, Integer mainNumber,
             String mainTitle) {
-        questionDao.updateMainTitleByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
-                mainNumber, mainTitle);
+        questionDao.updateMainTitle(examId, subjectCode, objective, mainNumber, mainTitle);
     }
 
     @Transactional
@@ -50,9 +55,9 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
 
     @Transactional
     @Override
-    public void deleteByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode, boolean objective,
-            Integer mainNumber) {
-        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective, mainNumber);
+    public void deleteByExamAndSubjectAndObjectiveAndGroupNumber(Integer examId, String subjectCode, boolean objective,
+            Integer groupNumber) {
+        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(examId, subjectCode, objective, groupNumber);
     }
 
     @Override
@@ -68,17 +73,10 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
     }
 
     @Override
-    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber) {
-        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
-                mainNumber);
-    }
-
-    @Override
-    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndPaperTypeAndMainNumber(Integer examId,
-            String subjectCode, boolean objective, String paperType, Integer mainNumber) {
-        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndPaperTypeAndMainNumber(examId, subjectCode,
-                objective, paperType, mainNumber, new Sort(Direction.ASC, "subNumber"));
+    public List<ExamQuestion> findByExamAndSubjectAndObjectiveAndGroupNumber(Integer examId, String subjectCode,
+            boolean objective, Integer groupNumber) {
+        return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(examId, subjectCode, objective,
+                groupNumber);
     }
 
     @Override
@@ -94,6 +92,13 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
         return questionDao.count(buildSpecification(query));
     }
 
+    @Override
+    public long countByExamAndSubjectAndObjectiveAndMainNumber(Integer examId, String subjectCode, boolean objective,
+            Integer mainNumber) {
+        return questionDao.countByExamIdAndSubjectCodeAndObjectiveAndMainNumber(examId, subjectCode, objective,
+                mainNumber);
+    }
+
     private Specification<ExamQuestion> buildSpecification(final ExamQuestionSearchQuery query) {
         return new Specification<ExamQuestion>() {
 
@@ -133,7 +138,7 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
 
     @Override
     public List<String> getPaperType(int examId, String subjectCode) {
-        return questionDao.getPaperType(examId,subjectCode,true);
+        return questionDao.getPaperType(examId, subjectCode, true);
     }
 
 }

+ 0 - 11
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java

@@ -12,7 +12,6 @@ import cn.com.qmth.stmms.biz.common.BaseQueryService;
 import cn.com.qmth.stmms.biz.exam.dao.MarkGroupDao;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroupPK;
-import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
 import cn.com.qmth.stmms.common.enums.MarkMode;
@@ -24,9 +23,6 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
     @Autowired
     private MarkGroupDao groupDao;
 
-    @Autowired
-    private ExamQuestionService questionService;
-
     @Transactional
     @Override
     public MarkGroup save(MarkGroup group) {
@@ -48,13 +44,6 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
                         : "");
     }
 
-    @Transactional
-    @Override
-    public void updateTitle(int examId, String subjectCode, int number, String title) {
-        groupDao.updateTitle(examId, subjectCode, number, title);
-        questionService.updateMainTitle(examId, subjectCode, false, number, title);
-    }
-
     @Transactional
     @Override
     public void updateBuildTime(int examId, String subjectCode, int number, Date time) {

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

@@ -215,7 +215,7 @@ public class MarkServiceImpl implements MarkService {
         markerDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 group.getNumber());
         // 小题数据
-        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(group.getExamId(), group.getSubjectCode(),
+        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 false, group.getNumber());
 
         releaseByGroup(group);
@@ -235,7 +235,7 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void updateGroup(MarkGroup group, List<Double> scores, ScorePolicy policy) {
-        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(group.getExamId(), group.getSubjectCode(),
+        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 false, group.getNumber());
         int i = 0;
         double totalScore = 0d;

+ 3 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java

@@ -198,7 +198,7 @@ public class TaskServiceImpl implements TaskService {
 
     private List<MarkStepDTO> buildMarkStep(MarkGroup group, Integer libraryId) {
         List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
-        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(group.getExamId(),
+        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
                 group.getSubjectCode(), false, group.getNumber());
         int number = 0;
         for (ExamQuestion question : sList) {
@@ -222,7 +222,7 @@ public class TaskServiceImpl implements TaskService {
 
     private List<MarkStepDTO> buildTrialStep(MarkGroup group, TrialHistory history) {
         List<MarkStepDTO> list = new LinkedList<MarkStepDTO>();
-        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(group.getExamId(),
+        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
                 group.getSubjectCode(), false, group.getNumber());
         int number = 0;
         for (ExamQuestion question : sList) {
@@ -362,7 +362,7 @@ public class TaskServiceImpl implements TaskService {
                 stepTask.setLibraryId(library.getId());
                 stepTask.setHeaderId(task.getHeaderId());
                 stepTask.setTags(task.getTags());
-                List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(
+                List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
                         library.getExamId(), library.getSubjectCode(), false, library.getGroupNumber());
                 if (start == end) {
                     end = end + sList.size() * 2 - 1;

+ 1 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ObjectiveQuestionDTO.java

@@ -53,6 +53,7 @@ public class ObjectiveQuestionDTO implements QuestionDTO {
         question.setPaperType(StringUtils.trimToNull(paperType));
         question.setMainNumber(mainNumber);
         question.setSubNumber(subNumber);
+        question.setGroupNumber(0);
         question.setObjective(true);
         question.setMainTitle(StringUtils.trimToNull(title));
         question.setAnswer(StringUtils.trimToNull(answer));

+ 46 - 27
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectQuestionDTO.java

@@ -60,16 +60,21 @@ public class SubjectQuestionDTO {
                 }
                 list.add(question);
             } else {
-                MarkGroup group = groups.get(question.getMainNumber());
+                MarkGroup group = groups.get(question.getGroupNumber());
                 if (group == null) {
-                    group = new MarkGroup(examId, subjectCode, question.getMainNumber(), question.getMainTitle(),
+                    group = new MarkGroup(examId, subjectCode, question.getGroupNumber(),
                             PictureConfigItem.parse(question.getPicList()), 0d, question.getDoubleRate(),
                             question.getArbitrateThreshold(), question.getScorePolicy(), question.getMarkMode(),
                             question.getTrialCount(), false);
-                    group.setQuestionList(new LinkedList<ExamQuestion>());
+                    group.setQuestionMap(new HashMap<Integer, List<ExamQuestion>>());
                     groups.put(question.getMainNumber(), group);
                 }
-                group.getQuestionList().add(question);
+                List<ExamQuestion> list = group.getQuestionMap().get(question.getMainNumber());
+                if (list == null) {
+                    list = new ArrayList<ExamQuestion>();
+                    group.getQuestionMap().put(question.getMainNumber(), list);
+                }
+                list.add(question);
                 group.setTotalScore(group.getTotalScore() + question.getTotalScore());
                 totalScore += question.getTotalScore();
             }
@@ -96,6 +101,7 @@ public class SubjectQuestionDTO {
         if (list.size() > 0) {
             Set<String> numberSet = new HashSet<String>();
             Map<Integer, String> titleMap = new HashMap<Integer, String>();
+            Map<Integer, Integer> groupNumberMap = new HashMap<Integer, Integer>();
             try {
                 // 基本字段合法性校验
                 for (ExamQuestion question : list) {
@@ -111,6 +117,10 @@ public class SubjectQuestionDTO {
                         error.add("[" + subjectCode + "] 有题号为空的记录");
                         return false;
                     }
+                    if (question.getGroupNumber() == null || question.getGroupNumber() < 0) {
+                        error.add("[" + subjectCode + "] 有评卷分组为空的记录");
+                        return false;
+                    }
                     if (question.getTotalScore() == null) {
                         error.add("[" + subjectCode + "] 有满分为空的记录");
                         return false;
@@ -128,6 +138,14 @@ public class SubjectQuestionDTO {
                         titleMap.put(question.getMainNumber(), question.getMainTitle());
                     }
 
+                    Integer groupNumber = groupNumberMap.get(question.getMainNumber());
+                    if (groupNumber != null && !groupNumber.equals(question.getGroupNumber())) {
+                        error.add("[" + subjectCode + "] 大题号" + question.getMainNumber() + "有评卷分组不一致的记录");
+                        return false;
+                    } else {
+                        groupNumberMap.put(question.getMainNumber(), question.getGroupNumber());
+                    }
+
                     String number = question.getQuestionNumber();
                     if (objective) {
                         number = StringUtils.trimToEmpty(question.getPaperType()) + " " + number;
@@ -155,13 +173,13 @@ public class SubjectQuestionDTO {
                             error.add("[" + subjectCode + "] 多个试卷类型总分不一致");
                             return false;
                         }
-                        if(questions!=null){
-                            String message = check(questions,entry.getValue());
-                            if(message!=null){
-                                error.add("[" + subjectCode + "] 多个试卷类型"+ message);
+                        if (questions != null) {
+                            String message = check(questions, entry.getValue());
+                            if (message != null) {
+                                error.add("[" + subjectCode + "] 多个试卷类型" + message);
                                 return false;
                             }
-                        }else{
+                        } else {
                             questions = entry.getValue();
                         }
                     }
@@ -170,6 +188,7 @@ public class SubjectQuestionDTO {
             } finally {
                 numberSet.clear();
                 titleMap.clear();
+                groupNumberMap.clear();
             }
         } else {
             return false;
@@ -177,19 +196,19 @@ public class SubjectQuestionDTO {
     }
 
     private String check(List<ExamQuestion> questions, List<ExamQuestion> value) {
-        if(questions.size()!=value.size()){
+        if (questions.size() != value.size()) {
             return "题目总数不一致";
         }
-        Map<Integer,Integer> subCountMap = new HashMap<Integer, Integer>();
-        Map<Integer,Double> mainScoreMap = new HashMap<Integer, Double>();
+        Map<Integer, Integer> subCountMap = new HashMap<Integer, Integer>();
+        Map<Integer, Double> mainScoreMap = new HashMap<Integer, Double>();
         int maniCount = 0;
         for (ExamQuestion question : questions) {
-            if(question.getTotalScore()!=0){
+            if (question.getTotalScore() != 0) {
                 Integer subCount = subCountMap.get(question.getMainNumber());
-                if (subCount != null ) {
-                    subCountMap.put(question.getMainNumber(), subCount+1);
+                if (subCount != null) {
+                    subCountMap.put(question.getMainNumber(), subCount + 1);
                     Double mainScore = mainScoreMap.get(question.getMainNumber());
-                    mainScoreMap.put(question.getMainNumber(), mainScore+question.getTotalScore());
+                    mainScoreMap.put(question.getMainNumber(), mainScore + question.getTotalScore());
                 } else {
                     subCountMap.put(question.getMainNumber(), 1);
                     mainScoreMap.put(question.getMainNumber(), question.getTotalScore());
@@ -197,17 +216,17 @@ public class SubjectQuestionDTO {
                 }
             }
         }
-        
-        Map<Integer,Integer> subCountMap1 = new HashMap<Integer, Integer>();
-        Map<Integer,Double> mainScoreMap1 = new HashMap<Integer, Double>();
+
+        Map<Integer, Integer> subCountMap1 = new HashMap<Integer, Integer>();
+        Map<Integer, Double> mainScoreMap1 = new HashMap<Integer, Double>();
         int maniCount1 = 0;
         for (ExamQuestion question : value) {
-            if(question.getTotalScore()!=0){
+            if (question.getTotalScore() != 0) {
                 Integer subCount = subCountMap1.get(question.getMainNumber());
-                if (subCount != null ) {
-                    subCountMap1.put(question.getMainNumber(), subCount+1);
+                if (subCount != null) {
+                    subCountMap1.put(question.getMainNumber(), subCount + 1);
                     Double mainScore = mainScoreMap1.get(question.getMainNumber());
-                    mainScoreMap1.put(question.getMainNumber(), mainScore+question.getTotalScore());
+                    mainScoreMap1.put(question.getMainNumber(), mainScore + question.getTotalScore());
                 } else {
                     subCountMap1.put(question.getMainNumber(), 1);
                     mainScoreMap1.put(question.getMainNumber(), question.getTotalScore());
@@ -215,14 +234,14 @@ public class SubjectQuestionDTO {
                 }
             }
         }
-        if(maniCount!=maniCount1){
+        if (maniCount != maniCount1) {
             return "大题数量不一致";
         }
-        for(Integer mainNumber : subCountMap.keySet()) {
-            if(!subCountMap.get(mainNumber).equals(subCountMap1.get(mainNumber))){
+        for (Integer mainNumber : subCountMap.keySet()) {
+            if (!subCountMap.get(mainNumber).equals(subCountMap1.get(mainNumber))) {
                 return "小题数量不一致";
             }
-            if(!mainScoreMap.get(mainNumber).equals(mainScoreMap1.get(mainNumber))){
+            if (!mainScoreMap.get(mainNumber).equals(mainScoreMap1.get(mainNumber))) {
                 return "大题总分不一致";
             }
         }

+ 19 - 6
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectiveQuestionDTO.java

@@ -31,22 +31,25 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
     @ExcelField(title = "间隔分", align = 2, sort = 70)
     private Double intervalScore;
 
-    @ExcelField(title = "图片序号(用英文逗号分割)", align = 2, sort = 80)
+    @ExcelField(title = "评卷分组(只能用小写数字)", align = 2, sort = 80)
+    private Integer groupNumber;
+
+    @ExcelField(title = "图片序号(用英文逗号分割)", align = 2, sort = 90)
     private String picList;
 
-    @ExcelField(title = "双评比例(0~1)", align = 2, sort = 90)
+    @ExcelField(title = "双评比例(0~1)", align = 2, sort = 100)
     private Double doubleRate;
 
-    @ExcelField(title = "仲裁阀值", align = 2, sort = 100)
+    @ExcelField(title = "仲裁阀值", align = 2, sort = 110)
     private Double arbitrateThreshold;
 
-    @ExcelField(title = "合分策略(1-平均,2-最高,3-最低)", align = 2, sort = 110)
+    @ExcelField(title = "合分策略(1-平均,2-最高,3-最低)", align = 2, sort = 120)
     private Integer scorePolicy;
 
-    @ExcelField(title = "评卷模式(common-普通,track-轨迹)", align = 2, sort = 120)
+    @ExcelField(title = "评卷模式(common-普通,track-轨迹)", align = 2, sort = 130)
     private String markMode;
 
-    @ExcelField(title = "试评数量(0-跳过试评)", align = 2, sort = 130)
+    @ExcelField(title = "试评数量(0-跳过试评)", align = 2, sort = 140)
     private Integer trialCount;
 
     public SubjectiveQuestionDTO() {
@@ -59,6 +62,7 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
         setMainNumber(question.getMainNumber());
         setTitle(question.getMainTitle());
         setSubNumber(question.getSubNumber());
+        setGroupNumber(question.getGroupNumber());
         setTotalScore(question.getTotalScore());
         setIntervalScore(question.getIntervalScore());
         setPicList(group != null ? group.getPicList() : "");
@@ -77,6 +81,7 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
         question.setMainTitle(StringUtils.trimToNull(title));
         question.setMainNumber(mainNumber);
         question.setSubNumber(subNumber);
+        question.setGroupNumber(groupNumber);
         question.setObjective(false);
         question.setTotalScore(totalScore);
         question.setIntervalScore(intervalScore != null && intervalScore > 0 ? intervalScore : 1d);
@@ -207,4 +212,12 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
         this.trialCount = trialCount;
     }
 
+    public Integer getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(Integer groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
 }

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

@@ -1,6 +1,7 @@
 package cn.com.qmth.stmms.admin.exam;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
@@ -21,6 +22,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import cn.com.qmth.stmms.admin.vo.ExamQuestionVO;
 import cn.com.qmth.stmms.biz.campus.model.Campus;
 import cn.com.qmth.stmms.biz.campus.service.CampusService;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
@@ -95,8 +97,8 @@ public class MarkGroupController extends BaseExamController {
         }
         List<MarkGroup> list = groupService.findByExamAndSubject(examId, subjectCode);
         for (MarkGroup group : list) {
-            group.setScoreList(questionService.findByExamAndSubjectAndObjectiveAndMainNumber(examId, subjectCode, false,
-                    group.getNumber()));
+            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode,
+                    false, group.getNumber()));
             group.setMarkerCount(markerService.countByExamAndSubjectAndGroup(examId, subjectCode, group.getNumber()));
             group.setCurrentCount(markService.applyCount(group));
             int percent = group.getLibraryCount() > 0
@@ -251,7 +253,7 @@ public class MarkGroupController extends BaseExamController {
             String pictureConfig = buildPictureConfig(group);
             group.setPicList(pictureConfig);
             model.addAttribute("group", group);
-            model.addAttribute("questions", questionService.findByExamAndSubjectAndObjectiveAndMainNumber(
+            model.addAttribute("questions", questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
                     group.getExamId(), group.getSubjectCode(), false, group.getNumber()));
             model.addAttribute("pictureConfig", pictureConfig);
             model.addAttribute("markModeList", MarkMode.values());
@@ -272,8 +274,8 @@ public class MarkGroupController extends BaseExamController {
         if (group != null) {
             String pictureConfig = buildPictureConfig(group);
             group.setPicList(pictureConfig);
-            group.setScoreList(
-                    questionService.findByExamAndSubjectAndObjectiveAndMainNumber(examId, subjectCode, false, number));
+            group.setQuestionList(
+                    questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number));
             model.addAttribute("group", group);
             model.addAttribute("pictureConfig", pictureConfig);
             model.addAttribute("markModeList", MarkMode.values());
@@ -322,7 +324,7 @@ public class MarkGroupController extends BaseExamController {
             @RequestParam(required = false) Integer trialCount, @RequestParam(required = false) boolean sheetView) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
-        List<ExamQuestion> questionList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(examId,
+        List<ExamQuestion> questionList = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
                 subjectCode, false, number);
         if (group != null) {
             // quick update
@@ -343,7 +345,7 @@ public class MarkGroupController extends BaseExamController {
             }
             title = StringUtils.trimToNull(title);
             if (title != null) {
-                groupService.updateTitle(examId, subjectCode, number, title);
+                questionService.updateMainTitle(examId, subjectCode, false, number, title);
             }
             if (doubleRate != null) {
                 groupService.updateDoubleRate(examId, subjectCode, number, doubleRate);
@@ -388,15 +390,19 @@ public class MarkGroupController extends BaseExamController {
     @RoleRequire(Role.SCHOOL_ADMIN)
     public String insert(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number,
-            @RequestParam(required = false) String title, @RequestParam(required = false) String picList,
-            @RequestParam(required = false) String scoreList, @RequestParam(required = false) Double doubleRate,
+            @RequestParam(required = false) String questionDetail, @RequestParam(required = false) String picList,
+            @RequestParam(required = false) Double doubleRate,
             @RequestParam(required = false) Double arbitrateThreshold,
             @RequestParam(required = false) Integer scorePolicy, @RequestParam(required = false) String markMode,
             @RequestParam(required = false) Integer trialCount, @RequestParam(required = false) boolean sheetView) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group != null) {
-            addMessage(redirectAttributes, "大题号不能重复");
+            addMessage(redirectAttributes, "评卷分组序号不能重复");
+            redirectAttributes.addAttribute("subjectCode", subjectCode);
+            return "redirect:/admin/exam/group/add";
+        } else if (StringUtils.isBlank(questionDetail)) {
+            addMessage(redirectAttributes, "大题详情必须设置");
             redirectAttributes.addAttribute("subjectCode", subjectCode);
             return "redirect:/admin/exam/group/add";
         } else if (StringUtils.isBlank(picList)) {
@@ -405,31 +411,42 @@ public class MarkGroupController extends BaseExamController {
             return "redirect:/admin/exam/group/add";
         } else {
             // create group
+            // build picList
             picList = StringEscapeUtils.unescapeHtml(picList);
             JSONArray array = JSONArray.fromObject(picList);
-            List<PictureConfigItem> list = JSONArray.toList(array, new PictureConfigItem(), new JsonConfig());
-            group = new MarkGroup(examId, subjectCode, number, StringUtils.trimToNull(title), list, 0d, doubleRate,
-                    arbitrateThreshold, scorePolicy, markMode, trialCount, sheetView);
-            List<Double> scores = buildDoubleList(scoreList);
-            if (group.getTitle() != null && group.getPicList() != null && scores.size() > 0) {
+            List<PictureConfigItem> picConfigList = JSONArray.toList(array, new PictureConfigItem(), new JsonConfig());
+            // build questionDetail
+            questionDetail = StringEscapeUtils.unescapeHtml(questionDetail);
+            array = JSONArray.fromObject(picList);
+            List<ExamQuestionVO> detailList = JSONArray.toList(array, new ExamQuestionVO(), new JsonConfig());
+
+            if (picConfigList != null && detailList != null && picConfigList.size() > 0 && detailList.size() > 0) {
+                group = new MarkGroup(examId, subjectCode, number, picConfigList, 0d, doubleRate, arbitrateThreshold,
+                        scorePolicy, markMode, trialCount, sheetView);
                 // clear and replace exam_question
-                questionService.deleteByExamAndSubjectAndObjectiveAndMainNumber(examId, subjectCode, false, number);
-                int i = 0;
+                questionService.deleteByExamAndSubjectAndObjectiveAndGroupNumber(examId, subjectCode, false, number);
+                List<ExamQuestion> list = new LinkedList<>();
                 double totalScore = 0d;
-                for (Double score : scores) {
-                    i++;
-                    totalScore += score;
-                    ExamQuestion question = new ExamQuestion();
-                    question.setExamId(examId);
-                    question.setSubjectCode(subjectCode);
-                    question.setMainTitle(group.getTitle());
-                    question.setMainNumber(number);
-                    question.setSubNumber(i);
-                    question.setObjective(false);
-                    question.setTotalScore(score);
-                    question.setIntervalScore(1d);
-                    questionService.save(question);
+                for (ExamQuestionVO detail : detailList) {
+                    int i = 0;
+                    List<Double> scores = buildDoubleList(detail.getScoreList());
+                    for (Double score : scores) {
+                        i++;
+                        totalScore += score;
+                        ExamQuestion question = new ExamQuestion();
+                        question.setExamId(examId);
+                        question.setSubjectCode(subjectCode);
+                        question.setMainTitle(detail.getMainTitle());
+                        question.setMainNumber(detail.getMainNumber());
+                        question.setSubNumber(i);
+                        question.setGroupNumber(number);
+                        question.setObjective(false);
+                        question.setTotalScore(score);
+                        question.setIntervalScore(1d);
+                        list.add(question);
+                    }
                 }
+                questionService.save(list);
                 group.setTotalScore(totalScore);
                 groupService.save(group);
                 subjectService.updateScore(examId, subjectCode, false, groupService.sumTotalScore(examId, subjectCode));

+ 24 - 19
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java

@@ -4,13 +4,11 @@ import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-
 import org.apache.commons.lang.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -29,7 +27,6 @@ import cn.com.qmth.stmms.admin.dto.ObjectiveQuestionDTO;
 import cn.com.qmth.stmms.admin.dto.QuestionDTO;
 import cn.com.qmth.stmms.admin.dto.SubjectQuestionDTO;
 import cn.com.qmth.stmms.admin.dto.SubjectiveQuestionDTO;
-import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
@@ -45,6 +42,8 @@ import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.ImportExcel;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
 
 @Controller("examPaperController")
 @RequestMapping("/admin/exam/paper")
@@ -58,7 +57,7 @@ public class PaperController extends BaseExamController {
 
     @Autowired
     private MarkGroupService groupService;
-    
+
     @Autowired
     private ExamService examService;
 
@@ -116,7 +115,7 @@ public class PaperController extends BaseExamController {
     }
 
     @RequestMapping(value = "/template")
-    public String importObjectiveTemplate(HttpServletResponse response, @RequestParam Boolean objective,
+    public String importTemplate(HttpServletResponse response, @RequestParam Boolean objective,
             RedirectAttributes redirectAttributes) {
         try {
             String fileName = objective ? "客观题导入模板.xlsx" : "主观题导入模板.xlsx";
@@ -153,14 +152,14 @@ public class PaperController extends BaseExamController {
         ExamQuestionSearchQuery query = new ExamQuestionSearchQuery();
         query.setExamId(examId);
         query.setObjective(objective);
-        query.setSort(new Sort(Direction.ASC, "subjectCode","paperType", "mainNumber", "subNumber"));
+        query.setSort(new Sort(Direction.ASC, "subjectCode", "paperType", "mainNumber", "subNumber"));
         query.setPageNumber(1);
         query.setPageSize(Integer.MAX_VALUE);
         query = questionService.findByQuery(query);
         for (ExamQuestion q : query.getResult()) {
             list.add(objective ? new ObjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()))
                     : new SubjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()),
-                            groupMap.get(q.getSubjectCode() + "_" + q.getMainNumber())));
+                            groupMap.get(q.getSubjectCode() + "_" + q.getGroupNumber())));
         }
         try {
             String fileName = objective ? "客观题数据.xlsx" : "主观题数据.xlsx";
@@ -179,9 +178,9 @@ public class PaperController extends BaseExamController {
     public String importFile(HttpServletRequest request, MultipartFile file, @RequestParam Boolean objective,
             RedirectAttributes redirectAttributes) {
         int examId = getSessionExamId(request);
-        Exam exam = examService.findById(examId);
-        exam.setNeedCalculate(true);//每次导入都需要重新统分
-        examService.save(exam);
+        // 每次导入都需要重新统分
+        examService.updateNeedCalculate(examId, true);
+
         List<String> error = new LinkedList<String>();
         Map<String, SubjectQuestionDTO> map = parseQuestion(file, examId, objective, error);
         if (error.isEmpty()) {
@@ -195,18 +194,24 @@ public class PaperController extends BaseExamController {
                             for (ExamQuestion question : dto.getQuestionList()) {
                                 questionService.save(question);
                             }
-                            subjectService.updateScore(examId, subject.getCode(), objective,dto.getTotalScore());
+                            subjectService.updateScore(examId, subject.getCode(), objective, dto.getTotalScore());
                         } else {
                             for (MarkGroup group : dto.getGroups().values()) {
-                            	MarkGroup old = groupService.findOne(examId, group.getSubjectCode(), group.getNumber()) ;
-                                if (old==null) {
-                                	for (ExamQuestion question : group.getQuestionList()) {
-                                		questionService.save(question);
-                                	}
+                                MarkGroup old = groupService.findOne(examId, group.getSubjectCode(), group.getNumber());
+                                // 已存在的评卷分组跳过
+                                if (old == null && group.getQuestionMap() != null) {
+                                    for (Entry<Integer, List<ExamQuestion>> entry : group.getQuestionMap().entrySet()) {
+                                        // 已存在的大题跳过
+                                        if (questionService.countByExamAndSubjectAndObjectiveAndMainNumber(examId,
+                                                group.getSubjectCode(), objective, entry.getKey()) == 0) {
+                                            questionService.save(entry.getValue());
+                                        }
+                                    }
                                     groupService.save(group);
                                 }
                             }
-                            subjectService.updateScore(examId, subject.getCode(), objective, groupService.sumTotalScore(examId, dto.getSubjectCode()));
+                            subjectService.updateScore(examId, subject.getCode(), objective,
+                                    groupService.sumTotalScore(examId, dto.getSubjectCode()));
                         }
                         success++;
                     }
@@ -252,7 +257,7 @@ public class PaperController extends BaseExamController {
         }
         return map;
     }
-    
+
     @RequestMapping("/getPaperType")
     @ResponseBody
     public JSONArray query(HttpServletRequest request, @RequestParam String subjectCode) {

+ 34 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/vo/ExamQuestionVO.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.stmms.admin.vo;
+
+public class ExamQuestionVO {
+
+    private Integer mainNumber;
+
+    private String mainTitle;
+
+    private String scoreList;
+
+    public Integer getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(Integer mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    public String getMainTitle() {
+        return mainTitle;
+    }
+
+    public void setMainTitle(String mainTitle) {
+        this.mainTitle = mainTitle;
+    }
+
+    public String getScoreList() {
+        return scoreList;
+    }
+
+    public void setScoreList(String scoreList) {
+        this.scoreList = scoreList;
+    }
+}