瀏覽代碼

Merge branch 'stmms_ft_dev' of http://git.qmth.com.cn/luoshi/stmms-ft.git into stmms_ft_dev

ting.yin 6 年之前
父節點
當前提交
d1a8f8ee86
共有 24 個文件被更改,包括 1332 次插入1042 次删除
  1. 6 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/MarkGroupDao.java
  2. 6 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java
  3. 26 16
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/MarkGroupServiceImpl.java
  4. 16 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockProvider.java
  5. 59 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/LockService.java
  6. 108 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/lock/impl/MemoryLockProvider.java
  7. 22 23
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkLibraryDao.java
  8. 5 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java
  9. 39 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkLockService.java
  10. 127 93
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  11. 9 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  12. 25 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/CurrentTaskUtil.java
  13. 17 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/LockType.java
  14. 1 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ArbitrateController.java
  15. 2 7
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkController.java
  16. 260 255
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java
  17. 383 376
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ScoreController.java
  18. 0 30
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TagController.java
  19. 13 6
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/ScoreCalculateThread.java
  20. 25 26
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  21. 56 60
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/interceptor/MarkInterceptor.java
  22. 0 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupList.jsp
  23. 126 138
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/tagInfo.jsp
  24. 1 1
      stmms-web/src/main/webapp/static/mark-new/js/modules/specialTag.js

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

@@ -43,6 +43,12 @@ public interface MarkGroupDao
     @Query("select count(q) from MarkGroup q where q.pk.examId=?1")
     long countByExamId(int examId);
 
+    @Query("select sum(g.libraryCount) from MarkGroup g where g.pk.examId=?1")
+    Integer sumLibraryCount(Integer examId);
+
+    @Query("select sum(g.markedCount) from MarkGroup g where g.pk.examId=?1")
+    Integer sumMarkedCount(Integer examId);
+
     @Modifying
     @Query("update MarkGroup g set g.libraryCount=(select count(l) from MarkLibrary l "
             + "where l.examId=?1 and l.subjectCode=?2 and l.groupNumber=g.pk.number) "

+ 6 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/MarkGroupService.java

@@ -30,10 +30,12 @@ public interface MarkGroupService {
 
     void resetBuildTime(int examId, String subjectCode);
 
-	void updateDoubleRate(int examId, String subjectCode, Integer number,
-			Double doubleRate);
+    void updateDoubleRate(int examId, String subjectCode, Integer number, Double doubleRate);
 
-	void updateArbitrateThreshold(int examId, String subjectCode,
-			Integer number, Double arbitrateThreshold);
+    void updateArbitrateThreshold(int examId, String subjectCode, Integer number, Double arbitrateThreshold);
+
+    long sumLibraryCount(Integer examId);
+
+    long sumMarkedCount(Integer examId);
 
 }

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

@@ -93,21 +93,31 @@ public class MarkGroupServiceImpl extends BaseQueryService<MarkGroup> implements
         return groupDao.countByExamId(examId);
     }
 
-	@Override
-	public void updateDoubleRate(int examId, String subjectCode,
-			Integer number, Double doubleRate) {
-		MarkGroup group  = findOne(examId, subjectCode, number); 
-		group.setDoubleRate(doubleRate);
-		save(group);
-	}
-
-	@Override
-	public void updateArbitrateThreshold(int examId, String subjectCode,
-			Integer number, Double arbitrateThreshold) {
-		MarkGroup group  = findOne(examId, subjectCode, number); 
-		group.setArbitrateThreshold(arbitrateThreshold);
-		save(group);
-		
-	}
+    @Override
+    public long sumLibraryCount(Integer examId) {
+        Integer count = groupDao.sumLibraryCount(examId);
+        return count != null ? count : 0;
+    }
+
+    @Override
+    public long sumMarkedCount(Integer examId) {
+        Integer count = groupDao.sumMarkedCount(examId);
+        return count != null ? count : 0;
+    }
+
+    @Override
+    public void updateDoubleRate(int examId, String subjectCode, Integer number, Double doubleRate) {
+        MarkGroup group = findOne(examId, subjectCode, number);
+        group.setDoubleRate(doubleRate);
+        save(group);
+    }
+
+    @Override
+    public void updateArbitrateThreshold(int examId, String subjectCode, Integer number, Double arbitrateThreshold) {
+        MarkGroup group = findOne(examId, subjectCode, number);
+        group.setArbitrateThreshold(arbitrateThreshold);
+        save(group);
+
+    }
 
 }

+ 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 = new HashMap<>();
+
+    @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();
+            }
+        }
+    }
+
+}

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

@@ -4,9 +4,12 @@ import java.util.Date;
 import java.util.List;
 import java.util.Set;
 
+import javax.persistence.LockModeType;
+
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Lock;
 import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 
@@ -15,33 +18,35 @@ import cn.com.qmth.stmms.common.enums.LibraryStatus;
 
 public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, JpaSpecificationExecutor<MarkLibrary> {
 
-    List<MarkLibrary> findByExamId(Integer examId, Pageable page);
+    @Lock(LockModeType.PESSIMISTIC_WRITE)
+    @Query("select l from MarkLibrary l where l.id=?1")
+    MarkLibrary findByIdForLock(Integer id);
 
     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);
 
     @Query("select count(*) from MarkLibrary f where f.studentId=?1 and f.markerId=?2")
     long countByStudentIdAndMarkerId(Integer studentId, Integer markerId);
 
+    @Query("select count(*) from MarkLibrary f where f.studentId=?1 and f.markerId=?2 and f.id!=?3")
+    long countByStudentIdAndMarkerIdAndIdNotEqual(Integer studentId, Integer markerId, Integer id);
+
     @Query("select distinct l.campusId from MarkLibrary l where l.examId=?1 and l.tags is not null")
     List<Integer> findTagCampusId(Integer examId);
 
@@ -50,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 where m.markerId=?1 and m.headerId!=null ")
@@ -60,25 +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 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);
@@ -91,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);
 
@@ -100,4 +93,10 @@ 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))")
+    int 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);
         }

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

@@ -27,6 +27,8 @@ public class MarkLockService {
 
     private Map<Object, AtomicBoolean> studentMap = new HashMap<>();
 
+    private Map<Object, AtomicBoolean> markerMap = new HashMap<>();
+
     /**
      * 对评卷分组进行重置/删除操作前,先尝试锁定
      * 
@@ -99,9 +101,46 @@ public class MarkLockService {
         }
     }
 
+    /**
+     * 评卷员进行评卷操作前,先尝试锁定
+     * 
+     * @param markerId
+     */
+    public void lockMarker(Integer markerId) {
+        AtomicBoolean lock = getLock(markerMap, markerId);
+        while (!lock.get()) {
+            lock.compareAndSet(false, true);
+        }
+    }
+
+    /**
+     * 评卷员进行评卷操作后,释放锁定
+     * 
+     * @param markerId
+     */
+    public void unlockMarker(Integer markerId) {
+        AtomicBoolean lock = getLock(markerMap, markerId);
+        while (lock.get()) {
+            lock.compareAndSet(true, false);
+        }
+    }
+
+    /**
+     * 等待评卷员释放锁定
+     * 
+     * @param studentId
+     */
+    public void waitUnlockMarker(Integer markerId) {
+        AtomicBoolean lock = getLock(markerMap, markerId);
+        while (lock.get()) {
+            ;
+        }
+    }
+
     public void clear() {
         clearLock(groupMap);
         clearLock(studentMap);
+        clearLock(markerMap);
     }
 
     private void clearLock(Map<Object, AtomicBoolean> map) {

+ 127 - 93
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;
@@ -194,7 +195,7 @@ public class MarkServiceImpl implements MarkService {
             subjectService.updateScore(group.getExamId(), group.getSubjectCode());
             resetGroup(group);
         } catch (Exception e) {
-        	e.printStackTrace();
+            e.printStackTrace();
             throw e;
         } finally {
             lockService.unlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
@@ -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));
+    }
+
     /**
      * 释放某个评卷员某个锁定任务
      * 
@@ -299,57 +313,76 @@ public class MarkServiceImpl implements MarkService {
         lockService.waitUnlockGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
         // 等待考生释放锁定
         lockService.waitUnlockStudent(library.getStudentId());
-        // 若该评卷任务已被删除,则直接返回
-        if (!libraryDao.exists(library.getId())) {
-            return;
-        }
-        // 保存阅卷轨迹
-        if (trackList != null) {
-            trackDao.deleteByLibraryId(library.getId());
-            trackDao.save(trackList);
-        }
-        // 保存特殊标记
-        if (tagList != null) {
-            specialTagDao.deleteByLibraryId(library.getId());
-            specialTagDao.save(tagList);
-        }
 
-        ArbitrateHistory history = null;
-        if (group.getArbitrateThreshold() != null && group.getArbitrateThreshold() > 0) {
-            // 多评模式
-            List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(library.getStudentId(),
-                    library.getGroupNumber());
-            for (MarkLibrary other : list) {
-                if (other.getId().equals(library.getId()) || other.getStatus() != LibraryStatus.MARKED
-                        || other.getMarkerScore() == null || other.getHeaderScore() != null) {
-                    // 未评卷或组长已打分,则跳过该任务
-                    continue;
-                }
-                if (Math.abs(other.getMarkerScore() - library.getMarkerScore()) > group.getArbitrateThreshold()) {
-                    // 分差超过阀值,触发仲裁
-                    history = new ArbitrateHistory();
-                    history.setExamId(library.getExamId());
-                    history.setSubjectCode(library.getSubjectCode());
-                    history.setGroupNumber(library.getGroupNumber());
-                    history.setStudentId(library.getStudentId());
-                    history.setExamNumber(library.getExamNumber());
-                    history.setStatus(HistoryStatus.WAITING);
-                    history.setCreateTime(new Date());
-                    arbitrateDao.save(history);
-
-                    library.setStatus(LibraryStatus.WAIT_ARBITRATE);
-                    break;
+        try {
+            Date now = new Date();
+            // 尝试锁定评卷员
+            lockService.lockMarker(library.getMarkerId());
+            // 校验总分是否超标
+            if (library.getMarkerScore() > group.getTotalScore() || StringUtils.isBlank(library.getMarkerScoreList())) {
+                return;
+            }
+            // 是否多评情况下已处理过该考生评卷任务
+            if (libraryDao.countByStudentIdAndMarkerIdAndIdNotEqual(library.getStudentId(), library.getMarkerId(),
+                    library.getId()) > 0) {
+                return;
+            }
+            // 尝试提交评卷结果
+            if (libraryDao.updateMarkerResult(library.getId(), library.getMarkerId(), library.getMarkerScore(),
+                    library.getMarkerScoreList(), now, library.getTags(), LibraryStatus.MARKED,
+                    LibraryStatus.WAITING) == 0) {
+                // 条件不符更新失败,直接返回
+                return;
+            }
+            // 保存阅卷轨迹
+            if (trackList != null) {
+                trackDao.deleteByLibraryId(library.getId());
+                trackDao.save(trackList);
+            }
+            // 保存特殊标记
+            if (tagList != null) {
+                specialTagDao.deleteByLibraryId(library.getId());
+                specialTagDao.save(tagList);
+            }
+            // 判断多评模式下是否需要仲裁
+            ArbitrateHistory history = null;
+            if (group.getArbitrateThreshold() != null && group.getArbitrateThreshold() > 0) {
+                // 多评模式
+                List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumberAndStatus(library.getStudentId(),
+                        library.getGroupNumber(), LibraryStatus.MARKED);
+                for (MarkLibrary other : list) {
+                    if (other.getId().equals(library.getId()) || other.getHeaderScore() != null) {
+                        // 本评卷任务或组长已打分,则跳过该任务
+                        continue;
+                    }
+                    if (Math.abs(other.getMarkerScore() - library.getMarkerScore()) > group.getArbitrateThreshold()) {
+                        // 分差超过阀值,触发仲裁
+                        history = new ArbitrateHistory();
+                        history.setExamId(library.getExamId());
+                        history.setSubjectCode(library.getSubjectCode());
+                        history.setGroupNumber(library.getGroupNumber());
+                        history.setStudentId(library.getStudentId());
+                        history.setExamNumber(library.getExamNumber());
+                        history.setStatus(HistoryStatus.WAITING);
+                        history.setCreateTime(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) {
-            // 评卷正常完成才尝试统分
-            scoreCalculate(library.getExamId(), library.getSubjectCode(), library.getStudentId());
+            if (history != null) {
+                // 触发仲裁后续状态更新
+                libraryDao.updateByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber(),
+                        LibraryStatus.WAIT_ARBITRATE);
+            } else {
+                // 评卷正常完成才尝试统分
+                // scoreCalculate(library.getExamId(), library.getSubjectCode(),
+                // library.getStudentId());
+            }
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            lockService.unlockMarker(library.getMarkerId());
         }
     }
 
@@ -433,48 +466,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;
             }
         }
         // 取平均分策略下,累计分数需要重新计算一次
@@ -582,8 +616,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;
+    }
+
+}

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

@@ -317,7 +317,7 @@ public class ArbitrateController extends BaseExamController {
 
             status.accumulate("totalCount", totalCount);
             status.accumulate("markedCount", markedCount);
-            status.accumulate("valid", totalCount > 0);
+            status.accumulate("valid", (totalCount + markedCount) > 0);
         } else {
             status.accumulate("valid", false);
         }

+ 2 - 7
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/MarkController.java

@@ -26,9 +26,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.query.ExamSubjectSearchQuery;
-import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
 import cn.com.qmth.stmms.common.domain.WebUser;
-import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.utils.ExportExcel;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
 
@@ -41,9 +39,6 @@ public class MarkController extends BaseExamController {
     @Autowired
     private ExamSubjectService subjectService;
 
-    @Autowired
-    private MarkLibraryService libraryService;
-
     @Autowired
     private MarkerService markerService;
 
@@ -76,8 +71,8 @@ public class MarkController extends BaseExamController {
             list.add(vo);
         }
 
-        double total = libraryService.countByExamAndSubjectAndGroupAndStatus(examId, null, 0, null);
-        double finish = libraryService.countByExamAndSubjectAndGroupAndStatus(examId, null, 0, LibraryStatus.MARKED);
+        double total = (double) groupService.sumLibraryCount(examId);
+        double finish = (double) groupService.sumMarkedCount(examId);
         double percent = total > 0 ? (finish * 100 / total) : 0;
         model.addAttribute("percent", new DecimalFormat("###.#").format(percent));
         model.addAttribute("resultList", list);

+ 260 - 255
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/PaperController.java

@@ -1,255 +1,260 @@
-package cn.com.qmth.stmms.admin.exam;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-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.data.domain.Sort;
-import org.springframework.data.domain.Sort.Direction;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.mvc.support.RedirectAttributes;
-
-import cn.com.qmth.stmms.admin.dto.ObjectiveQuestionDTO;
-import cn.com.qmth.stmms.admin.dto.QuestionDTO;
-import cn.com.qmth.stmms.admin.dto.SubjectQuestionDTO;
-import cn.com.qmth.stmms.admin.dto.SubjectiveQuestionDTO;
-import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
-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.ExamQuestionService;
-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.query.ExamQuestionSearchQuery;
-import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
-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.ImportExcel;
-import cn.com.qmth.stmms.common.utils.RequestUtils;
-
-@Controller("examPaperController")
-@RequestMapping("/admin/exam/paper")
-public class PaperController extends BaseExamController {
-
-    @Autowired
-    private ExamSubjectService subjectService;
-
-    @Autowired
-    private ExamQuestionService questionService;
-
-    @Autowired
-    private MarkGroupService groupService;
-
-    @Value("${card.server}")
-    private String cardServer;
-
-    @RequestMapping
-    public String list(Model model, HttpServletRequest request, ExamSubjectSearchQuery query,
-            @RequestParam(required = false) Boolean upload) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        int examId = getSessionExamId(request);
-        query.setExamId(examId);
-        if (upload != null) {
-            if (upload.booleanValue()) {
-                query.setUploadCountGt(0);
-            } else {
-                query.setUploadCountEqual(0);
-            }
-        }
-        subjectFilter(query, wu);
-        query = subjectService.findByQuery(query);
-        model.addAttribute("query", query);
-        model.addAttribute("upload", upload);
-        model.addAttribute("subjectList", getExamSubject(examId, wu));
-        model.addAttribute("levelList", subjectService.listLevel(examId));
-        model.addAttribute("categoryList", subjectService.listCategory(examId));
-        model.addAttribute("cardServer", cardServer);
-        return "modules/exam/paperList";
-    }
-
-    @RequestMapping("/detail")
-    public String detail(Model model, HttpServletRequest request, @RequestParam String subjectCode,
-            @RequestParam(required = false) Boolean objective) {
-        WebUser wu = RequestUtils.getWebUser(request);
-        int examId = getSessionExamId(request);
-        if (wu.isSubjectHeader() && !subjectCode.equals(wu.getUser().getSubjectCode())) {
-            return "redirect:/admin/exam/paper";
-        }
-        ExamSubject subject = subjectService.find(examId, subjectCode);
-        if (subject != null) {
-            List<ExamQuestion> list = new LinkedList<ExamQuestion>();
-            if (objective == null || objective.booleanValue() == true) {
-                list.addAll(questionService.findByExamAndSubjectAndObjective(examId, subjectCode, true));
-            }
-            if (objective == null || objective.booleanValue() == false) {
-                list.addAll(questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false));
-            }
-            model.addAttribute("questionList", list);
-            model.addAttribute("subject", subject);
-            model.addAttribute("objective", objective);
-            return "modules/exam/paperDetail";
-        } else {
-            return "redirect:/admin/exam/paper";
-        }
-    }
-
-    @RequestMapping("/clear")
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String clear(Model model, HttpServletRequest request, @RequestParam String subjectCode) {
-        int examId = getSessionExamId(request);
-        ExamSubject subject = subjectService.find(examId, subjectCode);
-        if (subject != null) {
-            questionService.deleteByExamAndSubject(examId, subjectCode);
-
-            subject.setObjectiveScore(0d);
-            subject.setSubjectiveScore(0d);
-            subject.setTotalScore(0d);
-            subjectService.save(subject);
-        }
-        return "redirect:/admin/exam/paper";
-    }
-
-    @RequestMapping(value = "/template")
-    public String importObjectiveTemplate(HttpServletResponse response, @RequestParam Boolean objective,
-            RedirectAttributes redirectAttributes) {
-        try {
-            String fileName = objective ? "客观题导入模板.xlsx" : "主观题导入模板.xlsx";
-            String title = objective ? "客观题数据" : "主观题数据";
-            List<QuestionDTO> list = new LinkedList<QuestionDTO>();
-            list.add(objective ? new ObjectiveQuestionDTO() : new SubjectiveQuestionDTO());
-            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2)
-                    .setDataList(list).write(response, fileName).dispose();
-            return null;
-        } catch (Exception e) {
-            addMessage(redirectAttributes, "导入模板下载失败!失败信息:" + e.getMessage());
-        }
-        return "redirect:/admin/exam/paper";
-    }
-
-    @RequestMapping(value = "/export")
-    public String exportFile(HttpServletRequest request, HttpServletResponse response, @RequestParam Boolean objective,
-            RedirectAttributes redirectAttributes) {
-        int examId = getSessionExamId(request);
-
-        Map<String, ExamSubject> subjectMap = new HashMap<String, ExamSubject>();
-        List<ExamSubject> subjectList = subjectService.list(examId);
-        for (ExamSubject subject : subjectList) {
-            subjectMap.put(subject.getCode(), subject);
-        }
-
-        Map<String, MarkGroup> groupMap = new HashMap<String, MarkGroup>();
-        List<MarkGroup> groupList = groupService.findByExam(examId);
-        for (MarkGroup group : groupList) {
-            groupMap.put(group.getSubjectCode() + "_" + group.getNumber(), group);
-        }
-
-        List<QuestionDTO> list = new LinkedList<QuestionDTO>();
-        ExamQuestionSearchQuery query = new ExamQuestionSearchQuery();
-        query.setExamId(examId);
-        query.setObjective(objective);
-        query.setSort(new Sort(Direction.ASC, "pk.subjectCode"));
-        query.setPageNumber(1);
-        query.setPageSize(Integer.MAX_VALUE);
-        query = questionService.findByQuery(query);
-        for (ExamQuestion q : query.getResult()) {
-            list.add(objective ? new ObjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()))
-                    : new SubjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()),
-                            groupMap.get(q.getSubjectCode() + "_" + q.getMainNumber())));
-        }
-        try {
-            String fileName = objective ? "客观题数据.xlsx" : "主观题数据.xlsx";
-            String title = objective ? "客观题数据" : "主观题数据";
-            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2)
-                    .setDataList(list).write(response, fileName).dispose();
-            return null;
-        } catch (Exception e) {
-            addMessage(redirectAttributes, "导出数据失败!失败信息:" + e.getMessage());
-        }
-        return "redirect:/admin/exam/paper";
-    }
-
-    @RequestMapping(value = "/import", method = RequestMethod.POST)
-    @RoleRequire(Role.SCHOOL_ADMIN)
-    public String importFile(HttpServletRequest request, MultipartFile file, @RequestParam Boolean objective,
-            RedirectAttributes redirectAttributes) {
-        int examId = getSessionExamId(request);
-
-        List<String> error = new LinkedList<String>();
-        Map<String, SubjectQuestionDTO> map = parseQuestion(file, examId, objective, error);
-        if (error.isEmpty()) {
-            int success = 0;
-            for (SubjectQuestionDTO dto : map.values()) {
-                ExamSubject subject = subjectService.find(examId, dto.getSubjectCode());
-                if (subject != null) {
-                    if (dto.validate(error)) {
-                        if (objective) {
-                            questionService.deleteByExamAndSubjectAndObjective(examId, dto.getSubjectCode(), objective);
-                            for (ExamQuestion question : dto.getQuestionList()) {
-                                questionService.save(question);
-                            }
-                        } else {
-                            for (MarkGroup group : dto.getGroups().values()) {
-                                if (groupService.findOne(examId, group.getSubjectCode(), group.getNumber()) == null) {
-                                    groupService.save(group);
-                                }
-                            }
-                        }
-                        subjectService.updateScore(examId, subject.getCode());
-                        success++;
-                    }
-                } else {
-                    error.add("[" + dto.getSubjectCode() + "] 科目代码不存在;");
-                }
-            }
-            error.add(0, success + "个科目导入成功;");
-            if (success > 0) {
-                subjectService.updateTotalScore(examId);
-            }
-        }
-        if (error.size() > 0) {
-        	error.add("<br\\>导入完毕后请重新统分!");
-            addMessage(redirectAttributes, StringUtils.join(error, "<br\\>"));
-        }
-        return "redirect:/admin/exam/paper";
-    }
-
-    private Map<String, SubjectQuestionDTO> parseQuestion(MultipartFile file, int examId, boolean objective,
-            List<String> error) {
-        Map<String, SubjectQuestionDTO> map = new HashMap<String, SubjectQuestionDTO>();
-        try {
-            ImportExcel excel = new ImportExcel(file, 1, 0);
-            List<? extends QuestionDTO> list = excel
-                    .getDataList(objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class);
-            if (list != null) {
-                for (QuestionDTO dto : list) {
-                    if (dto.validate()) {
-                        String subjectCode = dto.getSubjectCode();
-                        SubjectQuestionDTO subject = map.get(subjectCode);
-                        if (subject == null) {
-                            subject = new SubjectQuestionDTO(examId, subjectCode, objective);
-                            map.put(subjectCode, subject);
-                        }
-                        subject.addQuestion(dto.transform());
-                    }
-                }
-            }
-        } catch (Exception e) {
-            log.error("parse question excel error", e);
-            error.add("Excel文件解析失败");
-        }
-        return map;
-    }
-}
+package cn.com.qmth.stmms.admin.exam;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+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.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.admin.dto.ObjectiveQuestionDTO;
+import cn.com.qmth.stmms.admin.dto.QuestionDTO;
+import cn.com.qmth.stmms.admin.dto.SubjectQuestionDTO;
+import cn.com.qmth.stmms.admin.dto.SubjectiveQuestionDTO;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+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.ExamQuestionService;
+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.query.ExamQuestionSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
+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.ImportExcel;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+@Controller("examPaperController")
+@RequestMapping("/admin/exam/paper")
+public class PaperController extends BaseExamController {
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Value("${card.server}")
+    private String cardServer;
+
+    @RequestMapping
+    public String list(Model model, HttpServletRequest request, ExamSubjectSearchQuery query,
+            @RequestParam(required = false) Boolean upload) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        int examId = getSessionExamId(request);
+        query.setExamId(examId);
+        if (upload != null) {
+            if (upload.booleanValue()) {
+                query.setUploadCountGt(0);
+            } else {
+                query.setUploadCountEqual(0);
+            }
+        }
+        subjectFilter(query, wu);
+        query = subjectService.findByQuery(query);
+        model.addAttribute("query", query);
+        model.addAttribute("upload", upload);
+        model.addAttribute("subjectList", getExamSubject(examId, wu));
+        model.addAttribute("levelList", subjectService.listLevel(examId));
+        model.addAttribute("categoryList", subjectService.listCategory(examId));
+        model.addAttribute("cardServer", cardServer);
+        return "modules/exam/paperList";
+    }
+
+    @RequestMapping("/detail")
+    public String detail(Model model, HttpServletRequest request, @RequestParam String subjectCode,
+            @RequestParam(required = false) Boolean objective) {
+        WebUser wu = RequestUtils.getWebUser(request);
+        int examId = getSessionExamId(request);
+        if (wu.isSubjectHeader() && !subjectCode.equals(wu.getUser().getSubjectCode())) {
+            return "redirect:/admin/exam/paper";
+        }
+        ExamSubject subject = subjectService.find(examId, subjectCode);
+        if (subject != null) {
+            List<ExamQuestion> list = new LinkedList<ExamQuestion>();
+            if (objective == null || objective.booleanValue() == true) {
+                list.addAll(questionService.findByExamAndSubjectAndObjective(examId, subjectCode, true));
+            }
+            if (objective == null || objective.booleanValue() == false) {
+                list.addAll(questionService.findByExamAndSubjectAndObjective(examId, subjectCode, false));
+            }
+            model.addAttribute("questionList", list);
+            model.addAttribute("subject", subject);
+            model.addAttribute("objective", objective);
+            return "modules/exam/paperDetail";
+        } else {
+            return "redirect:/admin/exam/paper";
+        }
+    }
+
+    @RequestMapping("/clear")
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String clear(Model model, HttpServletRequest request, @RequestParam String subjectCode) {
+        int examId = getSessionExamId(request);
+        ExamSubject subject = subjectService.find(examId, subjectCode);
+        if (subject != null) {
+            questionService.deleteByExamAndSubject(examId, subjectCode);
+
+            subject.setObjectiveScore(0d);
+            subject.setSubjectiveScore(0d);
+            subject.setTotalScore(0d);
+            subjectService.save(subject);
+        }
+        return "redirect:/admin/exam/paper";
+    }
+
+    @RequestMapping(value = "/template")
+    public String importObjectiveTemplate(HttpServletResponse response, @RequestParam Boolean objective,
+            RedirectAttributes redirectAttributes) {
+        try {
+            String fileName = objective ? "客观题导入模板.xlsx" : "主观题导入模板.xlsx";
+            String title = objective ? "客观题数据" : "主观题数据";
+            List<QuestionDTO> list = new LinkedList<QuestionDTO>();
+            list.add(objective ? new ObjectiveQuestionDTO() : new SubjectiveQuestionDTO());
+            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2)
+                    .setDataList(list).write(response, fileName).dispose();
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "导入模板下载失败!失败信息:" + e.getMessage());
+        }
+        return "redirect:/admin/exam/paper";
+    }
+
+    @RequestMapping(value = "/export")
+    public String exportFile(HttpServletRequest request, HttpServletResponse response, @RequestParam Boolean objective,
+            RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+
+        Map<String, ExamSubject> subjectMap = new HashMap<String, ExamSubject>();
+        List<ExamSubject> subjectList = subjectService.list(examId);
+        for (ExamSubject subject : subjectList) {
+            subjectMap.put(subject.getCode(), subject);
+        }
+
+        Map<String, MarkGroup> groupMap = new HashMap<String, MarkGroup>();
+        List<MarkGroup> groupList = groupService.findByExam(examId);
+        for (MarkGroup group : groupList) {
+            groupMap.put(group.getSubjectCode() + "_" + group.getNumber(), group);
+        }
+
+        List<QuestionDTO> list = new LinkedList<QuestionDTO>();
+        ExamQuestionSearchQuery query = new ExamQuestionSearchQuery();
+        query.setExamId(examId);
+        query.setObjective(objective);
+        query.setSort(new Sort(Direction.ASC, "pk.subjectCode"));
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        query = questionService.findByQuery(query);
+        for (ExamQuestion q : query.getResult()) {
+            list.add(objective ? new ObjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()))
+                    : new SubjectiveQuestionDTO(q, subjectMap.get(q.getSubjectCode()),
+                            groupMap.get(q.getSubjectCode() + "_" + q.getMainNumber())));
+        }
+        try {
+            String fileName = objective ? "客观题数据.xlsx" : "主观题数据.xlsx";
+            String title = objective ? "客观题数据" : "主观题数据";
+            new ExportExcel(title, objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class, 2)
+                    .setDataList(list).write(response, fileName).dispose();
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "导出数据失败!失败信息:" + e.getMessage());
+        }
+        return "redirect:/admin/exam/paper";
+    }
+
+    @RequestMapping(value = "/import", method = RequestMethod.POST)
+    @RoleRequire(Role.SCHOOL_ADMIN)
+    public String importFile(HttpServletRequest request, MultipartFile file, @RequestParam Boolean objective,
+            RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+
+        List<String> error = new LinkedList<String>();
+        Map<String, SubjectQuestionDTO> map = parseQuestion(file, examId, objective, error);
+        if (error.isEmpty()) {
+            int success = 0;
+            for (SubjectQuestionDTO dto : map.values()) {
+                ExamSubject subject = subjectService.find(examId, dto.getSubjectCode());
+                if (subject != null) {
+                    if (dto.validate(error)) {
+                        if (objective) {
+                            questionService.deleteByExamAndSubjectAndObjective(examId, dto.getSubjectCode(), objective);
+                            for (ExamQuestion question : dto.getQuestionList()) {
+                                questionService.save(question);
+                            }
+                        } else {
+                            for (MarkGroup group : dto.getGroups().values()) {
+                                if (groupService.findOne(examId, group.getSubjectCode(), group.getNumber()) == null) {
+                                    questionService.deleteByExamAndSubjectAndObjectiveAndMainNumber(group.getExamId(),
+                                            group.getSubjectCode(), false, group.getNumber());
+                                    for (ExamQuestion question : group.getQuestionList()) {
+                                        questionService.save(question);
+                                    }
+                                    groupService.save(group);
+                                }
+                            }
+                        }
+                        subjectService.updateScore(examId, subject.getCode());
+                        success++;
+                    }
+                } else {
+                    error.add("[" + dto.getSubjectCode() + "] 科目代码不存在;");
+                }
+            }
+            error.add(0, success + "个科目导入成功;");
+            if (success > 0) {
+                subjectService.updateTotalScore(examId);
+            }
+        }
+        if (error.size() > 0) {
+            error.add("<br\\>导入完毕后请重新统分!");
+            addMessage(redirectAttributes, StringUtils.join(error, "<br\\>"));
+        }
+        return "redirect:/admin/exam/paper";
+    }
+
+    private Map<String, SubjectQuestionDTO> parseQuestion(MultipartFile file, int examId, boolean objective,
+            List<String> error) {
+        Map<String, SubjectQuestionDTO> map = new HashMap<String, SubjectQuestionDTO>();
+        try {
+            ImportExcel excel = new ImportExcel(file, 1, 0);
+            List<? extends QuestionDTO> list = excel
+                    .getDataList(objective ? ObjectiveQuestionDTO.class : SubjectiveQuestionDTO.class);
+            if (list != null) {
+                for (QuestionDTO dto : list) {
+                    if (dto.validate()) {
+                        String subjectCode = dto.getSubjectCode();
+                        SubjectQuestionDTO subject = map.get(subjectCode);
+                        if (subject == null) {
+                            subject = new SubjectQuestionDTO(examId, subjectCode, objective);
+                            map.put(subjectCode, subject);
+                        }
+                        subject.addQuestion(dto.transform());
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("parse question excel error", e);
+            error.add("Excel文件解析失败");
+        }
+        return map;
+    }
+}

+ 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;
+    }
+}

+ 0 - 30
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/TagController.java

@@ -12,8 +12,6 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
 
 import cn.com.qmth.stmms.biz.campus.model.Campus;
 import cn.com.qmth.stmms.biz.campus.service.CampusService;
@@ -23,14 +21,10 @@ import cn.com.qmth.stmms.biz.exam.service.TagService;
 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;
-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.Role;
 import cn.com.qmth.stmms.common.utils.DateUtils;
 import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
 import cn.com.qmth.stmms.common.utils.RequestUtils;
-import net.sf.json.JSONObject;
 
 @Controller("tagController")
 @RequestMapping("/admin/exam/tag")
@@ -50,9 +44,6 @@ public class TagController extends BaseExamController {
     @Autowired
     private TagService tagService;
 
-    @Autowired
-    private MarkService markService;
-
     @Value("${sheet.image.server}")
     private String imageServer;
 
@@ -95,27 +86,6 @@ public class TagController extends BaseExamController {
         return "modules/exam/tagInfo";
     }
 
-    @RequestMapping("/back")
-    @ResponseBody
-    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER })
-    public Object back(HttpServletRequest request, @RequestParam Integer libraryId) {
-        JSONObject result = new JSONObject();
-        MarkLibrary library = libraryService.findById(libraryId);
-        if (library != null) {
-            if (subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
-                markService.backLibrary(library);
-                result.accumulate("success", true);
-            } else {
-                result.accumulate("success", false);
-                result.accumulate("message", "没有操作该评卷任务的权限");
-            }
-        } else {
-            result.accumulate("success", false);
-            result.accumulate("message", "找不到对应的评卷任务");
-        }
-        return result;
-    }
-
     private List<Campus> getTagCampus(int examId) {
         List<Campus> list = new LinkedList<Campus>();
         List<Integer> ids = libraryService.findTagCampusId(examId);

+ 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()) {// 主观题有分数明细才统分

+ 25 - 26
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);
@@ -277,7 +254,7 @@ public class MarkController extends BaseController {
         try {
             task.setSpent((new Date().getTime() - task.getSpent()) / 1000);
             MarkLibrary library = libraryService.findById(task.getLibraryId());
-            if (library != null && library.getStatus() != LibraryStatus.ARBITRATED && library.getStatus() != LibraryStatus.WAIT_ARBITRATE) {
+            if (checkLibrary(library, marker)) {
                 library.setMarkerId(marker.getId());
                 library.setMarkerTime(new Date());
                 library.setMarkerScore(task.getTotalScore());
@@ -353,4 +330,26 @@ public class MarkController extends BaseController {
         return array;
     }
 
+    private boolean checkLibrary(MarkLibrary library, Marker marker) {
+        if (library == null) {
+            return false;
+        }
+        if (!library.getExamId().equals(marker.getExamId())) {
+            return false;
+        }
+        if (!library.getSubjectCode().equals(marker.getSubjectCode())) {
+            return false;
+        }
+        if (!library.getGroupNumber().equals(marker.getGroupNumber())) {
+            return false;
+        }
+        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;
+    }
+}

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

@@ -30,8 +30,6 @@
 			<a href="${ctx}/admin/exam/group/add?subjectCode=${subject.code}" class="btn">新增</a>
 			&nbsp;
 			<a href="${ctx}/admin/exam/group/check-count?subjectCode=${subject.code}" class="btn">数量校对</a>
-			&nbsp;
-			<a href="${ctx}/admin/exam/marker?subjectCode=${subject.code}" class="btn">评卷员管理</a>
 			</c:if>
 		</div>
 	</form>

+ 126 - 138
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/tagInfo.jsp

@@ -1,139 +1,127 @@
-<%@ page contentType="text/html;charset=UTF-8" %>
-<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
-<html>
-<head>
-	<title>标记试卷处理</title>
-	<meta name="decorator" content="default"/>
-	<%@include file="/WEB-INF/views/include/head.jsp" %>
-	<style type="text/css">.sort{color:#0663A2;cursor:pointer;}</style>
-</head>
-<body>
-	<form id="searchForm" action="${ctx}/admin/exam/tag" method="post" class="breadcrumb form-search">
-		<input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber }"/>
-		<input type="hidden" id="pageSize" name="pageSize" value="${query.pageSize }"/>
-		<div>
-			<label>科目</label>
-			<select class="input-large" id="subject-select" name="subjectCode">
-				<option value="">请选择</option>
-				<c:forEach items="${subjectList}" var="subject">
-				<option value="${subject.code}" <c:if test="${subject.code==query.subjectCode}">selected</c:if>>${subject.code}-${subject.name}</option>
-				</c:forEach>
-			</select>
-			<label>大题</label>
-			<select class="input-medium" id="group-select" name="groupNumber">
-				<option value="0">请选择</option>
-			</select>
-			<label>学习中心</label>
-			<select class="input-large" name="campusId">
-				<option value="0">请选择</option>
-				<c:forEach items="${campusList}" var="campus">
-				<option value="${campus.id}" <c:if test="${campus.id==query.campusId}">selected</c:if>>${campus.name}</option>
-				</c:forEach>
-			</select>
-			<label>标记类型</label>
-			<select class="input-medium" name="tagId">
-				<c:forEach items="${tagList}" var="tag">
-				<option value="${tag.id}" <c:if test="${tag.id==query.tagId}">selected</c:if>>${tag.name}</option>
-				</c:forEach>
-			</select>
-			<br/><br/>
-			<input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
-		</div>
-	</form>
-	<tags:message content="${message}"/>
-	<table id="contentTable" class="table table-striped table-bordered table-condensed">
-		<thead>
-			<tr>
-				<th>科目</th>
-				<th>学习中心</th>
-				<th>准考证号</th>
-				<th>姓名</th>
-				<th>提交时间</th>
-				<th>操作</th>
-			</tr>
-		</thead>
-		<tbody>
-		<c:forEach items="${resultList}" var="result">
-			<tr>
-				<td>${result.subjectCode}-${result.subjectName}</td>
-				<td>${result.campusName}</td>
-				<td>${result.examNumber}</td>
-				<td>${result.name}</td>
-				<td>${result.markTime}</td>
-				<td>
-					<a class="sheet-link" href="##" data-sheet-url="${result.sheetUrlString}" data-answer-url="<c:if test="${result.answerUrl!=null}">${cardServer}${result.answerUrl}</c:if>" data-title="${result.examNumber}&nbsp;&nbsp;${result.name}&nbsp;&nbsp;客观总分${result.objectiveScoreString}&nbsp;&nbsp;主观总分${result.subjectiveScoreString}&nbsp;&nbsp;全卷总分${result.totalScoreString}">原图</a>
-					<a href="##" class="back-button" data-library-id="${result.libraryId}">打回</a>
-				</td>
-			</tr>
-		</c:forEach>
-		</tbody>
-	</table>
-	<div class="pagination">${query}</div>
-	<%@include file="/WEB-INF/views/include/imageView.jsp" %>
-<script type="text/javascript">
-var searchSubjectCode = '${query.subjectCode}';
-var searchGroupNumber = '${query.groupNumber}';
-$(document).ready(function() {
-    /* new jBox('Image', {
-    	imageFade: 0,
-    	delayOpen: 0,
-    	delayClose: 0,
-    	maxHeight: $(window).height()*0.88
-    }); */
-    $('.sheet-link').click(function(){
-    	initImagePopover($(this).attr('data-title'), '${imageServer}', $(this).attr('data-sheet-url'), $(this).attr('data-answer-url'));
-    	return false;
-    });
-    $('.back-button').click(function(){
-        if(confirm('确定要打回改试卷?')){  
-        $.post('${ctx}/admin/exam/tag/back', {libraryId: $(this).attr('data-library-id')}, function(result){
-            if(result.success==true){
-            	$("#searchForm").submit();
-            }else{
-                alert(result.message || '提交失败,请稍后重试');
-            }
-        });
-        }
-    });
-
-    $('#subject-select').change(function(){
-        var code = $(this).val();
-        if(code==''){
-            $('#group-select').empty();
-            return;
-        }
-        $.post('${ctx}/admin/exam/group/query', {subjectCode: code}, function(result){
-            var parent = $('#group-select');
-            parent.empty();
-            for(var i=0;i<result.length;i++){
-                var group = result[i];
-                $('<option value="'+group.number+'">'+group.number+'-'+group.title+'</option>').appendTo(parent);
-            }
-            if(searchSubjectCode==code && searchGroupNumber!=''){
-                parent.val(searchGroupNumber);
-            }
-            parent.trigger('change');
-        });
-    });
-    $('#subject-select').trigger('change');
-});
-function page(n,s){
-	$("#pageNumber").val(n);
-	$("#searchForm").attr('action', '${ctx}/admin/exam/tag');
-	$("#searchForm").submit();
-	return false;
-}
-function goSearch(){
-	$("#pageNumber").val(1);
-	$("#searchForm").attr('action', '${ctx}/admin/exam/tag');
-	$("#searchForm").submit();
-	return false;
-}
-function goExport(){
-	$("#searchForm").attr('action', '${ctx}/admin/exam/tag/export');
-	$("#searchForm").submit();
-	return false;
-}
-</script>	
-</body>
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<html>
+<head>
+	<title>标记试卷处理</title>
+	<meta name="decorator" content="default"/>
+	<%@include file="/WEB-INF/views/include/head.jsp" %>
+	<style type="text/css">.sort{color:#0663A2;cursor:pointer;}</style>
+</head>
+<body>
+	<form id="searchForm" action="${ctx}/admin/exam/tag" method="post" class="breadcrumb form-search">
+		<input type="hidden" id="pageNumber" name="pageNumber" value="${query.pageNumber }"/>
+		<input type="hidden" id="pageSize" name="pageSize" value="${query.pageSize }"/>
+		<div>
+			<label>科目</label>
+			<select class="input-large" id="subject-select" name="subjectCode">
+				<option value="">请选择</option>
+				<c:forEach items="${subjectList}" var="subject">
+				<option value="${subject.code}" <c:if test="${subject.code==query.subjectCode}">selected</c:if>>${subject.code}-${subject.name}</option>
+				</c:forEach>
+			</select>
+			<label>大题</label>
+			<select class="input-medium" id="group-select" name="groupNumber">
+				<option value="0">请选择</option>
+			</select>
+			<label>学习中心</label>
+			<select class="input-large" name="campusId">
+				<option value="0">请选择</option>
+				<c:forEach items="${campusList}" var="campus">
+				<option value="${campus.id}" <c:if test="${campus.id==query.campusId}">selected</c:if>>${campus.name}</option>
+				</c:forEach>
+			</select>
+			<label>标记类型</label>
+			<select class="input-medium" name="tagId">
+				<c:forEach items="${tagList}" var="tag">
+				<option value="${tag.id}" <c:if test="${tag.id==query.tagId}">selected</c:if>>${tag.name}</option>
+				</c:forEach>
+			</select>
+			<br/><br/>
+			<input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
+		</div>
+	</form>
+	<tags:message content="${message}"/>
+	<table id="contentTable" class="table table-striped table-bordered table-condensed">
+		<thead>
+			<tr>
+				<th>科目</th>
+				<th>学习中心</th>
+				<th>准考证号</th>
+				<th>姓名</th>
+				<th>提交时间</th>
+				<th>操作</th>
+			</tr>
+		</thead>
+		<tbody>
+		<c:forEach items="${resultList}" var="result">
+			<tr>
+				<td>${result.subjectCode}-${result.subjectName}</td>
+				<td>${result.campusName}</td>
+				<td>${result.examNumber}</td>
+				<td>${result.name}</td>
+				<td>${result.markTime}</td>
+				<td>
+					<a class="sheet-link" href="##" data-sheet-url="${result.sheetUrlString}" data-answer-url="<c:if test="${result.answerUrl!=null}">${cardServer}${result.answerUrl}</c:if>" data-title="${result.examNumber}&nbsp;&nbsp;${result.name}&nbsp;&nbsp;客观总分${result.objectiveScoreString}&nbsp;&nbsp;主观总分${result.subjectiveScoreString}&nbsp;&nbsp;全卷总分${result.totalScoreString}">原图</a>
+				</td>
+			</tr>
+		</c:forEach>
+		</tbody>
+	</table>
+	<div class="pagination">${query}</div>
+	<%@include file="/WEB-INF/views/include/imageView.jsp" %>
+<script type="text/javascript">
+var searchSubjectCode = '${query.subjectCode}';
+var searchGroupNumber = '${query.groupNumber}';
+$(document).ready(function() {
+    /* new jBox('Image', {
+    	imageFade: 0,
+    	delayOpen: 0,
+    	delayClose: 0,
+    	maxHeight: $(window).height()*0.88
+    }); */
+    $('.sheet-link').click(function(){
+    	initImagePopover($(this).attr('data-title'), '${imageServer}', $(this).attr('data-sheet-url'), $(this).attr('data-answer-url'));
+    	return false;
+    });
+
+    $('#subject-select').change(function(){
+        var code = $(this).val();
+        if(code==''){
+            $('#group-select').empty();
+            return;
+        }
+        $.post('${ctx}/admin/exam/group/query', {subjectCode: code}, function(result){
+            var parent = $('#group-select');
+            parent.empty();
+            for(var i=0;i<result.length;i++){
+                var group = result[i];
+                $('<option value="'+group.number+'">'+group.number+'-'+group.title+'</option>').appendTo(parent);
+            }
+            if(searchSubjectCode==code && searchGroupNumber!=''){
+                parent.val(searchGroupNumber);
+            }
+            parent.trigger('change');
+        });
+    });
+    $('#subject-select').trigger('change');
+});
+function page(n,s){
+	$("#pageNumber").val(n);
+	$("#searchForm").attr('action', '${ctx}/admin/exam/tag');
+	$("#searchForm").submit();
+	return false;
+}
+function goSearch(){
+	$("#pageNumber").val(1);
+	$("#searchForm").attr('action', '${ctx}/admin/exam/tag');
+	$("#searchForm").submit();
+	return false;
+}
+function goExport(){
+	$("#searchForm").attr('action', '${ctx}/admin/exam/tag/export');
+	$("#searchForm").submit();
+	return false;
+}
+</script>	
+</body>
 </html>

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

@@ -25,7 +25,7 @@ function SpecialTag(option) {
         this.task = undefined;
     });
     this.markControl.on('image.click.event', this, function(event, context, eventObject) {
-        if(this.task!=undefined && this.tagName!=undefined){
+        if(this.task!=undefined && this.tagName!=undefined && this.show==true){
         	var specialTag = {
         		tagName : this.tagName,
         		positionX : eventObject.positionX,