Ver código fonte

feat: 签章功能

sunbw 1 mês atrás
pai
commit
64bbd42638
45 arquivos alterados com 2035 adições e 50 exclusões
  1. 1 0
      zfjg-api/zfjg-api-manage/src/main/java/com/zfjg/manage/api/domain/Signature/NewSigntureEntity.java
  2. 1 0
      zfjg-api/zfjg-api-manage/src/main/java/com/zfjg/manage/api/domain/Signature/SignatureEntity.java
  3. 0 7
      zfjg-api/zfjg-api-system/src/main/java/com/zfjg/system/api/domain/SysUser.java
  4. 1 0
      zfjg-common/pom.xml
  5. 64 0
      zfjg-common/zfjg-common-seal/pom.xml
  6. 66 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/adapter/SealSignAdapter.java
  7. 36 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/CommonResBean.java
  8. 39 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/OpenCommonReq.java
  9. 25 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/OpenCommonRes.java
  10. 29 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/BindTextInfo.java
  11. 12 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/DownLoadReq.java
  12. 13 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/QueryStatusReq.java
  13. 31 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/Rectangle.java
  14. 12 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/SealSignReq.java
  15. 63 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/SignContentReq.java
  16. 14 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/res/DownLoadRes.java
  17. 47 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/res/QueryStatusRes.java
  18. 12 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/res/SealSignRes.java
  19. 121 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/AbstractOpenRequestCommand.java
  20. 29 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/DownLoadCommand.java
  21. 25 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/QueryStatusCommand.java
  22. 46 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/SealSignCommand.java
  23. 16 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/constants/OpenUrlConstants.java
  24. 16 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/DecryptException.java
  25. 16 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/EncryptException.java
  26. 17 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/NotFindKeyException.java
  27. 15 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/SignBizException.java
  28. 16 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/SignException.java
  29. 33 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/notify/AppConfig.java
  30. 40 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/notify/OpenNotifyReq.java
  31. 68 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/Base64Utils.java
  32. 81 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/CloseableHttpClientUtils.java
  33. 140 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/GsonUtils.java
  34. 25 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/LocalDateTimeAdapter.java
  35. 42 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/NonceCodeUtils.java
  36. 237 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/SignRSAUtils.java
  37. 50 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/SignUtils.java
  38. 123 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/SnowflakeIdGenerator.java
  39. 68 0
      zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/URLUtils.java
  40. 38 0
      zfjg-common/zfjg-common-seal/src/test/java/com/zfjg/common/seal/AppTest.java
  41. 5 1
      zfjg-modules/zfjg-manage/pom.xml
  42. 76 42
      zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/service/impl/pdf/PDFServiceImpl.java
  43. 66 0
      zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/utils/seal/BindCodeDetails.java
  44. 129 0
      zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/utils/seal/SealSignUtils.java
  45. 31 0
      zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/utils/seal/SignType.java

+ 1 - 0
zfjg-api/zfjg-api-manage/src/main/java/com/zfjg/manage/api/domain/Signature/NewSigntureEntity.java

@@ -7,6 +7,7 @@ import java.util.List;
 @Data
 public class NewSigntureEntity {
 
+    private String enforceOrgName;
     private String thirdBizId;
     private List<fileInfo> fileInfo;
     @Data

+ 1 - 0
zfjg-api/zfjg-api-manage/src/main/java/com/zfjg/manage/api/domain/Signature/SignatureEntity.java

@@ -16,6 +16,7 @@ public class SignatureEntity {
     private String sealKey;
     private String mac;
     private String file;
+    private String enforceOrgName;
 
     @Data
     public static class SignatoriesList {

+ 0 - 7
zfjg-api/zfjg-api-system/src/main/java/com/zfjg/system/api/domain/SysUser.java

@@ -1,12 +1,9 @@
 package com.zfjg.system.api.domain;
 
-import cn.hutool.core.util.ReflectUtil;
-import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.sun.corba.se.impl.naming.cosnaming.NamingUtils;
 import com.zfjg.common.core.annotation.Excel;
 import com.zfjg.common.core.annotation.Excel.ColumnType;
 import com.zfjg.common.core.annotation.Excel.Type;
@@ -14,15 +11,11 @@ import com.zfjg.common.core.annotation.Excels;
 import com.zfjg.common.core.web.domain.BaseEntity;
 import com.zfjg.common.core.xss.Xss;
 import lombok.Data;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.xmlbeans.impl.common.NameUtil;
 
 import javax.validation.constraints.Email;
 import javax.validation.constraints.Size;
-import java.lang.reflect.Field;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 

+ 1 - 0
zfjg-common/pom.xml

@@ -17,6 +17,7 @@
         <module>zfjg-common-datascope</module>
         <module>zfjg-common-datasource</module>
         <module>zfjg-common-datasync</module>
+        <module>zfjg-common-seal</module>
     </modules>
 
     <artifactId>zfjg-common</artifactId>

+ 64 - 0
zfjg-common/zfjg-common-seal/pom.xml

@@ -0,0 +1,64 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.zfjg</groupId>
+        <artifactId>zfjg-common</artifactId>
+        <version>3.6.1</version>
+    </parent>
+
+    <groupId>com.zfjg.common.seal</groupId>
+    <artifactId>zfjg-common-seal</artifactId>
+    <packaging>jar</packaging>
+
+    <name>zfjg-common-seal</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.22</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.13</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+            <version>4.5.13</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.5</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>2.0.5</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.0.M2</version>
+        </dependency>
+    </dependencies>
+</project>

+ 66 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/adapter/SealSignAdapter.java

@@ -0,0 +1,66 @@
+package com.zfjg.common.seal.adapter;
+
+import com.zfjg.common.seal.bean.CommonResBean;
+import com.zfjg.common.seal.bean.req.DownLoadReq;
+import com.zfjg.common.seal.bean.req.SignContentReq;
+import com.zfjg.common.seal.bean.res.DownLoadRes;
+import com.zfjg.common.seal.bean.res.SealSignRes;
+import com.zfjg.common.seal.command.DownLoadCommand;
+import com.zfjg.common.seal.command.SealSignCommand;
+import com.zfjg.common.seal.exception.SignBizException;
+import com.zfjg.common.seal.notify.AppConfig;
+import com.zfjg.common.seal.utils.Base64Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author laopan
+ * @date 2024/11/3 上午10:03
+ */
+
+public class SealSignAdapter {
+
+    private static final Logger log = LoggerFactory.getLogger(SealSignAdapter.class);
+
+    public  static void sealSignAndDownLoad(AppConfig config, SignContentReq req, String downLoadPath) {
+        SealSignCommand command=new SealSignCommand();
+        CommonResBean<SealSignRes> request = command.request(req, config);
+        if("200".equals(request.getCode())){
+             SealSignRes data = request.getData();
+            delayDownFile(config,data.getFileCode(),downLoadPath,30);
+        }else{
+            log.info("调用上传签章文件出现错误");
+            throw new SignBizException("调用签章中心返回信息【"+request.getMsg()+"】");
+        }
+    }
+
+    private static void delayDownFile(AppConfig config, String fileCode, String downLoadPath, int time) {
+        //再次延迟5秒进行下载
+        //再次延迟5秒进行下载
+        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
+        try {
+            scheduledExecutorService.schedule(() -> SealSignAdapter.downLoadFile(config,fileCode,downLoadPath), time, TimeUnit.SECONDS);
+        }catch (Exception e){
+            log.info("在执行下载文件的时候出现异常",e);
+        }finally {
+            scheduledExecutorService.shutdown();
+        }
+    }
+
+    public static void downLoadFile(AppConfig config, String fileCode, String downLoadPath) {
+        DownLoadReq req=new DownLoadReq();
+        req.setFileCode(fileCode);
+        DownLoadCommand command=new DownLoadCommand();
+        CommonResBean<DownLoadRes> request = command.request(req, config);
+        if("200".equals(request.getCode())){
+            Base64Utils.writeFile(request.getData().getFileData(),downLoadPath);
+        }else{
+            //再次延迟5秒进行下载
+            delayDownFile(config,fileCode,downLoadPath,5);
+        }
+    }
+}

+ 36 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/CommonResBean.java

@@ -0,0 +1,36 @@
+package com.zfjg.common.seal.bean;
+
+import lombok.Data;
+
+/**
+ * @author laopan
+ * @date 2023/11/27 13:19
+ */
+@Data
+public class CommonResBean<T> {
+
+    /**
+     *  响应代码:
+     * 40004 业务处理失败
+     */
+    private String code;
+    /**
+     * 响应信息 文档上message 实际返回msg
+     */
+    private String msg;
+    /**
+     * 响应数据 格式字符串
+     */
+    private T data;
+
+    public CommonResBean(OpenCommonRes commonRes, T result) {
+        this.code=commonRes.getCode();
+        this.msg=commonRes.getMsg();
+        this.data=result;
+    }
+
+    public CommonResBean(OpenCommonRes commonRes) {
+        this.code=commonRes.getCode();
+        this.msg=commonRes.getMsg();
+    }
+}

+ 39 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/OpenCommonReq.java

@@ -0,0 +1,39 @@
+package com.zfjg.common.seal.bean;
+
+import lombok.Data;
+
+/**
+ * 通用的请求信息
+ * @author laopan
+ * @date 2023/12/8 9:04
+ */
+@Data
+public class OpenCommonReq {
+
+    /**
+     * 应用程序id(商户可以存在多个应用)
+     */
+    private String appId;
+    /**
+     * 签名字符串
+     */
+    private String sign;
+    /**
+     * 签名类型,暂时只支持RSA
+     */
+    private String signType;
+    /**
+     * 内容,将数据转换成json字符串,并对json字符串进行加密
+     */
+    private String content;
+
+    /**
+     * 时间戳,时间戳与服务器的时间间隔不能超过5分钟
+     */
+    private String timestamp;
+
+    /**
+     * 临时数(随机数)
+     */
+    private String nonce;
+}

+ 25 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/OpenCommonRes.java

@@ -0,0 +1,25 @@
+package com.zfjg.common.seal.bean;
+
+import lombok.Data;
+
+/**
+ * 通用的请求返回信息
+ * @author laopan
+ * @date 2023/12/8 9:04
+ */
+@Data
+public class OpenCommonRes {
+
+    /**
+     * 返回码
+     */
+    private String code;
+    /**
+     * 返回码描述
+     */
+    private String msg;
+    /**
+     * 返回数据
+     */
+    private String data;
+}

+ 29 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/BindTextInfo.java

@@ -0,0 +1,29 @@
+package com.zfjg.common.seal.bean.req;
+
+import lombok.Data;
+
+/**
+ *
+ * 和印章绑定的文本签署(可选)
+ * @author laopan
+ * @date 2024/9/29 下午4:37
+ */
+@Data
+public class BindTextInfo {
+    /**
+     * 签署字体 1:宋体 2:黑体 默认值1
+     */
+    private Integer font;
+    /**
+     * 签署字体颜色 black,red.... 默认值black
+     */
+    private String fontColor;
+    /**
+     * 签署推荐字号 默认为0 0则使用自适应大小的字体 单位:pt
+     */
+    private Integer fontSize;
+    /**
+     * 文本签署位置
+     */
+    private Rectangle rectangle;
+}

+ 12 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/DownLoadReq.java

@@ -0,0 +1,12 @@
+package com.zfjg.common.seal.bean.req;
+
+import lombok.Data;
+
+/**
+ * @author laopan
+ * @date 2024/10/28 下午7:33
+ */
+@Data
+public class DownLoadReq {
+    private String fileCode;
+}

+ 13 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/QueryStatusReq.java

@@ -0,0 +1,13 @@
+package com.zfjg.common.seal.bean.req;
+
+import lombok.Data;
+
+
+/**
+ * @author laopan
+ * @date 2024/10/28 下午7:33
+ */
+@Data
+public class QueryStatusReq {
+    private String fileCode;
+}

+ 31 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/Rectangle.java

@@ -0,0 +1,31 @@
+package com.zfjg.common.seal.bean.req;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @author laopan
+ * @date 2024/9/29 下午4:41
+ */
+@Data
+public class Rectangle {
+
+    /**
+     * 高
+     */
+    private BigDecimal  height;
+    /**
+     * 宽
+     */
+    private BigDecimal  width;
+    /**
+     * x坐标 文档左下角为原点
+     */
+    private BigDecimal  x;
+    /**
+     * y坐标 文档左下角为原点
+     */
+    private BigDecimal  y;
+
+}

+ 12 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/SealSignReq.java

@@ -0,0 +1,12 @@
+package com.zfjg.common.seal.bean.req;
+
+import lombok.Data;
+
+/**
+ * @author laopan
+ * @date 2024/10/28 上午9:55
+ */
+@Data
+public class SealSignReq {
+    private String content;
+}

+ 63 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/req/SignContentReq.java

@@ -0,0 +1,63 @@
+package com.zfjg.common.seal.bean.req;
+
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author laopan
+ * @date 2024/10/23 上午11:04
+ */
+@Data
+public class SignContentReq {
+
+    /**
+     * 文件的base64内容
+     */
+
+    private String fileBase64Content;
+
+    private String fileName;
+    /**
+     * 组织名称
+     */
+    private String orgName;
+    /**
+     * 签章任务名称
+     */
+    private String taskName;
+    /**
+     * 签署的文件类型,
+     */
+    private String signType;
+    /**
+     * 签署的业务系统的id,保证数据唯一
+     */
+    private String bizId;
+
+    /**
+     * 签章位置
+     */
+    private Rectangle rectangle;
+    /**
+     *  签名文本 如果需要签署文本,则出入签署文本的信息
+     */
+    private BindTextInfo bindTextInfo;
+
+    /**
+     * 签署页数 从1开始
+     */
+    private Integer page;
+
+    /**
+     * 使用签章的印章代码
+     */
+    private String sealCode;
+
+    /**
+     * 任务时间
+     */
+    private LocalDateTime taskTime;
+
+}

+ 14 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/res/DownLoadRes.java

@@ -0,0 +1,14 @@
+package com.zfjg.common.seal.bean.res;
+
+import lombok.Data;
+
+/**
+ * @author laopan
+ * @date 2024/10/28 下午7:34
+ */
+
+@Data
+public class DownLoadRes{
+    private String fileData;
+    private String appId;
+}

+ 47 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/res/QueryStatusRes.java

@@ -0,0 +1,47 @@
+package com.zfjg.common.seal.bean.res;
+
+import lombok.Data;
+
+/**
+ * @author laopan
+ * @date 2024/10/28 下午7:34
+ */
+@Data
+public class QueryStatusRes {
+    /**
+     * 应用id
+     */
+    private String appId;
+    /**
+     * 任务名称
+     */
+    private String taskName;
+
+    /**
+     * 签署类型
+     */
+    private String signType;
+
+    /**
+     * 单位名称
+     */
+    private String orgName;
+
+    /**
+     * 签章状态
+     */
+    private Integer status;
+    /**
+     * 签章状态描述
+     */
+    private String statusName;
+
+    /**
+     * 文件编码
+     */
+    private String fileCode;
+    /**
+     * 文件名称
+     */
+    private String fileName;
+}

+ 12 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/bean/res/SealSignRes.java

@@ -0,0 +1,12 @@
+package com.zfjg.common.seal.bean.res;
+
+import lombok.Data;
+
+/**
+ * @author laopan
+ * @date 2024/10/28 上午9:57
+ */
+@Data
+public class SealSignRes {
+    private String fileCode;
+}

+ 121 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/AbstractOpenRequestCommand.java

@@ -0,0 +1,121 @@
+package com.zfjg.common.seal.command;
+
+
+
+import com.zfjg.common.seal.bean.CommonResBean;
+import com.zfjg.common.seal.bean.OpenCommonReq;
+import com.zfjg.common.seal.bean.OpenCommonRes;
+import com.zfjg.common.seal.notify.AppConfig;
+import com.zfjg.common.seal.utils.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 抽象向支付系统开发平台请求命令
+ *
+ * @author laopan
+ * @date 2023/12/14
+ */
+@Slf4j
+public abstract  class AbstractOpenRequestCommand<T,R> {
+
+    private String getRequestAllUrl(AppConfig payConfig){
+        String requestUrl=getRequestUrl();
+        if(requestUrl==null||requestUrl.isEmpty()){
+            return payConfig.getDomain();
+        }
+        return payConfig.getDomain()+ requestUrl;
+    }
+
+    /**
+     * 获取请求url-域名后的后缀地址
+     *
+     * @return {@link String}
+     */
+    protected abstract String getRequestUrl();
+
+    /**
+     * 要求
+     *
+     * @param req    绿色
+     * @param config 配置
+     * @return {@link CommonResBean}
+     */
+    public CommonResBean<R> request(T req, AppConfig config){
+        if(req==null){
+            throw new RuntimeException("请求参数不能为空");
+        }
+        log.info("请求方原始数据:{}", logReqParser(req));
+        OpenCommonReq resBean=new OpenCommonReq();
+        resBean.setAppId(config.getAppId());
+        resBean.setSignType("RSA");
+        resBean.setTimestamp(String.valueOf(System.currentTimeMillis()));
+        resBean.setNonce(NonceCodeUtils.generate(8));
+        String content =getRequestContent(req,config);
+        resBean.setContent(content);
+        String sign = SignUtils.getSign(resBean, config.getPrivateKey());
+        resBean.setSign(sign);;
+        String url =getRequestAllUrl(config);
+        String resultData;
+        Map<String,String> header=new HashMap<>();
+        header.put("appId",config.getAppId());
+        header.put("requestId",String.valueOf(SnowflakeIdGenerator.nextId()));
+        resultData= CloseableHttpClientUtils.post(url, GsonUtils.toMap(resBean),header);
+        return convertResult(resultData,config);
+    }
+
+    /**
+     * 转换结果
+     *
+     * @param resultData 结果数据
+     * @param config     配置
+     * @return {@link CommonResBean}<{@link R}>
+     */
+    protected  CommonResBean<R> convertResult(String resultData, AppConfig config){
+        if(resultData==null){
+            return null;
+        }
+        OpenCommonRes commonRes=GsonUtils.fromJson(resultData,OpenCommonRes.class);
+        if(commonRes==null){
+            return null;
+        }
+        if("200".equals(commonRes.getCode())){
+            String data = commonRes.getData();
+                if(data==null||data.isEmpty()){
+                return new CommonResBean<>(commonRes,null);
+            }
+            String decrypt = SignRSAUtils.decrypt(data, config.getPrivateKey());
+            log.info("第三方返回数据解密结果:{}",logResParser(decrypt));
+            R result=GsonUtils.fromJson(decrypt,getResultClass());
+            return new CommonResBean<>(commonRes,result);
+        }
+        return new CommonResBean<>(commonRes);
+    }
+    protected String logReqParser(T req) {
+        return GsonUtils.toJson(req);
+    }
+    protected String logResParser(String decrypt) {
+        return decrypt;
+    }
+    /**
+     * 获取结果类
+     *
+     * @return {@link Class}<{@link R}>
+     */
+    protected abstract Class<R> getResultClass();
+
+    /**
+     * 获取请求内容
+     *
+     * @param req    请求数据
+     * @param config 配置
+     * @return {@link String}
+     */
+    protected  String getRequestContent(T req, AppConfig config){
+        String content = GsonUtils.toJson(req);
+        return SignRSAUtils.encrypt(content, config.getPlatformPublicKey());
+    }
+
+}

+ 29 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/DownLoadCommand.java

@@ -0,0 +1,29 @@
+package com.zfjg.common.seal.command;
+
+
+import com.zfjg.common.seal.bean.req.DownLoadReq;
+import com.zfjg.common.seal.bean.res.DownLoadRes;
+import com.zfjg.common.seal.constants.OpenUrlConstants;
+
+/**
+ * 记账信息查询命令
+ *
+ * @author laopan
+ * @date 2023/12/14 11:17
+ */
+public class DownLoadCommand extends  AbstractOpenRequestCommand<DownLoadReq, DownLoadRes>{
+    @Override
+    protected String getRequestUrl() {
+        return OpenUrlConstants.DOWN_LOAD;
+    }
+
+    @Override
+    protected Class<DownLoadRes> getResultClass() {
+        return DownLoadRes.class;
+    }
+
+    @Override
+    protected String logResParser(String decrypt) {
+        return "[文件数据太大,日志不显示]";
+    }
+}

+ 25 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/QueryStatusCommand.java

@@ -0,0 +1,25 @@
+package com.zfjg.common.seal.command;
+
+
+import com.zfjg.common.seal.bean.req.QueryStatusReq;
+import com.zfjg.common.seal.bean.res.QueryStatusRes;
+import com.zfjg.common.seal.constants.OpenUrlConstants;
+
+/**
+ * 记账信息查询命令
+ *
+ * @author laopan
+ * @date 2023/12/14 11:17
+ */
+public class QueryStatusCommand extends  AbstractOpenRequestCommand<QueryStatusReq, QueryStatusRes>{
+    @Override
+    protected String getRequestUrl() {
+        return OpenUrlConstants.QUERY_STATUS;
+    }
+
+    @Override
+    protected Class<QueryStatusRes> getResultClass() {
+        return QueryStatusRes.class;
+    }
+
+}

+ 46 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/command/SealSignCommand.java

@@ -0,0 +1,46 @@
+package com.zfjg.common.seal.command;
+
+
+import com.zfjg.common.seal.bean.req.SignContentReq;
+import com.zfjg.common.seal.bean.res.SealSignRes;
+import com.zfjg.common.seal.constants.OpenUrlConstants;
+import com.zfjg.common.seal.utils.GsonUtils;
+
+/**
+ * 记账信息查询命令
+ *
+ * @author laopan
+ * @date 2023/12/14 11:17
+ */
+public class SealSignCommand extends  AbstractOpenRequestCommand<SignContentReq, SealSignRes>{
+    @Override
+    protected String getRequestUrl() {
+        return OpenUrlConstants.SEAL_SIGN;
+    }
+
+    @Override
+    protected Class<SealSignRes> getResultClass() {
+        return SealSignRes.class;
+    }
+
+    /**
+     * 打印日志时剔除文件内容,文件的base64数据太大
+     * @param req
+     * @return
+     */
+    @Override
+    protected String logReqParser(SignContentReq req) {
+        SignContentReq log=new SignContentReq();
+        log.setFileName(req.getFileName());
+        log.setOrgName(req.getOrgName());
+        log.setTaskName(req.getTaskName());
+        log.setSignType(req.getSignType());
+        log.setBizId(req.getBizId());
+        log.setRectangle(req.getRectangle());
+        log.setBindTextInfo(req.getBindTextInfo());
+        log.setPage(req.getPage());
+        log.setSealCode(req.getSealCode());
+        log.setTaskTime(req.getTaskTime());
+        return GsonUtils.toJson(log);
+    }
+}

+ 16 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/constants/OpenUrlConstants.java

@@ -0,0 +1,16 @@
+package com.zfjg.common.seal.constants;
+
+/**
+ * @author laopan
+ * @date 2023/12/14 16:22
+ */
+public interface OpenUrlConstants {
+    /**
+     * url前缀
+     */
+    String URL_PREFIX="/v1/open-api";
+    String SEAL_SIGN=URL_PREFIX+"/sign/sealSign";
+    String DOWN_LOAD=URL_PREFIX+"/sign/downLoad";
+    String QUERY_STATUS=URL_PREFIX+"/sign/queryStatus";
+
+}

+ 16 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/DecryptException.java

@@ -0,0 +1,16 @@
+package com.zfjg.common.seal.exception;
+
+/**
+ * 加密异常
+ * @author laopan
+ * @date 2023/12/8 9:25
+ */
+public class DecryptException extends RuntimeException {
+    public DecryptException(String message) {
+        super(message);
+    }
+
+    public DecryptException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 16 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/EncryptException.java

@@ -0,0 +1,16 @@
+package com.zfjg.common.seal.exception;
+
+/**
+ * 加密异常
+ * @author laopan
+ * @date 2023/12/8 9:25
+ */
+public class EncryptException extends RuntimeException {
+    public EncryptException(String message) {
+        super(message);
+    }
+
+    public EncryptException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 17 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/NotFindKeyException.java

@@ -0,0 +1,17 @@
+package com.zfjg.common.seal.exception;
+
+/**
+ * 找不到密钥异常
+ *
+ * @author laopan
+ * @date 2023/12/8 9:29
+ */
+public class NotFindKeyException extends RuntimeException {
+    public NotFindKeyException(String message) {
+        super(message);
+    }
+
+    public NotFindKeyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 15 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/SignBizException.java

@@ -0,0 +1,15 @@
+package com.zfjg.common.seal.exception;
+
+/**
+ * @author laopan
+ * @date 2024/11/12 下午5:43
+ */
+public class SignBizException  extends RuntimeException {
+    public SignBizException(String message) {
+        super(message);
+    }
+
+    public SignBizException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 16 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/exception/SignException.java

@@ -0,0 +1,16 @@
+package com.zfjg.common.seal.exception;
+
+/**
+ * 签名异常
+ * @author laopan
+ * @date 2023/12/8 9:22
+ */
+public class SignException extends RuntimeException {
+    public SignException(String message) {
+        super(message);
+    }
+
+    public SignException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

+ 33 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/notify/AppConfig.java

@@ -0,0 +1,33 @@
+package com.zfjg.common.seal.notify;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 商户的支付配置
+ * @author laopan
+ * @date 2023/12/8 14:49
+ */
+@Configuration
+@ConfigurationProperties(prefix = "sign-seal")
+@Data
+public class AppConfig {
+
+    /**
+     * 域名
+     */
+    private String domain;
+    /**
+     * 应用程序id
+     */
+    private String appId;
+    /**
+     * 私钥
+     */
+    private String privateKey;
+    /**
+     * 平台公钥
+     */
+    private String platformPublicKey;
+}

+ 40 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/notify/OpenNotifyReq.java

@@ -0,0 +1,40 @@
+package com.zfjg.common.seal.notify;
+
+import lombok.Data;
+
+/**
+ * 通用的请求信息
+ * @author laopan
+ * @date 2023/12/8 9:04
+ */
+@Data
+public class OpenNotifyReq {
+
+    /**
+     * 商户编号
+     */
+    private String merchantNo;
+    /**
+     * 应用程序id(商户可以存在多个应用)
+     */
+    private String appId;
+    /**
+     * 签名字符串
+     */
+    private String sign;
+    /**
+     * 签名类型,暂时只支持RSA
+     */
+    private String signType;
+    /**
+     * 内容,将数据转换成json字符串,并对json字符串进行加密
+     */
+    private String content;
+    /**
+     * 回调类型
+     * 1、PAY_NOTIFY  支付回调
+     * 2、REFUND_NOTIFY 退款回调
+     * 3、RETURN_ACCOUNTING_NOTIFY 记账退回回调
+     */
+    private String notifyType;
+}

+ 68 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/Base64Utils.java

@@ -0,0 +1,68 @@
+package com.zfjg.common.seal.utils;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Base64;
+
+/**
+ * Base64实用程序
+ *
+ * @author laoapn
+ * @date 2023/12/08
+ */
+@Slf4j
+public class Base64Utils {
+
+	/**
+	 * <p> BASE64字符串解码为二进制数据 </p>
+	 * 
+	 * @param base64
+	 * @return
+	 * @throws Exception
+	 */
+	public static byte[] decode(String base64)  {
+		return Base64.getDecoder().decode(base64);
+	}
+
+	/**
+	 * <p> 二进制数据编码为BASE64字符串 </p>
+	 * 
+	 * @param bytes
+	 * @return
+	 * @throws Exception
+	 */
+	public static String encode(byte[] bytes) {
+		return Base64.getEncoder().encodeToString(bytes);
+	}
+	public static void writeFile(String base64Str,String filePath){
+		byte[] decodedBytes = Base64.getDecoder().decode(base64Str);
+		try {
+			// 将字节数组写入文件
+			Path path = Paths.get(filePath);
+			Path parentDir = path.getParent();
+			if (!Files.exists(parentDir)) {
+				Files.createDirectories(parentDir);
+			}
+			Files.write(path, decodedBytes);
+		} catch (Exception e) {
+			log.error("将base64写入文件时发生错误: " , e);
+		}
+	}
+	public static String convertFileToBase64(String filePath) throws IOException {
+		Path path = Paths.get(filePath);
+		byte[] fileContent  = Files.readAllBytes(path);
+		return Base64.getEncoder().encodeToString(fileContent);
+	}
+	public static String convertFileToBase64(File file) throws IOException {
+		Path path = Paths.get(file.toURI());
+		byte[] fileContent  = Files.readAllBytes(path);
+		return Base64.getEncoder().encodeToString(fileContent);
+	}
+}

+ 81 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/CloseableHttpClientUtils.java

@@ -0,0 +1,81 @@
+package com.zfjg.common.seal.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @author laopan
+ * @date 2023/11/30 11:33
+ */
+@Slf4j
+public class CloseableHttpClientUtils {
+
+    private static final RequestConfig REQUEST_CONFIG = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).setConnectionRequestTimeout(10000).build();
+
+    public static ContentType getContentType(String contentType) {
+        return ContentType.create(contentType, "utf-8");
+    }
+
+    public static String post(String requestUrl, Map<String, String> paramMap) {
+        return post(requestUrl,paramMap,null);
+    }
+    public static String post(String requestUrl, Map<String, String> paramMap, Map<String, String> header) {
+        log.info("向开放平台请求数据:{},请求方法:POST,请求接口:{}",GsonUtils.toJson(paramMap),requestUrl);
+        HttpEntity reqEntity =  new StringEntity(GsonUtils.toJson(paramMap), getContentType("UTF-8"));
+        HttpPost httpPost = new HttpPost(requestUrl);
+        httpPost.setEntity(reqEntity);
+        httpPost.addHeader("Accept", "application/json");
+        httpPost.addHeader("Content-Type", "application/json");
+        httpPost.setConfig(REQUEST_CONFIG);
+        if (header != null) {
+            Objects.requireNonNull(httpPost);
+            header.forEach(httpPost::addHeader);
+        }
+
+        return getResult(httpPost);
+    }
+
+    private static String getResult(HttpUriRequest request) {
+        CloseableHttpResponse response = null;
+        CloseableHttpClient httpClient = null;
+        try {
+            httpClient = HttpClients.createDefault();
+            response = httpClient.execute(request);
+            HttpEntity entity = response.getEntity();
+            String s = EntityUtils.toString(entity);
+            log.info("开放平台返回数据:{}",s);
+            return s;
+        } catch (IOException e) {
+            log.error("向开放平台请求数据异常,请求地址:{}}", request.getURI(),e);
+            return null;
+        } finally {
+            try {
+                if (response != null) {
+                    response.close();
+                }
+            } catch (IOException var22) {
+                var22.printStackTrace();
+            }
+            if (httpClient != null) {
+                try {
+                    httpClient.close();
+                } catch (IOException var21) {
+                    var21.printStackTrace();
+                }
+            }
+        }
+    }
+}

+ 140 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/GsonUtils.java

@@ -0,0 +1,140 @@
+package com.zfjg.common.seal.utils;
+
+import com.google.gson.*;
+import com.google.gson.reflect.TypeToken;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * json 的转换,默认采用gson.
+ *
+ * @author laopan
+ */
+public class GsonUtils {
+	private GsonUtils() {
+	}
+
+	private static final Gson GSON;
+
+	static {
+		GSON = new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
+				.disableHtmlEscaping()
+				.create();
+	}
+
+	/**
+	 * 对象转成字符串
+	 *
+	 * @param obj
+	 * @return
+	 */
+	public static String toJson(Object obj) {
+		return GSON.toJson(obj);
+	}
+
+	/**
+	 * json字符串 转 目标类型
+	 *
+	 * @param jsonStr json字符串
+	 * @param cls     目标类型
+	 * @return
+	 */
+	public static <T> T fromJson(String jsonStr, Class<T> cls) {
+		return GSON.fromJson(jsonStr, cls);
+	}
+
+	/**
+	 * json字符串 转 更广泛的类型,如:new TypeToken<SourceGpsResponseVO<ResultPageVO<CarVO>>>() {}.getType())
+	 *
+	 * @param jsonStr json字符串
+	 * @param type    如:new TypeToken<SourceGpsResponseVO<ResultPageVO<CarVO>>>() {}.getType())
+	 * @return
+	 */
+	public static <T> T fromJson(String jsonStr, Type type) {
+		return GSON.fromJson(jsonStr, type);
+	}
+
+	/**
+	 * 对象转成字符串,其中long类型序列化为string
+	 *
+	 * @param obj
+	 * @return
+	 */
+	public static String toLongJson(Object obj) {
+		Gson gsonBuilder = new GsonBuilder()
+				.setLongSerializationPolicy(LongSerializationPolicy.STRING)
+				.create();
+		return gsonBuilder.toJson(obj);
+	}
+
+	public static <T> List<T> fromJsonList(String jsonStr, Class<T> cls) {
+		return GSON.fromJson(jsonStr, new TypeToken<List<T>>() {
+		}.getType());
+	}
+	public static JsonObject toJsonTree(Object obj) {
+		return (JsonObject) GSON.toJsonTree(obj);
+	}
+
+	public static String getFiledData(Object obj, String filed) {
+		JsonObject jsonObject = toJsonTree(obj);
+		return jsonObject.get(filed).getAsString();
+	}
+
+	/**
+	 * 对象转JsonArray
+	 *
+	 * @param obj 对象
+	 * @return
+	 */
+	public static <T> JsonArray toJsonArray(Object obj) {
+		return GSON.toJsonTree(obj, new TypeToken<List<T>>() {
+		}.getType()).getAsJsonArray();
+	}
+
+	/**
+	 * 对象 转 map
+	 *
+	 * @param obj
+	 * @return
+	 */
+	public static Map<String, String> toMap(Object obj) {
+		Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
+		String json = gson.toJson(obj);
+		Type type = new TypeToken<Map<String, String>>() {
+		}.getType();
+		return gson.fromJson(json, type);
+	}
+
+	/**
+	 * json字符串 转 map
+	 *
+	 * @param json json字符串
+	 * @return
+	 */
+	public static Map<String, Object> toMap(String json) {
+		Type type = new TypeToken<Map<String, Object>>() {
+		}.getType();
+		return GSON.fromJson(json, type);
+	}
+
+	/**
+	 * 字符串 是否 json
+	 *
+	 * @param json
+	 * @return
+	 */
+	public static boolean isJson(String json) {
+		TypeAdapter<JsonElement> strictAdapter = new Gson().getAdapter(JsonElement.class);
+		try {
+			strictAdapter.fromJson(json);
+		} catch (JsonSyntaxException | IOException e) {
+			return false;
+		}
+		return true;
+	}
+}
+

+ 25 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/LocalDateTimeAdapter.java

@@ -0,0 +1,25 @@
+package com.zfjg.common.seal.utils;
+
+import com.google.gson.*;
+
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author laopan
+ * @date 2023/12/18 9:43
+ */
+public class LocalDateTimeAdapter implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
+    private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    @Override
+    public JsonElement serialize(LocalDateTime localDateTime, Type type, JsonSerializationContext jsonSerializationContext) {
+        return new JsonPrimitive(dateFormatter.format(localDateTime));
+    }
+
+    @Override
+    public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) {
+        return LocalDateTime.parse(jsonElement.getAsString(), dateFormatter);
+    }
+}

+ 42 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/NonceCodeUtils.java

@@ -0,0 +1,42 @@
+package com.zfjg.common.seal.utils;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * 随机字符串生成工具类
+ * @author laopan
+ * @className NonceCodeUtils
+ * @date 2021/2/22 17:06
+ */
+
+public class NonceCodeUtils {
+    private NonceCodeUtils(){}
+    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    private static final int DEFAULT_LENGTH = 32;
+    private static final Random RANDOM = new SecureRandom();
+
+    /**
+     * 随机字符串生成
+     * @param length 生成长度
+     * @return 随机数
+     */
+    public static String generate(int length) {
+        if (length <= 0) {
+            length = DEFAULT_LENGTH;
+        }
+        char[] nonceChars = new char[length];
+        for (int index = 0; index < nonceChars.length; ++index) {
+            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
+        }
+        return new String(nonceChars);
+    }
+
+    /**
+     * 32位随机数生成
+     * @return 随机数
+     */
+    public static String generate() {
+        return generate(DEFAULT_LENGTH);
+    }
+}

+ 237 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/SignRSAUtils.java

@@ -0,0 +1,237 @@
+package com.zfjg.common.seal.utils;
+
+
+import com.zfjg.common.seal.exception.DecryptException;
+import com.zfjg.common.seal.exception.EncryptException;
+import com.zfjg.common.seal.exception.NotFindKeyException;
+import com.zfjg.common.seal.exception.SignException;
+import org.apache.commons.lang.ArrayUtils;
+
+import javax.crypto.Cipher;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * SHA256 RSA工具类
+ *
+ * @author laopan
+ * @date 2023/10/30 13:42
+ */
+public class SignRSAUtils {
+
+    /**
+     * 密钥算法
+     */
+    private static final String KEY_ALGORITHM = "RSA";
+
+    /**
+     * 签名算法
+     */
+    private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
+
+    // 最大的加密明文长度
+    public static final int MAX_ENCRYPT_BLOCK = 245;
+
+    // 最大的解密密文长度
+    public static final int MAX_DECRYPT_BLOCK = 256;
+    private SignRSAUtils() {
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param content      文本内容
+     * @param publicKey 公钥
+     * @return {@link String}
+     */
+    public static String encrypt(String content, PublicKey publicKey) {
+        try {
+            byte[] bytes = encode(content.getBytes(),publicKey);
+            return Base64Utils.encode(bytes);
+        } catch (Exception e) {
+            throw new EncryptException("公钥加密数据失败",e);
+        }
+    }
+    public static byte[] encode(byte[] plainText, PublicKey publicKey) {
+        try {
+            Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
+            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+            int inputLen = plainText.length;
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            int offSet = 0;
+            byte[] cache;
+            int i = 0;
+            //对数据分段解密
+            while (inputLen - offSet > 0){
+                if (inputLen - offSet > MAX_ENCRYPT_BLOCK){
+                    cache = cipher.doFinal(plainText, offSet,MAX_ENCRYPT_BLOCK);
+                }else {
+                    cache = cipher.doFinal(plainText,offSet,inputLen - offSet);
+                }
+                out.write(cache,0,cache.length);
+                i++;
+                offSet = i*MAX_ENCRYPT_BLOCK;
+            }
+            byte[] decryptedData = out.toByteArray();
+            out.close();
+            return decryptedData;
+        } catch (Exception e) {
+            throw new EncryptException("公钥加密数据失败",e);
+         }
+    }
+    /**
+     * 公钥加密
+     *
+     * @param content   文本内容
+     * @param publicKey 公钥字符串
+     * @return {@link String}
+     */
+    public static String encrypt(String content, String publicKey) {
+      return encrypt(content,getPublicKey(publicKey));
+    }
+    /**
+     * 私钥解密
+     *
+     * @param encrypt    加密字符串
+     * @param privateKey 私钥
+     * @return {@link String}
+     */
+    public static String decrypt(String encrypt, PrivateKey privateKey) {
+        try {
+            byte[] encryptedBytes = Base64Utils.decode(encrypt);
+            byte[] bytes = decode(encryptedBytes,privateKey);
+            return new String(bytes, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            throw new DecryptException("私钥解密数据失败",e);
+        }
+    }
+
+    /**
+     * 解密
+     * @param encodedText
+     * @param privateKey
+     * @return
+     */
+    public static byte[] decode(byte[] encodedText, PrivateKey privateKey) {
+        try {
+            Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
+            cipher.init(Cipher.DECRYPT_MODE, privateKey);
+            //分段解密
+            byte[] enBytes = null;
+            for (int i = 0; i < encodedText.length; i += 256) {
+                //注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码
+                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(encodedText, i, i + 256));
+                enBytes = ArrayUtils.addAll(enBytes, doFinal);
+            }
+            return enBytes;
+        } catch (Exception e) {
+            throw new DecryptException("私钥解密数据失败", e);
+        }
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param encrypt    加密字符串
+     * @param privateKey 私钥
+     * @return {@link String}
+     */
+    public static String decrypt(String encrypt, String privateKey) {
+        return decrypt(encrypt,getPrivateKey(privateKey));
+    }
+    public static PublicKey getPublicKey(String publicKey) {
+        try {
+            byte[] keyBytes = Base64Utils.decode(publicKey);
+            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
+            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
+            return keyFactory.generatePublic(keySpec);
+        } catch (Exception e) {
+            throw new NotFindKeyException("获取公钥错误",e);
+        }
+    }
+
+    /**
+     * 获取私钥
+     *
+     * @param privateKey 私钥base64字符串
+     * @return {@link PrivateKey}
+     */
+    public static PrivateKey getPrivateKey(String privateKey) {
+        try {
+            byte[] keyBytes = Base64Utils.decode(privateKey);
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
+            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
+            return keyFactory.generatePrivate(keySpec);
+        } catch (Exception e) {
+            throw new NotFindKeyException("获取私钥错误",e);
+        }
+    }
+
+    /**
+     * 签名
+     *
+     * @param content    文本内容
+     * @param privateKey 私钥
+     * @return {@link String}
+     */
+    public static String sign(String content, PrivateKey privateKey) {
+        try {
+            byte[] data = content.getBytes(StandardCharsets.UTF_8);
+            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
+            signature.initSign(privateKey);
+            signature.update(data);
+            byte[] signedData = signature.sign();
+            return Base64Utils.encode(signedData);
+        } catch (Exception e) {
+            throw new SignException("签名异常",e);
+        }
+    }
+
+    /**
+     * 签名
+     *
+     * @param content    文本内容
+     * @param privateKey 私钥
+     * @return {@link String}
+     */
+    public static String sign(String content, String privateKey) {
+        return sign(content,getPrivateKey(privateKey));
+    }
+    /**
+     * 验签
+     *
+     * @param content   文本内容
+     * @param publicKey 公钥
+     * @param sign      签名
+     * @return boolean
+     */
+    public static boolean verify(String content, PublicKey publicKey, String sign) {
+        try {
+            byte[] data = content.getBytes(StandardCharsets.UTF_8);
+            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
+            signature.initVerify(publicKey);
+            signature.update(data);
+            return signature.verify(Base64Utils.decode(sign));
+        } catch (Exception e) {
+            throw new SignException("验签异常", e);
+        }
+    }
+
+    /**
+     * 验签
+     *
+     * @param content   文本内容
+     * @param publicKey 公钥
+     * @param sign      签名
+     * @return boolean
+     */
+    public static boolean verify(String content, String publicKey, String sign) {
+        return verify(content,getPublicKey(publicKey),sign);
+    }
+}

+ 50 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/SignUtils.java

@@ -0,0 +1,50 @@
+package com.zfjg.common.seal.utils;
+
+
+import com.zfjg.common.seal.bean.OpenCommonReq;
+
+import java.util.Map;
+
+/**
+ * 签署工具类
+ *
+ * @author laopan
+ * @date 2023/12/8 9:18
+ */
+public class SignUtils {
+
+    /**
+     * 获取签名字符串
+     *
+     * @param commonReq  公共req
+     * @param privateKey 私钥
+     * @return {@link String}
+     */
+    public static String getSign(OpenCommonReq commonReq, String privateKey){
+        String content=convertWaitSignContent(commonReq);
+        return SignRSAUtils.sign(content,privateKey);
+    }
+
+    /**
+     * 将对象转换成待签名的字符串
+     *
+     * @param commonReq 公共req
+     * @return {@link String}
+     */
+    public static String convertWaitSignContent(OpenCommonReq commonReq) {
+        Map<String, String> waitSignMap = GsonUtils.toMap(commonReq);
+        return URLUtils.covertUrl(waitSignMap);
+    }
+
+    /**
+     * 返回数据验签
+     * @param commonReq
+     * @param publicKey
+     * @return
+     */
+    public static boolean verify(OpenCommonReq commonReq, String publicKey){
+        String content=convertWaitSignContent(commonReq);
+        String sign = commonReq.getSign();
+        return SignRSAUtils.verify(content,publicKey,sign);
+    }
+}

+ 123 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/SnowflakeIdGenerator.java

@@ -0,0 +1,123 @@
+package com.zfjg.common.seal.utils;
+/**
+ *  雪花算法id生成器
+ * @author laopan
+ * @className SnowflakeIdGenerator
+ * @date 2020/12/9 14:17
+ */
+public class SnowflakeIdGenerator {
+    private final static long TWEPOCH = 1288834974657L;
+    /**
+     * 工作机器ID(0~31)
+     */
+    private final static long WORKER_ID = getWorkerId();
+    /**
+     * 数据中心ID(0~31)
+     */
+    private final static long DATA_CENTER_ID = getDataCenterId();
+    /**
+     * 机器id所占的位数
+     */
+    private final static long WORKER_ID_BITS = 8L;
+    /**
+     * 数据标识id所占的位数
+     */
+    private final static long DATA_CENTER_ID_BITS = 2L;
+    /**
+     * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
+     */
+    private final static long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
+    /**
+     * 支持的最大数据标识id,结果是31
+     */
+    private final static long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
+    /**
+     * 序列在id中占的位数
+     */
+    private final static long SEQUENCE_BITS = 12L;
+    /**
+     * 机器ID向左移12位
+     */
+    private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
+    /**
+     * 数据标识id向左移17位(12+5)
+     */
+    private final static long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
+    /**
+     * 时间截向左移22位(5+5+12)
+     */
+    private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
+    /**
+     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
+     */
+    private final static long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);
+    /**
+     * 毫秒内序列(0~4095)
+     */
+    private static long sequence = 0L;
+    /**
+     * 上次生成ID的时间截
+     */
+    private static long lastTimestamp = -1L;
+
+    /**
+     * 获得下一个ID (该方法是线程安全的)
+     *
+     * @return SnowflakeId
+     */
+    public static synchronized long nextId() {
+        if (WORKER_ID > MAX_WORKER_ID || WORKER_ID < 0) {
+            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
+        }
+        if (DATA_CENTER_ID > MAX_DATA_CENTER_ID || DATA_CENTER_ID < 0) {
+            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATA_CENTER_ID));
+        }
+        long timestamp = System.currentTimeMillis();
+        // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
+        if (timestamp < lastTimestamp) {
+            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
+        }
+        // 如果是同一时间生成的,则进行毫秒内序列
+        if (lastTimestamp == timestamp) {
+            sequence = (sequence + 1) & SEQUENCE_MASK;
+            // 毫秒内序列溢出
+            if (sequence == 0) {
+                // 阻塞到下一个毫秒,获得新的时间戳
+                timestamp = tilNextMillis(lastTimestamp);
+            }
+        }
+        // 时间戳改变,毫秒内序列重置
+        else {
+            sequence = 0L;
+        }
+        // 上次生成ID的时间截
+        lastTimestamp = timestamp;
+        // 移位并通过或运算拼到一起组成64位的ID
+        return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
+                | (DATA_CENTER_ID << DATA_CENTER_ID_SHIFT)
+                | (WORKER_ID << WORKER_ID_SHIFT)
+                | sequence;
+    }
+
+    /**
+     * 阻塞到下一个毫秒,直到获得新的时间戳
+     *
+     * @param lastTimestamp 上次生成ID的时间截
+     * @return 当前时间戳
+     */
+    private static long tilNextMillis(long lastTimestamp) {
+        long timestamp = System.currentTimeMillis();
+        while (timestamp <= lastTimestamp) {
+            timestamp = System.currentTimeMillis();
+        }
+        return timestamp;
+    }
+
+    public static Long getWorkerId() {
+        return 1L;
+    }
+
+    public static Long getDataCenterId() {
+        return (long) (Math.random() * 3 + 1);
+    }
+}

+ 68 - 0
zfjg-common/zfjg-common-seal/src/main/java/com/zfjg/common/seal/utils/URLUtils.java

@@ -0,0 +1,68 @@
+package com.zfjg.common.seal.utils;
+
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 参数转换
+ * @author laopan
+ * @date 2023/11/12 0012 9:45
+ */
+public class URLUtils {
+    /**
+     * 转换
+     *
+     * @param data 数据
+     * @return {@link String}
+     */
+    public static String covertUrl(Map<String,String> data) {
+        //keySet获取键集
+        List<String> keys = new ArrayList<>(data.keySet());
+        //对键集进行排序
+        Collections.sort(keys);
+        StringBuilder sb = new StringBuilder();
+        for(int i = 0, j = keys.size();i < j;i++) {
+            String curKey = keys.get(i);
+            if(curKey.equalsIgnoreCase("sign")){
+                continue;
+            }
+            String curValue = data.get(curKey);
+            sb.append(curKey).append("=").append(curValue).append("&");
+        }
+        sb.deleteCharAt(sb.length()-1);
+        return sb.toString();
+    }
+    public static Map<String, String> paraFilter(Map<String, String> sArray,boolean isFilterNull) {
+        Map<String, String> result = new TreeMap<>();
+        if (sArray == null || sArray.size() <= 0) {
+            return result;
+        }
+        for (String key : sArray.keySet()) {
+            String value = sArray.get(key);
+            if(key.equalsIgnoreCase("sign")){
+                continue;
+            }
+            if(isFilterNull){
+                if (null == value) {
+                    continue;
+                }
+            }
+            result.put(key, value);
+        }
+        return result;
+    }
+    /**
+     * 删除空数据
+     *
+     * @param data 数据
+     * @return {@link Map}<{@link String}, {@link String}>
+     */
+    public static Map<String, String> removeNullData(Map<String, String> data) {
+        return  data.entrySet().stream()
+                .filter(entry -> StringUtils.isNotBlank(entry.getValue()))
+                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+    }
+}

+ 38 - 0
zfjg-common/zfjg-common-seal/src/test/java/com/zfjg/common/seal/AppTest.java

@@ -0,0 +1,38 @@
+package com.zfjg.common.seal;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}

+ 5 - 1
zfjg-modules/zfjg-manage/pom.xml

@@ -110,7 +110,11 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>com.zfjg.common.seal</groupId>
+            <artifactId>zfjg-common-seal</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <!--        <dependency>-->
 <!--            <groupId>com.zfjg</groupId>-->
 <!--            <artifactId>zfjg-api-file</artifactId>-->

+ 76 - 42
zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/service/impl/pdf/PDFServiceImpl.java

@@ -8,14 +8,16 @@ import com.alibaba.fastjson.JSONObject;
 import com.alibaba.nacos.common.utils.CollectionUtils;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.itextpdf.text.pdf.PdfReader;
-import com.sun.org.apache.bcel.internal.generic.NEW;
 import com.zfjg.common.core.constant.GlobalConstant;
 import com.zfjg.common.core.enums.TemplateTypeEnum;
 import com.zfjg.common.core.exception.ServiceException;
 import com.zfjg.common.core.utils.http.HttpUtils;
 import com.zfjg.common.redis.service.RedisService;
+import com.zfjg.common.seal.notify.AppConfig;
 import com.zfjg.common.security.utils.SecurityUtils;
-import com.zfjg.manage.api.domain.Signature.*;
+import com.zfjg.manage.api.domain.Signature.NewSigntureEntity;
+import com.zfjg.manage.api.domain.Signature.SignatureEntity;
+import com.zfjg.manage.api.domain.Signature.signInfo;
 import com.zfjg.manage.api.domain.enforce.job.EnforceJob;
 import com.zfjg.manage.api.domain.enforce.job.EnforceJobCertificate;
 import com.zfjg.manage.api.domain.enforce.job.EnforceJobHonestReport;
@@ -42,6 +44,8 @@ import com.zfjg.manage.service.pdf.IPDFService;
 import com.zfjg.manage.service.sys.IFileService;
 import com.zfjg.manage.service.sys.ISysSealService;
 import com.zfjg.manage.utils.pdf.*;
+import com.zfjg.manage.utils.seal.SealSignUtils;
+import com.zfjg.manage.utils.seal.SignType;
 import com.zfjg.system.api.domain.SysDept;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;
@@ -49,13 +53,11 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.MediaType;
 import org.springframework.stereotype.Service;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
 import java.io.*;
-import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.*;
@@ -111,6 +113,8 @@ public class PDFServiceImpl implements IPDFService {
 
     @Value("${fire.now-url}")
     private String url;
+    @Autowired
+    private AppConfig appConfig;
 
     private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
     private static SimpleDateFormat format2 = new SimpleDateFormat("yyyy年MM月dd日hh时mm分");
@@ -250,6 +254,16 @@ public class PDFServiceImpl implements IPDFService {
         }
     }
 
+    /**
+     * 100002  ("checkTable","checkTables.ftl",      "简表 检查记录"),之前位置是死的          x:380,y:650  右上
+     * 100001  ("numerousTable","numerousTables.ftl","繁表 检查记录"),之前位置是死的          x:380,y:620  右上
+     * 100004  ("nowTable","nowTables.ftl",      "立改 通知书"),动态在signatureTarget位置     x:380,y:160  右下
+     * 100005  ("limitTable", "limitTables.ftl", "限改 通知书"),动态在signatureTarget位置     x:380,y:160  右下
+     * 100008  ("certificateLimit", "certificateTables.ftl", "限改 送达回证"),之前位置是死的  x:380,y:650  右上
+     * 100007  ("certificateNow",   "certificateTables.ftl", "立改 送达回证"),之前位置是死的  x:380,y:650  右上
+     * @param type
+     * @param jobId
+     */
     @Override
     public void initData(String type, Long jobId) {
         EnforceJob enforceJob = enforceJobService.getById(jobId);
@@ -279,7 +293,7 @@ public class PDFServiceImpl implements IPDFService {
                 String fileLimitName = "限改送达回证-" + jobId + ".pdf";
                 EnforceJobCertificate certificateEntityLimit = jobCertificate(jobId, "DEADLINE");
                 Map<String, Object> limitMap = createCertificatePdf(templateType, jobLimitDTO, fileLimitName, fireDir, certificateEntityLimit);
-                String certificateLimitFileId = certificateSign(limitMap, jobId, entity, fileLimitName, fireDir);
+                String certificateLimitFileId = certificateSign(limitMap, jobId, entity, fileLimitName, fireDir,SignType.DEADLINE_CORRECTION_DELIVERY_RECEIPT);
                 EnforceJobDTO newLimitJob = new EnforceJobDTO();
                 newLimitJob.setId(jobId);
                 newLimitJob.setCertificateLimitFileId(Long.parseLong(certificateLimitFileId));
@@ -291,7 +305,7 @@ public class PDFServiceImpl implements IPDFService {
                 EnforceJobCertificate certificateEntity = jobCertificate(jobId, "IMMEDIATELY");
                 Map<String, Object> certificateMap = createCertificatePdf(templateType, jobDTO, fileName, fireDir, certificateEntity);
                 String fileId = certificateSign(certificateMap,
-                        jobId, entity, fileName, fireDir);
+                        jobId, entity, fileName, fireDir, SignType.IMMEDIATE_CORRECTION_DELIVERY_RECEIPT);
                 EnforceJobDTO newJobDto = new EnforceJobDTO();
                 newJobDto.setId(jobId);
                 newJobDto.setCertificateNowFileId(Long.parseLong(fileId));
@@ -362,7 +376,7 @@ public class PDFServiceImpl implements IPDFService {
                 String fireDir = uploadDir + PDFUtils.datePath() + "/";
                 EnforceJobCertificate certificateEntity = jobCertificate(jobId, "DEADLINE");
                 Map<String, Object> certificateMap = createCertificatePdf("certificateTables.ftl", jobDTO, fileName, fireDir, certificateEntity);
-                String certificateLimitFileId = certificateSign(certificateMap, jobId, entity, fileName, fireDir);
+                String certificateLimitFileId = certificateSign(certificateMap, jobId, entity, fileName, fireDir, SignType.DEADLINE_CORRECTION_DELIVERY_RECEIPT);
                 EnforceJobDTO newJobDto = new EnforceJobDTO();
                 newJobDto.setId(jobId);
                 newJobDto.setCertificateLimitFileId(Long.parseLong(certificateLimitFileId));
@@ -426,7 +440,8 @@ public class PDFServiceImpl implements IPDFService {
                         }
                         String storageName;
                         try {
-                            storageName = getSignature(fireDir, fileName, file, entity);
+//                            storageName = getSignature(fireDir, fileName, file, entity);
+                            storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.SUMMARY_TABLE,String.valueOf(id),appConfig);
                         } catch (Exception e) {
                             pdfLog.setErrorLog(e.getMessage());
                             pdfLog.setSignatureResult("失败");
@@ -499,17 +514,19 @@ public class PDFServiceImpl implements IPDFService {
                             for (SignatureEntity.SignatoriesList signatorie : signatoriesList) {
                                 signatorie.setPage(matchItem.getPageNum());
                                 signatorie.setX("380.00");
-                                Float y = 760 - matchItem.getY();
-                                if (y <= 0) {
-                                    y = 20f;
-                                }
-                                signatorie.setY(String.valueOf(y));
+//                                Float y = 760 - matchItem.getY();
+//                                if (y <= 0) {
+//                                    y = 20f;
+//                                }
+//                                signatorie.setY(String.valueOf(y));
+                                signatorie.setY("160.00");
                             }
                         }
 
                         String storageName = null;
                         try {
-                            storageName = getSignature(fireDir, fileName, file, entity);
+//                            storageName = getSignature(fireDir, fileName, file, entity);
+                            storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.DEADLINE_CORRECTION_NOTICE,String.valueOf(id),appConfig);
                         } catch (Exception e) {
                             log.error("电子签章失败", e);
                             pdfLog.setErrorLog(e.getMessage());
@@ -571,7 +588,8 @@ public class PDFServiceImpl implements IPDFService {
                         }
                         String storageName = null;
                         try {
-                            storageName = getSignature(fireDir, fileName, file, entity);
+//                            storageName = getSignature(fireDir, fileName, file, entity);
+                            storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.COMPLEX_TABLE,String.valueOf(id),appConfig);
                         } catch (Exception e) {
                             log.error("电子签章失败", e);
                             // 电子签章失败则保存未签章pdf
@@ -650,7 +668,8 @@ public class PDFServiceImpl implements IPDFService {
                         EnforceJobDTO newJobTto = new EnforceJobDTO();
                         newJobTto.setId(id);
                         try {
-                            storageName = getSignature(fireDir, fileName, file, entity);
+//                            storageName = getSignature(fireDir, fileName, file, entity);
+                            storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.IMMEDIATE_CORRECTION_NOTICE,String.valueOf(id),appConfig);
                         } catch (Exception e) {
                             log.error("电子签章失败", e);
                             pdfLog.setErrorLog(e.getMessage());
@@ -707,7 +726,7 @@ public class PDFServiceImpl implements IPDFService {
                         // 生成盖章pdf
                         SignatureEntity entity = signatureInfo(false, jobDTO.getEnforceOrgId());
                         try {
-                            String certificateLimitFileId = certificateSign(map, id, entity, fileName, fireDir);
+                            String certificateLimitFileId = certificateSign(map, id, entity, fileName, fireDir, SignType.DEADLINE_CORRECTION_DELIVERY_RECEIPT);
                             EnforceJobDTO newLimitJob = new EnforceJobDTO();
                             newLimitJob.setId(id);
                             newLimitJob.setCertificateLimitFileId(Long.parseLong(certificateLimitFileId));
@@ -757,7 +776,7 @@ public class PDFServiceImpl implements IPDFService {
                         // 生成盖章pdf
                         SignatureEntity entity = signatureInfo(false, jobDTO.getEnforceOrgId());
                         try {
-                            String certificateNowFileId = certificateSign(map, id, entity, fileName, fireDir);
+                            String certificateNowFileId = certificateSign(map, id, entity, fileName, fireDir, SignType.IMMEDIATE_CORRECTION_DELIVERY_RECEIPT);
                             EnforceJobDTO newLimitJob = new EnforceJobDTO();
                             newLimitJob.setId(id);
                             newLimitJob.setCertificateNowFileId(Long.parseLong(certificateNowFileId));
@@ -944,7 +963,8 @@ public class PDFServiceImpl implements IPDFService {
             EnforceJobDTO newJobDto = new EnforceJobDTO();
             newJobDto.setId(jobId);
             try {
-                storageName = newGetSignature(fireDir, fileName, file, entity);
+//                storageName = newGetSignature(fireDir, fileName, file, entity);
+                storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity, SignType.SUMMARY_TABLE,appConfig);
             } catch (Exception e) {
                 log.error("电子签章失败", e);
                 // 电子签章失败则保存未签章pdf
@@ -989,14 +1009,16 @@ public class PDFServiceImpl implements IPDFService {
                 for (SignatureEntity.SignatoriesList signatorie : signatoriesList) {
                     signatorie.setPage(1);
                     signatorie.setX("380.00");
-                    signatorie.setY("100.00");
+                    //signatorie.setY("100.00");
+                    signatorie.setY("650.00");
                 }
             }
             String storageName = null;
             EnforceJobDTO newJobDto = new EnforceJobDTO();
             newJobDto.setId(jobId);
             try {
-                storageName = getSignature(fireDir, fileName, file, entity);
+//                storageName = getSignature(fireDir, fileName, file, entity);
+                storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.SUMMARY_TABLE,String.valueOf(jobId),appConfig);
             } catch (Exception e) {
                 log.error("电子签章失败", e);
                 // 电子签章失败则保存未签章pdf
@@ -1093,7 +1115,8 @@ public class PDFServiceImpl implements IPDFService {
             EnforceJobDTO newJobDto = new EnforceJobDTO();
             newJobDto.setId(jobId);
             try {
-                storageName = newGetSignature(fireDir, fileName, file, entity);
+//                storageName = newGetSignature(fireDir, fileName, file, entity);
+                storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity, SignType.COMPLEX_TABLE,appConfig);
             } catch (Exception e) {
                 log.error("电子签章失败", e);
                 // 电子签章失败则保存未签章pdf
@@ -1138,7 +1161,8 @@ public class PDFServiceImpl implements IPDFService {
                 for (SignatureEntity.SignatoriesList signatorie : signatoriesList) {
                     signatorie.setPage(1);
                     signatorie.setX("380.00");
-                    signatorie.setY("100.00");
+                    //signatorie.setY("160.00");
+                    signatorie.setY("620.00");
                 }
             }
 
@@ -1146,7 +1170,8 @@ public class PDFServiceImpl implements IPDFService {
             EnforceJobDTO newJobDto = new EnforceJobDTO();
             newJobDto.setId(jobId);
             try {
-                storageName = getSignature(fireDir, fileName, file, entity);
+//                storageName = getSignature(fireDir, fileName, file, entity);
+                storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.COMPLEX_TABLE,String.valueOf(jobId),appConfig);
             } catch (Exception e) {
                 log.error("电子签章失败", e);
                 // 电子签章失败则保存未签章pdf
@@ -1817,7 +1842,8 @@ public class PDFServiceImpl implements IPDFService {
             EnforceJobDTO newJobTto = new EnforceJobDTO();
             newJobTto.setId(id);
             try {
-                storageName = newGetSignature(fireDir, fileName, file, entity);
+//                storageName = newGetSignature(fireDir, fileName, file, entity);
+                storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity, SignType.IMMEDIATE_CORRECTION_NOTICE,appConfig);
                 ManageSysFileDTO fileDTO = new ManageSysFileDTO();
                 fileDTO.setFileName(fileName);
                 fileDTO.setPath(fireDir);
@@ -1914,18 +1940,20 @@ public class PDFServiceImpl implements IPDFService {
                 for (SignatureEntity.SignatoriesList signatorie : signatoriesList) {
                     signatorie.setPage(matchItem.getPageNum());
                     signatorie.setX("380.00");
-                    Float y = 760 - matchItem.getY();
-                    if (y <= 0) {
-                        y = 20f;
-                    }
-                    signatorie.setY(String.valueOf(y));
+//                    Float y = 760 - matchItem.getY();
+//                    if (y <= 0) {
+//                        y = 20f;
+//                    }
+//                    signatorie.setY(String.valueOf(y));
+                    signatorie.setY("160.00");
                 }
             }
             String storageName = null;
             EnforceJobDTO newJobTto = new EnforceJobDTO();
             newJobTto.setId(id);
             try {
-                storageName = getSignature(fireDir, fileName, file, entity);
+//                storageName = getSignature(fireDir, fileName, file, entity);
+                storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.IMMEDIATE_CORRECTION_NOTICE,String.valueOf(id),appConfig);
                 ManageSysFileDTO fileDTO = new ManageSysFileDTO();
                 fileDTO.setFileName(fileName);
                 fileDTO.setPath(fireDir);
@@ -2222,7 +2250,8 @@ public class PDFServiceImpl implements IPDFService {
         enforceJobDTO.setId(jobId);
 
         try {
-            storageName = newGetSignature(fireDir, fileName, file, entity);
+//            storageName = newGetSignature(fireDir, fileName, file, entity);
+            storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity, SignType.DEADLINE_CORRECTION_NOTICE,appConfig);
             ManageSysFileDTO fileDTO = new ManageSysFileDTO();
             fileDTO.setFileName(fileName);
             fileDTO.setPath(fireDir);
@@ -2283,11 +2312,12 @@ public class PDFServiceImpl implements IPDFService {
             for (SignatureEntity.SignatoriesList signatorie : signatoriesList) {
                 signatorie.setPage(matchItem.getPageNum());
                 signatorie.setX("380.00");
-                Float y = 760 - matchItem.getY();
-                if (y <= 0) {
-                    y = 20f;
-                }
-                signatorie.setY(String.valueOf(y));
+//                Float y = 760 - matchItem.getY();
+//                if (y <= 0) {
+//                    y = 20f;
+//                }
+//                signatorie.setY(String.valueOf(y));
+                signatorie.setY("160.00");
             }
         }
 
@@ -2296,7 +2326,8 @@ public class PDFServiceImpl implements IPDFService {
         enforceJobDTO.setId(jobId);
 
         try {
-            storageName = getSignature(fireDir, fileName, file, entity);
+//            storageName = getSignature(fireDir, fileName, file, entity);
+            storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,SignType.DEADLINE_CORRECTION_NOTICE,String.valueOf(jobId),appConfig);
             ManageSysFileDTO fileDTO = new ManageSysFileDTO();
             fileDTO.setFileName(fileName);
             fileDTO.setPath(fireDir);
@@ -2368,6 +2399,7 @@ public class PDFServiceImpl implements IPDFService {
             infoEntity.setSignInfo(signInfos);
             infoList.add(infoEntity);
             entity.setFileInfo(infoList);
+            entity.setEnforceOrgName(sysSeal.getEnforceOrgName());
         }
         return entity;
     }
@@ -2395,6 +2427,7 @@ public class PDFServiceImpl implements IPDFService {
             entity.setMac(sysSeal.getMac());
             entity.setSealKey(sysSeal.getSealKey());
             entity.setOutUserId(sysSeal.getOutUserId());
+            entity.setEnforceOrgName(sysSeal.getEnforceOrgName());
             List<SignatureEntity.SignatoriesList> signatoriesList = new ArrayList<>();
             // 设置电子签章位置
             SignatureEntity.SignatoriesList signatories = new SignatureEntity.SignatoriesList();
@@ -3200,7 +3233,7 @@ public class PDFServiceImpl implements IPDFService {
         EnforceJobDTO newJobDto = new EnforceJobDTO();
         newJobDto.setId(jobId);
         try {
-            storageName = newGetSignature(fireDir, fileName, file, entity);
+//            storageName = newGetSignature(fireDir, fileName, file, entity);
         } catch (Exception e) {
             ManageSysFile sysFileDto = (ManageSysFile) map.get("sysFileDto");
             fileRepository.save(sysFileDto);
@@ -3224,21 +3257,22 @@ public class PDFServiceImpl implements IPDFService {
     /**
      * 送达回证pdf获取电子签章
      */
-    private String certificateSign(Map<String, Object> map, Long jobId, SignatureEntity entity, String fileName, String fireDir) {
+    private String certificateSign(Map<String, Object> map, Long jobId, SignatureEntity entity, String fileName, String fireDir, SignType signType) {
         File file = (File) map.get("file");
         List<SignatureEntity.SignatoriesList> signatoriesList = entity.getSignatoriesList();
         if (CollectionUtils.isNotEmpty(signatoriesList)) {
             for (SignatureEntity.SignatoriesList signatorie : signatoriesList) {
                 signatorie.setPage(1);
                 signatorie.setX("380.00");
-                signatorie.setY("65.00");
+                signatorie.setY("650.00");
             }
         }
         String storageName = null;
         EnforceJobDTO newJobDto = new EnforceJobDTO();
         newJobDto.setId(jobId);
         try {
-            storageName = getSignature(fireDir, fileName, file, entity);
+//            storageName = getSignature(fireDir, fileName, file, entity);
+            storageName = SealSignUtils.sealSignAndDownLoad(fireDir, fileName, file, entity,signType,String.valueOf(jobId),appConfig);
         } catch (Exception e) {
             ManageSysFile sysFileDto = (ManageSysFile) map.get("sysFileDto");
             fileRepository.save(sysFileDto);

+ 66 - 0
zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/utils/seal/BindCodeDetails.java

@@ -0,0 +1,66 @@
+package com.zfjg.manage.utils.seal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author laopan
+ * @date 2024/9/30 下午2:31
+ */
+public class BindCodeDetails {
+    private   static final Map<String,String> BIND_CODE_MAP=new HashMap<>();
+
+    static {
+        BIND_CODE_MAP.put("重庆消防救援总队","100001");
+        BIND_CODE_MAP.put("沙坪坝区消防救援支队","50010600004174");
+        BIND_CODE_MAP.put("大渡口区消防救援支队","50010400003876");
+        BIND_CODE_MAP.put("北碚区消防救援支队","50010900003140");
+        BIND_CODE_MAP.put("九龙坡区消防救援支队","50010700004028");
+        BIND_CODE_MAP.put("渝北区消防救援支队","50011200004531");
+        BIND_CODE_MAP.put("南岸区消防救援支队","50010800004176");
+        BIND_CODE_MAP.put("渝中区消防救援支队","50010300004613");
+        BIND_CODE_MAP.put("江北区消防救援支队","50010500004528");
+        BIND_CODE_MAP.put("巴南区消防救援支队","50011300004832");
+        BIND_CODE_MAP.put("两江新区消防救援支队","50011200005587");
+        BIND_CODE_MAP.put("双桥经济技术开发区消防救援大队","50011100005905");
+        BIND_CODE_MAP.put("万州区消防救援支队","50010100004901");
+        BIND_CODE_MAP.put("涪陵区消防救援支队","50010200003652");
+        BIND_CODE_MAP.put("城口县消防救援大队","50022900008089");
+        BIND_CODE_MAP.put("彭水苗族土家族自治县消防救援大队","50024300007621");
+        BIND_CODE_MAP.put("綦江区消防救援支队","50011000006311");
+        BIND_CODE_MAP.put("秀山土家族苗族自治县消防救援大队","50024100008071");
+        BIND_CODE_MAP.put("梁平区消防救援支队","50015500007135");
+        BIND_CODE_MAP.put("长寿区消防救援支队","50011500006223");
+        BIND_CODE_MAP.put("巫山县消防救援大队","50023700006335");
+        BIND_CODE_MAP.put("忠县消防救援大队","50010300004980");
+        BIND_CODE_MAP.put("石柱土家族自治县消防救援大队","50024000007339");
+        BIND_CODE_MAP.put("开州区消防救援支队","50015400006346");
+        BIND_CODE_MAP.put("丰都县消防救援大队","50023000007531");
+        BIND_CODE_MAP.put("永川区消防救援支队","50011800003775");
+        BIND_CODE_MAP.put("南川区消防救援支队","50011900006244");
+        BIND_CODE_MAP.put("垫江县消防救援大队","50023100006229");
+        BIND_CODE_MAP.put("合川区消防救援支队","50011700005809");
+        BIND_CODE_MAP.put("黔江区消防救援支队","50011400005602");
+        BIND_CODE_MAP.put("大足区消防救援支队","50011100005907");
+        BIND_CODE_MAP.put("铜梁区消防救援支队","50015100004216");
+        BIND_CODE_MAP.put("酉阳土家族苗族自治县消防救援大队","50024200006750");
+        BIND_CODE_MAP.put("璧山区消防救援支队","50012000003408");
+        BIND_CODE_MAP.put("万盛经济技术开发区消防救援大队","50011000009036");
+        BIND_CODE_MAP.put("潼南区消防救援支队","50015200004385");
+        BIND_CODE_MAP.put("江津区消防救援支队","50011600007548");
+        BIND_CODE_MAP.put("武隆区消防救援支队","50010300004574");
+        BIND_CODE_MAP.put("荣昌区消防救援支队","50015300006492");
+        BIND_CODE_MAP.put("云阳县消防救援大队","50023500007500");
+        BIND_CODE_MAP.put("奉节县消防救援大队","50023600008894");
+        BIND_CODE_MAP.put("巫溪县消防救援大队","50023800008896");
+        BIND_CODE_MAP.put("重庆市消防救援总队水上支队","50011200008897");
+        BIND_CODE_MAP.put("重庆市消防救援总队轨道交通支队","50010600008898");
+    }
+    public static String getBindCode(String orgName){
+
+        return BIND_CODE_MAP.get(orgName);
+    }
+    public static boolean hasKey(String orgName){
+        return BIND_CODE_MAP.containsKey(orgName);
+    }
+}

+ 129 - 0
zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/utils/seal/SealSignUtils.java

@@ -0,0 +1,129 @@
+package com.zfjg.manage.utils.seal;
+
+import cn.hutool.core.lang.UUID;
+import com.zfjg.common.core.exception.ServiceException;
+import com.zfjg.common.seal.adapter.SealSignAdapter;
+import com.zfjg.common.seal.bean.req.Rectangle;
+import com.zfjg.common.seal.bean.req.SignContentReq;
+import com.zfjg.common.seal.exception.SignBizException;
+import com.zfjg.common.seal.notify.AppConfig;
+import com.zfjg.common.seal.utils.Base64Utils;
+import com.zfjg.common.seal.utils.GsonUtils;
+import com.zfjg.manage.api.domain.Signature.NewSigntureEntity;
+import com.zfjg.manage.api.domain.Signature.SignatureEntity;
+import com.zfjg.manage.api.domain.Signature.signInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 签章工具类,该工具使用最新的seal-sign-sdk中的流程进行签章
+ * @author laopan
+ * @date 2024/10/12 上午9:59
+ */
+@Slf4j
+public class SealSignUtils {
+
+    public static String sealSignAndDownLoad(String uploadDir, String fileName, File file, NewSigntureEntity signatureEntity, SignType signType,AppConfig appConfig) {
+        log.info("开始签章");
+        String storageName = UUID.fastUUID().toString().replace("-", "");
+        String downLoadPath=uploadDir +storageName ;
+        SignContentReq req=initData(file,fileName);
+        fillReqData(req,signatureEntity);
+        req.setSignType(signType.getCode());
+        req.setTaskName("");
+        sealSignAndDownLoad(appConfig,req,downLoadPath);
+        return storageName;
+    }
+
+    private static void fillReqData(SignContentReq contentReq, NewSigntureEntity signatureEntity) {
+        contentReq.setOrgName(signatureEntity.getEnforceOrgName());
+        contentReq.setSealCode(getBindCode(signatureEntity.getEnforceOrgName()));
+        contentReq.setBizId(signatureEntity.getThirdBizId());
+        final List<NewSigntureEntity.fileInfo> fileInfo = signatureEntity.getFileInfo();
+        if(CollectionUtils.isEmpty(fileInfo)){
+            throw new ServiceException("未获取到签章信息");
+        }
+        final NewSigntureEntity.fileInfo fileInfoData = fileInfo.get(0);
+        if(Objects.isNull(fileInfoData)){
+            throw new ServiceException("未获取到签章信息");
+        }
+        signInfo signInfo = fileInfoData.getSignInfo().get(0);
+        if(Objects.isNull(signInfo)){
+            throw new ServiceException("未获取到签章信息");
+        }
+        com.zfjg.manage.api.domain.Signature.signInfo.rectangle rectangleData = signInfo.getRectangle();
+        Rectangle rectangle = new Rectangle();
+        rectangle.setX(new BigDecimal(rectangleData.getX()));
+        rectangle.setY(new BigDecimal(rectangleData.getY()));
+        if(rectangleData.getWidth()!=null) {
+            rectangle.setWidth(new BigDecimal(rectangleData.getWidth()));
+        }
+        if(rectangleData.getHeight()!=null){
+            rectangle.setHeight(new BigDecimal(rectangleData.getHeight()));
+        }
+        contentReq.setRectangle(new Rectangle());
+        contentReq.setPage(signInfo.getPage());
+    }
+
+    private static String getBindCode(String enforceOrgName) {
+        return BindCodeDetails.getBindCode(enforceOrgName);
+    }
+
+    private static void sealSignAndDownLoad(AppConfig appConfig, SignContentReq req,String downLoadPath) {
+        log.info("开始调用签章API进行签章;{}", GsonUtils.toJson(req));
+        try {
+            SealSignAdapter.sealSignAndDownLoad(appConfig,req,downLoadPath);
+        }catch (SignBizException e){
+            throw new ServiceException(e.getMessage());
+        }
+
+    }
+    private static SignContentReq initData(File file, String fileName) {
+        SignContentReq contentReq=new SignContentReq();
+        try {
+            contentReq.setFileBase64Content(Base64Utils.convertFileToBase64(file));
+        } catch (IOException e) {
+            throw new ServiceException("获取待签章文件错误");
+        }
+        contentReq.setFileName(fileName);
+        contentReq.setTaskTime(LocalDateTime.now());
+         return contentReq;
+    }
+
+    public static String sealSignAndDownLoad(String uploadDir, String fileName, File file, SignatureEntity signatureEntity, SignType signType,String thirdBizId,AppConfig appConfig) {
+        String storageName = UUID.fastUUID().toString().replace("-", "");
+        String downLoadPath=uploadDir +storageName ;
+        SignContentReq req=initData(file,fileName);
+        fillReqData(req,signatureEntity);
+        req.setSignType(signType.getCode());
+        req.setTaskName("");
+        req.setBizId(thirdBizId);
+        sealSignAndDownLoad(appConfig,req,downLoadPath);
+        return storageName;
+    }
+
+    private static void fillReqData(SignContentReq contentReq,SignatureEntity signatureEntity) {
+        contentReq.setOrgName(signatureEntity.getEnforceOrgName());
+        contentReq.setSealCode(getBindCode(signatureEntity.getEnforceOrgName()));
+        final List<SignatureEntity.SignatoriesList> signatoriesList = signatureEntity.getSignatoriesList();
+        if(CollectionUtils.isEmpty(signatoriesList)){
+            throw new ServiceException("未获取到签章信息");
+        }
+        SignatureEntity.SignatoriesList signatoriesData = signatoriesList.get(0);
+        if(Objects.isNull(signatoriesData)){
+            throw new ServiceException("未获取到签章信息");
+        }
+        Rectangle rectangle = new Rectangle();
+        rectangle.setX(new BigDecimal(signatoriesData.getX()));
+        rectangle.setY(new BigDecimal(signatoriesData.getY()));
+        contentReq.setRectangle(rectangle);
+        contentReq.setPage(signatoriesData.getPage());
+    }
+}

+ 31 - 0
zfjg-modules/zfjg-manage/src/main/java/com/zfjg/manage/utils/seal/SignType.java

@@ -0,0 +1,31 @@
+package com.zfjg.manage.utils.seal;
+
+import lombok.Getter;
+
+/**
+ * @author laopan
+ * @date 2024/11/5 上午10:19
+ */
+@Getter
+public enum SignType {
+
+    COMPLEX_TABLE("100001","消防监督检查记录(繁表)"),
+    SUMMARY_TABLE("100002","消防监督检查记录(简表)"),
+    OPENING_INSPECTION("100003","消防安全检查记录(开业前检查)"),
+    IMMEDIATE_CORRECTION_NOTICE("100004","责令立即改正通知书"),
+    DEADLINE_CORRECTION_NOTICE("100005","责令限期改正通知书"),
+    SUPERVISE_TABLE("100006","廉洁执法监督表"),
+    IMMEDIATE_CORRECTION_DELIVERY_RECEIPT("100007","责令立即改正 送达回证"),
+    DEADLINE_CORRECTION_DELIVERY_RECEIPT("100008","责令限期改正 送达回证"),
+    NOTICE("100009","告知书"),
+    ;
+
+    private final String code;
+
+    private final String name;
+
+    SignType(String code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+}