1
0
Quellcode durchsuchen

Merge remote-tracking branch 'origin/release_1.2.7' into dev_1.3.0

Conflicts:
	stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamQuestionServiceImpl.java
ting.yin vor 4 Jahren
Ursprung
Commit
3fdc6bebb4
49 geänderte Dateien mit 1723 neuen und 734 gelöschten Zeilen
  1. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/common/BaseQuery.java
  2. 1 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamDao.java
  3. 5 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamStudentDao.java
  4. 18 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ImportQueryDao.java
  5. 4 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamQuestion.java
  6. 163 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ImportQuery.java
  7. 1 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamService.java
  8. 3 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamStudentService.java
  9. 13 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ImportQueryService.java
  10. 17 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/InspectedService.java
  11. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamServiceImpl.java
  12. 6 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamStudentServiceImpl.java
  13. 39 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ImportQueryServiceImpl.java
  14. 136 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/InspectedServiceImpl.java
  15. 9 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkLibraryDao.java
  16. 44 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/SubmitResult.java
  17. 36 37
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkCronService.java
  18. 70 66
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/MarkServiceImpl.java
  19. 5 16
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java
  20. 0 52
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/AbstractTask.java
  21. 0 34
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/BlockTaskCountUtil.java
  22. 0 197
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/CurrentTaskUtil2.java
  23. 232 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLock.java
  24. 43 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLockUtil.java
  25. 0 206
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TrialTaskUtil.java
  26. 31 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ImportType.java
  27. 2 2
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectiveQuestionDTO.java
  28. 1 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/BaseExamController.java
  29. 1 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java
  30. 312 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ImportQueryController.java
  31. 20 57
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/InspectedController.java
  32. 1 1
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/log/OperationLogController.java
  33. 64 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/utils/PageUtil.java
  34. 25 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/vo/InspectedStudentVO.java
  35. 7 8
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  36. 4 18
      stmms-web/src/main/java/cn/com/qmth/stmms/monitor/TaskMonitorController.java
  37. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examEdit.jsp
  38. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupAdd.jsp
  39. 2 2
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/groupEditSimple.jsp
  40. 241 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/inspectedImport.jsp
  41. 103 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/inspectedImportList.jsp
  42. 21 7
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/inspectedList.jsp
  43. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/subjectEdit.jsp
  44. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markNew.jsp
  45. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markTrack.jsp
  46. 20 1
      stmms-web/src/main/webapp/sql/stmms_ft.sql
  47. 6 1
      stmms-web/src/main/webapp/static/inspected/css/style.css
  48. 5 1
      stmms-web/src/main/webapp/static/mark-track/js/mark-control.js
  49. 3 3
      stmms-web/src/main/webapp/static/mark-track/js/modules/mark-board.js

+ 2 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/common/BaseQuery.java

@@ -261,10 +261,10 @@ public class BaseQuery<T> implements Pageable {
         sb.append("<li class=\"disabled controls\"><a href=\"javascript:\">当前 ");
         sb.append("<input type=\"text\" value=\"" + pageNumber
                 + "\" onkeypress=\"var e=window.event||this;var c=e.keyCode||e.which;if(c==13)");
-        sb.append(funcName + "(this.value," + pageSize + ");\" onclick=\"this.select();\"/> / ");
+        sb.append(funcName + "(this.value," + pageSize + ");\" onclick=\"this.select();\"/> / ");
         sb.append("<input type=\"text\" value=\"" + pageSize
                 + "\" onkeypress=\"var e=window.event||this;var c=e.keyCode||e.which;if(c==13)");
-        sb.append(funcName + "(" + pageNumber + ",this.value);\" onclick=\"this.select();\"/> ,");
+        sb.append(funcName + "(" + pageNumber + ",this.value);\" onclick=\"this.select();\"/> ,");
         sb.append("共 " + totalCount + " 条" + (message != null ? message : "") + "</a><li>\n");
 
         sb.insert(0, "<ul>\n").append("</ul>\n");

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

@@ -34,7 +34,7 @@ public interface ExamDao extends PagingAndSortingRepository<Exam, Integer>, JpaS
 
     List<Exam> findBySchoolIdAndTypeAndStatusOrderByIdDesc(Integer schoolId, ExamType type, ExamStatus status);
 
-    List<Exam> findBySchoolIdAndStatusOrderByIdDesc(Integer schoolId, ExamStatus status);
+    List<Exam> findBySchoolIdOrderByIdDesc(Integer schoolId);
 
     public Exam findFirstBySchoolIdAndCode(Integer schoolId, String code);
 

+ 5 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamStudentDao.java

@@ -2,6 +2,7 @@ package cn.com.qmth.stmms.biz.exam.dao;
 
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 import org.springframework.data.jpa.repository.Modifying;
@@ -11,8 +12,8 @@ import org.springframework.data.repository.PagingAndSortingRepository;
 import java.util.Date;
 import java.util.List;
 
-public interface ExamStudentDao
-        extends PagingAndSortingRepository<ExamStudent, Integer>, JpaSpecificationExecutor<ExamStudent> {
+public interface ExamStudentDao extends PagingAndSortingRepository<ExamStudent, Integer>,
+        JpaSpecificationExecutor<ExamStudent> {
 
     public List<ExamStudent> findByExamId(int examId, Pageable pageable);
 
@@ -191,4 +192,6 @@ public interface ExamStudentDao
     public void updateSubjectiveStatusAndTimeAndInspectorId(Integer studentId, SubjectiveStatus status,
             Date inspectTime, Integer inspectorId);
 
+    public List<ExamStudent> findByExamIdAndStudentCode(int examId, String studentCode);
+
 }

+ 18 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ImportQueryDao.java

@@ -0,0 +1,18 @@
+package cn.com.qmth.stmms.biz.exam.dao;
+
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+import cn.com.qmth.stmms.biz.exam.model.ImportQuery;
+import cn.com.qmth.stmms.common.enums.ImportType;
+
+public interface ImportQueryDao extends PagingAndSortingRepository<ImportQuery, Integer>,
+        JpaSpecificationExecutor<ImportQuery> {
+
+    @Modifying
+    void deleteByExamIdAndUserIdAndType(int examId, Integer userId, ImportType type);
+
+    ImportQuery findByExamIdAndUserIdAndType(int examId, Integer userId, ImportType type);
+
+}

+ 4 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamQuestion.java

@@ -161,9 +161,12 @@ public class ExamQuestion implements Serializable {
     public List<Double> getScoreList() {
         List<Double> list = new LinkedList<Double>();
         if (totalScore != null && intervalScore != null) {
+            totalScore = totalScore * 100;
+            intervalScore = intervalScore * 100;
             for (double score = 0; score <= totalScore; score += intervalScore) {
-                list.add(score);
+                list.add(score / 100);
             }
+            totalScore = totalScore / 100;
             if (list.get(list.size() - 1) < totalScore) {
                 list.add(totalScore);
             }

+ 163 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ImportQuery.java

@@ -0,0 +1,163 @@
+package cn.com.qmth.stmms.biz.exam.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.apache.commons.lang3.StringUtils;
+
+import net.sf.json.JSONObject;
+import cn.com.qmth.stmms.common.enums.ImportType;
+
+@Entity
+@Table(name = "eb_import_query")
+public class ImportQuery implements Serializable {
+
+    private static final long serialVersionUID = -7983129784062675039L;
+
+    public static final String DB_ITEM_JOINER = ",";
+
+    public static final String SUCCESS = "success";
+
+    public static final String FAILURE = "failure";
+
+    @Id
+    @GeneratedValue
+    private Integer id;
+
+    @Column(name = "exam_id", nullable = false)
+    private Integer examId;
+
+    @Column(name = "user_id", nullable = false)
+    private Integer userId;
+
+    @Enumerated(EnumType.STRING)
+    @Column(name = "type", length = 16, nullable = false)
+    private ImportType type;
+
+    @Column(name = "description")
+    private String description;
+
+    @Temporal(TemporalType.TIMESTAMP)
+    @Column(name = "create_time")
+    private Date createTime;
+
+    public ImportQuery(Integer examId, Integer userId, ImportType type, String description) {
+        this.examId = examId;
+        this.userId = userId;
+        this.type = type;
+        this.description = description;
+        this.createTime = new Date();
+    }
+
+    public ImportQuery() {
+    }
+
+    public ImportQuery(Integer examId, Integer userId, ImportType type, Set<Integer> successStudent,
+            Set<String> failureStudent) {
+        this.examId = examId;
+        this.userId = userId;
+        this.type = type;
+        JSONObject json = new JSONObject();
+        json.accumulate(SUCCESS, StringUtils.join(successStudent, DB_ITEM_JOINER));
+        json.accumulate(FAILURE, StringUtils.join(failureStudent, DB_ITEM_JOINER));
+        this.description = json.toString();
+        this.createTime = new Date();
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Integer examId) {
+        this.examId = examId;
+    }
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public ImportType getType() {
+        return type;
+    }
+
+    public void setType(ImportType type) {
+        this.type = type;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public List<String> getStudentCodeList() {
+        List<String> list = new ArrayList<String>();
+        JSONObject json = JSONObject.fromObject(description);
+        String failure = json.getString(FAILURE);
+        String[] values = StringUtils.split(failure, DB_ITEM_JOINER);
+        for (String studentCode : values) {
+            list.add(studentCode);
+        }
+        return list;
+    }
+
+    public List<Integer> getStudentIdList() {
+        List<Integer> list = new ArrayList<Integer>();
+        JSONObject json = JSONObject.fromObject(description);
+        String success = json.getString(SUCCESS);
+        String[] values = StringUtils.split(success, DB_ITEM_JOINER);
+        for (String studentId : values) {
+            list.add(Integer.parseInt(studentId));
+        }
+        return list;
+    }
+
+    public String getStudentIdString() {
+        JSONObject json = JSONObject.fromObject(description);
+        String success = json.getString(SUCCESS);
+        return success;
+    }
+
+    public String buildDescription(Set<Integer> successStudent, Set<String> failureStudent) {
+        JSONObject json = new JSONObject();
+        json.accumulate(SUCCESS, StringUtils.join(successStudent, DB_ITEM_JOINER));
+        json.accumulate(FAILURE, StringUtils.join(failureStudent, DB_ITEM_JOINER));
+        return json.toString();
+
+    }
+}

+ 1 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamService.java

@@ -22,7 +22,7 @@ public interface ExamService {
 
     List<Exam> findBySchoolIdAndTypeAndStatus(Integer schoolId, ExamType type);
 
-    List<Exam> findBySchoolIdAndStatus(Integer schoolId);
+    List<Exam> findBySchoolId(Integer schoolId);
 
     Exam findBySchoolAndCode(Integer schoolId, String code);
 

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

@@ -103,8 +103,7 @@ public interface ExamStudentService {
     public Long countByExamIdAndSubjectCodeAndCampus(Integer examId, String code, String campusName, boolean upload,
             boolean absent);
 
-    public long countByNoAbsentAndBreach(int examId, String subjectCode, boolean upload, boolean absent,
-            boolean breach);
+    public long countByNoAbsentAndBreach(int examId, String subjectCode, boolean upload, boolean absent, boolean breach);
 
     public long countByAbsentAndBreach(int examId, String subjectCode, Boolean absent, Boolean breach);
 
@@ -143,4 +142,6 @@ public interface ExamStudentService {
 
     public void updateSubjectiveStatusAndTimeAndInspectorId(Integer studentId, SubjectiveStatus status,
             Date inspectTime, Integer inspectorId);
+
+    public List<ExamStudent> findByExamIdAndStudentCode(int examId, String studentCode);
 }

+ 13 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ImportQueryService.java

@@ -0,0 +1,13 @@
+package cn.com.qmth.stmms.biz.exam.service;
+
+import cn.com.qmth.stmms.biz.exam.model.ImportQuery;
+import cn.com.qmth.stmms.common.enums.ImportType;
+
+public interface ImportQueryService {
+
+    ImportQuery save(ImportQuery i);
+
+    void deleteByExamIdAndUserIdAndType(int examId, Integer userId, ImportType type);
+
+    ImportQuery findByExamIdAndUserIdAndType(int examId, Integer userId, ImportType type);
+}

+ 17 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/InspectedService.java

@@ -0,0 +1,17 @@
+package cn.com.qmth.stmms.biz.exam.service;
+
+import java.util.List;
+
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
+public interface InspectedService {
+
+    List<ExamStudent> findByQuery(ExamStudentSearchQuery query, SubjectiveStatus status, Integer mainNumber,
+            Double mainStartScore, Double mainEndScore, Double questionScore);
+
+    Integer countByQuery(ExamStudentSearchQuery query, SubjectiveStatus status, Integer mainNumber,
+            Double mainStartScore, Double mainEndScore, Double questionScore);
+
+}

+ 2 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamServiceImpl.java

@@ -125,8 +125,8 @@ public class ExamServiceImpl extends BaseQueryService<Exam> implements ExamServi
     }
 
     @Override
-    public List<Exam> findBySchoolIdAndStatus(Integer schoolId) {
-        return examDao.findBySchoolIdAndStatusOrderByIdDesc(schoolId, ExamStatus.START);
+    public List<Exam> findBySchoolId(Integer schoolId) {
+        return examDao.findBySchoolIdOrderByIdDesc(schoolId);
     }
 
     @Override

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

@@ -667,7 +667,7 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
     @Override
     public ExamStudent findBySchoolIdAndSubjectCodeAndStudentCode(Integer schoolId, String subjectCode,
             String studentCode) {
-        List<Exam> exams = examService.findBySchoolIdAndStatus(schoolId);
+        List<Exam> exams = examService.findBySchoolId(schoolId);
         if (exams != null && exams.size() > 0) {
             return studentDao.findByExamIdAndSubjectCodeAndStudentCode(exams.get(0).getId(), subjectCode, studentCode);
         } else {
@@ -1024,4 +1024,9 @@ public class ExamStudentServiceImpl extends BaseQueryService<ExamStudent> implem
             Date inspectTime, Integer inspectorId) {
         studentDao.updateSubjectiveStatusAndTimeAndInspectorId(studentId, status, inspectTime, inspectorId);
     }
+
+    @Override
+    public List<ExamStudent> findByExamIdAndStudentCode(int examId, String studentCode) {
+        return studentDao.findByExamIdAndStudentCode(examId, studentCode);
+    }
 }

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

@@ -0,0 +1,39 @@
+package cn.com.qmth.stmms.biz.exam.service.impl;
+
+import java.util.Date;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.stmms.biz.common.BaseQueryService;
+import cn.com.qmth.stmms.biz.exam.dao.ImportQueryDao;
+import cn.com.qmth.stmms.biz.exam.model.ImportQuery;
+import cn.com.qmth.stmms.biz.exam.service.ImportQueryService;
+import cn.com.qmth.stmms.common.enums.ImportType;
+
+@Service
+public class ImportQueryServiceImpl extends BaseQueryService<ImportQuery> implements ImportQueryService {
+
+    @Autowired
+    private ImportQueryDao queryDao;
+
+    @Transactional
+    @Override
+    public ImportQuery save(ImportQuery i) {
+        i.setCreateTime(new Date());
+        return queryDao.save(i);
+    }
+
+    @Transactional
+    @Override
+    public void deleteByExamIdAndUserIdAndType(int examId, Integer userId, ImportType type) {
+        queryDao.deleteByExamIdAndUserIdAndType(examId, userId, type);
+    }
+
+    @Override
+    public ImportQuery findByExamIdAndUserIdAndType(int examId, Integer userId, ImportType type) {
+        return queryDao.findByExamIdAndUserIdAndType(examId, userId, type);
+    }
+
+}

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

@@ -0,0 +1,136 @@
+package cn.com.qmth.stmms.biz.exam.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.stmms.biz.common.BaseQueryService;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.InspectedService;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
+@Service
+public class InspectedServiceImpl extends BaseQueryService<ExamStudent> implements InspectedService {
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<ExamStudent> findByQuery(ExamStudentSearchQuery query, SubjectiveStatus status, Integer mainNumber,
+            Double mainStartScore, Double mainEndScore, Double questionScore) {
+        StringBuilder dataSql = new StringBuilder("select distinct s.id "
+                + " from eb_exam_student s left join eb_subjective_score e on e.student_id = s.id ");
+        StringBuilder limitSql = new StringBuilder(" limit :offset,:pageSize");
+        Query dataQuery = getQuery(query, status, mainNumber, mainStartScore, mainEndScore, questionScore, dataSql,
+                limitSql);
+        dataQuery.setParameter("offset", (query.getPageNumber() - 1) * query.getPageSize());
+        dataQuery.setParameter("pageSize", query.getPageSize());
+        List<Integer> list = dataQuery.getResultList();
+        List<ExamStudent> resultList = new ArrayList<ExamStudent>();
+        if (list != null && !list.isEmpty()) {
+            for (Integer id : list) {
+                ExamStudent e = studentService.findById(id);
+                resultList.add(e);
+            }
+        }
+        return resultList;
+
+    }
+
+    private Query getQuery(ExamStudentSearchQuery query, SubjectiveStatus status, Integer mainNumber,
+            Double mainStartScore, Double mainEndScore, Double questionScore, StringBuilder dataSql,
+            StringBuilder limitSql) {
+        StringBuilder whereSql = new StringBuilder(" WHERE s.is_upload = 1 and s.is_absent = 0 and s.is_breach = 0 ");
+        if (query.getExamId() != null) {
+            whereSql.append(" and s.exam_id = :examId");
+        }
+        if (StringUtils.isNotBlank(query.getSubjectCode())) {
+            whereSql.append(" and s.subject_code = :subjectCode");
+        }
+        if (status != null) {
+            whereSql.append(" and s.subjective_status = :status");
+        } else {
+            whereSql.append(" and s.subjective_status IN ('MARKED','INSPECTED')");
+        }
+        if (query.getStartScore() != null) {
+            whereSql.append(" and (s.objective_score+s.subjective_score) >= :startScore");
+        }
+        if (query.getEndScore() != null) {
+            whereSql.append(" and (s.objective_score+s.subjective_score) <= :endScore");
+        }
+        if (mainNumber != null) {
+            whereSql.append(" and e.main_number = :mainNumber");
+        }
+        if (mainStartScore != null) {
+            whereSql.append(" and e.main_score >= :mainStartScore");
+        }
+        if (mainEndScore != null) {
+            whereSql.append(" and e.main_score <= :mainEndScore");
+        }
+        if (questionScore != null) {
+            whereSql.append(" and e.score = :questionScore");
+        }
+        dataSql.append(whereSql);
+        // StringBuilder orderSql = new StringBuilder("order by s.id desc ");
+        // dataSql.append(orderSql);
+        if (limitSql != null) {
+            dataSql.append(limitSql);
+        }
+        Query dataQuery = entityManager.createNativeQuery(dataSql.toString());
+
+        if (query.getExamId() != null) {
+            dataQuery.setParameter("examId", query.getExamId());
+        }
+        if (StringUtils.isNotBlank(query.getSubjectCode())) {
+            dataQuery.setParameter("subjectCode", query.getSubjectCode());
+        }
+        if (status != null) {
+            dataQuery.setParameter("status", status.toString());
+        }
+        if (query.getStartScore() != null) {
+            dataQuery.setParameter("startScore", query.getStartScore());
+        }
+        if (query.getEndScore() != null) {
+            dataQuery.setParameter("endScore", query.getEndScore());
+        }
+        if (mainNumber != null) {
+            dataQuery.setParameter("mainNumber", mainNumber);
+        }
+        if (mainStartScore != null) {
+            dataQuery.setParameter("mainStartScore", mainStartScore);
+        }
+        if (mainEndScore != null) {
+            dataQuery.setParameter("mainEndScore", mainEndScore);
+        }
+        if (questionScore != null) {
+            dataQuery.setParameter("questionScore", questionScore);
+        }
+        return dataQuery;
+    }
+
+    @Override
+    public Integer countByQuery(ExamStudentSearchQuery query, SubjectiveStatus status, Integer mainNumber,
+            Double mainStartScore, Double mainEndScore, Double questionScore) {
+        StringBuilder countSql = new StringBuilder("select count(distinct s.id) "
+                + "from eb_exam_student s left join eb_subjective_score e on e.student_id = s.id ");
+        Query countQuery = getQuery(query, status, mainNumber, mainStartScore, mainEndScore, questionScore, countSql,
+                null);
+        Object singleResult = countQuery.getResultList().get(0);
+        Integer count = singleResult == null ? 0 : Integer.valueOf(singleResult.toString());
+        return count;
+    }
+
+}

+ 9 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/MarkLibraryDao.java

@@ -30,8 +30,8 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     @Query("select l1 from MarkLibrary l1 where l1.examId=?1 and l1.subjectCode=?2 and l1.groupNumber=?3 and l1.status in (?6) "
             + "and not exists (select l2 from MarkLibrary l2 where l2.studentId=l1.studentId and l2.id!=l1.id and l2.markerId=?4) "
             + "and exists (select mc.id from MarkerClass mc, ExamStudent s where l1.studentId=s.id and mc.userId=?5 and s.className=mc.className)")
-    List<MarkLibrary> findUnMarkedFilterClass(Integer examId, String subjectCode, Integer groupNumber,
-            Integer markerId, Integer userId, Set<LibraryStatus> statusSet, Pageable page);
+    List<MarkLibrary> findUnMarkedFilterClass(Integer examId, String subjectCode, Integer groupNumber, Integer markerId,
+            Integer userId, Set<LibraryStatus> statusSet, Pageable page);
 
     List<MarkLibrary> findByMarkerId(Integer markerId);
 
@@ -53,8 +53,8 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     long countByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer groupNumber);
 
     @Query("select count(f) from MarkLibrary f where f.examId=?1 and f.subjectCode=?2 and f.groupNumber=?3 and f.taskNumber=?4")
-    long countByExamIdAndSubjectCodeAndGroupNumberAndTaskNumber(Integer examId, String subjectCode,
-            Integer groupNumber, Integer taskNumber);
+    long countByExamIdAndSubjectCodeAndGroupNumberAndTaskNumber(Integer examId, String subjectCode, Integer groupNumber,
+            Integer taskNumber);
 
     @Query("select count(f) from MarkLibrary f where f.examId=?1 and f.subjectCode=?2 and f.groupNumber=?3 and f.status in (?4)")
     long countByExamIdAndSubjectCodeAndGroupNumberAndStatus(Integer examId, String subjectCode, Integer groupNumber,
@@ -69,6 +69,9 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     @Query("select count(f) 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 count(f) from MarkLibrary f where f.id=?1 and f.status=?2")
+    long countByIdAndStatus(Integer id, LibraryStatus status);
+
     @Modifying(clearAutomatically = true)
     @Query("update MarkLibrary m set m.status=?4, m.markerId=null, m.markerTime=null, m.markerScore=null, m.markerScoreList=null, m.markerSpent=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")
@@ -111,8 +114,8 @@ public interface MarkLibraryDao extends JpaRepository<MarkLibrary, Integer>, Jpa
     @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")
-    void updateHeaderResult(Integer studentId, Integer groupNumber, Integer userId, Double totalScore,
-            String scoreList, Date updateTime, LibraryStatus arbitrated);
+    void updateHeaderResult(Integer studentId, Integer groupNumber, Integer userId, Double totalScore, String scoreList,
+            Date updateTime, LibraryStatus arbitrated);
 
     @Modifying(clearAutomatically = true)
     @Query("update MarkLibrary m set m.status=?3 where m.studentId=?1 and m.groupNumber=?2")

+ 44 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/SubmitResult.java

@@ -0,0 +1,44 @@
+package cn.com.qmth.stmms.biz.mark.model;
+
+/**
+ * 评卷提交结果状态
+ */
+public class SubmitResult {
+
+    private boolean success;
+
+    private MarkLibrary markLibrary;
+
+    private TrialLibrary trialLibrary;
+
+    public static SubmitResult faile() {
+        return new SubmitResult(false, null, null);
+    }
+
+    public static SubmitResult success(MarkLibrary library) {
+        return new SubmitResult(true, library, null);
+    }
+
+    public static SubmitResult success(TrialLibrary library) {
+        return new SubmitResult(true, null, library);
+    }
+
+    private SubmitResult(boolean success, MarkLibrary markLibrary, TrialLibrary trialLibrary) {
+        this.success = success;
+        this.markLibrary = markLibrary;
+        this.trialLibrary = trialLibrary;
+    }
+
+    public boolean isSuccess() {
+        return success;
+    }
+
+    public MarkLibrary getMarkLibrary() {
+        return markLibrary;
+    }
+
+    public TrialLibrary getTrialLibrary() {
+        return trialLibrary;
+    }
+
+}

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

@@ -1,5 +1,15 @@
 package cn.com.qmth.stmms.biz.mark.service.Impl;
 
+import java.util.Date;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
@@ -10,19 +20,9 @@ import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
-import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
+import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.util.Date;
-import java.util.List;
 
 /**
  * 与评卷相关的所有定时任务
@@ -76,8 +76,7 @@ public class MarkCronService {
     @Scheduled(cron = "${mark.cleanTaskSchedule}")
     public void cronCleanTask() {
         try {
-            FormalTaskUtil.clearTimeoutTask(timeoutMinute);
-            TrialTaskUtil.clearTimeoutTask(timeoutMinute);
+            TaskLockUtil.clearTimeoutTask(timeoutMinute * 60 * 1000);
         } catch (Exception e) {
             log.error("CronCleanTask error", e);
         }
@@ -123,11 +122,11 @@ public class MarkCronService {
         // 获取主观题总分大于0的科目
         List<ExamSubject> subjects = subjectService.list(examId, 0);
         for (ExamSubject subject : subjects) {
-            List<MarkGroup> groups = groupService
-                    .findByExamAndSubjectAndStatus(examId, subject.getCode(), MarkStatus.FORMAL);
+            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subject.getCode(),
+                    MarkStatus.FORMAL);
             for (MarkGroup markGroup : groups) {
-                List<Marker> markers = markerService
-                        .findByExamAndSubjectAndGroup(examId, subject.getCode(), markGroup.getNumber());
+                List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subject.getCode(),
+                        markGroup.getNumber());
                 for (Marker marker : markers) {
                     try {
                         if (markService.needUpdateQuality(marker, markerActiveExpireMinute)) {
@@ -147,8 +146,8 @@ public class MarkCronService {
         List<ExamSubject> subjects = subjectService.listSubjectiveScore(examId, 0d);
         for (ExamSubject subject : subjects) {
             // 清除缺考考生和违纪考生
-            List<ExamStudent> list = studentService
-                    .findAbsentOrBreachLibraryStudent(subject.getExamId(), subject.getCode());
+            List<ExamStudent> list = studentService.findAbsentOrBreachLibraryStudent(subject.getExamId(),
+                    subject.getCode());
             if (list != null) {
                 for (ExamStudent student : list) {
                     try {
@@ -163,16 +162,15 @@ public class MarkCronService {
             }
             // 处理正常考生
             // 生成试评任务
-            List<MarkGroup> groups = groupService
-                    .findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(), MarkStatus.TRIAL);
+            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
+                    MarkStatus.TRIAL);
             for (MarkGroup group : groups) {
                 buildTrialLibrary(group);
             }
             // 生成正评任务
             // 试评状态的分组也提前生成
-            groups = groupService
-                    .findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(), MarkStatus.FORMAL,
-                            MarkStatus.TRIAL);
+            groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
+                    MarkStatus.FORMAL, MarkStatus.TRIAL);
             for (MarkGroup group : groups) {
                 buildFormalLibrary(subject, group);
             }
@@ -182,17 +180,16 @@ public class MarkCronService {
     private void buildFormalLibrary(ExamSubject subject, MarkGroup group) {
         Date lastBuildTime = group.getBuildTime();
         int count = 0;
-        ExamStudent student = studentService
-                .findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(), lastBuildTime);
+        ExamStudent student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(),
+                group.getNumber(), lastBuildTime);
         while (student != null) {
             // 尝试构造评卷任务
             try {
                 lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
                 lockService.watch(LockType.STUDENT, student.getId());
                 // 上锁后重复验证分组状态
-                if (!groupService
-                        .validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(), MarkStatus.FORMAL,
-                                MarkStatus.TRIAL)) {
+                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                        MarkStatus.FORMAL, MarkStatus.TRIAL)) {
                     break;
                 }
                 // 上锁后重复验证考生状态
@@ -202,15 +199,16 @@ public class MarkCronService {
                     count++;
                 }
             } catch (Exception e) {
-                log.error("build formal library error for studentId=" + student.getId() + ", groupNumber=" + group
-                        .getNumber(), e);
+                log.error(
+                        "build formal library error for studentId=" + student.getId() + ", groupNumber="
+                                + group.getNumber(), e);
             } finally {
                 lockService.unwatch(LockType.STUDENT, student.getId());
                 lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
             }
             // 取下一个考生
-            student = studentService
-                    .findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(), lastBuildTime);
+            student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(),
+                    lastBuildTime);
         }
         // 有新任务创建,同时正评状态,此时才需要更新任务数量
         if (count > 0) {
@@ -225,8 +223,8 @@ public class MarkCronService {
     private void buildTrialLibrary(MarkGroup group) {
         while (group.getTrialCount() > group.getLibraryCount()) {
             // 随机取一个未生成试评任务的考生
-            ExamStudent student = studentService
-                    .randomUnTrialStudent(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            ExamStudent student = studentService.randomUnTrialStudent(group.getExamId(), group.getSubjectCode(),
+                    group.getNumber());
             if (student == null) {
                 return;
             }
@@ -246,8 +244,9 @@ public class MarkCronService {
                     markService.updateMarkedCount(group);
                 }
             } catch (Exception e) {
-                log.error("build trial library error for studentId=" + student.getId() + ", groupNumber=" + group
-                        .getNumber(), e);
+                log.error(
+                        "build trial library error for studentId=" + student.getId() + ", groupNumber="
+                                + group.getNumber(), e);
             } finally {
                 lockService.unwatch(LockType.STUDENT, student.getId());
                 lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());

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

@@ -8,11 +8,11 @@ import cn.com.qmth.stmms.biz.mark.dao.*;
 import cn.com.qmth.stmms.biz.mark.model.*;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
 import cn.com.qmth.stmms.biz.mark.service.MarkService;
-import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
 import cn.com.qmth.stmms.biz.utils.ScoreItem;
-import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
+import cn.com.qmth.stmms.biz.utils.TaskLock;
+import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
 import cn.com.qmth.stmms.common.enums.*;
-
+import cn.com.qmth.stmms.common.utils.BigDecimalUtils;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,11 +21,7 @@ import org.springframework.data.domain.Sort;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -99,9 +95,11 @@ public class MarkServiceImpl implements MarkService {
     public int applyCount(MarkGroup group) {
         int count = 0;
         if (group.getStatus() == MarkStatus.TRIAL) {
-            count = TrialTaskUtil.count(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(group));
+            count = taskLock.count();
         } else if (group.getStatus() == MarkStatus.FORMAL) {
-            count = FormalTaskUtil.count(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(group));
+            count = taskLock.count();
         }
         return count;
     }
@@ -121,9 +119,11 @@ public class MarkServiceImpl implements MarkService {
         }
         if (group != null) {
             if (group.getStatus() == MarkStatus.FORMAL) {
-                return FormalTaskUtil.count(marker);
+                TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
+                return taskLock.count(marker.getId());
             } else if (group.getStatus() == MarkStatus.TRIAL) {
-                return TrialTaskUtil.count(marker);
+                TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+                return taskLock.count(marker.getId());
             }
         }
         return 0;
@@ -162,9 +162,11 @@ public class MarkServiceImpl implements MarkService {
     @Override
     public void releaseByGroup(MarkGroup group) {
         if (group.getStatus() == MarkStatus.TRIAL) {
-            TrialTaskUtil.clear(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(group));
+            taskLock.clear();
         } else if (group.getStatus() == MarkStatus.FORMAL) {
-            FormalTaskUtil.clear(group.getExamId(), group.getSubjectCode(), group.getNumber());
+            TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(group));
+            taskLock.clear();
         }
     }
 
@@ -275,12 +277,19 @@ public class MarkServiceImpl implements MarkService {
     @Override
     public boolean applyLibrary(MarkLibrary library, Marker marker) {
         // 查询待领取任务时,已经做了多评同一studentId互斥处理
-        // 首先判断多评情况下,同一个studentId是否已被该评卷员处理过
-        // if (libraryDao.countByStudentIdAndMarkerId(library.getStudentId(),
-        // marker.getId()) > 0) {
-        // return false;
-        // }
-        return FormalTaskUtil.add(marker, getApplyTaskId(library));
+        TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
+        boolean lock = taskLock.add(library.getStudentId(), library.getTaskNumber(), marker.getId());
+        // 上锁失败直接返回
+        if (!lock) {
+            return false;
+        }
+        // 重复校验任务状态
+        if (libraryDao.countByIdAndStatus(library.getId(), library.getStatus()) == 1) {
+            return true;
+        } else {
+            taskLock.remove(library.getStudentId(), library.getTaskNumber(), marker.getId());
+            return false;
+        }
     }
 
     /**
@@ -294,7 +303,8 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     public boolean applyLibrary(TrialLibrary library, Marker marker) {
-        return TrialTaskUtil.add(marker, getApplyTaskId(library, marker));
+        TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+        return taskLock.add(getApplyTaskId(library, marker), 1, marker.getId());
     }
 
     /**
@@ -308,7 +318,8 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     public boolean hasApplied(MarkLibrary library, Marker marker) {
-        return FormalTaskUtil.exists(marker, getApplyTaskId(library));
+        TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
+        return taskLock.exist(library.getStudentId(), library.getTaskNumber(), marker.getId());
     }
 
     /**
@@ -322,33 +333,29 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     public boolean hasApplied(TrialLibrary library, Marker marker) {
-        return TrialTaskUtil.exists(marker, getApplyTaskId(library, marker));
-    }
-
-    /**
-     * 释放某个评卷员已领取的正评任务
-     *
-     * @param library
-     *            - 正评任务
-     * @param marker
-     *            - 评卷员
-     */
-    @Override
-    public void releaseLibrary(MarkLibrary library, Marker marker) {
-        FormalTaskUtil.remove(marker, getApplyTaskId(library));
+        TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+        return taskLock.exist(getApplyTaskId(library, marker), 1, marker.getId());
     }
 
     /**
-     * 释放某个评卷员已领取的试评任务
+     * 释放某个评卷员已完成的评卷任务
      *
-     * @param library
-     *            - 试评任务
+     * @param result
+     *            - 评卷结果
      * @param marker
      *            - 评卷员
      */
     @Override
-    public void releaseLibrary(TrialLibrary library, Marker marker) {
-        TrialTaskUtil.remove(marker, getApplyTaskId(library, marker));
+    public void releaseTask(SubmitResult result, Marker marker) {
+        if (result.getMarkLibrary() != null) {
+            TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
+            taskLock.remove(result.getMarkLibrary().getStudentId(), result.getMarkLibrary().getTaskNumber(),
+                    marker.getId());
+            taskLock.refresh(marker.getId());
+        } else if (result.getTrialLibrary() != null) {
+            TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
+            taskLock.remove(getApplyTaskId(result.getTrialLibrary(), marker), 1, marker.getId());
+        }
     }
 
     /**
@@ -359,8 +366,8 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     public void releaseByMarker(Marker marker) {
-        FormalTaskUtil.clear(marker);
-        TrialTaskUtil.clear(marker);
+        TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
+        taskLock.clear(marker.getId());
     }
 
     /**
@@ -463,12 +470,12 @@ public class MarkServiceImpl implements MarkService {
      */
     @Override
     @Transactional
-    public boolean submitTask(MarkResult result, Marker marker) {
+    public SubmitResult submitTask(MarkResult result, Marker marker) {
         // 判断评卷分组是否存在/评卷是否结束
         MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
         if (group == null || group.getStatus() == MarkStatus.FINISH
                 || group.getStatus().getValue() != result.getStatusValue()) {
-            return false;
+            return SubmitResult.faile();
         }
 
         // 根据评卷状态选择读取不同的评卷任务
@@ -481,8 +488,8 @@ public class MarkServiceImpl implements MarkService {
                         result.getSpent(), LibraryStatus.WAITING, LibraryStatus.MARKED, LibraryStatus.INSPECTED) != 0) {
                     saveProblemHistory(result, library);
                     updateMarkedCount(group);
-                    releaseLibrary(library, marker);
-                    return true;
+                    // releaseLibrary(library, marker);
+                    return SubmitResult.success(library);
                 }
             }
             if (library != null && library.getExamId().equals(group.getExamId())
@@ -491,8 +498,8 @@ public class MarkServiceImpl implements MarkService {
                     && result.getTotalScore() <= group.getTotalScore() && StringUtils.isNotBlank(result.getScoreList())) {
                 if (submitLibrary(library, marker, group, result)) {
                     updateMarkedCount(group);
-                    releaseLibrary(library, marker);
-                    return true;
+                    // releaseLibrary(library, marker);
+                    return SubmitResult.success(library);
                 }
             }
         } else if (group.getStatus() == MarkStatus.TRIAL) {
@@ -523,12 +530,12 @@ public class MarkServiceImpl implements MarkService {
                         trialTagDao.save(result.getTagList(history));
                     }
                     updateMarkedCount(group);
-                    releaseLibrary(library, marker);
-                    return true;
+                    // releaseLibrary(library, marker);
+                    return SubmitResult.success(library);
                 }
             }
         }
-        return false;
+        return SubmitResult.faile();
     }
 
     private void saveProblemHistory(MarkResult result, MarkLibrary library) {
@@ -814,7 +821,7 @@ public class MarkServiceImpl implements MarkService {
         // 循环所有主观得分明细
         List<SubjectiveScore> list = scoreDao.findByStudentId(studentId);
         for (SubjectiveScore ss : list) {
-            totalScore += ss.getScore();
+            totalScore = BigDecimalUtils.add(totalScore, ss.getScore());
             scoreList.add(new ScoreItem(ss));
             List<SubjectiveScore> mainScoreList = map.get(ss.getMainNumber());
             if (mainScoreList == null) {
@@ -828,7 +835,7 @@ public class MarkServiceImpl implements MarkService {
             List<SubjectiveScore> mainScoreList = map.get(mainNumber);
             double mainScore = 0.0;
             for (SubjectiveScore subjectiveScore : mainScoreList) {
-                mainScore += subjectiveScore.getScore();
+                mainScore = BigDecimalUtils.add(mainScore, subjectiveScore.getScore());
             }
             for (SubjectiveScore subjectiveScore : mainScoreList) {
                 subjectiveScore.setMainScore(mainScore);
@@ -898,7 +905,7 @@ public class MarkServiceImpl implements MarkService {
                         try {
                             ScoreItem item = detail.get(i);
                             ScoreItem other = scores.get(i);
-                            item.setScore(item.getScore() + other.getScore());
+                            item.setScore(BigDecimalUtils.add(item.getScore(), other.getScore()));
                         } catch (Exception e) {
                             continue;
                         }
@@ -1088,17 +1095,6 @@ public class MarkServiceImpl implements MarkService {
         }
     }
 
-    /**
-     * 领取正式评卷任务时,用来区分的唯一标识<br/>
-     * 多评时同一个考生的多份任务不能被同一位评卷员领取
-     *
-     * @param library
-     *            - 正评任务
-     */
-    private String getApplyTaskId(MarkLibrary library) {
-        return library.getStudentId() + "_" + library.getGroupNumber();
-    }
-
     /**
      * 领取试评评卷任务时,用来区分的唯一标识
      *
@@ -1109,6 +1105,14 @@ public class MarkServiceImpl implements MarkService {
         return library.getId() + "_" + marker.getId();
     }
 
+    private String getGroupKey(MarkGroup group) {
+        return group.getExamId() + "_" + group.getSubjectCode() + "_" + group.getNumber();
+    }
+
+    private String getGroupKey(Marker marker) {
+        return marker.getExamId() + "_" + marker.getSubjectCode() + "_" + marker.getGroupNumber();
+    }
+
     /**
      * 重置评卷分组的连带操作
      *

+ 5 - 16
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/MarkService.java

@@ -1,10 +1,7 @@
 package cn.com.qmth.stmms.biz.mark.service;
 
 import cn.com.qmth.stmms.biz.exam.model.*;
-import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
-import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
-import cn.com.qmth.stmms.biz.mark.model.MarkResult;
-import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
+import cn.com.qmth.stmms.biz.mark.model.*;
 import cn.com.qmth.stmms.common.enums.ScorePolicy;
 import cn.com.qmth.stmms.common.enums.ThirdPolicy;
 
@@ -13,20 +10,12 @@ import java.util.List;
 public interface MarkService {
 
     /**
-     * 释放某个评卷员已领取的正评任务
+     * 释放某个评卷员已完成的评卷任务
      *
-     * @param library
-     * @param marker
-     */
-    void releaseLibrary(MarkLibrary library, Marker marker);
-
-    /**
-     * 释放某个评卷员已领取的试评任务
-     *
-     * @param library
+     * @param result
      * @param marker
      */
-    void releaseLibrary(TrialLibrary library, Marker marker);
+    void releaseTask(SubmitResult result, Marker marker);
 
     /**
      * /** 释放某个大题的锁定任务
@@ -198,7 +187,7 @@ public interface MarkService {
      * @param marker
      * @return
      */
-    boolean submitTask(MarkResult result, Marker marker);
+    SubmitResult submitTask(MarkResult result, Marker marker);
 
     /**
      * 管理员/组长重置某个试评任务

+ 0 - 52
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/AbstractTask.java

@@ -1,52 +0,0 @@
-package cn.com.qmth.stmms.biz.utils;
-
-import java.util.Date;
-
-import org.apache.log4j.Logger;
-
-public abstract class AbstractTask implements Runnable {
-
-    protected static final Logger logger = Logger.getLogger(AbstractTask.class);
-
-    private Date start;
-
-    private Date finish;
-
-    private String status;
-
-    public AbstractTask() {
-        status("等待开始");
-    }
-
-    @Override
-    public void run() {
-        logger.info("运行中");
-        start = new Date();
-        status("运行中");
-
-        process();
-
-        status("任务结束");
-        finish = new Date();
-        logger.info("任务结束");
-    }
-
-    protected abstract void process();
-
-    protected void status(String status) {
-        this.status = status;
-    }
-
-    public Date getStart() {
-        return start;
-    }
-
-    public Date getFinish() {
-        return finish;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-}

+ 0 - 34
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/BlockTaskCountUtil.java

@@ -1,34 +0,0 @@
-package cn.com.qmth.stmms.biz.utils;
-
-import org.springframework.dao.DataAccessException;
-import org.springframework.data.redis.connection.RedisConnection;
-import org.springframework.data.redis.core.RedisCallback;
-import org.springframework.data.redis.core.StringRedisTemplate;
-
-import cn.com.qmth.stmms.common.redis.RedisKeyBuilder;
-
-public class BlockTaskCountUtil {
-
-    public static void incrBlockTotalCount(StringRedisTemplate redisTemplate, final int blockId) {
-        redisTemplate.execute(new RedisCallback<Long>() {
-
-            @Override
-            public Long doInRedis(RedisConnection conn) throws DataAccessException {
-                try {
-                    conn.incr(RedisKeyBuilder.getBlockTotalCountKey(blockId).getBytes("UTF-8"));
-                } catch (Exception e) {
-                }
-                return 0l;
-            }
-        });
-    }
-
-    public static void addBlockMarkedTask(StringRedisTemplate redisTemplate, final int blockId, final int libraryId) {
-        redisTemplate.opsForSet().add(RedisKeyBuilder.getBlockMarkedTaskKey(blockId), String.valueOf(libraryId));
-    }
-
-    public static int getBlockMarkedTaskCount(StringRedisTemplate redisTemplate, final int blockId) {
-        Long count = redisTemplate.opsForSet().size(RedisKeyBuilder.getBlockMarkedTaskKey(blockId));
-        return count != null ? count.intValue() : 0;
-    }
-}

+ 0 - 197
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/CurrentTaskUtil2.java

@@ -1,197 +0,0 @@
-package cn.com.qmth.stmms.biz.utils;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import cn.com.qmth.stmms.biz.exam.model.Marker;
-
-/**
- * Redis zset替代方案,单JVM内部针对已领取未给分任务的管理工具
- * 
- * @author luoshi
- * 
- */
-public class CurrentTaskUtil2 {
-
-    private static Map<String, TaskGroup> store = new HashMap<String, TaskGroup>();
-
-    /**
-     * 尝试领取某个任务
-     * 
-     * @param marker
-     * @param libraryId
-     * @return
-     */
-    public static boolean add(Marker marker, int libraryId) {
-        return getTaskMap(marker.getExamId(), marker.getSubjectCode()).add(libraryId, marker.getId());
-    }
-
-    /**
-     * 删除某个已完成的任务
-     * 
-     * @param marker
-     * @param libraryId
-     */
-    public static void remove(Marker marker, int libraryId) {
-        getTaskMap(marker.getExamId(), marker.getSubjectCode()).remove(libraryId);
-    }
-
-    /**
-     * 某个科目已领取未给分的任务总数
-     * 
-     * @param examId
-     * @param subjectCode
-     * @return
-     */
-    public static int count(int examId, String subjectCode) {
-        return getTaskMap(examId, subjectCode).count();
-    }
-
-    /**
-     * 某个评卷员已领取未给分的任务总数
-     * 
-     * @param marker
-     * @return
-     */
-    public static int count(Marker marker) {
-        return getTaskMap(marker.getExamId(), marker.getSubjectCode()).count(marker.getId());
-    }
-
-    /**
-     * 清除某个科目已领取未给分的任务
-     * 
-     * @param examId
-     * @param subjectCode
-     */
-    public static void clear(int examId, String subjectCode) {
-        getTaskMap(examId, subjectCode).clear();
-    }
-
-    /**
-     * 清除某个评卷员已领取未给分的任务
-     * 
-     * @param examId
-     * @param subjectCode
-     */
-    public static void clear(Marker marker) {
-        getTaskMap(marker.getExamId(), marker.getSubjectCode()).clear(marker.getId());
-    }
-
-    /**
-     * 获取某个科目的任务控制器
-     * 
-     * @param examId
-     * @param subjectCode
-     * @return
-     */
-    private static TaskGroup getTaskMap(int examId, String subjectCode) {
-        String key = examId + "_" + subjectCode;
-        TaskGroup group = store.get(key);
-        if (group == null) {
-            synchronized (CurrentTaskUtil2.class) {
-                group = store.get(key);
-                if (group == null) {
-                    group = new TaskGroup();
-                    store.put(key, group);
-                }
-            }
-        }
-        return group;
-    }
-}
-
-class TaskGroup {
-
-    // 任务-评卷员对应关系
-    private Map<Integer, Integer> taskStore;
-
-    // 评卷员-任务对应关系
-    private Map<Integer, Set<Integer>> markerStore;
-
-    private Lock lock;
-
-    public TaskGroup() {
-        this.taskStore = new HashMap<Integer, Integer>();
-        this.markerStore = new HashMap<Integer, Set<Integer>>();
-        this.lock = new ReentrantLock();
-    }
-
-    public boolean add(int libraryId, int markerId) {
-        if (taskStore.containsKey(libraryId)) {
-            return false;
-        }
-        lock.lock();
-        try {
-            if (taskStore.containsKey(libraryId)) {
-                return false;
-            }
-            taskStore.put(libraryId, markerId);
-            Set<Integer> set = markerStore.get(markerId);
-            if (set == null) {
-                set = new HashSet<Integer>();
-                markerStore.put(markerId, set);
-            }
-            set.add(libraryId);
-            return true;
-        } catch (Exception e) {
-            return false;
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    public void remove(int libraryId) {
-        lock.lock();
-        try {
-            Integer markerId = taskStore.get(libraryId);
-            if (markerId != null) {
-                markerStore.get(markerId).remove(libraryId);
-            }
-            taskStore.remove(libraryId);
-        } catch (Exception e) {
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    public int count() {
-        return taskStore.size();
-    }
-
-    public int count(int markerId) {
-        try {
-            return markerStore.get(markerId).size();
-        } catch (Exception e) {
-            return 0;
-        }
-    }
-
-    public void clear() {
-        lock.lock();
-        try {
-            taskStore.clear();
-            markerStore.clear();
-        } catch (Exception e) {
-        } finally {
-            lock.unlock();
-        }
-    }
-
-    public void clear(int markerId) {
-        lock.lock();
-        try {
-            Set<Integer> set = markerStore.get(markerId);
-            for (Integer id : set) {
-                taskStore.remove(id);
-            }
-            set.clear();
-        } catch (Exception e) {
-        } finally {
-            lock.unlock();
-        }
-    }
-}

+ 232 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLock.java

@@ -0,0 +1,232 @@
+package cn.com.qmth.stmms.biz.utils;
+
+import cn.com.qmth.stmms.common.utils.DateUtils;
+
+import java.util.*;
+
+/**
+ * 链表模式实现的任务互斥锁工具
+ */
+public class TaskLock {
+
+    // 固定头节点
+    private LockNode head;
+
+    // 总量计数
+    private int count;
+
+    public TaskLock() {
+        head = new LockNode(0, 0, 0);
+        count = 0;
+    }
+
+    public synchronized boolean add(Object id, int number, Object owner) {
+        if (head.next == null) {
+            head.append(id, number, owner);
+            count++;
+            return true;
+        } else {
+            LockNode node = head.next;
+            while (true) {
+                // id+number,只能被一个owner获取
+                if (node.isId(id) && node.isNumber(number)) {
+                    return false;
+                }
+                // id只能被一个owner领取一个number
+                else if (node.isId(id) && node.isOwner(owner)) {
+                    return false;
+                }
+                // 跳到下一个node
+                else if (node.hasNext()) {
+                    node = node.next;
+                }
+                // 可以领取
+                else {
+                    node.append(id, number, owner);
+                    count++;
+                    return true;
+                }
+            }
+        }
+    }
+
+    public synchronized boolean exist(Object id, int number, Object owner) {
+        LockNode node = head.next;
+        while (node != null) {
+            if (node.isId(id) && node.isNumber(number)) {
+                return node.isOwner(owner);
+            } else {
+                node = node.next;
+            }
+        }
+        return false;
+    }
+
+    public synchronized boolean remove(Object id, int number, Object owner) {
+        LockNode node = head.next;
+        while (node != null) {
+            if (node.isId(id) && node.isNumber(number)) {
+                if (node.isOwner(owner)) {
+                    node.remove();
+                    count--;
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                node = node.next;
+            }
+        }
+        return false;
+    }
+
+    public synchronized void clear() {
+        head.next = null;
+        count = 0;
+    }
+
+    public synchronized void clear(Object owner) {
+        LockNode node = head.next;
+        while (node != null) {
+            if (node.isOwner(owner)) {
+                node.remove();
+                count--;
+            }
+            node = node.next;
+        }
+    }
+
+    public synchronized void expire(long expireTime) {
+        long current = System.currentTimeMillis();
+        LockNode node = head.next;
+        while (node != null) {
+            if ((current - node.time) > expireTime) {
+                node.remove();
+                count--;
+            }
+            node = node.next;
+        }
+    }
+
+    public synchronized boolean refresh(Object id, int number, Object owner) {
+        LockNode node = head.next;
+        while (node != null) {
+            if (node.isId(id) && node.isNumber(number) && node.isOwner(owner)) {
+                node.time = System.currentTimeMillis();
+                return true;
+            }
+            node = node.next;
+        }
+        return false;
+    }
+
+    public synchronized void refresh(Object owner) {
+        LockNode node = head.next;
+        while (node != null) {
+            if (node.isOwner(owner)) {
+                node.time = System.currentTimeMillis();
+            }
+            node = node.next;
+        }
+    }
+
+    public int count() {
+        return count;
+    }
+
+    public int count(Object owner) {
+        int count = 0;
+        LockNode node = head.next;
+        while (node != null) {
+            if (node.isOwner(owner)) {
+                count++;
+            }
+            node = node.next;
+        }
+        return count;
+    }
+
+    public List<Map<String, Object>> list() {
+        List<Map<String, Object>> list = new LinkedList<>();
+        LockNode node = head.next;
+        while (node != null) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("taskId", node.getId());
+            map.put("number", node.getNumber());
+            map.put("markerId", node.getOwner());
+            map.put("time", DateUtils.formatDateTime(new Date(node.getTime())));
+            list.add(map);
+            node = node.next;
+        }
+        return list;
+    }
+
+    private static class LockNode {
+
+        private LockNode previous;
+
+        private LockNode next;
+
+        private Object id;
+
+        private int number;
+
+        private Object owner;
+
+        private long time;
+
+        LockNode(Object id, int number, Object owner) {
+            this.id = id;
+            this.number = number;
+            this.owner = owner;
+            this.time = System.currentTimeMillis();
+        }
+
+        private boolean hasNext() {
+            return this.next != null;
+        }
+
+        private boolean isId(Object id) {
+            return this.id.equals(id);
+        }
+
+        private boolean isNumber(int number) {
+            return this.number == number;
+        }
+
+        private boolean isOwner(Object owner) {
+            return this.owner.equals(owner);
+        }
+
+        private void remove() {
+            this.previous.next = this.next;
+            if (this.next != null) {
+                this.next.previous = this.previous;
+            }
+        }
+
+        private void append(Object id, int number, Object owner) {
+            LockNode node = new LockNode(id, number, owner);
+            this.next = node;
+            node.previous = this;
+        }
+
+        public Object getId() {
+            return id;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public Object getOwner() {
+            return owner;
+        }
+
+        public long getTime() {
+            return time;
+        }
+
+    }
+
+}

+ 43 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TaskLockUtil.java

@@ -0,0 +1,43 @@
+package cn.com.qmth.stmms.biz.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TaskLockUtil {
+
+    // 评卷任务并发处理互斥锁
+    private static final Map<String, TaskLock> formalTaskMap = new HashMap<>();
+
+    // 试评任务并发处理互斥锁
+    private static final Map<String, TaskLock> trialTaskMap = new HashMap<>();
+
+    public static void clearTimeoutTask(long timeoutMinute) {
+        for (TaskLock taskLock : trialTaskMap.values()) {
+            taskLock.expire(timeoutMinute);
+        }
+        for (TaskLock taskLock : formalTaskMap.values()) {
+            taskLock.expire(timeoutMinute);
+        }
+    }
+
+    public static TaskLock getTrialTask(String key) {
+        TaskLock taskLock = trialTaskMap.get(key);
+        if (taskLock == null) {
+            synchronized (trialTaskMap) {
+                taskLock = trialTaskMap.computeIfAbsent(key, k -> new TaskLock());
+            }
+        }
+        return taskLock;
+    }
+
+    public static TaskLock getFormalTask(String key) {
+        TaskLock taskLock = formalTaskMap.get(key);
+        if (taskLock == null) {
+            synchronized (formalTaskMap) {
+                taskLock = formalTaskMap.computeIfAbsent(key, k -> new TaskLock());
+            }
+        }
+        return taskLock;
+    }
+
+}

+ 0 - 206
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/TrialTaskUtil.java

@@ -1,206 +0,0 @@
-package cn.com.qmth.stmms.biz.utils;
-
-import java.text.ParseException;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multiset;
-import com.google.common.collect.SetMultimap;
-
-import cn.com.qmth.stmms.biz.exam.model.Marker;
-
-/**
- * Redis zset替代方案,单JVM内部针对已领取未给分<strong>试评</strong>任务的内存锁
- * 
- * @author luoshi
- * 
- */
-public class TrialTaskUtil {
-
-    private static SetMultimap<String, TaskEntry> taskMap = HashMultimap.create();
-
-    /**
-     * 尝试领取某个任务
-     * 
-     * @param marker
-     * @param taskId
-     * @return
-     */
-    public static boolean add(Marker marker, String taskId) {
-        String key = getKey(marker);
-        TaskEntry obj = new TaskEntry(marker.getId(), taskId);
-
-        synchronized (TrialTaskUtil.class) {
-            if (taskMap.containsEntry(key, obj)) {
-                return false;
-            } else {
-                taskMap.put(key, obj);
-                return true;
-            }
-        }
-    }
-
-    /**
-     * 是否已领取某个任务
-     * 
-     * @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.getTaskId().equals(taskId) && entry.getMarkerId() == marker.getId()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 删除某个已完成的任务
-     * 
-     * @param marker
-     * @param taskId
-     */
-    public static void remove(Marker marker, String taskId) {
-        TaskEntry obj = new TaskEntry(marker.getId(), taskId);
-        synchronized (TrialTaskUtil.class) {
-            taskMap.remove(getKey(marker), obj);
-        }
-    }
-
-    /**
-     * 某个科目已领取未给分的任务总数
-     * 
-     * @param examId
-     * @param subjectCode
-     * @return
-     */
-    public static int count(int examId, String subjectCode, int number) {
-        Set<TaskEntry> set = taskMap.get(getKey(examId, subjectCode, number));
-        return set != null ? set.size() : 0;
-    }
-
-    /**
-     * 某个评卷员已领取未给分的任务总数
-     * 
-     * @param marker
-     * @return
-     */
-    public static int count(Marker marker) {
-        Set<TaskEntry> set = taskMap.get(getKey(marker));
-        int count = 0;
-        if (set != null) {
-            for (TaskEntry obj : set) {
-                if (obj.getMarkerId() == marker.getId()) {
-                    count++;
-                }
-            }
-        }
-        return count;
-    }
-
-    /**
-     * 清除某个科目已领取未给分的任务
-     * 
-     * @param examId
-     * @param subjectCode
-     */
-    public static void clear(int examId, String subjectCode, int number) {
-        synchronized (TrialTaskUtil.class) {
-            taskMap.removeAll(getKey(examId, subjectCode, number));
-        }
-    }
-
-    /**
-     * 清除某个评卷员已领取未给分的任务
-     * 
-     * @param marker
-     */
-    public static void clear(Marker marker) {
-        String key = getKey(marker);
-        Set<TaskEntry> set = new HashSet<TaskEntry>();
-        synchronized (TrialTaskUtil.class) {
-            set.addAll(taskMap.get(key));
-            for (TaskEntry obj : set) {
-                if (obj.getMarkerId() == marker.getId()) {
-                    taskMap.remove(key, obj);
-                }
-            }
-        }
-    }
-
-    /**
-     * 获取当前已领取的任务快照
-     * 
-     * @param examId
-     * @param subjectCode
-     * @param groupNumber
-     */
-    public static Set<TaskEntry> list(Integer examId, String subjectCode, Integer groupNumber) {
-        return taskMap.get(getKey(examId, subjectCode, groupNumber));
-    }
-
-    private static String getKey(int examId, String subjectCode, int number) {
-        return examId + "_" + subjectCode + "_" + number;
-    }
-
-    private static String getKey(Marker marker) {
-        return getKey(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
-    }
-
-    public static void clearTimeoutTask(long timeoutMinute) {
-        synchronized (TrialTaskUtil.class) {
-            SetMultimap<String, TaskEntry> taskMap1 = HashMultimap.create();
-            // System.out.println("任务池大小:"+taskMap.size());
-            // System.out.println("间隔时间:"+cleanMapinterval);
-            if (taskMap.size() > 0) {
-                Multiset<String> keysTemp = taskMap.keys();
-                Set<String> setKey = new HashSet<String>();
-                for (String key : keysTemp) {
-                    setKey.add(key);
-                }
-                for (String key : setKey) {
-                    Set<TaskEntry> set = taskMap.get(key);
-                    if (set != null) {
-                        for (TaskEntry obj : set) {
-                            if (getDateDifference(obj.getTimestamp()) >= timeoutMinute) {// 如果相隔时间超过阀值,则该试卷的放入清空池中
-                                taskMap1.put(key, obj);
-                            }
-                        }
-                    }
-                }
-            }
-            for (Map.Entry<String, TaskEntry> taskEntry : taskMap1.entries()) {
-                taskMap.remove(taskEntry.getKey(), taskEntry.getValue());
-            }
-            taskMap1.clear();
-        }
-    }
-
-    /**
-     * 获取当前时间戳
-     * 
-     * @return
-     * @throws ParseException
-     */
-    public static Long getDateString() {
-        return System.currentTimeMillis();
-    }
-
-    /**
-     * 时间戳相隔分钟
-     * 
-     * @return
-     * @throws ParseException
-     */
-    public static Long getDateDifference(Long oldTime) {
-        return (getDateString() - oldTime) / (1000 * 60);
-    }
-}

+ 31 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ImportType.java

@@ -0,0 +1,31 @@
+package cn.com.qmth.stmms.common.enums;
+
+public enum ImportType {
+    INSPECTED("复核", 0);
+
+    private String name;
+
+    private int value;
+
+    private ImportType(String name, int value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public static ImportType findByValue(int value) {
+        for (ImportType c : ImportType.values()) {
+            if (c.getValue() == value) {
+                return c;
+            }
+        }
+        return null;
+    }
+}

+ 2 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectiveQuestionDTO.java

@@ -73,7 +73,7 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
                 : 0d);
         setScorePolicy(group != null && group.getScorePolicy() != null ? group.getScorePolicy().getValue()
                 : ScorePolicy.AVG.getValue());
-        setMarkMode(group != null && group.getMarkMode() != null ? group.getMarkMode().getName() : "");
+        setMarkMode(group != null && group.getMarkMode() != null ? group.getMarkMode().toString() : "");
         setTrialCount(group != null && group.getTrialCount() != null ? group.getTrialCount() : 0);
     }
 
@@ -87,7 +87,7 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
         question.setGroupNumber(groupNumber);
         question.setObjective(false);
         question.setTotalScore(totalScore);
-        question.setIntervalScore(intervalScore != null && intervalScore > 0 ? intervalScore : 1d);
+        question.setIntervalScore(intervalScore != null && intervalScore >= 0.1 ? intervalScore : 1d);
         question.setPicList(picList);
         question.setDoubleRate(doubleRate);
         question.setArbitrateThreshold(arbitrateThreshold);

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

@@ -53,7 +53,7 @@ public class BaseExamController extends BaseController {
     }
 
     protected List<Exam> getExamList(WebUser wu) {
-        return examService.findBySchoolIdAndStatus(wu.getUser().getSchoolId());
+        return examService.findBySchoolId(wu.getUser().getSchoolId());
     }
 
     protected List<ExamSubject> getExamSubject(Integer examId, WebUser wu) {

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

@@ -252,7 +252,7 @@ public class ExamController extends BaseExamController {
         if (wu.isSubjectHeader() || wu.isInspector()) {
             examList = examService.findBySubjectHeaderUserId(user.getSchoolId(), user.getId());
         } else {
-            examList = examService.findBySchoolIdAndStatus(user.getSchoolId());
+            examList = examService.findBySchoolId(user.getSchoolId());
         }
         model.addAttribute("examList", examList);
         SessionExamUtils.clearExamId(request);

+ 312 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ImportQueryController.java

@@ -0,0 +1,312 @@
+package cn.com.qmth.stmms.admin.exam;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import cn.com.qmth.stmms.admin.utils.PageUtil;
+import cn.com.qmth.stmms.admin.vo.InspectedStudentVO;
+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.ImportQuery;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.model.SubjectiveScore;
+import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
+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.ImportQueryService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.SubjectiveScoreService;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
+import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
+import cn.com.qmth.stmms.biz.utils.OriginTag;
+import cn.com.qmth.stmms.biz.utils.PictureTag;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.domain.WebUser;
+import cn.com.qmth.stmms.common.enums.ImportType;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import cn.com.qmth.stmms.common.utils.ImportExcel;
+import cn.com.qmth.stmms.common.utils.RequestUtils;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
+
+@Controller("importQueryController")
+@RequestMapping("/admin/exam/inspected/import")
+public class ImportQueryController extends BaseExamController {
+
+    protected static Logger log = LoggerFactory.getLogger(ImportQueryController.class);
+
+    @Autowired
+    private ExamStudentService studentService;
+
+    @Autowired
+    private ImportQueryService queryService;
+
+    @Autowired
+    private FileService fileService;
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Autowired
+    private SubjectiveScoreService scoreService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private MarkTrackService markTrackService;
+
+    @Autowired
+    private MarkSpecialTagService markSpecialTagService;
+
+    @RequestMapping("/list")
+    public String list(Model model, HttpServletRequest request, ExamStudentSearchQuery query) {
+        int examId = getSessionExamId(request);
+        WebUser wu = RequestUtils.getWebUser(request);
+        ImportQuery importQuery = queryService.findByExamIdAndUserIdAndType(examId, wu.getId(), ImportType.INSPECTED);
+        int inspectCount = 0;
+        if (importQuery != null) {
+            inspectCount = importQuery.getStudentCodeList().size() > 0 ? 0 : importQuery.getStudentIdList().size();
+        }
+        model.addAttribute("inspectCount", inspectCount);
+        if (inspectCount > 0) {
+            List<?> ids = PageUtil
+                    .startPage(importQuery.getStudentIdList(), query.getPageNumber(), query.getPageSize());
+            List<ExamStudent> list = new ArrayList<ExamStudent>();
+            if (ids != null) {
+                for (Object id : ids) {
+                    list.add(studentService.findById((int) id));
+                }
+            }
+            query.setTotalPage(PageUtil.pageCount(importQuery.getStudentIdList(), query.getPageNumber(),
+                    query.getPageSize()));
+            query.setTotalCount(inspectCount);
+            query.setResult(list);
+        }
+        model.addAttribute("query", query);
+        return "modules/exam/inspectedImportList";
+    }
+
+    @RequestMapping(value = "/template")
+    public String template(HttpServletResponse response, RedirectAttributes redirectAttributes) {
+        try {
+            String fileName = "复核名单导入模板.xlsx";
+            List<InspectedStudentVO> list = Lists.newArrayList();
+            list.add(new InspectedStudentVO());
+            new ExportExcel("复核名单", InspectedStudentVO.class, 2).setDataList(list).write(response, fileName).dispose();
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "导入模板下载失败!失败信息:" + e.getMessage());
+        }
+        return "redirect:/admin/exam/inspected";
+    }
+
+    @Logging(menu = "导入复核名单", type = LogType.IMPORT_FILE)
+    @RequestMapping(method = RequestMethod.POST)
+    public String importFile(HttpServletRequest request, MultipartFile file, RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+        WebUser wu = RequestUtils.getWebUser(request);
+        queryService.deleteByExamIdAndUserIdAndType(examId, wu.getId(), ImportType.INSPECTED);
+        try {
+            int successNum = 0;
+            int failureNum = 0;
+            Set<Integer> successStudent = new LinkedHashSet<Integer>();
+            Set<String> failureStudent = new HashSet<String>();
+            ImportExcel ei = new ImportExcel(file, 1, 0);
+            List<InspectedStudentVO> list = ei.getDataList(InspectedStudentVO.class);
+            if (list.size() > 1000) {
+                addMessage(redirectAttributes, "导入考生失败!数据超过1000行");
+                return "redirect:/admin/exam/inspected";
+            }
+            for (InspectedStudentVO studentVO : list) {
+                if (StringUtils.isBlank(studentVO.getStudentCode())) {
+                    continue;
+                }
+                List<ExamStudent> studentList = studentService.findByExamIdAndStudentCode(examId,
+                        studentVO.getStudentCode());
+                if (!studentList.isEmpty()) {
+                    for (ExamStudent examStudent : studentList) {
+                        successStudent.add(examStudent.getId());
+                    }
+                    successNum++;
+                } else {
+                    failureStudent.add(studentVO.getStudentCode());
+                    failureNum++;
+                }
+            }
+            ImportQuery importQuery = new ImportQuery(examId, wu.getId(), ImportType.INSPECTED, successStudent,
+                    failureStudent);
+            queryService.save(importQuery);
+            String message = "已成功导入 " + successNum + " 条,关联考生记录 " + successStudent.size() + " 条";
+            if (failureNum > 0) {
+                message = "失败 " + failureNum + " 条";
+            }
+            addMessage(redirectAttributes, message);
+            RequestUtils.setLog(request, message);
+        } catch (Exception e) {
+            log.error("Batch import inpectedStudent error!", e);
+            addMessage(redirectAttributes, "导入考生失败!失败信息:" + e.getMessage());
+            return "redirect:/admin/exam/inspected";
+        }
+        return "redirect:/admin/exam/inspected/import/list";
+    }
+
+    @Logging(menu = "复核名单重置", type = LogType.UPDATE)
+    @RequestMapping(value = "/reset")
+    public String reset(HttpServletRequest request, Model model, RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+        WebUser wu = RequestUtils.getWebUser(request);
+        queryService.deleteByExamIdAndUserIdAndType(examId, wu.getId(), ImportType.INSPECTED);
+        addMessage(redirectAttributes, "重置成功");
+        return "redirect:/admin/exam/inspected";
+    }
+
+    @RequestMapping(value = "/export")
+    public String export(HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) {
+        int examId = getSessionExamId(request);
+        WebUser wu = RequestUtils.getWebUser(request);
+        try {
+            String fileName = "错误信息.xlsx";
+            List<InspectedStudentVO> list = Lists.newArrayList();
+            ImportQuery importQuery = queryService.findByExamIdAndUserIdAndType(examId, wu.getId(),
+                    ImportType.INSPECTED);
+            for (String studentCode : importQuery.getStudentCodeList()) {
+                list.add(new InspectedStudentVO(studentCode));
+            }
+            new ExportExcel("错误信息", InspectedStudentVO.class, 2).setDataList(list).write(response, fileName).dispose();
+            return null;
+        } catch (Exception e) {
+            addMessage(redirectAttributes, "错误信息下载失败!失败信息:" + e.getMessage());
+        }
+        return "redirect:/admin/exam/inspected/import/list";
+    }
+
+    @RequestMapping("/start")
+    @ResponseBody
+    public ModelAndView start(HttpServletRequest request) {
+        int examId = getSessionExamId(request);
+        WebUser wu = RequestUtils.getWebUser(request);
+        ImportQuery importQuery = queryService.findByExamIdAndUserIdAndType(examId, wu.getId(), ImportType.INSPECTED);
+        int inspectCount = 0;
+        if (importQuery != null) {
+            inspectCount = importQuery.getStudentCodeList().size() > 0 ? 0 : importQuery.getStudentIdList().size();
+        }
+        ModelAndView view = new ModelAndView("modules/exam/inspectedImport");
+        view.addObject("inspectCount", inspectCount);
+        view.addObject("fileServer", fileService.getFileServer());
+        view.addObject("ids", inspectCount > 0 ? importQuery.getStudentIdString() : "");
+        view.addObject("message", inspectCount > 0 ? "" : "没有待复核的任务");
+        return view;
+    }
+
+    @RequestMapping("/startById")
+    public ModelAndView startById(HttpServletRequest request, RedirectAttributes redirectAttributes,
+            @RequestParam Integer studentId) {
+        ModelAndView view = new ModelAndView("modules/exam/inspectedImport");
+        List<Integer> ids = new ArrayList<Integer>();
+        ids.add(studentId);
+        view.addObject("inspectCount", ids.size());
+        view.addObject("fileServer", fileService.getFileServer());
+        view.addObject("ids", StringUtils.join(ids, ","));
+        view.addObject("studentId", studentId);
+        return view;
+    }
+
+    @RequestMapping("/info")
+    @ResponseBody
+    public Object info(HttpServletRequest request, @RequestParam Integer studentId) {
+        ExamStudent student = studentService.findById(studentId);
+        JSONObject result = new JSONObject();
+        result.accumulate("id", student.getId());
+        result.accumulate("secretNumber", student.getSecretNumber());
+        result.accumulate("studentCode", student.getStudentCode());
+        result.accumulate("name", student.getName());
+        result.accumulate("subjectCode", student.getSubjectCode());
+        result.accumulate("subjectName", student.getSubjectName());
+        result.accumulate("totalScore", student.getTotalScore());
+        result.accumulate("upload", student.isUpload());
+        result.accumulate("absent", student.isAbsent());
+        result.accumulate("breach", student.isBreach());
+        List<MarkGroup> groups = groupService.findByExamAndSubject(student.getExamId(), student.getSubjectCode());
+        JSONArray groupArray = new JSONArray();
+        Map<Integer, String> mainTitleMap = new HashMap<Integer, String>();
+        for (MarkGroup markGroup : groups) {
+            JSONObject group = new JSONObject();
+            group.accumulate("groupNumber", markGroup.getNumber());
+            List<SubjectiveScore> scores = scoreService.findByStudentIdAndGroupNumber(student.getId(),
+                    markGroup.getNumber());
+            JSONArray array = new JSONArray();
+            for (SubjectiveScore scoreItem : scores) {
+                JSONObject obj = new JSONObject();
+                String mainTitle = mainTitleMap.get(scoreItem.getMainNumber());
+                if (mainTitle == null) {
+                    ExamQuestion q = questionService.findByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(
+                            student.getExamId(), student.getSubjectCode(), false, scoreItem.getMainNumber(),
+                            scoreItem.getSubNumber());
+                    mainTitleMap.put(scoreItem.getMainNumber(), mainTitle);
+                    mainTitle = q.getMainTitle();
+                }
+                obj.accumulate("mainTitle", mainTitle);
+                obj.accumulate("questionNumber", scoreItem.getMainNumber() + "-" + scoreItem.getSubNumber());
+                obj.accumulate("score", scoreItem.getScore());
+                array.add(obj);
+            }
+            group.accumulate("questions", array);
+            groupArray.add(group);
+        }
+        result.accumulate("groups", groupArray);
+        List<String> picUrls = fileService.getSheetUris(student.getExamId(), student.getExamNumber(), 1,
+                student.getSheetCount());
+        result.accumulate("picUrls", picUrls);
+        Map<Integer, List<PictureTag>> maps = studentService.buildSheetTags(student, false);
+        List<OriginTag> tags = new ArrayList<OriginTag>();
+        for (Entry<Integer, List<PictureTag>> entry : maps.entrySet()) {
+            List<PictureTag> pictureTags = entry.getValue();
+            for (PictureTag pictureTag : pictureTags) {
+                if (pictureTag.getContent().length == 1) {
+                    tags.add(new OriginTag(pictureTag.getContent()[0], entry.getKey(), pictureTag.getLeft(), pictureTag
+                            .getTop()));
+                }
+            }
+        }
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            result.accumulate("tags", mapper.writeValueAsString(tags));
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+            log.error("MarkTrackController-轨迹坐标获取出错", e);
+        }
+        result.accumulate("success", true);
+        return result;
+    }
+}

+ 20 - 57
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/InspectedController.java

@@ -33,10 +33,10 @@ import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.model.SubjectiveScore;
 import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
-import cn.com.qmth.stmms.biz.exam.query.SubjectiveScoreSearchQuery;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
 import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.InspectedService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.SubjectiveScoreService;
 import cn.com.qmth.stmms.biz.file.service.FileService;
@@ -63,6 +63,9 @@ public class InspectedController extends BaseExamController {
     @Autowired
     private ExamStudentService studentService;
 
+    @Autowired
+    private InspectedService inspectedService;
+
     @Autowired
     private ExamQuestionService questionService;
 
@@ -105,36 +108,18 @@ public class InspectedController extends BaseExamController {
         }
         WebUser wu = RequestUtils.getWebUser(request);
         query.setExamId(examId);
-        query.setUpload(true);
-        query.setAbsent(false);
-        query.setBreach(false);
         List<ExamSubject> subjectList = getExamSubject(examId, wu);
         if (StringUtils.isBlank(query.getSubjectCode()) && !subjectList.isEmpty()) {
             query.setSubjectCode(subjectList.get(0).getCode());
         }
-        if (status != null) {
-            query.addStatus(status);
-        } else {
-            query.addStatus(SubjectiveStatus.INSPECTED);
-            query.addStatus(SubjectiveStatus.MARKED);
-        }
-        if (mainStartScore != null || mainEndScore != null || questionScore != null) {
-            SubjectiveScoreSearchQuery query2 = new SubjectiveScoreSearchQuery();
-            query2.setExamId(examId);
-            query2.setSubjectCode(query.getSubjectCode());
-            query2.setMainNumber(mainNumber);
-            query2.setMainStartScore(mainStartScore);
-            query2.setMainEndScore(mainEndScore);
-            query2.setScore(questionScore);
-            query2.setPageSize(Integer.MAX_VALUE);
-            List<Integer> studentIds = scoreService.findStudentIdByQuery(query2);
-            query.setStudentIds(studentIds);
-        }
-        if (query.getStudentIds() == null || query.getStudentIds().size() != 0) {
-            query = studentService.findByQuery(query);
-        }
+        List<ExamStudent> list = inspectedService.findByQuery(query, status, mainNumber, mainStartScore, mainEndScore,
+                questionScore);
+        Integer count = inspectedService.countByQuery(query, status, mainNumber, mainStartScore, mainEndScore,
+                questionScore);
+        query.setResult(list);
+        query.setTotalCount(count);
         model.addAttribute("query", query);
-        model.addAttribute("inspectCount", query.getTotalCount());
+        model.addAttribute("inspectCount", count);
         model.addAttribute("questionList",
                 questionService.findMainByExamAndSubjectAndObjective(examId, query.getSubjectCode(), false));
         model.addAttribute("mainNumber", mainNumber);
@@ -144,6 +129,7 @@ public class InspectedController extends BaseExamController {
         model.addAttribute("subjectList", subjectList);
         model.addAttribute("statusList", SubjectiveStatus.getOptionList());
         model.addAttribute("status", status);
+        model.addAttribute("exam", exam);
         return "modules/exam/inspectedList";
     }
 
@@ -159,33 +145,17 @@ public class InspectedController extends BaseExamController {
         releaseByUser(wu.getUser().getId());
         ModelAndView view = new ModelAndView("modules/exam/inspected");
         query.setExamId(examId);
-        query.setUpload(true);
-        query.setAbsent(false);
-        query.setBreach(false);
-        query.addStatus(SubjectiveStatus.MARKED);
+        query.setPageNumber(1);
         query.setPageSize(Integer.MAX_VALUE);
         List<ExamSubject> subjectList = getExamSubject(examId, wu);
         if (StringUtils.isBlank(query.getSubjectCode()) && !subjectList.isEmpty()) {
             query.setSubjectCode(subjectList.get(0).getCode());
         }
-        if (mainNumber != null || mainStartScore != null || mainEndScore != null || questionScore != null) {
-            SubjectiveScoreSearchQuery query2 = new SubjectiveScoreSearchQuery();
-            query2.setExamId(examId);
-            query2.setSubjectCode(query.getSubjectCode());
-            query2.setMainNumber(mainNumber);
-            query2.setMainStartScore(mainStartScore);
-            query2.setMainEndScore(mainEndScore);
-            query2.setScore(questionScore);
-            query2.setPageSize(Integer.MAX_VALUE);
-            List<Integer> studentIds = scoreService.findStudentIdByQuery(query2);
-            query.setStudentIds(studentIds);
-        }
-        if (query.getStudentIds() == null || query.getStudentIds().size() != 0) {
-            query = studentService.findByQuery(query);
-        }
+        List<ExamStudent> list = inspectedService.findByQuery(query, SubjectiveStatus.MARKED, mainNumber,
+                mainStartScore, mainEndScore, questionScore);
         List<Integer> ids = new ArrayList<Integer>();
-        if (query.getResult() != null && query.getResult().size() > 0) {
-            for (ExamStudent student : query.getResult()) {
+        if (!list.isEmpty()) {
+            for (ExamStudent student : list) {
                 ids.add(student.getId());
             }
         }
@@ -206,6 +176,7 @@ public class InspectedController extends BaseExamController {
         List<Integer> ids = new ArrayList<Integer>();
         ExamStudent student = studentService.findById(studentId);
         if (student != null && student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED)) {
+            releaseTask(studentId);
             ids.add(studentId);
         }
         view.addObject("inspectCount", ids.size());
@@ -225,6 +196,8 @@ public class InspectedController extends BaseExamController {
                 && student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED)) {
             result.accumulate("id", student.getId());
             result.accumulate("secretNumber", student.getSecretNumber());
+            result.accumulate("studentCode", student.getStudentCode());
+            result.accumulate("name", student.getName());
             result.accumulate("subjectCode", student.getSubjectCode());
             result.accumulate("subjectName", student.getSubjectName());
             result.accumulate("totalScore", student.getTotalScore());
@@ -257,19 +230,9 @@ public class InspectedController extends BaseExamController {
                 groupArray.add(group);
             }
             result.accumulate("groups", groupArray);
-            // MarkGroup group = groupService.findOne(student.getExamId(),
-            // student.getSubjectCode(),
-            // library.getGroupNumber());
             List<String> picUrls = fileService.getSliceUris(student.getExamId(), student.getSecretNumber(), 1,
                     student.getSliceCount());
-            // List<MarkTrack> markTracks =
-            // markTrackService.findByLibraryId(library.getId());
-            // List<MarkSpecialTag> markSpecialTagList =
-            // markSpecialTagService.findByLibraryId(library.getId());
             result.accumulate("picUrls", picUrls);
-            // result.accumulate("pictureConfig", group.getPictureConfigList());
-            // result.accumulate("markTracks", markTracks);
-            // result.accumulate("markSpecialTagList", markSpecialTagList);
             result.accumulate("success", true);
         } else {
             result.accumulate("success", false);

+ 1 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/admin/log/OperationLogController.java

@@ -38,7 +38,7 @@ public class OperationLogController extends BaseExamController {
         query.orderByCreateTime();
         query = logService.findByQuery(query);
         model.addAttribute("query", query);
-        model.addAttribute("examList", examService.findBySchoolIdAndStatus(wu.getUser().getSchoolId()));
+        model.addAttribute("examList", examService.findBySchoolId(wu.getUser().getSchoolId()));
         model.addAttribute("typeList", LogType.values());
         return "modules/log/logList";
     }

+ 64 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/utils/PageUtil.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.stmms.admin.utils;
+
+import java.util.List;
+
+public class PageUtil {
+
+    /**
+     * 开始分页
+     * 
+     * @param list
+     * @param pageNum
+     *            页码
+     * @param pageSize
+     *            每页多少条数据
+     * @return
+     */
+    public static List<?> startPage(List<?> list, Integer pageNum, Integer pageSize) {
+        if (list == null) {
+            return null;
+        }
+        if (list.size() == 0) {
+            return null;
+        }
+
+        Integer count = list.size(); // 记录总数
+        Integer pageCount = pageCount(list, pageNum, pageSize);
+
+        int fromIndex = 0; // 开始索引
+        int toIndex = 0; // 结束索引
+
+        if (pageCount < pageNum) {
+            return null;
+        }
+        if (pageCount - pageNum != 0) {
+            fromIndex = (pageNum - 1) * pageSize;
+            toIndex = fromIndex + pageSize;
+        } else {
+            fromIndex = (pageNum - 1) * pageSize;
+            toIndex = count;
+        }
+
+        List<?> pageList = list.subList(fromIndex, toIndex);
+
+        return pageList;
+    }
+
+    public static Integer pageCount(List<?> list, Integer pageNum, Integer pageSize) {
+        if (list == null) {
+            return 0;
+        }
+        if (list.size() == 0) {
+            return 0;
+        }
+        Integer count = list.size(); // 记录总数
+        Integer pageCount = 0; // 页数
+        if (count % pageSize == 0) {
+            pageCount = count / pageSize;
+        } else {
+            pageCount = count / pageSize + 1;
+        }
+
+        return pageCount;
+    }
+}

+ 25 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/vo/InspectedStudentVO.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.stmms.admin.vo;
+
+import cn.com.qmth.stmms.common.annotation.ExcelField;
+
+public class InspectedStudentVO {
+
+    @ExcelField(title = "学号", align = 2, sort = 10)
+    private String studentCode;
+
+    public InspectedStudentVO(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public InspectedStudentVO() {
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+}

+ 7 - 8
stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java

@@ -447,19 +447,18 @@ public class MarkController extends BaseController {
         try {
             lockService.watch(LockType.EXAM_SUBJECT, marker.getExamId(), marker.getSubjectCode());
             lockService.watch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
-            lockService.watch(LockType.STUDENT, markResult.getStudentId());
             lockService.watch(LockType.MARKER, marker.getId());
-            lockService.waitlock(LockType.GROUP_LIBRARY, markResult.getStudentId(), marker.getExamId(),
-                    marker.getSubjectCode(), marker.getGroupNumber());
-            success = markService.submitTask(markResult, marker);
+            lockService.waitlock(LockType.STUDENT, markResult.getStudentId());
+            SubmitResult sr = markService.submitTask(markResult, marker);
+            if (sr.isSuccess()) {
+                markService.releaseTask(sr, marker);
+                success = true;
+            }
         } catch (Exception e) {
-            success = false;
             log.error("save task error", e);
         } finally {
-            lockService.unlock(LockType.GROUP_LIBRARY, markResult.getStudentId(), marker.getExamId(),
-                    marker.getSubjectCode(), marker.getGroupNumber());
+            lockService.unlock(LockType.STUDENT, markResult.getStudentId());
             lockService.unwatch(LockType.MARKER, marker.getId());
-            lockService.unwatch(LockType.STUDENT, markResult.getStudentId());
             lockService.unwatch(LockType.GROUP, marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
             lockService.unwatch(LockType.EXAM_SUBJECT, marker.getExamId(), marker.getSubjectCode());
         }

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

@@ -1,8 +1,5 @@
 package cn.com.qmth.stmms.monitor;
 
-import java.util.Date;
-import java.util.Set;
-
 import javax.servlet.http.HttpServletRequest;
 
 import org.springframework.stereotype.Controller;
@@ -10,11 +7,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
-import cn.com.qmth.stmms.biz.utils.TaskEntry;
-import cn.com.qmth.stmms.common.utils.DateUtils;
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
+import cn.com.qmth.stmms.biz.utils.TaskLock;
+import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
 
 @Controller("taskMonitor")
 @RequestMapping("/monitor/task")
@@ -24,16 +18,8 @@ public class TaskMonitorController {
     @ResponseBody
     public Object list(HttpServletRequest request, @RequestParam Integer examId, @RequestParam String subjectCode,
             @RequestParam Integer groupNumber) {
-        Set<TaskEntry> set = FormalTaskUtil.list(examId, subjectCode, groupNumber);
-        JSONArray array = new JSONArray();
-        for (TaskEntry entry : set) {
-            JSONObject obj = new JSONObject();
-            obj.accumulate("taskId", entry.getTaskId());
-            obj.accumulate("markerId", entry.getMarkerId());
-            obj.accumulate("time", DateUtils.formatDateTime(new Date(entry.getTimestamp())));
-            array.add(obj);
-        }
-        return array;
+        TaskLock taskLock = TaskLockUtil.getFormalTask(examId + "_" + subjectCode + "_" + groupNumber);
+        return taskLock.list();
     }
 
 }

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

@@ -130,7 +130,7 @@
                 <div class="controls">
                     <input name="picList" id="picList" type="hidden"/>
                     <a href="${ctx}/admin/exam/getSheetConfig?examId=${exam.id}" target="_blank" class="required"
-                       id="configuration">设置</a>
+                       id="configuration" rel="opener">设置</a>
                 </div>
             </div>
         </c:if>

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

@@ -98,7 +98,7 @@
             <label class="control-label">图片显示</label>
             <div class="controls">
                 <a href="${ctx}/admin/exam/group/getPictureConfig?subjectCode=${group.subjectCode}&number=${group.number}"
-                   target="_blank" id="configuration">设置</a>
+                   target="_blank" id="configuration" rel="opener">设置</a>
             </div>
         </div>
     </c:if>

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

@@ -158,7 +158,7 @@
         var fill = true;
         $('.interval-score-input').each(function () {
             var score = $(this).val();
-            if (score == '') {
+            if (score == '' || score<0.1) {
                 fill = false;
             } else {
                 array.push(score);
@@ -168,7 +168,7 @@
             $('#intervalScoreList').val(array.join(','));
             $('#inputForm').submit();
         } else {
-            alert('间隔分不能为空');
+            alert('间隔分不能为空且大于等于0.1分');
         }
     });
 </script>

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

@@ -0,0 +1,241 @@
+<%@ page language="java" pageEncoding="utf-8" %>
+<%@ include file="/WEB-INF/views/include/taglib.jsp" %>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>云阅卷</title>
+    <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
+    <link rel="stylesheet" href="${ctxStatic}/inspected/css/style.css"/>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.min.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery-ui.min.js "></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.mousewheel.min.js"></script>
+    
+    <script type="text/javascript" src="${ctxStatic}/answer-check/js/common.js"></script>
+    
+    <link rel="stylesheet" href="${ctxStatic}/iviewer/jquery.iviewer.css"/>
+    <script type="text/javascript" src="${ctxStatic}/iviewer/jquery.iviewer.js"></script>
+    
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery-ui.min.js"></script>
+    <script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.mousewheel.min.js"></script>
+    
+    <script type="text/javascript" src="${ctxStatic}/utils/image-utils.js"></script>
+
+</head>
+<body>
+<div style="height: 100vh;">
+    <div class="c-top cl">
+        <div class="z">
+            <span><b>学号:</b><span id="studentCode"></span></span>
+            <span><b>姓名:</b><span id="studentName"></span></span>
+            <span><b>科目:</b><span id="subject"></span></span>
+            <span><b>待复核:</b><span id="progress">${inspectCount}</span></span>
+        </div>
+        <div class="y">
+            <span><a class="user" href="">${web_user.name}</a></span>
+            <span><a class="close" href="javascript:window.close()">关闭</a></span>
+        </div>
+    </div>
+    <div class="c-cont cl">
+        <div class="mn">
+            <div id="image-holder-track" class="image-content" style="position: relative; "></div>
+        </div>
+        <div class="sd">
+            <div class="c-button pre-button">
+            	<a href="#" id="pre-button">上一个</a>
+            	<span></span>
+            	<a href="#" id="next-button">下一个</a>
+            </div>
+            <div class="c-theadlist">
+                <h1>试卷总分:<span id="totalScore"></span></h1>
+                <div id="question-content">
+                    <div class="mt">
+                        <table cellpadding="0" cellspacing="0" width="100%">
+                            <!-- <thead>
+                                <tr>
+                                    <td>题号</td>
+                                    <td>给分</td>
+                                    <td class="td"><a href="" id="back-button">打回</a></td>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                <tr>
+                                    <td>1</td>
+                                    <td>2</td>
+                                    <td class="td"></td>
+                                </tr>
+                            </tbody> -->
+                        </table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+</body>
+<script type="text/javascript">
+    var message = '${message}';
+    var ids = [${ids}];
+    var studentId = '${studentId}';
+    var current;
+    var student;
+    var iviewer;
+    var regex = /^[a-z]+$/ig;
+    $(document).ready(function () {
+        if (message != undefined && message != '') {
+            alert(message);
+            return;
+        }
+
+        $('#pre-button').click(pre);
+        $('#next-button').click(next);
+
+        if (ids.length == 0) {
+            $(".cont cl").css("display", "none");
+            return;
+        }
+        $(".tipsbox").css("display", "none");
+
+        window.onbeforeunload = function () {
+            $.post('${ctx}/admin/exam/inspected/clear', {
+                studentId: student.id
+            }, function (result) {
+            });
+        }
+		if(studentId){
+			$.post('${ctx}/admin/exam/inspected/clear', {
+                studentId: studentId
+            }, function (result) {
+            });
+		}
+		
+        $('#progress').html(ids.length);
+        process(1);
+    });
+
+    function process(index) {
+
+        if (index < 1) {
+            return;
+        }
+        if (index > ids.length) {
+            alert('所有考生已处理完毕,请返回重新搜索');
+            window.close();
+            return;
+        }
+        current = index;
+        $('#answer-content').empty();
+        $('#image-holder-track').hide();
+        $.post('${ctx}/admin/exam/inspected/import/info', {
+            studentId: ids[index - 1]
+        }, function (result) {
+            if (result.success == true) {
+                student = result;
+                render();
+            } else {
+                //$('#progress').html(ids.length-current);
+                process(current + 1);
+            }
+        }).error(function () {
+            alert('获取考生信息出错');
+            onProcessFinish(true);
+        });
+    }
+
+    function onProcessFinish(error) {
+        if (!error) {
+            $('#save-button').removeAttr("disabled");
+        }
+    }
+
+    function render() {
+        $('#studentCode').html(student.studentCode);
+        $('#studentName').html(student.name);
+        $('#subject').html(student.subjectCode + '-' + student.subjectName);
+        $('#totalScore').html(student.totalScore);
+        $("#question-content").empty();
+		if(!student.upload){
+			alert("该考生未上传,请点击下一个");
+			return;
+		}
+		if(student.absent){
+			alert("该考生缺考,请点击下一个");
+			return;
+		}
+		if(student.breach){
+			alert("该考生违纪,请点击下一个");
+			var pane = $('<canvas id="track-builder-canvas"></canvas>').appendTo($('#image-holder-track'));
+			var canvas = document.getElementById('track-builder-canvas');
+			var ctx = canvas.getContext('2d');
+			buildImages('${fileServer}', student.picUrls, null, canvas, ctx, null);
+		    $('#image-holder-track').show();
+			return;
+		}
+		
+        for (var i = 0; i < student.groups.length; i++) {
+            var group = student.groups[i];
+            $('<div class="mt" id="div_' + group.groupNumber + '"></div>').appendTo("#question-content");
+
+            $('<table id="table_' + group.groupNumber + '" cellpadding="0" cellspacing="0" width="100%"></table>').appendTo("#div_" + group.groupNumber);
+
+            $("<thead><tr><td>分组" + group.groupNumber + "</td><td>给分 &nbsp;</td></tr></thead>").appendTo("#table_" + group.groupNumber);
+            $('<tbody id="tbody_' + group.groupNumber + '"></tbody>').appendTo("#table_" + group.groupNumber);
+            for (var j = 0; j < group.questions.length; j++) {
+                var question = group.questions[j];
+                $("<tr><td>" +question.mainTitle+":"+ question.questionNumber + "</td><td>" + question.score + "</td></tr>").appendTo("#tbody_" + group.groupNumber);
+            }
+        }
+
+        var pane = $('<canvas id="track-builder-canvas"></canvas>').appendTo($('#image-holder-track'));
+        var canvas = document.getElementById('track-builder-canvas');
+        var ctx = canvas.getContext('2d');
+        var tags = student.tags;
+        buildImages('${fileServer}', student.picUrls, null, canvas, ctx, tags);
+        $('#image-holder-track').show();
+        onProcessFinish(false);
+    }
+
+    function next() {
+        if (student == undefined) {
+            return;
+        }
+		$('#progress').html(ids.length - current);
+		process(current + 1);
+    }
+    
+    function pre() {
+        if (student == undefined) {
+            return;
+        }
+        var index  = ids.length - current + 2;
+        if(index <= ids.length){
+			$('#progress').html(index);
+			process(current - 1);
+        }
+    }
+
+    function buildImages(imageServer, urls, config, canvas, ctx, tags) {
+    	canvas.style="width:100%;height:100%";
+        new ImageLoader({
+            server: imageServer,
+            flush: true,
+            strict: false,
+            split: [0,1]
+        }).combine(urls, canvas, [], function (layout) {
+            //阅卷轨迹
+            if (tags != undefined && tags.length > 0) {
+                ctx.font = "30px Arial";
+                ctx.fillStyle = 'red';
+                for (var j = 0; j < tags.length; j++) {
+                	var tag = tags[j];
+                    ctx.fillText(
+                        		tag.content,
+                        		tag.offsetX,
+                        		tag.offsetY+layout[tag.offsetIndex-1].top);
+                }
+            }
+        });
+    }
+
+</script>
+</html>

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

@@ -0,0 +1,103 @@
+<%@ 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/inspected/import/list" 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>
+        <input id="reset-button" class="btn btn-primary" type="button" value="重置" onclick="goReset()"/>
+        &nbsp;
+        <input id="export-button" class="btn" type="button" value="错误日志" onclick="goExport()"/>
+        &nbsp;
+        <input id="btnStart" class="btn" type="button" value="批量复核:${inspectCount }" onclick="goStart()"/>
+    </div>
+</form>
+<tags:message content="${message}"/>
+<table id="contentTable" class="table table-striped table-bordered table-condensed">
+    <thead>
+    <tr>
+        <th>科目</th>
+        <th>学号</th>
+        <th>试卷总分</th>
+        <th>得分明细</th>
+        <th>操作</th>
+    </tr>
+    </thead>
+    <tbody>
+    <c:forEach items="${query.result}" var="result">
+        <tr>
+            <td>${result.subjectCode}-${result.subjectName}</td>
+            <td>${result.studentCode}</td>
+            <td>${result.totalScore}</td>
+            <td>
+            	<c:choose>
+            		<c:when test="${result.absent == true || result.upload == false}">
+                        缺考
+                    </c:when>
+                     <c:when test="${result.breach == true}">
+                        违纪
+                    </c:when>
+                    <c:otherwise>
+                        ${result.subjectiveScoreList}
+                    </c:otherwise>
+            	</c:choose>
+            </td>
+            <td>
+            	<c:choose>
+            		<c:when test="${result.absent == true || result.upload == false}">
+                    </c:when>
+                    <c:otherwise>
+						<a target="_blank" href="${ctx}/admin/exam/inspected/import/startById?studentId=${result.id}">进入复核</a>
+                    </c:otherwise>
+            	</c:choose>
+            </td>
+        </tr>
+    </c:forEach>
+    </tbody>
+</table>
+<div class="pagination">${query}</div>
+<script type="text/javascript">
+    function page(n, s) {
+        $("#pageNumber").val(n);
+        $("#searchForm").attr('target', "_self");
+        $("#searchForm").attr('action', '${ctx}/admin/exam/inspected/import/list');
+        $("#searchForm").submit();
+        return false;
+    }
+    function goSearch() {
+        $("#pageNumber").val(1);
+        $("#searchForm").attr('target', "_self");
+        $("#searchForm").attr('action', '${ctx}/admin/exam/inspected/import/list');
+        $("#searchForm").submit();
+        return false;
+    }
+    function goStart() {
+    	$("#searchForm").attr('target', "_blank");
+        $("#searchForm").attr('action', '${ctx}/admin/exam/inspected/import/start');
+        $("#searchForm").submit();
+        return false;
+    }
+    function goExport() {
+        $("#searchForm").attr('action', '${ctx}/admin/exam/inspected/import/export');
+        $("#searchForm").submit();
+    }
+    function goReset() {
+    	$("#searchForm").attr('target', "");
+        $("#searchForm").attr('action', '${ctx}/admin/exam/inspected/import/reset');
+        $("#searchForm").submit();
+    }
+</script>
+</body>
+</html>

+ 21 - 7
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/inspectedList.jsp

@@ -11,6 +11,14 @@
     }</style>
 </head>
 <body>
+<div id="importBox" class="hide">
+    <form id="importForm" action="${ctx}/admin/exam/inspected/import" method="post" enctype="multipart/form-data"
+          style="padding-left:20px;text-align:center;" class="form-search" onsubmit="loading('正在导入,请稍等...');"><br/>
+        <input id="uploadFile" name="file" type="file" style="width:330px"/><br/><br/>  
+        <input id="btnImportSubmit" class="btn btn-primary" type="submit" value="   导    入   "/>
+        <a href="${ctx}/admin/exam/inspected/import/template">下载模板</a>
+    </form>
+</div>
 <form id="searchForm" action="${ctx}/admin/exam/inspected" 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 }"/>
@@ -56,7 +64,11 @@
         &nbsp;
         <input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
         &nbsp;
-         <input id="btnStart" class="btn" type="button" value="批量复核:${inspectCount }" onclick="goStart()"/>
+        <input id="btnStart" class="btn" type="button" value="批量复核:${inspectCount }" onclick="goStart()"/>
+        &nbsp;
+        <c:if test="${exam.status=='FINISH' && web_user.schoolAdmin==true}">
+       	<input id="btnImport" class="btn" type="button" value="导入"/>
+       	</c:if>
     </div>
 </form>
 <tags:message content="${message}"/>
@@ -82,12 +94,7 @@
             <td>${result.subjectiveScoreList}</td>
             <td>${result.inspectTime}</td>
             <td>
-                <c:if test="${examType=='MULTI_MEDIA'}">
-                    <a class="json-link" href="${ctx}/admin/exam/library/getJson?studentId=${result.id}" target="_blank">原图</a>
-                </c:if>
-                <c:if test="${examType!='MULTI_MEDIA'}">
-                    <a href="${ctx}/admin/exam/track/student/${result.id}" target="_blank">轨迹图</a>
-                </c:if>
+                <a href="${ctx}/admin/exam/track/student/${result.id}" target="_blank">轨迹图</a>
                 <c:if test="${result.subjectiveStatus=='MARKED'}">
 	                <a target="_blank" href="${ctx}/admin/exam/inspected/startById?studentId=${result.id}">进入复核</a>
                 </c:if>
@@ -146,6 +153,13 @@
                 parent.val(first).trigger('change');
             });
         });
+        
+        $("#btnImport").click(function () {
+            $.jBox($("#importBox").html(), {
+                title: "导入数据", buttons: {"关闭": true},
+                bottomText: "导入文件不能超过1000行,仅允许导入“xls”或“xlsx”格式文件!"
+            });
+        });
     });
 
     function page(n, s) {

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

@@ -43,7 +43,7 @@
             <label class="control-label">原图遮盖</label>
             <div class="controls">
                 <input name="picList" id="picList"  type="hidden"/>
-                <a href="${ctx}/admin/exam/getSheetConfig?examId=${subject.examId}&subjectCode=${subject.code}" target="_blank" class="required" id= "configuration">设置</a>
+                <a href="${ctx}/admin/exam/getSheetConfig?examId=${subject.examId}&subjectCode=${subject.code}" target="_blank" class="required" id= "configuration" rel="opener">设置</a>
             </div>
         </div>
 		<div class="form-actions">

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

@@ -41,7 +41,7 @@
     <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/problem-process.js"></script>
     <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/switch-group.js"></script>
 </head>
-<body>
+<body ondragstart="return false;" ondrop="return false;">
 <div class="container-fluid" id="container"></div>
 <script type="text/javascript">
     $(document).ajaxError(function (evt, req, settings) {

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

@@ -34,7 +34,7 @@
     <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/problem-process.js"></script>
     <script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/switch-group.js"></script>
 </head>
-<body>
+<body ondragstart="return false;" ondrop="return false;">
 <div class="container-fluid" id="container"></div>
 <script type="text/javascript">
     $(document).ajaxError(function (evt, req, settings) {

+ 20 - 1
stmms-web/src/main/webapp/sql/stmms_ft.sql

@@ -949,4 +949,23 @@ CREATE TABLE `s_range_subject`
     PRIMARY KEY (`id`),
     KEY `index1` (`exam_id`, `subject_code`)
 ) ENGINE = InnoDB
-  DEFAULT CHARSET = utf8mb4 COMMENT ='分段统计表';
+  DEFAULT CHARSET = utf8mb4 COMMENT ='分段统计表';
+  
+
+# Dump of table b_school
+# ------------------------------------------------------------
+
+DROP TABLE IF EXISTS `eb_import_query`;
+
+CREATE TABLE `eb_import_query` 
+(
+  `id` 			int(11) 	NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `create_time` datetime    NOT NULL COMMENT '创建时间',
+  `description` text 		DEFAULT NULL COMMENT '描述',
+  `exam_id` 	int(11) 	NOT NULL COMMENT '考试ID',
+  `type` 		varchar(16) NOT NULL COMMENT '类型',
+  `user_id` 	int(11)  	NOT NULL COMMENT '用户ID',
+  PRIMARY KEY (`id`),
+  KEY `index1` (`exam_id`, `user_id`, `type`)
+)  ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4 COMMENT ='导入查询表';

+ 6 - 1
stmms-web/src/main/webapp/static/inspected/css/style.css

@@ -159,7 +159,12 @@ a {
 .c-button a:hover {
 	background: #697B8D;
 }
-
+.pre-button{
+	display: flex;
+}
+.pre-button span{
+	width:30%;
+}
 
 
 

+ 5 - 1
stmms-web/src/main/webapp/static/mark-track/js/mark-control.js

@@ -708,6 +708,10 @@ function numberAdd(n1, n2) {
     return (n1 * 10 + n2 * 10) / 10;
 }
 
+function numberMinus(n1, n2) {
+    return (n1 * 10 - n2 * 10) / 10;
+}
+
 function getDom(content, markControl) {
     if (markControl != undefined && markControl.option.staticServer != undefined) {
         content = content.replace(/{staticServer}/g, markControl.option.staticServer);
@@ -746,4 +750,4 @@ function isArray(obj) {
 
 function getMessage(message) {
     return $.i18n.prop(message);
-}
+}

+ 3 - 3
stmms-web/src/main/webapp/static/mark-track/js/modules/mark-board.js

@@ -402,9 +402,9 @@ MarkBoard.prototype.updateStepScore = function (step) {
         if (totalScore == undefined) {
             totalScore = 0;
         }
-        totalScore += parseFloat(step.trackList[i].score);
+        totalScore = numberAdd(totalScore,step.trackList[i].score);
     }
-    var leftScore = totalScore != undefined ? (step.max - totalScore) : step.max;
+    var leftScore = totalScore != undefined ? numberMinus(step.max , totalScore) : step.max;
     for (var i = 0; i < step.scoreArray.length; i++) {
         var dom = step.scoreArray[i];
         dom.removeClass('selected');
@@ -438,7 +438,7 @@ MarkBoard.prototype.updateTotalScore = function () {
                 if (totalScore == '') {
                     totalScore = 0;
                 }
-                totalScore += this.stepList[i].markScore;
+                totalScore = numberAdd(totalScore,this.stepList[i].markScore);
             } else {
                 finish = false;
             }