xiaofei 6 mēneši atpakaļ
vecāks
revīzija
557fbd6cad
16 mainītis faili ar 488 papildinājumiem un 219 dzēšanām
  1. 29 14
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/params/ExamCardParams.java
  2. 90 13
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/params/GenericExamCardParams.java
  3. 1 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/ExamCard.java
  4. 2 3
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/ExamCardService.java
  5. 117 117
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/ExamCardServiceImpl.java
  6. 1 1
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/templete/service/impl/PdfTaskLogicServiceImpl.java
  7. 12 2
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/CreatePdfUtil.java
  8. 22 3
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/GoogleBarCodeUtil.java
  9. 89 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/PdfUtil.java
  10. 69 4
      distributed-print/install/mysql/init/teachcloud_db.sql
  11. 42 50
      distributed-print/install/mysql/upgrade/3.4.2.sql
  12. 2 2
      distributed-print/install/mysql/upgrade/3.4.3.sql
  13. 8 6
      distributed-print/src/main/java/com/qmth/distributed/print/api/ExamCardController.java
  14. 1 1
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/contant/SystemConstant.java
  15. 2 2
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkPaperServiceImpl.java
  16. 1 1
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanAnswerCardServiceImpl.java

+ 29 - 14
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/params/ExamCardParams.java

@@ -1,6 +1,7 @@
 package com.qmth.distributed.print.business.bean.params;
 package com.qmth.distributed.print.business.bean.params;
 
 
 import com.qmth.distributed.print.business.entity.ExamCard;
 import com.qmth.distributed.print.business.entity.ExamCard;
+import org.springframework.web.multipart.MultipartFile;
 
 
 /**
 /**
  * @Date: 2021/4/8.
  * @Date: 2021/4/8.
@@ -8,8 +9,10 @@ import com.qmth.distributed.print.business.entity.ExamCard;
 public class ExamCardParams extends ExamCard {
 public class ExamCardParams extends ExamCard {
 
 
     private String examTaskId;
     private String examTaskId;
-    private String content;
-    private String htmlContent;
+    private MultipartFile contentFile;
+    private String contentMd5;
+    private MultipartFile htmlContentFile;
+    private String htmlContentMd5;
     private String custAttachmentId;
     private String custAttachmentId;
     private Long templateId;
     private Long templateId;
 
 
@@ -21,24 +24,36 @@ public class ExamCardParams extends ExamCard {
         this.examTaskId = examTaskId;
         this.examTaskId = examTaskId;
     }
     }
 
 
-    @Override
-    public String getContent() {
-        return content;
+    public MultipartFile getContentFile() {
+        return contentFile;
     }
     }
 
 
-    @Override
-    public void setContent(String content) {
-        this.content = content;
+    public void setContentFile(MultipartFile contentFile) {
+        this.contentFile = contentFile;
     }
     }
 
 
-    @Override
-    public String getHtmlContent() {
-        return htmlContent;
+    public String getContentMd5() {
+        return contentMd5;
     }
     }
 
 
-    @Override
-    public void setHtmlContent(String htmlContent) {
-        this.htmlContent = htmlContent;
+    public void setContentMd5(String contentMd5) {
+        this.contentMd5 = contentMd5;
+    }
+
+    public MultipartFile getHtmlContentFile() {
+        return htmlContentFile;
+    }
+
+    public void setHtmlContentFile(MultipartFile htmlContentFile) {
+        this.htmlContentFile = htmlContentFile;
+    }
+
+    public String getHtmlContentMd5() {
+        return htmlContentMd5;
+    }
+
+    public void setHtmlContentMd5(String htmlContentMd5) {
+        this.htmlContentMd5 = htmlContentMd5;
     }
     }
 
 
     public String getCustAttachmentId() {
     public String getCustAttachmentId() {

+ 90 - 13
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/params/GenericExamCardParams.java

@@ -1,7 +1,11 @@
 package com.qmth.distributed.print.business.bean.params;
 package com.qmth.distributed.print.business.bean.params;
 
 
+import com.qmth.distributed.print.business.enums.CardTypeEnum;
+import com.qmth.distributed.print.business.enums.ExamCardStatusEnum;
+import com.qmth.distributed.print.business.enums.MakeMethodEnum;
 import com.qmth.teachcloud.common.annotation.EditKey;
 import com.qmth.teachcloud.common.annotation.EditKey;
 import com.qmth.teachcloud.common.enums.CardCreateMethodEnum;
 import com.qmth.teachcloud.common.enums.CardCreateMethodEnum;
+import org.springframework.web.multipart.MultipartFile;
 
 
 /**
 /**
  * 题卡管理(通卡)参数
  * 题卡管理(通卡)参数
@@ -14,15 +18,24 @@ public class GenericExamCardParams {
     @EditKey
     @EditKey
     private Long id;
     private Long id;
     private String title;
     private String title;
+    private Long courseId;
+    private String courseName;
+    private MakeMethodEnum makeMethod;
+    private CardTypeEnum type;
     private CardCreateMethodEnum createMethod;
     private CardCreateMethodEnum createMethod;
-    private String content;
-    private String htmlContent;
+    private MultipartFile contentFile;
+    private String contentMd5;
+    private MultipartFile htmlContentFile;
+    private String htmlContentMd5;
     // 上传附件ID
     // 上传附件ID
     private Long attachmentId;
     private Long attachmentId;
     private String remark;
     private String remark;
     private Long cardRuleId;
     private Long cardRuleId;
-    private String status;
+    private ExamCardStatusEnum status;
     private String pageSize;
     private String pageSize;
+    private Boolean openAb;
+
+    private Long paperId;
 
 
     public Long getId() {
     public Long getId() {
         return id;
         return id;
@@ -40,6 +53,38 @@ public class GenericExamCardParams {
         this.title = title;
         this.title = title;
     }
     }
 
 
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public MakeMethodEnum getMakeMethod() {
+        return makeMethod;
+    }
+
+    public void setMakeMethod(MakeMethodEnum makeMethod) {
+        this.makeMethod = makeMethod;
+    }
+
+    public CardTypeEnum getType() {
+        return type;
+    }
+
+    public void setType(CardTypeEnum type) {
+        this.type = type;
+    }
+
     public CardCreateMethodEnum getCreateMethod() {
     public CardCreateMethodEnum getCreateMethod() {
         return createMethod;
         return createMethod;
     }
     }
@@ -48,20 +93,36 @@ public class GenericExamCardParams {
         this.createMethod = createMethod;
         this.createMethod = createMethod;
     }
     }
 
 
-    public String getContent() {
-        return content;
+    public MultipartFile getContentFile() {
+        return contentFile;
     }
     }
 
 
-    public void setContent(String content) {
-        this.content = content;
+    public void setContentFile(MultipartFile contentFile) {
+        this.contentFile = contentFile;
     }
     }
 
 
-    public String getHtmlContent() {
-        return htmlContent;
+    public String getContentMd5() {
+        return contentMd5;
     }
     }
 
 
-    public void setHtmlContent(String htmlContent) {
-        this.htmlContent = htmlContent;
+    public void setContentMd5(String contentMd5) {
+        this.contentMd5 = contentMd5;
+    }
+
+    public MultipartFile getHtmlContentFile() {
+        return htmlContentFile;
+    }
+
+    public void setHtmlContentFile(MultipartFile htmlContentFile) {
+        this.htmlContentFile = htmlContentFile;
+    }
+
+    public String getHtmlContentMd5() {
+        return htmlContentMd5;
+    }
+
+    public void setHtmlContentMd5(String htmlContentMd5) {
+        this.htmlContentMd5 = htmlContentMd5;
     }
     }
 
 
     public Long getAttachmentId() {
     public Long getAttachmentId() {
@@ -88,11 +149,11 @@ public class GenericExamCardParams {
         this.cardRuleId = cardRuleId;
         this.cardRuleId = cardRuleId;
     }
     }
 
 
-    public String getStatus() {
+    public ExamCardStatusEnum getStatus() {
         return status;
         return status;
     }
     }
 
 
-    public void setStatus(String status) {
+    public void setStatus(ExamCardStatusEnum status) {
         this.status = status;
         this.status = status;
     }
     }
 
 
@@ -103,4 +164,20 @@ public class GenericExamCardParams {
     public void setPageSize(String pageSize) {
     public void setPageSize(String pageSize) {
         this.pageSize = pageSize;
         this.pageSize = pageSize;
     }
     }
+
+    public Boolean getOpenAb() {
+        return openAb;
+    }
+
+    public void setOpenAb(Boolean openAb) {
+        this.openAb = openAb;
+    }
+
+    public Long getPaperId() {
+        return paperId;
+    }
+
+    public void setPaperId(Long paperId) {
+        this.paperId = paperId;
+    }
 }
 }

+ 1 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/ExamCard.java

@@ -3,6 +3,7 @@ package com.qmth.distributed.print.business.entity;
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.qmth.distributed.print.business.enums.ExamCardStatusEnum;
 import com.qmth.distributed.print.business.enums.ExamCardStatusEnum;

+ 2 - 3
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/ExamCardService.java

@@ -2,10 +2,9 @@ package com.qmth.distributed.print.business.service;
 
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.qmth.teachcloud.common.bean.marking.CardJpgResult;
-import com.qmth.distributed.print.business.bean.params.ExamCardParams;
 import com.qmth.distributed.print.business.bean.params.GenericExamCardParams;
 import com.qmth.distributed.print.business.bean.params.GenericExamCardParams;
 import com.qmth.distributed.print.business.entity.ExamCard;
 import com.qmth.distributed.print.business.entity.ExamCard;
+import com.qmth.teachcloud.common.bean.marking.CardJpgResult;
 
 
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 import java.util.List;
@@ -21,7 +20,7 @@ import java.util.Map;
  */
  */
 public interface ExamCardService extends IService<ExamCard> {
 public interface ExamCardService extends IService<ExamCard> {
 
 
-    Map<String, String> saveExamCard(ExamCardParams examCardParams) throws Exception;
+    Map<String, String> saveExamCard(GenericExamCardParams params) throws Exception;
 
 
     List<ExamCard> listSelectCard(Long examId, Long courseId, String paperNumber, Long paperId);
     List<ExamCard> listSelectCard(Long examId, Long courseId, String paperNumber, Long paperId);
 
 

+ 117 - 117
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/ExamCardServiceImpl.java

@@ -9,15 +9,18 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.boot.api.exception.ApiException;
 import com.qmth.boot.api.exception.ApiException;
 import com.qmth.boot.core.concurrent.service.ConcurrentService;
 import com.qmth.boot.core.concurrent.service.ConcurrentService;
-import com.qmth.distributed.print.business.bean.params.ExamCardParams;
 import com.qmth.distributed.print.business.bean.params.GenericExamCardParams;
 import com.qmth.distributed.print.business.bean.params.GenericExamCardParams;
-import com.qmth.distributed.print.business.entity.*;
+import com.qmth.distributed.print.business.entity.BasicCardRule;
+import com.qmth.distributed.print.business.entity.BasicExamRule;
+import com.qmth.distributed.print.business.entity.ExamCard;
+import com.qmth.distributed.print.business.entity.ExamTaskDetail;
 import com.qmth.distributed.print.business.enums.CardTypeEnum;
 import com.qmth.distributed.print.business.enums.CardTypeEnum;
 import com.qmth.distributed.print.business.enums.ExamCardStatusEnum;
 import com.qmth.distributed.print.business.enums.ExamCardStatusEnum;
 import com.qmth.distributed.print.business.enums.MakeMethodEnum;
 import com.qmth.distributed.print.business.enums.MakeMethodEnum;
 import com.qmth.distributed.print.business.mapper.ExamCardMapper;
 import com.qmth.distributed.print.business.mapper.ExamCardMapper;
 import com.qmth.distributed.print.business.service.*;
 import com.qmth.distributed.print.business.service.*;
 import com.qmth.distributed.print.business.util.CreatePdfUtil;
 import com.qmth.distributed.print.business.util.CreatePdfUtil;
+import com.qmth.distributed.print.business.util.ExamTaskUtil;
 import com.qmth.distributed.print.business.util.HtmlToJpgUtil;
 import com.qmth.distributed.print.business.util.HtmlToJpgUtil;
 import com.qmth.distributed.print.business.util.PdfUtil;
 import com.qmth.distributed.print.business.util.PdfUtil;
 import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
 import com.qmth.teachcloud.common.bean.dto.DataPermissionRule;
@@ -35,13 +38,13 @@ import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.enums.PageSizeEnum;
 import com.qmth.teachcloud.common.enums.PageSizeEnum;
 import com.qmth.teachcloud.common.enums.UploadFileEnum;
 import com.qmth.teachcloud.common.enums.UploadFileEnum;
 import com.qmth.teachcloud.common.service.*;
 import com.qmth.teachcloud.common.service.*;
-import com.qmth.distributed.print.business.util.ExamTaskUtil;
 import com.qmth.teachcloud.common.util.Base64Util;
 import com.qmth.teachcloud.common.util.Base64Util;
 import com.qmth.teachcloud.common.util.FileUtil;
 import com.qmth.teachcloud.common.util.FileUtil;
 import com.qmth.teachcloud.common.util.HtmlToPdfUtil;
 import com.qmth.teachcloud.common.util.HtmlToPdfUtil;
 import com.qmth.teachcloud.common.util.ServletUtil;
 import com.qmth.teachcloud.common.util.ServletUtil;
 import com.qmth.teachcloud.mark.enums.LockType;
 import com.qmth.teachcloud.mark.enums.LockType;
 import com.qmth.teachcloud.mark.service.ScanAnswerCardService;
 import com.qmth.teachcloud.mark.service.ScanAnswerCardService;
+import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeanUtils;
@@ -51,6 +54,7 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.File;
+import java.io.IOException;
 import java.util.*;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.Stream;
@@ -97,25 +101,25 @@ public class ExamCardServiceImpl extends ServiceImpl<ExamCardMapper, ExamCard> i
 
 
     @Transactional
     @Transactional
     @Override
     @Override
-    public Map<String, String> saveExamCard(ExamCardParams examCardParams) {
+    public Map<String, String> saveExamCard(GenericExamCardParams params) {
         Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
         Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
         SysUser user = (SysUser) ServletUtil.getRequestUser();
         SysUser user = (SysUser) ServletUtil.getRequestUser();
 
 
-        concurrentService.getReadWriteLock(LockType.CUSTOM_CARD_SAVE + "-" + examCardParams.getCourseId() + examCardParams.getTitle()).writeLock().lock();
+        concurrentService.getReadWriteLock(LockType.CUSTOM_CARD_SAVE + "-" + params.getCourseId() + params.getTitle()).writeLock().lock();
         // 新增
         // 新增
         ExamCard examCard;
         ExamCard examCard;
         try {
         try {
-            if (examCardParams.getTitle().getBytes().length > 80) {
+            if (params.getTitle().getBytes().length > 80) {
                 throw ExceptionResultEnum.ERROR.exception("标题最长只能输入80个字符");
                 throw ExceptionResultEnum.ERROR.exception("标题最长只能输入80个字符");
             }
             }
-            if (examCardParams.getId() == null) {
-                validateCardData(examCardParams);
+            if (params.getId() == null) {
+                validateCardData(params);
 
 
-                String title = examCardParams.getPaperId() != null ? examCardParams.getTitle() + SystemConstant.generateRandomNumber(4) : examCardParams.getTitle();
+                String title = params.getPaperId() != null ? params.getTitle() + SystemConstant.generateRandomNumber(4) : params.getTitle();
 
 
                 QueryWrapper<ExamCard> queryWrapper = new QueryWrapper<>();
                 QueryWrapper<ExamCard> queryWrapper = new QueryWrapper<>();
                 queryWrapper.lambda().eq(ExamCard::getSchoolId, schoolId)
                 queryWrapper.lambda().eq(ExamCard::getSchoolId, schoolId)
-                        .eq(ExamCard::getCourseId, examCardParams.getCourseId())
+                        .eq(ExamCard::getCourseId, params.getCourseId())
                         .eq(ExamCard::getTitle, title);
                         .eq(ExamCard::getTitle, title);
                 List<ExamCard> examCardList = this.list(queryWrapper);
                 List<ExamCard> examCardList = this.list(queryWrapper);
                 if (!examCardList.isEmpty()) {
                 if (!examCardList.isEmpty()) {
@@ -124,50 +128,68 @@ public class ExamCardServiceImpl extends ServiceImpl<ExamCardMapper, ExamCard> i
 
 
                 examCard = new ExamCard();
                 examCard = new ExamCard();
                 examCard.setSchoolId(schoolId);
                 examCard.setSchoolId(schoolId);
-                examCard.setCourseId(examCardParams.getCourseId());
+                examCard.setCourseId(params.getCourseId());
                 examCard.setTitle(title);
                 examCard.setTitle(title);
-                examCard.setMakeMethod(examCardParams.getMakeMethod());
-                examCard.setStatus(examCardParams.getStatus());
-                examCard.setType(examCardParams.getType());
-                examCard.setAttachmentId(examCardParams.getAttachmentId());
-                examCard.setCardRuleId(examCardParams.getCardRuleId());
-                examCard.setPaperId(examCardParams.getPaperId());
-                examCard.setPageSize(examCardParams.getPageSize());
-                examCard.setOpenAb(examCardParams.getOpenAb());
+                examCard.setMakeMethod(params.getMakeMethod());
+                examCard.setStatus(params.getStatus());
+                examCard.setType(params.getType());
+                examCard.setAttachmentId(params.getAttachmentId());
+                examCard.setCardRuleId(params.getCardRuleId());
+                examCard.setPaperId(params.getPaperId());
+                examCard.setPageSize(params.getPageSize());
+                examCard.setOpenAb(params.getOpenAb());
                 examCard.insertInfo(user.getId());
                 examCard.insertInfo(user.getId());
             }
             }
             // 修改
             // 修改
             else {
             else {
-                String title = examCardParams.getTitle();
+                String title = params.getTitle();
                 QueryWrapper<ExamCard> queryWrapper = new QueryWrapper<>();
                 QueryWrapper<ExamCard> queryWrapper = new QueryWrapper<>();
                 queryWrapper.lambda().eq(ExamCard::getSchoolId, schoolId)
                 queryWrapper.lambda().eq(ExamCard::getSchoolId, schoolId)
-                        .eq(ExamCard::getCourseId, examCardParams.getCourseId())
+                        .eq(ExamCard::getCourseId, params.getCourseId())
                         .eq(ExamCard::getTitle, title)
                         .eq(ExamCard::getTitle, title)
-                        .ne(ExamCard::getId, examCardParams.getId());
+                        .ne(ExamCard::getId, params.getId());
                 List<ExamCard> examCardList = this.list(queryWrapper);
                 List<ExamCard> examCardList = this.list(queryWrapper);
                 if (!examCardList.isEmpty()) {
                 if (!examCardList.isEmpty()) {
                     throw ExceptionResultEnum.ERROR.exception("题卡名称已存在");
                     throw ExceptionResultEnum.ERROR.exception("题卡名称已存在");
                 }
                 }
 
 
-                examCard = this.getById(examCardParams.getId());
+                examCard = this.getById(params.getId());
                 examCard.setTitle(title);
                 examCard.setTitle(title);
-                examCard.setStatus(examCardParams.getStatus());
-                examCard.setOpenAb(examCardParams.getOpenAb());
+                examCard.setStatus(params.getStatus());
+                examCard.setOpenAb(params.getOpenAb());
                 examCard.setUpdateId(user.getId());
                 examCard.setUpdateId(user.getId());
                 examCard.setUpdateTime(System.currentTimeMillis());
                 examCard.setUpdateTime(System.currentTimeMillis());
             }
             }
 
 
-            // 自动保存
-            if (ExamCardStatusEnum.SUBMIT.equals(examCardParams.getStatus())) {
-                examCard.setContent(examCardParams.getContent());
-                examCard.setStageContent(null);
-                examCard.setHtmlContent(examCardParams.getHtmlContent());
-                createJpgImage(examCard);
-            } else {
-                examCard.setStageContent(examCardParams.getContent());
+            try {
+                // content文件
+                String contentMd5 = DigestUtils.md5Hex(params.getContentFile().getBytes());
+                if (!Objects.equals(contentMd5, params.getContentMd5())) {
+                    throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
+                }
+                String content = SystemConstant.readContent(params.getContentFile().getInputStream());
+
+                // 自动保存
+                if (ExamCardStatusEnum.SUBMIT.equals(params.getStatus())) {
+                    examCard.setContent(content);
+                    examCard.setStageContent(null);
+
+                    // htmlContent文件
+                    String htmlMd5 = DigestUtils.md5Hex(params.getHtmlContentFile().getBytes());
+                    if (!Objects.equals(htmlMd5, params.getHtmlContentMd5())) {
+                        throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
+                    }
+                    String htmlContent = SystemConstant.readContent(params.getHtmlContentFile().getInputStream());
+                    examCard.setHtmlContent(htmlContent);
+                    createJpgImage(examCard);
+                } else {
+                    examCard.setStageContent(content);
+                }
+            } catch (IOException e) {
+                throw ExceptionResultEnum.ERROR.exception("题卡内容文件解析失败");
             }
             }
-            this.saveOrUpdate(examCard);
 
 
+            this.saveOrUpdate(examCard);
 
 
             Map<String, String> map = new HashMap<>();
             Map<String, String> map = new HashMap<>();
             map.put("id", String.valueOf(examCard.getId()));
             map.put("id", String.valueOf(examCard.getId()));
@@ -176,7 +198,7 @@ public class ExamCardServiceImpl extends ServiceImpl<ExamCardMapper, ExamCard> i
         } catch (ApiException e) {
         } catch (ApiException e) {
             throw new RuntimeException(e);
             throw new RuntimeException(e);
         } finally {
         } finally {
-            concurrentService.getReadWriteLock(LockType.CUSTOM_CARD_SAVE + "-" + examCardParams.getCourseId() + examCardParams.getTitle()).writeLock().unlock();
+            concurrentService.getReadWriteLock(LockType.CUSTOM_CARD_SAVE + "-" + params.getCourseId() + params.getTitle()).writeLock().unlock();
         }
         }
     }
     }
 
 
@@ -263,52 +285,14 @@ public class ExamCardServiceImpl extends ServiceImpl<ExamCardMapper, ExamCard> i
                 examCard.setId(SystemConstant.getDbUuid());
                 examCard.setId(SystemConstant.getDbUuid());
                 examCard.setSchoolId(schoolId);
                 examCard.setSchoolId(schoolId);
                 examCard.setOrgId(sysUser.getOrgId());
                 examCard.setOrgId(sysUser.getOrgId());
-
-                examCard.setTitle(params.getTitle());
                 examCard.setMakeMethod(MakeMethodEnum.SELECT); // 默认值
                 examCard.setMakeMethod(MakeMethodEnum.SELECT); // 默认值
                 // 上传题卡,状态为提交
                 // 上传题卡,状态为提交
-                examCard.setStatus(CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod()) ? ExamCardStatusEnum.SUBMIT : ExamCardStatusEnum.valueOf(params.getStatus()));
                 examCard.setType(CardTypeEnum.GENERIC); // 默认值
                 examCard.setType(CardTypeEnum.GENERIC); // 默认值
                 examCard.setCreateMethod(params.getCreateMethod());
                 examCard.setCreateMethod(params.getCreateMethod());
-                examCard.setAttachmentId(params.getAttachmentId());
                 examCard.setEnable(true); // 默认值
                 examCard.setEnable(true); // 默认值
-                examCard.setCardRuleId(params.getCardRuleId());
-                examCard.setRemark(params.getRemark());
-                examCard.setPageSize(CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod()) ? PageSizeEnum.A3.name() : params.getPageSize());
                 examCard.setCreateId(sysUser.getId());
                 examCard.setCreateId(sysUser.getId());
                 examCard.setCreateTime(System.currentTimeMillis());
                 examCard.setCreateTime(System.currentTimeMillis());
                 this.save(examCard);
                 this.save(examCard);
-
-                // 方式为上传
-                String htmlContent;
-                if (CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod())) {
-                    if (params.getAttachmentId() == null) {
-                        throw ExceptionResultEnum.ERROR.exception("请上传模板文件");
-                    }
-                    BasicAttachment attachment = basicAttachmentService.getById(params.getAttachmentId());
-                    if (attachment == null) {
-                        throw ExceptionResultEnum.ERROR.exception("无模板文件记录,请重新上传");
-                    }
-                    if (!SystemConstant.HTML_PREFIX.equalsIgnoreCase(attachment.getType())) {
-                        throw ExceptionResultEnum.ERROR.exception("只能上传后缀为.html文件");
-                    }
-                    htmlContent = teachcloudCommonService.readFileContent(attachment.getPath());
-                    examCard.setHtmlContent(htmlContent);
-                }
-                // 方式为自定义
-                else if (CardCreateMethodEnum.STANDARD.equals(params.getCreateMethod())
-                        || CardCreateMethodEnum.FREE.equals(params.getCreateMethod())) {
-                    if (ExamCardStatusEnum.SUBMIT.name().equals(params.getStatus())) {
-                        examCard.setContent(params.getContent());
-                        examCard.setStageContent(null);
-                        htmlContent = params.getHtmlContent();
-                        examCard.setHtmlContent(htmlContent);
-                    } else {
-                        examCard.setStageContent(params.getContent());
-                    }
-                } else {
-                    throw ExceptionResultEnum.ERROR.exception("不存在的题卡创建方式");
-                }
             }
             }
             // 修改
             // 修改
             else {
             else {
@@ -316,47 +300,65 @@ public class ExamCardServiceImpl extends ServiceImpl<ExamCardMapper, ExamCard> i
                 if (checkTitleExamCardList != null && !Objects.equals(params.getId(), examCard.getId())) {
                 if (checkTitleExamCardList != null && !Objects.equals(params.getId(), examCard.getId())) {
                     throw ExceptionResultEnum.ERROR.exception("题卡名称重复");
                     throw ExceptionResultEnum.ERROR.exception("题卡名称重复");
                 }
                 }
-                examCard.setTitle(params.getTitle());
-                examCard.setAttachmentId(params.getAttachmentId());
-                examCard.setCardRuleId(params.getCardRuleId());
-                // 上传题卡,状态为提交
-                examCard.setStatus(CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod()) ? ExamCardStatusEnum.SUBMIT : ExamCardStatusEnum.valueOf(params.getStatus()));
-                examCard.setRemark(params.getRemark());
-                examCard.setPageSize(CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod()) ? PageSizeEnum.A3.name() : examCard.getPageSize());
                 examCard.setUpdateId(sysUser.getId());
                 examCard.setUpdateId(sysUser.getId());
                 examCard.setUpdateTime(System.currentTimeMillis());
                 examCard.setUpdateTime(System.currentTimeMillis());
-
-                String htmlContent;
-                // 方式为上传
-                if (CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod())) {
-                    if (params.getAttachmentId() == null) {
-                        throw ExceptionResultEnum.ERROR.exception("请上传模板文件");
-                    }
-                    BasicAttachment attachment = basicAttachmentService.getById(params.getAttachmentId());
-                    if (attachment == null) {
-                        throw ExceptionResultEnum.ERROR.exception("无模板文件记录,请重新上传");
-                    }
-                    if (!SystemConstant.HTML_PREFIX.equalsIgnoreCase(attachment.getType())) {
-                        throw ExceptionResultEnum.ERROR.exception("只能上传后缀为.html文件");
+            }
+            examCard.setTitle(params.getTitle());
+            examCard.setAttachmentId(params.getAttachmentId());
+            examCard.setCardRuleId(params.getCardRuleId());
+            // 上传题卡,状态为提交
+            examCard.setStatus(CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod()) ? ExamCardStatusEnum.SUBMIT : params.getStatus());
+            examCard.setRemark(params.getRemark());
+            examCard.setPageSize(CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod()) ? PageSizeEnum.A3.name() : examCard.getPageSize());
+
+            // 方式为上传
+
+            if (CardCreateMethodEnum.UPLOAD.equals(params.getCreateMethod())) {
+                if (params.getAttachmentId() == null) {
+                    throw ExceptionResultEnum.ERROR.exception("请上传模板文件");
+                }
+                BasicAttachment attachment = basicAttachmentService.getById(params.getAttachmentId());
+                if (attachment == null) {
+                    throw ExceptionResultEnum.ERROR.exception("无模板文件记录,请重新上传");
+                }
+                if (!SystemConstant.HTML_PREFIX.equalsIgnoreCase(attachment.getType())) {
+                    throw ExceptionResultEnum.ERROR.exception("只能上传后缀为.html文件");
+                }
+                String htmlContent = teachcloudCommonService.readFileContent(attachment.getPath());
+                examCard.setHtmlContent(htmlContent);
+            }
+            // 方式为自定义
+            else if (CardCreateMethodEnum.STANDARD.equals(params.getCreateMethod())
+                    || CardCreateMethodEnum.FREE.equals(params.getCreateMethod())) {
+                try {
+                    // content文件
+                    String contentMd5 = DigestUtils.md5Hex(params.getContentFile().getBytes());
+                    if (!Objects.equals(contentMd5, params.getContentMd5())) {
+                        throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
                     }
                     }
-                    htmlContent = teachcloudCommonService.readFileContent(attachment.getPath());
-                    examCard.setHtmlContent(htmlContent);
+                    String content = SystemConstant.readContent(params.getContentFile().getInputStream());
 
 
-                }
-                // 方式为自定义
-                else if (CardCreateMethodEnum.STANDARD.equals(params.getCreateMethod()) || CardCreateMethodEnum.FREE.equals(params.getCreateMethod())) {
                     if (ExamCardStatusEnum.SUBMIT.name().equals(params.getStatus())) {
                     if (ExamCardStatusEnum.SUBMIT.name().equals(params.getStatus())) {
-                        examCard.setContent(params.getContent());
+                        examCard.setContent(content);
                         examCard.setStageContent(null);
                         examCard.setStageContent(null);
-                        htmlContent = params.getHtmlContent();
+
+                        // content文件
+                        String htmlMd5 = DigestUtils.md5Hex(params.getHtmlContentFile().getBytes());
+                        if (!Objects.equals(htmlMd5, params.getHtmlContentMd5())) {
+                            throw ExceptionResultEnum.MD5_EQUALS_FALSE.exception();
+                        }
+                        String htmlContent = SystemConstant.readContent(params.getHtmlContentFile().getInputStream());
                         examCard.setHtmlContent(htmlContent);
                         examCard.setHtmlContent(htmlContent);
                     } else {
                     } else {
-                        examCard.setStageContent(params.getContent());
+                        examCard.setStageContent(content);
                     }
                     }
-                } else {
-                    throw ExceptionResultEnum.ERROR.exception("不存在的题卡创建方式");
+                } catch (IOException e) {
+                    throw ExceptionResultEnum.ERROR.exception("题卡内容文件解析失败");
                 }
                 }
+            } else {
+                throw ExceptionResultEnum.ERROR.exception("不存在的题卡创建方式");
             }
             }
+
             if (ExamCardStatusEnum.SUBMIT.name().equals(params.getStatus())) {
             if (ExamCardStatusEnum.SUBMIT.name().equals(params.getStatus())) {
                 // 生成题卡图片
                 // 生成题卡图片
                 createJpgImage(examCard);
                 createJpgImage(examCard);
@@ -559,37 +561,35 @@ public class ExamCardServiceImpl extends ServiceImpl<ExamCardMapper, ExamCard> i
     /**
     /**
      * 数据验证
      * 数据验证
      *
      *
-     * @param examCardParams
+     * @param params
      */
      */
-    private void validateCardData(ExamCardParams examCardParams) {
+    private void validateCardData(GenericExamCardParams params) {
         BasicExamRule basicExamRule = basicExamRuleService.getBySchoolId();
         BasicExamRule basicExamRule = basicExamRuleService.getBySchoolId();
         if (basicExamRule == null) {
         if (basicExamRule == null) {
             throw ExceptionResultEnum.ERROR.exception("通用规则未设置");
             throw ExceptionResultEnum.ERROR.exception("通用规则未设置");
         }
         }
 
 
-        if (examCardParams.getStatus() == null) {
+        if (params.getStatus() == null) {
             throw ExceptionResultEnum.ERROR.exception("提交方式不能为空");
             throw ExceptionResultEnum.ERROR.exception("提交方式不能为空");
         }
         }
-        if (CardTypeEnum.CUSTOM.name().equals(examCardParams.getType().name())) {
-            if (examCardParams.getCourseId() == null) {
+        if (CardTypeEnum.CUSTOM.name().equals(params.getType().name())) {
+            if (params.getCourseId() == null) {
                 throw ExceptionResultEnum.ERROR.exception("未获取到课程信息");
                 throw ExceptionResultEnum.ERROR.exception("未获取到课程信息");
             }
             }
-            if (MakeMethodEnum.SELECT.name().equals(examCardParams.getMakeMethod().name())) {
-                if (examCardParams.getId() == null) {
+            if (MakeMethodEnum.SELECT.name().equals(params.getMakeMethod().name())) {
+                if (params.getId() == null) {
                     throw ExceptionResultEnum.ERROR.exception("选择已有题卡时,题卡ID不能为空");
                     throw ExceptionResultEnum.ERROR.exception("选择已有题卡时,题卡ID不能为空");
                 }
                 }
                 // 当前选择题卡是否已绑定(不再校验是否使用  2023-06-20)
                 // 当前选择题卡是否已绑定(不再校验是否使用  2023-06-20)
-            } else if (MakeMethodEnum.SELF.name().equals(examCardParams.getMakeMethod().name())) {
-                if (StringUtils.isBlank(examCardParams.getTitle())) {
+            } else if (MakeMethodEnum.SELF.name().equals(params.getMakeMethod().name())) {
+                if (StringUtils.isBlank(params.getTitle())) {
                     throw ExceptionResultEnum.ERROR.exception("题卡标题不能为空");
                     throw ExceptionResultEnum.ERROR.exception("题卡标题不能为空");
                 }
                 }
-                if (StringUtils.isBlank(examCardParams.getContent())) {
+                if (params.getContentFile() == null || params.getContentFile().getSize() == 0) {
                     throw ExceptionResultEnum.ERROR.exception("题卡内容不能为空");
                     throw ExceptionResultEnum.ERROR.exception("题卡内容不能为空");
                 }
                 }
-                if (ExamCardStatusEnum.SUBMIT.name().equals(examCardParams.getStatus().name())) {
-                    if (StringUtils.isBlank(examCardParams.getHtmlContent())) {
-                        throw ExceptionResultEnum.ERROR.exception("提交题卡时,html内容不能为空");
-                    }
+                if (params.getHtmlContentFile() == null || params.getHtmlContentFile().getSize() == 0) {
+                    throw ExceptionResultEnum.ERROR.exception("提交题卡时,html内容不能为空");
                 }
                 }
             } else {
             } else {
                 throw ExceptionResultEnum.ERROR.exception("不支持的题卡制作方式");
                 throw ExceptionResultEnum.ERROR.exception("不支持的题卡制作方式");

+ 1 - 1
distributed-print-business/src/main/java/com/qmth/distributed/print/business/templete/service/impl/PdfTaskLogicServiceImpl.java

@@ -261,7 +261,7 @@ public class PdfTaskLogicServiceImpl implements PdfTaskLogicService {
                 }
                 }
 
 
                 // 获取备份试卷
                 // 获取备份试卷
-                PdfDto pdfDto = createPdfUtil.getBackupPaperPdfList(paperPdfDtoList, backupCount, createPdfDto);
+                PdfDto pdfDto = createPdfUtil.getBackupPaperPdfList(paperPdfDtoList, backupCount, createPdfDto, examDetail.getPackageCode());
                 if (Objects.nonNull(pdfDto)) {
                 if (Objects.nonNull(pdfDto)) {
                     examDetailCourse.setPaperPagesA3(pdfDto.getPageCount());
                     examDetailCourse.setPaperPagesA3(pdfDto.getPageCount());
                 }
                 }

+ 12 - 2
distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/CreatePdfUtil.java

@@ -4,6 +4,8 @@ import cn.hutool.core.date.DateUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
+import com.itextpdf.text.BadElementException;
+import com.itextpdf.text.Image;
 import com.itextpdf.text.pdf.PdfReader;
 import com.itextpdf.text.pdf.PdfReader;
 import com.qmth.boot.core.concurrent.annotation.Lockable;
 import com.qmth.boot.core.concurrent.annotation.Lockable;
 import com.qmth.distributed.print.business.bean.dto.*;
 import com.qmth.distributed.print.business.bean.dto.*;
@@ -393,7 +395,7 @@ public class CreatePdfUtil {
      * @param backupCount  备份数量
      * @param backupCount  备份数量
      * @param createPdfDto 生成pdf对象
      * @param createPdfDto 生成pdf对象
      */
      */
-    public PdfDto getBackupPaperPdfList(List<PaperPdfDto> paperPdfDto, Integer backupCount, CreatePdfDto createPdfDto) throws
+    public PdfDto getBackupPaperPdfList(List<PaperPdfDto> paperPdfDto, Integer backupCount, CreatePdfDto createPdfDto, String packageCode) throws
             IOException {
             IOException {
         Set<Integer> pagesList = new HashSet<>();
         Set<Integer> pagesList = new HashSet<>();
         boolean tag = false;
         boolean tag = false;
@@ -404,7 +406,15 @@ public class CreatePdfUtil {
                 tag = pages > 2;
                 tag = pages > 2;
                 PdfDto pdfDto = PdfUtil.addPdfPage(dto.getFile());
                 PdfDto pdfDto = PdfUtil.addPdfPage(dto.getFile());
                 for (int j = 1; j <= backupCount; j++) {
                 for (int j = 1; j <= backupCount; j++) {
-                    createPdfDto.getPaperPdfList().add(new PdfDto(dto.getFile().getPath(), dto.getPageSize(), pdfDto.getPageCount()));
+                    String backupPackageCode = packageCode + String.format(SystemConstant.DATE_TIME_FORMAT, j);
+                    byte[] barCode = GoogleBarCodeUtil.createBarCode(backupPackageCode);
+                    try {
+                        Image image = Image.getInstance(barCode);
+                        File waterFile = PdfUtil.addPaperWaterMark(dto.getFile(), image, backupPackageCode, 1f, 12, 0);
+                        createPdfDto.getPaperPdfList().add(new PdfDto(waterFile.getPath(), dto.getPageSize(), pdfDto.getPageCount()));
+                    } catch (BadElementException e) {
+                        throw new RuntimeException(e);
+                    }
                 }
                 }
             }
             }
             int pageCount = pagesList.stream().mapToInt(Integer::intValue).sum();
             int pageCount = pagesList.stream().mapToInt(Integer::intValue).sum();

+ 22 - 3
distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/GoogleBarCodeUtil.java

@@ -166,15 +166,15 @@ public class GoogleBarCodeUtil {
     /**
     /**
      * 创建条码
      * 创建条码
      *
      *
-     * @param contennt
+     * @param content
      * @param word
      * @param word
      * @return
      * @return
      * @throws IOException
      * @throws IOException
      */
      */
-    public static String createBarCode(String contennt, boolean word) {
+    public static String createBarCode(String content, boolean word) {
         ByteArrayOutputStream os = null;
         ByteArrayOutputStream os = null;
         try {
         try {
-            BufferedImage image = GoogleBarCodeUtil.getBarCode(contennt);
+            BufferedImage image = GoogleBarCodeUtil.getBarCode(content);
              os = new ByteArrayOutputStream();
              os = new ByteArrayOutputStream();
             ImageIO.write(image, "png", os);
             ImageIO.write(image, "png", os);
             String imageBase64 = Base64Util.encode(os.toByteArray());
             String imageBase64 = Base64Util.encode(os.toByteArray());
@@ -191,4 +191,23 @@ public class GoogleBarCodeUtil {
             }
             }
         }
         }
     }
     }
+
+    public static byte[] createBarCode(String content) {
+        ByteArrayOutputStream os = null;
+        try {
+            BufferedImage image = GoogleBarCodeUtil.getBarCode(content);
+            os = new ByteArrayOutputStream();
+            ImageIO.write(image, "png", os);
+            return os.toByteArray();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            try {
+                os.flush();
+                os.close();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
 }
 }

+ 89 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/util/PdfUtil.java

@@ -1,6 +1,7 @@
 package com.qmth.distributed.print.business.util;
 package com.qmth.distributed.print.business.util;
 
 
 import com.itextpdf.awt.AsianFontMapper;
 import com.itextpdf.awt.AsianFontMapper;
+import com.itextpdf.text.Image;
 import com.itextpdf.text.Rectangle;
 import com.itextpdf.text.Rectangle;
 import com.itextpdf.text.*;
 import com.itextpdf.text.*;
 import com.itextpdf.text.pdf.*;
 import com.itextpdf.text.pdf.*;
@@ -540,6 +541,94 @@ public class PdfUtil {
         return null;
         return null;
     }
     }
 
 
+    /**
+     * PDF添加试卷备卡
+     *
+     * @param inputFile 需要添加水印的文件
+     * @param image     需要添加的图片
+     * @param code      需要添加的水印文字
+     * @param opacity   水印字体透明度(0-1)
+     * @param fontsize  水印字体大小
+     * @param rotation  水印倾斜角度(0-360)
+     */
+    public static File addPaperWaterMark(File inputFile, Image image, String code, float opacity, int fontsize, int rotation) {
+        if (!inputFile.exists()) {
+            throw ExceptionResultEnum.ERROR.exception("试卷文件不存在");
+        }
+
+        if (image == null || code == null) {
+            return inputFile;
+        }
+
+        StringJoiner stringJoiner = new StringJoiner("");
+        stringJoiner = SystemConstant.getDirName(stringJoiner, UploadFileEnum.PAPER, true);
+        stringJoiner.add(SystemConstant.getNanoId()).add(SystemConstant.PDF_PREFIX);
+
+        File zipFile = SystemConstant.getFileTempDirVarForZip(stringJoiner.toString(), null);
+        String outputPath = stringJoiner.toString();
+        PdfReader reader = null;
+        PdfStamper stamper = null;
+        File outputFile = null;
+        try {
+            outputFile = new File(outputPath);
+            if (!outputFile.exists()) {
+                outputFile.getParentFile().mkdirs();
+                outputFile.createNewFile();
+            }
+            reader = new PdfReader(new FileInputStream(inputFile));
+            stamper = new PdfStamper(reader, new FileOutputStream(outputPath));
+            BaseFont base = BaseFont.createFont(AsianFontMapper.ChineseSimplifiedFont, AsianFontMapper.ChineseSimplifiedEncoding_H, BaseFont.NOT_EMBEDDED);
+            PdfGState gs = new PdfGState();
+            //这里是透明度设置
+            gs.setFillOpacity(opacity);
+            //这里是条纹不透明度
+            gs.setStrokeOpacity(0.2f);
+            Rectangle pageRect;
+            PdfContentByte under;
+            // 只打奇数页水印
+            pageRect = reader.getPageSizeWithRotation(1);
+            under = stamper.getOverContent(1);  //在内容上方添加水印
+            under.saveState();
+            under.setGState(gs);
+            under.beginText();
+            under.setFontAndSize(base, fontsize); //这里是水印字体大小
+
+            image.setAbsolutePosition(pageRect.getWidth() * 0.75f, pageRect.getHeight() - 40);
+            image.scaleAbsolute(150, 30);
+            under.addImage(image);
+
+            under.showTextAligned(Element.ALIGN_LEFT, code, pageRect.getWidth() * 0.75f + 50, pageRect.getHeight() - 50, rotation);
+
+            //添加水印文字
+            under.endText();
+            return outputFile;
+        } catch (IOException | DocumentException e) {
+            System.out.println("添加水印失败!错误信息为: " + e);
+            e.printStackTrace();
+        } finally {
+            //关闭流
+            if (stamper != null) {
+                try {
+                    stamper.close();
+                } catch (DocumentException e) {
+                    e.printStackTrace();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (reader != null) {
+                reader.close();
+            }
+            if (outputFile.exists()) {
+//                outputFile.delete();
+            }
+            if (Objects.nonNull(zipFile)) {
+                zipFile.delete();
+            }
+        }
+        return null;
+    }
+
     /**
     /**
      * pdf添加水印
      * pdf添加水印
      *
      *

+ 69 - 4
distributed-print/install/mysql/init/teachcloud_db.sql

@@ -240,7 +240,7 @@ CREATE TABLE IF NOT EXISTS `basic_message` (
   `id` bigint NOT NULL COMMENT '主键',
   `id` bigint NOT NULL COMMENT '主键',
   `school_id` bigint DEFAULT NULL COMMENT '学校id',
   `school_id` bigint DEFAULT NULL COMMENT '学校id',
   `org_id` bigint DEFAULT NULL COMMENT '机构id',
   `org_id` bigint DEFAULT NULL COMMENT '机构id',
-  `user_id` bigint NOT NULL COMMENT '消息接收人用户',
+  `user_id` bigint DEFAULT NULL COMMENT '消息接收人用户',
   `user_name` varchar(100) DEFAULT NULL COMMENT '用户名称',
   `user_name` varchar(100) DEFAULT NULL COMMENT '用户名称',
   `mobile_number` varchar(11) DEFAULT NULL COMMENT '电话号码',
   `mobile_number` varchar(11) DEFAULT NULL COMMENT '电话号码',
   `paper_number` varchar(100) DEFAULT NULL COMMENT '试卷编号',
   `paper_number` varchar(100) DEFAULT NULL COMMENT '试卷编号',
@@ -248,11 +248,11 @@ CREATE TABLE IF NOT EXISTS `basic_message` (
   `message_type` varchar(45) DEFAULT NULL COMMENT '消息类型',
   `message_type` varchar(45) DEFAULT NULL COMMENT '消息类型',
   `business_operate` varchar(45) NOT NULL COMMENT '业务操作',
   `business_operate` varchar(45) NOT NULL COMMENT '业务操作',
   `business_id` bigint DEFAULT NULL COMMENT '业务id',
   `business_id` bigint DEFAULT NULL COMMENT '业务id',
-  `template_code` varchar(45) NOT NULL COMMENT '消息模板代码',
+  `template_code` varchar(45) DEFAULT NULL COMMENT '消息模板代码',
   `variable_params` text COMMENT '变量参数内容',
   `variable_params` text COMMENT '变量参数内容',
   `template_content` mediumtext COMMENT '模板内容',
   `template_content` mediumtext COMMENT '模板内容',
   `send_status` varchar(45) DEFAULT NULL COMMENT '消息发送状态',
   `send_status` varchar(45) DEFAULT NULL COMMENT '消息发送状态',
-  `send_result` varchar(200) DEFAULT NULL COMMENT '消息发送结果',
+  `send_result` varchar(2000) DEFAULT NULL COMMENT '消息发送结果',
   `remark` text COMMENT '备注',
   `remark` text COMMENT '备注',
   `create_id` bigint DEFAULT NULL COMMENT '创建人',
   `create_id` bigint DEFAULT NULL COMMENT '创建人',
   `create_time` bigint DEFAULT NULL COMMENT '创建时间',
   `create_time` bigint DEFAULT NULL COMMENT '创建时间',
@@ -658,7 +658,7 @@ CREATE TABLE IF NOT EXISTS `client_upgrade` (
   PRIMARY KEY (`platform`, `tool_type`)
   PRIMARY KEY (`platform`, `tool_type`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 
 
-INSERT INTO `client_upgrade` (`platform`,`install_path`,`install_upload_time`) select 'WINDOWS','{"md5":"b458068a408a11a82625b65e7e252eff","path":"知学知考-印刷扫描客户端_V3.4.0.exe","type":"oss","uploadType":"UPGRADE"}', unix_timestamp()*1000;
+INSERT INTO `client_upgrade` (`platform`,`tool_type`, `install_path`,`install_upload_time`) select 'WINDOWS','TEACHCLOUD_CLIENT','{"md5":"b458068a408a11a82625b65e7e252eff","path":"知学知考-印刷扫描客户端_V3.4.0.exe","type":"oss","uploadType":"UPGRADE"}', unix_timestamp()*1000;
 
 
 -- ----------------------------
 -- ----------------------------
 -- Table structure for cloud_user_push_status
 -- Table structure for cloud_user_push_status
@@ -2682,6 +2682,7 @@ INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_nam
 INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_name`, `config_value`, `remark`, `enable`, `sort`, `create_id`, `create_time`, `update_id`, `update_time`) VALUES (41, NULL, NULL, 'package.barcode.position', '签到表条码坐标', '[0.7,0,0.3,0.1]', NULL, 1, 17, 1, NULL, NULL, NULL);
 INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_name`, `config_value`, `remark`, `enable`, `sort`, `create_id`, `create_time`, `update_id`, `update_time`) VALUES (41, NULL, NULL, 'package.barcode.position', '签到表条码坐标', '[0.7,0,0.3,0.1]', NULL, 1, 17, 1, NULL, NULL, NULL);
 INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_name`, `config_value`, `remark`, `enable`, `sort`, `create_id`, `create_time`, `update_id`, `update_time`) VALUES (42, NULL, NULL, 'create.pdf.job.db.limit', '创建pdf查询条数', 'limit 0,100', NULL, 1, 18, 1, NULL, NULL, NULL);
 INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_name`, `config_value`, `remark`, `enable`, `sort`, `create_id`, `create_time`, `update_id`, `update_time`) VALUES (42, NULL, NULL, 'create.pdf.job.db.limit', '创建pdf查询条数', 'limit 0,100', NULL, 1, 18, 1, NULL, NULL, NULL);
 INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_name`, `config_value`, `remark`, `enable`, `sort`, `create_id`, `create_time`, `update_id`, `update_time`) VALUES (43, NULL, NULL, 'sys.card.size.list', '全局题卡格式清单', '[A3, 8K]', NULL, 1, 2, 1, NULL, NULL, NULL);
 INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_name`, `config_value`, `remark`, `enable`, `sort`, `create_id`, `create_time`, `update_id`, `update_time`) VALUES (43, NULL, NULL, 'sys.card.size.list', '全局题卡格式清单', '[A3, 8K]', NULL, 1, 2, 1, NULL, NULL, NULL);
+INSERT INTO `sys_config` (`id`, `school_id`, `org_id`, `config_key`, `config_name`, `config_value`, `remark`, `enable`, `sort`, `create_id`, `create_time`, `update_id`, `update_time`) VALUES ('50', NULL, NULL, 'sms.sync.data.code', '数据同步失败通知', 'SMS_475870952', NULL, 1, 19, 1, '1733878447209', NULL, '1733878447249');
 
 
 -- ----------------------------
 -- ----------------------------
 -- Table structure for sys_org
 -- Table structure for sys_org
@@ -5952,6 +5953,70 @@ CREATE TABLE IF NOT EXISTS `t_school_role` (
   PRIMARY KEY (`id`) USING BTREE
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='学校角色权限表';
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='学校角色权限表';
 
 
+-- ----------------------------
+-- Table structure for t_sync_data
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `t_sync_data` (
+       `school_id` BIGINT(20) NOT NULL COMMENT '学校ID',
+       `semester_id` BIGINT(20) NOT NULL COMMENT '学期ID',
+       `exam_id` BIGINT(20) NOT NULL COMMENT '考试ID',
+       `data_type` VARCHAR(45) NOT NULL COMMENT '同步数据类型(A:机构同步,B:用户同步,C:课程同步,D:考生同步,E:命题任务同步)',
+       `start_time` BIGINT(20) NULL COMMENT '同步开始时间',
+       `end_time` BIGINT(20) NULL COMMENT '同步结束时间',
+       `last_sync_time` BIGINT(20) NULL COMMENT '最后一次同步时间',
+       `status` TINYINT(1) NULL DEFAULT 0 COMMENT '状态(0:未同步/已完成,1:同步中)',
+       `enable` TINYINT(1) NULL DEFAULT 1 COMMENT '启用/禁用',
+       `cron` VARCHAR(45) NULL COMMENT 'cron表达式',
+       `phone_number` VARCHAR(100) NULL COMMENT '手机号(多个用逗号分隔)',
+       `detail` VARCHAR(200) NULL COMMENT '同步明细',
+       `create_time` BIGINT(20) NULL COMMENT '创建时间',
+       `update_time` BIGINT(20) NULL COMMENT '更新时间',
+       PRIMARY KEY (`school_id`, `exam_id`))
+    COMMENT = '数据同步设置';
+
+-- ----------------------------
+-- Table structure for t_sync_data_log
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `t_sync_data_log` (
+       `id` BIGINT(20) NOT NULL,
+       `school_id` BIGINT(20) NOT NULL COMMENT '学校ID',
+       `exam_id` BIGINT(20) NOT NULL COMMENT '考试ID',
+       `data_type` VARCHAR(45) NOT NULL COMMENT '同步数据类型',
+       `start_time` BIGINT(20) NULL COMMENT '开始同步时间',
+       `end_time` BIGINT(20) NULL COMMENT '结束同步时间',
+       `count` INT NULL COMMENT '同步数量',
+       `error_msg` MEDIUMTEXT NULL COMMENT '失败原因',
+       PRIMARY KEY (`id`))
+    COMMENT = '数据同步日志';
+
+-- ----------------------------
+-- Table structure for t_sync_data_student
+-- ----------------------------
+CREATE TABLE IF NOT EXISTS `t_sync_data_student`  (
+        `id` bigint NOT NULL COMMENT 'id',
+        `school_id` bigint NOT NULL COMMENT '学校id',
+        `exam_id` bigint NOT NULL COMMENT '考试id',
+        `course_code` varchar(100) NULL DEFAULT NULL COMMENT '课程编号',
+        `course_name` varchar(200) NULL DEFAULT NULL COMMENT '课程名称',
+        `course_college_name` varchar(200) NULL DEFAULT NULL COMMENT '课程开课学院',
+        `paper_number` varchar(100) NULL DEFAULT NULL COMMENT '试卷编号',
+        `student_name` varchar(50) NOT NULL COMMENT '姓名',
+        `student_code` varchar(50) NOT NULL COMMENT '学号',
+        `site_number` varchar(10) NULL DEFAULT NULL COMMENT '座位号',
+        `student_college_name` varchar(100) NULL DEFAULT NULL COMMENT '学院',
+        `major_name` varchar(100) NULL DEFAULT NULL COMMENT '专业',
+        `teach_class_name` varchar(100) NULL DEFAULT NULL,
+        `class_name` varchar(100) NULL DEFAULT NULL,
+        `exam_start_time` bigint NULL DEFAULT NULL COMMENT '考试开始时间',
+        `exam_end_time` bigint NULL DEFAULT NULL COMMENT '考试结束时间',
+        `exam_place` varchar(50) NULL DEFAULT NULL COMMENT '考点',
+        `exam_room` varchar(50) NULL DEFAULT NULL COMMENT '考场',
+        `teacher_code` varchar(50) NULL COMMENT '命题老师工号',
+        `teacher_name` varchar(50) NULL COMMENT '命题老师姓名',
+        `update_status` TINYINT(1) NULL DEFAULT 0 COMMENT '是否已同步到考生表中',
+        PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '数据同步考生' ROW_FORMAT = Dynamic;
+
 -- ----------------------------
 -- ----------------------------
 -- Table structure for t_sync_exam_log
 -- Table structure for t_sync_exam_log
 -- ----------------------------
 -- ----------------------------

+ 42 - 50
distributed-print/install/mysql/upgrade/3.4.2.sql

@@ -3,13 +3,13 @@ USE teachcloud_db;
 ALTER TABLE obe_culture_program_requirement ADD UNIQUE INDEX obe_culture_program_requirement_unique (culture_program_id ASC, name ASC);
 ALTER TABLE obe_culture_program_requirement ADD UNIQUE INDEX obe_culture_program_requirement_unique (culture_program_id ASC, name ASC);
 
 
 CREATE TABLE obe_course_outline_audit_report (
 CREATE TABLE obe_course_outline_audit_report (
-         id BIGINT NOT NULL,
-         course_outline_id BIGINT NOT NULL,
-         weight_setting_sign BIGINT NULL COMMENT '课程权重设置标识',
-         course_basic_info MEDIUMTEXT NULL COMMENT '课程基础信息',
-         course_target_info MEDIUMTEXT NULL COMMENT '课程目标情况',
-         rationality_evaluation MEDIUMTEXT NULL COMMENT '合理性评价',
-         PRIMARY KEY (id)
+                                                 id BIGINT NOT NULL,
+                                                 course_outline_id BIGINT NOT NULL,
+                                                 weight_setting_sign BIGINT NULL COMMENT '课程权重设置标识',
+                                                 course_basic_info MEDIUMTEXT NULL COMMENT '课程基础信息',
+                                                 course_target_info MEDIUMTEXT NULL COMMENT '课程目标情况',
+                                                 rationality_evaluation MEDIUMTEXT NULL COMMENT '合理性评价',
+                                                 PRIMARY KEY (id)
 )  ENGINE=INNODB DEFAULT CHARACTER SET=UTF8MB4 COLLATE=utf8mb4_general_ci COMMENT='课程目标达成情况评价依据合理性审核表';
 )  ENGINE=INNODB DEFAULT CHARACTER SET=UTF8MB4 COLLATE=utf8mb4_general_ci COMMENT='课程目标达成情况评价依据合理性审核表';
 
 
 INSERT INTO sys_privilege (id, name, url, type, parent_id, sequence, property, enable, default_auth, front_display) VALUES ('3060', '课程大纲-审核', '/api/admin/obe/course_outline/audit_report', 'URL', '3000', '7', 'AUTH', '1', '1', '1');
 INSERT INTO sys_privilege (id, name, url, type, parent_id, sequence, property, enable, default_auth, front_display) VALUES ('3060', '课程大纲-审核', '/api/admin/obe/course_outline/audit_report', 'URL', '3000', '7', 'AUTH', '1', '1', '1');
@@ -55,21 +55,18 @@ ALTER TABLE t_r_basic_info ADD CONSTRAINT t_r_basic_info_unique UNIQUE KEY (cult
 
 
 UPDATE sys_privilege SET name='成绩管理', url='TargetScoreManage', `type`='MENU', parent_id=2100, `sequence`=1, property=NULL, related='2003,2004,2005,3053', enable=1, default_auth=0, front_display=1 WHERE id=2001;
 UPDATE sys_privilege SET name='成绩管理', url='TargetScoreManage', `type`='MENU', parent_id=2100, `sequence`=1, property=NULL, related='2003,2004,2005,3053', enable=1, default_auth=0, front_display=1 WHERE id=2001;
 
 
-ALTER TABLE `exam_task_detail` CHANGE COLUMN `draw_count` `open_ab` TINYINT(1) NULL DEFAULT 0 COMMENT '是否开启AB卷' ;
-update exam_task_detail set open_ab = (case open_ab when 1 then 0 else 1 end);
-
 ALTER TABLE `basic_print_config` ADD COLUMN `open_ab` TINYINT(1) NULL DEFAULT 0 COMMENT '是否开启AB卷' AFTER `card_rule_id`;
 ALTER TABLE `basic_print_config` ADD COLUMN `open_ab` TINYINT(1) NULL DEFAULT 0 COMMENT '是否开启AB卷' AFTER `card_rule_id`;
 ALTER TABLE `mark_student` ADD COLUMN `omr_breach` TINYINT(1) NULL DEFAULT 0 COMMENT '识别违纪' AFTER `omr_absent_checked`;
 ALTER TABLE `mark_student` ADD COLUMN `omr_breach` TINYINT(1) NULL DEFAULT 0 COMMENT '识别违纪' AFTER `omr_absent_checked`;
 ALTER TABLE mark_student ADD COLUMN paper_type_check_status VARCHAR(32) NOT NULL DEFAULT 'NORMAL' COMMENT '卷型检查状态' AFTER omr_breach;
 ALTER TABLE mark_student ADD COLUMN paper_type_check_status VARCHAR(32) NOT NULL DEFAULT 'NORMAL' COMMENT '卷型检查状态' AFTER omr_breach;
 UPDATE mark_student SET paper_type_check_status = 'NORMAL' WHERE paper_type_check_status = '' or paper_type_check_status is null;
 UPDATE mark_student SET paper_type_check_status = 'NORMAL' WHERE paper_type_check_status = '' or paper_type_check_status is null;
 
 
 ALTER TABLE `exam_task_detail`
 ALTER TABLE `exam_task_detail`
-DROP COLUMN `open_ab`,
-ADD COLUMN `serial_number` int(2) NULL COMMENT '备用卷序号(1,2,3,4…)' AFTER `exam_task_id`,
+    ADD COLUMN `serial_number` int(2) NULL COMMENT '备用卷序号(1,2,3,4…)' AFTER `exam_task_id`,
 ADD COLUMN `exposed` TINYINT(1) NULL DEFAULT 0 COMMENT '是否曝光' AFTER `remark`;
 ADD COLUMN `exposed` TINYINT(1) NULL DEFAULT 0 COMMENT '是否曝光' AFTER `remark`;
 ALTER TABLE `exam_task_detail` ADD COLUMN `exposed_exam_id` BIGINT(20) NULL COMMENT '曝光的考试ID' AFTER `exposed`;
 ALTER TABLE `exam_task_detail` ADD COLUMN `exposed_exam_id` BIGINT(20) NULL COMMENT '曝光的考试ID' AFTER `exposed`;
 
 
 ALTER TABLE `exam_task` ADD COLUMN `open_ab` TINYINT(1) NULL DEFAULT 0 COMMENT '是否开启AB卷' AFTER `paper_number`;
 ALTER TABLE `exam_task` ADD COLUMN `open_ab` TINYINT(1) NULL DEFAULT 0 COMMENT '是否开启AB卷' AFTER `paper_number`;
+update exam_task et set open_ab = (select case when draw_count > 1 then 1 else 0 end from exam_task_detail etd where et.id = etd.exam_task_id) ;
 
 
 ALTER TABLE `basic_card_rule`
 ALTER TABLE `basic_card_rule`
     ADD COLUMN `contains_letter` TINYINT(1) NULL DEFAULT 0 COMMENT '学号是否包含字母' AFTER `default_option_number`,
     ADD COLUMN `contains_letter` TINYINT(1) NULL DEFAULT 0 COMMENT '学号是否包含字母' AFTER `default_option_number`,
@@ -89,20 +86,20 @@ ALTER TABLE `mark_student` ADD COLUMN `serial_number` INT(2) NULL COMMENT '备
 ALTER TABLE `scan_answer_card` ADD COLUMN `serial_number` INT(2) NULL COMMENT '备用卷序号' AFTER `course_paper_id`;
 ALTER TABLE `scan_answer_card` ADD COLUMN `serial_number` INT(2) NULL COMMENT '备用卷序号' AFTER `course_paper_id`;
 
 
 CREATE TABLE `mark_question_answer` (
 CREATE TABLE `mark_question_answer` (
-        `id` BIGINT(20) NOT NULL,
-        `exam_id` BIGINT(20) NULL COMMENT '考试ID',
-        `paper_number` VARCHAR(50) NULL COMMENT '试卷编号',
-        `paper_type` VARCHAR(10) NULL COMMENT '卷型',
-        `main_number` INT NULL COMMENT '大题号',
-        `sub_number` INT NULL COMMENT '小题号',
-        `answer` VARCHAR(16) NULL COMMENT '客观题标答',
-        `objective_policy` VARCHAR(16) NULL COMMENT '判分策略',
-        `objective_policy_score` DOUBLE NULL COMMENT '客观题判分策略计分(漏选给分)',
-        `create_id` BIGINT(20) NULL,
-        `create_time` BIGINT(20) NULL,
-        `update_id` BIGINT(20) NULL,
-        `update_time` BIGINT(20) NULL,
-        PRIMARY KEY (`id`))
+                                        `id` BIGINT(20) NOT NULL,
+                                        `exam_id` BIGINT(20) NULL COMMENT '考试ID',
+                                        `paper_number` VARCHAR(50) NULL COMMENT '试卷编号',
+                                        `paper_type` VARCHAR(10) NULL COMMENT '卷型',
+                                        `main_number` INT NULL COMMENT '大题号',
+                                        `sub_number` INT NULL COMMENT '小题号',
+                                        `answer` VARCHAR(16) NULL COMMENT '客观题标答',
+                                        `objective_policy` VARCHAR(16) NULL COMMENT '判分策略',
+                                        `objective_policy_score` DOUBLE NULL COMMENT '客观题判分策略计分(漏选给分)',
+                                        `create_id` BIGINT(20) NULL,
+                                        `create_time` BIGINT(20) NULL,
+                                        `update_id` BIGINT(20) NULL,
+                                        `update_time` BIGINT(20) NULL,
+                                        PRIMARY KEY (`id`))
     COMMENT = '客观题信息';
     COMMENT = '客观题信息';
 
 
 ALTER TABLE `exam_task` ADD COLUMN `exam_task_detail_enable` TINYINT(1) NULL DEFAULT 1 COMMENT '卷库启用禁用状态。是否启用,false:停用,true:启用' AFTER `enable`;
 ALTER TABLE `exam_task` ADD COLUMN `exam_task_detail_enable` TINYINT(1) NULL DEFAULT 1 COMMENT '卷库启用禁用状态。是否启用,false:停用,true:启用' AFTER `enable`;
@@ -144,6 +141,7 @@ ALTER TABLE `t_sync_exam_student_score` CHANGE COLUMN `paper_number` `paper_numb
 ALTER TABLE `exam_card` ADD COLUMN `open_ab` TINYINT(1) NULL DEFAULT 0 COMMENT '是否AB卷(AB卷题卡)' AFTER `page_size`;
 ALTER TABLE `exam_card` ADD COLUMN `open_ab` TINYINT(1) NULL DEFAULT 0 COMMENT '是否AB卷(AB卷题卡)' AFTER `page_size`;
 
 
 update mark_paper set paper_file_path = concat('[',paper_file_path, ']') where paper_file_path is not null;
 update mark_paper set paper_file_path = concat('[',paper_file_path, ']') where paper_file_path is not null;
+update mark_document set file_path = concat('[',file_path, ']') where type = 'PAPER' and file_path is not null;
 
 
 -- 2024-11-11
 -- 2024-11-11
 ALTER TABLE obe_course_outline MODIFY COLUMN course_type varchar(50) NOT NULL COMMENT '课程类别';
 ALTER TABLE obe_course_outline MODIFY COLUMN course_type varchar(50) NOT NULL COMMENT '课程类别';
@@ -153,23 +151,23 @@ ALTER TABLE `t_b_session` CHANGE COLUMN `type` `type` VARCHAR(200) NOT NULL COMM
 ALTER TABLE `mark_student` ADD COLUMN `is_manual_breach` TINYINT(1) NULL DEFAULT 0 COMMENT '人工指定缺考' AFTER `omr_breach`;
 ALTER TABLE `mark_student` ADD COLUMN `is_manual_breach` TINYINT(1) NULL DEFAULT 0 COMMENT '人工指定缺考' AFTER `omr_breach`;
 
 
 CREATE TABLE `scan_answer_card_question`  (
 CREATE TABLE `scan_answer_card_question`  (
-      `id` bigint NOT NULL COMMENT '主键',
-      `exam_id` bigint NOT NULL COMMENT '考试ID',
-      `paper_number` varchar(100) NOT NULL COMMENT '试卷类型',
-      `course_paper_id` varchar(32) NULL DEFAULT NULL COMMENT '试卷编号(交互)',
-      `serial_number` int NULL DEFAULT NULL COMMENT '备用卷',
-      `card_number` int NULL DEFAULT NULL COMMENT '卡格式序号',
-      `main_number` int NOT NULL COMMENT '大题号',
-      `sub_number` int NOT NULL COMMENT '小题号',
-      `option_count` int NOT NULL COMMENT '选项个数',
-      `question_type` varchar(2) NULL DEFAULT NULL COMMENT '题型',
-      `paper_index` int NULL DEFAULT NULL,
-      `page_index` int NULL DEFAULT NULL,
-      `create_id` bigint NULL DEFAULT NULL,
-      `create_time` bigint NULL DEFAULT NULL,
-      `update_id` bigint NULL DEFAULT NULL,
-      `update_time` bigint NULL DEFAULT NULL,
-      PRIMARY KEY (`id`) USING BTREE
+                                              `id` bigint NOT NULL COMMENT '主键',
+                                              `exam_id` bigint NOT NULL COMMENT '考试ID',
+                                              `paper_number` varchar(100) NOT NULL COMMENT '试卷类型',
+                                              `course_paper_id` varchar(32) NULL DEFAULT NULL COMMENT '试卷编号(交互)',
+                                              `serial_number` int NULL DEFAULT NULL COMMENT '备用卷',
+                                              `card_number` int NULL DEFAULT NULL COMMENT '卡格式序号',
+                                              `main_number` int NOT NULL COMMENT '大题号',
+                                              `sub_number` int NOT NULL COMMENT '小题号',
+                                              `option_count` int NOT NULL COMMENT '选项个数',
+                                              `question_type` varchar(2) NULL DEFAULT NULL COMMENT '题型',
+                                              `paper_index` int NULL DEFAULT NULL,
+                                              `page_index` int NULL DEFAULT NULL,
+                                              `create_id` bigint NULL DEFAULT NULL,
+                                              `create_time` bigint NULL DEFAULT NULL,
+                                              `update_id` bigint NULL DEFAULT NULL,
+                                              `update_time` bigint NULL DEFAULT NULL,
+                                              PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '卡格式题型';
 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '卡格式题型';
 
 
 -- 2024-11-15
 -- 2024-11-15
@@ -211,10 +209,4 @@ CREATE INDEX t_c_final_score_culture_program_id_IDX USING BTREE ON t_c_final_sco
 
 
 CREATE INDEX t_c_usual_score_culture_program_id_IDX USING BTREE ON t_c_usual_score (culture_program_id,exam_id,course_id,paper_number);
 CREATE INDEX t_c_usual_score_culture_program_id_IDX USING BTREE ON t_c_usual_score (culture_program_id,exam_id,course_id,paper_number);
 
 
-CREATE INDEX t_r_basic_info_program_id_IDX USING BTREE ON t_r_basic_info (culture_program_id,exam_id,course_id,paper_number);
-
-ALTER TABLE `client_upgrade`
-    ADD COLUMN `tool_type` VARCHAR(45) NOT NULL COMMENT '文件类型' AFTER `platform`,
-DROP PRIMARY KEY,
-ADD PRIMARY KEY USING BTREE (`platform`, `tool_type`);
-UPDATE `client_upgrade` SET `tool_type` = 'TEACHCLOUD_CLIENT' WHERE (`platform` = 'WINDOWS') and (`tool_type` is null);
+CREATE INDEX t_r_basic_info_program_id_IDX USING BTREE ON t_r_basic_info (culture_program_id,exam_id,course_id,paper_number);

+ 2 - 2
distributed-print/install/mysql/upgrade/3.4.3.sql

@@ -64,10 +64,10 @@ CREATE TABLE `t_sync_data_student`  (
 
 
 ALTER TABLE `basic_message`
 ALTER TABLE `basic_message`
     CHANGE COLUMN `user_id` `user_id` BIGINT NULL COMMENT '消息接收人用户' ,
     CHANGE COLUMN `user_id` `user_id` BIGINT NULL COMMENT '消息接收人用户' ,
-    CHANGE COLUMN `mobile_number` `mobile_number` VARCHAR(11) CHARACTER SET 'utf8mb4' NOT NULL COMMENT '电话号码' ;
+    CHANGE COLUMN `mobile_number` `mobile_number` VARCHAR(11) NOT NULL COMMENT '电话号码' ;
 
 
 ALTER TABLE `basic_message`
 ALTER TABLE `basic_message`
-    CHANGE COLUMN `template_code` `template_code` VARCHAR(45) CHARACTER SET 'utf8mb4' NULL COMMENT '消息模板代码' ;
+    CHANGE COLUMN `template_code` `template_code` VARCHAR(45) NULL COMMENT '消息模板代码' ;
 
 
 INSERT INTO `sys_config` (`id`, `config_key`, `config_name`, `config_value`, `enable`, `sort`, `create_id`, `create_time`, `update_time`) VALUES ('50', 'sms.sync.data.code', '数据同步失败通知', 'SMS_475870952', '1', '19', '1', '1733878447209', '1733878447249');
 INSERT INTO `sys_config` (`id`, `config_key`, `config_name`, `config_value`, `enable`, `sort`, `create_id`, `create_time`, `update_time`) VALUES ('50', 'sms.sync.data.code', '数据同步失败通知', 'SMS_475870952', '1', '19', '1', '1733878447209', '1733878447249');
 
 

+ 8 - 6
distributed-print/src/main/java/com/qmth/distributed/print/api/ExamCardController.java

@@ -3,7 +3,6 @@ package com.qmth.distributed.print.api;
 
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.qmth.boot.api.constant.ApiConstant;
 import com.qmth.boot.api.constant.ApiConstant;
-import com.qmth.distributed.print.business.bean.params.ExamCardParams;
 import com.qmth.distributed.print.business.bean.params.GenericExamCardParams;
 import com.qmth.distributed.print.business.bean.params.GenericExamCardParams;
 import com.qmth.distributed.print.business.entity.ExamCard;
 import com.qmth.distributed.print.business.entity.ExamCard;
 import com.qmth.distributed.print.business.service.ExamCardService;
 import com.qmth.distributed.print.business.service.ExamCardService;
@@ -16,7 +15,10 @@ import com.qmth.teachcloud.common.util.Result;
 import com.qmth.teachcloud.common.util.ResultUtil;
 import com.qmth.teachcloud.common.util.ResultUtil;
 import io.swagger.annotations.*;
 import io.swagger.annotations.*;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
+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 javax.annotation.Resource;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
@@ -76,7 +78,7 @@ public class ExamCardController {
     @ApiOperation(value = "新增/修改")
     @ApiOperation(value = "新增/修改")
     @RequestMapping(value = "/save_generic", method = RequestMethod.POST)
     @RequestMapping(value = "/save_generic", method = RequestMethod.POST)
     @OperationLogDetail(operationType = OperationTypeEnum.SAVE, detail = "新增/修改操作,题卡ID:{{params.id}}、题卡标题:{{params.title}}")
     @OperationLogDetail(operationType = OperationTypeEnum.SAVE, detail = "新增/修改操作,题卡ID:{{params.id}}、题卡标题:{{params.title}}")
-    public Result save(@RequestBody GenericExamCardParams params) throws Exception {
+    public Result saveGeneric(GenericExamCardParams params) throws Exception {
         Map<String, String> map = examCardService.saveGeneric(params);
         Map<String, String> map = examCardService.saveGeneric(params);
         return ResultUtil.ok(map);
         return ResultUtil.ok(map);
     }
     }
@@ -110,13 +112,13 @@ public class ExamCardController {
     /**
     /**
      * 新建专卡
      * 新建专卡
      *
      *
-     * @param examCardParams
+     * @param params
      */
      */
     @ApiOperation(value = "新建")
     @ApiOperation(value = "新建")
     @RequestMapping(value = "/save", method = RequestMethod.POST)
     @RequestMapping(value = "/save", method = RequestMethod.POST)
     @OperationLogDetail(operationType = OperationTypeEnum.SAVE, detail = "新增/修改操作,题卡ID:{{examCardParams.id}}、题卡标题:{{examCardParams.title}}")
     @OperationLogDetail(operationType = OperationTypeEnum.SAVE, detail = "新增/修改操作,题卡ID:{{examCardParams.id}}、题卡标题:{{examCardParams.title}}")
-    public Result save(@RequestBody ExamCardParams examCardParams) throws Exception {
-        Map<String, String> map = examCardService.saveExamCard(examCardParams);
+    public Result save(GenericExamCardParams params) throws Exception {
+        Map<String, String> map = examCardService.saveExamCard(params);
         return ResultUtil.ok(map);
         return ResultUtil.ok(map);
     }
     }
 
 

+ 1 - 1
teachcloud-common/src/main/java/com/qmth/teachcloud/common/contant/SystemConstant.java

@@ -1319,7 +1319,7 @@ public class SystemConstant {
         return score;
         return score;
     }
     }
 
 
-    public static String readJson(InputStream inputStream) {
+    public static String readContent(InputStream inputStream) {
         InputStreamReader reader = null;
         InputStreamReader reader = null;
         try {
         try {
             reader = new InputStreamReader(inputStream, "UTF-8");
             reader = new InputStreamReader(inputStream, "UTF-8");

+ 2 - 2
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkPaperServiceImpl.java

@@ -543,7 +543,7 @@ public class MarkPaperServiceImpl extends ServiceImpl<MarkPaperMapper, MarkPaper
             String descriptionJson = "description.json";
             String descriptionJson = "description.json";
 
 
             // 读取指定文件内容description.json
             // 读取指定文件内容description.json
-            String descriptionContent = SystemConstant.readJson(zipReader.read(zipRoot + "/" + descriptionJson));
+            String descriptionContent = SystemConstant.readContent(zipReader.read(zipRoot + "/" + descriptionJson));
             if (StringUtils.isBlank(descriptionContent)) {
             if (StringUtils.isBlank(descriptionContent)) {
                 throw ExceptionResultEnum.ERROR.exception("数据包description.json文件内容为空");
                 throw ExceptionResultEnum.ERROR.exception("数据包description.json文件内容为空");
             }
             }
@@ -571,7 +571,7 @@ public class MarkPaperServiceImpl extends ServiceImpl<MarkPaperMapper, MarkPaper
                 String courseCode = entry.getKey();
                 String courseCode = entry.getKey();
                 CourseCard courseCard = entry.getValue();
                 CourseCard courseCard = entry.getValue();
                 String jsonFileName = zipRoot + "/" + courseCard.getCode() + SystemConstant.JSON_PREFIX;
                 String jsonFileName = zipRoot + "/" + courseCard.getCode() + SystemConstant.JSON_PREFIX;
-                String jsonContent = SystemConstant.readJson(zipReader.read(jsonFileName));
+                String jsonContent = SystemConstant.readContent(zipReader.read(jsonFileName));
 
 
                 // 解析试卷结构,结构为空,跳过导入
                 // 解析试卷结构,结构为空,跳过导入
                 List<Struct> structList = CardParseUtils.parseCardContent(jsonContent);
                 List<Struct> structList = CardParseUtils.parseCardContent(jsonContent);

+ 1 - 1
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanAnswerCardServiceImpl.java

@@ -340,7 +340,7 @@ public class ScanAnswerCardServiceImpl extends ServiceImpl<ScanAnswerCardMapper,
             if (scanAnswerCard != null && StringUtils.isNotBlank(scanAnswerCard.getAdapteUri())) {
             if (scanAnswerCard != null && StringUtils.isNotBlank(scanAnswerCard.getAdapteUri())) {
                 FilePathVo filePathVo = JSON.parseObject(scanAnswerCard.getAdapteUri(), FilePathVo.class);
                 FilePathVo filePathVo = JSON.parseObject(scanAnswerCard.getAdapteUri(), FilePathVo.class);
                 if (filePathVo != null && StringUtils.isNotBlank(filePathVo.getPath())) {
                 if (filePathVo != null && StringUtils.isNotBlank(filePathVo.getPath())) {
-                    String jsonContent = SystemConstant.readJson(fileUploadService.downloadFile(filePathVo.getPath(), filePathVo.getUploadType(), filePathVo.getType()));
+                    String jsonContent = SystemConstant.readContent(fileUploadService.downloadFile(filePathVo.getPath(), filePathVo.getUploadType(), filePathVo.getType()));
                     if (StringUtils.isNotBlank(jsonContent)) {
                     if (StringUtils.isNotBlank(jsonContent)) {
                         AnswerCard answerCard = JSON.parseObject(jsonContent, AnswerCard.class);
                         AnswerCard answerCard = JSON.parseObject(jsonContent, AnswerCard.class);
                         if (answerCard != null && CollectionUtils.isNotEmpty(answerCard.getPages())) {
                         if (answerCard != null && CollectionUtils.isNotEmpty(answerCard.getPages())) {