فهرست منبع

新增问题卷

ting.yin 5 سال پیش
والد
کامیت
16c6500de0
34فایلهای تغییر یافته به همراه1159 افزوده شده و 110 حذف شده
  1. 15 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamServiceImpl.java
  2. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkLibraryDao.java
  3. 4 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkSpecialTagDao.java
  4. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkTrackDao.java
  5. 46 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/ProblemHistoryDao.java
  6. 14 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/ProblemTypeDao.java
  7. 26 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkResult.java
  8. 234 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/ProblemHistory.java
  9. 71 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/ProblemType.java
  10. 96 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/query/ProblemHistorySearchQuery.java
  11. 42 5
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  12. 114 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/ProblemHistoryServiceImpl.java
  13. 44 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/ProblemTypeServiceImpl.java
  14. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  15. 20 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/ProblemHistoryService.java
  16. 19 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/ProblemTypeService.java
  17. 2 1
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/LibraryStatus.java
  18. 3 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java
  19. 112 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ProblemTypeController.java
  20. 21 5
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  21. 0 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examIndex.jsp
  22. 1 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examList.jsp
  23. 2 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/libraryList.jsp
  24. 49 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/problemEdit.jsp
  25. 48 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/problemForm.jsp
  26. 65 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/problemList.jsp
  27. 6 4
      stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markNew.jsp
  28. 6 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markTrack.jsp
  29. 14 12
      stmms-web/src/main/webapp/sql/stmms_ft.sql
  30. 3 1
      stmms-web/src/main/webapp/static/mark-new/js/mark-control.js
  31. 6 0
      stmms-web/src/main/webapp/static/mark-new/js/modules/mark-history.js
  32. 64 60
      stmms-web/src/main/webapp/static/mark-new/js/modules/problem-process.js
  33. 3 1
      stmms-web/src/main/webapp/static/mark-new/js/modules/warning-info.js
  34. 3 1
      stmms-web/src/main/webapp/static/mark-track/js/mark-control.js

+ 15 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamServiceImpl.java

@@ -22,6 +22,8 @@ import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.query.ExamSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
+import cn.com.qmth.stmms.biz.mark.service.ProblemTypeService;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
 
 @Service
@@ -30,6 +32,9 @@ public class ExamServiceImpl extends BaseQueryService<Exam> implements ExamServi
     @Autowired
     private ExamDao examDao;
 
+    @Autowired
+    private ProblemTypeService problemService;
+
     @Transactional
     // @CachePut(value = "exam_cache", key = "#exam.id", condition =
     // "#exam!=null && #exam.id!=null")
@@ -37,10 +42,16 @@ public class ExamServiceImpl extends BaseQueryService<Exam> implements ExamServi
         Integer id = exam.getId();
         if (id != null) {
             exam.setUpdateTime(new Date());
+            examDao.save(exam);
         } else {
             exam.setCreateTime(new Date());
+            examDao.save(exam);
+            ProblemType p1 = new ProblemType(exam.getId(), "空白卷", false);
+            ProblemType p2 = new ProblemType(exam.getId(), "雷同卷", false);
+            problemService.save(p1);
+            problemService.save(p2);
         }
-        return examDao.save(exam);
+        return exam;
     }
 
     // @Cacheable(value = "exam_cache", key = "#id", condition = "#id!=null")
@@ -125,8 +136,8 @@ public class ExamServiceImpl extends BaseQueryService<Exam> implements ExamServi
                     }
                 }
 
-                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()]));
             }
         }, query);
         fillResult(result, query);
@@ -151,7 +162,7 @@ public class ExamServiceImpl extends BaseQueryService<Exam> implements ExamServi
     @Transactional
     @Override
     public void updateProcess(Integer id, Double process) {
-        examDao.updateProcess(id,process);
+        examDao.updateProcess(id, process);
     }
 
 }

+ 2 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkLibraryDao.java

@@ -79,9 +79,9 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
 
     @Modifying(clearAutomatically = true)
     @Query("update MarkLibrary m set m.status=?2, m.tags=null, m.markerId=null, m.markerTime=null, m.markerScore=null, m.markerScoreList=null, m.markerSpent=null, "
-            + "m.headerId=null , m.headerTime=null , m.headerScore=null , m.headerScoreList=null  where m.markerId=?1 and m.status!=?3 and m.status!=?4 ")
+            + "m.headerId=null , m.headerTime=null , m.headerScore=null , m.headerScoreList=null  where m.markerId=?1 and m.status!=?3 and m.status not in (?4) ")
     void resetByMarkerId(Integer markerId, LibraryStatus status, LibraryStatus libraryStatus1,
-            LibraryStatus libraryStatus2);
+            LibraryStatus... libraryStatus);
 
     @Modifying(clearAutomatically = true)
     @Query("update MarkLibrary m set m.status=?2, m.tags=null, m.markerId=null, m.markerTime=null, m.markerScore=null, m.markerScoreList=null, m.markerSpent=null, "

+ 4 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkSpecialTagDao.java

@@ -10,8 +10,8 @@ import org.springframework.data.repository.PagingAndSortingRepository;
 import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 
-public interface MarkSpecialTagDao
-        extends PagingAndSortingRepository<MarkSpecialTag, Integer>, JpaSpecificationExecutor<MarkSpecialTag> {
+public interface MarkSpecialTagDao extends PagingAndSortingRepository<MarkSpecialTag, Integer>,
+        JpaSpecificationExecutor<MarkSpecialTag> {
 
     @Modifying
     @Query("delete from MarkSpecialTag s where s.libraryId = ?1")
@@ -25,8 +25,8 @@ public interface MarkSpecialTagDao
     public List<MarkSpecialTag> findByLibraryIdOrderByIdAsc(Integer libraryId);
 
     @Modifying
-    @Query("delete from MarkSpecialTag s where s.libraryId in (select m.id from MarkLibrary m where m.markerId=?1  and m.status!=?2 and m.status!=?3 ) ")
-    public void deleteByMarkerId(Integer markerId, LibraryStatus arbitrated, LibraryStatus waitArbitrate);
+    @Query("delete from MarkSpecialTag s where s.libraryId in (select m.id from MarkLibrary m where m.markerId=?1  and m.status!=?2 and m.status not in (?3) ) ")
+    public void deleteByMarkerId(Integer markerId, LibraryStatus arbitrated, LibraryStatus... waitArbitrate);
 
     @Modifying
     @Query("delete from MarkSpecialTag s where s.libraryId in (select m.id from MarkLibrary m where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3)")

+ 2 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkTrackDao.java

@@ -26,8 +26,8 @@ public interface MarkTrackDao extends JpaRepository<MarkTrack, MarkTrackPK>, Jpa
     void deleteByStudentId(Integer studentId);
 
     @Modifying
-    @Query("delete from MarkTrack t where t.pk.libraryId in (select m.id from MarkLibrary m where m.markerId=?1  and m.status!=?2 and m.status!=?3 )")
-    void deleteByMarkerId(Integer markerId, LibraryStatus arbitrated, LibraryStatus waitArbitrate);
+    @Query("delete from MarkTrack t where t.pk.libraryId in (select m.id from MarkLibrary m where m.markerId=?1  and m.status!=?2 and m.status not in (?3) )")
+    void deleteByMarkerId(Integer markerId, LibraryStatus arbitrated, LibraryStatus... waitArbitrate);
 
     @Modifying
     @Query("delete from MarkTrack t where t.examId=?1 and t.subjectCode=?2 and t.groupNumber=?3")

+ 46 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/ProblemHistoryDao.java

@@ -0,0 +1,46 @@
+package cn.com.qmth.stmms.biz.mark.dao;
+
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
+import cn.com.qmth.stmms.common.enums.HistoryStatus;
+
+public interface ProblemHistoryDao extends JpaRepository<ProblemHistory, Integer>,
+        JpaSpecificationExecutor<ProblemHistory> {
+
+    List<ProblemHistory> findByExamIdAndSubjectCode(Integer examId, String subjectCode, Pageable page);
+
+    List<ProblemHistory> findByExamIdAndSubjectCodeAndStatus(Integer examId, String subjectCode, HistoryStatus status,
+            Pageable page);
+
+    List<ProblemHistory> findByExamIdAndSubjectCodeAndStatusIn(Integer examId, String subjectCode,
+            Set<HistoryStatus> statusSet, Pageable page);
+
+    @Modifying
+    @Query("delete ProblemHistory m where m.studentId=?1")
+    void deleteByStudentId(Integer studentId);
+
+    @Query("select count(s) from ProblemHistory s where s.examId=?1 and s.problemId=?2 ")
+    long countByExamIdAndProblemId(Integer examId, Integer problemId);
+
+    @Query("select distinct l.subjectCode from ProblemHistory l where l.examId=?1 ")
+    List<String> findProblemSubjectCode(Integer examId);
+
+    ProblemHistory findByStudentIdAndStatus(Integer studentId, HistoryStatus status);
+
+    @Modifying
+    @Query("delete from ProblemHistory s where s.studentId in (select m.studentId from MarkLibrary m where m.markerId=?1 ) ")
+    void deleteByMarkerId(Integer markerId);
+
+    @Modifying
+    @Query("delete from ProblemHistory s where s.examId=?1 and s.subjectCode=?2 and s.groupNumber=?3  ")
+    void deleteByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber);
+
+}

+ 14 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/ProblemTypeDao.java

@@ -0,0 +1,14 @@
+package cn.com.qmth.stmms.biz.mark.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
+
+public interface ProblemTypeDao extends PagingAndSortingRepository<ProblemType, Integer>, JpaSpecificationExecutor<ProblemType> {
+
+    List<ProblemType> findByExamId(Integer examId);
+
+}

+ 26 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkResult.java

@@ -55,6 +55,16 @@ public class MarkResult {
      */
     private int spent;
 
+    /**
+     * 是否问题卷
+     */
+    private boolean isProblem;
+
+    /**
+     * 问题类型
+     */
+    private int reason;
+
     public int getStatusValue() {
         return statusValue;
     }
@@ -191,4 +201,20 @@ public class MarkResult {
         return list;
     }
 
+    public boolean isProblem() {
+        return isProblem;
+    }
+
+    public void setProblem(boolean isProblem) {
+        this.isProblem = isProblem;
+    }
+
+    public int getReason() {
+        return reason;
+    }
+
+    public void setReason(int reason) {
+        this.reason = reason;
+    }
+
 }

+ 234 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/ProblemHistory.java

@@ -0,0 +1,234 @@
+package cn.com.qmth.stmms.biz.mark.model;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.enums.HistoryStatus;
+
+/**
+ * 问题卷记录表
+ * 
+ */
+@Entity
+@Table(name = "m_problem_history")
+public class ProblemHistory implements Serializable {
+
+    private static final long serialVersionUID = 778358337221751946L;
+
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    /**
+     * 考试ID
+     */
+    @Column(name = "exam_id")
+    private Integer examId;
+
+    /**
+     * 科目CODE
+     */
+    @Column(name = "subject_code")
+    private String subjectCode;
+
+    /**
+     * 大题序号
+     */
+    @Column(name = "group_number")
+    private Integer groupNumber;
+
+    /**
+     * 问题ID
+     */
+    @Column(name = "problem_id")
+    private Integer problemId;
+
+    /**
+     * 考生编号
+     */
+    @Column(name = "student_id")
+    private Integer studentId;
+
+    /**
+     * 任务编号
+     */
+    @Column(name = "library_id")
+    private Integer libraryId;
+
+    /**
+     * 准考证号
+     */
+    @Column(name = "exam_number")
+    private String examNumber;
+
+    /**
+     * 处理用户ID
+     */
+    @Column(name = "user_id", nullable = true)
+    private Integer userId;
+
+    /**
+     * 给分总分
+     */
+    @Column(name = "total_score", nullable = true)
+    private Double totalScore;
+
+    /**
+     * 给分明细
+     */
+    @Column(name = "score_list", nullable = true, length = 255)
+    private String scoreList;
+
+    /**
+     * 状态
+     */
+    @Column(name = "status")
+    @Enumerated(EnumType.STRING)
+    private HistoryStatus status;
+
+    /**
+     * 处理时间
+     */
+    @Column(name = "update_time", nullable = true)
+    private Date updateTime;
+
+    /**
+     * 创建时间
+     */
+    @Column(name = "create_time", nullable = false)
+    private Date createTime;
+
+    @Transient
+    private User user;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Integer examId) {
+        this.examId = examId;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public Integer getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Integer studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public String getScoreList() {
+        return scoreList;
+    }
+
+    public void setScoreList(String scoreList) {
+        this.scoreList = scoreList;
+    }
+
+    public HistoryStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(HistoryStatus status) {
+        this.status = status;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Integer getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Integer problemId) {
+        this.problemId = problemId;
+    }
+
+    public Integer getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(Integer groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
+    public Integer getLibraryId() {
+        return libraryId;
+    }
+
+    public void setLibraryId(Integer libraryId) {
+        this.libraryId = libraryId;
+    }
+
+}

+ 71 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/ProblemType.java

@@ -0,0 +1,71 @@
+package cn.com.qmth.stmms.biz.mark.model;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "m_problem_type")
+public class ProblemType implements Serializable {
+
+    private static final long serialVersionUID = 97815264735540151L;
+
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    private String name;
+
+    @Column(name = "exam_id")
+    private Integer examId;
+
+    @Column(name = "is_custom")
+    private boolean custom;
+
+    public ProblemType(Integer examId, String name, boolean custom) {
+        this.examId = examId;
+        this.name = name;
+        this.custom = custom;
+    }
+
+    public ProblemType() {
+
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Integer examId) {
+        this.examId = examId;
+    }
+
+    public boolean isCustom() {
+        return custom;
+    }
+
+    public void setCustom(boolean custom) {
+        this.custom = custom;
+    }
+
+}

+ 96 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/query/ProblemHistorySearchQuery.java

@@ -0,0 +1,96 @@
+package cn.com.qmth.stmms.biz.mark.query;
+
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+
+import cn.com.qmth.stmms.biz.common.BaseQuery;
+import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
+import cn.com.qmth.stmms.common.enums.HistoryStatus;
+
+public class ProblemHistorySearchQuery extends BaseQuery<ProblemHistory> {
+
+    private int examId;
+
+    private String subjectCode;
+
+    private Integer studentId;
+
+    private Integer problemId;
+
+    private String examNumber;
+
+    private int groupNumber;
+
+    private HistoryStatus status;
+
+    private int userId;
+
+    public void orderByExamNumber() {
+        setSort(new Sort(Direction.ASC, "examNumber"));
+    }
+
+    public int getExamId() {
+        return examId;
+    }
+
+    public void setExamId(int examId) {
+        this.examId = examId;
+    }
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public Integer getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Integer studentId) {
+        this.studentId = studentId;
+    }
+
+    public Integer getProblemId() {
+        return problemId;
+    }
+
+    public void setProblemId(Integer problemId) {
+        this.problemId = problemId;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public HistoryStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(HistoryStatus status) {
+        this.status = status;
+    }
+
+    public int getUserId() {
+        return userId;
+    }
+
+    public void setUserId(int userId) {
+        this.userId = userId;
+    }
+
+    public int getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(int groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
+}

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

@@ -29,6 +29,7 @@ import cn.com.qmth.stmms.biz.mark.dao.ArbitrateHistoryDao;
 import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
 import cn.com.qmth.stmms.biz.mark.dao.MarkSpecialTagDao;
 import cn.com.qmth.stmms.biz.mark.dao.MarkTrackDao;
+import cn.com.qmth.stmms.biz.mark.dao.ProblemHistoryDao;
 import cn.com.qmth.stmms.biz.mark.dao.TrialHistoryDao;
 import cn.com.qmth.stmms.biz.mark.dao.TrialLibraryDao;
 import cn.com.qmth.stmms.biz.mark.dao.TrialTagDao;
@@ -37,9 +38,11 @@ import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.model.MarkResult;
 import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
+import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
 import cn.com.qmth.stmms.biz.mark.model.TrialHistory;
 import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.biz.mark.service.ProblemHistoryService;
 import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
@@ -102,6 +105,12 @@ public class MarkServiceImpl implements MarkService {
     @Autowired
     private ExamService examService;
 
+    @Autowired
+    private ProblemHistoryService problemHistoryService;
+
+    @Autowired
+    private ProblemHistoryDao problemHistoryDao;
+
     /**
      * 某个大题已申请的评卷任务数量
      * 
@@ -213,6 +222,8 @@ public class MarkServiceImpl implements MarkService {
         specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
         arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 group.getNumber());
+        problemHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
         libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 group.getNumber());
         // 评卷员数据
@@ -355,10 +366,12 @@ public class MarkServiceImpl implements MarkService {
         }
         if (group.getStatus() == MarkStatus.FORMAL) {
             // 仲裁和等待仲裁的任务不被重置
-            trackDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
-            specialTagDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
+            trackDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE,
+                    LibraryStatus.PROBLEM);
+            specialTagDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE,
+                    LibraryStatus.PROBLEM);
             libraryDao.resetByMarkerId(marker.getId(), LibraryStatus.WAITING, LibraryStatus.ARBITRATED,
-                    LibraryStatus.WAIT_ARBITRATE);
+                    LibraryStatus.WAIT_ARBITRATE, LibraryStatus.PROBLEM);
             markerDao.resetById(marker.getId());
         } else if (group.getStatus() == MarkStatus.TRIAL) {
             trialTagDao.deleteByMarkerId(marker.getId());
@@ -381,6 +394,7 @@ public class MarkServiceImpl implements MarkService {
         trackDao.deleteByStudentId(student.getId());
         specialTagDao.deleteByStudentId(student.getId());
         arbitrateDao.deleteByStudentId(student.getId());
+        problemHistoryDao.deleteByStudentId(student.getId());
         libraryDao.deleteByStudentId(student.getId());
         // 试评相关数据
         trialTrackDao.deleteByStudentId(student.getId());
@@ -411,6 +425,25 @@ public class MarkServiceImpl implements MarkService {
         // 根据评卷状态选择读取不同的评卷任务
         if (group.getStatus() == MarkStatus.FORMAL) {
             MarkLibrary library = libraryDao.findOne(result.getLibraryId());
+            if (result.isProblem()) {
+                ProblemHistory history = new ProblemHistory();
+                history.setCreateTime(new Date());
+                history.setExamId(library.getExamId());
+                history.setExamNumber(library.getExamNumber());
+                history.setStudentId(library.getStudentId());
+                history.setLibraryId(library.getId());
+                history.setSubjectCode(library.getSubjectCode());
+                history.setGroupNumber(library.getGroupNumber());
+                history.setProblemId(result.getReason());
+                history.setStatus(HistoryStatus.WAITING);
+                problemHistoryService.save(history);
+                // 状态更新
+                libraryDao.updateByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber(),
+                        LibraryStatus.PROBLEM);
+                updateMarkedCount(group);
+                releaseLibrary(library, marker);
+                return true;
+            }
             if (library != null && library.getExamId().equals(group.getExamId())
                     && library.getSubjectCode().equals(group.getSubjectCode())
                     && library.getGroupNumber().equals(group.getNumber())
@@ -597,15 +630,17 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     @Transactional
-    public boolean backLibrary(MarkLibrary library) {
+    public boolean backLibrary(MarkLibrary library, Integer userId) {
         MarkGroup group = groupDao.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
         if (group.getStatus() == MarkStatus.FINISH) {
             return false;
         }
-        if (libraryDao.resetById(library.getId(), LibraryStatus.WAITING, LibraryStatus.MARKED, LibraryStatus.INSPECTED) > 0) {
+        if (libraryDao.resetById(library.getId(), LibraryStatus.WAITING, LibraryStatus.MARKED, LibraryStatus.INSPECTED,
+                LibraryStatus.PROBLEM) > 0) {
             trackDao.deleteByLibraryId(library.getId());
             specialTagDao.deleteByLibraryId(library.getId());
             updateMarkedCount(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
+            problemHistoryService.resetByStudentId(library.getStudentId(), userId);
             return true;
         } else {
             return false;
@@ -959,6 +994,8 @@ public class MarkServiceImpl implements MarkService {
             specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
             arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                     group.getNumber());
+            problemHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                    group.getNumber());
             libraryDao.resetByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                     group.getNumber(), LibraryStatus.WAITING);
             libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumberAndTaskNumber(group.getExamId(),

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

@@ -0,0 +1,114 @@
+package cn.com.qmth.stmms.biz.mark.service.Impl;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+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.BaseQueryService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.mark.dao.ProblemHistoryDao;
+import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
+import cn.com.qmth.stmms.biz.mark.query.ProblemHistorySearchQuery;
+import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
+import cn.com.qmth.stmms.biz.mark.service.ProblemHistoryService;
+import cn.com.qmth.stmms.common.enums.HistoryStatus;
+
+@Service
+public class ProblemHistoryServiceImpl extends BaseQueryService<ProblemHistory> implements ProblemHistoryService {
+
+    @Autowired
+    private ProblemHistoryDao historyDao;
+
+    @Autowired
+    private MarkLibraryService libraryService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Override
+    public ProblemHistory save(ProblemHistory p) {
+        return historyDao.save(p);
+    }
+
+    @Override
+    public long countByExamIdAndProblemId(Integer examId, Integer problemId) {
+        return historyDao.countByExamIdAndProblemId(examId, problemId);
+    }
+
+    @Override
+    public List<String> findProblemSubjectCode(Integer examId) {
+        return historyDao.findProblemSubjectCode(examId);
+    }
+
+    @Transactional
+    @Override
+    public boolean resetByStudentId(Integer studentId, Integer userId) {
+        ProblemHistory history = historyDao.findByStudentIdAndStatus(studentId, HistoryStatus.WAITING);
+        if (history != null) {
+            history.setUserId(userId);
+            history.setUpdateTime(new Date());
+            history.setStatus(HistoryStatus.BACK);
+            historyDao.save(history);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public ProblemHistorySearchQuery findByQuery(final ProblemHistorySearchQuery query) {
+        checkQuery(query);
+        Page<ProblemHistory> result = historyDao.findAll(buildSpecification(query), query);
+        fillResult(result, query);
+        return query;
+    }
+
+    private Specification<ProblemHistory> buildSpecification(final ProblemHistorySearchQuery query) {
+        return new Specification<ProblemHistory>() {
+
+            @Override
+            public Predicate toPredicate(Root<ProblemHistory> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
+                List<Predicate> predicates = new LinkedList<Predicate>();
+                if (query.getExamId() > 0) {
+                    predicates.add(cb.equal(root.get("examId"), query.getExamId()));
+                }
+                if (StringUtils.isNotBlank(query.getSubjectCode())) {
+                    predicates.add(cb.equal(root.get("subjectCode"), query.getSubjectCode()));
+                }
+                if (StringUtils.isNotBlank(query.getExamNumber())) {
+                    predicates.add(cb.equal(root.get("examNumber"), query.getExamNumber()));
+                }
+                if (query.getStudentId() != null) {
+                    predicates.add(cb.equal(root.get("studentId"), query.getStudentId()));
+                }
+                if (query.getUserId() > 0) {
+                    predicates.add(cb.equal(root.get("userId"), query.getUserId()));
+                }
+                if (query.getStatus() != null) {
+                    predicates.add(cb.equal(root.get("status").as(HistoryStatus.class), query.getStatus()));
+                }
+                if (query.getProblemId() != null) {
+                    predicates.add(cb.equal(root.get("problemId"), query.getProblemId()));
+                }
+                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
+                        .size()]));
+            }
+        };
+    }
+
+}

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

@@ -0,0 +1,44 @@
+package cn.com.qmth.stmms.biz.mark.service.Impl;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.stmms.biz.common.BaseQueryService;
+import cn.com.qmth.stmms.biz.mark.dao.ProblemTypeDao;
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
+import cn.com.qmth.stmms.biz.mark.service.ProblemTypeService;
+
+@Service
+public class ProblemTypeServiceImpl extends BaseQueryService<ProblemType> implements ProblemTypeService {
+
+    @Autowired
+    private ProblemTypeDao problemDao;
+
+    @Override
+    public ProblemType save(ProblemType p) {
+        return problemDao.save(p);
+    }
+
+    @Override
+    public ProblemType findById(Integer id) {
+        return problemDao.findOne(id);
+    }
+
+    @Override
+    public void deleteById(Integer id) {
+        problemDao.delete(id);
+    }
+
+    @Override
+    public void delete(ProblemType p) {
+        problemDao.delete(p);
+    }
+
+    @Override
+    public List<ProblemType> findByExamId(Integer examId) {
+        return problemDao.findByExamId(examId);
+    }
+
+}

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

@@ -58,7 +58,7 @@ public interface MarkService {
      * 修改并重置某个大题
      * 
      * @param group
-     * @param third 
+     * @param third
      */
     void updateGroup(MarkGroup group, List<ExamQuestion> questionList, ScorePolicy policy, ThirdPolicy third);
 
@@ -140,7 +140,7 @@ public interface MarkService {
      * @param library
      * @return
      */
-    boolean backLibrary(MarkLibrary library);
+    boolean backLibrary(MarkLibrary library, Integer userId);
 
     /**
      * 更新某个大题已评任务数量

+ 20 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/ProblemHistoryService.java

@@ -0,0 +1,20 @@
+package cn.com.qmth.stmms.biz.mark.service;
+
+import java.util.List;
+
+import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
+import cn.com.qmth.stmms.biz.mark.query.ProblemHistorySearchQuery;
+
+public interface ProblemHistoryService {
+
+    ProblemHistory save(ProblemHistory history);
+
+    long countByExamIdAndProblemId(Integer examId, Integer problemId);
+
+    List<String> findProblemSubjectCode(Integer examId);
+
+    ProblemHistorySearchQuery findByQuery(ProblemHistorySearchQuery query);
+
+    boolean resetByStudentId(Integer studentId, Integer userId);
+
+}

+ 19 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/ProblemTypeService.java

@@ -0,0 +1,19 @@
+package cn.com.qmth.stmms.biz.mark.service;
+
+import java.util.List;
+
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
+
+public interface ProblemTypeService {
+
+    ProblemType save(ProblemType p);
+
+    ProblemType findById(Integer id);
+
+    void deleteById(Integer id);
+
+    void delete(ProblemType p);
+
+    List<ProblemType> findByExamId(Integer examId);
+
+}

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

@@ -9,7 +9,8 @@ public enum LibraryStatus {
     BACKED("已打回", 2), 
     WAIT_ARBITRATE("等待仲裁", 3), 
     ARBITRATED("已仲裁", 4),
-    INSPECTED("已复核",5);
+    INSPECTED("已复核",5),
+    PROBLEM("问题卷",6);
 
     private String name;
 

+ 3 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java

@@ -119,12 +119,14 @@ public class LibraryController extends BaseExamController {
     public JSONObject back(HttpServletRequest request, @RequestParam Integer id) {
         JSONObject obj = new JSONObject();
         MarkLibrary library = libraryService.findById(id);
+        WebUser wu = RequestUtils.getWebUser(request);
         if (library != null) {
             if (subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
                 try {
                     lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
                             library.getGroupNumber());
-                    if (library.getStatus().equals(LibraryStatus.MARKED) && markService.backLibrary(library)) {
+                    if (library.getStatus().equals(LibraryStatus.MARKED)
+                            && markService.backLibrary(library, wu.getId())) {
                         obj.accumulate("success", true);
                     } else {
                         obj.accumulate("success", false);

+ 112 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ProblemTypeController.java

@@ -0,0 +1,112 @@
+package cn.com.qmth.stmms.admin.exam;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
+import cn.com.qmth.stmms.biz.mark.service.ProblemHistoryService;
+import cn.com.qmth.stmms.biz.mark.service.ProblemTypeService;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.session.model.StmmsSession;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+@Controller
+@RequestMapping("/admin/exam/problem/type")
+public class ProblemTypeController extends BaseExamController {
+
+    protected static Logger log = LoggerFactory.getLogger(ProblemTypeController.class);
+
+    @Autowired
+    private ProblemTypeService problemService;
+
+    @Autowired
+    private ProblemHistoryService historyService;
+
+    @RequestMapping
+    public String list(HttpServletRequest request, Model model, @RequestParam(required = false) Integer examId) {
+        if (examId != null) {
+            StmmsSession session = RequestUtils.getSession(request);
+            session.setParameter("examId", examId.toString());
+        }
+        examId = getSessionExamId(request);
+        List<ProblemType> list = problemService.findByExamId(examId);
+        model.addAttribute("list", list);
+        return "modules/exam/problemList";
+    }
+
+    @RequestMapping("/add")
+    public String add(ProblemType problem, Model model) {
+        model.addAttribute("problem", problem);
+        return "modules/exam/problemForm";
+    }
+
+    @RequestMapping("/save")
+    public String save(HttpServletRequest request, ProblemType problem, RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+        List<ProblemType> list = problemService.findByExamId(examId);
+        if (list.size() >= 10) {
+            addMessage(redirectAttributes, "问题卷类型不能超过10个");
+            return "redirect:/admin/exam/problem/type";
+        }
+        if (!StringUtils.isNotBlank(problem.getName())) {
+            addMessage(redirectAttributes, "名称不能为空");
+            return "redirect:/admin/exam/problem/type";
+        }
+        problem.setExamId(examId);
+        problem.setCustom(true);
+        problem = problemService.save(problem);
+        addMessage(redirectAttributes, "创建'" + problem.getName() + "'成功");
+        return "redirect:/admin/exam/problem/type";
+    }
+
+    @RequestMapping("/edit/{id}")
+    public String edit(@PathVariable Integer id, Model model) {
+        ProblemType problem = problemService.findById(id);
+        model.addAttribute("problem", problem);
+        return "modules/exam/problemEdit";
+    }
+
+    @RequestMapping(value = "/edit", method = RequestMethod.POST)
+    public String examEdit(HttpServletRequest request, ProblemType problem) {
+        WebUser user = RequestUtils.getWebUser(request);
+        ProblemType old = problemService.findById(problem.getId());
+        if (user.isSchoolAdmin() && StringUtils.isNotBlank(problem.getName())) {
+            old.setName(problem.getName());
+            problemService.save(old);
+        }
+        return "redirect:/admin/exam/problem/type";
+    }
+
+    @RequestMapping(value = "/delete", method = RequestMethod.POST)
+    @ResponseBody
+    public Object delete(HttpServletRequest request, RedirectAttributes redirectAttributes,
+            @RequestParam Integer problemId) {
+        int examId = getSessionExamId(request);
+        JSONObject result = new JSONObject();
+        long historyCount = historyService.countByExamIdAndProblemId(examId, problemId);
+        if (historyCount > 0) {
+            result.accumulate("success", false);
+            result.accumulate("message", "该问题卷类型已被使用,不可删除");
+        } else {
+            problemService.deleteById(problemId);
+            result.accumulate("success", true);
+        }
+        return result;
+    }
+}

+ 21 - 5
stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java

@@ -6,6 +6,9 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
 import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
@@ -22,9 +25,6 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.servlet.ModelAndView;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
@@ -39,12 +39,14 @@ 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.MarkResult;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
+import cn.com.qmth.stmms.biz.mark.model.ProblemType;
 import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.model.TrialHistory;
 import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
 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.ProblemTypeService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.mark.service.TrialService;
 import cn.com.qmth.stmms.common.controller.BaseController;
@@ -53,8 +55,9 @@ import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkMode;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 @Controller
 @RequestMapping("/mark")
@@ -92,6 +95,9 @@ public class MarkController extends BaseController {
     @Autowired
     private LockService lockService;
 
+    @Autowired
+    private ProblemTypeService problemTypeService;
+
     @Value("${slice.image.server}")
     private String sliceServer;
 
@@ -160,6 +166,7 @@ public class MarkController extends BaseController {
         ModelAndView view = new ModelAndView(mode == MarkMode.TRACK ? "modules/mark/markTrack" : "modules/mark/markNew");
         view.addObject("forceMode", forceMode);
         view.addObject("sheetView", group.isSheetView());
+        view.addObject("isFormal", group.getStatus() == MarkStatus.FORMAL);
         return view;
     }
 
@@ -195,6 +202,14 @@ public class MarkController extends BaseController {
         }
         modelAndView.addObject("sheetConfig", sheetConfig);
         releaseMarker(marker);
+
+        List<ProblemType> problemTypes = problemTypeService.findByExamId(marker.getExamId());
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            modelAndView.addObject("problemTypes", mapper.writeValueAsString(problemTypes));
+        } catch (JsonProcessingException e) {
+            log.error("MarkController-问题类型获取出错", e);
+        }
     }
 
     @RequestMapping("/clear")
@@ -239,6 +254,7 @@ public class MarkController extends BaseController {
 
             query.clearStatus();
             query.addStatus(LibraryStatus.WAIT_ARBITRATE);
+            query.addStatus(LibraryStatus.PROBLEM);
             exceptionCount = libraryService.countByQuery(query);
         } else if (group.getStatus() == MarkStatus.TRIAL) {
             totalCount = trialService.countLibrary(group.getExamId(), group.getSubjectCode(), group.getNumber());

+ 0 - 2
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examIndex.jsp

@@ -96,7 +96,6 @@
 											<li><a href="${ctx}/admin/exam/mark" target="mainFrame" ><i class="icon-pencil"></i>评卷管理</a></li>
 											<li><a href="${ctx}/admin/exam/score" target="mainFrame" ><i class="icon-search"></i>成绩查询</a></li>
 											<li><a href="${ctx}/admin/exam/reportSubjectRange" target="mainFrame" ><i class="icon-search"></i>成绩分析</a></li>
-											<li><a href="${ctx}/admin/exam/tag" target="mainFrame" ><i class="icon-tag"></i>标记试卷</a></li>	
 											<li><a href="${ctx}/admin/exam/check/answer" target="mainFrame" ><i class="icon-check"></i>数据检查</a></li>			
 											</c:if>
 											
@@ -106,7 +105,6 @@
                                             <li><a href="${ctx}/admin/exam/mark" target="mainFrame" ><i class="icon-pencil"></i>评卷管理</a></li>
                                             <li><a href="${ctx}/admin/exam/score" target="mainFrame" ><i class="icon-search"></i>成绩查询</a></li> 
                                             <li><a href="${ctx}/admin/exam/reportSubjectRange" target="mainFrame" ><i class="icon-search"></i>成绩分析</a></li>     
-                                            <li><a href="${ctx}/admin/exam/tag" target="mainFrame" ><i class="icon-tag"></i>标记试卷</a></li>         
                                             </c:if>
                                             
                                             <c:if test="${web_user.schoolViewer==true}">

+ 1 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examList.jsp

@@ -56,6 +56,7 @@
     				<c:if test="${web_user.schoolAdmin==true}">
     				&nbsp;
     				<a href="${ctx}/admin/exam-edit/${exam.id}">编辑</a>
+    				<a href="${ctx}/admin/exam/problem/type?examId=${exam.id}">问题卷分类</a>
     				</c:if>
 				</td>
 			</tr>

+ 2 - 2
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/libraryList.jsp

@@ -120,10 +120,10 @@
                     </c:if>
 				</td>
 				<td>
-				    <c:if test="${result.status.value==1 || result.status.value==3 ||result.status.value==5}">
+				    <c:if test="${result.status.value==1 || result.status.value==3 ||result.status.value==5 ||result.status.value==6}">
 				    <a class="track-link" href="##" data-image-url="${ctx}/admin/exam/track/byLibrary?libraryId=${result.id}" data-title="${result.examNumber}">阅卷轨迹</a>
 				    </c:if>
-				    <c:if test="${result.status.value==1 || result.status.value==5}">
+				    <c:if test="${result.status.value==1 || result.status.value==5 ||result.status.value==6}">
 				    &nbsp;
                     <a href="##" data-id="${result.id}" class="back-link">打回</a>
 					</c:if>

+ 49 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/problemEdit.jsp

@@ -0,0 +1,49 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<html>
+<head>
+	<title>考试管理</title>
+	<meta name="decorator" content="default"/>
+	<%@include file="/WEB-INF/views/include/head.jsp" %>
+	<script type="text/javascript">
+		$(document).ready(function() {
+			$("#name").focus();
+			$("#inputForm").validate({
+				submitHandler: function(form){
+						loading('正在提交,请稍等...');
+						form.submit();
+				},
+				errorContainer: "#messageBox",
+				errorPlacement: function(error, element) {
+					$("#messageBox").text("输入有误,请先更正。");
+					if (element.is(":checkbox")||element.is(":radio")||element.parent().is(".input-append")){
+						error.appendTo(element.parent().parent());
+					} else {
+						error.insertAfter(element);
+					}
+				}
+			});
+		});
+	</script>
+</head>
+<body>
+	<ul class="nav nav-tabs">
+		<li><a href="${ctx}/admin/exam/problem/type">分类列表</a></li>
+		<li class="active"><a href="${ctx}/admin/exam/problem/type/edit/${problem.id}">编辑分类</a></li>
+	</ul><br/>
+	<form:form id="inputForm" modelAttribute="problem" action="${ctx}/admin/exam/problem/type/edit" method="post" class="form-horizontal">
+		<form:hidden path="id"/>
+		<tags:message content="${message}"/>
+		<div class="control-group">
+			<label class="control-label">名称:</label>
+			<div class="controls">
+				<form:input path="name" htmlEscape="false" maxlength="20" class="required"/>*20字以内
+			</div>
+		</div>
+		<div class="form-actions">
+			<input id="btnSubmit" class="btn btn-primary" type="submit" value="保 存"/>&nbsp;
+			<input id="btnCancel" class="btn" type="button" value="返 回" onclick="history.go(-1)"/>
+		</div>
+	</form:form>
+</body>
+</html>

+ 48 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/problemForm.jsp

@@ -0,0 +1,48 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<html>
+<head>
+	<title>问题卷分类管理</title>
+	<meta name="decorator" content="default"/>
+	<%@include file="/WEB-INF/views/include/head.jsp" %>
+	<script type="text/javascript">
+		$(document).ready(function() {
+			$("#name").focus();
+			$("#inputForm").validate({
+				submitHandler: function(form){
+					loading('正在提交,请稍等...');
+					form.submit();
+				},
+				errorContainer: "#messageBox",
+				errorPlacement: function(error, element) {
+					$("#messageBox").text("输入有误,请先更正。");
+					if (element.is(":checkbox")||element.is(":radio")||element.parent().is(".input-append")){
+						error.appendTo(element.parent().parent());
+					} else {
+						error.insertAfter(element);
+					}
+				}
+			});
+		});
+	</script>
+</head>
+<body>
+	<ul class="nav nav-tabs">
+		<li><a href="${ctx}/admin/exam/problem/type">分类列表</a></li>
+		<li class="active"><a href="${ctx}/admin/exam/problem/type/add">添加分类</a></li>
+	</ul><br/>
+	<form:form id="inputForm" modelAttribute="problem" action="${ctx}/admin/exam/problem/type/save" method="post" class="form-horizontal">
+		<tags:message content="${message}"/>
+		<div class="control-group">
+			<label class="control-label">名称:</label>
+			<div class="controls">
+				<form:input path="name" htmlEscape="false" maxlength="20" class="required"/>*20字以内
+			</div>
+		</div>
+		<div class="form-actions">
+			<input id="btnSubmit" class="btn btn-primary" type="submit" value="保 存"/>&nbsp;
+			<input id="btnCancel" class="btn" type="button" value="返 回" onclick="history.go(-1)"/>
+		</div>
+	</form:form>
+</body>
+</html>

+ 65 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/problemList.jsp

@@ -0,0 +1,65 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<html>
+<head>
+	<title>问题卷分类管理</title>
+	<meta name="decorator" content="default"/>
+	<%@include file="/WEB-INF/views/include/head.jsp" %>
+	<%@include file="/WEB-INF/views/include/dialog.jsp" %>
+	<style type="text/css">.sort{color:#0663A2;cursor:pointer;}</style>
+</head>
+<body>
+	<ul class="nav nav-tabs">
+		<li class="active"><a href="${ctx}/admin/exam/problem/type">分类列表</a></li>
+		<c:if test="${web_user.schoolAdmin==true}">
+		<li><a href="${ctx}/admin/exam/problem/type/add">添加分类</a></li>
+		</c:if>
+	</ul>
+	<tags:message content="${message}"/>
+	<table id="contentTable" class="table table-striped table-bordered table-condensed">
+		<thead>
+			<tr>
+				<th>编号</th>
+				<th>分类名称</th>
+				<th>类型</th>
+				<th>操作</th>
+			</tr>
+		</thead>
+		<tbody>
+		<c:forEach items="${list}" var="problem">
+			<tr>
+				<td>${problem.id}</td>
+				<td>
+					${problem.name}
+				</td>
+				<td>
+				<c:if test="${problem.custom==true}">自定义</c:if>
+				<c:if test="${problem.custom!=true}">默认</c:if>		
+				</td>
+				<td>
+    				<c:if test="${problem.custom==true}">  				
+    				<a href="${ctx}/admin/exam/problem/type/edit/${problem.id}">编辑</a>
+    				<a href="##" class="delete-button" data-problem-id="${problem.id}">删除</a>
+    				</c:if>
+				</td>
+			</tr>
+		</c:forEach>
+		</tbody>
+	</table>
+<script type="text/javascript">
+$(document).ready(function() {
+	$('.delete-button').click(function(){
+	    if(confirm('确定要删除当前的分类?')){  
+	    	$.post('${ctx}/admin/exam/problem/type/delete', {problemId: $(this).attr('data-problem-id')}, function(result){
+		        if(result.success==true){
+		        	window.location.reload();
+		        }else{
+		            alert(result.message || '提交失败,请稍后重试');
+		        }
+		    });
+	    }
+	});
+})
+</script>	
+</body>
+</html>

+ 6 - 4
stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markNew.jsp

@@ -32,9 +32,9 @@
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/thumbnail.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/single-image-view.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/change-name.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/tag-process.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/view-sidebar.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/specialTag.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/problem-process.js"></script>
 
 </head>
 <body>
@@ -90,12 +90,14 @@
 					},
 				 	'thumbnail':{
 					}, 
+					<c:if test="${isFormal==true}">
+					'problem-process':{
+						problemTypes : '${problemTypes}'
+	                },
+					</c:if>	                
 					'change-name':{
 						url : '${ctx}/mark/change-name'
 					},
-					/* 'tag-process':{
-						url : '${ctx}/mark/tags'
-					}, */
 					'view-sidebar':{
 						list: [
 							{title:'试卷',  url:'<c:if test="${subject.hasPaper==true}">${cardServer}${subject.paperUrl}</c:if>'},

+ 6 - 1
stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markTrack.jsp

@@ -25,9 +25,9 @@
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/thumbnail.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-track/js/modules/single-image-view.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/change-name.js"></script>
-<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/tag-process.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/view-sidebar.js"></script>
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/specialTag.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/problem-process.js"></script>
 </head>
 <body>
 	<div class="container-fluid" id="container"></div>
@@ -74,6 +74,11 @@
 					},
 					'warning-info': {
 					},
+					<c:if test="${isFormal==true}">
+					'problem-process':{
+						problemTypes : '${problemTypes}'
+	                },
+	                </c:if>
 					'change-name':{
 						url : '${ctx}/mark/change-name'
 					},

+ 14 - 12
stmms-web/src/main/webapp/sql/stmms_ft.sql

@@ -540,22 +540,24 @@ CREATE TABLE `m_problem_type` (
 # ------------------------------------------------------------
 
 DROP TABLE IF EXISTS `m_problem_history`;
-CREATE TABLE `m_problem_history` (
-  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+CREATE TABLE `m_arbitrate_history` (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
   `exam_id` int(11) NOT NULL COMMENT '考试ID',
-  `subject_code` varchar(255) NOT NULL DEFAULT '' COMMENT '科目代码',
+  `subject_code` varchar(32) NOT NULL COMMENT '科目代码',
+  `group_number` int(11) NOT NULL COMMENT '大题号',
+  `exam_number` varchar(64) NOT NULL COMMENT '准考证号',
   `student_id` int(11) NOT NULL COMMENT '考生ID',
   `library_id` int(11) NOT NULL COMMENT '评卷任务ID',
-  `exam_number` varchar(64) NOT NULL COMMENT '准考证号',
-  `problem_id` int(11) NOT NULL COMMENT '问题ID',
-  `status` varchar(11) NOT NULL COMMENT '状态',
-  `user_id` int(11) DEFAULT NULL COMMENT '操作人ID',
-  `total_score` float DEFAULT NULL COMMENT '总分',
-  `score_list` varchar(255) DEFAULT NULL COMMENT '给分分点',
-  `create_time` datetime NOT NULL,
-  `update_time` datetime DEFAULT NULL,
+  `status` int(11) NOT NULL COMMENT '状态',
+  `user_id` int(11) DEFAULT NULL COMMENT '处理人ID',
+  `total_score` double DEFAULT NULL COMMENT '总分',
+  `score_list` varchar(255) DEFAULT NULL COMMENT '给分明细',
+  `create_time` datetime NOT NULL COMMENT '创建时间',
+  `update_time` datetime DEFAULT NULL COMMENT '处理时间',
   PRIMARY KEY (`id`),
-  KEY `index1` (`exam_id`,`subject_code`,`status`) USING BTREE
+  KEY `index1` (`exam_id`,`subject_code`,`group_number`,`status`),
+  KEY `index2` (`user_id`,`status`,`update_time`),
+  KEY `index3` (`student_id`,`status`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='问题卷历史表';
 
 

+ 3 - 1
stmms-web/src/main/webapp/static/mark-new/js/mark-control.js

@@ -579,7 +579,9 @@ MarkControl.prototype.submitTask = function(submitUrl) {
             scoreList: task.scoreList,
             trackList: [],
             tagList: task.tagList,
-            spent: new Date().getTime() - task.spent
+            spent: new Date().getTime() - task.spent,
+            problem : task.problem,
+            reason : task.reason
         }
 
         this.trigger('task.submit.before');

+ 6 - 0
stmms-web/src/main/webapp/static/mark-new/js/modules/mark-history.js

@@ -86,6 +86,12 @@ function MarkHistory(option) {
         if (task.previous == true) {
             this.updateTaskScore(task.totalScore);
         }
+        if(task.problem == true){
+        	var self = this;
+        	self.toggle(false);
+        	self.markControl.context.task = undefined;
+            self.markControl.getTask();
+        }
     });
 }
 

+ 64 - 60
stmms-web/src/main/webapp/static/mark-new/js/modules/problem-process.js

@@ -1,4 +1,4 @@
-//问题卷处理模块
+//问题卷分类
 var problem_process = function(option, success) {
     var object = new ProblemProcess(option);
     success();
@@ -7,99 +7,103 @@ var problem_process = function(option, success) {
 
 function ProblemProcess(option) {
     this.markControl = option.markControl;
-    this.autoSubmit = false;
-    this.reasons = option.reasons;
+    this.problemTypes =JSON.parse(option.problemTypes);
+    this.context = option.markControl.context;
     this.init();
     this.markControl.on('task.get.before', this, function(event, context, eventObject) {
         this.task = undefined;
-        this.reasonType = undefined;
+        this.reset();
     });
     this.markControl.on('task.get.success', this, function(event, context, eventObject) {
         this.task = context.task;
-        if (this.task.previous != true && this.task.back != true) {
-            this.container.show();
-        } else {
-            this.container.hide();
-        }
+        this.reset();
     });
     this.markControl.on('task.get.none', this, function(event, context, eventObject) {
-        this.container.hide();
+        this.reset();
     });
     this.markControl.on('task.get.error', this, function(event, context, eventObject) {
-        this.container.hide();
+        this.reset();
     });
 }
 
 ProblemProcess.prototype.init = function() {
     this.container = getDom(this.container_dom, this.markControl).appendTo(this.markControl.container.assistant);
-    this.container.list = this.container.find('#problem-list');
-    this.container.title = this.container.find('.popover-title');
-    for (var i in this.reasons) {
-        var button = getDom(this.button_dom, this.markControl).appendTo(this.container.list);
-        button.attr('data-reason', i);
-        button.html(this.reasons[i]);
-    }
-    this.container.list.find('.problem-select-button').click(this, function(event) {
-        var problemProcess = event.data;
-        var button = $(event.target);
-
-        problemProcess.container.list.find('.problem-select-button').each(function(index, obj) {
-            $(obj).removeClass('curr');
-        });
-        problemProcess.onReasonSelect(button);
-    });
-
     this.popover = getDom(this.popover_dom, this.markControl);
-    this.popover.hide();
+    this.popover.list = this.popover.find('#problem-type-list');
     this.popover.appendTo(this.markControl.container);
     var self = this;
-    this.popover.find('#submit-button').click(function() {
-        self.submit();
-        self.reset();
+    for (var i = 0; i < this.problemTypes.length; i++) {
+        var button = getDom(self.button_dom).appendTo(self.popover.list);
+        var name = getDom("<span>"+this.problemTypes[i].name+"</span><br/>").appendTo(self.popover.list);
+        button.attr('data-value', this.problemTypes[i].id);
+    }
+    this.popover.find('a.submit-btn').click(function() {
+    	var problemId ; 
+    	self.popover.find('.problemId').each(function(index, obj) {
+    		if($(obj).is(':checked')){
+    			problemId = $(obj).attr('data-value');
+    		}
+    	});
+    	if(problemId != undefined){
+    		self.submit(problemId);
+    		self.reset();
+    		self.toggle(false);
+    	}else{
+    		alert("请选择问题分类");
+    	}
     });
-    this.popover.find('#cancel-button').click(function() {
-        self.reset();
+    this.popover.find('a.close-btn').click(function() {
+    	self.reset();
+    	self.toggle(false);
+    });
+    this.popover.find('p.image-close').click(function() {
+    	self.reset();
+        self.toggle(false);
+    });
+    this.container.find('#show-problem-button').click(function() {
+        self.markControl.container.assistant.hide();
+        self.toggle(true);
     });
 }
 
 ProblemProcess.prototype.reset = function() {
     this.popover.hide();
-    this.container.list.find('.problem-select-button').each(function(index, obj) {
-        $(obj).removeClass('curr');
-    });
+    this.popover.find('.problemId').each(function(index, obj) {
+		$(obj).prop("checked",false);
+	});
 }
 
-ProblemProcess.prototype.onReasonSelect = function(button) {
-    var self = this;
-    if (this.task != undefined && this.task.problem != true) {
-        button.addClass('curr');
-        this.reasonType = button.attr('data-reason');
-        if (this.autoSubmit) {
-            this.submit();
-        } else {
-            this.popover.show();
-        }
+ProblemProcess.prototype.toggle = function(enable) {
+	var self = this;
+    if (enable == true) {
+    	self.enable = true;
+    	self.popover.show();
+        self.context.listenKeyboard = false;
+    } else {
+    	self.enable = false;
+    	self.popover.hide();
+        self.context.listenKeyboard = true;
     }
-    return false;
 }
 
-ProblemProcess.prototype.submit = function() {
-    if (this.task != undefined && this.reasonType != undefined) {
+ProblemProcess.prototype.submit = function(problemId) {
+    if (this.task != undefined && problemId != undefined) {
         this.task.problem = true;
-        this.task.reason = this.reasonType;
+        this.task.reason = problemId;
         this.markControl.submitTask();
     }
 }
 
-
 ProblemProcess.prototype.container_dom = '<h3 class="popover-title">问题卷</h3>\
-<div class="popover-content"><p id="problem-list" class="popover-list">\
-</p></div>';
+	<div class="popover-content"><p id="type-info-list" class="popover-list">\
+	<a href="#" id="show-problem-button">选择问题卷类型</a>\
+	</p></div>';
 
-ProblemProcess.prototype.button_dom = '<a class="problem-select-button" href="#"></a>';
+ProblemProcess.prototype.button_dom = '<input type="radio" class="problemId" name="problemId">';
 
-ProblemProcess.prototype.popover_dom = '<div class="warning-popover">\
-<p>确认要将本试卷置为问题卷吗?</p>\
-<a href="#" class="btn btn-large btn-primary text-c" id="submit-button">确定</a>\
-<a href="#" class="btn btn-large text-c" id="cancel-button">取消</a>\
-</div>';
+ProblemProcess.prototype.popover_dom = '<div class="message-popover" style="display:none;"><div class="popover-header">\
+	<p class="title">问题卷</p><p class="image-close"><img src="{staticServer}/mark-new/images/images-close.png" /></p></div>\
+	<div id="problem-type-list" style="text-align:left;"></div>\
+	<a href="#" class="btn btn-small btn-info submit-btn">确定</a>\
+	<a href="#" class="btn btn-small btn-info close-btn">取消</a>\
+	</div>';

+ 3 - 1
stmms-web/src/main/webapp/static/mark-new/js/modules/warning-info.js

@@ -49,7 +49,9 @@ function WarningInfo(option) {
         this.toggle(true, '强制特殊标记已开启,至少使用一个特殊标记', '关闭');
     });
     this.markControl.on('history.submit.success', this, function(event, context, task) {
-    	if(task.previous==true){
+    	if(task.problem==true){
+    		this.toggle(true, '回评成功,已提交问题卷','关闭');
+    	}else if(task.previous==true){
     		this.toggle(true, '回评成功,总分:'+task.totalScore, '关闭');
     	}
     });

+ 3 - 1
stmms-web/src/main/webapp/static/mark-track/js/mark-control.js

@@ -591,7 +591,9 @@ MarkControl.prototype.submitTask = function(submitUrl) {
             scoreList: task.scoreList,
             trackList: task.trackList,
             tagList: task.tagList,
-            spent: new Date().getTime() - task.spent
+            spent: new Date().getTime() - task.spent,
+            problem : task.problem,
+            reason : task.reason
         }
 
         this.trigger('task.submit.before');