Selaa lähdekoodia

增加试评管理页面与试评任务详情弹出框

luoshi 6 vuotta sitten
vanhempi
commit
37c8a8e884
26 muutettua tiedostoa jossa 578 lisäystä ja 34 poistoa
  1. 3 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java
  2. 2 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java
  3. 5 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java
  4. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/TrialHistoryDao.java
  5. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/TrialLibraryDao.java
  6. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/TrialTagDao.java
  7. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/TrialTrackDao.java
  8. 17 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkResult.java
  9. 42 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/Task.java
  10. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java
  11. 33 9
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  12. 10 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java
  13. 9 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  14. 7 7
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/FormalTaskUtil.java
  15. 10 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java
  16. 157 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TrialController.java
  17. 2 2
      stmms-web/src/main/java/cn/com/qmth/stmms/monitor/TaskMonitorController.java
  18. 122 0
      stmms-web/src/main/webapp/WEB-INF/views/include/trialDetail.jsp
  19. 1 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/arbitrateList.jsp
  20. 8 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupEditFull.jsp
  21. 1 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupList.jsp
  22. 1 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/libraryList.jsp
  23. 1 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/markerList.jsp
  24. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/scoreList.jsp
  25. 124 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/trialList.jsp
  26. 4 4
      stmms-web/src/main/webapp/static/mark-new/js/modules/mark-status.js

+ 3 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java

@@ -28,6 +28,9 @@ public interface MarkGroupDao
     @Query("delete from MarkGroup q where q.pk.examId=?1 and q.pk.subjectCode=?2")
     void deleteByExamIdAndSubjectCode(Integer examId, String subjectCode);
 
+    @Query("select count(q) from MarkGroup q where q.pk.examId=?1 and q.status in (?2)")
+    long countByExamIdAndStatus(Integer examId, MarkStatus... status);
+
     @Query("select count(q) from MarkGroup q where q.pk.examId=?1 and q.pk.subjectCode=?2")
     long countByExamIdAndSubjectCode(Integer examId, String subjectCode);
 

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

@@ -55,4 +55,6 @@ public interface MarkGroupService {
 
     boolean needTrialLibrary(Integer examId, String subjectCode, Integer number);
 
+    long countByExamAndStatus(Integer examId, MarkStatus... status);
+
 }

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

@@ -106,6 +106,11 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
         return groupDao.findOne(pk);
     }
 
+    @Override
+    public long countByExamAndStatus(Integer examId, MarkStatus... status) {
+        return groupDao.countByExamIdAndStatus(examId, status);
+    }
+
     @Override
     public long countByExamAndSubject(int examId, String subjectCode) {
         return groupDao.countByExamIdAndSubjectCode(examId, subjectCode);

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

@@ -29,6 +29,10 @@ public interface TrialHistoryDao extends JpaRepository<TrialHistory, Integer>, J
     @Query("delete TrialHistory m where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3")
     void deleteByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber);
 
+    @Modifying
+    @Query("delete TrialHistory m where m.pk.libraryId=?1")
+    void deleteByLibraryId(Integer libraryId);
+
     @Modifying
     @Query("delete TrialHistory m where m.studentId=?1")
     void deleteByStudentId(Integer studentId);

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

@@ -36,6 +36,10 @@ public interface TrialLibraryDao extends JpaRepository<TrialLibrary, Integer>, J
     @Query("select count(*) from TrialLibrary f where f.studentId=?1 and f.groupNumber=?2")
     long countByStudentIdAndGroupNumber(Integer studentId, Integer groupNumber);
 
+    @Modifying
+    @Query("update TrialLibrary l set l.markCount=?2 where l.id=?1")
+    void updateMarkCount(Integer id, int count);
+
     @Modifying
     @Query("delete TrialLibrary m where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3")
     void deleteByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber);

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

@@ -11,6 +11,10 @@ import cn.com.qmth.stmms.biz.mark.model.TrialTag;
 
 public interface TrialTagDao extends PagingAndSortingRepository<TrialTag, Integer>, JpaSpecificationExecutor<TrialTag> {
 
+    @Modifying
+    @Query("delete from TrialTag s where s.libraryId = ?1")
+    public void deleteByLibraryId(Integer libraryId);
+
     @Modifying
     @Query("delete from TrialTag s where s.libraryId = ?1 and s.markerId=?2")
     public void deleteByLibraryIdAndMarkerId(Integer libraryId, Integer markerId);

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

@@ -16,6 +16,10 @@ public interface TrialTrackDao extends JpaRepository<TrialTrack, TrialTrackPK>,
 
     List<TrialTrack> findByStudentId(Integer studentId);
 
+    @Modifying
+    @Query("delete from TrialTrack t where t.pk.libraryId=?1")
+    void deleteByLibraryId(Integer libraryId);
+
     @Modifying
     @Query("delete from TrialTrack t where t.pk.libraryId=?1 and t.pk.markerId=?2")
     void deleteByLibraryIdAndMarkerId(Integer libraryId, Integer markerId);

+ 17 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkResult.java

@@ -13,10 +13,15 @@ import java.util.Map;
  */
 public class MarkResult {
 
+    /**
+     * 评卷状态
+     */
+    private int statusValue;
+
     /**
      * 考生编号
      */
-    private String studentId;
+    private Integer studentId;
 
     /**
      * 评卷任务编号
@@ -48,6 +53,14 @@ public class MarkResult {
      */
     private long spent;
 
+    public int getStatusValue() {
+        return statusValue;
+    }
+
+    public void setStatusValue(int statusValue) {
+        this.statusValue = statusValue;
+    }
+
     public Integer getLibraryId() {
         return libraryId;
     }
@@ -96,11 +109,11 @@ public class MarkResult {
         this.spent = spent;
     }
 
-    public String getStudentId() {
+    public Integer getStudentId() {
         return studentId;
     }
 
-    public void setStudentId(String studentId) {
+    public void setStudentId(Integer studentId) {
         this.studentId = studentId;
     }
 
@@ -175,4 +188,5 @@ public class MarkResult {
         }
         return list;
     }
+
 }

+ 42 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/Task.java

@@ -8,6 +8,24 @@ public class Task extends MarkResult implements Serializable {
 
     private static final long serialVersionUID = 4912665442008033200L;
 
+    /**
+     * 评卷状态
+     * 
+     */
+    private String statusName;
+
+    /**
+     * 显示任务编号
+     * 
+     */
+    private String taskNumber;
+
+    /**
+     * 显示考生编号
+     * 
+     */
+    private String studentNumber;
+
     /**
      * 题卡图片地址
      */
@@ -264,4 +282,28 @@ public class Task extends MarkResult implements Serializable {
         this.arbitrationList = arbitrationList;
     }
 
+    public String getStatusName() {
+        return statusName;
+    }
+
+    public void setStatusName(String statusName) {
+        this.statusName = statusName;
+    }
+
+    public String getTaskNumber() {
+        return taskNumber;
+    }
+
+    public void setTaskNumber(String taskNumber) {
+        this.taskNumber = taskNumber;
+    }
+
+    public String getStudentNumber() {
+        return studentNumber;
+    }
+
+    public void setStudentNumber(String studentNumber) {
+        this.studentNumber = studentNumber;
+    }
+
 }

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

@@ -22,7 +22,7 @@ import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.lock.impl.MemoryLockProvider;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.biz.utils.CurrentTaskUtil;
+import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
 import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 
@@ -80,7 +80,7 @@ public class MarkCronService {
     @Scheduled(cron = "${mark.cleanTaskSchedule}")
     public void cronCleanTask() {
         try {
-            CurrentTaskUtil.clearTimeoutTask(timeoutMinute);
+            FormalTaskUtil.clearTimeoutTask(timeoutMinute);
             TrialTaskUtil.clearTimeoutTask(timeoutMinute);
         } catch (Exception e) {
             log.error("CronCleanTask error", e);

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

@@ -35,7 +35,7 @@ import cn.com.qmth.stmms.biz.mark.model.MarkResult;
 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.utils.CurrentTaskUtil;
+import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
 import cn.com.qmth.stmms.common.enums.HistoryStatus;
@@ -108,7 +108,7 @@ public class MarkServiceImpl implements MarkService {
         if (group.getStatus() == MarkStatus.TRIAL) {
             count = TrialTaskUtil.count(group.getExamId(), group.getSubjectCode(), group.getNumber());
         } else if (group.getStatus() == MarkStatus.FORMAL) {
-            count = CurrentTaskUtil.count(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            count = FormalTaskUtil.count(group.getExamId(), group.getSubjectCode(), group.getNumber());
         }
         return count;
     }
@@ -121,7 +121,7 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     public int applyCount(Marker marker) {
-        return CurrentTaskUtil.count(marker);
+        return FormalTaskUtil.count(marker);
     }
 
     /**
@@ -134,7 +134,7 @@ public class MarkServiceImpl implements MarkService {
         if (group.getStatus() == MarkStatus.TRIAL) {
             TrialTaskUtil.clear(group.getExamId(), group.getSubjectCode(), group.getNumber());
         } else if (group.getStatus() == MarkStatus.FORMAL) {
-            CurrentTaskUtil.clear(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            FormalTaskUtil.clear(group.getExamId(), group.getSubjectCode(), group.getNumber());
         }
     }
 
@@ -257,7 +257,7 @@ public class MarkServiceImpl implements MarkService {
         // marker.getId()) > 0) {
         // return false;
         // }
-        return CurrentTaskUtil.add(marker, getApplyTaskId(library));
+        return FormalTaskUtil.add(marker, getApplyTaskId(library));
     }
 
     /**
@@ -280,7 +280,7 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     public boolean hasApplied(MarkLibrary library, Marker marker) {
-        return CurrentTaskUtil.exists(marker, getApplyTaskId(library));
+        return FormalTaskUtil.exists(marker, getApplyTaskId(library));
     }
 
     /**
@@ -303,7 +303,7 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     public void releaseLibrary(MarkLibrary library, Marker marker) {
-        CurrentTaskUtil.remove(marker, getApplyTaskId(library));
+        FormalTaskUtil.remove(marker, getApplyTaskId(library));
     }
 
     /**
@@ -321,10 +321,11 @@ public class MarkServiceImpl implements MarkService {
      * 释放某个评卷员的所有锁定任务
      * 
      * @param marker
+     * @param group
      */
     @Override
     public void releaseByMarker(Marker marker) {
-        CurrentTaskUtil.clear(marker);
+        FormalTaskUtil.clear(marker);
         TrialTaskUtil.clear(marker);
     }
 
@@ -405,7 +406,8 @@ public class MarkServiceImpl implements MarkService {
     public boolean submitTask(MarkResult result, Marker marker) {
         // 判断大题是否存在/评卷是否结束
         MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
-        if (group == null || group.getStatus() == MarkStatus.FINISH) {
+        if (group == null || group.getStatus() == MarkStatus.FINISH
+                || group.getStatus().getValue() != result.getStatusValue()) {
             return false;
         }
         // 等待大题释放锁定
@@ -454,6 +456,8 @@ public class MarkServiceImpl implements MarkService {
                             trialTagDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
                             trialTagDao.save(result.getTagList(history));
                         }
+                        trialLibraryDao.updateMarkCount(library.getId(),
+                                (int) trialHistoryDao.countByLibraryId(library.getId()));
                         releaseLibrary(library, marker);
                         return true;
                     }
@@ -559,6 +563,26 @@ public class MarkServiceImpl implements MarkService {
         }
     }
 
+    /**
+     * 管理员/组长重置某个试评任务
+     * 
+     * @param library
+     */
+    @Override
+    @Transactional
+    public boolean resetLibrary(TrialLibrary library) {
+        if (library != null) {
+            lockService.waitUnlockGroup(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
+            trialHistoryDao.deleteByLibraryId(library.getId());
+            trialTrackDao.deleteByLibraryId(library.getId());
+            trialTagDao.deleteByLibraryId(library.getId());
+            trialLibraryDao.updateMarkCount(library.getId(), 0);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     /**
      * 管理员/组长处理仲裁卷
      * 

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

@@ -40,6 +40,7 @@ import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.mark.service.TrialService;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
 
 /**
@@ -100,7 +101,7 @@ public class TaskServiceImpl implements TaskService {
 
         Task task = new Task();
         task.setExist(true);
-        task.setStudentId(student.getExamNumber());
+        task.setStudentId(student.getId());
         task.setLibraryId(history.getId());
         task.setMarkStepList(buildMarkStep(group, null));
         task.setPictureUrls(PictureUrlBuilder.getSliceUrls(student.getExamId(), campusId, student.getSubjectCode(),
@@ -134,9 +135,9 @@ public class TaskServiceImpl implements TaskService {
         ExamStudent student = studentService.findByExamIdAndExamNumber(library.getExamId(), library.getExamNumber());
         MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
         Task task = new Task();
-        task.setTagList(getMarkSpecialTagList(library.getId()));
         task.setExist(true);
-        task.setStudentId(library.getStudentId().toString());
+        task.setStatusValue(MarkStatus.FORMAL.getValue());
+        task.setStudentId(library.getStudentId());
         task.setLibraryId(library.getId());
         task.setMarkStepList(buildMarkStep(group, library.getId()));
         task.setPictureUrls(PictureUrlBuilder.getSliceUrls(library.getExamId(), library.getCampusId(),
@@ -149,6 +150,7 @@ public class TaskServiceImpl implements TaskService {
         task.setObjectiveScore(student != null ? student.getObjectiveScore() : 0);
         task.setMarkTime(library.getMarkerTime());
         task.setTags(library.getTags());
+        task.setTagList(getMarkSpecialTagList(library.getId()));
         if (library.getMarkerScore() != null) {
             task.setTotalScore(library.getMarkerScore());
         }
@@ -168,7 +170,9 @@ public class TaskServiceImpl implements TaskService {
         MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
         Task task = new Task();
         task.setExist(true);
-        task.setStudentId(library.getStudentId().toString());
+        task.setStatusValue(MarkStatus.TRIAL.getValue());
+        task.setStatusName(MarkStatus.TRIAL.getName());
+        task.setStudentId(library.getStudentId());
         task.setLibraryId(library.getId());
         task.setMarkStepList(buildTrialStep(group, history));
         task.setPictureUrls(PictureUrlBuilder.getSliceUrls(library.getExamId(), library.getCampusId(),
@@ -289,8 +293,8 @@ public class TaskServiceImpl implements TaskService {
         ExamStudent student = studentService.findById(studentId);
         Task task = new Task();
         task.setExist(true);
-        task.setStudentId(student.getExamNumber());
-        task.setLibraryId(studentId);
+        task.setStudentId(studentId);
+        // task.setLibraryId(studentId);
         task.setMarkStepList(buildMarkStep(student));
         Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
         task.setPictureUrls(PictureUrlBuilder.getSliceUrls(student.getExamId(), campus.getId(),

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

@@ -97,6 +97,7 @@ public interface MarkService {
      * 释放某个评卷员的所有锁定任务
      * 
      * @param marker
+     * @param group
      */
     void releaseByMarker(Marker marker);
 
@@ -206,4 +207,12 @@ public interface MarkService {
      */
     boolean submitTask(MarkResult result, Marker marker);
 
+    /**
+     * 管理员/组长重置某个试评任务
+     * 
+     * @param library
+     * @return
+     */
+    boolean resetLibrary(TrialLibrary library);
+
 }

+ 7 - 7
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/CurrentTaskUtil.java → stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/FormalTaskUtil.java

@@ -12,12 +12,12 @@ import com.google.common.collect.SetMultimap;
 import cn.com.qmth.stmms.biz.exam.model.Marker;
 
 /**
- * Redis zset替代方案,单JVM内部针对已领取未给分任务的管理工具
+ * Redis zset替代方案,单JVM内部针对已领取未给分正评任务的管理工具
  * 
  * @author luoshi
  * 
  */
-public class CurrentTaskUtil {
+public class FormalTaskUtil {
 
     private static SetMultimap<String, TaskEntry> taskMap = HashMultimap.create();
 
@@ -32,7 +32,7 @@ public class CurrentTaskUtil {
         String key = getKey(marker);
         TaskEntry obj = new TaskEntry(marker.getId(), taskId);
 
-        synchronized (CurrentTaskUtil.class) {
+        synchronized (FormalTaskUtil.class) {
             if (taskMap.containsEntry(key, obj)) {
                 return false;
             } else {
@@ -70,7 +70,7 @@ public class CurrentTaskUtil {
      */
     public static void remove(Marker marker, String taskId) {
         TaskEntry obj = new TaskEntry(marker.getId(), taskId);
-        synchronized (CurrentTaskUtil.class) {
+        synchronized (FormalTaskUtil.class) {
             taskMap.remove(getKey(marker), obj);
         }
     }
@@ -113,7 +113,7 @@ public class CurrentTaskUtil {
      * @param subjectCode
      */
     public static void clear(int examId, String subjectCode, int number) {
-        synchronized (CurrentTaskUtil.class) {
+        synchronized (FormalTaskUtil.class) {
             taskMap.removeAll(getKey(examId, subjectCode, number));
         }
     }
@@ -126,7 +126,7 @@ public class CurrentTaskUtil {
     public static void clear(Marker marker) {
         String key = getKey(marker);
         Set<TaskEntry> set = new HashSet<TaskEntry>();
-        synchronized (CurrentTaskUtil.class) {
+        synchronized (FormalTaskUtil.class) {
             set.addAll(taskMap.get(key));
             for (TaskEntry obj : set) {
                 if (obj.getMarkerId() == marker.getId()) {
@@ -156,7 +156,7 @@ public class CurrentTaskUtil {
     }
 
     public static void clearTimeoutTask(long timeoutMinute) {
-        synchronized (CurrentTaskUtil.class) {
+        synchronized (FormalTaskUtil.class) {
             SetMultimap<String, TaskEntry> taskMap1 = HashMultimap.create();
             // System.out.println("任务池大小:"+taskMap.size());
             // System.out.println("间隔时间:"+cleanMapinterval);

+ 10 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java

@@ -38,6 +38,7 @@ import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
@@ -114,6 +115,7 @@ public class ScoreController extends BaseExamController {
         view.addObject("filter", filter);
         view.addObject("subjectList", getExamSubject(examId, wu));
         view.addObject("running", lockService.isLocked(LockType.EXAM, examId));
+        view.addObject("enableExport", enableExport(examId));
         view.addObject("imageServer", imageServer);
         view.addObject("packageServer", packageServer);
         view.addObject("cardServer", cardServer);
@@ -192,6 +194,10 @@ public class ScoreController extends BaseExamController {
             RedirectAttributes redirectAttributes) {
         WebUser wu = RequestUtils.getWebUser(request);
         int examId = getSessionExamId(request);
+        if (!enableExport(examId)) {
+            addMessage(redirectAttributes, "评卷未结束不能导出成绩");
+            return "redirect:/admin/exam/score";
+        }
         String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
         query.setExamId(examId);
         query.setPageNumber(1);
@@ -294,4 +300,8 @@ public class ScoreController extends BaseExamController {
         }
     }
 
+    private boolean enableExport(int examId) {
+        return groupService.countByExamAndStatus(examId, MarkStatus.TRIAL, MarkStatus.FORMAL) == 0;
+    }
+
 }

+ 157 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TrialController.java

@@ -0,0 +1,157 @@
+package cn.com.qmth.stmms.admin.exam;
+
+import java.text.DecimalFormat;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+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 cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+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.TrialLibrarySearchQuery;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.biz.mark.service.TrialService;
+import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.DateUtils;
+import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+@Controller("trialController")
+@RequestMapping("/admin/exam/trial")
+public class TrialController extends BaseExamController {
+
+    protected static Logger log = LoggerFactory.getLogger(TrialController.class);
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Autowired
+    private TrialService trialService;
+
+    @Autowired
+    private MarkService markService;
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Value("${slice.image.server}")
+    private String sliceServer;
+
+    @RequestMapping
+    public String list(Model model, HttpServletRequest request, TrialLibrarySearchQuery query,
+            @RequestParam(required = false) LibraryStatus status) {
+        int examId = getSessionExamId(request);
+        WebUser wu = RequestUtils.getWebUser(request);
+        List<ExamSubject> subjectList = getExamSubject(examId, wu);
+        if (subjectList.isEmpty()) {
+            return "redirect:/admin/exam/mark";
+        }
+        query.setExamId(examId);
+        if (query.getSubjectCode() == null && !subjectList.isEmpty()) {
+            query.setSubjectCode(subjectList.get(0).getCode());
+        }
+        subjectFilter(query, wu);
+        List<MarkGroup> groupList = groupService.findByExamAndSubject(examId, query.getSubjectCode());
+        if (!groupList.isEmpty()) {
+            if (query.getGroupNumber() == null) {
+                query.setGroupNumber(groupList.get(0).getNumber());
+            }
+            query.orderById();
+            query = trialService.findLibrary(query);
+            model.addAttribute("group",
+                    groupService.findOne(query.getExamId(), query.getSubjectCode(), query.getGroupNumber()));
+        }
+        model.addAttribute("query", query);
+        model.addAttribute("subjectList", getExamSubject(examId, wu));
+        model.addAttribute("groupList", groupList);
+        return "modules/exam/trialList";
+    }
+
+    @RequestMapping(value = "/reset", method = RequestMethod.POST)
+    @ResponseBody
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
+    public JSONObject reset(HttpServletRequest request, @RequestParam Integer libraryId) {
+        JSONObject obj = new JSONObject();
+        int examId = getSessionExamId(request);
+        TrialLibrary library = trialService.findLibrary(libraryId);
+        if (library != null) {
+            if (library.getExamId().equals(examId)
+                    && subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
+                if (markService.resetLibrary(library)) {
+                    obj.accumulate("success", true);
+                } else {
+                    obj.accumulate("success", false);
+                    obj.accumulate("message", "无法打回该评卷任务");
+                }
+            } else {
+                obj.accumulate("success", false);
+                obj.accumulate("message", "没有操作该评卷任务的权限");
+            }
+        } else {
+            obj.accumulate("success", false);
+            obj.accumulate("message", "该评卷任务不存在");
+        }
+        return obj;
+    }
+
+    @RequestMapping(value = "/detail")
+    @ResponseBody
+    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
+    public JSONObject detail(HttpServletRequest request, @RequestParam Integer libraryId) {
+        JSONObject obj = new JSONObject();
+        TrialLibrary library = trialService.findLibrary(libraryId);
+        if (library != null) {
+            ExamStudent student = studentService.findById(library.getStudentId());
+            MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(),
+                    library.getGroupNumber());
+            if (group != null && student != null) {
+                obj.accumulate("success", true);
+                // 裁切图配置
+                obj.accumulate("imageServer", sliceServer);
+                obj.accumulate("urls", PictureUrlBuilder.getSliceUrls(library.getExamId(), library.getCampusId(),
+                        library.getSubjectCode(), library.getExamNumber(), student.getSliceCount()));
+                obj.accumulate("pictureConfig", group.getPictureConfigList());
+                // 评卷记录集合
+                JSONArray array = new JSONArray();
+                List<TrialHistory> list = trialService.findHistory(libraryId);
+                DecimalFormat format = new DecimalFormat("###.##");
+                for (TrialHistory history : list) {
+                    Marker marker = markerService.findById(history.getMarkerId());
+                    JSONObject item = new JSONObject();
+                    item.accumulate("name", marker != null ? marker.getLoginName() : "");
+                    item.accumulate("score", format.format(history.getMarkerScore()));
+                    item.accumulate("time", DateUtils.formatDateTime(history.getMarkerTime()));
+                    array.add(item);
+                }
+                obj.accumulate("historyList", array);
+            }
+        }
+        return obj;
+    }
+
+}

+ 2 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/monitor/TaskMonitorController.java

@@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-import cn.com.qmth.stmms.biz.utils.CurrentTaskUtil;
+import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
 import cn.com.qmth.stmms.biz.utils.TaskEntry;
 import cn.com.qmth.stmms.common.utils.DateUtils;
 import net.sf.json.JSONArray;
@@ -24,7 +24,7 @@ public class TaskMonitorController {
     @ResponseBody
     public Object list(HttpServletRequest request, @RequestParam Integer examId, @RequestParam String subjectCode,
             @RequestParam Integer groupNumber) {
-        Set<TaskEntry> set = CurrentTaskUtil.list(examId, subjectCode, groupNumber);
+        Set<TaskEntry> set = FormalTaskUtil.list(examId, subjectCode, groupNumber);
         JSONArray array = new JSONArray();
         for (TaskEntry entry : set) {
             JSONObject obj = new JSONObject();

+ 122 - 0
stmms-web/src/main/webapp/WEB-INF/views/include/trialDetail.jsp

@@ -0,0 +1,122 @@
+<%@ page contentType="text/html;charset=UTF-8"%>
+<link rel="stylesheet" type="text/css" href="${ctxStatic}/jBox/Source/jBox.css">
+<script type="text/javascript" src="${ctxStatic}/jBox/Source/jBox.min.js"></script>
+
+<div id="trial-detail-content" class="container-fluid" style="display:none">
+    <div id="trial-left-div" class="span6" style="overflow:scroll">
+        <canvas id="trial-canvas"></canvas>
+    </div>
+    <div id="trial-right-div" class="span6">
+        <table id="contentTable" class="table table-striped table-bordered table-condensed">
+            <thead>
+                <tr>
+                    <th>评卷员</th>
+                    <th>给分</th>
+                    <th>时间</th>
+                </tr>
+            </thead>
+            <tbody id="trial-history">
+            </tbody>
+        </table>
+    </div>
+</div>
+
+<script type="text/javascript">
+var trailDetailModal = new jBox('Modal');
+
+$(document).ready(function() {
+    $('#trial-detail-left').height($(window).height()*0.83);
+});
+
+function initTrialDetailPopover(title, libraryId) {
+	initTrialDetailContent(libraryId); 
+	trailDetailModal.setWidth($(window).width()*0.9);
+	trailDetailModal.setHeight($(window).height()*0.85);
+	trailDetailModal.setTitle(title);
+	trailDetailModal.open();
+}
+
+function initTrialDetailContent(libraryId){
+	$('#trial-history').empty();
+	$('#trial-canvas').hide();
+	$.ajax({
+        type:"GET",
+        url:'${ctx}/admin/exam/trial/detail?libraryId='+libraryId,  
+        dataType:"json",
+        success:function(data){
+	        if(data.success==true) {
+	            for (var i = 0; i < data.historyList.length; i++) {
+	                var history = data.historyList[i];
+	                $('#trial-history').append('<tr><td>'+history.name+'</td><td>'+history.score+'</td><td>'+history.time+'</td></tr>');
+	            }
+	            var canvas = document.getElementById('trial-canvas');
+	            var ctx = canvas.getContext('2d');
+	            buildImages(data.imageServer, data.urls, data.pictureConfig, canvas, ctx);
+	            $('#trial-canvas').show();
+	            trailDetailModal.setContent('#trial-detail-content');
+	        }else {
+	            trailDetailModal.setTitle('加载失败')
+	        }
+        }
+    });
+}
+
+function buildImages(imageServer, picUrls, config, canvas, ctx) {
+    var indexSet = {};
+    for(var i=0;i<config.length;i++){
+        indexSet[config[i].i-1] = true;
+    }
+    //调用图片预加载函数,实现回调函数
+    var imageObjects = [];
+    loadImages(imageObjects, imageServer, indexSet, picUrls, 0,function(images) {
+        var maxWidth = 0;
+        var totalHeight = 0;
+        for (var i = 0; i < config.length; i++) {
+            //计算最大宽度与合计高度
+            if(config[i].w<=0){
+                config[i].w=images[config[i].i-1].width;
+            }
+            if(config[i].h<=0){
+                config[i].h=images[config[i].i-1].height;
+            }
+            maxWidth = Math.max(maxWidth, config[i].w);
+            totalHeight += config[i].h;
+        }
+        if (maxWidth > 0 && totalHeight > 0) {
+            //设置画布大小及背景颜色
+            canvas.width = maxWidth;
+            canvas.height = totalHeight;
+            //ctx.fillStyle = "#FFFFFF";
+            //ctx.fillRect(0, 0, maxWidth, totalHeight);
+            //绘画到画布
+            var height = 0;
+            for (var i = 0; i < config.length; i++) {
+                var image = images[config[i].i-1];
+                ctx.drawImage(image, config[i].x, config[i].y, config[i].w, config[i].h, 0, height, config[i].w, config[i].h);
+                height += config[i].h;
+            }
+        }
+    });
+}
+
+function loadImages(images, imageServer, indexSet, urls, number, callback) {
+    if (urls != undefined && number < urls.length) {
+        if(indexSet[number]==true) {
+            var img = new Image();
+            img.onload = function() {
+                images.push(img);
+                loadImages(images, imageServer, indexSet, urls, number + 1, callback);
+            }
+            img.src = imageServer + urls[number] + '?' + new Date().getTime();
+        }else {
+            images.push({});
+            loadImages(images, imageServer, indexSet, urls, number + 1, callback);
+        }
+    } else {
+        callback.call(this, images);
+    }
+}
+</script>
+
+
+

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

@@ -12,6 +12,7 @@
         <li><a href="${ctx}/admin/exam/group?subjectCode=${query.subjectCode}">大题管理</a></li>
         <li><a href="${ctx}/admin/exam/marker?subjectCode=${query.subjectCode}">评卷员管理</a></li>
         <li><a href="${ctx}/admin/exam/library?subjectCode=${query.subjectCode}">评卷任务管理</a></li>
+        <li><a href="${ctx}/admin/exam/trial?subjectCode=${query.subjectCode}">试评管理</a></li>
         <li class="active"><a href="##">仲裁管理</a></li>
     </ul>
 	<form id="searchForm" action="${ctx}/admin/exam/arbitrate" method="post" class="breadcrumb form-search">

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

@@ -70,6 +70,14 @@
 				<form:input path="title" htmlEscape="false" maxlength="30" class="required"/>
 			</div>
 		</div>
+		<c:if test="${group.status==TRIAL}">
+        <div class="control-group">
+            <label class="control-label">试评数量</label>
+            <div class="controls">
+                <form:input path="trialCount" htmlEscape="false" min="1" class="required digits" type="number"/>
+            </div>
+        </div>
+        </c:if>
         <div class="control-group">
             <label class="control-label">步骤分</label>
             <div class="controls">

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

@@ -12,6 +12,7 @@
         <li class="active"><a href="##">大题管理</a></li>
         <li><a href="${ctx}/admin/exam/marker?subjectCode=${subject.code}">评卷员管理</a></li>
         <li><a href="${ctx}/admin/exam/library?subjectCode=${subject.code}">评卷任务管理</a></li>
+        <li><a href="${ctx}/admin/exam/trial?subjectCode=${subject.code}">试评管理</a></li>
         <li><a href="${ctx}/admin/exam/arbitrate?subjectCode=${subject.code}">仲裁管理</a></li>
     </ul>
 	<form id="searchForm" action="${ctx}/admin/exam/group" method="post" class="breadcrumb form-search">

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

@@ -12,6 +12,7 @@
         <li><a href="${ctx}/admin/exam/group?subjectCode=${query.subjectCode}">大题管理</a></li>
         <li><a href="${ctx}/admin/exam/marker?subjectCode=${query.subjectCode}">评卷员管理</a></li>
         <li class="active"><a href="##">评卷任务管理</a></li>
+        <li><a href="${ctx}/admin/exam/trial?subjectCode=${query.subjectCode}">试评管理</a></li>
         <li><a href="${ctx}/admin/exam/arbitrate?subjectCode=${query.subjectCode}">仲裁管理</a></li>
     </ul>
 	<form id="searchForm" action="${ctx}/admin/exam/library" method="post" class="breadcrumb form-search">

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

@@ -81,6 +81,7 @@
         <li><a href="${ctx}/admin/exam/group?subjectCode=${query.subjectCode}">大题管理</a></li>
         <li class="active"><a href="##">评卷员管理</a></li>
         <li><a href="${ctx}/admin/exam/library?subjectCode=${query.subjectCode}">评卷任务管理</a></li>
+        <li><a href="${ctx}/admin/exam/trial?subjectCode=${query.subjectCode}">试评管理</a></li>
         <li><a href="${ctx}/admin/exam/arbitrate?subjectCode=${query.subjectCode}">仲裁管理</a></li>
     </ul>
     <div id="importBox" class="hide">

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

@@ -58,7 +58,7 @@
 			
 			<input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
 			&nbsp;
-			<c:if test="${!web_user.schoolViewer}">
+			<c:if test="${!web_user.schoolViewer} && ${enableExport==true}">
 			<input id="export-button" class="btn" type="button" value="导出" onclick="goExport()"/>
 			</c:if>
 			<c:if test="${web_user.schoolAdmin}">

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

@@ -0,0 +1,124 @@
+<%@ 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" %>
+	<style type="text/css">.sort{color:#0663A2;cursor:pointer;}</style>
+</head>
+<body>
+    <ul class="nav nav-tabs">
+        <li><a href="${ctx}/admin/exam/group?subjectCode=${query.subjectCode}">大题管理</a></li>
+        <li><a href="${ctx}/admin/exam/marker?subjectCode=${query.subjectCode}">评卷员管理</a></li>
+        <li><a href="${ctx}/admin/exam/library?subjectCode=${query.subjectCode}">评卷任务管理</a></li>
+        <li class="active"><a href="##">试评管理</a></li>
+        <li><a href="${ctx}/admin/exam/arbitrate?subjectCode=${query.subjectCode}">仲裁管理</a></li>
+    </ul>
+	<form id="searchForm" action="${ctx}/admin/exam/trial" method="post" class="breadcrumb form-search">
+	    <input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber }"/>
+	    <input type="hidden" id="pageSize" name="pageSize" value="${query.pageSize }"/>
+	    <div>
+			<label>科目</label>
+			<select class="input-large" id="subject-select" name="subjectCode">
+				<c:forEach items="${subjectList}" var="item">
+				<option value="${item.code}" <c:if test="${item.code==query.subjectCode}">selected</c:if>>${item.code}-${item.name}</option>
+				</c:forEach>
+			</select>
+			<label>大题</label>
+            <select class="input-medium" id="group-select" name="groupNumber">
+                <c:forEach items="${groupList}" var="item">
+                <option value="${item.number}" <c:if test="${item.number==query.groupNumber}">selected</c:if>>${item.number}-${item.title}</option>
+                </c:forEach>
+            </select>
+            <label>准考证号</label>
+            <input type="text" name="examNumber" value="${query.examNumber}" maxlength="10" class="input-medium"/>
+			&nbsp;
+			<input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
+		</div>
+	</form>
+	<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>
+				<th>操作</th>
+			</tr>
+		</thead>
+		<tbody>
+		<c:forEach items="${query.result}" var="result">
+			<tr>
+				<td>${result.subjectCode}</td>
+				<td>${result.groupNumber}</td>
+				<td>${result.examNumber}</td>
+				<td>${result.markCount}</td>
+				<td>
+				    <c:if test="${result.markCount>0}">
+				    <a href="##" class="detail-link" data-title="试评给分详情" data-id="${result.id}">详情</a>
+				    <c:if test="${group!=null && group.status==TRIAL}">
+				    &nbsp;
+                    <a href="##" class="reset-link" data-id="${result.id}">重置</a>
+					</c:if>
+					</c:if>
+				</td>
+			</tr>
+		</c:forEach>
+		</tbody>
+	</table>
+	<div class="pagination">${query}</div>
+	<%@include file="/WEB-INF/views/include/trialDetail.jsp" %>
+<script type="text/javascript">
+$('.reset-link').click(function(){
+	if(!confirm('确定要重置该评卷任务吗?')){
+	    return;
+	}
+	$.post('${ctx}/admin/exam/trial/reset', {libraryId: $(this).attr('data-id')}, function(result){
+	    if(result.success==true){
+            alert('重置成功');
+            $("#searchForm").submit();
+        }else{
+            alert(result.message);
+        }
+	});
+});
+$('.detail-link').click(function(){
+    initTrialDetailPopover($(this).attr('data-title'), $(this).attr('data-id'));
+    return false;
+});
+$('#subject-select').change(function(){
+    var code = $(this).val();
+    $('#group-select').empty();
+    if(code==''){
+        $('#group-select').val('').trigger('change');
+        return;
+    }
+    $.post('${ctx}/admin/exam/group/query', {subjectCode: code, status: 'TRIAL'}, function(result){
+        var parent = $('#group-select');
+        var first = '';
+        for(var i=0;i<result.length;i++){
+            var group = result[i];
+            $('<option value="'+group.number+'">'+group.number+'-'+group.title+'</option>').appendTo(parent);
+            if(i==0){
+                first = group.number;
+            }
+        }
+        parent.val(first).trigger('change');
+    });
+});
+function page(n,s){
+    $("#pageNumber").val(n);
+    $("#pageSize").val(s);
+    $("#searchForm").submit();
+    return false;
+}
+function goSearch(){
+    $("#pageNumber").val(1);
+    $("#searchForm").submit();
+    return false;
+}
+</script>	
+</body>
+</html>

+ 4 - 4
stmms-web/src/main/webapp/static/mark-new/js/modules/mark-status.js

@@ -16,14 +16,14 @@ function MarkStatus(option) {
         var task = context.task;
         this.changeStatus(task.statusName);
         //修改页面显示
-        if(task.studentId && task.studentId!=''){
-        	this.topStatus.find('#student-number').html(task.studentId);
+        if(task.studentNumber && task.studentNumber!=''){
+        	this.topStatus.find('#student-number').html(task.studentNumber);
         	this.studentTitle.show();
         }else{
         	this.studentTitle.hide();
         }
-        if(task.libraryId && task.libraryId!=''){
-        	this.topStatus.find('#library-number').html(task.libraryId);
+        if(task.taskNumber && task.taskNumber!=''){
+        	this.topStatus.find('#library-number').html(task.taskNumber);
         	this.libraryTitle.show();
         }else{
         	this.libraryTitle.hide();