소스 검색

Merge commit '8d9eb122663606c816c2370c968dae222fe67780' into
stmms_ft_dev

* commit '8d9eb122663606c816c2370c968dae222fe67780':
评卷分组与阅卷轨迹部分重构
重新修改锁机制,并增加自动清理功能,防止内存占用过多
简化全局内存锁的评卷分组锁实现方式
bug修复与功能优化

# Conflicts:
# stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkTrackServiceImpl.java
# stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkTrackService.java
# stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkTrackController.java

ting.yin 6 년 전
부모
커밋
4c60ab3794
26개의 변경된 파일589개의 추가작업 그리고 686개의 파일을 삭제
  1. 6 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java
  2. 9 17
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkTrackDao.java
  3. 29 18
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkTrack.java
  4. 10 10
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkTrackPK.java
  5. 10 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/Task.java
  6. 3 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/TrackDTO.java
  7. 16 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java
  8. 48 16
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkLockService.java
  9. 53 47
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  10. 5 40
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkTrackServiceImpl.java
  11. 0 101
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java
  12. 4 11
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  13. 15 27
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkTrackService.java
  14. 198 198
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java
  15. 15 15
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java
  16. 22 21
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkTrackController.java
  17. 0 4
      stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java
  18. 5 6
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  19. 2 1
      stmms-web/src/main/webapp/WEB-INF/application.properties
  20. 3 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markNew.jsp
  21. 3 8
      stmms-web/src/main/webapp/static/mark-new/js/mark-control.js
  22. 9 7
      stmms-web/src/main/webapp/static/mark-new/js/modules/mark-board.js
  23. 3 2
      stmms-web/src/main/webapp/static/mark-new/js/modules/mark-history.js
  24. 15 15
      stmms-web/src/main/webapp/static/mark-new/js/modules/simple-image-view.js
  25. 42 38
      stmms-web/src/main/webapp/static/mark-new/js/modules/single-image-view.js
  26. 64 79
      stmms-web/src/main/webapp/static/mark-new/js/modules/specialTag.js

+ 6 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java

@@ -11,6 +11,7 @@ import org.springframework.data.repository.PagingAndSortingRepository;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroupPK;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.ScorePolicy;
 
 public interface MarkGroupDao
         extends PagingAndSortingRepository<MarkGroup, MarkGroupPK>, JpaSpecificationExecutor<MarkGroup> {
@@ -89,10 +90,14 @@ public interface MarkGroupDao
     @Query("update MarkGroup g set g.title=?4 where g.pk.examId=?1 and g.pk.subjectCode=?2 and g.pk.number=?3")
     void updateTitle(int examId, String subjectCode, int number, String title);
 
-    @Modifying
+    @Modifying(clearAutomatically = true)
     @Query("update MarkGroup g set g.totalScore=?4 where g.pk.examId=?1 and g.pk.subjectCode=?2 and g.pk.number=?3")
     void updateTotalScore(int examId, String subjectCode, int number, Double score);
 
+    @Modifying(clearAutomatically = true)
+    @Query("update MarkGroup g set g.scorePolicy=?4 where g.pk.examId=?1 and g.pk.subjectCode=?2 and g.pk.number=?3")
+    void updateScorePolicy(int examId, String subjectCode, int number, ScorePolicy policy);
+
     @Modifying(clearAutomatically = true)
     @Query("update MarkGroup g set g.buildTime=?4 where g.pk.examId=?1 and g.pk.subjectCode=?2 and g.pk.number=?3")
     void updateBuildTime(int examId, String subjectCode, int number, Date time);

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

@@ -12,33 +12,25 @@ import cn.com.qmth.stmms.biz.mark.model.MarkTrackPK;
 
 public interface MarkTrackDao extends JpaRepository<MarkTrack, MarkTrackPK>, JpaSpecificationExecutor<MarkTrack> {
 
-    List<MarkTrack> findByPkStudentId(Integer studentId);
+    List<MarkTrack> findByPkLibraryId(Integer libraryId);
 
-    @Modifying
-    @Query("delete from MarkTrack t where t.pk.studentId=?1 and t.markerId=?2")
-    void deleteByStudentIdAndMarkerId(Integer studentId, Integer markerId);
-
-    @Modifying
-    @Query("delete from MarkTrack t where t.pk.studentId=?1")
-    void deleteByStudentId(Integer studentId);
-
-    @Modifying
-    @Query("delete from MarkTrack t where t.pk.studentId=?1 and t.pk.questionNumber=?2")
-    void deleteByStudentIdAndQuestionNumber(Integer studentId, String questionNumber);
+    List<MarkTrack> findByStudentId(Integer studentId);
 
     @Modifying
-    @Query("delete from MarkTrack t where t.examId=?1")
-    void deleteByExamId(Integer examId);
+    @Query("delete from MarkTrack t where t.pk.libraryId=?1")
+    void deleteByLibraryId(Integer libraryId);
 
     @Modifying
-    @Query("delete from MarkTrack t where t.examId=?1 and t.subjectCode=?2 and t.groupNumber=?3")
-    void deleteByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer number);
+    @Query("delete from MarkTrack t where t.studentId=?1")
+    void deleteByStudentId(Integer studentId);
 
     @Modifying
     @Query("delete from MarkTrack t where t.markerId=?1")
     void deleteByMarkerId(Integer markerId);
 
-    List<MarkTrack> findByPkStudentIdAndGroupNumber(Integer studentId, Integer number);
+    @Modifying
+    @Query("delete from MarkTrack t where t.examId=?1 and t.subjectCode=?2 and t.groupNumber=?3")
+    void deleteByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber);
 
 	List<MarkTrack> findByPkStudentIdAndGroupNumberAndMarkerId(Integer studentId,Integer number, Integer markerId);
 

+ 29 - 18
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkTrack.java

@@ -18,6 +18,9 @@ public class MarkTrack {
     @EmbeddedId
     private MarkTrackPK pk;
 
+    @Column(name = "student_id", nullable = false)
+    private Integer studentId;
+
     @Column(name = "exam_id", nullable = false)
     private Integer examId;
 
@@ -52,12 +55,12 @@ public class MarkTrack {
         this.pk = new MarkTrackPK();
     }
 
-    public Integer getStudentId() {
-        return pk.getStudentId();
+    public Integer getLibraryId() {
+        return pk.getLibraryId();
     }
 
-    public void setStudentId(Integer studentId) {
-        pk.setStudentId(studentId);
+    public void setLibraryId(Integer libraryId) {
+        pk.setLibraryId(libraryId);
     }
 
     public String getQuestionNumber() {
@@ -100,36 +103,44 @@ public class MarkTrack {
         this.positionY = positionY;
     }
 
-    public Integer getExamId() {
-        return examId;
+    public void setMarkerId(Integer markerId) {
+        this.markerId = markerId;
     }
 
-    public void setExamId(Integer examId) {
-        this.examId = examId;
+    public Integer getGroupNumber() {
+        return groupNumber;
     }
 
-    public String getSubjectCode() {
-        return subjectCode;
+    public void setGroupNumber(Integer groupNumber) {
+        this.groupNumber = groupNumber;
     }
 
-    public void setSubjectCode(String subjectCode) {
-        this.subjectCode = subjectCode;
+    public Integer getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Integer studentId) {
+        this.studentId = studentId;
     }
 
     public Integer getMarkerId() {
         return markerId;
     }
 
-    public void setMarkerId(Integer markerId) {
-        this.markerId = markerId;
+    public Integer getExamId() {
+        return examId;
     }
 
-    public Integer getGroupNumber() {
-        return groupNumber;
+    public void setExamId(Integer examId) {
+        this.examId = examId;
     }
 
-    public void setGroupNumber(Integer groupNumber) {
-        this.groupNumber = groupNumber;
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
     }
 
 }

+ 10 - 10
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkTrackPK.java

@@ -10,8 +10,8 @@ public class MarkTrackPK implements Serializable {
 
     private static final long serialVersionUID = 9096523417236451724L;
 
-    @Column(name = "student_id", nullable = false)
-    private Integer studentId;
+    @Column(name = "library_id", nullable = false)
+    private Integer libraryId;
 
     @Column(name = "question_number", nullable = false)
     private String questionNumber;
@@ -19,12 +19,12 @@ public class MarkTrackPK implements Serializable {
     @Column(name = "number", nullable = false)
     private Integer number;
 
-    public Integer getStudentId() {
-        return studentId;
+    public Integer getLibraryId() {
+        return libraryId;
     }
 
-    public void setStudentId(Integer studentId) {
-        this.studentId = studentId;
+    public void setLibraryId(Integer libraryId) {
+        this.libraryId = libraryId;
     }
 
     public String getQuestionNumber() {
@@ -47,7 +47,7 @@ public class MarkTrackPK implements Serializable {
     public int hashCode() {
         final int PRIME = 31;
         int result = 1;
-        result = PRIME * result + ((studentId == null) ? 0 : studentId.hashCode());
+        result = PRIME * result + ((libraryId == null) ? 0 : libraryId.hashCode());
         result = PRIME * result + ((questionNumber == null) ? 0 : questionNumber.hashCode());
         result = PRIME * result + ((number == null) ? 0 : number.hashCode());
         return result;
@@ -62,10 +62,10 @@ public class MarkTrackPK implements Serializable {
         if (getClass() != obj.getClass())
             return false;
         final MarkTrackPK other = (MarkTrackPK) obj;
-        if (studentId == null) {
-            if (other.studentId != null)
+        if (libraryId == null) {
+            if (other.libraryId != null)
                 return false;
-        } else if (!studentId.equals(other.studentId))
+        } else if (!libraryId.equals(other.libraryId))
             return false;
         if (questionNumber == null) {
             if (other.questionNumber != null)

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

@@ -348,6 +348,16 @@ public class Task implements Serializable {
         return map;
     }
 
+    public List<MarkTrack> getTrackList(MarkLibrary library) {
+        List<MarkTrack> list = new LinkedList<>();
+        if (trackList != null) {
+            for (TrackDTO dto : trackList) {
+                list.add(dto.transform(library));
+            }
+        }
+        return list;
+    }
+
     public SpecialTagDTO[] getTagList() {
         return tagList;
     }

+ 3 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/TrackDTO.java

@@ -36,16 +36,17 @@ public class TrackDTO implements Serializable {
 
     public MarkTrack transform(MarkLibrary library) {
         MarkTrack track = new MarkTrack();
-        track.setStudentId(library.getStudentId());
+        track.setLibraryId(library.getId());
         track.setQuestionNumber(getQuestionNumber());
         track.setNumber(getNumber());
+        track.setStudentId(library.getStudentId());
         track.setExamId(library.getExamId());
         track.setSubjectCode(library.getSubjectCode());
+        track.setGroupNumber(library.getGroupNumber());
         track.setMarkerId(library.getMarkerId());
         track.setScore(getScore());
         track.setPositionX(getPositionX());
         track.setPositionY(getPositionY());
-        track.setGroupNumber(library.getGroupNumber());
         return track;
     }
 

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

@@ -58,13 +58,28 @@ public class MarkCronService {
     @Autowired
     private MarkService markService;
 
+    @Autowired
+    private MarkLockService lockService;
+
     @Value("${mark.cleanTimeoutMinute}")
     private long timeoutMinute;
 
+    /**
+     * 自动释放可清除的锁
+     */
+    @Scheduled(cron = "${mark.cleanLockSchedule}")
+    public void cronCleanLock() {
+        try {
+            lockService.clear();
+        } catch (Exception e) {
+            log.error("CronCleanTask error", e);
+        }
+    }
+
     /**
      * 自动释放超时未处理的评卷任务
      */
-    @Scheduled(cron = "${mark.cleanSchedule}")
+    @Scheduled(cron = "${mark.cleanTaskSchedule}")
     public void cronCleanTask() {
         try {
             CurrentTaskUtil.clearTimeoutTask(timeoutMinute);

+ 48 - 16
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkLockService.java

@@ -1,8 +1,10 @@
 package cn.com.qmth.stmms.biz.mark.service.Impl;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.slf4j.Logger;
@@ -21,9 +23,9 @@ public class MarkLockService {
 
     protected static final Logger log = LoggerFactory.getLogger(MarkLockService.class);
 
-    private Map<String, AtomicBoolean> groupMap = new HashMap<>();
+    private Map<Object, AtomicBoolean> groupMap = new HashMap<>();
 
-    private Map<Integer, Boolean> studentMap = new ConcurrentHashMap<>();
+    private Map<Object, AtomicBoolean> studentMap = new HashMap<>();
 
     /**
      * 对评卷分组进行重置/删除操作前,先尝试锁定
@@ -31,9 +33,9 @@ public class MarkLockService {
      * @param group
      */
     public void lockGroup(Integer examId, String subjectCode, Integer groupNumber) {
-        AtomicBoolean groupLock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
-        while (!groupLock.get()) {
-            groupLock.compareAndSet(false, true);
+        AtomicBoolean lock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
+        while (!lock.get()) {
+            lock.compareAndSet(false, true);
         }
     }
 
@@ -43,9 +45,9 @@ public class MarkLockService {
      * @param group
      */
     public void unlockGroup(Integer examId, String subjectCode, Integer groupNumber) {
-        AtomicBoolean groupLock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
-        while (groupLock.get()) {
-            groupLock.compareAndSet(true, false);
+        AtomicBoolean lock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
+        while (lock != null && lock.get()) {
+            lock.compareAndSet(true, false);
         }
     }
 
@@ -55,8 +57,8 @@ public class MarkLockService {
      * @param group
      */
     public void waitUnlockGroup(Integer examId, String subjectCode, Integer groupNumber) {
-        AtomicBoolean groupLock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
-        while (groupLock.get()) {
+        AtomicBoolean lock = getLock(groupMap, getKey(examId, subjectCode, groupNumber));
+        while (lock.get()) {
             ;
         }
     }
@@ -67,7 +69,10 @@ public class MarkLockService {
      * @param studentId
      */
     public void lockStudent(Integer studentId) {
-        studentMap.put(studentId, Boolean.TRUE);
+        AtomicBoolean lock = getLock(studentMap, studentId);
+        while (!lock.get()) {
+            lock.compareAndSet(false, true);
+        }
     }
 
     /**
@@ -76,7 +81,10 @@ public class MarkLockService {
      * @param studentId
      */
     public void unlockStudent(Integer studentId) {
-        studentMap.remove(studentId);
+        AtomicBoolean lock = getLock(studentMap, studentId);
+        while (lock.get()) {
+            lock.compareAndSet(true, false);
+        }
     }
 
     /**
@@ -85,17 +93,41 @@ public class MarkLockService {
      * @param studentId
      */
     public void waitUnlockStudent(Integer studentId) {
-        while (studentMap.get(studentId) != null) {
+        AtomicBoolean lock = getLock(studentMap, studentId);
+        while (lock.get()) {
             ;
         }
     }
 
-    private AtomicBoolean getLock(Map<String, AtomicBoolean> map, String key) {
+    public void clear() {
+        clearLock(groupMap);
+        clearLock(studentMap);
+    }
+
+    private void clearLock(Map<Object, AtomicBoolean> map) {
+        if (map.isEmpty()) {
+            return;
+        }
+        synchronized (map) {
+            Set<Object> keys = new HashSet<>();
+            for (Entry<Object, AtomicBoolean> entry : map.entrySet()) {
+                if (!entry.getValue().get()) {
+                    keys.add(entry.getKey());
+                }
+            }
+            for (Object key : keys) {
+                map.remove(key);
+            }
+            keys.clear();
+        }
+    }
+
+    private AtomicBoolean getLock(Map<Object, AtomicBoolean> map, Object key) {
         AtomicBoolean lock = map.get(key);
         if (lock == null) {
             synchronized (map) {
                 lock = map.get(key);
-                if (lock != null) {
+                if (lock == null) {
                     lock = new AtomicBoolean(false);
                     map.put(key, lock);
                 }

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

@@ -3,7 +3,6 @@ package cn.com.qmth.stmms.biz.mark.service.Impl;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
-import java.util.Map;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -124,15 +123,7 @@ public class MarkServiceImpl implements MarkService {
     public void resetByGroup(MarkGroup group) {
         try {
             lockService.lockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            groupDao.resetCount(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            libraryDao.resetByExamIdAndSubjectCodeAndNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber(), LibraryStatus.WAITING);
-            arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            releaseByGroup(group);
+            resetGroup(group);
         } catch (Exception e) {
             throw e;
         } finally {
@@ -176,28 +167,37 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     @Transactional
-    public void updateGroupScore(MarkGroup group, List<Double> scores) {
-        resetByGroup(group);
-        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(group.getExamId(), group.getSubjectCode(),
-                false, group.getNumber());
-        int i = 0;
-        double totalScore = 0d;
-        for (Double score : scores) {
-            i++;
-            totalScore += score;
-            ExamQuestion question = new ExamQuestion();
-            question.setExamId(group.getExamId());
-            question.setSubjectCode(group.getSubjectCode());
-            question.setMainTitle(group.getTitle());
-            question.setMainNumber(group.getNumber());
-            question.setSubNumber(i);
-            question.setObjective(false);
-            question.setTotalScore(score);
-            question.setIntervalScore(1d);
-            questionDao.save(question);
+    public void updateGroup(MarkGroup group, List<Double> scores, ScorePolicy policy) {
+        try {
+            lockService.lockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
+
+            questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(group.getExamId(), group.getSubjectCode(),
+                    false, group.getNumber());
+            int i = 0;
+            double totalScore = 0d;
+            for (Double score : scores) {
+                i++;
+                totalScore += score;
+                ExamQuestion question = new ExamQuestion();
+                question.setExamId(group.getExamId());
+                question.setSubjectCode(group.getSubjectCode());
+                question.setMainTitle(group.getTitle());
+                question.setMainNumber(group.getNumber());
+                question.setSubNumber(i);
+                question.setObjective(false);
+                question.setTotalScore(score);
+                question.setIntervalScore(1d);
+                questionDao.save(question);
+            }
+            groupDao.updateTotalScore(group.getExamId(), group.getSubjectCode(), group.getNumber(), totalScore);
+            groupDao.updateScorePolicy(group.getExamId(), group.getSubjectCode(), group.getNumber(), policy);
+            subjectService.updateScore(group.getExamId(), group.getSubjectCode());
+            resetGroup(group);
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            lockService.unlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
         }
-        groupDao.updateTotalScore(group.getExamId(), group.getSubjectCode(), group.getNumber(), totalScore);
-        subjectService.updateScore(group.getExamId(), group.getSubjectCode());
     }
 
     /**
@@ -289,8 +289,7 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     @Transactional
-    public void submitLibrary(MarkLibrary library, Map<String, List<MarkTrack>> trackMap,
-            List<MarkSpecialTag> tagList) {
+    public void submitLibrary(MarkLibrary library, List<MarkTrack> trackList, List<MarkSpecialTag> tagList) {
         // 判断大题是否存在
         MarkGroup group = groupDao.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
         if (group == null) {
@@ -305,14 +304,12 @@ public class MarkServiceImpl implements MarkService {
             return;
         }
         // 保存阅卷轨迹
-        if (trackMap != null) {
-            for (String questionNumber : trackMap.keySet()) {
-                trackDao.deleteByStudentIdAndQuestionNumber(library.getStudentId(), questionNumber);
-                trackDao.save(trackMap.get(questionNumber));
-            }
+        if (trackList != null) {
+            trackDao.deleteByLibraryId(library.getId());
+            trackDao.save(trackList);
         }
         // 保存特殊标记
-        if (tagList != null && tagList.size() > 0) {
+        if (tagList != null) {
             specialTagDao.deleteByLibraryId(library.getId());
             specialTagDao.save(tagList);
         }
@@ -366,7 +363,7 @@ public class MarkServiceImpl implements MarkService {
     public void backLibrary(MarkLibrary library) {
         if (library.getStatus() == LibraryStatus.MARKED) {
             lockService.waitUnlockGroup(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
-            trackDao.deleteByStudentIdAndMarkerId(library.getStudentId(), library.getMarkerId());
+            trackDao.deleteByLibraryId(library.getId());
             specialTagDao.deleteByLibraryId(library.getId());
             libraryDao.resetById(library.getId(), LibraryStatus.BACKED);
             updateLibraryCount(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
@@ -578,12 +575,21 @@ public class MarkServiceImpl implements MarkService {
         return library.getStudentId() + "_" + library.getGroupNumber();
     }
 
-    @Transactional
-	@Override
-	public void updateGroupScorePolicy(MarkGroup group, Integer scorePolicy) {
-		group.setScorePolicy(ScorePolicy.findByValue(scorePolicy));
-		groupDao.save(group);
-		resetByGroup(group);
-	}
+    /**
+     * 重置评卷分组的连带操作
+     * 
+     * @param group
+     */
+    private void resetGroup(MarkGroup group) {
+        groupDao.resetCount(group.getExamId(), group.getSubjectCode(), group.getNumber());
+        libraryDao.resetByExamIdAndSubjectCodeAndNumber(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                LibraryStatus.WAITING);
+        arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
+        releaseByGroup(group);
+    }
 
 }

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

@@ -19,53 +19,18 @@ public class MarkTrackServiceImpl extends BaseQueryService<MarkTrack> implements
 
     @Override
     public List<MarkTrack> findByStudentId(Integer studentId) {
-        return markTrackDao.findByPkStudentId(studentId);
+        return markTrackDao.findByStudentId(studentId);
     }
 
-    @Transactional
-    @Override
-    public MarkTrack save(MarkTrack track) {
-        return markTrackDao.save(track);
-    }
-
-    @Transactional
-    @Override
-    public void deleteByStudentId(Integer studentId) {
-        markTrackDao.deleteByStudentId(studentId);
-    }
-
-    @Transactional
-    @Override
-    public void deleteByStudentIdAndQuestionNumber(Integer studentId, String questionNumber) {
-        markTrackDao.deleteByStudentIdAndQuestionNumber(studentId, questionNumber);
-    }
-
-    @Transactional
-    @Override
-    public void deleteByExamId(Integer examId) {
-        markTrackDao.deleteByExamId(examId);
-    }
-
-    @Transactional
     @Override
-    public void deleteByExamAndSubjectAndGroup(Integer examId, String subjectCode, Integer number) {
-        markTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, number);
+    public List<MarkTrack> findByLibraryId(Integer libraryId) {
+        return markTrackDao.findByPkLibraryId(libraryId);
     }
 
     @Transactional
     @Override
-    public void deleteByMarkerId(Integer markerId) {
-        markTrackDao.deleteByMarkerId(markerId);
-    }
-
-    @Override
-    public List<MarkTrack> findByStudentIdAndGroupNumber(Integer studentId, Integer number) {
-        return markTrackDao.findByPkStudentIdAndGroupNumber(studentId, number);
+    public MarkTrack save(MarkTrack track) {
+        return markTrackDao.save(track);
     }
 
-	@Override
-	public List<MarkTrack> findByStudentIdAndGroupNumberAndMarkerId(
-			Integer studentId, Integer number, Integer markerId) {
-		return markTrackDao.findByPkStudentIdAndGroupNumberAndMarkerId(studentId, number,markerId);
-	}
 }

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

@@ -1,12 +1,9 @@
 package cn.com.qmth.stmms.biz.mark.service.Impl;
 
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
-import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -33,12 +30,10 @@ import cn.com.qmth.stmms.biz.mark.model.SpecialTagDTO;
 import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.model.TrackDTO;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
-import cn.com.qmth.stmms.biz.mark.service.ArbitrateHistoryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
 import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
-import cn.com.qmth.stmms.common.enums.HistoryStatus;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
 
@@ -77,9 +72,6 @@ public class TaskServiceImpl implements TaskService {
     @Autowired
     private MarkerService markerService;
 
-    @Autowired
-    private ArbitrateHistoryService arbitrateService;
-
     @Override
     public List<Task> findByQuery(MarkLibrarySearchQuery query) {
         List<Task> list = new LinkedList<Task>();
@@ -202,99 +194,6 @@ public class TaskServiceImpl implements TaskService {
         }
     }
 
-    /**
-     * 保存评卷任务
-     * 
-     * @param task
-     * @return
-     */
-    private MarkLibrary updateLibrary(Task task) {
-        MarkLibrary library = libraryService.findById(task.getLibraryId());
-        if (library != null) {
-            // 判断大题是否存在
-            MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(),
-                    library.getGroupNumber());
-            if (group == null) {
-                return null;
-            }
-            // 保存阅卷轨迹
-            Map<String, List<TrackDTO>> trackMap = task.getTrackMap();
-            for (String questionNumber : trackMap.keySet()) {
-                trackService.deleteByStudentIdAndQuestionNumber(library.getStudentId(), questionNumber);
-                List<TrackDTO> list = trackMap.get(questionNumber);
-                for (TrackDTO dto : list) {
-                    trackService.save(dto.transform(library));
-                }
-            }
-            // 保存特殊标记
-            SpecialTagDTO[] tagList = task.getTagList();
-            if (tagList != null && tagList.length > 0) {
-                markSpecialTagService.deleteByLibraryId(library.getId());
-                for (SpecialTagDTO s : tagList) {
-                    markSpecialTagService.save(s.transform(library));
-                }
-            }
-
-            if (task.getHeaderId() != null) {
-                // 组长或管理员打分模式
-                library.setHeaderId(task.getHeaderId());
-                library.setHeaderTime(new Date());
-                library.setHeaderScore(task.getHeaderScore());
-                library.setHeaderScoreList(task.getHeaderScoreList());
-                library.setStatus(LibraryStatus.MARKED);
-                library.setTags(StringUtils.trimToNull(task.getTags()));
-            } else {
-                // 评卷员打分模式
-                library.setMarkerId(task.getMarkId());
-                library.setMarkerTime(new Date());
-                library.setMarkerScore(task.getTotalScore());
-                library.setMarkerScoreList(task.getScoreList());
-                library.setStatus(LibraryStatus.MARKED);
-                library.setTags(StringUtils.trimToNull(task.getTags()));
-
-                List<MarkLibrary> list = libraryService.findByStudentAndGroup(library.getStudentId(),
-                        library.getGroupNumber());
-                ArbitrateHistory history = null;
-                if (group.getArbitrateThreshold() != null && group.getArbitrateThreshold() > 0) {
-                    // 多评模式
-                    for (MarkLibrary other : list) {
-                        if (other.getId().equals(library.getId()) || other.getStatus() != LibraryStatus.MARKED
-                                || other.getMarkerScore() == null || other.getHeaderScore() != null) {
-                            // 未评卷或组长已打分,则跳过该任务
-                            continue;
-                        }
-                        if (Math.abs(other.getMarkerScore() - library.getMarkerScore()) > group
-                                .getArbitrateThreshold()) {
-                            // 分差超过阀值,触发仲裁
-                            history = new ArbitrateHistory();
-                            history.setExamId(library.getExamId());
-                            history.setSubjectCode(library.getSubjectCode());
-                            history.setGroupNumber(library.getGroupNumber());
-                            history.setStudentId(library.getStudentId());
-                            history.setExamNumber(library.getExamNumber());
-                            history.setStatus(HistoryStatus.WAITING);
-                            history.setCreateTime(new Date());
-                            arbitrateService.save(history);
-
-                            library.setStatus(LibraryStatus.WAIT_ARBITRATE);
-                            break;
-                        }
-                    }
-                }
-                // library = libraryService.save(library);
-                // 触发仲裁后续处理
-                if (history != null) {
-                    // libraryService.setArbitrate(history.getStudentId(),
-                    // history.getGroupNumber());
-                }
-            }
-            // 更新评卷任务数量
-            // groupService.updateLibraryCount(library.getExamId(),
-            // library.getSubjectCode(), library.getGroupNumber());
-        }
-        return library;
-    }
-
     public SpecialTagDTO[] getMarkSpecialTagList(Integer libraryId) {
         SpecialTagDTO[] specialTags = null;
         List<MarkSpecialTag> list = markSpecialTagService.findByLibraryId(libraryId);

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

@@ -1,7 +1,6 @@
 package cn.com.qmth.stmms.biz.mark.service;
 
 import java.util.List;
-import java.util.Map;
 
 import cn.com.qmth.stmms.biz.campus.model.Campus;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
@@ -12,6 +11,7 @@ import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.model.MarkSpecialTag;
 import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
 import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.enums.ScorePolicy;
 
 public interface MarkService {
 
@@ -41,7 +41,7 @@ public interface MarkService {
      * 
      * @param group
      */
-    void updateGroupScore(MarkGroup group, List<Double> scores);
+    void updateGroup(MarkGroup group, List<Double> scores, ScorePolicy policy);
 
     /**
      * 评卷员申请领取某个评卷任务
@@ -99,10 +99,10 @@ public interface MarkService {
      * 评卷员提交某个评卷任务
      * 
      * @param library
-     * @param trackMap
+     * @param trackList
      * @param tagList
      */
-    void submitLibrary(MarkLibrary library, Map<String, List<MarkTrack>> trackMap, List<MarkSpecialTag> tagList);
+    void submitLibrary(MarkLibrary library, List<MarkTrack> trackList, List<MarkSpecialTag> tagList);
 
     /**
      * 管理员/组长直接对考生打分
@@ -162,11 +162,4 @@ public interface MarkService {
      */
     void buildLibrary(ExamStudent student, Campus campus, MarkGroup group);
 
-    /**
-     * 更新某个大题判分策略并重置
-     * @param group
-     * @param scorePolicy
-     */
-	void updateGroupScorePolicy(MarkGroup group, Integer scorePolicy);
-
 }

+ 15 - 27
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkTrackService.java

@@ -1,27 +1,15 @@
-package cn.com.qmth.stmms.biz.mark.service;
-
-import java.util.List;
-
-import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
-
-public interface MarkTrackService {
-
-    MarkTrack save(MarkTrack track);
-
-    List<MarkTrack> findByStudentId(Integer studentId);
-
-    void deleteByStudentId(Integer studentId);
-
-    void deleteByStudentIdAndQuestionNumber(Integer studentId, String questionNumber);
-
-    void deleteByExamId(Integer examId);
-
-    void deleteByMarkerId(Integer markerId);
-
-    List<MarkTrack> findByStudentIdAndGroupNumber(Integer studentId, Integer number);
-
-    void deleteByExamAndSubjectAndGroup(Integer examId, String subjectCode, Integer number);
-
-	List<MarkTrack> findByStudentIdAndGroupNumberAndMarkerId(Integer studentId,Integer number, Integer markerId);
-
-}
+package cn.com.qmth.stmms.biz.mark.service;
+
+import java.util.List;
+
+import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
+
+public interface MarkTrackService {
+
+    MarkTrack save(MarkTrack track);
+
+    List<MarkTrack> findByStudentId(Integer studentId);
+
+    List<MarkTrack> findByLibraryId(Integer libraryId);
+
+}

+ 198 - 198
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java

@@ -1,198 +1,198 @@
-package cn.com.qmth.stmms.admin.exam;
-
-import java.util.ArrayList;
-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.stereotype.Controller;
-import org.springframework.transaction.annotation.Transactional;
-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.admin.utils.SessionExamUtils;
-import cn.com.qmth.stmms.admin.vo.ExamInfoVO;
-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.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
-import cn.com.qmth.stmms.biz.exam.service.MarkerService;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.ExamStatus;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.Paginator;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-@Controller
-@RequestMapping("/admin")
-public class ExamController extends BaseExamController {
-
-    protected static Logger log = LoggerFactory.getLogger(ExamController.class);
-
-    @Autowired
-    private ExamService examService;
-
-    @Autowired
-    private ExamStudentService examStudentService;
-
-    @Autowired
-    private MarkerService markerService;
-
-    @Autowired
-    private ExamSubjectService examSubjectService;
-
-    @RequestMapping(value = { "/exam-list" })
-    public String examList(Model model, HttpServletRequest request, ExamSearchQuery query) {
-        SessionExamUtils.clearExamId(request);
-        User user = RequestUtils.getWebUser(request).getUser();
-        query.setSchoolId(user.getSchoolId());
-        query.setOrderByCreateTimeDesc(true);
-        query = examService.findByQuery(query);
-        if (query.getCurrentCount() > 0) {
-            model.addAttribute("examList", query.getResult());
-            model.addAttribute("paginator",
-                    new Paginator(query.getPageNumber(), query.getPageSize(), (int) query.getTotalCount()));
-        }
-        model.addAttribute("query", query);
-        return "modules/exam/examList";
-    }
-
-    @RequestMapping("/exam-create")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String add(Exam exam, Model model) {
-        model.addAttribute("exam", exam);
-        model.addAttribute("statusList", ExamStatus.values());
-        return "modules/exam/examForm";
-    }
-    
-    @RequestMapping("/exam-edit/{examId}")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String edit(@PathVariable Integer examId, Model model) {
-    	Exam exam = examService.findById(examId);
-        model.addAttribute("exam", exam);
-        model.addAttribute("statusList", ExamStatus.values());
-        return "modules/exam/examEdit";
-    }
-    
-    @RequestMapping(value = "/exam-save", method = RequestMethod.POST)
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String save(HttpServletRequest request, Exam exam, RedirectAttributes redirectAttributes) {
-        User user = RequestUtils.getWebUser(request).getUser();
-        exam.setSchoolId(user.getSchoolId());
-        exam.setCreatorId(user.getId());
-        exam.setStatus(ExamStatus.START);
-        exam = examService.save(exam);
-        addMessage(redirectAttributes, "创建考试'" + exam.getName() + "'成功");
-        return "redirect:/admin/exam-list";
-    }
-
-    @RequestMapping(value = "/exam-edit", method = RequestMethod.POST)
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String examEdit(HttpServletRequest request,Exam exam,int StatusValue, RedirectAttributes redirectAttributes) {
-        User user = RequestUtils.getWebUser(request).getUser();
-        Exam oldExam = examService.findById(exam.getId());
-        if (oldExam != null && oldExam.getCreatorId().intValue() == user.getId().intValue()) {
-            oldExam.setName(exam.getName());
-            oldExam.setExamTime(exam.getExamTime());
-            oldExam.setDescription(exam.getDescription());
-            oldExam.setStatus(ExamStatus.findByValue(StatusValue));
-            oldExam.setForceSpecialTag(exam.isForceSpecialTag());
-            examService.save(oldExam);
-        }
-        return "redirect:/admin/exam-list";
-    }
-
-    @RequestMapping("/exam-select/{examId}")
-    public String select(Model model, HttpServletRequest request, @PathVariable Integer examId) {
-        SessionExamUtils.setExamId(request, examId);
-        WebUser wu = RequestUtils.getWebUser(request);
-        if(wu.isSchoolViewer()){
-        	return "redirect:/admin/exam/score";
-        }
-        return "redirect:/admin/exam/student";
-    }
-
-    @RequestMapping("/exam-view/{examId}")
-    public String view(Model model, HttpServletRequest request, @PathVariable Integer examId) {
-        SessionExamUtils.setExamId(request, examId);
-
-        Exam exam = examService.findById(examId);
-        long studentCount = examStudentService.countByExamId(exam.getId());
-        long subjectCount = examSubjectService.count(examId);
-        long campusCount = examStudentService.countCampusByExam(examId);
-        long markerCount = markerService.countByExam(exam.getId());
-        long scanCount = examStudentService.countByExamIdAndUpload(examId, true);
-        long markedCount = examStudentService.countByExamIdAndSubjectiveScoreListIsNotNull(examId);
-
-        List<ExamInfoVO> voList = new ArrayList<ExamInfoVO>();
-
-        ExamInfoVO student = new ExamInfoVO();
-        student.setName("考生");
-        student.setAttr("共导入 " + studentCount + " 个考生");
-        student.setUrl("/admin/exam/student");
-
-        ExamInfoVO subject = new ExamInfoVO();
-        subject.setName("科目");
-        subject.setAttr("共导入 " + subjectCount + " 个科目");
-        subject.setUrl("/admin/exam/paper");
-
-        ExamInfoVO campus = new ExamInfoVO();
-        campus.setName("学习中心");
-        campus.setAttr("共导入 " + campusCount + " 个学习中心");
-        campus.setUrl("/admin/exam/student");
-
-        ExamInfoVO marker = new ExamInfoVO();
-        marker.setName("评卷员");
-        marker.setAttr("已创建 " + markerCount + " 个评卷员");
-        marker.setUrl("/admin/exam/marker");
-
-        ExamInfoVO scan = new ExamInfoVO();
-        scan.setName("扫描进度");
-        scan.setAttr("已扫描 " + scanCount + " 个考生");
-        scan.setUrl("/admin/exam/scan");
-
-        ExamInfoVO mark = new ExamInfoVO();
-        mark.setName("评卷进度");
-        mark.setAttr("已评完 " + markedCount + " 个考生");
-        mark.setUrl("/admin/exam/mark");
-
-        voList.add(student);
-        voList.add(subject);
-        voList.add(campus);
-        voList.add(marker);
-        voList.add(scan);
-        voList.add(mark);
-        model.addAttribute("exam", exam);
-        model.addAttribute("list", voList);
-        return "modules/exam/examInfo";
-    }
-
-    @RequestMapping("/exam/info")
-    @ResponseBody
-    public Exam getExamInfo(HttpServletRequest request) {
-        Exam exam = examService.findById(getSessionExamId(request));
-        return exam;
-    }
-
-    @RequestMapping("/exam/finish")
-    @Transactional
-    @ResponseBody
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public void finished(HttpServletRequest request) {
-        Exam exam = examService.findById(getSessionExamId(request));
-        exam.setStatus(ExamStatus.FINISH);
-        examService.save(exam);
-    }
-
-}
+package cn.com.qmth.stmms.admin.exam;
+
+import java.util.ArrayList;
+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.stereotype.Controller;
+import org.springframework.transaction.annotation.Transactional;
+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.ResponseBody;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.admin.utils.SessionExamUtils;
+import cn.com.qmth.stmms.admin.vo.ExamInfoVO;
+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.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.ExamStatus;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.Paginator;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+@Controller
+@RequestMapping("/admin")
+public class ExamController extends BaseExamController {
+
+    protected static Logger log = LoggerFactory.getLogger(ExamController.class);
+
+    @Autowired
+    private ExamService examService;
+
+    @Autowired
+    private ExamStudentService examStudentService;
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Autowired
+    private ExamSubjectService examSubjectService;
+
+    @RequestMapping(value = { "/exam-list" })
+    public String examList(Model model, HttpServletRequest request, ExamSearchQuery query) {
+        SessionExamUtils.clearExamId(request);
+        User user = RequestUtils.getWebUser(request).getUser();
+        query.setSchoolId(user.getSchoolId());
+        query.setOrderByCreateTimeDesc(true);
+        query = examService.findByQuery(query);
+        if (query.getCurrentCount() > 0) {
+            model.addAttribute("examList", query.getResult());
+            model.addAttribute("paginator",
+                    new Paginator(query.getPageNumber(), query.getPageSize(), (int) query.getTotalCount()));
+        }
+        model.addAttribute("query", query);
+        return "modules/exam/examList";
+    }
+
+    @RequestMapping("/exam-create")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String add(Exam exam, Model model) {
+        model.addAttribute("exam", exam);
+        model.addAttribute("statusList", ExamStatus.values());
+        return "modules/exam/examForm";
+    }
+
+    @RequestMapping("/exam-edit/{examId}")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String edit(@PathVariable Integer examId, Model model) {
+        Exam exam = examService.findById(examId);
+        model.addAttribute("exam", exam);
+        model.addAttribute("statusList", ExamStatus.values());
+        return "modules/exam/examEdit";
+    }
+
+    @RequestMapping(value = "/exam-save", method = RequestMethod.POST)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String save(HttpServletRequest request, Exam exam, RedirectAttributes redirectAttributes) {
+        User user = RequestUtils.getWebUser(request).getUser();
+        exam.setSchoolId(user.getSchoolId());
+        exam.setCreatorId(user.getId());
+        exam.setStatus(ExamStatus.START);
+        exam = examService.save(exam);
+        addMessage(redirectAttributes, "创建考试'" + exam.getName() + "'成功");
+        return "redirect:/admin/exam-list";
+    }
+
+    @RequestMapping(value = "/exam-edit", method = RequestMethod.POST)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String examEdit(HttpServletRequest request, Exam exam, int StatusValue,
+            RedirectAttributes redirectAttributes) {
+        User user = RequestUtils.getWebUser(request).getUser();
+        Exam oldExam = examService.findById(exam.getId());
+        if (oldExam != null && oldExam.getCreatorId().intValue() == user.getId().intValue()) {
+            oldExam.setName(exam.getName());
+            oldExam.setExamTime(exam.getExamTime());
+            oldExam.setDescription(exam.getDescription());
+            oldExam.setStatus(ExamStatus.findByValue(StatusValue));
+            oldExam.setForceSpecialTag(exam.isForceSpecialTag());
+            examService.save(oldExam);
+        }
+        return "redirect:/admin/exam-list";
+    }
+
+    @RequestMapping("/exam-select/{examId}")
+    public String select(Model model, HttpServletRequest request, @PathVariable Integer examId) {
+        SessionExamUtils.setExamId(request, examId);
+        WebUser wu = RequestUtils.getWebUser(request);
+        if (wu.isSchoolViewer()) {
+            return "redirect:/admin/exam/score";
+        }
+        return "redirect:/admin/exam/student";
+    }
+
+    @RequestMapping("/exam-view/{examId}")
+    public String view(Model model, HttpServletRequest request, @PathVariable Integer examId) {
+        SessionExamUtils.setExamId(request, examId);
+
+        Exam exam = examService.findById(examId);
+        long studentCount = examStudentService.countByExamId(exam.getId());
+        long subjectCount = examSubjectService.count(examId);
+        long campusCount = examStudentService.countCampusByExam(examId);
+        long markerCount = markerService.countByExam(exam.getId());
+        long scanCount = examStudentService.countByExamIdAndUpload(examId, true);
+        long markedCount = examStudentService.countByExamIdAndSubjectiveScoreListIsNotNull(examId);
+
+        List<ExamInfoVO> voList = new ArrayList<ExamInfoVO>();
+
+        ExamInfoVO student = new ExamInfoVO();
+        student.setName("考生");
+        student.setAttr("共导入 " + studentCount + " 个考生");
+        student.setUrl("/admin/exam/student");
+
+        ExamInfoVO subject = new ExamInfoVO();
+        subject.setName("科目");
+        subject.setAttr("共导入 " + subjectCount + " 个科目");
+        subject.setUrl("/admin/exam/paper");
+
+        ExamInfoVO campus = new ExamInfoVO();
+        campus.setName("学习中心");
+        campus.setAttr("共导入 " + campusCount + " 个学习中心");
+        campus.setUrl("/admin/exam/student");
+
+        ExamInfoVO marker = new ExamInfoVO();
+        marker.setName("评卷员");
+        marker.setAttr("已创建 " + markerCount + " 个评卷员");
+        marker.setUrl("/admin/exam/marker");
+
+        ExamInfoVO scan = new ExamInfoVO();
+        scan.setName("扫描进度");
+        scan.setAttr("已扫描 " + scanCount + " 个考生");
+        scan.setUrl("/admin/exam/scan");
+
+        ExamInfoVO mark = new ExamInfoVO();
+        mark.setName("评卷进度");
+        mark.setAttr("已评完 " + markedCount + " 个考生");
+        mark.setUrl("/admin/exam/mark");
+
+        voList.add(student);
+        voList.add(subject);
+        voList.add(campus);
+        voList.add(marker);
+        voList.add(scan);
+        voList.add(mark);
+        model.addAttribute("exam", exam);
+        model.addAttribute("list", voList);
+        return "modules/exam/examInfo";
+    }
+
+    @RequestMapping("/exam/info")
+    @ResponseBody
+    public Exam getExamInfo(HttpServletRequest request) {
+        Exam exam = examService.findById(getSessionExamId(request));
+        return exam;
+    }
+
+    @RequestMapping("/exam/finish")
+    @Transactional
+    @ResponseBody
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public void finished(HttpServletRequest request) {
+        Exam exam = examService.findById(getSessionExamId(request));
+        exam.setStatus(ExamStatus.FINISH);
+        examService.save(exam);
+    }
+
+}

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

@@ -44,6 +44,7 @@ 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;
+import net.sf.json.JsonConfig;
 
 @Controller("markGroupController")
 @RequestMapping("/admin/exam/group")
@@ -190,8 +191,8 @@ public class MarkGroupController extends BaseExamController {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group != null) {
-        	String pictureConfig = buildPictureConfig(group);
-        	group.setPicList(pictureConfig);
+            String pictureConfig = buildPictureConfig(group);
+            group.setPicList(pictureConfig);
             model.addAttribute("group", group);
             model.addAttribute("questions", questionService.findByExamAndSubjectAndObjectiveAndMainNumber(
                     group.getExamId(), group.getSubjectCode(), false, group.getNumber()));
@@ -211,8 +212,8 @@ public class MarkGroupController extends BaseExamController {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         if (group != null) {
-        	String pictureConfig = buildPictureConfig(group);
-        	group.setPicList(pictureConfig);
+            String pictureConfig = buildPictureConfig(group);
+            group.setPicList(pictureConfig);
             group.setScoreList(
                     questionService.findByExamAndSubjectAndObjectiveAndMainNumber(examId, subjectCode, false, number));
             model.addAttribute("group", group);
@@ -240,16 +241,16 @@ public class MarkGroupController extends BaseExamController {
         return "redirect:/admin/exam/group";
     }
 
+    @SuppressWarnings("unchecked")
     @RequestMapping("/save")
     @RoleRequire(Role.SCHOOL_ADMIN)
     public String save(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes,
             @RequestParam String subjectCode, @RequestParam Integer number,
             @RequestParam(required = false) String title, @RequestParam(required = false) String picList,
-            @RequestParam(required = false) String intervalScoreList,
-            @RequestParam(required = false) String scoreList,
+            @RequestParam(required = false) String intervalScoreList, @RequestParam(required = false) String scoreList,
             @RequestParam(required = false) Double doubleRate,
             @RequestParam(required = false) Double arbitrateThreshold,
-            @RequestParam(required = false) Integer scorePolicy ) {
+            @RequestParam(required = false) Integer scorePolicy) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
         List<ExamQuestion> questionList = questionService.findByExamAndSubjectAndObjectiveAndMainNumber(examId,
@@ -258,7 +259,7 @@ public class MarkGroupController extends BaseExamController {
             // quick update
             picList = StringEscapeUtils.unescapeHtml(picList);
             JSONArray array = JSONArray.fromObject(picList);
-            List<PictureConfigItem> list = JSONArray.toList(array, PictureConfigItem.class);
+            List<PictureConfigItem> list = JSONArray.toList(array, new PictureConfigItem(), new JsonConfig());
             // List<PictureConfigItem> list = PictureConfigItem.parse(picList);
             if (list != null && !list.isEmpty()) {
                 groupService.updatePicList(examId, subjectCode, number, list);
@@ -276,19 +277,17 @@ public class MarkGroupController extends BaseExamController {
                 groupService.updateTitle(examId, subjectCode, number, title);
             }
             if (doubleRate != null) {
-            	group.setDoubleRate(doubleRate);
+                group.setDoubleRate(doubleRate);
             }
             if (arbitrateThreshold != null) {
-            	group.setArbitrateThreshold(arbitrateThreshold);
+                group.setArbitrateThreshold(arbitrateThreshold);
             }
             groupService.save(group);
             // advance update
+            ScorePolicy policy = scorePolicy != null ? ScorePolicy.findByValue(scorePolicy) : null;
             List<Double> scores = buildDoubleList(scoreList);
             if (scores.size() > 0) {
-                markService.updateGroupScore(group, scores);
-            }
-            if(scorePolicy!=null && !ScorePolicy.findByValue(scorePolicy).equals(group.getScorePolicy())){
-            	markService.updateGroupScorePolicy(group, scorePolicy);
+                markService.updateGroup(group, scores, policy);
             }
             redirectAttributes.addAttribute("subjectCode", subjectCode);
             redirectAttributes.addAttribute("number", number);
@@ -301,6 +300,7 @@ public class MarkGroupController extends BaseExamController {
         }
     }
 
+    @SuppressWarnings("unchecked")
     @RequestMapping("/insert")
     @Transactional
     @RoleRequire(Role.SCHOOL_ADMIN)
@@ -324,7 +324,7 @@ public class MarkGroupController extends BaseExamController {
             // create group
             picList = StringEscapeUtils.unescapeHtml(picList);
             JSONArray array = JSONArray.fromObject(picList);
-            List<PictureConfigItem> list = JSONArray.toList(array, PictureConfigItem.class);
+            List<PictureConfigItem> list = JSONArray.toList(array, new PictureConfigItem(), new JsonConfig());
             group = new MarkGroup(examId, subjectCode, number, StringUtils.trimToNull(title), list, 0d, doubleRate,
                     arbitrateThreshold, scorePolicy);
             List<Double> scores = buildDoubleList(scoreList);

+ 22 - 21
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkTrackController.java

@@ -63,16 +63,17 @@ public class MarkTrackController extends BaseExamController {
     @ResponseBody
     @RequestMapping
     public HashMap<String, Object> list(Integer studentId) {
-    	ExamStudent examStudent = studentService.findById(studentId);
-    	Campus campus = campusService.findBySchoolAndName(examStudent.getSchoolId(), examStudent.getCampusName());
-    	List<Object> list = new ArrayList<Object>();
-    	HashMap<String, Object> map = new HashMap<String, Object>();
         List<MarkLibrary> librarys = libraryService.findByStudentId(studentId);
+        ExamStudent examStudent = studentService.findById(studentId);
+        Campus campus = campusService.findBySchoolAndName(examStudent.getSchoolId(), examStudent.getCampusName());
+        List<Object> list = new ArrayList<Object>();
+        HashMap<String, Object> map = new HashMap<String, Object>();
         for (MarkLibrary library : librarys) {
-        	if(null!=library.getTaskNumber() && library.getTaskNumber()==2){
-        		continue;
-        	}
-        	putAttr(examStudent, campus, list, library);
+            if (null != library.getTaskNumber() && library.getTaskNumber() == 2) {
+                continue;
+            }
+            HashMap<String, Object> groups = set(library, examStudent, campus);
+            list.add(groups);
         }
         map.put("list", list);
         map.put("imageServer", imageServer);
@@ -82,25 +83,24 @@ public class MarkTrackController extends BaseExamController {
     @ResponseBody
     @RequestMapping("/byLibrary")
     public HashMap<String, Object> byLibrary(Integer libraryId) {
-    	MarkLibrary library = libraryService.findById(libraryId);
-    	ExamStudent examStudent = studentService.findById(library.getStudentId());
-    	Campus campus = campusService.findBySchoolAndName(examStudent.getSchoolId(), examStudent.getCampusName());
-    	List<Object> list = new ArrayList<Object>();
-    	HashMap<String, Object> map = new HashMap<String, Object>();
-        putAttr(examStudent, campus, list, library);
+        MarkLibrary library = libraryService.findById(libraryId);
+        ExamStudent examStudent = studentService.findById(library.getStudentId());
+        Campus campus = campusService.findBySchoolAndName(examStudent.getSchoolId(), examStudent.getCampusName());
+        List<Object> list = new ArrayList<Object>();
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        HashMap<String, Object> groups = set(library, examStudent, campus);
+        list.add(groups);
         map.put("list", list);
         map.put("imageServer", imageServer);
         return map;
     }
 
-	private void putAttr(ExamStudent examStudent, Campus campus,
-			List<Object> list, MarkLibrary library) {
+	private HashMap<String, Object> set(MarkLibrary library,ExamStudent examStudent, Campus campus) {
 		MarkGroup group = groupService.findOne(examStudent.getExamId(), examStudent.getSubjectCode(),
-				library.getGroupNumber());
+		        library.getGroupNumber());
 		List<String> picUrls = PictureUrlBuilder.getSliceUrls(examStudent.getExamId(), campus.getId(),
-				examStudent.getSubjectCode(), examStudent.getExamNumber(), examStudent.getSliceCount());
-		List<MarkTrack> markTracks = markTrackService.findByStudentIdAndGroupNumberAndMarkerId(library.getStudentId(),
-				group.getNumber(),library.getMarkerId());
+		        examStudent.getSubjectCode(), examStudent.getExamNumber(), examStudent.getSliceCount());
+		List<MarkTrack> markTracks = markTrackService.findByLibraryId(library.getId());
 		HashMap<String, Object> groups = new HashMap<String, Object>();
 		List<MarkSpecialTag> markSpecialTagList = markSpecialTagService.findByLibraryId(library.getId());
 		groups.put("picUrls", picUrls);
@@ -109,6 +109,7 @@ public class MarkTrackController extends BaseExamController {
 		groups.put("groupTitle", group.getTitle());
 		groups.put("groupNumber", group.getNumber());
 		groups.put("markSpecialTagList", markSpecialTagList);
-		list.add(groups);
+		return groups;
 	}
+
 }

+ 0 - 4
stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java

@@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.InitBinder;
 
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
-import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.user.model.User;
@@ -25,9 +24,6 @@ import cn.com.qmth.stmms.common.utils.DateUtils;
 
 public class BaseController {
 
-    @Autowired
-    private ExamService examService;
-
     @Autowired
     private UserService userService;
 

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

@@ -7,9 +7,6 @@ import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,6 +39,8 @@ import cn.com.qmth.stmms.common.enums.ExamSubjectStatus;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.session.model.StmmsSession;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
 
 @Controller
 @RequestMapping("/mark")
@@ -66,7 +65,7 @@ public class MarkController extends BaseController {
 
     @Autowired
     private MarkService markService;
-    
+
     @Autowired
     private ExamService examService;
 
@@ -165,7 +164,7 @@ public class MarkController extends BaseController {
         modelAndView.addObject("marker", marker);
         modelAndView.addObject("subject", subjectService.find(marker.getExamId(), marker.getSubjectCode()));
         Exam exam = examService.findById(marker.getExamId());
-        modelAndView.addObject("forceSpecialTag",exam.isForceSpecialTag() );
+        modelAndView.addObject("forceSpecialTag", exam.isForceSpecialTag());
         markService.releaseByMarker(marker);
     }
 
@@ -284,7 +283,7 @@ public class MarkController extends BaseController {
                 library.setMarkerScoreList(task.getScoreList());
                 library.setStatus(LibraryStatus.MARKED);
                 library.setTags(StringUtils.trimToNull(task.getTags()));
-                markService.submitLibrary(library, task.getTrackMap(library), task.getSpecialTagList(library));
+                markService.submitLibrary(library, task.getTrackList(library), task.getSpecialTagList(library));
                 markService.releaseLibrary(library, marker);
                 result.accumulate("success", true);
                 result.accumulate("status", status(request));

+ 2 - 1
stmms-web/src/main/webapp/WEB-INF/application.properties

@@ -37,6 +37,7 @@ upyun.slice.username=qmth-picture
 upyun.slice.password=qmth12345678
 
 mark.cleanTimeoutMinute=20
-mark.cleanSchedule=0 0/10 6-23 * * ?
+mark.cleanTaskSchedule=0 0/10 6-23 * * ?
+mark.cleanLockSchedule=0 0 3 * * ?
 
 marker.showBtnImportAndBtnUpdateImport=false

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

@@ -31,6 +31,7 @@
 <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/warning-info.js"></script>
 <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/simple-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>
@@ -52,6 +53,7 @@
 				forceSpecialTag : '${forceSpecialTag}',
 				modules : {
 					'single-image-view' : {},
+			        //'simple-image-view' : {},
 					'image-builder': {},
 					'mark-status': {
 						simple : false,
@@ -64,7 +66,7 @@
 						showScoreBoard : false,
 						autoSubmit : false,
 						needConfirm : false,
-              //        	showTotalScore:true
+						//showTotalScore:true
 					},
 					'sheet-view' : {
 						server : '${sheetServer}'

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

@@ -10,8 +10,7 @@ function MarkControl(option) {
         staticServer: option.staticServer,
         isFinish: false,
         prefetching: false,
-        prefetchTask: [],
-        forceSpecialTag:option.forceSpecialTag
+        prefetchTask: []
     };
     //初始化容器结构
     this.initContainer();
@@ -510,17 +509,15 @@ MarkControl.prototype.submitTask = function(submitUrl) {
         task.arbitrationList = undefined;
 
         this.trigger('task.submit.before');
-        this.trigger('mark.specialTag.before');
         
         //开启强制标记
-        if(markControl.context.forceSpecialTag){
+        if(this.option.forceSpecialTag===true){
         	console.log(task);
         	if(task.tagList==undefined ||task.tagList==null ||task.tagList.length <= 0){
-        		return ;
+        		return;
         	}
         }
         
-        
         if (this.taskControl != undefined) {
             //已定义任务引擎
             this.taskControl.submit(task, function(status) {
@@ -547,7 +544,6 @@ MarkControl.prototype.submitTask = function(submitUrl) {
                     if (result.success == true) {
                         //markControl.context.task = undefined;
                         markControl.trigger('task.submit.success');
-                        markControl.trigger('mark.specialTag.success');
                         //markControl.getTask();
                     } else {
                         markControl.trigger('task.submit.error', result.message);
@@ -559,7 +555,6 @@ MarkControl.prototype.submitTask = function(submitUrl) {
             });
         } else {
             markControl.trigger('task.submit.success');
-            markControl.trigger('mark.specialTag.success');
             //markControl.getTask();
         }
     }

+ 9 - 7
stmms-web/src/main/webapp/static/mark-new/js/modules/mark-board.js

@@ -107,15 +107,17 @@ function MarkBoard(option) {
 MarkBoard.prototype.init = function() {
     this.stepBoard = getDom(this.step_board_dom, this.markControl).appendTo(this.markControl.container.centerContent);
     this.stepBoard.height(this.markControl.container.centerContent.height());
+    
     this.stepBoard.selectiveHeader = this.stepBoard.find('#step-board-selective-header');
     this.stepBoard.stepHolder = this.stepBoard.find('.step-list');
-    this.stepBoard.stepHolder.height(this.markControl.container.centerContent.height() - 90);
-    this.stepBoard.stepHolder.perfectScrollbar({
-        wheelSpeed: 20,
-        useKeyboard: false,
-        minScrollbarLength: 30,
-        suppressScrollX: true
-    });
+    this.stepBoard.stepHolder.height(this.markControl.container.centerContent.height() - 120);
+    this.stepBoard.stepHolder.css('overflow', 'scroll');
+    //this.stepBoard.stepHolder.perfectScrollbar({
+    //    wheelSpeed: 20,
+    //    useKeyboard: false,
+    //    minScrollbarLength: 30,
+    //    suppressScrollX: true
+    //});
 
     this.scoreBoard = getDom(this.scoreBoard_dom, this.markControl).appendTo(this.markControl.container);
     this.scoreBoard.header = getDom(this.scoreBoard_header_dom, this.markControl).appendTo(this.scoreBoard).find('#score-board-header');

+ 3 - 2
stmms-web/src/main/webapp/static/mark-new/js/modules/mark-history.js

@@ -84,10 +84,11 @@ MarkHistory.prototype.init = function() {
     this.container.paginator = getDom(this.paginator_dom, this.markControl).appendTo(this.container);
     this.container.list = getDom(this.history_list_dom, this.markControl).appendTo(this.container).find('#history-list');
 
+	var self = this;
     this.container.header.find('#close-history-button').click(this, function(event) {
         event.data.toggle(false);
-        this.markControl.context.task = undefined;
-        this.markControl.getTask();
+        self.markControl.context.task = undefined;
+        self.markControl.getTask();
     })
     this.container.paginator.find('#last-page-button').click(this, function(event) {
         var markHistory = event.data;

+ 15 - 15
stmms-web/src/main/webapp/static/mark-new/js/modules/simple-image-view.js

@@ -1,11 +1,11 @@
 //简单多张图片排列显示模块
-var single_image_view = function(option, success) {
-    var object = new SingleImageView(option);
+var simple_image_view = function(option, success) {
+    var object = new SimpleImageView(option);
     success();
     return object;
 }
 
-function SingleImageView(option) {
+function SimpleImageView(option) {
     this.markControl = option.markControl;
     this.defaultZoom = 100;
     this.loading = false;
@@ -60,7 +60,7 @@ function SingleImageView(option) {
     });
 }
 
-SingleImageView.prototype.init = function() {
+SimpleImageView.prototype.init = function() {
     this.configCache = {};
 
     //this.container = getDom(this.container_dom).appendTo(this.markControl.container.imageContent);
@@ -113,7 +113,7 @@ SingleImageView.prototype.init = function() {
     });
 }
 
-SingleImageView.prototype.render = function() {
+SimpleImageView.prototype.render = function() {
     if (this.task != undefined) {
         var config = this.configCache[this.task.blockId];
         if (config == undefined) {
@@ -135,7 +135,7 @@ SingleImageView.prototype.render = function() {
     }
 }
 
-SingleImageView.prototype.loadImage = function() {
+SimpleImageView.prototype.loadImage = function() {
     if (this.task.imageData != undefined && this.loading == false) {
         this.loading = true;
         if (this.iviewer == undefined) {
@@ -168,7 +168,7 @@ SingleImageView.prototype.loadImage = function() {
     }
 }
 
-SingleImageView.prototype.onImageLoad = function() {
+SimpleImageView.prototype.onImageLoad = function() {
     this.loading = false;
     this.container.scrollTop(0);
     if (this.currentConfig != undefined && this.currentConfig.zoom != undefined && this.currentConfig.zoom != this.defaultZoom) {
@@ -192,7 +192,7 @@ SingleImageView.prototype.onImageLoad = function() {
     this.markControl.trigger('task.load.finish');
 }
 
-SingleImageView.prototype.onZoomSet = function(zoom) {
+SimpleImageView.prototype.onZoomSet = function(zoom) {
     if (this.loading == false && this.currentConfig != undefined) {
         this.currentConfig.zoom = zoom;
         this.updateScrollTop(this.currentConfig.scrollTop);
@@ -200,20 +200,20 @@ SingleImageView.prototype.onZoomSet = function(zoom) {
     }
 }
 
-SingleImageView.prototype.onStopDrag = function(point) {
+SimpleImageView.prototype.onStopDrag = function(point) {
     if (this.loading == false) {
         this.currentConfig.dx = point.x;
         this.currentConfig.dy = point.y;
     }
 }
 
-SingleImageView.prototype.onRotate = function(angle) {
+SimpleImageView.prototype.onRotate = function(angle) {
     if (this.loading == false) {
         this.currentConfig.rotate = angle;
     }
 }
 
-SingleImageView.prototype.updateScrollTop = function(scrollTopPercent) {
+SimpleImageView.prototype.updateScrollTop = function(scrollTopPercent) {
     var height = this.imageHolder.height();
     var minHeight = this.container.height();
     if (scrollTopPercent != undefined && scrollTopPercent >= 0 && scrollTopPercent <= 1 && height > minHeight) {
@@ -225,7 +225,7 @@ SingleImageView.prototype.updateScrollTop = function(scrollTopPercent) {
     }
 }
 
-SingleImageView.prototype.updateScrollLeft = function(scrollLeftPercent) {
+SimpleImageView.prototype.updateScrollLeft = function(scrollLeftPercent) {
     var width = this.imageHolder.width();
     var minWidth = this.container.width();
     if (scrollLeftPercent != undefined && scrollLeftPercent >= 0 && scrollLeftPercent <= 1 && width > minWidth) {
@@ -237,9 +237,9 @@ SingleImageView.prototype.updateScrollLeft = function(scrollLeftPercent) {
     }
 }
 
-SingleImageView.prototype.container_dom = '<div class="image-content"></div>';
+SimpleImageView.prototype.container_dom = '<div class="image-content"></div>';
 
-SingleImageView.prototype.image_control_dom = '<em>\
+SimpleImageView.prototype.image_control_dom = '<em>\
 <a href="#" class="btn zoom-out-button">放大</a>\
 <a href="#" class="btn zoom-in-button">缩小</a>\
 <a href="#" class="btn zoom-fit-button">适应</a>\
@@ -247,4 +247,4 @@ SingleImageView.prototype.image_control_dom = '<em>\
 <a href="#" class="btn rotate-button">旋转</a>\
 </em>';
 
-SingleImageView.prototype.image_holder_dom = '<div style="position: relative;"></div>';
+SimpleImageView.prototype.image_holder_dom = '<div style="position: relative;"></div>';

+ 42 - 38
stmms-web/src/main/webapp/static/mark-new/js/modules/single-image-view.js

@@ -9,7 +9,20 @@ function SingleImageView(option) {
     this.markControl = option.markControl;
     this.init();
     this.markControl.on('center.width.change', this, function(event, context, eventObject) {
-        //this.container.perfectScrollbar('update');
+        this.reloadImage();
+        this.markControl.trigger('image.reload.event');
+    });
+    this.markControl.on('step.board.show', this, function(event, context, eventObject) {
+        this.container.removeClass('span12');
+        this.container.addClass('span10');
+        this.reloadImage();
+        this.markControl.trigger('image.reload.event');
+    });
+    this.markControl.on('step.board.hide', this, function(event, context, eventObject) {
+        this.container.removeClass('span10');
+        this.container.addClass('span12');
+        this.reloadImage();
+        this.markControl.trigger('image.reload.event');
     });
     this.markControl.on('task.get.before', this, function(event, context, eventObject) {
         this.task = undefined;
@@ -26,47 +39,34 @@ function SingleImageView(option) {
         this.image = undefined;
         this.render();
     });
-    this.markControl.on('mark.track.show', this, function(event, context, track){
-        if(this.task != undefined && track != undefined){
-        	this.drawScore(track);
+    this.markControl.on('mark.tag.show', this, function(event, context, tag){
+        if(this.task != undefined && tag != undefined){
+        	this.drawTag(tag);
         }
     });
-    this.markControl.on('mark.specialTag.show', this, function(event, context, specialTag){
-        if(this.task != undefined && specialTag != undefined){
-        	this.drawTag(specialTag);
-        }
-    });
-    this.markControl.on('mark.track.clear', this, function(event, context, track){
+    this.markControl.on('mark.tag.clear', this, function(event, context, track){
         this.reloadImage();
-        //显示特殊标记的历史痕迹
-        this.markControl.trigger('show.specialTag.event');
+        this.markControl.trigger('image.reload.event');
     });
-    this.markControl.on('mark.specialTag.clear', this, function(event, context, track){
-        this.reloadImage();
-        this.markControl.trigger('show.track.event');
+    this.markControl.on('image.position.change', this, function(event, context, topPercent) {
+        if (this.task != undefined) {
+            this.updateScrollTop(topPercent);
+        }
     });
 }
 
 SingleImageView.prototype.init = function() {
     var self = this;
-    this.container = getDom(this.container_dom, this.markControl).appendTo(this.markControl.container.centerContent);
+    this.container = this.markControl.container.imageContent;
     this.container.height(this.markControl.container.centerContent.height());
-    //this.container.perfectScrollbar({
-    //    wheelSpeed: 20
-    //});
-    this.markControl.addNavGroup('答卷', this.container);
+    this.container.css('overflow','scroll');
     
-    this.canvas = this.container.find('canvas')[0];
+    this.canvas = $('<canvas></canvas>').appendTo(this.container)[0];
     this.ctx = this.canvas.getContext("2d");
     
     $(this.canvas).click(function(event){
         if(self.task != undefined){
-        	self.markControl.trigger('mark.track.set', {
-        		positionX: ((event.pageX - $(self.canvas).offset().left)/$(self.canvas).width()).toFixed(3),
-                positionY: ((event.pageY - $(self.canvas).offset().top)/$(self.canvas).height()).toFixed(3)
-        	});
-
-        	self.markControl.trigger('mark.specialTag.set', {
+        	self.markControl.trigger('image.click.event', {
         		positionX: ((event.pageX - $(self.canvas).offset().left)/$(self.canvas).width()).toFixed(3),
                 positionY: ((event.pageY - $(self.canvas).offset().top)/$(self.canvas).height()).toFixed(3)
         	});
@@ -95,24 +95,28 @@ SingleImageView.prototype.render = function() {
 
 SingleImageView.prototype.reloadImage = function() {
     if(this.image != undefined){
+    	this.canvas.width = Math.min(this.container.width(), this.image.width);
+        this.canvas.height = this.canvas.width * this.image.height / this.image.width;
         this.ctx.drawImage(this.image, 0, 0, this.image.width, this.image.height, 0, 0, this.canvas.width, this.canvas.height);
     }
 }
 
-SingleImageView.prototype.drawScore = function(track){
-    if(track != undefined && track.positionX > 0 && track.positionY > 0){
+SingleImageView.prototype.drawTag = function(tag){
+    if(tag!=undefined && tag.positionX>0 && tag.positionY>0){
         this.ctx.font ="60px Arial";
         this.ctx.fillStyle = 'red';
-        this.ctx.fillText(track.score, track.positionX*this.canvas.width, track.positionY*this.canvas.height);
+        this.ctx.fillText(tag.content, tag.positionX*this.canvas.width, tag.positionY*this.canvas.height);
     }
 }
 
-SingleImageView.prototype.drawTag = function(specialTag){
-    if(specialTag != undefined && specialTag.positionX > 0 && specialTag.positionY > 0){
-        this.ctx.font ="60px Arial";
-        this.ctx.fillStyle = 'red';
-        this.ctx.fillText(specialTag.tagName, specialTag.positionX*this.canvas.width, specialTag.positionY*this.canvas.height);
+SingleImageView.prototype.updateScrollTop = function(scrollTopPercent) {
+    var height = this.canvas.height;
+    var minHeight = this.container.height();
+    if (scrollTopPercent != undefined && scrollTopPercent >= 0 && scrollTopPercent <= 1 && height > minHeight) {
+        var left = height * (1 - scrollTopPercent);
+        var scrollTop = left > minHeight ? (height - left) : (height - minHeight);
+        this.container.scrollTop(scrollTop);
+    } else {
+        this.container.scrollTop(0);
     }
-}
-
-SingleImageView.prototype.container_dom = '<div style="overflow: scroll; width: 100%"><canvas></canvas></div>';
+}

+ 64 - 79
stmms-web/src/main/webapp/static/mark-new/js/modules/specialTag.js

@@ -9,59 +9,48 @@ function SpecialTag(option) {
     this.markControl = option.markControl;
     this.maxWidth = option.width != undefined ? option.width : 200;
     this.show = false;
-    this.tagList = [];
-    this.specialTag = undefined;
+    this.tagName = undefined;
     this.init();
-    this.markControl.on('mark.specialTag.set', this, function(event, context, specialTag) {
-        if(this.specialTag != undefined && this.tagList != undefined){
-            specialTag.tagName = this.specialTag;
-            this.tagList.push(specialTag);
-            this.markControl.trigger('mark.specialTag.show', specialTag);
-        }
-    })
-    //提交保存之前
-    this.markControl.on('mark.specialTag.before', this, function(event, context, eventObject) {
+    this.markControl.on('task.get.before', this, function(event, context, eventObject) {
+        this.task = undefined;
+        this.reset();
+    });
+    this.markControl.on('task.get.success', this, function(event, context, eventObject) {
         this.task = context.task;
-        if (this.tagList.length > 0) {
-            this.task.tagList = this.tagList;
-        } else {
-            this.task.tagList = undefined;
+        if(this.task.tagList==undefined){
+        	this.task.tagList=[];
         }
     });
-    //提交保存成功后
-    this.markControl.on('mark.specialTag.success', this, function(event, context, eventObject) {
-        this.markControl.trigger('clear.specialTag.click');
-        this.specialTag = undefined;
-        this.tagList = [];
+    this.markControl.on('task.get.none', this, function(event, context, eventObject) {
+        this.task = undefined;
     });
-
-    //禁止 特殊标识使用
-    this.markControl.on('click.track.before', this, function(event, context, eventObject) {
-        this.specialTag = undefined;
-        this.markControl.trigger('clear.specialTag.click');
-    });
-
-    //显示痕迹
-    this.markControl.on('show.specialTag.event', this, function(event, context, eventObject) {
-        this.task = context.task;
-        if(this.task != undefined ){
-            if(this.tagList.length == 0 && this.task.tagList != null && this.task.tagList.length>0){
-                this.tagList = this.task.tagList;
-            }
-            for(var j = 0; j <this.tagList.length; j++) {
-                this.markControl.trigger('mark.specialTag.show', this.tagList[j]);
+    this.markControl.on('image.click.event', this, function(event, context, eventObject) {
+        if(this.task!=undefined && this.tagName!=undefined){
+        	var specialTag = {
+        		tagName : this.tagName,
+        		positionX : eventObject.positionX,
+        		positionY : eventObject.positionY
+        	}
+            this.task.tagList.push(specialTag);
+            this.markControl.trigger('mark.tag.show', {
+                	content: specialTag.tagName,
+                	positionX: specialTag.positionX,
+                	positionY: specialTag.positionY
+            });
+        }
+    })
+    //图片重新加载后,恢复显示所有标记
+    this.markControl.on('image.reload.event', this, function(event, context, eventObject) {
+        if(this.task != undefined){
+            for(var i = 0; i <this.task.tagList.length; i++) {
+                this.markControl.trigger('mark.tag.show', {
+                	content: this.task.tagList[i].tagName,
+                	positionX: this.task.tagList[i].positionX,
+                	positionY: this.task.tagList[i].positionY
+                });
             }
         }
     });
-
-    //清除按钮选中
-    this.markControl.on('clear.specialTag.click', this, function(event, context, eventObject) {
-        this.specialTag = undefined;
-        var aList = this.onclickList.find('a');
-        for(var i =0 ;i < aList.length;i++){
-                $(aList[i]).removeClass('selected');
-        }
-    })
 }
 
 SpecialTag.prototype.init = function() {
@@ -70,7 +59,7 @@ SpecialTag.prototype.init = function() {
     this.container.width(this.maxWidth);
     this.container.offset({
         top: 100,
-        left:800
+        left: 800
     });
     this.container.header = getDom(this.container_header_dom, this.markControl).appendTo(this.container);
     this.container.header.width('100%');
@@ -80,44 +69,35 @@ SpecialTag.prototype.init = function() {
 
     this.onclickList = this.container.content.find('#problem-list');
 
-    this.clearList = this.container.content.find('#clear');
-
     this.container.draggable({
         containment: "window"
     });
 
     this.onclickList.find('a').click(function () {
-        self.specialTag = undefined;
-        self.markControl.trigger('click.specialTag.before');
-        if($(this).hasClass('selected')){
-            $(this).removeClass('selected');
-        }else {
-            self.specialTag = $(this).attr('value');
-            $(this).addClass('selected');
-            var aList = self.onclickList.find('a');
-            for(var i =0 ;i < aList.length;i++){
-                if($(aList[i]).attr('value') != $(this).attr('value')){
-                    $(aList[i]).removeClass('selected');
-                }
-            }
+        self.tagName = undefined;
+        if(self.task!=undefined){
+        	if($(this).hasClass('selected')){
+        		self.tagName = undefined;
+        		$(this).removeClass('selected');
+        	}else{
+        		self.tagName = $(this).attr('value');
+        		self.onclickList.find('a').removeClass('selected');
+        		$(this).addClass('selected');
+        	}
         }
     });
-
-    this.clearList.find('a').click(function () {
-        self.markControl.trigger('clear.specialTag.click');
-        self.markControl.trigger('click.specialTag.before');
-        if(self.tagList != undefined && self.tagList.length>0){
-            self.markControl.trigger('mark.specialTag.clear');
-            if($(this).attr('class') == 'goBack'){
-                    self.tagList.pop();
-                    self.markControl.trigger('show.specialTag.event');
-                }else if($(this).attr('class') == 'clearAll'){
-                    self.tagList = [];
-                    self.task.tagList = [];
-            }
-        }
+    this.container.find('#undo-button').click(function(){
+    	if(self.task!=undefined){
+    		self.task.tagList.pop();
+    		self.markControl.trigger('mark.tag.clear');
+    	}
+    });
+    this.container.find('#clear-button').click(function(){
+    	if(self.task!=undefined){
+    		self.task.tagList=[];
+    		self.markControl.trigger('mark.tag.clear');
+    	}
     });
-
     this.container.header.find('#close-button').click(function() {
         self.toggle(false);
     });
@@ -134,7 +114,6 @@ SpecialTag.prototype.init = function() {
 }
 
 SpecialTag.prototype.toggle = function(show) {
-    this.specialTag = undefined;
     if (show == true) {
         this.show = true;
         this.container.show();
@@ -144,6 +123,11 @@ SpecialTag.prototype.toggle = function(show) {
     }
 }
 
+SpecialTag.prototype.reset = function() {
+    this.tagName = $(this).attr('value');
+    this.onclickList.find('a').removeClass('selected');
+}
+
 SpecialTag.prototype.container_dom = '<div class="score-board score-board-popover" style="display:none"></div>';
 
 SpecialTag.prototype.container_header_dom = '<div class="header" style="border-radius: 15px 15px 0px 0px;">\
@@ -155,8 +139,9 @@ SpecialTag.prototype.container_content_dom = '<div class="content popover-conten
 <p id="problem-list" class="popover-list buttonCss">\
     <a href="#" value="√">√</a>\
     <a href="#" value="X">×</a>\
-    <a href="#" value="乄">乄</a>\
-    </p><p id="clear"><a href="#" class="goBack" style="margin-left: 20px;">回&nbsp;&nbsp;&nbsp;退</a><a href="#" class="clearAll">全部清除</a></p></div>';
+    <a href="#" value="乄">乄</a></p>\
+<p><a href="#" id="undo-button" style="margin-left: 20px;">回&nbsp;&nbsp;&nbsp;退</a>\
+<a href="#" id="clear-button">全部清除</a></p></div>';
 
 SpecialTag.prototype.control_dom = '<h3 class="popover-title">特殊标记</h3>\
 <div class="popover-content"><p id="problem-list" class="popover-list">\