Bladeren bron

1、增加交易所印章操作相关接口(实盘使用);
2、优化爱签API接口代码。

zhou.xiaoning 1 jaar geleden
bovenliggende
commit
8b332a9023

+ 7 - 7
config/config.xml

@@ -13,11 +13,11 @@
     <DbAddress value="192.168.31.88"/>
     <DbName value="orcl"/>
     <DbPort value="1521"/>
-    <DbUser value="mtp2_test134"/>
+    <DbUser value="mtp2_test204"/>
     <DbPwd value="muchinfo"/>
   </DbSetting>
   <RedisSetting>
-    <Address value="192.168.31.134"/>
+    <Address value="192.168.31.204"/>
     <Port value="5007"/>
     <Timeout value="3"/>
     <ConnNum value="1"/>
@@ -25,11 +25,11 @@
     <Pwd value=""/>
   </RedisSetting>
   <MqSetting>
-    <Url value="amqp://guest:guest@192.168.31.134:5020/test"/>
+    <Url value="amqp://guest:guest@192.168.31.204:5020/test"/>
     <Exchange value="entry"/>
   </MqSetting>
   <MongoDBSetting>
-    <HostName value="192.168.31.134"/>
+    <HostName value="192.168.31.204"/>
     <Port value="5025"/>
     <DBName value="HistoryQuote"/>
     <Username value="quote_test01"/>
@@ -38,8 +38,8 @@
   <MySQLSetting>
     <Host value="192.168.30.72"/>
     <Port value="3306"/>
-    <DBName value="historyquote_test134"/>
-    <Username value="quote_test134"/>
+    <DBName value="historyquote_test204"/>
+    <Username value="quote_test204"/>
     <Password value="123456"/>
   </MySQLSetting>
   <Tencent>
@@ -59,6 +59,6 @@
     <AppId value="765406597"/>
     <PrivateKey value="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCM9cXNowVdVPg7E3yRaQoRxxeCoVVred3xY6SA/SnhZR3uMqSiirjhoyw6BWyLAMoxTn/m5z2e8w9/Rha9aYz+rbtPG8ui7LCwclomI8OLOsZFa89gfppD3Xgb+cBMdFaDnUnnVy16MMw4yOsKcJ3S4nFNze5msA5oyNCb1edz8A60ohaxKgbHcBBZm2Vfap/csJqK04pGRFKPfWHlJ8Znjf2biUghLJk/OunsCwB4VBNCYkXXmOa0P13/njDCRHDC2pOzsbEYjTNuHFfZ1TbWBhFMk/PbDWD4UvIxJ4/kBPXO9Xo9s1OyjMV6UeNeAQbVZBYVQTeROAitla3GDD1HAgMBAAECggEAVk+zg7vbcA+0Q3AmKV948/PFeI5TIJEpjkImG3lo6IOi9RR7bK+mjzr7vLdPhDnClLhtZrSXLbnE9nizOd3koJk+hS0O3r8xXYlXm9zwQlOFzkI6Y+qBiSaWAgNGdUoTI1nvQ8FErnoswmR8TxMwAE35dNuwq54H5K951MIAjCM1A17CAsIbNyp/W8D7DvfUwbfYYrcLSpuTybp7poWIoLaps2mzcT4EMEAiGF/mD/MKOeYw/cRmmFu1Ceap+fZHLVNWHQ0A9GbhAdp/Oi4hCVRM/QgBzIxyRI86kjL/UtJcKwReSQ4NLKggrZHSEeWrb64BTFgCLvj63iJHVctriQKBgQDzSFIbivY/yfBwZlITF5+niRhZ06J6O+hLmN8/S+Jyij7wG1reJxyW6UBPj2RkheyVq6IIALC34V/9dFT4IZRmXzZCFZ6L3PwsIGzYxLuBMXlVsY/kYExCmbkfTj5uHdeQEZGhUs5ItfU3980KJ/lLVXCmz66XxzmMOs/rRtQTSwKBgQCUVCRp3GKaOv3dKmvv96ldbjmZv+p8rY1rqsIbN7prx4VtpqPq5IKJRZNK1tEebsH87dIxp7C5Z9FGq/AVDvKyxTSeX0zsgreiW2xxFoiVJJ72lMohRknUtm+ff1L+w9aN6j8p6HZE42KM5Jl00d/3ou0u1lu2jQtD3/Y6Rs7EdQKBgQDlQg7WE8Lw490oHtRTV5zFZYVECRMA+3lx3BLtdBZmd+7NmAmudc8US6SmFOu3axlDBiXAQt5FuiYaE8mKtyJAwpK//d2V0d71umy4bdSA7912qsh9n13eGT0Byls8q/SauGOzUfRBQu9eGa/oVTkxC8gH+oh+UWjiw/1xqyJpNwKBgChYYrcwrNlKpnab7VQOR1EUhDJf00YqZsccYUPJ+rkKQi67HubrVN5FwHSgzay7b6R+g6fh9t3XcUMJzXnM4RLjNRbG0UOboptUxsBT4r6+gUaM3hYC6l4jaWU22rpVRmy/hIK/qju+pbWxEFSIKaVP+be9sqCK6L+DkZP0YpVpAoGBAN/j1zSAbXpw0zPtzVT38V8k8G7GoEgJEmYsVy/eUrM+2MpvjCyZMAtcaLigYwVSANc91pSnx2RX2ptWNdqrkgmvAHUhvQ5siGPjr8EZ919CmayzU15L9+ESPFwq3He58fWm2IUGD8BpipSFrlkiPT8F0MLgXO0NVJ1ynKOj6x0S"/>
     <NotifyUrl value="http://218.17.158.45:15160/api/Asign/HandleASignCompleted"/>
-    <OpenApiUrl value="http://192.168.31.134:5015/mtp2-onlineopen"/>
+    <OpenApiUrl value="http://192.168.31.204:5015/mtp2-onlineopen"/>
   </Asign>
 </Configuration>

+ 1 - 1
config/readme.md

@@ -28,7 +28,7 @@ cfg.json 用于配置Web交易端所需要的相关服务地址:
   </Tencent>
 
 Enabled="1" 表示启用腾讯电子签功能,如不需要则改为"0"
-SecretId 和 SecretKey 是腾讯电子签密钥信息,使用公司账号登录 https://qian.tencent.com/console/ 获取
+SecretId 和 SecretKey 是腾讯电子签密钥信息,使用公司账号登录 https://qian.tencent.com/console/ 获取 (测试环境:https://beta.qian.tencent.cn/console/)
 EndPoint 腾讯电子签API接入地址,测试环境填 "essbasic.test.ess.tencent.cn" ,生产环境填 "essbasic.tencentcloudapi.com"
 AppId 子客第三方应用ID,使用公司账号登录 https://qian.tencent.com/console/ 设置和获取
 ProxyOrganizationName 子客企业名称,生产环境应填写子客企业全名

+ 32 - 4
controllers/asign/auth.go

@@ -12,16 +12,44 @@ import (
 	"github.com/gin-gonic/gin"
 )
 
-// BankCard4 银行卡四要素认证
-// @Summary  银行卡四要素认证
+// CaptchaVerify 创建印章
+// @Summary  创建印章
 // @Produce     json
 // @Security ApiKeyAuth
 // @accept      application/json
+// @Param    data body     asignService.CreateSealReq true "入参"
+// @Success  200  {object} app.Response
+// @Failure     500  {object} app.Response
+// @Router   /Asign/CreateSeal [post]
+// @Tags        爱签
+func CreateSeal(c *gin.Context) {
+	appG := app.Gin{C: c}
+
+	// 获取请求参数
+	var req asignService.CreateSealReq
+	if err := appG.C.ShouldBindJSON(&req); err != nil {
+		logger.GetLogger().Errorf(err.Error())
+		appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
+		return
+	}
+
+	if err := asignService.CreateSeal(req); err == nil {
+		appG.Response(http.StatusOK, e.SUCCESS, "ok")
+	} else {
+		appG.ResponseByMsg(http.StatusBadRequest, e.ERROR, err.Error(), nil)
+	}
+}
+
+// BankCard4 银行卡四要素认证
+// @Summary  银行卡四要素认证
+// @Produce  json
+// @Security ApiKeyAuth
+// @accept   application/json
 // @Param    data body     asignService.BankCard4Req true "入参"
 // @Success  200  {object} asignService.BankCard4Rsp
-// @Failure     500  {object} app.Response
+// @Failure  500  {object} app.Response
 // @Router   /Asign/BankCard4 [post]
-// @Tags        爱签
+// @Tags     爱签
 func BankCard4(c *gin.Context) {
 	appG := app.Gin{C: c}
 

+ 8 - 4
controllers/asign/test.go

@@ -31,7 +31,8 @@ func TestBankCard4(c *gin.Context) {
 		return
 	}
 
-	if rsp, err := asignService.APICompanyBankCard4(req); err == nil {
+	if rsp, err := asignService.APIPost[asignService.APICompanyBankCard4Req, asignService.APIBankCard4Rsp](
+		asignService.APIReq[asignService.APICompanyBankCard4Req]{Data: req}, asignService.APIURL_Company_BankCard4); err == nil {
 		appG.Response(http.StatusOK, e.SUCCESS, rsp)
 	} else {
 		appG.ResponseByMsg(http.StatusBadRequest, e.ERROR, err.Error(), nil)
@@ -60,7 +61,8 @@ func TestCaptcaResend(c *gin.Context) {
 		return
 	}
 
-	if rsp, err := asignService.APICaptchaResend(req); err == nil {
+	if rsp, err := asignService.APIPost[asignService.APICaptchaResendReq, interface{}](
+		asignService.APIReq[asignService.APICaptchaResendReq]{Data: req}, asignService.APIURL_Captcha_Resend); err == nil {
 		appG.Response(http.StatusOK, e.SUCCESS, rsp)
 	} else {
 		appG.ResponseByMsg(http.StatusBadRequest, e.ERROR, err.Error(), nil)
@@ -89,7 +91,8 @@ func TestCaptchaVerify(c *gin.Context) {
 		return
 	}
 
-	if rsp, err := asignService.APICaptchaVerify(req); err == nil {
+	if rsp, err := asignService.APIPost[asignService.APICaptchaVerifyReq, asignService.APICaptchaVerifyRsp](
+		asignService.APIReq[asignService.APICaptchaVerifyReq]{Data: req}, asignService.APIURL_Captcha_Verify); err == nil {
 		appG.Response(http.StatusOK, e.SUCCESS, rsp)
 	} else {
 		appG.ResponseByMsg(http.StatusBadRequest, e.ERROR, err.Error(), nil)
@@ -117,7 +120,8 @@ func TestAddEnterpriseUser(c *gin.Context) {
 		return
 	}
 
-	if rsp, err := asignService.APIAddEnterpriseUser(req); err == nil {
+	if rsp, err := asignService.APIPost[asignService.APIAddEnterpriseUserReq, asignService.APIAddUserRsp](
+		asignService.APIReq[asignService.APIAddEnterpriseUserReq]{Data: req}, asignService.APIURL_V2_User_AddEnterpriseUser); err == nil {
 		appG.Response(http.StatusOK, e.SUCCESS, rsp)
 	} else {
 		appG.ResponseByMsg(http.StatusBadRequest, e.ERROR, err.Error(), nil)

+ 68 - 0
docs/docs.go

@@ -195,6 +195,50 @@ const docTemplate = `{
                 }
             }
         },
+        "/Asign/CreateSeal": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "爱签"
+                ],
+                "summary": "创建印章",
+                "parameters": [
+                    {
+                        "description": "入参",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/asign.CreateSealReq"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/app.Response"
+                        }
+                    },
+                    "500": {
+                        "description": "Internal Server Error",
+                        "schema": {
+                            "$ref": "#/definitions/app.Response"
+                        }
+                    }
+                }
+            }
+        },
         "/Asign/QueryUsereSignRecords": {
             "get": {
                 "security": [
@@ -23621,6 +23665,30 @@ const docTemplate = `{
                 }
             }
         },
+        "asign.CreateSealReq": {
+            "type": "object",
+            "required": [
+                "account"
+            ],
+            "properties": {
+                "IsDefault": {
+                    "description": "是否为默认印章:1 - 是,0 - 否",
+                    "type": "integer"
+                },
+                "account": {
+                    "description": "用户唯一识别码",
+                    "type": "string"
+                },
+                "base64ImageStr": {
+                    "description": "base64格式的印模图片",
+                    "type": "string"
+                },
+                "sealName": {
+                    "description": "印章抬头文字(60字符以内)【注】印章下方横向展示的文字,例如“合同专用章”等。若不显示文字可传空格",
+                    "type": "string"
+                }
+            }
+        },
         "asign.PersonBankCard4": {
             "type": "object",
             "properties": {

+ 68 - 0
docs/swagger.json

@@ -186,6 +186,50 @@
                 }
             }
         },
+        "/Asign/CreateSeal": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "爱签"
+                ],
+                "summary": "创建印章",
+                "parameters": [
+                    {
+                        "description": "入参",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/asign.CreateSealReq"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/app.Response"
+                        }
+                    },
+                    "500": {
+                        "description": "Internal Server Error",
+                        "schema": {
+                            "$ref": "#/definitions/app.Response"
+                        }
+                    }
+                }
+            }
+        },
         "/Asign/QueryUsereSignRecords": {
             "get": {
                 "security": [
@@ -23612,6 +23656,30 @@
                 }
             }
         },
+        "asign.CreateSealReq": {
+            "type": "object",
+            "required": [
+                "account"
+            ],
+            "properties": {
+                "IsDefault": {
+                    "description": "是否为默认印章:1 - 是,0 - 否",
+                    "type": "integer"
+                },
+                "account": {
+                    "description": "用户唯一识别码",
+                    "type": "string"
+                },
+                "base64ImageStr": {
+                    "description": "base64格式的印模图片",
+                    "type": "string"
+                },
+                "sealName": {
+                    "description": "印章抬头文字(60字符以内)【注】印章下方横向展示的文字,例如“合同专用章”等。若不显示文字可传空格",
+                    "type": "string"
+                }
+            }
+        },
         "asign.PersonBankCard4": {
             "type": "object",
             "properties": {

+ 44 - 0
docs/swagger.yaml

@@ -234,6 +234,23 @@ definitions:
         description: 合同签署链接
         type: string
     type: object
+  asign.CreateSealReq:
+    properties:
+      IsDefault:
+        description: 是否为默认印章:1 - 是,0 - 否
+        type: integer
+      account:
+        description: 用户唯一识别码
+        type: string
+      base64ImageStr:
+        description: base64格式的印模图片
+        type: string
+      sealName:
+        description: 印章抬头文字(60字符以内)【注】印章下方横向展示的文字,例如“合同专用章”等。若不显示文字可传空格
+        type: string
+    required:
+    - account
+    type: object
   asign.PersonBankCard4:
     properties:
       bankCard:
@@ -31252,6 +31269,33 @@ paths:
       summary: 创建合同
       tags:
       - 爱签
+  /Asign/CreateSeal:
+    post:
+      consumes:
+      - application/json
+      parameters:
+      - description: 入参
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/asign.CreateSealReq'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/app.Response'
+        "500":
+          description: Internal Server Error
+          schema:
+            $ref: '#/definitions/app.Response'
+      security:
+      - ApiKeyAuth: []
+      summary: 创建印章
+      tags:
+      - 爱签
   /Asign/QueryUsereSignRecords:
     get:
       consumes:

+ 2 - 0
routers/router.go

@@ -853,6 +853,8 @@ func InitRouter() *gin.Engine {
 		asignR.Use().POST("SyncContractStatus", asign.SyncContractStatus)
 		asignR.Use().POST("HandleASignCompleted", asign.HandleASignCompleted)
 
+		asignR.Use().POST("CreateSeal", asign.CreateSeal)
+
 		asignR.Use(token.Auth()).POST("BankCard4", asign.BankCard4)
 		asignR.Use(token.Auth()).POST("CaptcaResend", asign.CaptcaResend)
 		asignR.Use(token.Auth()).POST("CaptchaVerify", asign.CaptchaVerify)

+ 203 - 200
services/asign/api.go

@@ -1,43 +1,51 @@
 package asign
 
 import (
+	"bytes"
+	"crypto"
+	"crypto/md5"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha1"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/hex"
 	"encoding/json"
+	"encoding/pem"
+	"fmt"
+	"io"
+	"mime/multipart"
 	"mtp2_if/config"
 	"mtp2_if/logger"
+	"net/http"
+	"reflect"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
 
 	"github.com/fatih/structs"
 )
 
-// APIPersonBankCard4 个人银行卡四要素认证
-func APIPersonBankCard4(req APIPersonBankCard4Req) (rsp *APIRsp[APIBankCard4Rsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/auth/person/bankCard4"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
-	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
-	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
-		return
+func APIPost[T1 APIReqData, T2 APIRspData](req APIReq[T1], apiPort APIURL) (rsp *APIRsp[T2], err error) {
+	apiUrl := config.SerCfg.AsignCfg.Url + string(apiPort)
+
+	var rspBody []byte
+	if len(req.Datas) > 0 {
+		// 数组入参
+		reqArray := make([]map[string]interface{}, 0)
+		for _, item := range req.Datas {
+			reqArray = append(reqArray, structs.Map(item))
+		}
+		logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqArray)
+		rspBody, err = httpPost_Array(apiUrl, reqArray)
+	} else {
+		// 对象入参
+		reqMap := structs.Map(req.Data)
+		logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
+		rspBody, err = httpPost(apiUrl, reqMap)
 	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APIBankCard4Rsp])
-	err = json.Unmarshal(rspBody, rsp)
-
-	return
-}
 
-// APICompanyBankCard4 企业法人银行卡四要素认证
-func APICompanyBankCard4(req APICompanyBankCard4Req) (rsp *APIRsp[APIBankCard4Rsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/auth/company/bankCard4"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
 	if err != nil {
 		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
 		return
@@ -48,218 +56,213 @@ func APICompanyBankCard4(req APICompanyBankCard4Req) (rsp *APIRsp[APIBankCard4Rs
 		return
 	}
 	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APIBankCard4Rsp])
+	rsp = new(APIRsp[T2])
 	err = json.Unmarshal(rspBody, rsp)
 
 	return
 }
 
-// APICaptchaVerify 认证验证码校验
-func APICaptchaResend(req APICaptchaResendReq) (rsp *APIRsp[interface{}], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/auth/captcha/resend"
+func httpPost(apiUrl string, bizData map[string]interface{}) (rspBody []byte, err error) {
+	appId := config.SerCfg.AsignCfg.AppId
+	privateKey := fmt.Sprintf("-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----", config.SerCfg.AsignCfg.PrivateKey)
 
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
-	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
-	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
-		return
-	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[interface{}])
-	err = json.Unmarshal(rspBody, rsp)
+	// fuck asign dev.
+	timestamp := strconv.Itoa(int(time.Now().UnixMilli() + 10000*60))
 
-	return
-}
-
-// APICaptchaVerify 认证验证码校验
-func APICaptchaVerify(req APICaptchaVerifyReq) (rsp *APIRsp[APICaptchaVerifyRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/auth/captcha/verify"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
+	// 签名
+	sortedData := sortMapByKey(bizData)
+	signature, err := getSignature(sortedData, appId, timestamp, privateKey)
 	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
+		logger.GetLogger().Errorf("签名失败:" + err.Error())
 		return
 	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
-		return
-	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APICaptchaVerifyRsp])
-	err = json.Unmarshal(rspBody, rsp)
 
-	return
-}
-
-// APIGetUser 查询用户信息
-func APIGetUser(req APIGetUserReq) (rsp *APIRsp[[]APIGetUserRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/user/getUser"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
+	// 构建form-data请求参数
+	var requestBody bytes.Buffer
+	multipartWriter := multipart.NewWriter(&requestBody)
+	multipartWriter.WriteField("appId", appId)
+	multipartWriter.WriteField("timestamp", timestamp)
+	multipartWriter.WriteField("bizData", sortedData)
+	multipartWriter.Close()
+	// 构建请求
+	req, err := http.NewRequest("POST", apiUrl, &requestBody)
+	// 设置请求头
+	req.Header.Set("sign", signature)
+	req.Header.Set("timestamp", timestamp)
+	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())
+
+	// 调用接口
+	client := &http.Client{}
+	rsp, err := client.Do(req)
 	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
-	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
+		logger.GetLogger().Errorf("调用接口失败:" + err.Error())
 		return
 	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[[]APIGetUserRsp])
-	err = json.Unmarshal(rspBody, rsp)
+	defer rsp.Body.Close()
+	rspBody, err = io.ReadAll(rsp.Body)
 
 	return
 }
 
-// APIAddPersonalUser 添加个人用户(V2)
-func APIAddPersonalUser(req APIAddPersonalUserReq) (rsp *APIRsp[APIAddUserRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/v2/user/addPersonalUser"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
-	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
+// bizData入参为数组
+func httpPost_Array(apiUrl string, bizData []map[string]interface{}) (rspBody []byte, err error) {
+	appId := config.SerCfg.AsignCfg.AppId
+	privateKey := fmt.Sprintf("-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----", config.SerCfg.AsignCfg.PrivateKey)
+
+	// fuck asign dev.
+	timestamp := strconv.Itoa(int(time.Now().UnixMilli() + 10000*60))
+
+	// 签名
+	sortedData := "["
+	for i, item := range bizData {
+		if i > 0 {
+			sortedData += ","
+		}
+		sortedData += sortMapByKey(item)
 	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
-		return
-	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APIAddUserRsp])
-	err = json.Unmarshal(rspBody, rsp)
+	sortedData += "]"
 
-	return
-}
-
-// APIAddEnterpriseUser 添加企业用户(V2)
-func APIAddEnterpriseUser(req APIAddEnterpriseUserReq) (rsp *APIRsp[APIAddUserRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/v2/user/addEnterpriseUser"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
+	signature, err := getSignature(sortedData, appId, timestamp, privateKey)
 	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
+		logger.GetLogger().Errorf("签名失败:" + err.Error())
 		return
 	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
-		return
-	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APIAddUserRsp])
-	err = json.Unmarshal(rspBody, rsp)
-
-	return
-}
 
-// APITemplateList 查询模板列表
-func APITemplateList(req APITemplateListReq) (rsp *APIRsp[APITemplateListRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/template/list"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
+	// 构建form-data请求参数
+	var requestBody bytes.Buffer
+	multipartWriter := multipart.NewWriter(&requestBody)
+	multipartWriter.WriteField("appId", appId)
+	multipartWriter.WriteField("timestamp", timestamp)
+	multipartWriter.WriteField("bizData", sortedData)
+	multipartWriter.Close()
+	// 构建请求
+	req, err := http.NewRequest("POST", apiUrl, &requestBody)
+	// 设置请求头
+	req.Header.Set("sign", signature)
+	req.Header.Set("timestamp", timestamp)
+	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())
+
+	// 调用接口
+	client := &http.Client{}
+	rsp, err := client.Do(req)
 	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
-	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
+		logger.GetLogger().Errorf("调用接口失败:" + err.Error())
 		return
 	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APITemplateListRsp])
-	err = json.Unmarshal(rspBody, rsp)
+	defer rsp.Body.Close()
+	rspBody, err = io.ReadAll(rsp.Body)
 
 	return
 }
 
-// APICreateContract 上传待签署文件
-func APICreateContract(req APICreateContractReq) (rsp *APIRsp[APICreateContractRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/contract/createContract"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
+// 签名规范:
+// 1、表单提交方式:form-data
+// 示例:1B2M2Y8AsgTpgAmY7PhCfg==
+// 2、请求头部参数
+// 参数1:sign(签名值,具体算法参考一下的前面算法)
+// 参数2:timestamp(时间戳,13位)
+// 3、请求体参数:
+// 参数1:appId(appId值,每个接入者唯一一个)
+// 参数2:timestamp(时间戳,13位,与上述一致)
+// 参数3:bizData(json字符串,举个例子,比方说要传合同编号如:{"contractNo":"0001"})
+// 4、签名算法:
+// 4.1、将上述3所属的bizData(json字符串),按照阿拉伯字母排序(如:{"ba":1,"ac":2}--->{"ac":2,"ba":1}),
+// 4.2、将4.1排序后的字符串,将【bizData+md5(bizData)+ appId + timestatmp】拼接后利用RSA非对称加密算法(SHA1withRSA),计算出最后的签名sign,对其base64编码,放入head的key(sign)中。
+//
+//	 String  sign ;
+//	 String  rsaSuffix = jsonStr + DigestUtils.md5Hex ( jsonStr ) + appId + timestatmp ;
+//	 byte  []  bytes = RSAUtils.generateSHA1withRSASignature ( rsaSuffix, privateKey ) ;
+//	 try {
+//	     sign = Base64Utils.encode ( bytes ) ;
+//	     sign = sign.replaceAll ( " \r\n" , "" ) ;
+//	 }  catch ( Exception e )  {
+//	     log.error( "sdk异常",  e ) ;
+//	     return  ApiRespBody.create ( ApiResponseInfo. _ERROR ) ;
+//	}
+func getSignature(sortedData string, appId, timestamp, privateKey string) (signature string, err error) {
+	pem10, _ := pem.Decode([]byte(privateKey))
+	privateKey10I, _ := x509.ParsePKCS8PrivateKey(pem10.Bytes)
+	privateKey10 := privateKey10I.(*rsa.PrivateKey)
+
+	// md5(bizData)
+	m := md5.New()
+	m.Write([]byte(sortedData))
+	bdMd5Hx := hex.EncodeToString(m.Sum(nil))
+
+	// 待签内容
+	message := sortedData + bdMd5Hx + appId + timestamp
+	h := sha1.New()
+	h.Write([]byte(message))
+	sum := h.Sum(nil)
+
+	// 使用私钥进行签名
+	sign, err := rsa.SignPKCS1v15(rand.Reader, privateKey10, crypto.SHA1, sum)
 	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
-	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
+		fmt.Println("Error signing:", err)
 		return
 	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APICreateContractRsp])
-	err = json.Unmarshal(rspBody, rsp)
-
-	return
-}
 
-// APIAddSigner 添加签署方
-func APIAddSigner(req []APIAddSignerReq) (rsp *APIRsp[APIAddSignerRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/contract/addSigner"
-
-	reqArray := make([]map[string]interface{}, 0)
-	for _, item := range req {
-		reqArray = append(reqArray, structs.Map(item))
-	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqArray)
-	rspBody, err := HttpPost2(apiUrl, reqArray)
-	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
-	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
-		return
-	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APIAddSignerRsp])
-	err = json.Unmarshal(rspBody, rsp)
+	// fmt.Println(signature)
+	signature = base64.StdEncoding.EncodeToString(sign)
+	signature = strings.ReplaceAll(signature, "\r\n", "")
 
 	return
 }
 
-// APIContractStatus 查询合同状态
-func APIContractStatus(req APIContractStatusReq) (rsp *APIRsp[APIContractStatusRsp], err error) {
-	apiUrl := config.SerCfg.AsignCfg.Url + "/contract/status"
-
-	reqMap := structs.Map(req)
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", reqMap)
-	rspBody, err := HttpPost(apiUrl, reqMap)
-	if err != nil {
-		logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error())
-		return
+// sortMapByKey 按key排序map返回string
+func sortMapByKey(data map[string]interface{}) (sortedData string) {
+	keys := make([]string, 0, len(data))
+	for k := range data {
+		keys = append(keys, k)
 	}
-	rspStr := string(rspBody)
-	if len(rspStr) == 0 {
-		logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空")
-		return
+	sort.Strings(keys)
+	for i, k := range keys {
+		if i > 0 {
+			sortedData += ","
+		}
+
+		// 判断是否指针
+		v := reflect.ValueOf(data[k])
+		if v.Kind() == reflect.Ptr {
+			switch data[k].(type) {
+			case *string:
+				sortedData += fmt.Sprintf(`"%s":"%s"`, k, *(data[k].(*string)))
+			case *map[string]interface{}:
+				sortedData += fmt.Sprintf(`"%s":%s`, k, sortMapByKey(*(data[k].(*map[string]interface{}))))
+			case *[]interface{}:
+				list := data[k].([]interface{})
+				sortedData += fmt.Sprintf(`"%s":[`, k)
+				for j, item := range list {
+					if j > 0 {
+						sortedData += ","
+					}
+					sortedData += sortMapByKey(item.(map[string]interface{}))
+				}
+				sortedData += "]"
+			default:
+				sortedData += fmt.Sprintf(`"%s":%v`, k, reflect.ValueOf(data[k]).Elem())
+			}
+		} else {
+			switch data[k].(type) {
+			case string:
+				sortedData += fmt.Sprintf(`"%s":"%s"`, k, data[k].(string))
+			case map[string]interface{}:
+				sortedData += fmt.Sprintf(`"%s":%s`, k, sortMapByKey(data[k].(map[string]interface{})))
+			case []interface{}:
+				list := data[k].([]interface{})
+				sortedData += fmt.Sprintf(`"%s":[`, k)
+				for j, item := range list {
+					if j > 0 {
+						sortedData += ","
+					}
+					sortedData += sortMapByKey(item.(map[string]interface{}))
+				}
+				sortedData += "]"
+			default:
+				sortedData += fmt.Sprintf(`"%s":%v`, k, data[k])
+			}
+		}
 	}
-	logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", rspStr)
-	rsp = new(APIRsp[APIContractStatusRsp])
-	err = json.Unmarshal(rspBody, rsp)
 
-	return
+	return fmt.Sprintf("{%s}", sortedData)
 }

+ 41 - 4
services/asign/apiModels.go

@@ -1,5 +1,14 @@
 package asign
 
+type APIReqData interface {
+	interface{} | APIPersonBankCard4Req | APICompanyBankCard4Req | APICaptchaResendReq | APICaptchaVerifyReq | APIGetUserReq | APIAddPersonalUserReq | APIAddEnterpriseUserReq | APITemplateListReq | APICreateContractReq | APIAddSignerReq | APIContractStatusReq | APIDownloadContractReq | APICreateSealReq | APIModifySealReq
+}
+
+type APIReq[T APIReqData] struct {
+	Data  T   // 请求数据
+	Datas []T // 数组类型请求数据
+}
+
 type APIRspData interface {
 	interface{} | APIBankCard4Rsp | APIAddUserRsp | APICaptchaVerifyRsp | APIGetUserRsp
 }
@@ -295,10 +304,10 @@ type APIContractStatusRsp struct {
 
 // APIDownloadContractReq 下载合同入参
 type APIDownloadContractReq struct {
-	ContractNo       string `json:"contractNo" binding:"required"` // 合同唯一编码
-	Outfile          string `json:"outfile"`                       // 文件本地路径(下载的文件,写到本地,当文件数为1时,下载的文件类型是pdf,否则为zip)
-	Force            int    `json:"force"`                         // 强制下载标识 0(默认):未签署完的无法下载 1:无论什么状态都强制下载
-	DownloadFileType int    `json:"downloadFileType"`              // 下载文件类型,多附件下载:1:PDF文件 2:多个单张PNG文件,含PDF文件,单附件对应单张图片 3:分页PNG压缩文件,含PDF文件 4:合同单张图片,不含PDF文件 5:所有分页图片,不含PDF文件
+	ContractNo       string `json:"contractNo" binding:"required"`         // 合同唯一编码
+	Outfile          string `json:"outfile" structs:",omitempty"`          // 文件本地路径(下载的文件,写到本地,当文件数为1时,下载的文件类型是pdf,否则为zip)
+	Force            int    `json:"force" structs:",omitempty"`            // 强制下载标识 0(默认):未签署完的无法下载 1:无论什么状态都强制下载
+	DownloadFileType int    `json:"downloadFileType" structs:",omitempty"` // 下载文件类型,多附件下载:1:PDF文件 2:多个单张PNG文件,含PDF文件,单附件对应单张图片 3:分页PNG压缩文件,含PDF文件 4:合同单张图片,不含PDF文件 5:所有分页图片,不含PDF文件
 }
 
 // APIDownloadContractRsp 下载合同出参
@@ -309,3 +318,31 @@ type APIDownloadContractRsp struct {
 	Size     int    `json:"size"`     // 文件大小
 	Data     string `json:"data"`     // Base64字符串
 }
+
+// APICreateSealReq 创建印章入参
+type APICreateSealReq struct {
+	Account        string  `json:"account" binding:"required"`          // 用户唯一识别码
+	Image          string  `json:"image" structs:"-"`                   // 印章图片(不传以默认模板生成印章)
+	IsDefault      int     `json:"isDefault"`                           // 是否为默认印章:1 - 是,0 - 否
+	Base64ImageStr string  `json:"base64ImageStr" structs:",omitempty"` // base64格式的印模图片
+	SealName       string  `json:"sealName"`                            // 印章抬头文字(60字符以内)【注】印章下方横向展示的文字,例如“合同专用章”等。若不显示文字可传空格
+	SealNo         string  `json:"sealNo" binding:"required"`           // 印章唯一编号(32字符以内)
+	Scaling        float64 `json:"scaling" structs:",omitempty"`        // 图片缩放百分比(介于0~1之间的数字)
+	Color          string  `json:"color" structs:",omitempty"`          // 印章颜色(仅个人印章有效)【注】参数为RGB16进制数,例如:000000(黑),FF0000(红),默认为黑色。
+	HasBorder      int     `json:"hasBorder" structs:",omitempty"`      // 是否带边框(仅个人印章有效)1 - 是,0 - 否(默认)
+	Shape          int     `json:"shape" structs:",omitempty"`          // 边框样式(仅个人印章有效)
+}
+
+// APIModifySealReq 修改印章入参
+type APIModifySealReq struct {
+	Account        string  `json:"account" binding:"required"`          // 用户唯一识别码
+	Image          string  `json:"image" structs:"-"`                   // 印章图片(不传以默认模板生成印章)
+	IsDefault      int     `json:"isDefault"`                           // 是否为默认印章:1 - 是,0 - 否
+	Base64ImageStr string  `json:"base64ImageStr" structs:",omitempty"` // base64格式的印模图片
+	SealName       string  `json:"sealName"`                            // 印章抬头文字(60字符以内)【注】印章下方横向展示的文字,例如“合同专用章”等。若不显示文字可传空格
+	SealNo         string  `json:"sealNo" binding:"required"`           // 印章编号(搜索印章用,不做更新)
+	Scaling        float64 `json:"scaling" structs:",omitempty"`        // 图片缩放百分比(介于0~1之间的数字)
+	Color          string  `json:"color" structs:",omitempty"`          // 印章颜色(仅个人印章有效)【注】参数为RGB16进制数,例如:000000(黑),FF0000(红),默认为黑色。
+	HasBorder      int     `json:"hasBorder" structs:",omitempty"`      // 是否带边框(仅个人印章有效)1 - 是,0 - 否(默认)
+	Shape          int     `json:"shape" structs:",omitempty"`          // 边框样式(仅个人印章有效)
+}

+ 18 - 0
services/asign/const.go

@@ -3,3 +3,21 @@ package asign
 var (
 	CODE_SUCCESS = 100000 // 成功
 )
+
+type APIURL string
+
+const (
+	APIURL_CreateSeal                = "/user/createSeal"           // 创建印章
+	APIURL_ModifySeal                = "/user/modifySeal"           // 修改印章
+	APIURL_GetUser                   = "/user/getUser"              // 查询用户信息
+	APIURL_Person_BankCard4          = "/auth/person/bankCard4"     // 个人银行卡四要素认证
+	APIURL_Company_BankCard4         = "/auth/company/bankCard4"    // 企业法人银行卡四要素认证
+	APIURL_Captcha_Resend            = "/auth/captcha/resend"       // 认证验证码校验
+	APIURL_Captcha_Verify            = "/auth/captcha/verify"       // 认证验证码校验
+	APIURL_V2_User_AddPersonalUser   = "/v2/user/addPersonalUser"   // 添加个人用户(V2)
+	APIURL_V2_User_AddEnterpriseUser = "/v2/user/addEnterpriseUser" // 添加企业用户(V2)
+	APIURL_Template_List             = "/template/list"             // 查询模板列表
+	APIURL_CreateContract            = "/contract/createContract"   // 上传待签署文件
+	APIURL_AddSigner                 = "/contract/addSigner"        // 添加签署方
+	APIURL_Contract_Status           = "/contract/status"           // 查询合同状态
+)

+ 0 - 232
services/asign/http.go

@@ -1,232 +0,0 @@
-package asign
-
-import (
-	"bytes"
-	"crypto"
-	"crypto/md5"
-	"crypto/rand"
-	"crypto/rsa"
-	"crypto/sha1"
-	"crypto/x509"
-	"encoding/base64"
-	"encoding/hex"
-	"encoding/pem"
-	"fmt"
-	"io"
-	"mime/multipart"
-	"mtp2_if/config"
-	"mtp2_if/logger"
-	"net/http"
-	"reflect"
-	"sort"
-	"strconv"
-	"strings"
-	"time"
-)
-
-// HttpPost
-//
-//	爱签HttpPost请求方法
-func HttpPost(apiUrl string, bizData map[string]interface{}) (rspBody []byte, err error) {
-	appId := config.SerCfg.AsignCfg.AppId
-	privateKey := fmt.Sprintf("-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----", config.SerCfg.AsignCfg.PrivateKey)
-
-	// fuck asign dev.
-	timestamp := strconv.Itoa(int(time.Now().UnixMilli() + 10000*60))
-
-	// 签名
-	sortedData := sortMapByKey(bizData)
-	signature, err := getSignature(sortedData, appId, timestamp, privateKey)
-	if err != nil {
-		logger.GetLogger().Errorf("[asign.HttpPost] 签名失败:" + err.Error())
-		return
-	}
-
-	// 构建form-data请求参数
-	var requestBody bytes.Buffer
-	multipartWriter := multipart.NewWriter(&requestBody)
-	multipartWriter.WriteField("appId", appId)
-	multipartWriter.WriteField("timestamp", timestamp)
-	multipartWriter.WriteField("bizData", sortedData)
-	multipartWriter.Close()
-	// 构建请求
-	req, err := http.NewRequest("POST", apiUrl, &requestBody)
-	// 设置请求头
-	req.Header.Set("sign", signature)
-	req.Header.Set("timestamp", timestamp)
-	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())
-
-	// 调用接口
-	client := &http.Client{}
-	rsp, err := client.Do(req)
-	if err != nil {
-		logger.GetLogger().Errorf("[asign.HttpPost] 调用接口失败:" + err.Error())
-		return
-	}
-	defer rsp.Body.Close()
-	rspBody, err = io.ReadAll(rsp.Body)
-
-	return
-}
-
-func HttpPost2(apiUrl string, bizData []map[string]interface{}) (rspBody []byte, err error) {
-	appId := config.SerCfg.AsignCfg.AppId
-	privateKey := fmt.Sprintf("-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----", config.SerCfg.AsignCfg.PrivateKey)
-
-	// fuck asign dev.
-	timestamp := strconv.Itoa(int(time.Now().UnixMilli() + 10000*60))
-
-	// 签名
-	sortedData := "["
-	for i, item := range bizData {
-		if i > 0 {
-			sortedData += ","
-		}
-		sortedData += sortMapByKey(item)
-	}
-	sortedData += "]"
-
-	signature, err := getSignature(sortedData, appId, timestamp, privateKey)
-	if err != nil {
-		logger.GetLogger().Errorf("[asign.HttpPost] 签名失败:" + err.Error())
-		return
-	}
-
-	// 构建form-data请求参数
-	var requestBody bytes.Buffer
-	multipartWriter := multipart.NewWriter(&requestBody)
-	multipartWriter.WriteField("appId", appId)
-	multipartWriter.WriteField("timestamp", timestamp)
-	multipartWriter.WriteField("bizData", sortedData)
-	multipartWriter.Close()
-	// 构建请求
-	req, err := http.NewRequest("POST", apiUrl, &requestBody)
-	// 设置请求头
-	req.Header.Set("sign", signature)
-	req.Header.Set("timestamp", timestamp)
-	req.Header.Set("Content-Type", multipartWriter.FormDataContentType())
-
-	// 调用接口
-	client := &http.Client{}
-	rsp, err := client.Do(req)
-	if err != nil {
-		logger.GetLogger().Errorf("[asign.HttpPost] 调用接口失败:" + err.Error())
-		return
-	}
-	defer rsp.Body.Close()
-	rspBody, err = io.ReadAll(rsp.Body)
-
-	return
-}
-
-// 签名规范:
-// 1、表单提交方式:form-data
-// 示例:1B2M2Y8AsgTpgAmY7PhCfg==
-// 2、请求头部参数
-// 参数1:sign(签名值,具体算法参考一下的前面算法)
-// 参数2:timestamp(时间戳,13位)
-// 3、请求体参数:
-// 参数1:appId(appId值,每个接入者唯一一个)
-// 参数2:timestamp(时间戳,13位,与上述一致)
-// 参数3:bizData(json字符串,举个例子,比方说要传合同编号如:{"contractNo":"0001"})
-// 4、签名算法:
-// 4.1、将上述3所属的bizData(json字符串),按照阿拉伯字母排序(如:{"ba":1,"ac":2}--->{"ac":2,"ba":1}),
-// 4.2、将4.1排序后的字符串,将【bizData+md5(bizData)+ appId + timestatmp】拼接后利用RSA非对称加密算法(SHA1withRSA),计算出最后的签名sign,对其base64编码,放入head的key(sign)中。
-//
-//	 String  sign ;
-//	 String  rsaSuffix = jsonStr + DigestUtils.md5Hex ( jsonStr ) + appId + timestatmp ;
-//	 byte  []  bytes = RSAUtils.generateSHA1withRSASignature ( rsaSuffix, privateKey ) ;
-//	 try {
-//	     sign = Base64Utils.encode ( bytes ) ;
-//	     sign = sign.replaceAll ( " \r\n" , "" ) ;
-//	 }  catch ( Exception e )  {
-//	     log.error( "sdk异常",  e ) ;
-//	     return  ApiRespBody.create ( ApiResponseInfo. _ERROR ) ;
-//	}
-func getSignature(sortedData string, appId, timestamp, privateKey string) (signature string, err error) {
-	pem10, _ := pem.Decode([]byte(privateKey))
-	privateKey10I, _ := x509.ParsePKCS8PrivateKey(pem10.Bytes)
-	privateKey10 := privateKey10I.(*rsa.PrivateKey)
-
-	// md5(bizData)
-	m := md5.New()
-	m.Write([]byte(sortedData))
-	bdMd5Hx := hex.EncodeToString(m.Sum(nil))
-
-	// 待签内容
-	message := sortedData + bdMd5Hx + appId + timestamp
-	h := sha1.New()
-	h.Write([]byte(message))
-	sum := h.Sum(nil)
-
-	// 使用私钥进行签名
-	sign, err := rsa.SignPKCS1v15(rand.Reader, privateKey10, crypto.SHA1, sum)
-	if err != nil {
-		fmt.Println("Error signing:", err)
-		return
-	}
-
-	// fmt.Println(signature)
-	signature = base64.StdEncoding.EncodeToString(sign)
-	signature = strings.ReplaceAll(signature, "\r\n", "")
-
-	return
-}
-
-// sortMapByKey 按key排序map返回string
-func sortMapByKey(data map[string]interface{}) (sortedData string) {
-	keys := make([]string, 0, len(data))
-	for k := range data {
-		keys = append(keys, k)
-	}
-	sort.Strings(keys)
-	for i, k := range keys {
-		if i > 0 {
-			sortedData += ","
-		}
-
-		// 判断是否指针
-		v := reflect.ValueOf(data[k])
-		if v.Kind() == reflect.Ptr {
-			switch data[k].(type) {
-			case *string:
-				sortedData += fmt.Sprintf(`"%s":"%s"`, k, *(data[k].(*string)))
-			case *map[string]interface{}:
-				sortedData += fmt.Sprintf(`"%s":%s`, k, sortMapByKey(*(data[k].(*map[string]interface{}))))
-			case *[]interface{}:
-				list := data[k].([]interface{})
-				sortedData += fmt.Sprintf(`"%s":[`, k)
-				for j, item := range list {
-					if j > 0 {
-						sortedData += ","
-					}
-					sortedData += sortMapByKey(item.(map[string]interface{}))
-				}
-				sortedData += "]"
-			default:
-				sortedData += fmt.Sprintf(`"%s":%v`, k, reflect.ValueOf(data[k]).Elem())
-			}
-		} else {
-			switch data[k].(type) {
-			case string:
-				sortedData += fmt.Sprintf(`"%s":"%s"`, k, data[k].(string))
-			case map[string]interface{}:
-				sortedData += fmt.Sprintf(`"%s":%s`, k, sortMapByKey(data[k].(map[string]interface{})))
-			case []interface{}:
-				list := data[k].([]interface{})
-				sortedData += fmt.Sprintf(`"%s":[`, k)
-				for j, item := range list {
-					if j > 0 {
-						sortedData += ","
-					}
-					sortedData += sortMapByKey(item.(map[string]interface{}))
-				}
-				sortedData += "]"
-			default:
-				sortedData += fmt.Sprintf(`"%s":%v`, k, data[k])
-			}
-		}
-	}
-
-	return fmt.Sprintf("{%s}", sortedData)
-}

+ 17 - 0
services/asign/models.go

@@ -2,6 +2,23 @@ package asign
 
 import "time"
 
+// CreateSealReq 创建印章
+type CreateSealReq struct {
+	Account        string `json:"account" binding:"required"` // 用户唯一识别码
+	IsDefault      int    `json:"IsDefault"`                  // 是否为默认印章:1 - 是,0 - 否
+	Base64ImageStr string `json:"base64ImageStr"`             // base64格式的印模图片
+	SealName       string `json:"sealName"`                   // 印章抬头文字(60字符以内)【注】印章下方横向展示的文字,例如“合同专用章”等。若不显示文字可传空格
+}
+
+// ModifySealReq 修改印章
+type ModifySealReq struct {
+	Account        string `json:"account" binding:"required"` // 用户唯一识别码
+	IsDefault      int    `json:"IsDefault"`                  // 是否为默认印章:1 - 是,0 - 否
+	Base64ImageStr string `json:"base64ImageStr"`             // base64格式的印模图片
+	SealName       string `json:"sealName"`                   // 印章抬头文字(60字符以内)【注】印章下方横向展示的文字,例如“合同专用章”等。若不显示文字可传空格
+	SealNo         string `json:"sealNo"  binding:"required"` // 印章编号(搜索印章用,不做更新)
+}
+
 // PersonBankCard4 个人银行卡四要素
 type PersonBankCard4 struct {
 	RealName           string `json:"realName"`           // 真实姓名

+ 111 - 48
services/asign/servcies.go

@@ -15,6 +15,54 @@ import (
 	"strconv"
 )
 
+// CreateSeal 创建印章
+func CreateSeal(req CreateSealReq) (err error) {
+	apiReq := APIReq[APICreateSealReq]{
+		Data: APICreateSealReq{
+			Account:        req.Account,
+			IsDefault:      req.IsDefault,
+			Base64ImageStr: req.Base64ImageStr,
+			SealName:       req.SealName,
+			SealNo:         strconv.Itoa(int(utils.GenID())),
+		},
+	}
+
+	apiRsp, err := APIPost[APICreateSealReq, interface{}](apiReq, APIURL_CreateSeal)
+	if err != nil {
+		return
+	}
+	if apiRsp.Code != CODE_SUCCESS {
+		err = errors.New(apiRsp.Msg)
+		return
+	}
+
+	return
+}
+
+// ModifySeal 修改印章
+func ModifySeal(req ModifySealReq) (err error) {
+	apiReq := APIReq[APIModifySealReq]{
+		Data: APIModifySealReq{
+			Account:        req.Account,
+			IsDefault:      req.IsDefault,
+			Base64ImageStr: req.Base64ImageStr,
+			SealName:       req.SealName,
+			SealNo:         req.SealNo,
+		},
+	}
+
+	apiRsp, err := APIPost[APIModifySealReq, interface{}](apiReq, APIURL_ModifySeal)
+	if err != nil {
+		return
+	}
+	if apiRsp.Code != CODE_SUCCESS {
+		err = errors.New(apiRsp.Msg)
+		return
+	}
+
+	return
+}
+
 // BankCard4 银行卡四要素认证
 func BankCard4(req BankCard4Req) (rsp BankCard4Rsp, err error) {
 	// 校验入参
@@ -28,9 +76,12 @@ func BankCard4(req BankCard4Req) (rsp BankCard4Rsp, err error) {
 	}
 
 	// 判断证件号码是否已经在爱签平台存在
-	var getUserReq APIGetUserReq
-	getUserReq.Account = strconv.Itoa(req.UserId)
-	apiRsp, err := APIGetUser(getUserReq)
+	// var getUserReq APIGetUserReq
+	// getUserReq.Account = strconv.Itoa(req.UserId)
+	getUserReq := APIReq[APIGetUserReq]{
+		Data: APIGetUserReq{Account: strconv.Itoa(req.UserId)},
+	}
+	apiRsp, err := APIPost[APIGetUserReq, []APIGetUserRsp](getUserReq, APIURL_GetUser)
 	if err != nil {
 		return
 	}
@@ -39,13 +90,13 @@ func BankCard4(req BankCard4Req) (rsp BankCard4Rsp, err error) {
 		logger.GetLogger().Error("电子签平台用户编号已存在, apiRsp:", apiRsp)
 		return
 	}
-	getUserReq.Account = ""
+	getUserReq.Data.Account = ""
 	if req.Type == 1 {
-		getUserReq.IdCard = req.Person.IdCardNo
+		getUserReq.Data.IdCard = req.Person.IdCardNo
 	} else {
-		getUserReq.CreditCode = req.Company.CreditCode
+		getUserReq.Data.CreditCode = req.Company.CreditCode
 	}
-	apiRsp, err = APIGetUser(getUserReq)
+	apiRsp, err = APIPost[APIGetUserReq, []APIGetUserRsp](getUserReq, APIURL_GetUser)
 	if err != nil {
 		return
 	}
@@ -76,28 +127,32 @@ func BankCard4(req BankCard4Req) (rsp BankCard4Rsp, err error) {
 	var authinfo []byte
 	var bankCard4Rsp *APIRsp[APIBankCard4Rsp]
 	if req.Type == 1 { // 个人
-		personBankCard4Req := APIPersonBankCard4Req{
-			RealName: req.Person.RealName,
-			IdCardNo: req.Person.IdCardNo,
-			BankCard: req.Person.BankCard,
-			Mobile:   req.Person.Mobile,
+		personBankCard4Req := APIReq[APIPersonBankCard4Req]{
+			Data: APIPersonBankCard4Req{
+				RealName: req.Person.RealName,
+				IdCardNo: req.Person.IdCardNo,
+				BankCard: req.Person.BankCard,
+				Mobile:   req.Person.Mobile,
+			},
 		}
-		if bankCard4Rsp, err = APIPersonBankCard4(personBankCard4Req); err != nil {
+		if bankCard4Rsp, err = APIPost[APIPersonBankCard4Req, APIBankCard4Rsp](personBankCard4Req, APIURL_Person_BankCard4); err != nil {
 			return
 		}
 		if authinfo, err = json.Marshal(req.Person); err != nil {
 			return
 		}
 	} else { // 企业
-		companyBankCard4Req := APICompanyBankCard4Req{
-			CompanyName: req.Company.CompanyName,
-			CreditCode:  req.Company.CreditCode,
-			RealName:    req.Company.RealName,
-			IdCardNo:    req.Company.IdCardNo,
-			BankCard:    req.Company.BankCard,
-			Mobile:      req.Company.Mobile,
+		companyBankCard4Req := APIReq[APICompanyBankCard4Req]{
+			Data: APICompanyBankCard4Req{
+				CompanyName: req.Company.CompanyName,
+				CreditCode:  req.Company.CreditCode,
+				RealName:    req.Company.RealName,
+				IdCardNo:    req.Company.IdCardNo,
+				BankCard:    req.Company.BankCard,
+				Mobile:      req.Company.Mobile,
+			},
 		}
-		if bankCard4Rsp, err = APICompanyBankCard4(companyBankCard4Req); err != nil {
+		if bankCard4Rsp, err = APIPost[APICompanyBankCard4Req, APIBankCard4Rsp](companyBankCard4Req, APIURL_Person_BankCard4); err != nil {
 			return
 		}
 		if authinfo, err = json.Marshal(req.Company); err != nil {
@@ -124,8 +179,8 @@ func BankCard4(req BankCard4Req) (rsp BankCard4Rsp, err error) {
 
 // CaptcaResend 重新发送认证验证码
 func CaptcaResend(req CaptchaResendReq) (err error) {
-	apiReq := APICaptchaResendReq{SerialNo: req.SerialNo}
-	apiRsp, err := APICaptchaResend(apiReq)
+	apiReq := APIReq[APICaptchaResendReq]{Data: APICaptchaResendReq{SerialNo: req.SerialNo}}
+	apiRsp, err := APIPost[APICaptchaResendReq, interface{}](apiReq, APIURL_Captcha_Resend)
 	if err != nil {
 		return
 	}
@@ -152,11 +207,13 @@ func CaptchaVerify(req CaptchaVerifyReq) (err error) {
 	}
 
 	// 调用爱签接口 - 认证验证码校验
-	apiReq := APICaptchaVerifyReq{
-		SerialNo: req.SerialNo,
-		Captcha:  req.Captcha,
+	apiReq := APIReq[APICaptchaVerifyReq]{
+		Data: APICaptchaVerifyReq{
+			SerialNo: req.SerialNo,
+			Captcha:  req.Captcha,
+		},
 	}
-	apiRsp, err := APICaptchaVerify(apiReq)
+	apiRsp, err := APIPost[APICaptchaVerifyReq, interface{}](apiReq, APIURL_Captcha_Verify)
 	if err != nil {
 		return
 	}
@@ -168,19 +225,23 @@ func CaptchaVerify(req CaptchaVerifyReq) (err error) {
 	// 调用爱签接口 - 添加用户
 	var addUserRsp *APIRsp[APIAddUserRsp]
 	if req.Type == 1 { // 个人
-		addPersonalUserReq := APIAddPersonalUserReq{
-			Account:  strconv.Itoa(req.UserId),
-			SerialNo: req.SerialNo,
+		addPersonalUserReq := APIReq[APIAddPersonalUserReq]{
+			Data: APIAddPersonalUserReq{
+				Account:  strconv.Itoa(req.UserId),
+				SerialNo: req.SerialNo,
+			},
 		}
-		if addUserRsp, err = APIAddPersonalUser(addPersonalUserReq); err != nil {
+		if addUserRsp, err = APIPost[APIAddPersonalUserReq, APIAddUserRsp](addPersonalUserReq, APIURL_V2_User_AddPersonalUser); err != nil {
 			return
 		}
 	} else { // 企业
-		addEnterpriseUserReq := APIAddEnterpriseUserReq{
-			Account:  strconv.Itoa(req.UserId),
-			SerialNo: req.SerialNo,
+		addEnterpriseUserReq := APIReq[APIAddEnterpriseUserReq]{
+			Data: APIAddEnterpriseUserReq{
+				Account:  strconv.Itoa(req.UserId),
+				SerialNo: req.SerialNo,
+			},
 		}
-		if addUserRsp, err = APIAddEnterpriseUser(addEnterpriseUserReq); err != nil {
+		if addUserRsp, err = APIPost[APIAddEnterpriseUserReq, APIAddUserRsp](addEnterpriseUserReq, APIURL_V2_User_AddEnterpriseUser); err != nil {
 			return
 		}
 	}
@@ -290,8 +351,8 @@ func SyncContractStatus(req SyncContractStatusReq) (rsp SyncContractStatusRsp, e
 	}
 
 	// 调用爱签接口 - 查询合同状态
-	apiReq := APIContractStatusReq{ContractNo: record.CONTRACTNO}
-	apiRsp, err := APIContractStatus(apiReq)
+	apiReq := APIReq[APIContractStatusReq]{Data: APIContractStatusReq{ContractNo: record.CONTRACTNO}}
+	apiRsp, err := APIPost[APIContractStatusReq, APIContractStatusRsp](apiReq, APIURL_Contract_Status)
 	if err != nil {
 		return
 	}
@@ -367,8 +428,8 @@ func CreateContract(req CreateContractReq) (rsp CreateContractRsp, err error) {
 	}
 
 	// 调用爱签接口 - 查询模板列表
-	apiReq := APITemplateListReq{Page: 1, Rows: 10}
-	apiRsp, err := APITemplateList(apiReq)
+	apiReq := APIReq[APITemplateListReq]{Data: APITemplateListReq{Page: 1, Rows: 10}}
+	apiRsp, err := APIPost[APITemplateListReq, APITemplateListRsp](apiReq, APIURL_Template_List)
 	if err != nil {
 		return
 	}
@@ -423,19 +484,21 @@ func CreateContract(req CreateContractReq) (rsp CreateContractRsp, err error) {
 
 	// 调用爱签接口 - 上传待签署文件
 	contractNo := strconv.Itoa(int(utils.GenID()))
-	apiCreateContractReq := APICreateContractReq{
-		ContractNo:   contractNo,
-		ContractName: templateName,
-		SignOrder:    1,
-		ValidityTime: 30,
-		NotifyUrl:    config.SerCfg.AsignCfg.NotifyUrl,
+	apiCreateContractReq := APIReq[APICreateContractReq]{
+		Data: APICreateContractReq{
+			ContractNo:   contractNo,
+			ContractName: templateName,
+			SignOrder:    1,
+			ValidityTime: 30,
+			NotifyUrl:    config.SerCfg.AsignCfg.NotifyUrl,
+		},
 	}
-	apiCreateContractReq.Templates = []APITemplate{
+	apiCreateContractReq.Data.Templates = []APITemplate{
 		{
 			TemplateNo: templateIdent,
 		},
 	}
-	apiCreateContractRsp, err := APICreateContract(apiCreateContractReq)
+	apiCreateContractRsp, err := APIPost[APICreateContractReq, APICreateContractRsp](apiCreateContractReq, APIURL_CreateContract)
 	if err != nil {
 		return
 	}
@@ -532,7 +595,7 @@ func CreateContract(req CreateContractReq) (rsp CreateContractRsp, err error) {
 	if len(receiverFillStrategyList) > 0 {
 		appAddSignerReq2.ReceiverFillStrategyList = receiverFillStrategyList
 	}
-	apiAddSignerRsp, err := APIAddSigner([]APIAddSignerReq{appAddSignerReq1, appAddSignerReq2})
+	apiAddSignerRsp, err := APIPost[APIAddSignerReq, APIAddSignerRsp](APIReq[APIAddSignerReq]{Datas: []APIAddSignerReq{appAddSignerReq1, appAddSignerReq2}}, APIURL_AddSigner)
 	if err != nil {
 		return
 	}