1
0
Quellcode durchsuchen

代码结构优化

1.评卷员提交评卷任务改为SQL更新模式,避免数据库行锁使用
2.提交前增加前置判断是否已领取,是否超过总分等判断
3.增加新的内存锁实现,暂时在考试级的后台任务使用
4.统分任务增加主观题合分逻辑
5.删除有风险的评卷员切换科目相关代码
luoshi vor 6 Jahren
Ursprung
Commit
11586a8cc0

+ 16 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockProvider.java

@@ -0,0 +1,16 @@
+package cn.com.qmth.stmms.biz.lock;
+
+import cn.com.qmth.stmms.common.enums.LockType;
+
+public interface LockProvider {
+
+    void waitLock(LockType type, String key);
+
+    boolean tryLock(LockType type, String key);
+
+    void unlock(LockType type, String key);
+
+    boolean isLocked(LockType type, String key);
+
+    void waitUnlock(LockType type, String key);
+}

+ 59 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockService.java

@@ -0,0 +1,59 @@
+package cn.com.qmth.stmms.biz.lock;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.stmms.biz.lock.impl.MemoryLockProvider;
+import cn.com.qmth.stmms.common.enums.LockType;
+
+@Component("lockService")
+public class LockService implements InitializingBean, ApplicationContextAware {
+
+    protected static final Logger log = LoggerFactory.getLogger(LockService.class);
+
+    private static final String KEY_JOINER = "_";
+
+    private ApplicationContext context;
+
+    private LockProvider provider;
+
+    public boolean trylock(LockType type, Object... keys) {
+        return provider.tryLock(type, getKeys(keys));
+    }
+
+    public void waitlock(LockType type, Object... keys) {
+        provider.waitLock(type, getKeys(keys));
+    }
+
+    public void unlock(LockType type, Object... keys) {
+        provider.unlock(type, getKeys(keys));
+    }
+
+    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));
+    }
+
+    private String getKeys(Object... keys) {
+        return StringUtils.join(keys, KEY_JOINER);
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.provider = this.context.getBean(MemoryLockProvider.class);
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException {
+        this.context = context;
+    }
+}

+ 108 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/impl/MemoryLockProvider.java

@@ -0,0 +1,108 @@
+package cn.com.qmth.stmms.biz.lock.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.springframework.stereotype.Component;
+
+import cn.com.qmth.stmms.biz.lock.LockProvider;
+import cn.com.qmth.stmms.common.enums.LockType;
+
+/**
+ * JVM内存实现的锁控制
+ * 
+ * @author luoshi
+ *
+ */
+@Component("memoryLockProvider")
+public class MemoryLockProvider implements LockProvider {
+
+    private Map<LockType, Map<String, AtomicBoolean>> lockMap;
+
+    @Override
+    public void waitLock(LockType type, String key) {
+        AtomicBoolean lock = getLock(type, key);
+        while (!lock.get()) {
+            lock.compareAndSet(false, true);
+        }
+    }
+
+    @Override
+    public boolean tryLock(LockType type, String key) {
+        AtomicBoolean lock = getLock(type, key);
+        return lock.compareAndSet(false, true);
+    }
+
+    @Override
+    public boolean isLocked(LockType type, String key) {
+        AtomicBoolean lock = getLock(type, key);
+        return lock.get();
+    }
+
+    @Override
+    public void unlock(LockType type, String key) {
+        AtomicBoolean lock = getLock(type, key);
+        while (lock.get()) {
+            lock.compareAndSet(true, false);
+        }
+    }
+
+    @Override
+    public void waitUnlock(LockType type, String key) {
+        AtomicBoolean lock = getLock(type, key);
+        while (lock.get()) {
+            ;
+        }
+    }
+
+    private AtomicBoolean getLock(LockType type, String key) {
+        Map<String, AtomicBoolean> map = lockMap.get(type);
+        if (map == null) {
+            synchronized (lockMap) {
+                map = lockMap.get(type);
+                if (map == null) {
+                    map = new HashMap<>();
+                    lockMap.put(type, map);
+                }
+            }
+        }
+
+        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;
+    }
+
+    public void clear() {
+        if (lockMap.isEmpty()) {
+            return;
+        }
+        synchronized (lockMap) {
+            for (Map<String, AtomicBoolean> map : lockMap.values()) {
+                Set<String> keys = new HashSet<>();
+                for (Entry<String, AtomicBoolean> entry : map.entrySet()) {
+                    if (!entry.getValue().get()) {
+                        keys.add(entry.getKey());
+                    }
+                }
+                for (String key : keys) {
+                    map.remove(key);
+                }
+                keys.clear();
+            }
+        }
+    }
+
+}

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

@@ -22,27 +22,22 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     @Query("select l from MarkLibrary l where l.id=?1")
     MarkLibrary findByIdForLock(Integer id);
 
-    List<MarkLibrary> findByExamId(Integer examId, Pageable page);
-
     List<MarkLibrary> findByExamIdAndSubjectCode(Integer examId, String subjectCode, Pageable page);
 
-    List<MarkLibrary> findByExamIdAndStatus(Integer examId, LibraryStatus status, Pageable page);
-
     List<MarkLibrary> findByExamIdAndSubjectCodeAndGroupNumberAndStatus(Integer examId, String subjectCode,
             Integer groupNumber, LibraryStatus status, Pageable page);
 
     List<MarkLibrary> findByExamIdAndSubjectCodeAndGroupNumberAndStatusIn(Integer examId, String subjectCode,
             Integer groupNumber, Set<LibraryStatus> statusSet, Pageable page);
 
-    List<MarkLibrary> findByExamIdAndMarkerId(Integer examId, Integer markerId, Pageable page);
-
-    List<MarkLibrary> findByExamIdAndExamNumber(Integer examId, String examNumber);
-
     @Query("select l from MarkLibrary l where l.studentId=?1 order by l.groupNumber ")
     List<MarkLibrary> findByStudentId(Integer studentId);
 
     List<MarkLibrary> findByStudentIdAndGroupNumber(Integer studentId, Integer groupNumber);
 
+    List<MarkLibrary> findByStudentIdAndGroupNumberAndStatus(Integer studentId, Integer groupNumber,
+            LibraryStatus status);
+
     @Query("select count(*) from MarkLibrary f where f.studentId=?1 and f.groupNumber=?2")
     long countByStudentIdAndGroupNumber(Integer studentId, Integer groupNumber);
 
@@ -60,7 +55,8 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
 
     @Modifying(clearAutomatically = true)
     @Query("update MarkLibrary m set m.status=?4, m.tags=null, m.markerId=null, m.markerTime=null, m.markerScore=null, m.markerScoreList=null , m.headerId=null , m.headerTime=null , m.headerScore=null , m.headerScoreList=null where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3")
-    void resetByExamIdAndSubjectCodeAndNumber(Integer examId, String subjectCode, Integer number, LibraryStatus status);
+    void resetByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber,
+            LibraryStatus status);
 
     @Modifying(clearAutomatically = true)
     @Query("update MarkLibrary m set m.status=?2, m.tags=null, m.markerId=null, m.markerTime=null, m.markerScore=null, m.markerScoreList=null , m.headerId=null , m.headerTime=null , m.headerScore=null , m.headerScoreList=null where m.markerId=?1")
@@ -70,28 +66,12 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     @Query("update MarkLibrary m set m.status=?2, m.tags=null, m.markerId=null, m.markerTime=null, m.markerScore=null, m.markerScoreList=null , m.headerId=null , m.headerTime=null , m.headerScore=null , m.headerScoreList=null where m.id=?1")
     void resetById(Integer id, LibraryStatus status);
 
-    @Query("select count(*) from MarkLibrary f where f.examId=?1")
-    long countByExamId(Integer examId);
-
-    @Query("select count(*) from MarkLibrary f where f.examId=?1 and f.subjectCode=?2")
-    long countByExamIdAndSubjectCode(Integer examId, String subjectCode);
-
-    @Query("select count(*) from MarkLibrary f where f.examId=?1 and f.subjectCode=?2 and f.status=?3")
-    long countByExamIdAndSubjectCodeAndStatus(Integer examId, String subjectCode, LibraryStatus status);
-
-    @Query("select count(*) from MarkLibrary f where f.id=?1 and f.markerId=?2 and f.status=?3")
-    long countByIdAndMarkerIdAndStatus(Integer id, Integer markerId, LibraryStatus status);
-
     @Query("select f.markerId, count(*) as markerCount from MarkLibrary f where f.examId=?1 and f.status=?2 group by f.markerId")
     List<Object[]> countByMarkerAndStatus(Integer examId, LibraryStatus status);
 
     @Query("select f.markerId, count(*) as markerCount from MarkLibrary f where f.examId=?1 and f.subjectCode=?2 and f.status=?3 group by f.markerId")
     List<Object[]> countByMarkerAndStatus(Integer examId, String subjectCode, LibraryStatus status);
 
-    @Modifying
-    @Query("delete MarkLibrary m where m.examId=?1")
-    void deleteByExamId(Integer examId);
-
     @Modifying
     @Query("delete MarkLibrary m where m.examId=?1 and m.subjectCode=?2 and m.groupNumber=?3")
     void deleteByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber);
@@ -104,8 +84,8 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     List<MarkLibrary> findByStudentIdAndStatus(Integer studentId, LibraryStatus status);
 
     @Modifying(clearAutomatically = true)
-    @Query("update MarkLibrary m set m.headerId=?3, m.headerScore=?4, m.headerScoreList=?5, m.headerTime=?6, "
-            + "m.status=?7 where m.studentId=?1 and m.groupNumber=?2")
+    @Query("update MarkLibrary m set m.headerId=?3, m.headerScore=?4, m.headerScoreList=?5, m.headerTime=?6, m.status=?7 "
+            + " where m.studentId=?1 and m.groupNumber=?2")
     void updateHeaderResult(Integer studentId, Integer groupNumber, Integer userId, Double totalScore, String scoreList,
             Date updateTime, LibraryStatus arbitrated);
 
@@ -113,4 +93,11 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     @Query("update MarkLibrary m set m.status=?3 where m.studentId=?1 and m.groupNumber=?2")
     void updateByStudentIdAndGroupNumber(Integer studentId, Integer groupNumber, LibraryStatus status);
 
+    @Modifying(clearAutomatically = true)
+    @Query("update MarkLibrary l set l.markerId=?2, l.markerScore=?3, l.markerScoreList=?4, l.markerTime=?5, l.tags=?6, l.status=?7 "
+            + "where l.id=?1 and (l.status=?8 or (l.status=?7 and l.markerId=?2)) "
+            + "and not exists (select m.id from MarkLibrary m where m.studentId=l.studentId and m.markerId=?2 and m.id!=l.id)")
+    long updateMarkerResult(Integer id, Integer markerId, Double markerScore, String markerScoreList, Date markerTime,
+            String tags, LibraryStatus markedStatus, LibraryStatus waitStatus);
+
 }

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

@@ -25,6 +25,7 @@ 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.lock.impl.MemoryLockProvider;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.utils.CurrentTaskUtil;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
@@ -61,6 +62,9 @@ public class MarkCronService {
     @Autowired
     private MarkLockService lockService;
 
+    @Autowired
+    private MemoryLockProvider memoryLock;
+
     @Value("${mark.cleanTimeoutMinute}")
     private long timeoutMinute;
 
@@ -71,6 +75,7 @@ public class MarkCronService {
     public void cronCleanLock() {
         try {
             lockService.clear();
+            memoryLock.clear();
         } catch (Exception e) {
             log.error("CronCleanTask error", e);
         }

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

@@ -4,6 +4,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -210,12 +211,25 @@ public class MarkServiceImpl implements MarkService {
     @Override
     public boolean applyLibrary(MarkLibrary library, Marker marker) {
         // 首先判断多评情况下,同一个studentId是否已被该评卷员处理过
-        if (libraryDao.countByStudentIdAndMarkerId(library.getStudentId(), marker.getId()) > 0) {
-            return false;
-        }
+        // if (libraryDao.countByStudentIdAndMarkerId(library.getStudentId(),
+        // marker.getId()) > 0) {
+        // return false;
+        // }
         return CurrentTaskUtil.add(marker, getApplyTaskId(library));
     }
 
+    /**
+     * 评卷员是否已领取了某个评卷任务
+     * 
+     * @param library
+     * @param marker
+     * @return
+     */
+    @Override
+    public boolean hasApplied(MarkLibrary library, Marker marker) {
+        return CurrentTaskUtil.exists(marker, getApplyTaskId(library));
+    }
+
     /**
      * 释放某个评卷员某个锁定任务
      * 
@@ -302,25 +316,18 @@ public class MarkServiceImpl implements MarkService {
         lockService.waitUnlockStudent(library.getStudentId());
 
         try {
+            Date now = new Date();
             // 尝试锁定评卷员
             lockService.lockMarker(library.getMarkerId());
-            // 锁定该评卷任务
-            MarkLibrary previous = libraryDao.findByIdForLock(library.getId());
-            // 若该评卷任务已被删除,则直接返回
-            if (previous == null) {
-                return;
-            }
-            // 该评卷任务已被别人评过,也直接返回
-            if (previous.getMarkerId() != null && !previous.getMarkerId().equals(library.getMarkerId())) {
+            // 校验总分是否超标
+            if (library.getMarkerScore() > group.getTotalScore() || StringUtils.isBlank(library.getMarkerScoreList())) {
                 return;
             }
-            // 该评卷任务状态不是已评或未评,也直接返回
-            if (previous.getStatus() != LibraryStatus.WAITING && previous.getStatus() != LibraryStatus.MARKED) {
-                return;
-            }
-            // 已经评过该考生的其他评卷任务,则直接返回
-            if (libraryDao.countByStudentIdAndMarkerIdAndIdNotEqual(library.getStudentId(), library.getMarkerId(),
-                    library.getId()) > 0) {
+            // 尝试提交评卷结果
+            if (libraryDao.updateMarkerResult(library.getId(), library.getMarkerId(), library.getMarkerScore(),
+                    library.getMarkerScoreList(), now, library.getTags(), LibraryStatus.MARKED,
+                    LibraryStatus.WAITING) == 0) {
+                // 条件不符更新失败,直接返回
                 return;
             }
             // 保存阅卷轨迹
@@ -337,12 +344,11 @@ public class MarkServiceImpl implements MarkService {
             ArbitrateHistory history = null;
             if (group.getArbitrateThreshold() != null && group.getArbitrateThreshold() > 0) {
                 // 多评模式
-                List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(library.getStudentId(),
-                        library.getGroupNumber());
+                List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumberAndStatus(library.getStudentId(),
+                        library.getGroupNumber(), LibraryStatus.MARKED);
                 for (MarkLibrary other : list) {
-                    if (other.getId().equals(library.getId()) || other.getStatus() != LibraryStatus.MARKED
-                            || other.getMarkerScore() == null || other.getHeaderScore() != null) {
-                        // 未评卷或组长已打分,则跳过该任务
+                    if (other.getId().equals(library.getId()) || other.getHeaderScore() != null) {
+                        // 本评卷任务或组长已打分,则跳过该任务
                         continue;
                     }
                     if (Math.abs(other.getMarkerScore() - library.getMarkerScore()) > group.getArbitrateThreshold()) {
@@ -354,20 +360,17 @@ public class MarkServiceImpl implements MarkService {
                         history.setStudentId(library.getStudentId());
                         history.setExamNumber(library.getExamNumber());
                         history.setStatus(HistoryStatus.WAITING);
-                        history.setCreateTime(new Date());
-                        arbitrateDao.save(history);
-
-                        library.setStatus(LibraryStatus.WAIT_ARBITRATE);
+                        history.setCreateTime(now);
+                        arbitrateDao.saveAndFlush(history);
                         break;
                     }
                 }
             }
-            library = libraryDao.saveAndFlush(library);
-            // 触发仲裁后续处理
             if (history != null) {
+                // 触发仲裁后续状态更新
                 libraryDao.updateByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber(),
                         LibraryStatus.WAIT_ARBITRATE);
-            } else if (library.getStatus() == LibraryStatus.MARKED) {
+            } else {
                 // 评卷正常完成才尝试统分
                 scoreCalculate(library.getExamId(), library.getSubjectCode(), library.getStudentId());
             }
@@ -458,48 +461,49 @@ public class MarkServiceImpl implements MarkService {
         ScorePolicy policy = group.getScorePolicy() != null ? group.getScorePolicy() : ScorePolicy.AVG;
         List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(studentId, group.getNumber());
         for (MarkLibrary library : list) {
-            if (library.getStatus() == LibraryStatus.MARKED || library.getStatus() == LibraryStatus.ARBITRATED) {
-                count++;
-                Double current = (library.getHeaderScore() != null ? library.getHeaderScore()
-                        : library.getMarkerScore());
-                List<ScoreItem> scores = library.getScoreList();
-                if (count == 1) {
-                    // 首份评卷任务,直接取总分与明细
-                    score = current;
-                    detail = scores;
-                } else {
-                    switch (policy) {
-                    case AVG:
-                        // 直接累加
-                        score += current;
-                        for (int i = 0; i < detail.size(); i++) {
-                            try {
-                                ScoreItem item = detail.get(i);
-                                ScoreItem other = scores.get(i);
-                                item.setScore(item.getScore() + other.getScore());
-                            } catch (Exception e) {
-                                continue;
-                            }
-                        }
-                        break;
-                    case MAX:
-                        if (current > score) {
-                            score = current;
-                            detail = scores;
-                        }
-                        break;
-                    case MIN:
-                        if (current < score) {
-                            score = current;
-                            detail = scores;
+            if (library.getStatus() != LibraryStatus.MARKED && library.getStatus() != LibraryStatus.ARBITRATED) {
+                // 有非完成状态的评卷任务,直接返回
+                return false;
+            }
+            count++;
+            Double current = (library.getHeaderScore() != null ? library.getHeaderScore() : library.getMarkerScore());
+            List<ScoreItem> scores = library.getScoreList();
+            if (count == 1) {
+                // 首份评卷任务,直接取总分与明细
+                score = current;
+                detail = scores;
+            } else {
+                switch (policy) {
+                case AVG:
+                    // 直接累加
+                    score += current;
+                    for (int i = 0; i < detail.size(); i++) {
+                        try {
+                            ScoreItem item = detail.get(i);
+                            ScoreItem other = scores.get(i);
+                            item.setScore(item.getScore() + other.getScore());
+                        } catch (Exception e) {
+                            continue;
                         }
-                        break;
-                    default:
-                        break;
                     }
+                    break;
+                case MAX:
+                    // 高分优先
+                    if (current > score) {
+                        score = current;
+                        detail = scores;
+                    }
+                    break;
+                case MIN:
+                    // 低分优先
+                    if (current < score) {
+                        score = current;
+                        detail = scores;
+                    }
+                    break;
+                default:
+                    break;
                 }
-            } else {
-                return false;
             }
         }
         // 取平均分策略下,累计分数需要重新计算一次
@@ -607,8 +611,8 @@ public class MarkServiceImpl implements MarkService {
      */
     private void resetGroup(MarkGroup group) {
         groupDao.resetCount(group.getExamId(), group.getSubjectCode(), group.getNumber());
-        libraryDao.resetByExamIdAndSubjectCodeAndNumber(group.getExamId(), group.getSubjectCode(), group.getNumber(),
-                LibraryStatus.WAITING);
+        libraryDao.resetByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
+                group.getNumber(), LibraryStatus.WAITING);
         arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                 group.getNumber());
         trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),

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

@@ -51,6 +51,15 @@ public interface MarkService {
      */
     boolean applyLibrary(MarkLibrary library, Marker marker);
 
+    /**
+     * 评卷员是否已领取某个评卷任务
+     * 
+     * @param library
+     * @param marker
+     * @return
+     */
+    boolean hasApplied(MarkLibrary library, Marker marker);
+
     /**
      * 释放某个评卷员某个锁定任务
      * 

+ 25 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/CurrentTaskUtil.java

@@ -27,7 +27,7 @@ public class CurrentTaskUtil {
      * 尝试领取某个任务
      * 
      * @param marker
-     * @param libraryId
+     * @param taskId
      * @return
      */
     public static boolean add(Marker marker, String taskId) {
@@ -44,11 +44,31 @@ public class CurrentTaskUtil {
         }
     }
 
+    /**
+     * 是否已领取某个任务
+     * 
+     * @param marker
+     * @param taskId
+     * @return
+     */
+    public static boolean exists(Marker marker, String taskId) {
+        String key = getKey(marker);
+        Set<TaskEntry> set = taskMap.get(key);
+        if (set != null) {
+            for (TaskEntry entry : set) {
+                if (entry.taskId.equals(taskId) && entry.markerId == marker.getId()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * 删除某个已完成的任务
      * 
      * @param marker
-     * @param libraryId
+     * @param taskId
      */
     public static void remove(Marker marker, String taskId) {
         TaskEntry obj = new TaskEntry(marker.getId(), taskId);
@@ -131,7 +151,7 @@ public class CurrentTaskUtil {
             SetMultimap<String, TaskEntry> taskMap1 = HashMultimap.create();
             // System.out.println("任务池大小:"+taskMap.size());
             // System.out.println("间隔时间:"+cleanMapinterval);
-            if (taskMap != null && taskMap.size() > 0) {
+            if (taskMap.size() > 0) {
                 Multiset<String> keysTemp = taskMap.keys();
                 Set<String> setKey = new HashSet<String>();
                 for (String key : keysTemp) {
@@ -141,7 +161,7 @@ public class CurrentTaskUtil {
                     Set<TaskEntry> set = taskMap.get(key);
                     if (set != null) {
                         for (TaskEntry obj : set) {
-                            if (getDateDifference(obj.timestamp) >= timeoutMinute) {// 如果相隔20分钟,则该试卷的放入清空池中
+                            if (getDateDifference(obj.timestamp) >= timeoutMinute) {// 如果相隔时间超过阀值,则该试卷的放入清空池中
                                 taskMap1.put(key, obj);
                             }
                         }
@@ -209,4 +229,5 @@ class TaskEntry {
             return false;
         }
     }
+
 }

+ 17 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/LockType.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.stmms.common.enums;
+
+public enum LockType {
+    EXAM("exam"), GROUP("group"), STUDENT("student"), MARKER("marker"), USER("user"), LIBRARY("library"), ARBITRATE(
+            "arbitrate");
+
+    private String name;
+
+    private LockType(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+}

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

@@ -1,376 +1,383 @@
-package cn.com.qmth.stmms.admin.exam;
-
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import net.sf.json.JSONObject;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.task.AsyncTaskExecutor;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
-import cn.com.qmth.stmms.admin.dto.ExamStudentDTO;
-import cn.com.qmth.stmms.admin.dto.ScoreEditDTO;
-import cn.com.qmth.stmms.admin.dto.ScoreExportDTO;
-import cn.com.qmth.stmms.admin.thread.ScoreCalculateThread;
-import cn.com.qmth.stmms.biz.campus.model.Campus;
-import cn.com.qmth.stmms.biz.campus.service.CampusService;
-import cn.com.qmth.stmms.biz.exam.model.ExamPackage;
-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.Marker;
-import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
-import cn.com.qmth.stmms.biz.exam.service.ExamPackageService;
-import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
-import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
-import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
-import cn.com.qmth.stmms.biz.exam.service.MarkerService;
-import cn.com.qmth.stmms.biz.exam.service.ScoreRateService;
-import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
-import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
-import cn.com.qmth.stmms.biz.user.model.User;
-import cn.com.qmth.stmms.biz.user.service.UserService;
-import cn.com.qmth.stmms.biz.utils.ScoreItem;
-import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.Role;
-import cn.com.qmth.stmms.common.utils.ExportExcel;
-import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-@Controller
-@RequestMapping("/admin/exam/score")
-public class ScoreController extends BaseExamController {
-
-    @Autowired
-    private CampusService campusService;
-
-    @Autowired
-    private ExamSubjectService subjectService;
-
-    @Autowired
-    private ExamStudentService studentService;
-
-    @Autowired
-    private ExamQuestionService questionService;
-
-    @Autowired
-    private ExamPackageService packageService;
-    
-    @Autowired
-    private MarkLibraryService markLibraryService;
-    
-    @Autowired
-    private ScoreRateService scoreRateService;
-
-    @Value("${sheet.image.server}")
-    private String imageServer;
-
-    @Value("${package.image.server}")
-    private String packageServer;
-
-    @Value("${card.server}")
-    private String cardServer;
-    
-    @Autowired
-    private MarkerService markerService;
-    
-    @Autowired
-    private UserService userService;
-
-    @Autowired
-    private AsyncTaskExecutor taskExecutor;
-
-    private AtomicBoolean running = new AtomicBoolean(false);
-
-    @RequestMapping
-    public ModelAndView list(HttpServletRequest request, ExamStudentSearchQuery query,
-            @RequestParam(defaultValue = "0") Integer filter) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        int examId = getSessionExamId(request);
-        String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
-        ModelAndView view = new ModelAndView("modules/exam/scoreList");
-        if (subjectCode != null) {
-            query.setSubjectCode(subjectCode);
-        }
-        query.setExamId(examId);
-       // query.setUpload(true);
-       // query.setAbsent(false);
-        query.orderByExamNumber();
-        if (filter == 1) {
-            query.setObjectiveScore(0d);
-            query.setSubjectiveScoreGt(0d);
-        } else if (filter == 2) {
-            query.setObjectiveScoreGt(0d);
-            query.setSubjectiveScore(0d);
-        }
-        String ids = getStudentIdsByMarkLoginAndMarkName(query.getMarkLogin(),query.getMarkName());
-        if(StringUtils.isNotBlank(ids)){
-            query.setIds(ids);
-        }
-        subjectFilter(query, wu);
-        query = studentService.findByQuery(query);
-        for (ExamStudent student : query.getResult()) {
-            buildSheetUrl(student);
-            buildPackageUrl(student);
-            buildAnswerUrl(student);
-            student.setSubjectRemark(subjectService.find(student.getExamId(), student.getSubjectCode()).getRemark());
-            List<Marker> markers = markerService.findByStudentId(student.getId());
-            if(!markers.isEmpty()){
-            	String loginNames = "";
-            	String names = "";
-            	for (Marker marker : markers) {
-            		loginNames = loginNames.equals("")?marker.getLoginName():loginNames+","+marker.getLoginName();
-            		names = names.equals("")?marker.getName():names +","+marker.getName();
-				}
-                student.setMarkLogin(loginNames);
-                student.setMarkName(names);
-            }
-            List<MarkLibrary> markLibrarys = markLibraryService.findByStudentId(student.getId());
-            if(!markLibrarys.isEmpty()){
-            	Set<String> tags = new HashSet<String>();
-            	for (MarkLibrary markLibrary : markLibrarys) {
-            		if(markLibrary.getTags()!=null){
-            			String[] tag = markLibrary.getTags().split(",");
-            			for (String s : tag) {
-            				tags.add(s);
-						}
-            		}
-            		if(markLibrary.getHeaderId()!=null){
-            			User user = userService.findById(markLibrary.getHeaderId());
-            			student.setMarkLogin(user.getLoginName());
-            			 student.setMarkName(user.getName());
-            		}
-				}
-            	if(!tags.isEmpty()){
-            		student.setTagValue(tags.toString());
-            	}
-            }
-        }
-        view.addObject("query", query);
-        view.addObject("filter", filter);
-        view.addObject("subjectList", getExamSubject(examId, wu));
-        view.addObject("running", running.get());
-        view.addObject("imageServer", imageServer);
-        view.addObject("packageServer", packageServer);
-        view.addObject("cardServer", cardServer);
-        return view;
-    }
-
-    @RequestMapping("/detail")
-    public ModelAndView detail(HttpServletRequest request, @RequestParam String examNumber,
-            @RequestParam String queryString) {
-        int examId = getSessionExamId(request);
-        ExamStudent student = studentService.findByExamIdAndExamNumber(examId, examNumber);
-        if (student != null) {
-            buildSheetUrl(student);
-            buildPackageUrl(student);
-            buildAnswerUrl(student);
-            ModelAndView view = new ModelAndView("modules/exam/scoreDetail");
-            view.addObject("student", student);
-            view.addObject("scoreList", buildScoreList(student));
-            view.addObject("imageServer", imageServer);
-            view.addObject("packageServer", packageServer);
-            view.addObject("cardServer", cardServer);
-            view.addObject("queryString", StringUtils.trimToEmpty(queryString));
-            return view;
-        } else {
-            return new ModelAndView("redirect:/admin/exam/score");
-        }
-    }
-
-    @RequestMapping("/edit")
-    @ResponseBody
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public JSONObject edit(HttpServletRequest request, ScoreEditDTO dto) {
-        JSONObject result = new JSONObject();
-        int examId = getSessionExamId(request);
-        ExamStudent student = studentService.findByExamIdAndExamNumber(examId, dto.getExamNumber());
-        if (student != null) {
-            double oScore = 0d;
-            List<ScoreItem> oList = dto.getObjectiveScoreItems();
-            for (ScoreItem item : oList) {
-                oScore += item.getScore();
-            }
-            student.setObjectiveScore(oScore);
-            student.setScoreList(oList, true);
-
-            double sScore = 0d;
-            List<ScoreItem> sList = dto.getSubjectiveScoreItems();
-            for (ScoreItem item : sList) {
-                sScore += item.getScore();
-            }
-            student.setSubjectiveScore(sScore);
-            student.setScoreList(sList, false);
-
-            studentService.save(student);
-            result.accumulate("success", true);
-        } else {
-            result.accumulate("success", false);
-            result.accumulate("message", "找不到对应的考生");
-        }
-        return result;
-    }
-
-    @RequestMapping("/calculate")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public ModelAndView calculate(HttpServletRequest request) {
-        int examId = getSessionExamId(request);
-        if (running.compareAndSet(false, true) == true) {
-            ScoreCalculateThread thread = new ScoreCalculateThread(examId, running, studentService, questionService,subjectService,scoreRateService,markLibraryService);
-            taskExecutor.submit(thread);
-        }
-        return new ModelAndView("redirect:/admin/exam/score");
-    }
-
-    @RequestMapping("/export")
-    public String export(ExamStudentSearchQuery query, HttpServletRequest request, HttpServletResponse response,
-            RedirectAttributes redirectAttributes) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        int examId = getSessionExamId(request);
-        String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
-        query.setExamId(examId);
-//        query.setUpload(true);
-//        query.setAbsent(false);
-        query.setPageNumber(1);
-        query.setPageSize(Integer.MAX_VALUE);
-        subjectFilter(query, wu);
-        String ids = getStudentIdsByMarkLoginAndMarkName(query.getMarkLogin(),query.getMarkName());
-        if(StringUtils.isNotBlank(ids)){
-            query.setIds(ids);
-        }
-        query = studentService.findByQuery(query);
-        String fileName = "成绩单.xlsx";
-        try {
-            if (subjectCode != null) {
-                fileName = subjectService.find(examId, subjectCode).getName() + "-机阅考试成绩表";
-                List<ScoreExportDTO> list = new LinkedList<ScoreExportDTO>();
-                for (ExamStudent student : query.getResult()) {
-                    if(student.isBreach() || student.isAbsent()){
-                        student.setObjectiveScore(0d);
-                        student.setSubjectiveScore(0d);
-                    }
-                    list.add(new ScoreExportDTO(student));
-                }
-                new ExportExcel(subjectCode + "-" + subjectService.find(examId, subjectCode).getName(),
-                        ScoreExportDTO.class).setDataList(list).write(response, fileName + ".xlsx").dispose();
-                return null;
-            }
-            List<ExamStudentDTO> list = new LinkedList<ExamStudentDTO>();
-            for (ExamStudent student : query.getResult()) {
-                if(student.isBreach() || student.isAbsent()){
-                    student.setObjectiveScore(0d);
-                    student.setSubjectiveScore(0d);
-                }
-                list.add(new ExamStudentDTO(student));
-            }
-            new ExportExcel("成绩单", ExamStudentDTO.class).setDataList(list).write(response, fileName).dispose();
-            return null;
-        } catch (Exception e) {
-            addMessage(redirectAttributes, "导出成绩失败!" + e.getMessage());
-            return "redirect:/admin/exam/score";
-        }
-    }
-
-    private List<ScoreItem> buildScoreList(ExamStudent student) {
-        List<ScoreItem> scoreList = new LinkedList<ScoreItem>();
-
-        List<ExamQuestion> oList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
-                student.getSubjectCode(), true);
-        List<ScoreItem> list1 = student.getScoreList(true);
-        int index = 0;
-        for (ExamQuestion question : oList) {
-            try {
-                ScoreItem item = list1.get(index);
-                item.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
-                item.setMainNumber(question.getMainNumber());
-                item.setSubNumber(question.getSubNumber());
-            } catch (Exception e) {
-            }
-            index++;
-        }
-        scoreList.addAll(list1);
-
-        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
-                student.getSubjectCode(), false);
-        List<ScoreItem> list2 = student.getScoreList(false);
-        index = 0;
-        for (ExamQuestion question : sList) {
-            try {
-                ScoreItem item = list2.get(index);
-                item.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
-                item.setMainNumber(question.getMainNumber());
-                item.setSubNumber(question.getSubNumber());
-            } catch (Exception e) {
-            }
-            index++;
-        }
-        scoreList.addAll(list2);
-
-        return scoreList;
-    }
-
-    private void buildSheetUrl(ExamStudent student) {
-        Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
-        student.setSheetUrls(PictureUrlBuilder.getSheetUrls(student.getExamId(), campus.getId(),
-                student.getSubjectCode(), student.getExamNumber(), student.getSheetCount()));
-    }
-
-    private void buildPackageUrl(ExamStudent student) {
-        if (StringUtils.isNotBlank(student.getPackageCode())) {
-            ExamPackage ep = packageService.find(student.getExamId(), student.getPackageCode());
-            if (ep != null && ep.getPicCount() > 0) {
-                student.setPackageUrls(PictureUrlBuilder.getPackageUrls(student.getExamId(), student.getPackageCode(),
-                        ep.getPicCount()));
-            }
-        }
-    }
-
-    private void buildAnswerUrl(ExamStudent student) {
-        ExamSubject subject = subjectService.find(student.getExamId(), student.getSubjectCode());
-        if (subject != null && subject.getHasAnswer().booleanValue()) {
-            student.setAnswerUrl(subject.getAnswerUrl());
-        }
-    }
-    
-    public String getStudentIdsByMarkLoginAndMarkName(String markLogin,String markName){
-        String ids = "";
-        if(StringUtils.isNotBlank(markLogin)){
-            ids += studentService.findIdsByMarkLogin(markLogin);
-            if(ids.equals("")){
-                ids = "-1";
-            }
-        }
-        if(StringUtils.isNotBlank(markName)){
-            String s =  studentService.findIdsByMarkName(markName);
-            if(StringUtils.isBlank(ids)){
-                if(s.equals("")){
-                    ids = "-1";
-                }else{
-                    ids += s;
-                }
-            }else{
-                if(!ids.equals("-1")){
-                    if(StringUtils.isBlank(s)){
-                        ids = "-1";
-                    }
-                }
-            }
-        }
-        return ids;
-    }
-}
+package cn.com.qmth.stmms.admin.exam;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.admin.dto.ExamStudentDTO;
+import cn.com.qmth.stmms.admin.dto.ScoreEditDTO;
+import cn.com.qmth.stmms.admin.dto.ScoreExportDTO;
+import cn.com.qmth.stmms.admin.thread.ScoreCalculateThread;
+import cn.com.qmth.stmms.biz.campus.model.Campus;
+import cn.com.qmth.stmms.biz.campus.service.CampusService;
+import cn.com.qmth.stmms.biz.exam.model.ExamPackage;
+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.Marker;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.ExamPackageService;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.exam.service.ScoreRateService;
+import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.biz.utils.ScoreItem;
+import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.Role;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+import net.sf.json.JSONObject;
+
+@Controller
+@RequestMapping("/admin/exam/score")
+public class ScoreController extends BaseExamController {
+
+    @Autowired
+    private CampusService campusService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private ExamPackageService packageService;
+
+    @Autowired
+    private MarkService markService;
+
+    @Autowired
+    private MarkLibraryService markLibraryService;
+
+    @Autowired
+    private ScoreRateService scoreRateService;
+
+    @Value("${sheet.image.server}")
+    private String imageServer;
+
+    @Value("${package.image.server}")
+    private String packageServer;
+
+    @Value("${card.server}")
+    private String cardServer;
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private AsyncTaskExecutor taskExecutor;
+
+    @Autowired
+    private LockService lockService;
+
+    @RequestMapping
+    public ModelAndView list(HttpServletRequest request, ExamStudentSearchQuery query,
+            @RequestParam(defaultValue = "0") Integer filter) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        int examId = getSessionExamId(request);
+        String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
+        ModelAndView view = new ModelAndView("modules/exam/scoreList");
+        if (subjectCode != null) {
+            query.setSubjectCode(subjectCode);
+        }
+        query.setExamId(examId);
+        // query.setUpload(true);
+        // query.setAbsent(false);
+        query.orderByExamNumber();
+        if (filter == 1) {
+            query.setObjectiveScore(0d);
+            query.setSubjectiveScoreGt(0d);
+        } else if (filter == 2) {
+            query.setObjectiveScoreGt(0d);
+            query.setSubjectiveScore(0d);
+        }
+        String ids = getStudentIdsByMarkLoginAndMarkName(query.getMarkLogin(), query.getMarkName());
+        if (StringUtils.isNotBlank(ids)) {
+            query.setIds(ids);
+        }
+        subjectFilter(query, wu);
+        query = studentService.findByQuery(query);
+        for (ExamStudent student : query.getResult()) {
+            buildSheetUrl(student);
+            buildPackageUrl(student);
+            buildAnswerUrl(student);
+            student.setSubjectRemark(subjectService.find(student.getExamId(), student.getSubjectCode()).getRemark());
+            List<Marker> markers = markerService.findByStudentId(student.getId());
+            if (!markers.isEmpty()) {
+                String loginNames = "";
+                String names = "";
+                for (Marker marker : markers) {
+                    loginNames = loginNames.equals("") ? marker.getLoginName()
+                            : loginNames + "," + marker.getLoginName();
+                    names = names.equals("") ? marker.getName() : names + "," + marker.getName();
+                }
+                student.setMarkLogin(loginNames);
+                student.setMarkName(names);
+            }
+            List<MarkLibrary> markLibrarys = markLibraryService.findByStudentId(student.getId());
+            if (!markLibrarys.isEmpty()) {
+                Set<String> tags = new HashSet<String>();
+                for (MarkLibrary markLibrary : markLibrarys) {
+                    if (markLibrary.getTags() != null) {
+                        String[] tag = markLibrary.getTags().split(",");
+                        for (String s : tag) {
+                            tags.add(s);
+                        }
+                    }
+                    if (markLibrary.getHeaderId() != null) {
+                        User user = userService.findById(markLibrary.getHeaderId());
+                        student.setMarkLogin(user.getLoginName());
+                        student.setMarkName(user.getName());
+                    }
+                }
+                if (!tags.isEmpty()) {
+                    student.setTagValue(tags.toString());
+                }
+            }
+        }
+        view.addObject("query", query);
+        view.addObject("filter", filter);
+        view.addObject("subjectList", getExamSubject(examId, wu));
+        view.addObject("running", lockService.isLocked(LockType.EXAM, examId));
+        view.addObject("imageServer", imageServer);
+        view.addObject("packageServer", packageServer);
+        view.addObject("cardServer", cardServer);
+        return view;
+    }
+
+    @RequestMapping("/detail")
+    public ModelAndView detail(HttpServletRequest request, @RequestParam String examNumber,
+            @RequestParam String queryString) {
+        int examId = getSessionExamId(request);
+        ExamStudent student = studentService.findByExamIdAndExamNumber(examId, examNumber);
+        if (student != null) {
+            buildSheetUrl(student);
+            buildPackageUrl(student);
+            buildAnswerUrl(student);
+            ModelAndView view = new ModelAndView("modules/exam/scoreDetail");
+            view.addObject("student", student);
+            view.addObject("scoreList", buildScoreList(student));
+            view.addObject("imageServer", imageServer);
+            view.addObject("packageServer", packageServer);
+            view.addObject("cardServer", cardServer);
+            view.addObject("queryString", StringUtils.trimToEmpty(queryString));
+            return view;
+        } else {
+            return new ModelAndView("redirect:/admin/exam/score");
+        }
+    }
+
+    @RequestMapping("/edit")
+    @ResponseBody
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public JSONObject edit(HttpServletRequest request, ScoreEditDTO dto) {
+        JSONObject result = new JSONObject();
+        int examId = getSessionExamId(request);
+        ExamStudent student = studentService.findByExamIdAndExamNumber(examId, dto.getExamNumber());
+        if (student != null) {
+            double oScore = 0d;
+            List<ScoreItem> oList = dto.getObjectiveScoreItems();
+            for (ScoreItem item : oList) {
+                oScore += item.getScore();
+            }
+            student.setObjectiveScore(oScore);
+            student.setScoreList(oList, true);
+
+            double sScore = 0d;
+            List<ScoreItem> sList = dto.getSubjectiveScoreItems();
+            for (ScoreItem item : sList) {
+                sScore += item.getScore();
+            }
+            student.setSubjectiveScore(sScore);
+            student.setScoreList(sList, false);
+
+            studentService.save(student);
+            result.accumulate("success", true);
+        } else {
+            result.accumulate("success", false);
+            result.accumulate("message", "找不到对应的考生");
+        }
+        return result;
+    }
+
+    @RequestMapping("/calculate")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public ModelAndView calculate(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        if (lockService.trylock(LockType.EXAM, examId)) {
+            ScoreCalculateThread thread = new ScoreCalculateThread(examId, lockService, studentService, questionService,
+                    subjectService, scoreRateService, markLibraryService, markService);
+            taskExecutor.submit(thread);
+        }
+        return new ModelAndView("redirect:/admin/exam/score");
+    }
+
+    @RequestMapping("/export")
+    public String export(ExamStudentSearchQuery query, HttpServletRequest request, HttpServletResponse response,
+            RedirectAttributes redirectAttributes) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        int examId = getSessionExamId(request);
+        String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
+        query.setExamId(examId);
+        // query.setUpload(true);
+        // query.setAbsent(false);
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        subjectFilter(query, wu);
+        String ids = getStudentIdsByMarkLoginAndMarkName(query.getMarkLogin(), query.getMarkName());
+        if (StringUtils.isNotBlank(ids)) {
+            query.setIds(ids);
+        }
+        query = studentService.findByQuery(query);
+        String fileName = "成绩单.xlsx";
+        try {
+            if (subjectCode != null) {
+                fileName = subjectService.find(examId, subjectCode).getName() + "-机阅考试成绩表";
+                List<ScoreExportDTO> list = new LinkedList<ScoreExportDTO>();
+                for (ExamStudent student : query.getResult()) {
+                    if (student.isBreach() || student.isAbsent()) {
+                        student.setObjectiveScore(0d);
+                        student.setSubjectiveScore(0d);
+                    }
+                    list.add(new ScoreExportDTO(student));
+                }
+                new ExportExcel(subjectCode + "-" + subjectService.find(examId, subjectCode).getName(),
+                        ScoreExportDTO.class).setDataList(list).write(response, fileName + ".xlsx").dispose();
+                return null;
+            }
+            List<ExamStudentDTO> list = new LinkedList<ExamStudentDTO>();
+            for (ExamStudent student : query.getResult()) {
+                if (student.isBreach() || student.isAbsent()) {
+                    student.setObjectiveScore(0d);
+                    student.setSubjectiveScore(0d);
+                }
+                list.add(new ExamStudentDTO(student));
+            }
+            new ExportExcel("成绩单", ExamStudentDTO.class).setDataList(list).write(response, fileName).dispose();
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "导出成绩失败!" + e.getMessage());
+            return "redirect:/admin/exam/score";
+        }
+    }
+
+    private List<ScoreItem> buildScoreList(ExamStudent student) {
+        List<ScoreItem> scoreList = new LinkedList<ScoreItem>();
+
+        List<ExamQuestion> oList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
+                student.getSubjectCode(), true);
+        List<ScoreItem> list1 = student.getScoreList(true);
+        int index = 0;
+        for (ExamQuestion question : oList) {
+            try {
+                ScoreItem item = list1.get(index);
+                item.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
+                item.setMainNumber(question.getMainNumber());
+                item.setSubNumber(question.getSubNumber());
+            } catch (Exception e) {
+            }
+            index++;
+        }
+        scoreList.addAll(list1);
+
+        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
+                student.getSubjectCode(), false);
+        List<ScoreItem> list2 = student.getScoreList(false);
+        index = 0;
+        for (ExamQuestion question : sList) {
+            try {
+                ScoreItem item = list2.get(index);
+                item.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
+                item.setMainNumber(question.getMainNumber());
+                item.setSubNumber(question.getSubNumber());
+            } catch (Exception e) {
+            }
+            index++;
+        }
+        scoreList.addAll(list2);
+
+        return scoreList;
+    }
+
+    private void buildSheetUrl(ExamStudent student) {
+        Campus campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
+        student.setSheetUrls(PictureUrlBuilder.getSheetUrls(student.getExamId(), campus.getId(),
+                student.getSubjectCode(), student.getExamNumber(), student.getSheetCount()));
+    }
+
+    private void buildPackageUrl(ExamStudent student) {
+        if (StringUtils.isNotBlank(student.getPackageCode())) {
+            ExamPackage ep = packageService.find(student.getExamId(), student.getPackageCode());
+            if (ep != null && ep.getPicCount() > 0) {
+                student.setPackageUrls(PictureUrlBuilder.getPackageUrls(student.getExamId(), student.getPackageCode(),
+                        ep.getPicCount()));
+            }
+        }
+    }
+
+    private void buildAnswerUrl(ExamStudent student) {
+        ExamSubject subject = subjectService.find(student.getExamId(), student.getSubjectCode());
+        if (subject != null && subject.getHasAnswer().booleanValue()) {
+            student.setAnswerUrl(subject.getAnswerUrl());
+        }
+    }
+
+    public String getStudentIdsByMarkLoginAndMarkName(String markLogin, String markName) {
+        String ids = "";
+        if (StringUtils.isNotBlank(markLogin)) {
+            ids += studentService.findIdsByMarkLogin(markLogin);
+            if (ids.equals("")) {
+                ids = "-1";
+            }
+        }
+        if (StringUtils.isNotBlank(markName)) {
+            String s = studentService.findIdsByMarkName(markName);
+            if (StringUtils.isBlank(ids)) {
+                if (s.equals("")) {
+                    ids = "-1";
+                } else {
+                    ids += s;
+                }
+            } else {
+                if (!ids.equals("-1")) {
+                    if (StringUtils.isBlank(s)) {
+                        ids = "-1";
+                    }
+                }
+            }
+        }
+        return ids;
+    }
+}

+ 13 - 6
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java

@@ -7,7 +7,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
@@ -21,11 +20,14 @@ import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 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.ScoreRateService;
+import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
+import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.utils.ScoreCalculateUtil;
 import cn.com.qmth.stmms.biz.utils.ScoreInfo;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
+import cn.com.qmth.stmms.common.enums.LockType;
 
 public class ScoreCalculateThread implements Runnable {
 
@@ -37,13 +39,15 @@ public class ScoreCalculateThread implements Runnable {
 
     private ExamSubjectService subjectService;
 
+    private MarkService markService;
+
     private ScoreRateService scoreRateService;
 
     private MarkLibraryService libraryService;
 
     private int examId;
 
-    private AtomicBoolean running;
+    private LockService lockService;
 
     private Map<String, List<ExamQuestion>> objectiveMap;
 
@@ -53,16 +57,17 @@ public class ScoreCalculateThread implements Runnable {
 
     private Map<String, Double> scoreRateMap;
 
-    public ScoreCalculateThread(int examId, AtomicBoolean running, ExamStudentService studentService,
+    public ScoreCalculateThread(int examId, LockService lockService, ExamStudentService studentService,
             ExamQuestionService questionService, ExamSubjectService subjectService, ScoreRateService scoreRateService,
-            MarkLibraryService libraryService) {
+            MarkLibraryService libraryService, MarkService markService) {
         this.examId = examId;
-        this.running = running;
+        this.lockService = lockService;
         this.studentService = studentService;
         this.questionService = questionService;
         this.subjectService = subjectService;
         this.scoreRateService = scoreRateService;
         this.libraryService = libraryService;
+        this.markService = markService;
         this.objectiveMap = new HashMap<String, List<ExamQuestion>>();
         this.subjectiveMap = new HashMap<String, List<ExamQuestion>>();
         this.scoreRateMap = new HashMap<String, Double>();
@@ -96,7 +101,7 @@ public class ScoreCalculateThread implements Runnable {
         } catch (Exception e) {
             log.error("calculate exception for examId=" + examId, e);
         } finally {
-            running.set(false);
+            lockService.unlock(LockType.EXAM, examId);
             log.info("finish calculate for examId=" + examId);
         }
     }
@@ -149,6 +154,8 @@ public class ScoreCalculateThread implements Runnable {
         student.setScoreList(info.getScoreList(), true);
 
         studentService.save(student);
+        // 增加主观题总分统计
+        markService.scoreCalculate(student.getExamId(), student.getSubjectCode(), student.getId());
 
         try {
             if (!student.getScoreList(false).isEmpty()) {// 主观题有分数明细才统分

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

@@ -37,7 +37,6 @@ import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.common.controller.BaseController;
 import cn.com.qmth.stmms.common.enums.ExamSubjectStatus;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
-import cn.com.qmth.stmms.common.session.model.StmmsSession;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
@@ -97,33 +96,11 @@ public class MarkController extends BaseController {
         return new ModelAndView("redirect:/mark/index");
     }
 
-    @RequestMapping(value = "/subject-select", method = RequestMethod.GET)
-    public ModelAndView select(HttpServletRequest request) {
-        Marker marker = RequestUtils.getWebUser(request).getMarker();
-        ModelAndView modelAndView = new ModelAndView("modules/mark/subjectSelect");
-        modelAndView.addObject("marker", marker);
-        modelAndView.addObject("subjectList", subjectService.list(marker.getExamId(), ExamSubjectStatus.MARKING, 0));
-
-        if (StringUtils.isNotBlank(marker.getSubjectCode())) {
-            markService.releaseByMarker(marker);
-        }
-        return modelAndView;
-    }
-
-    @RequestMapping(value = "/subject-select", method = RequestMethod.POST)
-    public ModelAndView select(HttpServletRequest request, @RequestParam String subjectCode) {
-        Marker current = RequestUtils.getWebUser(request).getMarker();
-        current.setSubjectCode(subjectCode);
-        markerService.save(current);
-        return new ModelAndView("redirect:/mark/index");
-    }
-
     @RequestMapping("/index")
     public ModelAndView index(HttpServletRequest request, @RequestParam(value = "mode", required = false) String mode) {
-        StmmsSession session = RequestUtils.getSession(request);
         Marker marker = RequestUtils.getWebUser(request).getMarker();
         ModelAndView modelAndView = getMarkModeView(marker, mode);
-        preProcess(marker, session, modelAndView);
+        preProcess(marker, modelAndView);
         return modelAndView;
     }
 
@@ -157,7 +134,7 @@ public class MarkController extends BaseController {
      * @param modelAndView
      * @param modelAndView
      */
-    private void preProcess(Marker marker, StmmsSession session, ModelAndView modelAndView) {
+    private void preProcess(Marker marker, ModelAndView modelAndView) {
         modelAndView.addObject("sliceServer", sliceServer);
         modelAndView.addObject("sheetServer", sheetServer);
         modelAndView.addObject("cardServer", cardServer);
@@ -369,6 +346,9 @@ public class MarkController extends BaseController {
         if (library.getStatus() != LibraryStatus.WAITING && library.getStatus() != LibraryStatus.MARKED) {
             return false;
         }
+        if (library.getStatus() == LibraryStatus.WAITING && !markService.hasApplied(library, marker)) {
+            return false;
+        }
         return true;
     }
 

+ 56 - 60
stmms-web/src/main/java/cn/com/qmth/stmms/mark/interceptor/MarkInterceptor.java

@@ -1,60 +1,56 @@
-package cn.com.qmth.stmms.mark.interceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-
-import cn.com.qmth.stmms.biz.exam.model.Marker;
-import cn.com.qmth.stmms.biz.exam.service.MarkerService;
-import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.interceptor.CommonInterceptor;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-/**
- * SubjectHeader模块操作拦截器
- * 
- * @author XKJ
- * 
- */
-public class MarkInterceptor extends CommonInterceptor {
-
-    @Autowired
-    private MarkerService markerService;
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
-        String uri = RequestUtils.getURI(request);
-        Marker marker = buildMarker(RequestUtils.getWebUser(request));
-        if (marker != null) {
-            if (uri.endsWith("mark/reset") || uri.endsWith("mark/subject-select")) {
-                return true;
-            } else if (marker.getLastLoginTime() == null) {
-                // 首次登录,强制重置密码及姓名
-                return redirect(request, response, "/mark/reset");
-            } else if (StringUtils.isBlank(marker.getSubjectCode())) {
-                // 通用帐号未选择科目,首先选择强制选择评卷科目
-                return redirect(request, response, "/mark/subject-select");
-            }
-            return true;
-        }
-        return sessionExpire(request, response, "/mark-login");
-    }
-
-    private Marker buildMarker(WebUser wu) {
-        Marker marker = null;
-        if (wu != null) {
-            marker = wu.getMarker();
-            if (marker == null) {
-                marker = markerService.findById(wu.getId());
-                if (marker != null) {
-                    wu.setDataObject(marker);
-                    wu.setName(marker.getName());
-                }
-            }
-        }
-        return marker;
-    }
-}
+package cn.com.qmth.stmms.mark.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.interceptor.CommonInterceptor;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+/**
+ * SubjectHeader模块操作拦截器
+ * 
+ * @author XKJ
+ * 
+ */
+public class MarkInterceptor extends CommonInterceptor {
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+            throws Exception {
+        String uri = RequestUtils.getURI(request);
+        Marker marker = buildMarker(RequestUtils.getWebUser(request));
+        if (marker != null) {
+            if (uri.endsWith("mark/reset")) {
+                return true;
+            } else if (marker.getLastLoginTime() == null) {
+                // 首次登录,强制重置密码及姓名
+                return redirect(request, response, "/mark/reset");
+            }
+            return true;
+        }
+        return sessionExpire(request, response, "/mark-login");
+    }
+
+    private Marker buildMarker(WebUser wu) {
+        Marker marker = null;
+        if (wu != null) {
+            marker = wu.getMarker();
+            if (marker == null) {
+                marker = markerService.findById(wu.getId());
+                if (marker != null) {
+                    wu.setDataObject(marker);
+                    wu.setName(marker.getName());
+                }
+            }
+        }
+        return marker;
+    }
+}