瀏覽代碼

Merge branch 'dev_20201016' into dev_1.3.0

ting.yin 4 年之前
父節點
當前提交
9bbcb54aeb
共有 22 個文件被更改,包括 674 次插入88 次删除
  1. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/dao/ExamQuestionDao.java
  2. 41 11
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/DataSync.java
  3. 3 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/ExamQuestion.java
  4. 4 0
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroupStudent.java
  5. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScore.java
  6. 3 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScorePK.java
  7. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/ExamQuestionService.java
  8. 2 2
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/service/impl/ExamQuestionServiceImpl.java
  9. 3 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/model/MarkStepDTO.java
  10. 31 31
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreCalculateUtil.java
  11. 5 6
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreInfo.java
  12. 3 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreItem.java
  13. 34 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ExamSource.java
  14. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExamQuestionDTO.java
  15. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExceptionQuestionDTO.java
  16. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ObjectiveQuestionDTO.java
  17. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/SubjectiveQuestionDTO.java
  18. 5 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/DataSyncController.java
  19. 3 3
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/DataSyncThread.java
  20. 275 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/OnlineExamThread.java
  21. 243 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/utils/OnlineExamHttpUtil.java
  22. 1 2
      stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/CoreController.java

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

@@ -23,7 +23,7 @@ public interface ExamQuestionDao extends JpaRepository<ExamQuestion, Integer>, J
 
     @Query("select count(q) from ExamQuestion q where q.examId=?1 and q.subjectCode=?2 and q.objective=?3 and q.mainNumber=?4 and q.subNumber=?5")
     public long countByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber);
+            boolean objective, Integer mainNumber, String subNumber);
 
     // @Query("select q from ExamQuestion q where q.examId=?1 and
     // q.subjectCode=?2 and q.objective=?3 "
@@ -80,6 +80,6 @@ public interface ExamQuestionDao extends JpaRepository<ExamQuestion, Integer>, J
             boolean objective, Integer mainNumber, String paperType);
 
     public ExamQuestion findByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(Integer examId,
-            String subjectCode, boolean objective, Integer mainNumber, Integer subNumber);
+            String subjectCode, boolean objective, Integer mainNumber, String subNumber);
 
 }

+ 41 - 11
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/DataSync.java

@@ -5,11 +5,15 @@ import java.util.Date;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.Id;
 import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 
+import cn.com.qmth.stmms.common.enums.ExamSource;
+
 @Entity
 @Table(name = "eb_data_sync")
 public class DataSync implements Serializable {
@@ -22,6 +26,13 @@ public class DataSync implements Serializable {
     @Column(name = "school_id")
     private Integer schoolId;
 
+    /**
+     * 来源
+     */
+    @Column(name = "source", length = 16, nullable = false)
+    @Enumerated(EnumType.STRING)
+    private ExamSource source;
+
     @Temporal(TemporalType.TIMESTAMP)
     @Column(name = "create_time")
     private Date createTime;
@@ -30,7 +41,7 @@ public class DataSync implements Serializable {
     @Column(name = "update_time")
     private Date updateTime;
 
-    @Column(name = "root_org_id", nullable = false)
+    @Column(name = "root_org_id")
     private String rootOrgId;
 
     @Column(name = "cloud_exam_id", nullable = false)
@@ -42,18 +53,21 @@ public class DataSync implements Serializable {
     @Column(name = "next_id")
     private Long nextId;
 
-    @Column(name = "student_url", nullable = false)
-    private String studentUrl;
-
-    @Column(name = "app_id", nullable = false)
+    @Column(name = "app_id")
     private String appId;
 
-    @Column(name = "secret_key", nullable = false)
-    private String secretKey;
+    @Column(name = "access_key", nullable = true, length = 64)
+    private String accessKey;
+
+    @Column(name = "access_secret", nullable = true, length = 64)
+    private String accessSecret;
 
     @Column(name = "finished", nullable = false)
     private boolean finished;
 
+    @Column(name = "student_url", nullable = false)
+    private String studentUrl;
+
     @Column(name = "subject_url", nullable = false)
     private String subjectUrl;
 
@@ -156,12 +170,28 @@ public class DataSync implements Serializable {
         this.appId = appId;
     }
 
-    public String getSecretKey() {
-        return secretKey;
+    public ExamSource getSource() {
+        return source;
+    }
+
+    public void setSource(ExamSource source) {
+        this.source = source;
+    }
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getAccessSecret() {
+        return accessSecret;
     }
 
-    public void setSecretKey(String secretKey) {
-        this.secretKey = secretKey;
+    public void setAccessSecret(String accessSecret) {
+        this.accessSecret = accessSecret;
     }
 
     public String getSubjectPaperUrl() {

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

@@ -33,7 +33,7 @@ public class ExamQuestion implements Serializable {
     private Integer mainNumber;
 
     @Column(name = "sub_number", nullable = false)
-    private Integer subNumber;
+    private String subNumber;
 
     @Column(name = "main_title", nullable = false, length = 32)
     private String mainTitle;
@@ -126,11 +126,11 @@ public class ExamQuestion implements Serializable {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 4 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/MarkGroupStudent.java

@@ -5,7 +5,10 @@ import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
 import javax.persistence.Column;
 import javax.persistence.EmbeddedId;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.Table;
+
 import java.io.Serializable;
 
 /**
@@ -36,6 +39,7 @@ public class MarkGroupStudent implements Serializable {
      * 状态
      */
     @Column(name = "status", length = 16, nullable = false)
+    @Enumerated(EnumType.STRING)
     private SubjectiveStatus status;
 
     public MarkGroupStudent() {

+ 2 - 2
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScore.java

@@ -74,11 +74,11 @@ public class SubjectiveScore implements Serializable {
         this.pk.setMainNumber(mainNumber);
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return pk.getSubNumber();
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.pk.setSubNumber(subNumber);
     }
 

+ 3 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/SubjectiveScorePK.java

@@ -16,7 +16,7 @@ public class SubjectiveScorePK implements Serializable {
     private Integer mainNumber;
 
     @Column(name = "sub_number", nullable = false)
-    private Integer subNumber;
+    private String subNumber;
 
     public Integer getStudentId() {
         return studentId;
@@ -34,11 +34,11 @@ public class SubjectiveScorePK implements Serializable {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

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

@@ -49,14 +49,14 @@ public interface ExamQuestionService {
             boolean objective, Integer mainNumber, String paperType);
 
     long countByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber);
+            boolean objective, Integer mainNumber, String subNumber);
 
     ExamQuestion updateObjectivePolicy(Integer questionId, ObjectivePolicy objectivePolicy);
 
     ExamQuestion findById(Integer questionId);
 
     ExamQuestion findByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber);
+            boolean objective, Integer mainNumber, String subNumber);
 
     List<ExamQuestion> findMainByExamAndSubjectAndObjective(Integer examId, String subjectCode, boolean objective);
 

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

@@ -180,7 +180,7 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
 
     @Override
     public long countByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber) {
+            boolean objective, Integer mainNumber, String subNumber) {
         return questionDao.countByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(examId, subjectCode,
                 objective, mainNumber, subNumber);
     }
@@ -202,7 +202,7 @@ public class ExamQuestionServiceImpl extends BaseQueryService<ExamQuestion> impl
 
     @Override
     public ExamQuestion findByExamAndSubjectAndObjectiveAndMainNumberAndSubNumber(Integer examId, String subjectCode,
-            boolean objective, Integer mainNumber, Integer subNumber) {
+            boolean objective, Integer mainNumber, String subNumber) {
         return questionDao.findByExamIdAndSubjectCodeAndObjectiveAndMainNumberAndSubNumber(examId, subjectCode,
                 objective, mainNumber, subNumber);
     }

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

@@ -14,7 +14,7 @@ public class MarkStepDTO implements Serializable {
 
     private int mainNumber;
 
-    private int subNumber;
+    private String subNumber;
 
     private int number;
 
@@ -86,11 +86,11 @@ public class MarkStepDTO implements Serializable {
         this.mainNumber = mainNumber;
     }
 
-    public int getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(int subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 31 - 31
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreCalculateUtil.java

@@ -104,37 +104,37 @@ public class ScoreCalculateUtil {
         ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
 
         List<ExamQuestion> oList = new LinkedList<ExamQuestion>();
-        ExamQuestion q = new ExamQuestion();
-        q.setMainNumber(1);
-        q.setMainTitle("单选");
-        q.setSubNumber(1);
-        q.setAnswer("B");
-        q.setTotalScore(2d);
-        oList.add(q);
-
-        q = new ExamQuestion();
-        q.setMainNumber(1);
-        q.setMainTitle("单选");
-        q.setSubNumber(2);
-        q.setAnswer("B");
-        q.setTotalScore(2d);
-        oList.add(q);
-
-        q = new ExamQuestion();
-        q.setMainNumber(2);
-        q.setMainTitle("多选");
-        q.setSubNumber(1);
-        q.setAnswer("AC");
-        q.setTotalScore(3d);
-        oList.add(q);
-
-        q = new ExamQuestion();
-        q.setMainNumber(2);
-        q.setMainTitle("多选");
-        q.setSubNumber(2);
-        q.setAnswer("C");
-        q.setTotalScore(3d);
-        oList.add(q);
+        // ExamQuestion q = new ExamQuestion();
+        // q.setMainNumber(1);
+        // q.setMainTitle("单选");
+        // q.setSubNumber(1);
+        // q.setAnswer("B");
+        // q.setTotalScore(2d);
+        // oList.add(q);
+        //
+        // q = new ExamQuestion();
+        // q.setMainNumber(1);
+        // q.setMainTitle("单选");
+        // q.setSubNumber(2);
+        // q.setAnswer("B");
+        // q.setTotalScore(2d);
+        // oList.add(q);
+        //
+        // q = new ExamQuestion();
+        // q.setMainNumber(2);
+        // q.setMainTitle("多选");
+        // q.setSubNumber(1);
+        // q.setAnswer("AC");
+        // q.setTotalScore(3d);
+        // oList.add(q);
+        //
+        // q = new ExamQuestion();
+        // q.setMainNumber(2);
+        // q.setMainTitle("多选");
+        // q.setSubNumber(2);
+        // q.setAnswer("C");
+        // q.setTotalScore(3d);
+        // oList.add(q);
 
         ScoreInfo info = util.calculate(oList, null);
         System.out.println(info.getObjectiveScore());

+ 5 - 6
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreInfo.java

@@ -7,7 +7,7 @@ public class ScoreInfo {
 
     private int mainNumber;
 
-    private int subNumber;
+    private String subNumber;
 
     private String mainTitle;
 
@@ -21,7 +21,7 @@ public class ScoreInfo {
 
     public ScoreInfo() {
         this.mainNumber = 0;
-        this.subNumber = 0;
+        this.subNumber = "";
         this.objectiveScore = 0d;
         this.subjectiveScore = 0d;
         this.totalScore = 0d;
@@ -31,16 +31,15 @@ public class ScoreInfo {
     public int incrMainNumber(String title) {
         mainTitle = title;
         mainNumber++;
-        subNumber = 0;
+        subNumber = "";
         return mainNumber;
     }
 
-    public void addScore(String title, int number, double score, String answer, boolean objective) {
-        subNumber++;
+    public void addScore(String title, String number, double score, String answer, boolean objective) {
 
         ScoreItem item = new ScoreItem(objective);
         item.setMainNumber(mainNumber);
-        item.setSubNumber(number > 0 ? number : subNumber);
+        item.setSubNumber(number);
         item.setTitle(title != null ? title : mainTitle + "-" + item.getSubNumber());
         item.setAnswer(answer);
         item.setScore(score);

+ 3 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/utils/ScoreItem.java

@@ -12,7 +12,7 @@ public class ScoreItem {
 
     private int mainNumber;
 
-    private int subNumber;
+    private String subNumber;
 
     private String title;
 
@@ -41,11 +41,11 @@ public class ScoreItem {
         this.mainNumber = mainNumber;
     }
 
-    public int getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(int subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 34 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ExamSource.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.stmms.common.enums;
+
+public enum ExamSource {
+
+    EXAM_CLOUD("云平台", 1), ONLINE_EXAM("在线考试平台", 2);
+
+    private String name;
+
+    private int value;
+
+    private ExamSource(String name, int value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public static ExamSource findByValue(int value) {
+        ExamSource status = null;
+        for (ExamSource s : ExamSource.values()) {
+            if (s.getValue() == value) {
+                status = s;
+                break;
+            }
+        }
+        return status;
+    }
+}

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExamQuestionDTO.java

@@ -12,7 +12,7 @@ public class ExamQuestionDTO {
 
     private String mainTitle;
 
-    private Integer subNumber;
+    private String subNumber;
 
     private List<Double> scoreList;
 
@@ -26,11 +26,11 @@ public class ExamQuestionDTO {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ExceptionQuestionDTO.java

@@ -19,7 +19,7 @@ public class ExceptionQuestionDTO {
     private Integer mainNumber;
 
     @ExcelField(title = "小题号", align = 2, sort = 50)
-    private Integer subNumber;
+    private String subNumber;
 
     public ExceptionQuestionDTO(ExamQuestion question, ExamSubject subject) {
         setSubjectCode(question.getSubjectCode());
@@ -61,11 +61,11 @@ public class ExceptionQuestionDTO {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 }

+ 3 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/dto/ObjectiveQuestionDTO.java

@@ -26,7 +26,7 @@ public class ObjectiveQuestionDTO implements QuestionDTO {
     private Integer mainNumber;
 
     @ExcelField(title = "小题号(只能用小写数字)", align = 2, sort = 60)
-    private Integer subNumber;
+    private String subNumber;
 
     @ExcelField(title = "标准答案", align = 2, sort = 70)
     private String answer;
@@ -91,11 +91,11 @@ public class ObjectiveQuestionDTO implements QuestionDTO {
         this.mainNumber = mainNumber;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

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

@@ -25,7 +25,7 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
     private Integer mainNumber;
 
     @ExcelField(title = "小题号(只能用小写数字)", align = 2, sort = 50)
-    private Integer subNumber;
+    private String subNumber;
 
     @ExcelField(title = "小题满分", align = 2, sort = 60)
     private Double totalScore;
@@ -142,11 +142,11 @@ public class SubjectiveQuestionDTO implements QuestionDTO {
         this.title = title;
     }
 
-    public Integer getSubNumber() {
+    public String getSubNumber() {
         return subNumber;
     }
 
-    public void setSubNumber(Integer subNumber) {
+    public void setSubNumber(String subNumber) {
         this.subNumber = subNumber;
     }
 

+ 5 - 3
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/DataSyncController.java

@@ -9,6 +9,7 @@ import cn.com.qmth.stmms.biz.file.service.FileService;
 import cn.com.qmth.stmms.biz.lock.LockService;
 import cn.com.qmth.stmms.common.annotation.Logging;
 import cn.com.qmth.stmms.common.annotation.RoleRequire;
+import cn.com.qmth.stmms.common.enums.ExamSource;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.LogType;
 import cn.com.qmth.stmms.common.enums.Role;
@@ -107,11 +108,12 @@ public class DataSyncController extends BaseExamController {
             dataSync.setSubjectUrl(host + ":" + port + subjectUri);
             dataSync.setSubjectPaperUrl(host + ":" + port + subjectPaperUri);
             dataSync.setAppId(appId);
-            dataSync.setSecretKey(secretKey);
+            dataSync.setAccessKey(secretKey);
         }
         ModelAndView view = new ModelAndView("modules/exam/dataSync");
         view.addObject("running", lockService.isLocked(LockType.DATA_SYNC, examId));
         view.addObject("exam", examService.findById(examId));
+        view.addObject("sourceList", ExamSource.values());
         view.addObject("dataSync", dataSync);
         return view;
     }
@@ -157,7 +159,7 @@ public class DataSyncController extends BaseExamController {
     private List<SubjectiveQuestionDTO> getPaperStruct(DataSync sync) throws Exception {
         List<SubjectiveQuestionDTO> list = new ArrayList<SubjectiveQuestionDTO>();
 
-        HttpUtil subjectHttp = new HttpUtil(sync.getSubjectUrl(), sync.getSecretKey(), sync.getAppId(),
+        HttpUtil subjectHttp = new HttpUtil(sync.getSubjectUrl(), sync.getAccessKey(), sync.getAppId(),
                 sync.getRootOrgId());
         JSONObject datas = new JSONObject();
         datas.accumulate("examId", sync.getCloudExamId());
@@ -181,7 +183,7 @@ public class DataSyncController extends BaseExamController {
                 questionDTO.setSubjectName(subject.getString("subjectName"));
                 questionDTO.setMainNumber(question.getInt("mainNumber"));
                 questionDTO.setTitle(question.getString("mainTitle"));
-                questionDTO.setSubNumber(question.getInt("subNumber"));
+                questionDTO.setSubNumber(question.getString("subNumber"));
                 questionDTO.setTotalScore(question.getDouble("totalScore"));
                 list.add(questionDTO);
             }

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

@@ -86,11 +86,11 @@ public class DataSyncThread implements Runnable {
         this.subjectService = subjectService;
         this.fileService = fileService;
         this.pageSize = pageSize;
-        this.subjectHttp = new HttpUtil(dataSync.getSubjectUrl(), dataSync.getSecretKey(), dataSync.getAppId(),
+        this.subjectHttp = new HttpUtil(dataSync.getSubjectUrl(), dataSync.getAccessKey(), dataSync.getAppId(),
                 dataSync.getRootOrgId());
-        this.studentHttp = new HttpUtil(dataSync.getStudentUrl(), dataSync.getSecretKey(), dataSync.getAppId(),
+        this.studentHttp = new HttpUtil(dataSync.getStudentUrl(), dataSync.getAccessKey(), dataSync.getAppId(),
                 dataSync.getRootOrgId());
-        this.subjectPaperHttp = new HttpUtil(dataSync.getSubjectPaperUrl(), dataSync.getSecretKey(),
+        this.subjectPaperHttp = new HttpUtil(dataSync.getSubjectPaperUrl(), dataSync.getAccessKey(),
                 dataSync.getAppId(), dataSync.getRootOrgId());
     }
 

+ 275 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/thread/OnlineExamThread.java

@@ -0,0 +1,275 @@
+package cn.com.qmth.stmms.admin.thread;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.com.qmth.stmms.admin.utils.OnlineExamHttpUtil;
+import cn.com.qmth.stmms.biz.exam.model.DataSync;
+import cn.com.qmth.stmms.biz.exam.model.Exam;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.service.DataSyncService;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
+import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.file.enums.FormatType;
+import cn.com.qmth.stmms.biz.file.service.FileService;
+import cn.com.qmth.stmms.biz.lock.LockService;
+import cn.com.qmth.stmms.common.enums.LockType;
+import cn.com.qmth.stmms.common.enums.MarkMode;
+import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
+
+import com.aliyun.oss.common.utils.BinaryUtil;
+
+public class OnlineExamThread implements Runnable {
+
+    protected static Logger log = LoggerFactory.getLogger(OnlineExamThread.class);
+
+    private DataSync dataSync;
+
+    private ExamStudentService studentService;
+
+    private ExamSubjectService subjectService;
+
+    private ExamQuestionService questionService;
+
+    private MarkGroupService groupService;
+
+    private ExamService examService;
+
+    private FileService fileService;
+
+    private LockService lockService;
+
+    private DataSyncService dataSyncService;
+
+    private OnlineExamHttpUtil studentHttp;
+
+    private OnlineExamHttpUtil subjectPaperHttp;
+
+    public static final String PAPER_STRUCT = "paperStructList";
+
+    public static final String ID = "id";
+
+    public static final String EXAM_ID = "examId";
+
+    public static final String PAPER_ID = "paperId";
+
+    public static final String COURSE_CODE = "courseCode";
+
+    public static final String COURSE_NAME = "courseName";
+
+    public static final String ANSWERS = "answers";
+
+    public static final String RECORDS = "records";
+
+    public static final String START_ID = "examStudentIdGt";
+
+    public static final String DEFAULT_NULL = "#";
+
+    public static final String COUNT = "count";
+
+    public static final String FILTER = "filter";
+
+    public OnlineExamThread(DataSync dataSync, String host, LockService lockService, DataSyncService dataSyncService,
+            ExamService examService, ExamStudentService studentService, ExamSubjectService subjectService,
+            ExamQuestionService questionService, MarkGroupService groupService, FileService fileService) {
+        this.dataSync = dataSync;
+        this.lockService = lockService;
+        this.dataSyncService = dataSyncService;
+        this.examService = examService;
+        this.studentService = studentService;
+        this.subjectService = subjectService;
+        this.questionService = questionService;
+        this.groupService = groupService;
+        this.fileService = fileService;
+        this.studentHttp = new OnlineExamHttpUtil(dataSync.getAccessKey(), dataSync.getAccessSecret(), host,
+                dataSync.getStudentUrl());
+        this.subjectPaperHttp = new OnlineExamHttpUtil(dataSync.getAccessKey(), dataSync.getAccessSecret(), host,
+                dataSync.getSubjectPaperUrl());
+    }
+
+    @Override
+    public void run() {
+        log.info("start online data sync for examId=" + dataSync.getExamId());
+        try {
+            // 获取考试信息
+            Exam exam = examService.findById(dataSync.getExamId());
+            DataSync sync = dataSyncService.findByExamId(dataSync.getExamId());
+            if (sync == null) {
+                sync = dataSync;
+                sync.setCreateTime(new Date());
+                dataSyncService.save(sync);
+            }
+            Map<String, ExamSubject> subjectMap = new HashMap<String, ExamSubject>();
+            Map<String, Object> datas = new HashMap<String, Object>();
+            datas.put(EXAM_ID, dataSync.getCloudExamId());
+
+            // 获取考生
+            Long startId = 0L;
+            if (sync.getNextId() != null) {
+                startId = sync.getNextId();
+            }
+            while (startId != null) {
+                datas.put(START_ID, startId);
+                String studentResult = studentHttp.httpAction(null, datas);
+                JSONArray studentArray = JSONArray.fromObject(studentResult);
+                if (studentArray.size() == 0) {
+                    startId = null;
+                }
+                for (int i = 0; i < studentArray.size(); i++) {
+                    JSONObject student = studentArray.getJSONObject(i);
+                    String subjectName = student.getString(COURSE_NAME);
+                    JSONArray recordArray = student.getJSONArray(RECORDS);
+                    List<ExamStudent> list = new ArrayList<ExamStudent>();
+                    Date now = new Date();
+                    for (int j = 0; j < recordArray.size(); j++) {
+                        JSONObject record = recordArray.getJSONObject(j);
+                        // 保存科目试卷
+                        datas.put(ID, record.getString(PAPER_ID));
+                        datas.put(FILTER, "subjective");
+                        String paperResult = subjectPaperHttp.httpAction(null, datas);
+                        JSONObject paperJson = JSONObject.fromObject(paperResult);
+                        String subjectCode = paperJson.getString("paperId");
+                        String paper = paperJson.getString("details");
+                        byte[] paperData = paper.getBytes(StandardCharsets.UTF_8);
+                        fileService.uploadPaper(new ByteArrayInputStream(paperData), BinaryUtil.encodeMD5(paperData),
+                                exam.getId(), subjectCode, FormatType.JSON);
+                        this.saveSubject(subjectMap, exam.getId(), subjectCode, subjectName,
+                                JSONArray.fromObject(paper));
+                        // 保存考生数据
+                        ExamStudent examStudent = getExamStudent(exam, student, subjectCode, record.getString(ID), now);
+                        list.add(examStudent);
+                        // 保存考生作答
+                        String answerJson = record.getString(ANSWERS);
+                        byte[] jsonData = answerJson.getBytes(StandardCharsets.UTF_8);
+                        fileService.uploadJson(new ByteArrayInputStream(jsonData), BinaryUtil.encodeMD5(jsonData),
+                                exam.getId(), examStudent.getSecretNumber());
+                        subjectService.updateUploadCount(exam.getId(), subjectCode,
+                                (int) studentService.countUploadedByExamIdAndSubjectCode(exam.getId(), subjectCode));
+                    }
+                    startId = student.getLong(ID);
+                }
+                sync.setUpdateTime(new Date());
+                sync.setNextId(startId);
+                dataSyncService.save(sync);
+            }
+
+            sync.setFinished(true);
+            dataSyncService.save(sync);
+        } catch (Exception e) {
+            log.error("data sync exception for examId=" + dataSync.getExamId(), e);
+        } finally {
+            lockService.unlock(LockType.DATA_SYNC, dataSync.getExamId());
+            log.info("finish data sync for examId=" + dataSync.getExamId());
+        }
+    }
+
+    private ExamStudent getExamStudent(Exam exam, JSONObject student, String paperCode, String examNumber, Date now) {
+        ExamStudent examStudent = new ExamStudent();
+        examStudent.setExamId(exam.getId());
+        examStudent.setStudentCode(student.getString("identity"));
+        examStudent.setName(student.getString("name"));
+        examStudent.setSubjectCode(paperCode);
+        examStudent.setSubjectName(student.getString(COURSE_NAME));
+        examStudent.setExamNumber(examNumber);
+        examStudent.setSecretNumber(examNumber);
+        examStudent.setCollege(DEFAULT_NULL);
+        examStudent.setClassName(DEFAULT_NULL);
+        examStudent.setTeacher(DEFAULT_NULL);
+        examStudent.setCampusName(DEFAULT_NULL);
+        examStudent.setPaperType(DEFAULT_NULL);
+        examStudent.setSchoolId(exam.getSchoolId());
+        examStudent.setAbsent(false);
+        examStudent.setUpload(true);
+        examStudent.setManualAbsent(false);
+        examStudent.setBreach(false);
+        examStudent.setException(false);
+        examStudent.setSliceCount(0);
+        examStudent.setSheetCount(0);
+        examStudent.setObjectiveScore(0d);
+        examStudent.setSubjectiveScore(0d);
+        examStudent.setSubjectiveStatus(SubjectiveStatus.UNMARK);
+        examStudent.setAnswers(null);
+        examStudent.setBatchCode(null);
+        examStudent.setUploadTime(now);
+        return examStudent;
+    }
+
+    private ExamSubject saveSubject(Map<String, ExamSubject> subjectMap, Integer examId, String subjectCode,
+            String subjectName, JSONArray questionArray) {
+        ExamSubject subject = subjectMap.get(subjectCode);
+        if (subject != null) {
+            return subject;
+        }
+        subject = subjectService.find(examId, subjectCode);
+        if (subject != null) {
+            subjectMap.put(subjectCode, subject);
+            return subject;
+        }
+        subject = new ExamSubject();
+        subject.setExamId(examId);
+        subject.setCode(subjectCode);
+        subject.setName(subjectName);
+        subject.setPaperFileType(FormatType.JSON);
+        subject.setAnswerFileType(FormatType.JSON);
+        subject.setObjectiveScore(0d);
+        subject.setSubjectiveScore(0d);
+        subject.setTotalScore(0d);
+        subject.setUploadCount(0);
+        subjectService.save(subject);
+        subjectMap.put(subjectCode, subject);
+
+        double totalScore = 0d;
+        List<ExamQuestion> list = new ArrayList<>();
+        for (int i = 0; i < questionArray.size(); i++) {
+            JSONObject questionJson = questionArray.getJSONObject(i);
+            int mainNumber = questionJson.getInt("number");
+            String mainTitle = questionJson.getString("name");
+            JSONArray qArray = questionJson.getJSONArray("questions");
+            for (int j = 0; j < qArray.size(); j++) {
+                JSONObject sub = qArray.getJSONObject(j);
+                JSONArray subJson = sub.getJSONArray("subQuestions");
+                if (!subJson.isEmpty()) {
+
+                } else {
+                    ExamQuestion q = new ExamQuestion();
+                    q.setExamId(examId);
+                    q.setSubjectCode(subjectCode);
+                    q.setPaperType("#");
+                    q.setObjective(false);
+                    q.setMainNumber(mainNumber);
+                    String subNumber = sub.getString("number");
+                    q.setSubNumber(subNumber);
+                    q.setMainTitle(mainTitle);
+                    q.setGroupNumber(1);
+                    double score = sub.getDouble("score");
+                    q.setTotalScore(score);
+                    q.setIntervalScore(0.5);
+                    totalScore = totalScore + score;
+                    list.add(q);
+                }
+            }
+        }
+        questionService.save(list);
+        MarkGroup group = new MarkGroup(examId, subjectCode, 1, null, totalScore, 0d, null, null,
+                MarkMode.COMMON.toString(), 0, false, false, null);
+        groupService.save(group);
+        return subject;
+    }
+}

+ 243 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/utils/OnlineExamHttpUtil.java

@@ -0,0 +1,243 @@
+package cn.com.qmth.stmms.admin.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import cn.com.qmth.stmms.common.signature.SignatureInfo;
+import cn.com.qmth.stmms.common.signature.SignatureType;
+
+public class OnlineExamHttpUtil {
+
+    private static Logger log = LoggerFactory.getLogger(OnlineExamHttpUtil.class);
+
+    /** 默认的编码格式 */
+    public static final String DEFAULT_CHARSET = "UTF-8";
+
+    public static final String CONTENT_TYPE = "Content-Type";
+
+    public static final String APPLICATION_JSON = "application/x-www-form-urlencoded;charset=utf-8";
+
+    public static final String METHOD_POST = "POST";
+
+    public static final String AUTH = "Authorization";
+
+    protected String host = null;
+
+    protected String uri = null;
+
+    protected String accessKey = null;
+
+    protected String accessSecret = null;
+
+    public OnlineExamHttpUtil(String accessKey, String accessSecret, String host, String uri) {
+        this.accessKey = accessKey;
+        this.accessSecret = accessSecret;
+        this.host = host;
+        this.uri = uri;
+    }
+
+    /**
+     * 
+     * @param params
+     *            headers参数
+     * @param datas
+     *            requestParams参数
+     * @return
+     */
+    public String httpAction(Map<String, String> params, Map<String, Object> datas) {
+        String result = null;
+        HttpsURLConnection conn = null;
+        OutputStream os = null;
+        InputStream is = null;
+
+        try {
+
+            // 获取链接
+            URL url = new URL(host + uri);
+            conn = (HttpsURLConnection) url.openConnection();
+
+            conn.setRequestMethod(METHOD_POST);
+            conn.setRequestProperty(CONTENT_TYPE, APPLICATION_JSON);
+            // 设置鉴权
+            long ss = System.currentTimeMillis();
+            String signature = SignatureInfo.build(SignatureType.SECRET, METHOD_POST, uri, ss, accessKey, accessSecret);
+            conn.setRequestProperty(AUTH, signature);
+            conn.setRequestProperty("time", String.valueOf(ss));
+            // ssl
+            SSLContext context = SSLContext.getInstance("SSL", "SunJSSE");
+            TrustManager[] tm = new TrustManager[] { new X509TrustManager() {
+
+                @Override
+                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                }
+
+                @Override
+                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+                }
+
+                @Override
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+            } };
+            // 初始化
+            context.init(null, tm, new java.security.SecureRandom());
+            // 获取SSLSocketFactory对象
+            SSLSocketFactory ssf = context.getSocketFactory();
+            conn.setSSLSocketFactory(ssf);
+
+            conn.setUseCaches(false);
+            conn.setDoOutput(true);
+
+            // 设置额外的参数
+            if (params != null && !params.isEmpty()) {
+
+                for (Map.Entry<String, String> param : params.entrySet()) {
+                    conn.setRequestProperty(param.getKey(), param.getValue());
+                }
+            }
+            // 创建链接
+            conn.connect();
+            // 设置请求参数
+            if (datas != null) {
+                StringBuilder sb = new StringBuilder();
+                for (Map.Entry<String, Object> data : datas.entrySet()) {
+                    sb.append(data.getKey()).append("=").append(data.getValue()).append("&");
+                }
+                os = conn.getOutputStream();
+                os.write(sb.toString().getBytes());
+                os.flush();
+            }
+
+            result = getResult(conn);
+        } catch (Exception e) {
+            // 操作失败
+            log.error("OnlineExamHttpUtil connection error!", e);
+            e.printStackTrace();
+            return null;
+        } finally {
+            try {
+                if (os != null) {
+                    os.close();
+                    os = null;
+                }
+                if (is != null) {
+                    is.close();
+                    is = null;
+                }
+            } catch (IOException e) {
+                log.error("OnlineExamHttpUtil connection error!", e);
+            }
+
+            if (conn != null) {
+                conn.disconnect();
+                conn = null;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 获得连接请求的返回数据
+     * 
+     * @param conn
+     * 
+     * @return 字符串
+     */
+    private String getResult(HttpURLConnection conn) throws IOException {
+
+        StringBuilder text = new StringBuilder();
+
+        InputStream is = null;
+        InputStreamReader sr = null;
+        BufferedReader br = null;
+
+        int code = conn.getResponseCode();
+
+        try {
+            is = code >= 400 ? conn.getErrorStream() : conn.getInputStream();
+
+            sr = new InputStreamReader(is, DEFAULT_CHARSET);
+            br = new BufferedReader(sr);
+
+            char[] chars = new char[4096];
+            int length = 0;
+
+            while ((length = br.read(chars)) != -1) {
+                text.append(chars, 0, length);
+            }
+        } finally {
+            if (br != null) {
+                br.close();
+                br = null;
+            }
+            if (sr != null) {
+                sr.close();
+                sr = null;
+            }
+            if (is != null) {
+                is.close();
+                is = null;
+            }
+        }
+        if (code >= 400) {
+            throw new IOException(text.toString());
+        }
+        return text.toString();
+    }
+
+    public static void main(String[] args) throws IOException {
+        // Map<String, Object> datas = new HashMap<String, Object>();
+        // datas.put("examId", "62209205889142784");
+        // datas.put("examId", "71613103238311936");
+        // String url =
+        // "https://test1.online-exam-test.cn/api/open/exam/course/query";
+        // OnlineExamHttpUtil subjectHttp = new OnlineExamHttpUtil("123456",
+        // "123", url);
+        // String subjectJson = subjectHttp.httpAction(null, datas);
+        // System.out.println(subjectJson);
+
+        // datas.put("courseCode", "A0001");
+        // url =
+        // "https://test1.online-exam-test.cn/api/open/exam/record/need_mark";
+        // OnlineExamHttpUtil studentHttp = new OnlineExamHttpUtil("123456",
+        // "123", url);
+        // String studentJson = studentHttp.httpAction(null, datas);
+        // System.out.println(studentJson);
+
+        // datas.put("id", "62212585906769920");
+        // url = "https://test1.online-exam-test.cn/api/open/exam/paper/detail";
+        // OnlineExamHttpUtil paperHttp = new OnlineExamHttpUtil("123456",
+        // "123", url);
+        // String paperJson = paperHttp.httpAction(null, datas);
+        // System.out.println(paperJson);
+        // ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        // IOUtils.copy(new
+        // ByteArrayInputStream(subjectJson.getBytes(StandardCharsets.UTF_8)),
+        // buffer);
+        // byte[] data = buffer.toByteArray();
+        // File target = new File("/Users/ting.yin/Desktop/test123.json");
+        // target.getParentFile().mkdirs();
+        // FileOutputStream ous = new FileOutputStream(target);
+        // ous.write(data);
+        // IOUtils.closeQuietly(ous);
+    }
+}

+ 1 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/CoreController.java

@@ -59,8 +59,7 @@ public class CoreController extends BaseApiController {
     @ResponseBody
     @RoleRequire({ Role.SCHOOL_ADMIN, Role.SCHOOL_DEV })
     public JSONObject examSave(HttpServletRequest request, @RequestParam(required = false) Integer id,
-            @RequestParam(required = false) String code, @RequestParam String name,
-            @RequestParam(required = false) String examTime) {
+            @RequestParam(required = false) String code, @RequestParam String name, @RequestParam String examTime) {
         ApiUser user = RequestUtils.getApiUser(request);
         JSONObject result = new JSONObject();
         // 输入字段预处理并初步校验