Pārlūkot izejas kodu

试卷结构上传

xiaof 3 gadi atpakaļ
vecāks
revīzija
0cd171acc7
20 mainītis faili ar 1248 papildinājumiem un 2 dzēšanām
  1. 9 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CardRuleDto.java
  2. 92 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/ExamPaperObjectiveStructureDto.java
  3. 73 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/ExamPaperStructureDto.java
  4. 73 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/ExamPaperSubjectiveStructureDto.java
  5. 14 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/BasicCardRule.java
  6. 196 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/ExamPaperStructure.java
  7. 48 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/ExamPaperStructureStatusEnum.java
  8. 14 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/mapper/ExamPaperStructureMapper.java
  9. 6 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/DataSyncService.java
  10. 25 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/ExamPaperStructureService.java
  11. 29 1
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/DataSyncServiceImpl.java
  12. 332 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/ExamPaperStructureServiceImpl.java
  13. 2 1
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/HttpKit.java
  14. 79 0
      distributed-print/src/main/java/com/qmth/distributed/print/api/ExamPaperStructureController.java
  15. 51 0
      distributed-print/src/main/java/com/qmth/distributed/print/api/SsoController.java
  16. 6 0
      distributed-print/src/main/resources/application-dev.properties
  17. 6 0
      distributed-print/src/main/resources/application-release.properties
  18. 6 0
      distributed-print/src/main/resources/application-test.properties
  19. 30 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/domain/SyncDataDomain.java
  20. 157 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/sync/StmmsUtils.java

+ 9 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CardRuleDto.java

@@ -18,6 +18,7 @@ public class CardRuleDto {
     private PaperTypeEnum paperType;
     private Boolean examAbsent;
     private Boolean writeSign;
+    private Boolean discipline;
     private String requiredFields;
     private String extendFields;
     private String titleRule;
@@ -86,6 +87,14 @@ public class CardRuleDto {
         this.writeSign = writeSign;
     }
 
+    public Boolean getDiscipline() {
+        return discipline;
+    }
+
+    public void setDiscipline(Boolean discipline) {
+        this.discipline = discipline;
+    }
+
     public String getRequiredFields() {
         return requiredFields;
     }

+ 92 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/ExamPaperObjectiveStructureDto.java

@@ -0,0 +1,92 @@
+package com.qmth.distributed.print.business.bean.dto;
+
+import com.qmth.teachcloud.common.annotation.ExcelNote;
+
+import java.io.Serializable;
+
+/**
+ * @Date: 2021/10/28.
+ */
+public class ExamPaperObjectiveStructureDto implements Serializable {
+
+    @ExcelNote(value = "科目代码")
+    private String courseCode;
+    @ExcelNote(value = "科目名称")
+    private String courseName;
+    @ExcelNote(value = "试卷类型")
+    private String paperType;
+    @ExcelNote(value = "大题名称")
+    private String mainName;
+    @ExcelNote(value = "大题号(只能用小写数字)")
+    private String mainNumber;
+    @ExcelNote(value = "小题号(只能用小写数字)")
+    private String subNumber;
+    @ExcelNote(value = "标准答案")
+    private String answer;
+    @ExcelNote(value = "小题满分")
+    private String score;
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public String getMainName() {
+        return mainName;
+    }
+
+    public void setMainName(String mainName) {
+        this.mainName = mainName;
+    }
+
+    public String getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(String mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    public String getSubNumber() {
+        return subNumber;
+    }
+
+    public void setSubNumber(String subNumber) {
+        this.subNumber = subNumber;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public String getScore() {
+        return score;
+    }
+
+    public void setScore(String score) {
+        this.score = score;
+    }
+}

+ 73 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/ExamPaperStructureDto.java

@@ -0,0 +1,73 @@
+package com.qmth.distributed.print.business.bean.dto;
+
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * @Date: 2021/10/28.
+ */
+public class ExamPaperStructureDto {
+
+    private String paperType;
+    private MultipartFile subjectiveQuestionFile;
+    private String subjectiveQuestionMd5;
+    private MultipartFile objectiveQuestionFile;
+    private String objectiveQuestionMd5;
+    private MultipartFile standardAnswerFile;
+    private String standardAnswerMd5;
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public MultipartFile getSubjectiveQuestionFile() {
+        return subjectiveQuestionFile;
+    }
+
+    public void setSubjectiveQuestionFile(MultipartFile subjectiveQuestionFile) {
+        this.subjectiveQuestionFile = subjectiveQuestionFile;
+    }
+
+    public String getSubjectiveQuestionMd5() {
+        return subjectiveQuestionMd5;
+    }
+
+    public void setSubjectiveQuestionMd5(String subjectiveQuestionMd5) {
+        this.subjectiveQuestionMd5 = subjectiveQuestionMd5;
+    }
+
+    public MultipartFile getObjectiveQuestionFile() {
+        return objectiveQuestionFile;
+    }
+
+    public void setObjectiveQuestionFile(MultipartFile objectiveQuestionFile) {
+        this.objectiveQuestionFile = objectiveQuestionFile;
+    }
+
+    public String getObjectiveQuestionMd5() {
+        return objectiveQuestionMd5;
+    }
+
+    public void setObjectiveQuestionMd5(String objectiveQuestionMd5) {
+        this.objectiveQuestionMd5 = objectiveQuestionMd5;
+    }
+
+    public MultipartFile getStandardAnswerFile() {
+        return standardAnswerFile;
+    }
+
+    public void setStandardAnswerFile(MultipartFile standardAnswerFile) {
+        this.standardAnswerFile = standardAnswerFile;
+    }
+
+    public String getStandardAnswerMd5() {
+        return standardAnswerMd5;
+    }
+
+    public void setStandardAnswerMd5(String standardAnswerMd5) {
+        this.standardAnswerMd5 = standardAnswerMd5;
+    }
+}

+ 73 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/ExamPaperSubjectiveStructureDto.java

@@ -0,0 +1,73 @@
+package com.qmth.distributed.print.business.bean.dto;
+
+import com.qmth.teachcloud.common.annotation.ExcelNote;
+
+import java.io.Serializable;
+
+/**
+ * @Date: 2021/10/28.
+ */
+
+public class ExamPaperSubjectiveStructureDto implements Serializable {
+
+    @ExcelNote(value = "科目代码")
+    private String courseCode;
+    @ExcelNote(value = "科目名称")
+    private String courseName;
+    @ExcelNote(value = "大题名称")
+    private String mainName;
+    @ExcelNote(value = "大题号(只能用小写数字)")
+    private String mainNumber;
+    @ExcelNote(value = "小题号(只能用小写数字)")
+    private String subNumber;
+    @ExcelNote(value = "小题满分")
+    private String score;
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getMainName() {
+        return mainName;
+    }
+
+    public void setMainName(String mainName) {
+        this.mainName = mainName;
+    }
+
+    public String getMainNumber() {
+        return mainNumber;
+    }
+
+    public void setMainNumber(String mainNumber) {
+        this.mainNumber = mainNumber;
+    }
+
+    public String getSubNumber() {
+        return subNumber;
+    }
+
+    public void setSubNumber(String subNumber) {
+        this.subNumber = subNumber;
+    }
+
+    public String getScore() {
+        return score;
+    }
+
+    public void setScore(String score) {
+        this.score = score;
+    }
+}

+ 14 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/BasicCardRule.java

@@ -59,6 +59,12 @@ public class BasicCardRule extends BaseEntity implements Serializable {
      */
     @TableField("write_sign")
     private Boolean writeSign;
+
+    /**
+     * 0-禁用,1-启用
+     */
+    @TableField("discipline")
+    private Boolean discipline;
     /**
      * 必选字段
      */
@@ -161,6 +167,14 @@ public class BasicCardRule extends BaseEntity implements Serializable {
         this.writeSign = writeSign;
     }
 
+    public Boolean getDiscipline() {
+        return discipline;
+    }
+
+    public void setDiscipline(Boolean discipline) {
+        this.discipline = discipline;
+    }
+
     public String getRequiredFields() {
         return requiredFields;
     }

+ 196 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/ExamPaperStructure.java

@@ -0,0 +1,196 @@
+package com.qmth.distributed.print.business.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.distributed.print.business.enums.ExamPaperStructureStatusEnum;
+import com.qmth.teachcloud.common.base.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 考试试卷结构表
+ * </p>
+ *
+ * @author xf
+ */
+@TableName("exam_paper_structure")
+public class ExamPaperStructure extends BaseEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "学校id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableField("school_id")
+    private Long schoolId;
+
+    @ApiModelProperty(value = "云阅卷考试ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableField(value = "third_relate_id")
+    private Long thirdRelateId;
+
+    /**
+     * 云阅卷考试名称
+     */
+    @TableField("third_relate_name")
+    private String thirdRelateName;
+
+    /**
+     * 试卷编号
+     */
+    @TableField("paper_number")
+    private String paperNumber;
+
+    /**
+     * 课程代码
+     */
+    @TableField("course_code")
+    private String courseCode;
+    /**
+     * 课程名称
+     */
+    @TableField("course_name")
+    private String courseName;
+    /**
+     * 试卷类型
+     */
+    @TableField("paper_type")
+    private String paperType;
+    /**
+     * 状态
+     */
+    private ExamPaperStructureStatusEnum status;
+    /**
+     * 试卷原卷和标答附件ID
+     */
+    @TableField("paper_answer")
+    private String paperAnswer;
+    /**
+     * 客观题试卷结构JSON
+     */
+    @TableField("objective_structure")
+    private String objectiveStructure;
+    /**
+     * 主观题试卷结构JSON
+     */
+    @TableField("subjective_structure")
+    private String subjectiveStructure;
+    /**
+     * 命题老师ID
+     */
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableField("proposition_teacher_id")
+    private Long propositionTeacherId;
+
+    /**
+     * 0-禁用,1-启用
+     */
+    private Boolean enable;
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public Long getThirdRelateId() {
+        return thirdRelateId;
+    }
+
+    public void setThirdRelateId(Long thirdRelateId) {
+        this.thirdRelateId = thirdRelateId;
+    }
+
+    public String getThirdRelateName() {
+        return thirdRelateName;
+    }
+
+    public void setThirdRelateName(String thirdRelateName) {
+        this.thirdRelateName = thirdRelateName;
+    }
+
+    public String getPaperNumber() {
+        return paperNumber;
+    }
+
+    public void setPaperNumber(String paperNumber) {
+        this.paperNumber = paperNumber;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public ExamPaperStructureStatusEnum getStatus() {
+        return status;
+    }
+
+    public void setStatus(ExamPaperStructureStatusEnum status) {
+        this.status = status;
+    }
+
+    public String getPaperAnswer() {
+        return paperAnswer;
+    }
+
+    public void setPaperAnswer(String paperAnswer) {
+        this.paperAnswer = paperAnswer;
+    }
+
+    public String getObjectiveStructure() {
+        return objectiveStructure;
+    }
+
+    public void setObjectiveStructure(String objectiveStructure) {
+        this.objectiveStructure = objectiveStructure;
+    }
+
+    public String getSubjectiveStructure() {
+        return subjectiveStructure;
+    }
+
+    public void setSubjectiveStructure(String subjectiveStructure) {
+        this.subjectiveStructure = subjectiveStructure;
+    }
+
+    public Long getPropositionTeacherId() {
+        return propositionTeacherId;
+    }
+
+    public void setPropositionTeacherId(Long propositionTeacherId) {
+        this.propositionTeacherId = propositionTeacherId;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+}

+ 48 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/ExamPaperStructureStatusEnum.java

@@ -0,0 +1,48 @@
+package com.qmth.distributed.print.business.enums;
+
+import com.qmth.teachcloud.common.enums.EnumResult;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 考试试卷结构同步状态
+ */
+public enum ExamPaperStructureStatusEnum {
+
+    INIT("未开始"),
+    UPLOAD_FINISH("上传完成"),
+    START_SYNC("开始同步"),
+    PAPER_FINISH("试卷同步成功"),
+    ANSWER_FINISH("标答同步成功"),
+    OBJECTIVE_FINISH("客观题结构同步成功"),
+    SUBJECTIVE_FINISH("主观题结构同步成功"),
+    FINISH("结束");
+
+    ExamPaperStructureStatusEnum(String desc) {
+        this.desc = desc;
+    }
+
+    private String desc;
+
+    public String getDesc() {
+        return desc;
+    }
+
+    /**
+     * @return
+     */
+    public static List<EnumResult> listTypes() {
+        List<EnumResult> list = new ArrayList<EnumResult>();
+        for (ExamPaperStructureStatusEnum value : ExamPaperStructureStatusEnum.values()) {
+            EnumResult result = new EnumResult();
+            result.setName(value.name());
+            result.setOrdinal(value.ordinal());
+            result.setCode(null);
+            result.setDesc(value.getDesc());
+            list.add(result);
+        }
+        return list;
+    }
+
+}

+ 14 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/mapper/ExamPaperStructureMapper.java

@@ -0,0 +1,14 @@
+package com.qmth.distributed.print.business.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.distributed.print.business.entity.ExamPaperStructure;
+
+/**
+ * <p>
+ * 考试试卷结构 Mapper 接口
+ * </p>
+ *
+ * @author xf
+ */
+public interface ExamPaperStructureMapper extends BaseMapper<ExamPaperStructure> {
+}

+ 6 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/DataSyncService.java

@@ -2,6 +2,8 @@ package com.qmth.distributed.print.business.service;
 
 import com.qmth.distributed.print.business.bean.params.SyncDataParam;
 
+import java.util.Map;
+
 /**
  * 同步数据到云阅卷 服务类
  * @Date: 2021/5/20.
@@ -12,4 +14,8 @@ public interface DataSyncService {
     void syncDataCloud(Long printPlanId, Long thirdRelateId);
 
     void syncDataMerge(SyncDataParam syncDataParam);
+
+    void syncStructure(Long id);
+
+    Map<String, Object> markerLoginInfo();
 }

+ 25 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/ExamPaperStructureService.java

@@ -0,0 +1,25 @@
+package com.qmth.distributed.print.business.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.distributed.print.business.entity.ExamPaperStructure;
+import com.qmth.distributed.print.business.enums.ExamPaperStructureStatusEnum;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 试卷结构 服务类
+ * </p>
+ *
+ * @author xf
+ */
+public interface ExamPaperStructureService extends IService<ExamPaperStructure> {
+
+    IPage<ExamPaperStructure> listByPropositionTeacherId(Integer pageNumber, Integer pageSize);
+
+    void upload(Long id, String md5, String paperType, MultipartFile[] files);
+
+    void updateStatusById(Long id, ExamPaperStructureStatusEnum startSync);
+}

+ 29 - 1
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/DataSyncServiceImpl.java

@@ -9,6 +9,7 @@ import com.qmth.distributed.print.business.bean.dto.SyncExamCardDto;
 import com.qmth.distributed.print.business.bean.dto.SyncExamStudentDto;
 import com.qmth.distributed.print.business.bean.params.SyncDataParam;
 import com.qmth.distributed.print.business.entity.*;
+import com.qmth.distributed.print.business.enums.ExamPaperStructureStatusEnum;
 import com.qmth.distributed.print.business.enums.PrintPlanStatusEnum;
 import com.qmth.distributed.print.business.service.*;
 import com.qmth.distributed.print.business.util.HttpKit;
@@ -17,6 +18,7 @@ import com.qmth.teachcloud.common.config.DictionaryConfig;
 import com.qmth.teachcloud.common.contant.SystemConstant;
 import com.qmth.teachcloud.common.entity.BasicSchool;
 import com.qmth.teachcloud.common.entity.SysConfig;
+import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.enums.TaskResultEnum;
 import com.qmth.teachcloud.common.enums.TaskStatusEnum;
@@ -66,6 +68,9 @@ public class DataSyncServiceImpl implements DataSyncService {
     @Autowired
     private ExamCardService examCardService;
 
+    @Autowired
+    private ExamPaperStructureService examPaperStructureService;
+
     @Autowired
     private SysConfigService sysConfigService;
 
@@ -120,7 +125,7 @@ public class DataSyncServiceImpl implements DataSyncService {
             throw ExceptionResultEnum.ERROR.exception("请选择需要合并的印刷任务");
         }
 
-        if(Objects.isNull(syncDataParam.getThirdRelateId()) && Objects.isNull(syncDataParam.getThirdRelateName())){
+        if (Objects.isNull(syncDataParam.getThirdRelateId()) && Objects.isNull(syncDataParam.getThirdRelateName())) {
             throw ExceptionResultEnum.ERROR.exception("考试ID、考试名称至少填一个");
         }
 
@@ -142,6 +147,29 @@ public class DataSyncServiceImpl implements DataSyncService {
         }
     }
 
+    @Override
+    public void syncStructure(Long id) {
+        // 开始同步
+        examPaperStructureService.updateStatusById(id, ExamPaperStructureStatusEnum.START_SYNC);
+
+    }
+
+    @Override
+    public Map<String, Object> markerLoginInfo() {
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        Map<String, Object> map = new HashMap<>();
+        long time = System.currentTimeMillis();
+        String rootUrl = dictionaryConfig.syncDataDomain().getHostUrl();
+        String markLoginUrl = dictionaryConfig.syncDataDomain().getMarkLoginUrl();
+        map.put("redirectUrl", rootUrl + markLoginUrl);
+        String account = "M_" + sysUser.getLoginName();
+        map.put("account", account);
+        map.put("name", sysUser.getLoginName());
+        map.put("time", time);
+        map.put("authorization", createSign(sysUser.getSchoolId(), time, markLoginUrl));
+        return map;
+    }
+
     private TimerTask syncData(ExamPrintPlan examPrintPlan, Long thirdRelateId, String thirdRelateName) {
         return new TimerTask() {
             @Override

+ 332 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/ExamPaperStructureServiceImpl.java

@@ -0,0 +1,332 @@
+package com.qmth.distributed.print.business.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import com.qmth.boot.api.exception.ApiException;
+import com.qmth.distributed.print.business.bean.dto.ExamPaperObjectiveStructureDto;
+import com.qmth.distributed.print.business.bean.dto.ExamPaperStructureDto;
+import com.qmth.distributed.print.business.bean.dto.ExamPaperSubjectiveStructureDto;
+import com.qmth.distributed.print.business.entity.ExamPaperStructure;
+import com.qmth.distributed.print.business.entity.ExamTask;
+import com.qmth.distributed.print.business.entity.ExamTaskDetail;
+import com.qmth.distributed.print.business.enums.ExamPaperStructureStatusEnum;
+import com.qmth.distributed.print.business.mapper.ExamPaperStructureMapper;
+import com.qmth.distributed.print.business.service.ExamPaperStructureService;
+import com.qmth.distributed.print.business.service.ExamTaskDetailService;
+import com.qmth.distributed.print.business.service.ExamTaskService;
+import com.qmth.distributed.print.business.service.PrintCommonService;
+import com.qmth.teachcloud.common.entity.BasicAttachment;
+import com.qmth.teachcloud.common.entity.SysUser;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.enums.UploadFileEnum;
+import com.qmth.teachcloud.common.service.BasicAttachmentService;
+import com.qmth.teachcloud.common.util.ExcelUtil;
+import com.qmth.teachcloud.common.util.ResultUtil;
+import com.qmth.teachcloud.common.util.ServletUtil;
+import com.qmth.teachcloud.common.util.excel.ExcelError;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 试卷结构 服务实现类
+ * </p>
+ */
+@Service
+public class ExamPaperStructureServiceImpl extends ServiceImpl<ExamPaperStructureMapper, ExamPaperStructure> implements ExamPaperStructureService {
+
+    @Autowired
+    ExamTaskService examTaskService;
+
+    @Autowired
+    ExamTaskDetailService examTaskDetailService;
+
+    @Resource
+    PrintCommonService printCommonService;
+
+    @Autowired
+    BasicAttachmentService basicAttachmentService;
+
+    @Override
+    public IPage<ExamPaperStructure> listByPropositionTeacherId(Integer pageNumber, Integer pageSize) {
+        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        Page<ExamPaperStructure> page = new Page<>(pageNumber, pageSize);
+        QueryWrapper<ExamPaperStructure> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(ExamPaperStructure::getSchoolId, schoolId).eq(ExamPaperStructure::getPropositionTeacherId, sysUser.getId());
+        return this.page(page, queryWrapper);
+    }
+
+    @Transactional
+    @Override
+    public void upload(Long id, String md5, String paperType, MultipartFile[] files) {
+        // 组装ExamPaperStructureDto
+        List<ExamPaperStructureDto> examPaperStructureDtos = createExamPaperStructure(md5, paperType, files);
+        List<BasicAttachment> basicAttachmentList = new ArrayList<>();
+
+        List<Map<String, Object>> paperAnswerList = new ArrayList<>();
+        List<Map<String, Object>> subjectiveStructureList = new ArrayList<>();
+        List<Map<String, Object>> objectiveStructureList = new ArrayList<>();
+
+        // 试卷卷型及原卷附件ID
+        ExamPaperStructure examPaperStructure = this.getById(id);
+        Map<String, String> paperMap = createExamTaskAttachmentIds(examPaperStructure);
+
+        // 上传试卷结构、标答文件
+        for (ExamPaperStructureDto examPaperStructureDto : examPaperStructureDtos) {
+            Map<String, Object> paperAnswerMap = new HashMap<>();
+            paperAnswerMap.put("paperType", examPaperStructureDto.getPaperType());
+            Map<String, Object> subjectiveStructureMap = new HashMap<>();
+            subjectiveStructureMap.put("paperType", examPaperStructureDto.getPaperType());
+            Map<String, Object> objectiveStructureMap = new HashMap<>();
+            objectiveStructureMap.put("paperType", examPaperStructureDto.getPaperType());
+            try {
+                // 主观题附件
+                BasicAttachment subjectiveBasicAttachment = printCommonService.saveAttachment(examPaperStructureDto.getSubjectiveQuestionFile(), examPaperStructureDto.getSubjectiveQuestionMd5(), UploadFileEnum.UPLOAD);
+                if (Objects.isNull(subjectiveBasicAttachment)) {
+                    throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
+                }
+                // 解析试卷结构
+                List<Object> subjectiveStructrue = analyzPaperSubjectiveStructure(examPaperStructureDto.getSubjectiveQuestionFile());
+                subjectiveStructureMap.put("content", CollectionUtils.isEmpty(subjectiveStructrue) ? null : subjectiveStructrue);
+                // 客观题附件
+                basicAttachmentList.add(subjectiveBasicAttachment);
+                BasicAttachment objectiveBasicAttachment = printCommonService.saveAttachment(examPaperStructureDto.getObjectiveQuestionFile(), examPaperStructureDto.getObjectiveQuestionMd5(), UploadFileEnum.UPLOAD);
+                if (Objects.isNull(objectiveBasicAttachment)) {
+                    throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
+                }
+                // 解析试卷结构
+                List<Object> objectiveStructure = analyzPaperObjectiveStructure(examPaperStructureDto.getObjectiveQuestionFile());
+                objectiveStructureMap.put("content", CollectionUtils.isEmpty(objectiveStructure) ? null : objectiveStructure);
+                // 标答附件
+                basicAttachmentList.add(objectiveBasicAttachment);
+                BasicAttachment answerBasicAttachment = printCommonService.saveAttachment(examPaperStructureDto.getStandardAnswerFile(), examPaperStructureDto.getStandardAnswerMd5(), UploadFileEnum.UPLOAD);
+                if (Objects.isNull(answerBasicAttachment)) {
+                    throw ExceptionResultEnum.ATTACHMENT_ERROR.exception();
+                }
+                paperAnswerMap.put("answer", answerBasicAttachment.getId());
+                basicAttachmentList.add(answerBasicAttachment);
+
+                // 试卷原卷
+                paperAnswerMap.put("paper", paperMap.get(examPaperStructureDto.getPaperType()));
+                paperAnswerList.add(paperAnswerMap);
+                subjectiveStructureList.add(subjectiveStructureMap);
+                objectiveStructureList.add(objectiveStructureMap);
+            } catch (Exception e) {
+                log.error("请求出错", e);
+                for (BasicAttachment basicAttachment : basicAttachmentList) {
+                    basicAttachmentService.deleteAttachment(basicAttachment);
+                }
+                if (e instanceof ApiException) {
+                    ResultUtil.error((ApiException) e, e.getMessage());
+                } else {
+                    ResultUtil.error(e.getMessage());
+                }
+            }
+        }
+
+        // 保存
+        UpdateWrapper<ExamPaperStructure> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(ExamPaperStructure::getPaperAnswer, JSONObject.toJSONString(paperAnswerList))
+                .set(ExamPaperStructure::getSubjectiveStructure, JSONObject.toJSONString(subjectiveStructureList))
+                .set(ExamPaperStructure::getObjectiveStructure, JSONObject.toJSONString(objectiveStructureList))
+                // 上传成功
+                .set(ExamPaperStructure::getStatus, ExamPaperStructureStatusEnum.UPLOAD_FINISH)
+                .eq(ExamPaperStructure::getId, id);
+        this.update(updateWrapper);
+    }
+
+    @Override
+    public void updateStatusById(Long id, ExamPaperStructureStatusEnum status) {
+        UpdateWrapper<ExamPaperStructure> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.lambda().set(ExamPaperStructure::getStatus, status).eq(ExamPaperStructure::getId, id);
+        this.update(updateWrapper);
+    }
+
+    private Map<String, String> createExamTaskAttachmentIds(ExamPaperStructure examPaperStructure) {
+        ExamTask examTask = examTaskService.getByCourseCodeAndPaperNumber(examPaperStructure.getSchoolId(), examPaperStructure.getCourseCode(), examPaperStructure.getPaperNumber());
+
+        QueryWrapper<ExamTaskDetail> examTaskDetailQueryWrapper = new QueryWrapper<>();
+        examTaskDetailQueryWrapper.lambda().eq(ExamTaskDetail::getExamTaskId, examTask.getId());
+        ExamTaskDetail examTaskDetail = examTaskDetailService.getOne(examTaskDetailQueryWrapper);
+
+        String paperAttachmentIds = examTaskDetail.getPaperAttachmentIds();
+        List<Map> list = JSONObject.parseArray(paperAttachmentIds, Map.class);
+        if (CollectionUtils.isEmpty(list)) {
+            throw ExceptionResultEnum.ERROR.exception("未找到关联的试卷文件");
+        }
+
+        Map<String, String> paperMap = list.stream().collect(Collectors.toMap(m -> m.get("name").toString(), m -> m.get("attachmentId").toString()));
+
+        return paperMap;
+    }
+
+    /**
+     * 解析excel文件内容
+     *
+     * @param file
+     * @return
+     */
+    private List<Object> analyzPaperSubjectiveStructure(MultipartFile file) throws IOException, NoSuchFieldException {
+        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(ExamPaperSubjectiveStructureDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
+            List<ExcelError> excelErrorTemp = new ArrayList<>();
+            // 只允许导入一个sheet
+            if (finalExcelList.size() > 1) {
+                throw ExceptionResultEnum.ERROR.exception("excel中只允许有一个sheet");
+            }
+            for (int i = 0; i < finalExcelList.size(); i++) {
+                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
+                List<Object> examTaskTempList = excelMap.get(i);
+                for (int y = 0; y < examTaskTempList.size(); y++) {
+                    ExamPaperSubjectiveStructureDto subjectiveStructureDto = (ExamPaperSubjectiveStructureDto) examTaskTempList.get(y);
+                    if (StringUtils.isBlank(subjectiveStructureDto.getCourseCode())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]必填"));
+                    }
+                    if (StringUtils.isBlank(subjectiveStructureDto.getCourseName())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]必填"));
+                    }
+                    if (StringUtils.isBlank(subjectiveStructureDto.getMainName())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题名称]必填"));
+                    }
+                    if (StringUtils.isBlank(subjectiveStructureDto.getMainNumber())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题号]必填"));
+                    }
+                    if (StringUtils.isBlank(subjectiveStructureDto.getSubNumber())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题号]必填"));
+                    }
+                    if (StringUtils.isBlank(subjectiveStructureDto.getScore())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题满分]必填"));
+                    }
+                }
+            }
+            if (excelErrorTemp.size() > 0) {
+                List<String> errors = excelErrorTemp.stream().map(m -> m.getExcelErrorType()).collect(Collectors.toList());
+                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
+            }
+            return finalExcelList;
+        });
+        List<Object> list = new ArrayList<>();
+        for (LinkedMultiValueMap<Integer, Object> map : finalList) {
+            for (Map.Entry<Integer, List<Object>> entry : map.entrySet()) {
+                list.add(entry.getValue());
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 解析excel文件内容
+     *
+     * @param file
+     * @return
+     */
+    private List<Object> analyzPaperObjectiveStructure(MultipartFile file) throws IOException, NoSuchFieldException {
+        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(ExamPaperObjectiveStructureDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
+            List<ExcelError> excelErrorTemp = new ArrayList<>();
+            // 只允许导入一个sheet
+            if (finalExcelList.size() > 1) {
+                throw ExceptionResultEnum.ERROR.exception("excel中只允许有一个sheet");
+            }
+            for (int i = 0; i < finalExcelList.size(); i++) {
+                LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
+                List<Object> examTaskTempList = excelMap.get(i);
+                for (int y = 0; y < examTaskTempList.size(); y++) {
+                    ExamPaperObjectiveStructureDto objectiveStructureDto = (ExamPaperObjectiveStructureDto) examTaskTempList.get(y);
+                    if (StringUtils.isBlank(objectiveStructureDto.getCourseCode())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程代码]必填"));
+                    }
+                    if (StringUtils.isBlank(objectiveStructureDto.getCourseName())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[课程名称]必填"));
+                    }
+                    if (StringUtils.isBlank(objectiveStructureDto.getPaperType())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[试卷类型]必填"));
+                    }
+                    if (StringUtils.isBlank(objectiveStructureDto.getMainName())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题名称]必填"));
+                    }
+                    if (StringUtils.isBlank(objectiveStructureDto.getMainNumber())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[大题号]必填"));
+                    }
+                    if (StringUtils.isBlank(objectiveStructureDto.getSubNumber())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题号]必填"));
+                    }
+                    if (StringUtils.isBlank(objectiveStructureDto.getAnswer())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[标准答案]必填"));
+                    }
+                    if (StringUtils.isBlank(objectiveStructureDto.getScore())) {
+                        excelErrorTemp.add(new ExcelError(y + 1, "excel第" + (i + 1) + "个sheet第" + (y + 1) + "行[小题满分]必填"));
+                    }
+                }
+            }
+            if (excelErrorTemp.size() > 0) {
+                List<String> errors = excelErrorTemp.stream().map(m -> m.getExcelErrorType()).collect(Collectors.toList());
+                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(errors));
+            }
+            return finalExcelList;
+        });
+        List<Object> list = new ArrayList<>();
+        for (LinkedMultiValueMap<Integer, Object> map : finalList) {
+            for (Map.Entry<Integer, List<Object>> entry : map.entrySet()) {
+                list.add(entry.getValue());
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 组装ExamPaperStructureDto
+     *
+     * @param md5
+     * @param paperType
+     * @param files
+     * @return
+     */
+    private List<ExamPaperStructureDto> createExamPaperStructure(String md5, String paperType, MultipartFile[] files) {
+        int COUNT = 3;
+        if (files.length == 0) {
+            throw ExceptionResultEnum.ERROR.exception("请上传文件");
+        }
+        if (StringUtils.isBlank(paperType)) {
+            throw ExceptionResultEnum.ERROR.exception("试卷卷型有误");
+        }
+        String[] paperTypes = paperType.split(",");
+        if (paperTypes.length * COUNT - files.length != 0) {
+            throw ExceptionResultEnum.ERROR.exception("上传文件有误");
+        }
+        if (StringUtils.isBlank(md5)) {
+            throw ExceptionResultEnum.ERROR.exception("上传文件有误");
+        }
+        String[] md5s = md5.split(",");
+        if (md5s.length - files.length != 0) {
+            throw ExceptionResultEnum.ERROR.exception("上传文件有误");
+        }
+        List<ExamPaperStructureDto> list = new ArrayList<>();
+        for (int i = 0; i < paperTypes.length; i++) {
+            ExamPaperStructureDto examPaperStructureDto = new ExamPaperStructureDto();
+            examPaperStructureDto.setPaperType(paperTypes[i]);
+            examPaperStructureDto.setObjectiveQuestionFile(files[COUNT * i]);
+            examPaperStructureDto.setObjectiveQuestionMd5(md5s[COUNT * i]);
+            examPaperStructureDto.setSubjectiveQuestionFile(files[COUNT * i + 1]);
+            examPaperStructureDto.setSubjectiveQuestionMd5(md5s[COUNT * i + 1]);
+            examPaperStructureDto.setStandardAnswerFile(files[COUNT * i + 2]);
+            examPaperStructureDto.setStandardAnswerMd5(md5s[COUNT * i + 2]);
+            list.add(examPaperStructureDto);
+        }
+        return list;
+    }
+}

+ 2 - 1
distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/HttpKit.java

@@ -11,6 +11,7 @@
         <result column="paper_type" property="paperType" />
         <result column="exam_absent" property="examAbsent" />
         <result column="write_sign" property="writeSign" />
+        <result column="discipline" property="discipline" />
         <result column="required_fields" property="requiredFields" />
         <result column="extend_fields" property="extendFields" />
         <result column="title_rule" property="titleRule" />
@@ -27,7 +28,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        select id, school_id, name, exam_number_style, paper_type, exam_absent, write_sign, required_fields, extend_fields, title_rule, attention, objective_attention, subjective_attention, enable, remark, create_id, create_time, update_id, update_time from basic_card_rule
+        select id, school_id, name, exam_number_style, paper_type, exam_absent, write_sign, discipline, required_fields, extend_fields, title_rule, attention, objective_attention, subjective_attention, enable, remark, create_id, create_time, update_id, update_time from basic_card_rule
     </sql>
     <select id="listPage" resultType="com.qmth.distributed.print.business.bean.dto.CardRuleDto">
         <include refid="Base_Column_List"></include>

+ 79 - 0
distributed-print/src/main/java/com/qmth/distributed/print/api/ExamPaperStructureController.java

@@ -0,0 +1,79 @@
+package com.qmth.distributed.print.api;
+
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.distributed.print.business.entity.ExamPaperStructure;
+import com.qmth.distributed.print.business.service.DataSyncService;
+import com.qmth.distributed.print.business.service.ExamPaperStructureService;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.util.Result;
+import com.qmth.teachcloud.common.util.ResultUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+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.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import java.util.List;
+
+/**
+ * <p>
+ * 试卷结构 前端控制器
+ * </p>
+ *
+ * @author xf
+ */
+@Api(tags = "试卷结构Controller")
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/${prefix.url.exam}/structure")
+public class ExamPaperStructureController {
+
+    @Autowired
+    private ExamPaperStructureService examPaperStructureService;
+
+    @Autowired
+    private DataSyncService dataSyncService;
+
+    /**
+     * 查询
+     *
+     * @return
+     */
+    @ApiOperation(value = "查询")
+    @RequestMapping(value = "/list", method = RequestMethod.POST)
+    public Result list(@RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) Integer pageNumber,
+                       @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) Integer pageSize) {
+        IPage<ExamPaperStructure> examPaperStructureIPage = examPaperStructureService.listByPropositionTeacherId(pageNumber, pageSize);
+        return ResultUtil.ok(examPaperStructureIPage);
+    }
+
+
+    /**
+     * 上传
+     * @param id
+     * @param md5 文件md5
+     * @param paperType 试卷类型
+     * @param files 文件数组
+     * @return
+     */
+    @ApiOperation(value = "上传试卷结构、标答")
+    @RequestMapping(value = "/upload", method = RequestMethod.POST)
+    public Result upload(@RequestParam("id") Long id,
+                         @RequestParam("md5") String md5,
+                         @RequestParam("paperType") String paperType,
+                         @RequestParam("files") MultipartFile[] files) {
+        examPaperStructureService.upload(id, md5, paperType, files);
+        // 推送云阅卷
+        dataSyncService.syncStructure(id);
+        return ResultUtil.ok(true, null);
+    }
+}
+

+ 51 - 0
distributed-print/src/main/java/com/qmth/distributed/print/api/SsoController.java

@@ -0,0 +1,51 @@
+package com.qmth.distributed.print.api;
+
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.annotation.BOOL;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.distributed.print.business.entity.ExamPaperStructure;
+import com.qmth.distributed.print.business.service.DataSyncService;
+import com.qmth.distributed.print.business.service.ExamPaperStructureService;
+import com.qmth.teachcloud.common.util.Result;
+import com.qmth.teachcloud.common.util.ResultUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+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.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * 单点登录 前端控制器
+ * </p>
+ *
+ * @author xf
+ */
+@Api(tags = "单点登录Controller")
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/${prefix.url.exam}/sso")
+public class SsoController {
+
+    @Autowired
+    DataSyncService dataSyncService;
+
+    /**
+     * 评卷员单点登录
+     *
+     * @return
+     */
+    @ApiOperation(value = "评卷员单点登录")
+    @RequestMapping(value = "/marker_login", method = RequestMethod.POST)
+    public Result list() {
+        Map<String, Object> map = dataSyncService.markerLoginInfo();
+        return ResultUtil.ok(map);
+    }
+}
+

+ 6 - 0
distributed-print/src/main/resources/application-dev.properties

@@ -130,6 +130,12 @@ sync.config.examSaveUrl=/api/exam/save
 sync.config.studentSaveUrl=/api/exam/student/save
 #\u036C\uFFFD\uFFFD\uFFFD\u2FE8
 sync.config.cardUploadUrl=/api/file/card/upload
+#第三方登录
+sync.config.markLoginUrl=/open/mark/login
+#成绩回传考生数量
+sync.config.studentCountUrl=/api/exam/student/count
+#成绩回传
+sync.config.studentScoreUrl=/api/exam/student/score
 
 
 sms.config.smsNormalCode=qmth

+ 6 - 0
distributed-print/src/main/resources/application-release.properties

@@ -128,6 +128,12 @@ sync.config.examSaveUrl=/api/exam/save
 sync.config.studentSaveUrl=/api/exam/student/save
 #\u036C\uFFFD\uFFFD\uFFFD\u2FE8
 sync.config.cardUploadUrl=/api/file/card/upload
+#第三方登录
+sync.config.markLoginUrl=/open/mark/login
+#成绩回传考生数量
+sync.config.studentCountUrl=/api/exam/student/count
+#成绩回传
+sync.config.studentScoreUrl=/api/exam/student/score
 
 
 sms.config.smsNormalCode=8635

+ 6 - 0
distributed-print/src/main/resources/application-test.properties

@@ -128,6 +128,12 @@ sync.config.examSaveUrl=/api/exam/save
 sync.config.studentSaveUrl=/api/exam/student/save
 #\u540C\u6B65\u9898\u5361
 sync.config.cardUploadUrl=/api/file/card/upload
+#第三方登录
+sync.config.markLoginUrl=/open/mark/login
+#成绩回传考生数量
+sync.config.studentCountUrl=/api/exam/student/count
+#成绩回传
+sync.config.studentScoreUrl=/api/exam/student/score
 
 
 sms.config.smsNormalCode=8635

+ 30 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/domain/SyncDataDomain.java

@@ -13,6 +13,12 @@ public class SyncDataDomain {
 
     String cardUploadUrl;
 
+    String markLoginUrl;
+
+    String studentCountUrl;
+
+    String studentScoreUrl;
+
     public String getHostUrl() {
         return hostUrl;
     }
@@ -44,4 +50,28 @@ public class SyncDataDomain {
     public void setCardUploadUrl(String cardUploadUrl) {
         this.cardUploadUrl = cardUploadUrl;
     }
+
+    public String getMarkLoginUrl() {
+        return markLoginUrl;
+    }
+
+    public void setMarkLoginUrl(String markLoginUrl) {
+        this.markLoginUrl = markLoginUrl;
+    }
+
+    public String getStudentCountUrl() {
+        return studentCountUrl;
+    }
+
+    public void setStudentCountUrl(String studentCountUrl) {
+        this.studentCountUrl = studentCountUrl;
+    }
+
+    public String getStudentScoreUrl() {
+        return studentScoreUrl;
+    }
+
+    public void setStudentScoreUrl(String studentScoreUrl) {
+        this.studentScoreUrl = studentScoreUrl;
+    }
 }

+ 157 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/sync/StmmsUtils.java

@@ -0,0 +1,157 @@
+package com.qmth.teachcloud.common.sync;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qmth.boot.tools.signature.SignatureType;
+import com.qmth.teachcloud.common.SignatureEntityTest;
+import com.qmth.teachcloud.common.config.DictionaryConfig;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.entity.BasicSchool;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.service.CommonCacheService;
+import com.qmth.teachcloud.common.util.ServletUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 同步云阅卷接口工具类
+ *
+ * @Date: 2021/10/29.
+ */
+public class StmmsUtils {
+
+    // 考试类型(逸教云默认是纸笔"SCAN_IMAGE")
+    private static final String SAVE_EXAM_TYPE = "SCAN_IMAGE";
+    // 所有请求方法默认为"POST"
+    private static final String POST_METHOD = "POST";
+
+    @Autowired
+    private CommonCacheService commonCacheService;
+
+    @Autowired
+    private DictionaryConfig dictionaryConfig;
+
+    /**
+     * 考试创建/更新接口
+     *
+     * @param code     唯一标识
+     * @param name     考试名称
+     * @param examTime 考试时间
+     * @return
+     */
+    public Long syncExam(String code, String name, String examTime) {
+//        String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
+//        String examSaveUrl = dictionaryConfig.syncDataDomain().getExamSaveUrl();
+//        String postUrl = hostUrl.concat(examSaveUrl);
+//        //参数
+//        Map<String, String> map = new HashMap<>();
+//        map.put("code", code);
+//        map.put("name", name);
+//        map.put("examTime", examTime);
+//        map.put("type", SAVE_EXAM_TYPE);
+//
+//        String result = HttpKit.sendPost(postUrl, getHeaders(examSaveUrl), map, null, null, null);
+//        JSONObject jsonObject = JSONObject.parseObject(result);
+//        if (jsonObject.containsKey("id")) {
+//            return Long.valueOf(jsonObject.get("id").toString());
+//        } else {
+//            throw ExceptionResultEnum.ERROR.exception("考试同步失败");
+//        }
+        return null;
+    }
+
+    /**
+     * 考生创建/更新接口
+     *
+     * @param examId      考试ID
+     * @param examNumber  准考证号
+     * @param studentCode 学号
+     * @param name        姓名
+     * @param college     学院
+     * @param className   班级名称
+     * @param teacher     教师姓名
+     * @param subjectCode 科目代码
+     * @param subjectName 科目名称
+     * @param packageCode 签到表编号
+     * @param paperType   试卷类型
+     * @param examSite    考点
+     * @param examRoom    考场
+     * @return
+     */
+    public Long syncStudent(String examId, String examNumber, String studentCode, String name, String college,
+                            String className, String teacher, String subjectCode, String subjectName,
+                            String packageCode, String paperType, String examSite, String examRoom) {
+        String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
+        String studentSaveUrl = dictionaryConfig.syncDataDomain().getStudentSaveUrl();
+        String postUrl = hostUrl.concat(studentSaveUrl);
+//        try {
+//            //参数
+//            Map<String, String> map = new HashMap<>();
+//            map.put("examId", examId);
+//            map.put("examNumber", examNumber);
+//            map.put("studentCode", studentCode);
+//            map.put("name", name);
+//            map.put("college", StringUtils.isBlank(college) ? "无" : college);
+//            map.put("className", StringUtils);
+//            map.put("teacher", "无");
+//            map.put("subjectCode", examStudent.getPaperNumber()); // 取试卷编号
+//            map.put("subjectName", examStudent.getCourseName());
+//
+//            String result = HttpKit.sendPost(postUrl, getHeaders(examPrintPlan.getSchoolId(), studentSaveUrl), map, null, null, null);
+//            JSONObject jsonObject = JSONObject.parseObject(result);
+//            if (jsonObject.containsKey("updateTime")) {
+//                UpdateWrapper<ExamStudent> updateWrapper = new UpdateWrapper<>();
+//                updateWrapper.lambda().set(ExamStudent::getSyncStatus, true).eq(ExamStudent::getId, examStudent.getId());
+//                examStudentService.update(updateWrapper);
+//            }
+//        } catch (Exception e) {
+//            throw ExceptionResultEnum.ERROR.exception(e.getMessage());
+//        }
+        return  null;
+    }
+
+    /**
+     * http请求头
+     *
+     * @param url
+     * @return
+     */
+    private Map<String, String> getHeaders(String url) {
+        long time = System.currentTimeMillis();
+        Map<String, String> header = new HashMap<>();
+        header.put(SystemConstant.HEADER_AUTHORIZATION, createSign(time, url));
+        header.put(SystemConstant.HEADER_TIME, String.valueOf(time));
+        return header;
+    }
+
+    /**
+     * 签名
+     *
+     * @param time
+     * @param url
+     * @return
+     */
+    private String createSign(long time, String url) {
+        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
+        BasicSchool basicSchool = commonCacheService.schoolCache(schoolId);
+        if (basicSchool == null) {
+            throw ExceptionResultEnum.ERROR.exception("学校不存在");
+        }
+        if (!basicSchool.getEnable()) {
+            throw ExceptionResultEnum.ERROR.exception("学校已禁用");
+        }
+
+        String signature = SignatureEntityTest.build(SignatureType.SECRET, POST_METHOD, url, time, basicSchool.getAccessKey(), basicSchool.getAccessSecret());
+        return signature;
+    }
+
+    private String validParam(String value, String defaulValue, boolean require, String name) {
+        if (require && StringUtils.isAnyBlank(name, defaulValue)) {
+            throw ExceptionResultEnum.ERROR.exception((StringUtils.isBlank(name) ? "" : name) + "值必填");
+        }
+
+        return StringUtils.isBlank(value) ? defaulValue : value;
+    }
+}