Răsfoiți Sursa

锁实现机制重构,区分读写锁;转移锁的控制点,从事务中挪出来;去掉部分无用锁,统一一套锁的实现逻辑

luoshi 6 ani în urmă
părinte
comite
5eaf7ad71f
25 a modificat fișierele cu 589 adăugiri și 583 ștergeri
  1. 18 15
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamStudentDao.java
  2. 9 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java
  3. 2 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamStudentService.java
  4. 2 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java
  5. 5 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java
  6. 5 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java
  7. 5 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockProvider.java
  8. 54 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockService.java
  9. 20 30
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/impl/MemoryLockProvider.java
  10. 83 54
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java
  11. 0 179
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkLockService.java
  12. 135 196
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  13. 3 2
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/LockType.java
  14. 12 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java
  15. 20 4
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java
  16. 65 21
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkGroupController.java
  17. 9 8
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkQualityController.java
  18. 44 5
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkerController.java
  19. 9 9
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java
  20. 23 5
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TrialController.java
  21. 3 8
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java
  22. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/PictureController.java
  23. 1 1
      stmms-web/src/main/java/cn/com/qmth/stmms/api/utils/SheetDownloadThread.java
  24. 58 33
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  25. 1 1
      stmms-web/src/main/webapp/WEB-INF/application.properties

+ 18 - 15
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamStudentDao.java

@@ -1,14 +1,15 @@
 package cn.com.qmth.stmms.biz.exam.dao;
 
-import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import java.util.Date;
+import java.util.List;
+
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.PagingAndSortingRepository;
 
-import java.util.Date;
-import java.util.List;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 
 public interface ExamStudentDao
         extends PagingAndSortingRepository<ExamStudent, Integer>, JpaSpecificationExecutor<ExamStudent> {
@@ -16,7 +17,7 @@ public interface ExamStudentDao
     public List<ExamStudent> findByExamId(int examId, Pageable pageable);
 
     public List<ExamStudent> findByExamIdAndUploadAndAbsent(int examId, boolean upload, boolean absent,
-                                                            Pageable pageable);
+            Pageable pageable);
 
     public List<ExamStudent> findByExamIdAndCampusName(int examId, String campusName, Pageable pageable);
 
@@ -62,7 +63,7 @@ public interface ExamStudentDao
     @Modifying
     @Query("update ExamStudent s set s.subjectName=?3, s.subjectLevel=?4, s.subjectCategory=?5 where s.examId=?1 and s.subjectCode=?2")
     public void updateSubjectInfo(int examId, String subjectCode, String subjectName, String subjectLevel,
-                                  String subjectCategory);
+            String subjectCategory);
 
     @Modifying
     @Query("update ExamStudent s set s.exception=?2 where s.id=?1")
@@ -80,7 +81,7 @@ public interface ExamStudentDao
     @Query("update ExamStudent s set s.sheetCount=?2, s.sliceCount=?3, s.answers=?4, s.batchCode=?5, s.paperType=?6,"
             + " s.absent=?7, s.upload=true, s.uploadTime=?8, s.objectiveScore=?9, s.objectiveScoreList=?10 where s.id=?1")
     public int updateScanInfo(Integer id, Integer sheetCount, Integer sliceCount, String answers, String batchCode,
-                              String paperType, boolean absent, Date uploadTime, Double objectiveScore, String objectiveScoreList);
+            String paperType, boolean absent, Date uploadTime, Double objectiveScore, String objectiveScoreList);
 
     @Modifying
     @Query("update ExamStudent s set s.manualAbsent=0 where s.examId=?1")
@@ -97,30 +98,30 @@ public interface ExamStudentDao
     public ExamStudent findByExamIdAndSubjectCodeAndStudentCode(Integer id, String subjectCode, String studentCode);
 
     public ExamStudent findBySchoolIdAndSubjectCodeAndStudentCodeAndRemark(Integer schoolId, String subjectCode,
-                                                                           String studentCode, String examSeqCode);
+            String studentCode, String examSeqCode);
 
     public List<ExamStudent> findByExamIdAndUploadTimeAfter(int examId, Date date);
 
     public List<ExamStudent> findByExamIdAndSubjectCodeAndUploadTimeAfter(int examId, String code, Date date,
-                                                                          Pageable page);
+            Pageable page);
 
     @Query("select count(s) from ExamStudent s where s.examId=?1 and s.subjectiveScoreList != null ")
     public long countByExamIdAndSubjectiveScoreListNotNull(Integer examId);
 
     public List<ExamStudent> findByExamIdAndSubjectCodeAndUploadTimeNotNull(int examId, String code);
 
-    public List<ExamStudent> findByExamIdAndSubjectCodeAndUploadTimeNotNullAndAbsent(int examId, String code, Boolean absent);
+    public List<ExamStudent> findByExamIdAndSubjectCodeAndUploadTimeNotNullAndAbsent(int examId, String code,
+            Boolean absent);
 
-
-    @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.absent=false and s.breach=false and s.uploadTime!=null "
+    @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.upload=true and s.absent=false and s.breach=false and s.uploadTime!=null "
             + "and not exists (select l.id from MarkLibrary l where l.studentId=s.id and l.groupNumber=?3)")
     public List<ExamStudent> findUnLibraryStudent(Integer examId, String subjectCode, Integer groupNumber,
-                                                  Pageable page);
+            Pageable page);
 
-    @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.absent=false and s.breach=false and s.uploadTime!=null and s.uploadTime>=?4 "
+    @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.upload=true and s.absent=false and s.breach=false and s.uploadTime!=null and s.uploadTime>=?4 "
             + "and not exists (select l.id from MarkLibrary l where l.studentId=s.id and l.groupNumber=?3)")
     public List<ExamStudent> findUnLibraryStudent(Integer examId, String subjectCode, Integer groupNumber,
-                                                  Date minUploadTime, Pageable page);
+            Date minUploadTime, Pageable page);
 
     @Query("select s from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.upload=true and s.absent=false and s.breach=false "
             + "and not exists (select l.id from TrialLibrary l where l.studentId=s.id and l.groupNumber=?3)")
@@ -162,7 +163,7 @@ public interface ExamStudentDao
             + "sum(case when ((case when s.objectiveScore is null then 0 else s.objectiveScore end) + (case when s.subjectiveScore is null then 0 else s.subjectiveScore end)) between 60 and 100 then 1 else 0 end) "
             + " from ExamStudent s where s.examId=?1 and s.subjectCode=?2 and s.upload=?3  and s.absent=?4 and s.breach = ?5")
     public List<Object[]> statisticsByAbsentAndBreach(Integer examId, String code, Boolean upload, boolean absent,
-                                                      boolean breach);
+            boolean breach);
 
     @Query(value = "select s.id from eb_exam_student s where 1=1 and s.id in (select m.student_id from m_library m where 1=1 and m.marker_id in (select m.id from eb_marker m where 1=1 and m.login_name = ?1 ))", nativeQuery = true)
     public List<String[]> findIdsByMarkLogin(String markLogin);
@@ -170,4 +171,6 @@ public interface ExamStudentDao
     @Query(value = "select s.id from eb_exam_student s where 1=1 and s.id in (select m.student_id from m_library m where 1=1 and m.marker_id in (select m.id from eb_marker m where 1=1 and m.name = ?1))", nativeQuery = true)
     public List<String[]> findIdsByMarkName(String markName);
 
+    public long countByIdAndUploadAndAbsentAndBreach(Integer id, boolean upload, boolean absent, boolean breach);
+
 }

+ 9 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java

@@ -20,11 +20,11 @@ public interface MarkGroupDao
     @Query("select q from MarkGroup q where q.pk.examId=?1 and q.pk.subjectCode=?2 and q.pk.number=?3")
     MarkGroup findOne(Integer examId, String subjectCode, Integer groupNumber);
 
-    @Modifying
+    @Modifying(clearAutomatically = true)
     @Query("delete from MarkGroup q where q.pk.examId=?1")
     void deleteByExamId(Integer examId);
 
-    @Modifying
+    @Modifying(clearAutomatically = true)
     @Query("delete from MarkGroup q where q.pk.examId=?1 and q.pk.subjectCode=?2")
     void deleteByExamIdAndSubjectCode(Integer examId, String subjectCode);
 
@@ -34,6 +34,11 @@ public interface MarkGroupDao
     @Query("select count(q) from MarkGroup q where q.pk.examId=?1 and q.pk.subjectCode=?2")
     long countByExamIdAndSubjectCode(Integer examId, String subjectCode);
 
+    @Query("select count(q) from MarkGroup q where q.pk.examId=?1 and q.pk.subjectCode=?2 "
+            + "and q.pk.number=?3 and q.status in (?4)")
+    long countByExamIdAndSubjectCodeAndNumberAndStatus(Integer examId, String subjectCode, Integer groupNumber,
+            MarkStatus... status);
+
     @Query("select q from MarkGroup q where q.pk.examId=?1 order by q.pk.number")
     List<MarkGroup> findByExamId(Integer examId);
 
@@ -71,10 +76,10 @@ public interface MarkGroupDao
     @Modifying(clearAutomatically = true)
     @Query("update MarkGroup g set g.libraryCount=?4, g.leftCount=(?4-g.markedCount) where g.pk.examId=?1 and g.pk.subjectCode=?2 and g.pk.number=?3")
     void updateLibraryCount(Integer examId, String subjectCode, Integer number, Integer totalCount);
-    
+
     @Modifying(clearAutomatically = true)
     @Query("update MarkGroup g set g.markedCount=?4, g.leftCount=(g.libraryCount-?4) where g.pk.examId=?1 and g.pk.subjectCode=?2 and g.pk.number=?3")
-    void updateMarkedCount(Integer examId, String subjectCode, Integer number,Integer markedCount);
+    void updateMarkedCount(Integer examId, String subjectCode, Integer number, Integer markedCount);
 
     @Modifying(clearAutomatically = true)
     @Query("update MarkGroup g set g.leftCount = g.libraryCount, g.markedCount = 0 "

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

@@ -128,4 +128,6 @@ public interface ExamStudentService {
 
     long countByExamIdAndManualAbsent(int examId, boolean manualAbsent);
 
+    boolean validateStatus(Integer id);
+
 }

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

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

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

@@ -941,4 +941,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
         query.setManualAbsent(manualAbsent);
         return countByQuery(query);
     }
+
+    @Override
+    public boolean validateStatus(Integer id) {
+        return studentDao.countByIdAndUploadAndAbsentAndBreach(id, true, false, false) > 0;
+    }
 }

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

@@ -170,4 +170,9 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
         return score != null ? score.doubleValue() : 0d;
     }
 
+    @Override
+    public boolean validateStatus(Integer examId, String subjectCode, Integer number, MarkStatus status) {
+        return groupDao.countByExamIdAndSubjectCodeAndNumberAndStatus(examId, subjectCode, number, status) > 0;
+    }
+
 }

+ 5 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockProvider.java

@@ -12,5 +12,9 @@ public interface LockProvider {
 
     boolean isLocked(LockType type, String key);
 
-    void waitUnlock(LockType type, String key);
+    void watch(LockType type, String key);
+
+    void unwatch(LockType type, String key);
+
+    void clear();
 }

+ 54 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockService.java

@@ -17,30 +17,81 @@ public class LockService implements InitializingBean, ApplicationContextAware {
 
     protected static final Logger log = LoggerFactory.getLogger(LockService.class);
 
-    private static final String KEY_JOINER = "_";
+    private static final String KEY_JOINER = "\t";
 
     private ApplicationContext context;
 
     private LockProvider provider;
 
+    /**
+     * 尝试获取排他锁,立即返回获取结果
+     * 
+     * @param type
+     * @param keys
+     * @return
+     */
     public boolean trylock(LockType type, Object... keys) {
         return provider.tryLock(type, getKeys(keys));
     }
 
+    /**
+     * 等待获取排他锁,线程阻塞直到成功获取
+     * 
+     * @param type
+     * @param keys
+     */
     public void waitlock(LockType type, Object... keys) {
         provider.waitLock(type, getKeys(keys));
     }
 
+    /**
+     * 
+     * 释放已获取的排他锁
+     * 
+     * @param type
+     * @param keys
+     */
     public void unlock(LockType type, Object... keys) {
         provider.unlock(type, getKeys(keys));
     }
 
+    /**
+     * 检测排他锁是否已被获取
+     * 
+     * @param type
+     * @param keys
+     * @return
+     */
     public boolean isLocked(LockType type, Object... keys) {
         return provider.isLocked(type, getKeys(keys));
     }
 
-    public void waitUnlock(LockType type, Object... keys) {
-        provider.waitUnlock(type, getKeys(keys));
+    /**
+     * 等待获取可重入的读锁
+     * 
+     * @param type
+     * @param keys
+     */
+    public void watch(LockType type, Object... keys) {
+        provider.waitLock(type, getKeys(keys));
+    }
+
+    /**
+     * 
+     * 释放已获取的读锁
+     * 
+     * @param type
+     * @param keys
+     */
+    public void unwatch(LockType type, Object... keys) {
+        provider.unlock(type, getKeys(keys));
+    }
+
+    /**
+     * 释放长时间未使用的锁
+     */
+    public void clear() {
+        provider.clear();
     }
 
     private String getKeys(Object... keys) {

+ 20 - 30
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/impl/MemoryLockProvider.java

@@ -5,7 +5,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import org.springframework.stereotype.Component;
 
@@ -21,50 +21,40 @@ import cn.com.qmth.stmms.common.enums.LockType;
 @Component("memoryLockProvider")
 public class MemoryLockProvider implements LockProvider {
 
-    private Map<LockType, Map<String, AtomicBoolean>> lockMap = new HashMap<>();
+    private Map<LockType, Map<String, ReentrantReadWriteLock>> lockMap = new HashMap<>();
 
     @Override
     public void waitLock(LockType type, String key) {
-        AtomicBoolean lock = getLock(type, key);
-        while (true) {
-            if (lock.compareAndSet(false, true)) {
-                return;
-            }
-        }
+        getLock(type, key).writeLock().lock();
     }
 
     @Override
     public boolean tryLock(LockType type, String key) {
-        AtomicBoolean lock = getLock(type, key);
-        return lock.compareAndSet(false, true);
+        return getLock(type, key).writeLock().tryLock();
     }
 
     @Override
     public boolean isLocked(LockType type, String key) {
-        AtomicBoolean lock = getLock(type, key);
-        return lock.get();
+        return getLock(type, key).writeLock().getHoldCount() > 0;
     }
 
     @Override
     public void unlock(LockType type, String key) {
-        AtomicBoolean lock = getLock(type, key);
-        while (true) {
-            if (lock.compareAndSet(true, false)) {
-                return;
-            }
-        }
+        getLock(type, key).writeLock().unlock();
     }
 
     @Override
-    public void waitUnlock(LockType type, String key) {
-        AtomicBoolean lock = getLock(type, key);
-        while (lock.get()) {
-            ;
-        }
+    public void watch(LockType type, String key) {
+        getLock(type, key).readLock().lock();
+    }
+
+    @Override
+    public void unwatch(LockType type, String key) {
+        getLock(type, key).readLock().unlock();
     }
 
-    private AtomicBoolean getLock(LockType type, String key) {
-        Map<String, AtomicBoolean> map = lockMap.get(type);
+    private ReentrantReadWriteLock getLock(LockType type, String key) {
+        Map<String, ReentrantReadWriteLock> map = lockMap.get(type);
         if (map == null) {
             synchronized (lockMap) {
                 map = lockMap.get(type);
@@ -75,12 +65,12 @@ public class MemoryLockProvider implements LockProvider {
             }
         }
 
-        AtomicBoolean lock = map.get(key);
+        ReentrantReadWriteLock lock = map.get(key);
         if (lock == null) {
             synchronized (map) {
                 lock = map.get(key);
                 if (lock == null) {
-                    lock = new AtomicBoolean(false);
+                    lock = new ReentrantReadWriteLock();
                     map.put(key, lock);
                 }
             }
@@ -94,10 +84,10 @@ public class MemoryLockProvider implements LockProvider {
             return;
         }
         synchronized (lockMap) {
-            for (Map<String, AtomicBoolean> map : lockMap.values()) {
+            for (Map<String, ReentrantReadWriteLock> map : lockMap.values()) {
                 Set<String> keys = new HashSet<>();
-                for (Entry<String, AtomicBoolean> entry : map.entrySet()) {
-                    if (!entry.getValue().get()) {
+                for (Entry<String, ReentrantReadWriteLock> entry : map.entrySet()) {
+                    if (!entry.getValue().isWriteLocked()) {
                         keys.add(entry.getKey());
                     }
                 }

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

@@ -22,10 +22,11 @@ import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
-import cn.com.qmth.stmms.biz.lock.impl.MemoryLockProvider;
+import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
 import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
+import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 
 /**
@@ -55,10 +56,7 @@ public class MarkCronService {
     private MarkService markService;
 
     @Autowired
-    private MarkLockService markLockService;
-
-    @Autowired
-    private MemoryLockProvider memoryLock;
+    private LockService lockService;
 
     @Autowired
     private MarkerService markerService;
@@ -72,8 +70,7 @@ public class MarkCronService {
     @Scheduled(cron = "${mark.cleanLockSchedule}")
     public void cronCleanLock() {
         try {
-            markLockService.clear();
-            memoryLock.clear();
+            lockService.clear();
         } catch (Exception e) {
             log.error("CronCleanTask error", e);
         }
@@ -95,7 +92,7 @@ public class MarkCronService {
     /**
      * 定时生成评卷任务
      */
-    @Scheduled(fixedDelay = 2 * 60 * 1000, initialDelay = 60 * 1000)
+    @Scheduled(fixedDelay = 60 * 1000, initialDelay = 60 * 1000)
     public void buildLibrary() {
         log.info("start auto-create library");
         try {
@@ -114,7 +111,7 @@ public class MarkCronService {
     /**
      * 定时更新评卷员质量监控指标
      */
-    @Scheduled(fixedDelay = 2 * 60 * 1000, initialDelay = 60 * 1000)
+    @Scheduled(fixedDelay = 60 * 1000, initialDelay = 60 * 1000)
     public void updateMarkerQuality() {
         log.debug("start auto-update marker");
         try {
@@ -158,73 +155,105 @@ public class MarkCronService {
                     subject.getCode());
             if (list != null) {
                 for (ExamStudent student : list) {
-                    markService.deleteByStudent(student);
+                    try {
+                        lockService.waitlock(LockType.STUDENT, student.getId());
+                        markService.deleteByStudent(student);
+                    } catch (Exception e) {
+                        log.error("delete student library error", e);
+                    } finally {
+                        lockService.unlock(LockType.STUDENT, student.getId());
+                    }
                 }
             }
             // 处理正常考生
             // 生成正评任务
-            buildFormalLibrary(subject, campusMap);
+            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
+                    MarkStatus.FORMAL);
+            for (MarkGroup group : groups) {
+                buildFormalLibrary(subject, campusMap, group);
+            }
             // 生成试评任务
-            buildTrialLibrary(subject, campusMap);
+            groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
+                    MarkStatus.TRIAL);
+            for (MarkGroup group : groups) {
+                buildTrialLibrary(campusMap, group);
+            }
         }
     }
 
-    private void buildFormalLibrary(ExamSubject subject, Map<String, Campus> campusMap) {
-        List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
-                MarkStatus.FORMAL);
-        for (MarkGroup group : groups) {
-            Date lastBuildTime = group.getBuildTime();
-            ExamStudent student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber(), lastBuildTime);
-            while (student != null) {
-                // 补充学习中心集合
-                Campus campus = getCampus(student, campusMap);
-                if (campus == null) {
-                    log.error("campus unexist for student id=" + student.getId());
-                    continue;
+    private void buildFormalLibrary(ExamSubject subject, Map<String, Campus> campusMap, MarkGroup group) {
+        Date lastBuildTime = group.getBuildTime();
+        ExamStudent student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(),
+                group.getNumber(), lastBuildTime);
+        while (student != null) {
+            // 补充学习中心集合
+            Campus campus = getCampus(student, campusMap);
+            if (campus == null) {
+                log.error("campus unexist for student id=" + student.getId());
+                continue;
+            }
+            // 尝试构造评卷任务
+            try {
+                lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+                lockService.watch(LockType.STUDENT, student.getId());
+                // 上锁后重新验证状态
+                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                        MarkStatus.FORMAL)) {
+                    return;
                 }
-                // 尝试构造评卷任务
-                try {
+                if (studentService.validateStatus(student.getId())) {
                     markService.buildFormalLibrary(student, campus, group, subject);
                     markService.updateLibraryCount(group);
                     markService.updateMarkedCount(group);
                     lastBuildTime = student.getUploadTime();
-                } catch (Exception e) {
-                    log.error("build formal library error for studentId=" + student.getId() + ", groupNumber="
-                            + group.getNumber(), e);
                 }
-                // 取下一个考生
-                student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(),
-                        group.getNumber(), lastBuildTime);
+            } catch (Exception e) {
+                log.error("build formal library error for studentId=" + student.getId() + ", groupNumber="
+                        + group.getNumber(), e);
+            } finally {
+                lockService.unwatch(LockType.STUDENT, student.getId());
+                lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
             }
+            // 取下一个考生
+            student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                    lastBuildTime);
         }
     }
 
-    private void buildTrialLibrary(ExamSubject subject, Map<String, Campus> campusMap) {
-        List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
-                MarkStatus.TRIAL);
-        for (MarkGroup group : groups) {
-            while (group.getTrialCount() > group.getLibraryCount()) {
-                // 随机取一个未生成试评任务的考生
-                ExamStudent student = studentService.randomUnTrialStudent(group.getExamId(), group.getSubjectCode(),
-                        group.getNumber());
-                if (student == null) {
-                    break;
-                }
-                // 补充学习中心集合
-                Campus campus = getCampus(student, campusMap);
-                if (campus == null) {
-                    log.error("campus unexist for student id=" + student.getId());
-                    continue;
+    private void buildTrialLibrary(Map<String, Campus> campusMap, MarkGroup group) {
+        while (group.getTrialCount() > group.getLibraryCount()) {
+            // 随机取一个未生成试评任务的考生
+            ExamStudent student = studentService.randomUnTrialStudent(group.getExamId(), group.getSubjectCode(),
+                    group.getNumber());
+            if (student == null) {
+                return;
+            }
+            // 补充学习中心集合
+            Campus campus = getCampus(student, campusMap);
+            if (campus == null) {
+                log.error("campus unexist for student id=" + student.getId());
+                continue;
+            }
+            // 尝试构造试评任务
+            try {
+                lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+                lockService.watch(LockType.STUDENT, student.getId());
+                // 上锁后重新验证状态
+                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                        MarkStatus.TRIAL)) {
+                    return;
                 }
-                // 尝试构造试评任务
-                try {
+                if (studentService.validateStatus(student.getId())) {
                     markService.buildTrialLibrary(student, campus, group);
                     markService.updateLibraryCount(group);
-                } catch (Exception e) {
-                    log.error("build trial library error for studentId=" + student.getId() + ", groupNumber="
-                            + group.getNumber(), e);
+                    markService.updateMarkedCount(group);
                 }
+            } catch (Exception e) {
+                log.error("build trial library error for studentId=" + student.getId() + ", groupNumber="
+                        + group.getNumber(), e);
+            } finally {
+                lockService.unwatch(LockType.STUDENT, student.getId());
+                lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
             }
         }
     }

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

@@ -1,179 +0,0 @@
-package cn.com.qmth.stmms.biz.mark.service.Impl;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-/**
- * 与评卷相关的全局状态锁<br>
- * 暂时只实现JVM内存锁,后续加上Redis实现的集群锁
- * 
- * @author luoshi
- *
- */
-@Component
-public class MarkLockService {
-
-    protected static final Logger log = LoggerFactory.getLogger(MarkLockService.class);
-
-    private Map<Object, AtomicBoolean> groupMap = new HashMap<>();
-
-    private Map<Object, AtomicBoolean> studentMap = new HashMap<>();
-
-    private Map<Object, AtomicBoolean> markerMap = new HashMap<>();
-
-    /**
-     * 对评卷分组进行重置/删除操作前,先尝试锁定
-     * 
-     * @param group
-     */
-    public void lockGroup(Integer examId, String subjectCode, Integer groupNumber) {
-        lock(getLock(groupMap, getKey(examId, subjectCode, groupNumber)));
-    }
-
-    /**
-     * 对评卷分组进行重置/删除操作后,释放锁定
-     * 
-     * @param group
-     */
-    public void unlockGroup(Integer examId, String subjectCode, Integer groupNumber) {
-        unlock(getLock(groupMap, getKey(examId, subjectCode, groupNumber)));
-    }
-
-    /**
-     * 等待评卷分组释放锁定
-     * 
-     * @param group
-     */
-    public void waitUnlockGroup(Integer examId, String subjectCode, Integer groupNumber) {
-        waitUnlock(getLock(groupMap, getKey(examId, subjectCode, groupNumber)));
-    }
-
-    /**
-     * 对考生评卷任务进行删除操作前,先尝试锁定
-     * 
-     * @param studentId
-     */
-    public void lockStudent(Integer studentId) {
-        lock(getLock(studentMap, studentId));
-    }
-
-    /**
-     * 对考生评卷任务进行删除操作后,释放锁定
-     * 
-     * @param studentId
-     */
-    public void unlockStudent(Integer studentId) {
-        unlock(getLock(studentMap, studentId));
-    }
-
-    /**
-     * 等待考生释放锁定
-     * 
-     * @param studentId
-     */
-    public void waitUnlockStudent(Object studentId) {
-        waitUnlock(getLock(studentMap, studentId));
-    }
-
-    /**
-     * 评卷员进行评卷操作前,先尝试锁定
-     * 
-     * @param markerId
-     */
-    public void lockMarker(Integer markerId) {
-        lock(getLock(markerMap, markerId));
-    }
-
-    /**
-     * 评卷员进行评卷操作后,释放锁定
-     * 
-     * @param markerId
-     */
-    public void unlockMarker(Integer markerId) {
-        unlock(getLock(markerMap, markerId));
-    }
-
-    /**
-     * 等待评卷员释放锁定
-     * 
-     * @param studentId
-     */
-    public void waitUnlockMarker(Integer markerId) {
-        waitUnlock(getLock(markerMap, markerId));
-    }
-
-    public void clear() {
-        clearLock(groupMap);
-        clearLock(studentMap);
-        clearLock(markerMap);
-    }
-
-    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) {
-                    lock = new AtomicBoolean(false);
-                    map.put(key, lock);
-                }
-            }
-        }
-        return lock;
-    }
-
-    private String getKey(Integer examId, String subjectCode, Integer groupNumber) {
-        return examId + "_" + subjectCode + "_" + groupNumber;
-    }
-
-    private void lock(AtomicBoolean lock) {
-        while (true) {
-            if (lock.compareAndSet(false, true)) {
-                return;
-            }
-        }
-    }
-
-    private void unlock(AtomicBoolean lock) {
-        while (true) {
-            if (lock.compareAndSet(true, false)) {
-                return;
-            }
-        }
-    }
-
-    private void waitUnlock(AtomicBoolean lock) {
-        while (true) {
-            if (!lock.get()) {
-                return;
-            }
-        }
-    }
-
-}

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

@@ -15,7 +15,6 @@ import cn.com.qmth.stmms.biz.campus.model.Campus;
 import cn.com.qmth.stmms.biz.exam.dao.ExamQuestionDao;
 import cn.com.qmth.stmms.biz.exam.dao.MarkGroupDao;
 import cn.com.qmth.stmms.biz.exam.dao.MarkerDao;
-import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
@@ -96,9 +95,6 @@ public class MarkServiceImpl implements MarkService {
     @Autowired
     private TrialTagDao trialTagDao;
 
-    @Autowired
-    private MarkLockService lockService;
-
     @Autowired
     private ExamService examService;
 
@@ -188,14 +184,7 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void resetByGroup(MarkGroup group) {
-        try {
-            lockService.lockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            resetGroup(group);
-        } catch (Exception e) {
-            throw e;
-        } finally {
-            lockService.unlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-        }
+        resetGroup(group);
     }
 
     /**
@@ -206,45 +195,36 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void deleteGroup(MarkGroup group) {
-        try {
-            lockService.lockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            // 试评相关数据
-            trialTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            trialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            trialHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            trialLibraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            // 正评相关数据
-            trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-            arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            // 评卷员数据
-            markerDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
-                    group.getNumber());
-            // 小题数据
-            questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(group.getExamId(), group.getSubjectCode(),
-                    false, group.getNumber());
-
-            releaseByGroup(group);
-            groupDao.delete(group);
-            // 科目总分
-            subjectService.updateScore(group.getExamId(), group.getSubjectCode(), false,
-                    sumTotalScore(group.getExamId(), group.getSubjectCode()));
-            // 需要重新统分
-            Exam exam = examService.findById(group.getExamId());
-            exam.setNeedCalculate(true);
-            examService.save(exam);
-        } catch (Exception e) {
-            throw e;
-        } finally {
-            lockService.unlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-        }
+        // 试评相关数据
+        trialTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        trialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
+        trialHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        trialLibraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        // 正评相关数据
+        trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
+        arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        // 评卷员数据
+        markerDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber());
+        // 小题数据
+        questionDao.deleteByExamIdAndSubjectCodeAndObjectiveAndMainNumber(group.getExamId(), group.getSubjectCode(),
+                false, group.getNumber());
+
+        releaseByGroup(group);
+        groupDao.delete(group);
+        // 科目总分
+        subjectService.updateScore(group.getExamId(), group.getSubjectCode(), false,
+                sumTotalScore(group.getExamId(), group.getSubjectCode()));
+        // 需要重新统分
+        examService.updateNeedCalculate(group.getExamId(), true);
     }
 
     /**
@@ -255,38 +235,29 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     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.saveAndFlush(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(), false,
-                    sumTotalScore(group.getExamId(), group.getSubjectCode()));
-            resetGroup(group);
-        } catch (Exception e) {
-            log.error("update group error", e);
-            throw new RuntimeException(e);
-        } finally {
-            lockService.unlockGroup(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.saveAndFlush(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(), false,
+                sumTotalScore(group.getExamId(), group.getSubjectCode()));
+        resetGroup(group);
     }
 
     /**
@@ -383,33 +354,24 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void resetMarker(Marker marker) {
-        try {
-            lockService.waitUnlockGroup(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
-            lockService.lockMarker(marker.getId());
-
-            MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
-            if (group == null) {
-                return;
-            }
-            if (group.getStatus() == MarkStatus.FORMAL) {
-                // 仲裁和等待仲裁的任务不被重置
-                trackDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
-                specialTagDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
-                libraryDao.resetByMarkerId(marker.getId(), LibraryStatus.WAITING, LibraryStatus.ARBITRATED,
-                        LibraryStatus.WAIT_ARBITRATE);
-                markerDao.resetById(marker.getId());
-            } else if (group.getStatus() == MarkStatus.TRIAL) {
-                trialTagDao.deleteByMarkerId(marker.getId());
-                trialTrackDao.deleteByMarkerId(marker.getId());
-                trialHistoryDao.deleteByMarkerId(marker.getId());
-            }
-            updateMarkedCount(group);
-            releaseByMarker(marker);
-        } catch (Exception e) {
-            throw e;
-        } finally {
-            lockService.unlockMarker(marker.getId());
+        MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
+        if (group == null || !markerDao.exists(marker.getId())) {
+            return;
         }
+        if (group.getStatus() == MarkStatus.FORMAL) {
+            // 仲裁和等待仲裁的任务不被重置
+            trackDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
+            specialTagDao.deleteByMarkerId(marker.getId(), LibraryStatus.ARBITRATED, LibraryStatus.WAIT_ARBITRATE);
+            libraryDao.resetByMarkerId(marker.getId(), LibraryStatus.WAITING, LibraryStatus.ARBITRATED,
+                    LibraryStatus.WAIT_ARBITRATE);
+            markerDao.resetById(marker.getId());
+        } else if (group.getStatus() == MarkStatus.TRIAL) {
+            trialTagDao.deleteByMarkerId(marker.getId());
+            trialTrackDao.deleteByMarkerId(marker.getId());
+            trialHistoryDao.deleteByMarkerId(marker.getId());
+        }
+        updateMarkedCount(group);
+        releaseByMarker(marker);
     }
 
     /**
@@ -420,25 +382,18 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void deleteByStudent(ExamStudent student) {
-        try {
-            lockService.lockStudent(student.getId());
-            // 正评相关数据
-            trackDao.deleteByStudentId(student.getId());
-            specialTagDao.deleteByStudentId(student.getId());
-            arbitrateDao.deleteByStudentId(student.getId());
-            libraryDao.deleteByStudentId(student.getId());
-            // 试评相关数据
-            trialTrackDao.deleteByStudentId(student.getId());
-            trialTagDao.deleteByStudentId(student.getId());
-            trialHistoryDao.deleteByStudentId(student.getId());
-            trialLibraryDao.deleteByStudentId(student.getId());
-
-            updateAllCount(student.getExamId(), student.getSubjectCode());
-        } catch (Exception e) {
-            throw e;
-        } finally {
-            lockService.unlockStudent(student.getId());
-        }
+        // 正评相关数据
+        trackDao.deleteByStudentId(student.getId());
+        specialTagDao.deleteByStudentId(student.getId());
+        arbitrateDao.deleteByStudentId(student.getId());
+        libraryDao.deleteByStudentId(student.getId());
+        // 试评相关数据
+        trialTrackDao.deleteByStudentId(student.getId());
+        trialTagDao.deleteByStudentId(student.getId());
+        trialHistoryDao.deleteByStudentId(student.getId());
+        trialLibraryDao.deleteByStudentId(student.getId());
+
+        updateAllCount(student.getExamId(), student.getSubjectCode());
     }
 
     /**
@@ -457,60 +412,53 @@ public class MarkServiceImpl implements MarkService {
                 || group.getStatus().getValue() != result.getStatusValue()) {
             return false;
         }
-        // 等待大题释放锁定
-        lockService.waitUnlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-        // 等待考生释放锁定
-        lockService.waitUnlockStudent(result.getStudentId());
-        try {
-            // 根据评卷状态选择读取不同的评卷任务
-            if (group.getStatus() == MarkStatus.FORMAL) {
-                MarkLibrary library = libraryDao.findOne(result.getLibraryId());
-                if (library != null && library.getExamId().equals(group.getExamId())
-                        && library.getSubjectCode().equals(group.getSubjectCode())
-                        && library.getGroupNumber().equals(group.getNumber())
-                        && result.getTotalScore() <= group.getTotalScore()
-                        && StringUtils.isNotBlank(result.getScoreList())) {
-                    if (submitLibrary(library, marker, group, result)) {
-                        updateMarkedCount(group);
-                        releaseLibrary(library, marker);
-                        return true;
-                    }
+
+        // 根据评卷状态选择读取不同的评卷任务
+        if (group.getStatus() == MarkStatus.FORMAL) {
+            MarkLibrary library = libraryDao.findOne(result.getLibraryId());
+            if (library != null && library.getExamId().equals(group.getExamId())
+                    && library.getSubjectCode().equals(group.getSubjectCode())
+                    && library.getGroupNumber().equals(group.getNumber())
+                    && result.getTotalScore() <= group.getTotalScore()
+                    && StringUtils.isNotBlank(result.getScoreList())) {
+                if (submitLibrary(library, marker, group, result)) {
+                    updateMarkedCount(group);
+                    releaseLibrary(library, marker);
+                    return true;
                 }
-            } else if (group.getStatus() == MarkStatus.TRIAL) {
-                TrialLibrary library = trialLibraryDao.findOne(result.getLibraryId());
-                if (library != null && library.getExamId().equals(group.getExamId())
-                        && library.getSubjectCode().equals(group.getSubjectCode())
-                        && library.getGroupNumber().equals(group.getNumber())
-                        && result.getTotalScore() <= group.getTotalScore()
-                        && StringUtils.isNotBlank(result.getScoreList())) {
-                    TrialHistory history = new TrialHistory();
-                    history.setExamId(library.getExamId());
-                    history.setSubjectCode(library.getSubjectCode());
-                    history.setGroupNumber(library.getGroupNumber());
-                    history.setLibraryId(library.getId());
-                    history.setStudentId(library.getStudentId());
-                    history.setMarkerId(marker.getId());
-                    history.setMarkerTime(new Date());
-                    history.setMarkerScore(result.getTotalScore());
-                    history.setMarkerScoreList(result.getScoreList());
-                    history = trialHistoryDao.save(history);
-                    if (history != null) {
-                        if (result.getTrackList() != null) {
-                            trialTrackDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
-                            trialTrackDao.save(result.getTrackList(history));
-                        }
-                        if (result.getTagList() != null) {
-                            trialTagDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
-                            trialTagDao.save(result.getTagList(history));
-                        }
-                        updateMarkedCount(group);
-                        releaseLibrary(library, marker);
-                        return true;
+            }
+        } else if (group.getStatus() == MarkStatus.TRIAL) {
+            TrialLibrary library = trialLibraryDao.findOne(result.getLibraryId());
+            if (library != null && library.getExamId().equals(group.getExamId())
+                    && library.getSubjectCode().equals(group.getSubjectCode())
+                    && library.getGroupNumber().equals(group.getNumber())
+                    && result.getTotalScore() <= group.getTotalScore()
+                    && StringUtils.isNotBlank(result.getScoreList())) {
+                TrialHistory history = new TrialHistory();
+                history.setExamId(library.getExamId());
+                history.setSubjectCode(library.getSubjectCode());
+                history.setGroupNumber(library.getGroupNumber());
+                history.setLibraryId(library.getId());
+                history.setStudentId(library.getStudentId());
+                history.setMarkerId(marker.getId());
+                history.setMarkerTime(new Date());
+                history.setMarkerScore(result.getTotalScore());
+                history.setMarkerScoreList(result.getScoreList());
+                history = trialHistoryDao.save(history);
+                if (history != null) {
+                    if (result.getTrackList() != null) {
+                        trialTrackDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
+                        trialTrackDao.save(result.getTrackList(history));
+                    }
+                    if (result.getTagList() != null) {
+                        trialTagDao.deleteByLibraryIdAndMarkerId(history.getLibraryId(), history.getMarkerId());
+                        trialTagDao.save(result.getTagList(history));
                     }
+                    updateMarkedCount(group);
+                    releaseLibrary(library, marker);
+                    return true;
                 }
             }
-        } catch (Exception e) {
-            throw new RuntimeException("submit task error", e);
         }
         return false;
     }
@@ -599,7 +547,6 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public boolean backLibrary(MarkLibrary library) {
-        lockService.waitUnlockGroup(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
         if (libraryDao.resetById(library.getId(), LibraryStatus.WAITING, LibraryStatus.MARKED) > 0) {
             trackDao.deleteByLibraryId(library.getId());
             specialTagDao.deleteByLibraryId(library.getId());
@@ -619,7 +566,6 @@ public class MarkServiceImpl implements MarkService {
     @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());
@@ -637,13 +583,14 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void processArbitrate(ArbitrateHistory history) {
-        lockService.waitUnlockGroup(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
-        arbitrateDao.saveAndFlush(history);
-        libraryDao.updateHeaderResult(history.getStudentId(), history.getGroupNumber(), history.getUserId(),
-                history.getTotalScore(), history.getScoreList(), history.getUpdateTime(), LibraryStatus.ARBITRATED);
-        updateMarkedCount(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
-        scoreCalculate(studentService.findById(history.getStudentId()),
-                groupDao.findByExamIdAndSubjectCode(history.getExamId(), history.getSubjectCode()));
+        if (arbitrateDao.exists(history.getId())) {
+            arbitrateDao.saveAndFlush(history);
+            libraryDao.updateHeaderResult(history.getStudentId(), history.getGroupNumber(), history.getUserId(),
+                    history.getTotalScore(), history.getScoreList(), history.getUpdateTime(), LibraryStatus.ARBITRATED);
+            updateMarkedCount(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
+            scoreCalculate(studentService.findById(history.getStudentId()),
+                    groupDao.findByExamIdAndSubjectCode(history.getExamId(), history.getSubjectCode()));
+        }
     }
 
     /**
@@ -768,7 +715,7 @@ public class MarkServiceImpl implements MarkService {
         if (group.getStatus() == MarkStatus.FORMAL || group.getStatus() == MarkStatus.FINISH) {
             groupDao.updateMarkedCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                     (int) libraryDao.countByExamIdAndSubjectCodeAndGroupNumberAndStatus(group.getExamId(),
-                            group.getSubjectCode(), group.getNumber(), LibraryStatus.MARKED, LibraryStatus.ARBITRATED) );
+                            group.getSubjectCode(), group.getNumber(), LibraryStatus.MARKED, LibraryStatus.ARBITRATED));
         } else if (group.getStatus() == MarkStatus.TRIAL) {
             groupDao.updateMarkedCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                     (int) trialLibraryDao.countMarked(group.getExamId(), group.getSubjectCode(), group.getNumber()));
@@ -824,10 +771,6 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void buildFormalLibrary(ExamStudent student, Campus campus, MarkGroup group, ExamSubject subject) {
-        // 等待大题释放锁定
-        lockService.waitUnlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-        // 等待考生释放锁定
-        lockService.waitUnlockStudent(student.getId());
         // 查询是否已创建评卷任务
         if (libraryDao.countByStudentIdAndGroupNumber(student.getId(), group.getNumber()) == 0) {
             MarkLibrary library = new MarkLibrary();
@@ -884,10 +827,6 @@ public class MarkServiceImpl implements MarkService {
     @Override
     @Transactional
     public void buildTrialLibrary(ExamStudent student, Campus campus, MarkGroup group) {
-        // 等待大题释放锁定
-        lockService.waitUnlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
-        // 等待考生释放锁定
-        lockService.waitUnlockStudent(student.getId());
         // 查询是否已创建评卷任务
         if (trialLibraryDao.countByStudentIdAndGroupNumber(student.getId(), group.getNumber()) == 0) {
             TrialLibrary library = new TrialLibrary();

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

@@ -1,8 +1,9 @@
 package cn.com.qmth.stmms.common.enums;
 
 public enum LockType {
-    EXAM("exam"), GROUP("group"), STUDENT("student"), MARKER("marker"), USER("user"), LIBRARY("library"), ARBITRATE(
-            "arbitrate");
+    GROUP("group"), STUDENT("student"), MARKER("marker"), USER("user"), FORMAL_LIBRARY("formal_library"), TRIAL_LIBRARY(
+            "trial_library"), ARBITRATE("arbitrate"), BATCH_QUALITY(
+                    "batch_quality"), SAVE_SHEET("save_sheet"), SCORE_CALCULATE("score_calculate");
 
     private String name;
 

+ 12 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java

@@ -27,6 +27,7 @@ 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.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
 import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.query.ArbitrateHistorySearchQuery;
@@ -37,6 +38,7 @@ import cn.com.qmth.stmms.biz.user.service.UserService;
 import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.HistoryStatus;
+import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 import net.sf.json.JSONObject;
@@ -65,6 +67,9 @@ public class ArbitrateController extends BaseExamController {
     @Autowired
     private MarkService markService;
 
+    @Autowired
+    private LockService lockService;
+
     @Value("${slice.image.server}")
     private String sliceServer;
 
@@ -268,11 +273,14 @@ public class ArbitrateController extends BaseExamController {
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
     public JSONObject saveTask(HttpServletRequest request, @RequestBody Task task) {
         WebUser wu = RequestUtils.getWebUser(request);
-        ArbitrateHistory history = arbitrateService.findById(task.getLibraryId());
         JSONObject result = new JSONObject();
+        ArbitrateHistory history = arbitrateService.findById(task.getLibraryId());
         if (history != null && subjectCheck(history.getSubjectCode(), wu) && task.getTotalScore() >= 0
                 && task.getScoreList() != null) {
             try {
+                lockService.watch(LockType.GROUP, history.getExamId(), history.getSubjectCode(),
+                        history.getGroupNumber());
+
                 history.setUserId(wu.getUser().getId());
                 history.setTotalScore(task.getTotalScore());
                 history.setScoreList(task.getScoreList());
@@ -285,6 +293,9 @@ public class ArbitrateController extends BaseExamController {
             } catch (Exception e) {
                 log.error("ArbitrateController-处理仲裁卷出错", e);
                 result.accumulate("success", false);
+            } finally {
+                lockService.unwatch(LockType.GROUP, history.getExamId(), history.getSubjectCode(),
+                        history.getGroupNumber());
             }
         } else {
             result.accumulate("success", false);

+ 20 - 4
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/LibraryController.java

@@ -18,6 +18,7 @@ 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.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
@@ -25,6 +26,7 @@ import cn.com.qmth.stmms.biz.mark.service.MarkService;
 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.LockType;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
@@ -48,6 +50,9 @@ public class LibraryController extends BaseExamController {
     @Autowired
     private MarkService markService;
 
+    @Autowired
+    private LockService lockService;
+
     @RequestMapping
     public String list(Model model, HttpServletRequest request, MarkLibrarySearchQuery query,
             @RequestParam(required = false) LibraryStatus status) {
@@ -100,11 +105,22 @@ public class LibraryController extends BaseExamController {
         MarkLibrary library = libraryService.findById(id);
         if (library != null) {
             if (subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
-                if (markService.backLibrary(library)) {
-                    obj.accumulate("success", true);
-                } else {
+                try {
+                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
+                            library.getGroupNumber());
+                    if (markService.backLibrary(library)) {
+                        obj.accumulate("success", true);
+                    } else {
+                        obj.accumulate("success", false);
+                        obj.accumulate("message", "无法打回该评卷任务");
+                    }
+                } catch (Exception e) {
                     obj.accumulate("success", false);
-                    obj.accumulate("message", "无法打回该评卷任务");
+                    obj.accumulate("message", "打回评卷任务失败");
+                    log.error("back library error", e);
+                } finally {
+                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
+                            library.getGroupNumber());
                 }
             } else {
                 obj.accumulate("success", false);

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

@@ -33,10 +33,12 @@ import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 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.MarkMode;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.enums.Role;
@@ -74,6 +76,9 @@ public class MarkGroupController extends BaseExamController {
     @Autowired
     private MarkService markService;
 
+    @Autowired
+    private LockService lockService;
+
     @Value("${slice.image.server}")
     private String imageServer;
 
@@ -149,8 +154,12 @@ public class MarkGroupController extends BaseExamController {
         if (group == null) {
             return "redirect:/admin/exam/mark";
         }
-        markService.releaseByGroup(group);
-
+        try {
+            lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+            markService.releaseByGroup(group);
+        } finally {
+            lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+        }
         redirectAttributes.addAttribute("subjectCode", subjectCode);
         return "redirect:/admin/exam/group";
     }
@@ -164,7 +173,15 @@ public class MarkGroupController extends BaseExamController {
         if (group == null) {
             return "redirect:/admin/exam/mark";
         }
-        markService.resetByGroup(group);
+        try {
+            lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+            markService.resetByGroup(group);
+        } catch (Exception e) {
+            log.error("reset group error", e);
+            throw new RuntimeException("重置大题失败", e);
+        } finally {
+            lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+        }
 
         redirectAttributes.addAttribute("subjectCode", subjectCode);
         return "redirect:/admin/exam/group";
@@ -179,23 +196,30 @@ public class MarkGroupController extends BaseExamController {
         if (group == null) {
             return "redirect:/admin/exam/mark";
         }
-        boolean allow = false;
-        if (group.getStatus() == MarkStatus.TRIAL && status == MarkStatus.FORMAL) {
-            allow = true;
-        } else if (group.getStatus() == MarkStatus.FORMAL && status == MarkStatus.FINISH && group.getLeftCount() == 0) {
-            allow = true;
-        } else if (group.getStatus() == MarkStatus.FINISH && status == MarkStatus.FORMAL) {
-            allow = true;
-        }
-        if (allow) {
-            if (groupService.updateStatus(examId, subjectCode, number, status, group.getStatus())
-                    && status == MarkStatus.FORMAL) {
-                // 切换到正评成功后刷新任务数量
-                group.setStatus(status);
-                markService.updateMarkedCount(group);
+        try {
+            lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
+            boolean allow = false;
+            if (group.getStatus() == MarkStatus.TRIAL && status == MarkStatus.FORMAL) {
+                allow = true;
+            } else if (group.getStatus() == MarkStatus.FORMAL && status == MarkStatus.FINISH
+                    && group.getLeftCount() == 0) {
+                allow = true;
+            } else if (group.getStatus() == MarkStatus.FINISH && status == MarkStatus.FORMAL) {
+                allow = true;
             }
-        } else {
-            redirectAttributes.addAttribute("message", "不能切换到指定的评卷状态");
+            if (allow) {
+                if (groupService.updateStatus(examId, subjectCode, number, status, group.getStatus())
+                        && status == MarkStatus.FORMAL) {
+                    // 切换到正评成功后刷新任务数量
+                    group.setStatus(status);
+                    markService.updateLibraryCount(group);
+                    markService.updateMarkedCount(group);
+                }
+            } else {
+                redirectAttributes.addAttribute("message", "不能切换到指定的评卷状态");
+            }
+        } finally {
+            lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
         }
         redirectAttributes.addAttribute("subjectCode", subjectCode);
         return "redirect:/admin/exam/group";
@@ -267,9 +291,19 @@ public class MarkGroupController extends BaseExamController {
             @RequestParam String subjectCode, @RequestParam Integer number) {
         int examId = getSessionExamId(request);
         MarkGroup group = groupService.findOne(examId, subjectCode, number);
-        if (group != null) {
+        if (group == null) {
+            return "redirect:/admin/exam/group";
+        }
+        try {
+            lockService.waitlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
             markService.deleteGroup(group);
+        } catch (Exception e) {
+            log.error("delete group error", e);
+            throw new RuntimeException("删除大题失败", e);
+        } finally {
+            lockService.unlock(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
         }
+
         redirectAttributes.addAttribute("subjectCode", subjectCode);
         redirectAttributes.addAttribute("number", number);
         return "redirect:/admin/exam/group";
@@ -326,7 +360,17 @@ public class MarkGroupController extends BaseExamController {
             ScorePolicy policy = scorePolicy != null ? ScorePolicy.findByValue(scorePolicy) : null;
             List<Double> scores = buildDoubleList(scoreList);
             if (scores.size() > 0) {
-                markService.updateGroup(group, scores, policy);
+                try {
+                    lockService.waitlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(),
+                            group.getNumber());
+                    markService.updateGroup(group, scores, policy);
+                } catch (Exception e) {
+                    log.error("update group error", e);
+                    throw new RuntimeException("更新大题失败", e);
+                } finally {
+                    lockService.unlock(LockType.GROUP, true, group.getExamId(), group.getSubjectCode(),
+                            group.getNumber());
+                }
             }
             redirectAttributes.addAttribute("subjectCode", subjectCode);
             redirectAttributes.addAttribute("number", number);

+ 9 - 8
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkQualityController.java

@@ -59,7 +59,7 @@ public class MarkQualityController extends BaseExamController {
 
     @Autowired
     private MarkLibraryService libraryService;
-    
+
     @RequestMapping
     public String list(Model model, HttpServletRequest request, MarkerSearchQuery query) {
         int examId = getSessionExamId(request);
@@ -80,7 +80,7 @@ public class MarkQualityController extends BaseExamController {
         }
         if (query.getSubjectCode() != null && query.getGroupNumber() != null) {
             query = markerService.findByQuery(query);
-            model.addAttribute("running", lockService.isLocked(LockType.GROUP,
+            model.addAttribute("running", lockService.isLocked(LockType.BATCH_QUALITY,
                     getLockKey(examId, query.getSubjectCode(), query.getGroupNumber())));
         }
         model.addAttribute("query", query);
@@ -96,13 +96,13 @@ public class MarkQualityController extends BaseExamController {
         MarkGroup group = groupService.findOne(examId, subjectCode, groupNumber);
         if (group != null) {
             final String lockKey = getLockKey(examId, subjectCode, groupNumber);
-            if (lockService.trylock(LockType.GROUP, lockKey)) {
+            if (lockService.trylock(LockType.BATCH_QUALITY, lockKey)) {
                 taskExecutor.submit(new MarkQualityThread(
                         markerService.findByExamAndSubjectAndGroup(examId, subjectCode, groupNumber), new Callback() {
 
                             @Override
                             public void invoke() {
-                                lockService.unlock(LockType.GROUP, lockKey);
+                                lockService.unlock(LockType.BATCH_QUALITY, lockKey);
                             }
                         }));
             }
@@ -126,7 +126,8 @@ public class MarkQualityController extends BaseExamController {
             List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subjectCode, groupNumber);
             List<Double> scores = libraryService.findScore(examId, subjectCode, groupNumber, LibraryStatus.MARKED);
             for (Marker marker : markers) {
-                List<Object[]> libraries = libraryService.findScoreCount(examId, subjectCode, groupNumber,marker.getId(), LibraryStatus.MARKED);
+                List<Object[]> libraries = libraryService.findScoreCount(examId, subjectCode, groupNumber,
+                        marker.getId(), LibraryStatus.MARKED);
                 Map<Double, Long> scoreCount = new HashMap<Double, Long>();
                 for (Object[] array : libraries) {
                     Double score = (Double) array[0];
@@ -158,7 +159,7 @@ public class MarkQualityController extends BaseExamController {
         List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subjectCode, groupNumber);
         for (Marker marker : markers) {
             List<Object[]> libraries = libraryService.findScoreCount(examId, subjectCode, groupNumber, marker.getId(),
-            			LibraryStatus.MARKED);
+                    LibraryStatus.MARKED);
             MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
             query.setMarkerId(marker.getId());
             query.addStatus(LibraryStatus.MARKED);
@@ -168,8 +169,8 @@ public class MarkQualityController extends BaseExamController {
                 Double score = (Double) array[0];
                 Long count = (Long) array[1];
                 double percent = 0;
-                if(totalCount!=0){
-                	percent = count * 100 / totalCount;
+                if (totalCount != 0) {
+                    percent = count * 100 / totalCount;
                 }
                 scorePercent.put(score, percent);
             }

+ 44 - 5
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkerController.java

@@ -35,9 +35,11 @@ import cn.com.qmth.stmms.biz.exam.query.MarkerSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 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.MarkerExcelError;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
@@ -66,6 +68,9 @@ public class MarkerController extends BaseExamController {
     @Autowired
     private MarkService markService;
 
+    @Autowired
+    private LockService lockService;
+
     @Value("${marker.showBtnImportAndBtnUpdateImport}")
     private String showBtnImport;
 
@@ -113,7 +118,14 @@ public class MarkerController extends BaseExamController {
         JSONObject result = new JSONObject();
         Marker marker = markerService.findById(id);
         if (marker != null) {
-            markService.releaseByMarker(marker);
+            try {
+                lockService.waitlock(LockType.MARKER, marker.getId());
+                markService.releaseByMarker(marker);
+            } catch (Exception e) {
+                log.error("release marker error", e);
+            } finally {
+                lockService.unlock(LockType.MARKER, marker.getId());
+            }
             result.accumulate("success", true);
         } else {
             result.accumulate("success", false);
@@ -160,10 +172,23 @@ public class MarkerController extends BaseExamController {
 
     @RequestMapping(value = "/reset")
     @RoleRequire(Role.SCHOOL_ADMIN)
-    public String delete(@RequestParam Integer id, Model model, RedirectAttributes redirectAttributes) {
+    public String resetMarker(@RequestParam Integer id, Model model, RedirectAttributes redirectAttributes) {
         Marker marker = markerService.findById(id);
         if (marker != null) {
-            markService.resetMarker(marker);
+            try {
+                lockService.waitlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
+                        marker.getGroupNumber());
+                lockService.waitlock(LockType.MARKER, marker.getId());
+
+                markService.resetMarker(marker);
+            } catch (Exception e) {
+                log.error("reset marker error", e);
+                throw new RuntimeException("重置评卷员失败", e);
+            } finally {
+                lockService.unlock(LockType.MARKER, marker.getId());
+                lockService.unlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
+                        marker.getGroupNumber());
+            }
         }
         redirectAttributes.addAttribute("subjectCode", marker.getSubjectCode());
         redirectAttributes.addAttribute("groupNumber", marker.getGroupNumber());
@@ -177,8 +202,22 @@ public class MarkerController extends BaseExamController {
         Marker marker = markerService.findById(id);
         JSONObject obj = new JSONObject();
         if (marker != null) {
-            markService.resetMarker(marker);
-            obj.accumulate("success", true);
+            try {
+                lockService.waitlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
+                        marker.getGroupNumber());
+                lockService.waitlock(LockType.MARKER, marker.getId());
+
+                markService.resetMarker(marker);
+                obj.accumulate("success", true);
+            } catch (Exception e) {
+                log.error("reset marker error", e);
+                obj.accumulate("success", false);
+                obj.accumulate("message", "重置评卷员失败");
+            } finally {
+                lockService.unlock(LockType.MARKER, marker.getId());
+                lockService.unlock(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(),
+                        marker.getGroupNumber());
+            }
         } else {
             obj.accumulate("success", false);
             obj.accumulate("message", "该评卷员不存在");

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

@@ -35,7 +35,6 @@ import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
-import cn.com.qmth.stmms.biz.exam.service.ScoreRateService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.report.service.ReportService;
@@ -72,9 +71,6 @@ public class ScoreController extends BaseExamController {
     @Autowired
     private MarkGroupService groupService;
 
-    @Autowired
-    private ScoreRateService scoreRateService;
-
     @Autowired
     private AsyncTaskExecutor taskExecutor;
 
@@ -125,7 +121,9 @@ public class ScoreController extends BaseExamController {
             buildPackageUrl(student);
             buildAnswerUrl(student);
         }
-        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode())?enableExport(examId,query.getSubjectCode()):enableExport(examId);
+        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode())
+                ? enableExport(examId, query.getSubjectCode())
+                : enableExport(examId);
         if (exportMessage != null) {
             view.addObject("exportMessage", exportMessage);
             view.addObject("enableExport", false);
@@ -135,7 +133,7 @@ public class ScoreController extends BaseExamController {
         view.addObject("query", query);
         view.addObject("filter", filter);
         view.addObject("subjectList", getExamSubject(examId, wu));
-        view.addObject("running", lockService.isLocked(LockType.EXAM, examId));
+        view.addObject("running", lockService.isLocked(LockType.SCORE_CALCULATE, examId));
         view.addObject("imageServer", imageServer);
         view.addObject("packageServer", packageServer);
         view.addObject("cardServer", cardServer);
@@ -201,9 +199,9 @@ public class ScoreController extends BaseExamController {
     @RoleRequire(Role.SCHOOL_ADMIN)
     public ModelAndView calculate(HttpServletRequest request) {
         int examId = getSessionExamId(request);
-        if (lockService.trylock(LockType.EXAM, examId)) {
+        if (lockService.trylock(LockType.SCORE_CALCULATE, examId)) {
             ScoreCalculateThread thread = new ScoreCalculateThread(examId, lockService, studentService, questionService,
-                    markService, checkStudentService, reportService, examService, subjectService, groupService);
+                    markService, reportService, examService, subjectService, groupService);
             taskExecutor.submit(thread);
         }
         return new ModelAndView("redirect:/admin/exam/score");
@@ -214,7 +212,9 @@ public class ScoreController extends BaseExamController {
             RedirectAttributes redirectAttributes) {
         WebUser wu = RequestUtils.getWebUser(request);
         int examId = getSessionExamId(request);
-        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode())?enableExport(examId,query.getSubjectCode()):enableExport(examId);
+        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode())
+                ? enableExport(examId, query.getSubjectCode())
+                : enableExport(examId);
         if (exportMessage == null && StringUtils.isNotBlank(query.getStudentCode())) {
             exportMessage = enableExport(examId, query.getStudentCode());
         }

+ 23 - 5
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TrialController.java

@@ -23,6 +23,7 @@ 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.lock.LockService;
 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;
@@ -31,6 +32,7 @@ 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.LockType;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.DateUtils;
@@ -60,6 +62,9 @@ public class TrialController extends BaseExamController {
     @Autowired
     private MarkerService markerService;
 
+    @Autowired
+    private LockService lockService;
+
     @Value("${slice.image.server}")
     private String sliceServer;
 
@@ -86,8 +91,8 @@ public class TrialController extends BaseExamController {
             query.orderById();
             query = trialService.findLibrary(query);
             for (TrialLibrary library : query.getResult()) {
-            	library.setMarkCount(trialService.countHistory(library.getId()));
-			}
+                library.setMarkCount(trialService.countHistory(library.getId()));
+            }
             model.addAttribute("group",
                     groupService.findOne(query.getExamId(), query.getSubjectCode(), query.getGroupNumber()));
         }
@@ -107,11 +112,24 @@ public class TrialController extends BaseExamController {
         if (library != null) {
             if (library.getExamId().equals(examId)
                     && subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
-                if (markService.resetLibrary(library)) {
-                    obj.accumulate("success", true);
-                } else {
+                try {
+                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
+                            library.getGroupNumber());
+                    lockService.waitlock(LockType.TRIAL_LIBRARY, library.getId());
+                    if (markService.resetLibrary(library)) {
+                        obj.accumulate("success", true);
+                    } else {
+                        obj.accumulate("success", false);
+                        obj.accumulate("message", "无法打回该评卷任务");
+                    }
+                } catch (Exception e) {
+                    log.error("back trial library error", e);
                     obj.accumulate("success", false);
                     obj.accumulate("message", "无法打回该评卷任务");
+                } finally {
+                    lockService.unlock(LockType.TRIAL_LIBRARY, library.getId());
+                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
+                            library.getGroupNumber());
                 }
             } else {
                 obj.accumulate("success", false);

+ 3 - 8
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java

@@ -13,7 +13,6 @@ import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 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.service.CheckStudentService;
 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;
@@ -51,8 +50,6 @@ public class ScoreCalculateThread implements Runnable {
 
     private Map<String, List<MarkGroup>> groupMap;
 
-    private CheckStudentService checkStudentService;
-
     private MarkGroupService groupService;
 
     private ExamSubjectService subjectService;
@@ -62,15 +59,13 @@ public class ScoreCalculateThread implements Runnable {
     private ReportContext context;
 
     public ScoreCalculateThread(int examId, LockService lockService, ExamStudentService studentService,
-            ExamQuestionService questionService, MarkService markService, CheckStudentService checkStudentService,
-            ReportService reportService, ExamService examService, ExamSubjectService subjectService,
-            MarkGroupService groupService) {
+            ExamQuestionService questionService, MarkService markService, ReportService reportService,
+            ExamService examService, ExamSubjectService subjectService, MarkGroupService groupService) {
         this.examId = examId;
         this.lockService = lockService;
         this.studentService = studentService;
         this.questionService = questionService;
         this.markService = markService;
-        this.checkStudentService = checkStudentService;
         this.reportService = reportService;
         this.examService = examService;
         this.subjectService = subjectService;
@@ -126,7 +121,7 @@ public class ScoreCalculateThread implements Runnable {
         } catch (Exception e) {
             log.error("calculate exception for examId=" + examId, e);
         } finally {
-            lockService.unlock(LockType.EXAM, examId);
+            lockService.unlock(LockType.SCORE_CALCULATE, examId);
             log.info("finish calculate for examId=" + examId);
         }
     }

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/PictureController.java

@@ -114,10 +114,10 @@ public class PictureController {
             result.accumulate("message", "file.save not exists");
             return result;
         }
-        if (lockService.trylock(LockType.EXAM, examId)) {
+        if (lockService.trylock(LockType.SAVE_SHEET, examId)) {
             taskExecutor.submit(new SheetDownloadThread(examId, saveDir, withTag, this, studentService, lockService));
         }
-        result.accumulate("running", lockService.isLocked(LockType.EXAM, examId));
+        result.accumulate("running", lockService.isLocked(LockType.SAVE_SHEET, examId));
         return result;
     }
 
@@ -125,7 +125,7 @@ public class PictureController {
     @ResponseBody
     public Object saveAllSheetStatus(HttpServletResponse response, @PathVariable Integer examId) throws IOException {
         JSONObject result = new JSONObject();
-        result.accumulate("running", lockService.isLocked(LockType.EXAM, examId));
+        result.accumulate("running", lockService.isLocked(LockType.SAVE_SHEET, examId));
         return result;
     }
 

+ 1 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/api/utils/SheetDownloadThread.java

@@ -64,7 +64,7 @@ public class SheetDownloadThread implements Runnable {
             }
         }
 
-        lockService.unlock(LockType.EXAM, examId);
+        lockService.unlock(LockType.SAVE_SHEET, examId);
     }
 
     private void download(ExamStudent student, int index) {

+ 58 - 33
stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java

@@ -29,6 +29,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.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.exam.service.TagService;
+import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
 import cn.com.qmth.stmms.biz.mark.model.MarkResult;
 import cn.com.qmth.stmms.biz.mark.model.Task;
@@ -39,9 +40,9 @@ import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.mark.service.TrialService;
-import cn.com.qmth.stmms.biz.mark.service.Impl.MarkLockService;
 import cn.com.qmth.stmms.common.controller.BaseController;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkMode;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
@@ -82,7 +83,7 @@ public class MarkController extends BaseController {
     private MarkGroupService groupService;
 
     @Autowired
-    private MarkLockService lockService;
+    private LockService lockService;
 
     @Value("${slice.image.server}")
     private String sliceServer;
@@ -126,8 +127,7 @@ public class MarkController extends BaseController {
     private ModelAndView getMarkModeView(Marker marker, MarkMode mode) {
         boolean forceMode = false;
         MarkMode sysMode = MarkMode.findByName(forceMarkMode);
-        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(),
-                marker.getGroupNumber());
+        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
         if (sysMode != null) {
             // 全局配置的强制评卷模式
             mode = sysMode;
@@ -159,8 +159,7 @@ public class MarkController extends BaseController {
 
     @RequestMapping("/logout")
     public ModelAndView logout(HttpServletRequest request) {
-        Marker marker = RequestUtils.getWebUser(request).getMarker();
-        markService.releaseByMarker(marker);
+        releaseMarker(RequestUtils.getWebUser(request).getMarker());
         return new ModelAndView("redirect:/logout");
     }
 
@@ -180,17 +179,13 @@ public class MarkController extends BaseController {
         modelAndView.addObject("subject", subjectService.find(marker.getExamId(), marker.getSubjectCode()));
         Exam exam = examService.findById(marker.getExamId());
         modelAndView.addObject("forceSpecialTag", exam.isForceSpecialTag());
-        markService.releaseByMarker(marker);
+        releaseMarker(marker);
     }
 
     @RequestMapping("/clear")
     @ResponseBody
     public void clear(HttpServletRequest request) {
-        Marker marker = RequestUtils.getWebUser(request).getMarker();
-        if (marker != null) {
-            // 清除该评卷员当前正在评卷的指定任务
-            markService.releaseByMarker(marker);
-        }
+        releaseMarker(RequestUtils.getWebUser(request).getMarker());
     }
 
     @RequestMapping("/status")
@@ -247,26 +242,36 @@ public class MarkController extends BaseController {
     @ResponseBody
     public Task getTask(HttpServletRequest request) {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
-        lockService.waitUnlockMarker(marker.getId());
-        MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
         Task task = null;
-        if (group == null) {
-            task = new Task();
-            task.setExist(false);
-            task.setMessage("评卷大题不存在");
-        } else if (group.getStatus() == MarkStatus.FINISH) {
-            task = new Task();
-            task.setExist(false);
-            task.setMessage("评卷已结束");
-        } else if (group.getStatus() == MarkStatus.TRIAL) {
-            task = getTrialTask(marker);
-        } else if (group.getStatus() == MarkStatus.FORMAL) {
-            task = getFormalTask(marker);
-        }
-        if (task == null) {
-            task = new Task();
-            task.setExist(false);
-            task.setMessage("当前无评卷任务");
+        try {
+            lockService.watch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
+            lockService.watch(LockType.MARKER, marker.getId());
+
+            MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(),
+                    marker.getGroupNumber());
+            if (group == null) {
+                task = new Task();
+                task.setExist(false);
+                task.setMessage("评卷大题不存在");
+            } else if (group.getStatus() == MarkStatus.FINISH) {
+                task = new Task();
+                task.setExist(false);
+                task.setMessage("评卷已结束");
+            } else if (group.getStatus() == MarkStatus.TRIAL) {
+                task = getTrialTask(marker);
+            } else if (group.getStatus() == MarkStatus.FORMAL) {
+                task = getFormalTask(marker);
+            }
+            if (task == null) {
+                task = new Task();
+                task.setExist(false);
+                task.setMessage("当前无评卷任务");
+            }
+        } catch (Exception e) {
+            log.error("get task error", e);
+        } finally {
+            lockService.unwatch(LockType.MARKER, marker.getId());
+            lockService.unwatch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
         }
         return task;
     }
@@ -320,12 +325,18 @@ public class MarkController extends BaseController {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
         boolean success = false;
         try {
-            lockService.lockMarker(marker.getId());
+            lockService.watch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
+            lockService.watch(LockType.STUDENT, markResult.getStudentId());
+            lockService.watch(LockType.MARKER, marker.getId());
+
             success = markService.submitTask(markResult, marker);
         } catch (Exception e) {
+            success = false;
             log.error("save task error", e);
         } finally {
-            lockService.unlockMarker(marker.getId());
+            lockService.unwatch(LockType.MARKER, marker.getId());
+            lockService.unwatch(LockType.STUDENT, markResult.getStudentId());
+            lockService.unwatch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
         }
         result.accumulate("success", success);
         result.accumulate("status", status(request));
@@ -405,4 +416,18 @@ public class MarkController extends BaseController {
         return array;
     }
 
+    private void releaseMarker(Marker marker) {
+        if (marker == null) {
+            return;
+        }
+        try {
+            lockService.waitlock(LockType.MARKER, marker.getId());
+            markService.releaseByMarker(marker);
+        } catch (Exception e) {
+            log.error("release marker error", e);
+        } finally {
+            lockService.unlock(LockType.MARKER, marker.getId());
+        }
+    }
+
 }

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

@@ -1,7 +1,7 @@
 #jdbc config
 driverClassName=com.mysql.jdbc.Driver
 #jdbc config
-url=jdbc:mysql://localhost:3306/stmms_ft?useUnicode=true&characterEncoding=UTF-8
+url=jdbc:mysql://192.168.10.30:3306/stmms_ft_online?useUnicode=true&characterEncoding=UTF-8
 username=root
 password=root