zhou.xiaoning vor 2 Jahren
Ursprung
Commit
121b505a09

+ 2 - 1
.gitignore

@@ -1,3 +1,4 @@
 log/
 __debug_bin.exe
-bin/
+bin/
+py/__pycache__/

+ 68 - 0
api/v1/account/certification.go

@@ -0,0 +1,68 @@
+package account
+
+import (
+	"mtp20access/global"
+	"mtp20access/model/account/request"
+	"mtp20access/model/common/response"
+	signService "mtp20access/service/sign"
+	"mtp20access/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+// AddUser 实名认证添加用户
+// @Summary  实名认证添加用户
+// @Security ApiKeyAuth
+// @accept   application/json
+// @Produce  application/json
+// @Param    data body     request.AddUserReq            true "入参"
+// @Success  200  {object} response.Response{msg=string} "出参"
+// @Router   /Account/AddUser [post]
+// @Tags     账户服务
+func AddUser(c *gin.Context) {
+	g := utils.GinUtils{C: c}
+	r := request.AddUserReq{}
+	g.BindJsonReq(&r)
+	if g.Err != nil {
+		return
+	}
+
+	// 获取请求账号信息
+	claims := g.GetClaims()
+	if g.Err != nil {
+		return
+	}
+
+	if err := signService.AddUser(r, claims.UserID); err == nil {
+		response.Ok(g.C)
+	} else {
+		global.M2A_LOG.Error(err.Error(), zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+	}
+}
+
+// AddUser 查询用户电子签记录表
+// @Summary  查询用户电子签记录表
+// @Security ApiKeyAuth
+// @accept   application/json
+// @Produce  application/json
+// @Success  200 {object} response.Response{data=[]account.Useresignrecord,msg=string} "出参"
+// @Router   /Account/QueryUserESignRecord [get]
+// @Tags     账户服务
+func QueryUserESignRecord(c *gin.Context) {
+	g := utils.GinUtils{C: c}
+
+	// 获取请求账号信息
+	claims := g.GetClaims()
+	if g.Err != nil {
+		return
+	}
+
+	if rsp, err := signService.QueryUserESignRecord(claims.UserID); err == nil {
+		response.OkWithDetailed(rsp, "查询成功", g.C)
+	} else {
+		global.M2A_LOG.Error(err.Error(), zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+	}
+}

+ 2 - 2
client/client.go

@@ -79,7 +79,7 @@ func (r *Client) SetQuoteSubs(req request.QuoteSubscribeReq) {
 
 // SetQuoteSubs 设置交易通知订阅信息
 func (r *Client) SetTradeNft() {
-	global.M2A_LOG.Info("SetTradeNft 11111111111111111111111111111")
+	// global.M2A_LOG.Info("SetTradeNft 11111111111111111111111111111")
 	// r.tmtx.Lock()
 	// defer r.tmtx.Unlock()
 
@@ -198,7 +198,7 @@ func (r *Client) GetAllAsyncTask() *map[string]*AsyncTask {
 // **************** Quote WebSocket ****************
 
 func (r *Client) SetQuoteWebSocket(ws *websocket.Conn) (err error) {
-	global.M2A_LOG.Info("行情长连 SetQuoteWebSocket 22222222222222")
+	// global.M2A_LOG.Info("行情长连 SetQuoteWebSocket 22222222222222")
 
 	r.mtx.Lock()
 	defer r.mtx.Unlock()

+ 7 - 1
config.yaml

@@ -6,7 +6,7 @@ zap:
   director: 'log'
   encode-level: 'LowercaseColorLevelEncoder'
   stacktrace-key: 'stacktrace'
-  max-age: 30 # 默认日志留存默认以天为单位
+  max-age: 365 # 默认日志留存默认以天为单位
   show-line: true
   log-in-console: true
 
@@ -51,6 +51,12 @@ rabbitmq:
   url: 'amqp://guest:guest@192.168.31.204:5020/test'
   exchange: 'entry'
 
+# 爱签开放平台配置
+asign:
+  url: 'https://prev.asign.cn/' # 测试环境 - https://prev.asign.cn/  正式环境 - https://oapi.asign.cn/
+  appId: '290912417'
+  privateKey: 'MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAkMD+72J6iAF0ZNV+3t628lsRHfJ80nKZWK5/C7Pg+AZmOIzJlwHsKhRzCvxoxqYHQprhiFzW9l73v9vD9l1JYwIDAQABAkBVijccr01JYdKuY5t9iI8D2NzcnZc1pZMI3NUmzT18Uyg7b9CUvGHlLeg/gdT4QtVd7wIzHYCY4letEcEMh54BAiEAwzNWusj5XiLmty7PI0Hbakx4HtcND1+P0UHLEWqWOuECIQC91zQuL7nStgGzT3HvaeBB5Ouapa39fHRm2nCjHaxwwwIgRR2XdvmUOj23XWMomr5F14SN/7V7fVcD0D8wjNElsmECIDYavV5kb7tj7/wgqkInlKhzC8rZaUsTS0F9BBkY/eptAiAQJ8Saz8YlMIESdHMxANGSog01fECbcZqLFMuNf8SorA=='
+
 # 跨域配置
 # 需要配合 server/initialize/router.go#L32 使用
 cors:

+ 7 - 0
config/asign.go

@@ -0,0 +1,7 @@
+package config
+
+type Asign struct {
+	URL        string `mapstructure:"url" json:"url" yaml:"url"`                      // 接口地址
+	AppId      string `mapstructure:"appId" json:"appId" yaml:"appId"`                // AppId
+	PrivateKey string `mapstructure:"privateKey" json:"privateKey" yaml:"privateKey"` // 应用私钥
+}

+ 3 - 0
config/config.go

@@ -14,4 +14,7 @@ type Server struct {
 
 	// 跨域配置
 	Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
+
+	// 爱签开放平台配置
+	Asign Asign `mapstructure:"asign" json:"asign" yaml:"asign"`
 }

+ 197 - 0
docs/docs.go

@@ -16,6 +16,56 @@ const docTemplate = `{
     "host": "{{.Host}}",
     "basePath": "{{.BasePath}}",
     "paths": {
+        "/Account/AddUser": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "账户服务"
+                ],
+                "summary": "实名认证添加用户",
+                "parameters": [
+                    {
+                        "description": "入参",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/request.AddUserReq"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "出参",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/response.Response"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "msg": {
+                                            "type": "string"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/Account/Login": {
             "post": {
                 "consumes": [
@@ -103,6 +153,51 @@ const docTemplate = `{
                 }
             }
         },
+        "/Account/QueryUserESignRecord": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "账户服务"
+                ],
+                "summary": "查询用户电子签记录表",
+                "responses": {
+                    "200": {
+                        "description": "出参",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/response.Response"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/account.Useresignrecord"
+                                            }
+                                        },
+                                        "msg": {
+                                            "type": "string"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/Account/TokenCheck": {
             "get": {
                 "security": [
@@ -292,6 +387,108 @@ const docTemplate = `{
         }
     },
     "definitions": {
+        "account.Useresignrecord": {
+            "type": "object",
+            "properties": {
+                "authinfo": {
+                    "description": "认证信息",
+                    "type": "string"
+                },
+                "contractfileaddr": {
+                    "description": "合同签署文件地址",
+                    "type": "string"
+                },
+                "contractno": {
+                    "description": "合同编号",
+                    "type": "string"
+                },
+                "createtime": {
+                    "description": "创建时间",
+                    "type": "string"
+                },
+                "creatorid": {
+                    "description": "创建人",
+                    "type": "integer"
+                },
+                "orderindex": {
+                    "description": "显示顺序",
+                    "type": "integer"
+                },
+                "recordid": {
+                    "description": "记录ID(SEQ_USERESIGNRECORD)",
+                    "type": "integer"
+                },
+                "recordstatus": {
+                    "description": "记录状态 - 1:未签署 2:签署中 3:已签署 4:签署拒绝",
+                    "type": "integer"
+                },
+                "signremark": {
+                    "description": "签署备注",
+                    "type": "string"
+                },
+                "signurl": {
+                    "description": "合同签署URL(三方URL)",
+                    "type": "string"
+                },
+                "templateconfigid": {
+                    "description": "模板配置ID",
+                    "type": "integer"
+                },
+                "templatename": {
+                    "description": "模板名称",
+                    "type": "string"
+                },
+                "templateno": {
+                    "description": "模板编号(电子签类型对应的模板编号)",
+                    "type": "string"
+                },
+                "templatetype": {
+                    "description": "模板类型 - 1:实名认证 2:开户协议",
+                    "type": "integer"
+                },
+                "updatetime": {
+                    "description": "更新时间",
+                    "type": "string"
+                },
+                "userid": {
+                    "description": "用户ID",
+                    "type": "integer"
+                }
+            }
+        },
+        "request.AddUserReq": {
+            "type": "object",
+            "required": [
+                "idCard",
+                "name"
+            ],
+            "properties": {
+                "idCard": {
+                    "description": "证件号码",
+                    "type": "string"
+                },
+                "idCardPhoto": {
+                    "description": "证件照正面",
+                    "type": "string"
+                },
+                "idCardPhotoBackURL": {
+                    "description": "证件照背面",
+                    "type": "string"
+                },
+                "idCardType": {
+                    "description": "证件类型(默认1):1-居民身份证 2-台湾居民来往内地通行证 3-港澳居民往来内地通行证 10-武装警察身份证 11-军人身份证 15-警察(警官)证 21-外国人永久居留证 23-护照",
+                    "type": "integer"
+                },
+                "mobile": {
+                    "description": "手机号码",
+                    "type": "string"
+                },
+                "name": {
+                    "description": "用户姓名",
+                    "type": "string"
+                }
+            }
+        },
         "request.LoginReq": {
             "type": "object",
             "required": [

+ 197 - 0
docs/swagger.json

@@ -7,6 +7,56 @@
         "version": "0.0.1"
     },
     "paths": {
+        "/Account/AddUser": {
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "账户服务"
+                ],
+                "summary": "实名认证添加用户",
+                "parameters": [
+                    {
+                        "description": "入参",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/request.AddUserReq"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "出参",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/response.Response"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "msg": {
+                                            "type": "string"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/Account/Login": {
             "post": {
                 "consumes": [
@@ -94,6 +144,51 @@
                 }
             }
         },
+        "/Account/QueryUserESignRecord": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "账户服务"
+                ],
+                "summary": "查询用户电子签记录表",
+                "responses": {
+                    "200": {
+                        "description": "出参",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/response.Response"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/account.Useresignrecord"
+                                            }
+                                        },
+                                        "msg": {
+                                            "type": "string"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/Account/TokenCheck": {
             "get": {
                 "security": [
@@ -283,6 +378,108 @@
         }
     },
     "definitions": {
+        "account.Useresignrecord": {
+            "type": "object",
+            "properties": {
+                "authinfo": {
+                    "description": "认证信息",
+                    "type": "string"
+                },
+                "contractfileaddr": {
+                    "description": "合同签署文件地址",
+                    "type": "string"
+                },
+                "contractno": {
+                    "description": "合同编号",
+                    "type": "string"
+                },
+                "createtime": {
+                    "description": "创建时间",
+                    "type": "string"
+                },
+                "creatorid": {
+                    "description": "创建人",
+                    "type": "integer"
+                },
+                "orderindex": {
+                    "description": "显示顺序",
+                    "type": "integer"
+                },
+                "recordid": {
+                    "description": "记录ID(SEQ_USERESIGNRECORD)",
+                    "type": "integer"
+                },
+                "recordstatus": {
+                    "description": "记录状态 - 1:未签署 2:签署中 3:已签署 4:签署拒绝",
+                    "type": "integer"
+                },
+                "signremark": {
+                    "description": "签署备注",
+                    "type": "string"
+                },
+                "signurl": {
+                    "description": "合同签署URL(三方URL)",
+                    "type": "string"
+                },
+                "templateconfigid": {
+                    "description": "模板配置ID",
+                    "type": "integer"
+                },
+                "templatename": {
+                    "description": "模板名称",
+                    "type": "string"
+                },
+                "templateno": {
+                    "description": "模板编号(电子签类型对应的模板编号)",
+                    "type": "string"
+                },
+                "templatetype": {
+                    "description": "模板类型 - 1:实名认证 2:开户协议",
+                    "type": "integer"
+                },
+                "updatetime": {
+                    "description": "更新时间",
+                    "type": "string"
+                },
+                "userid": {
+                    "description": "用户ID",
+                    "type": "integer"
+                }
+            }
+        },
+        "request.AddUserReq": {
+            "type": "object",
+            "required": [
+                "idCard",
+                "name"
+            ],
+            "properties": {
+                "idCard": {
+                    "description": "证件号码",
+                    "type": "string"
+                },
+                "idCardPhoto": {
+                    "description": "证件照正面",
+                    "type": "string"
+                },
+                "idCardPhotoBackURL": {
+                    "description": "证件照背面",
+                    "type": "string"
+                },
+                "idCardType": {
+                    "description": "证件类型(默认1):1-居民身份证 2-台湾居民来往内地通行证 3-港澳居民往来内地通行证 10-武装警察身份证 11-军人身份证 15-警察(警官)证 21-外国人永久居留证 23-护照",
+                    "type": "integer"
+                },
+                "mobile": {
+                    "description": "手机号码",
+                    "type": "string"
+                },
+                "name": {
+                    "description": "用户姓名",
+                    "type": "string"
+                }
+            }
+        },
         "request.LoginReq": {
             "type": "object",
             "required": [

+ 129 - 0
docs/swagger.yaml

@@ -1,4 +1,80 @@
 definitions:
+  account.Useresignrecord:
+    properties:
+      authinfo:
+        description: 认证信息
+        type: string
+      contractfileaddr:
+        description: 合同签署文件地址
+        type: string
+      contractno:
+        description: 合同编号
+        type: string
+      createtime:
+        description: 创建时间
+        type: string
+      creatorid:
+        description: 创建人
+        type: integer
+      orderindex:
+        description: 显示顺序
+        type: integer
+      recordid:
+        description: 记录ID(SEQ_USERESIGNRECORD)
+        type: integer
+      recordstatus:
+        description: 记录状态 - 1:未签署 2:签署中 3:已签署 4:签署拒绝
+        type: integer
+      signremark:
+        description: 签署备注
+        type: string
+      signurl:
+        description: 合同签署URL(三方URL)
+        type: string
+      templateconfigid:
+        description: 模板配置ID
+        type: integer
+      templatename:
+        description: 模板名称
+        type: string
+      templateno:
+        description: 模板编号(电子签类型对应的模板编号)
+        type: string
+      templatetype:
+        description: 模板类型 - 1:实名认证 2:开户协议
+        type: integer
+      updatetime:
+        description: 更新时间
+        type: string
+      userid:
+        description: 用户ID
+        type: integer
+    type: object
+  request.AddUserReq:
+    properties:
+      idCard:
+        description: 证件号码
+        type: string
+      idCardPhoto:
+        description: 证件照正面
+        type: string
+      idCardPhotoBackURL:
+        description: 证件照背面
+        type: string
+      idCardType:
+        description: 证件类型(默认1):1-居民身份证 2-台湾居民来往内地通行证 3-港澳居民往来内地通行证 10-武装警察身份证 11-军人身份证
+          15-警察(警官)证 21-外国人永久居留证 23-护照
+        type: integer
+      mobile:
+        description: 手机号码
+        type: string
+      name:
+        description: 用户姓名
+        type: string
+    required:
+    - idCard
+    - name
+    type: object
   request.LoginReq:
     properties:
       clientType:
@@ -107,6 +183,34 @@ info:
   title: Swagger Example API
   version: 0.0.1
 paths:
+  /Account/AddUser:
+    post:
+      consumes:
+      - application/json
+      parameters:
+      - description: 入参
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/request.AddUserReq'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: 出参
+          schema:
+            allOf:
+            - $ref: '#/definitions/response.Response'
+            - properties:
+                msg:
+                  type: string
+              type: object
+      security:
+      - ApiKeyAuth: []
+      summary: 实名认证添加用户
+      tags:
+      - 账户服务
   /Account/Login:
     post:
       consumes:
@@ -156,6 +260,31 @@ paths:
       summary: 用户登出请求
       tags:
       - 账户服务
+  /Account/QueryUserESignRecord:
+    get:
+      consumes:
+      - application/json
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: 出参
+          schema:
+            allOf:
+            - $ref: '#/definitions/response.Response'
+            - properties:
+                data:
+                  items:
+                    $ref: '#/definitions/account.Useresignrecord'
+                  type: array
+                msg:
+                  type: string
+              type: object
+      security:
+      - ApiKeyAuth: []
+      summary: 查询用户电子签记录表
+      tags:
+      - 账户服务
   /Account/TokenCheck:
     get:
       consumes:

+ 12 - 9
go.mod

@@ -7,8 +7,8 @@ require (
 	github.com/gin-gonic/gin v1.8.1
 	github.com/go-redis/redis/v8 v8.11.5
 	github.com/gofrs/uuid v4.0.0+incompatible
-	github.com/golang-jwt/jwt/v4 v4.4.2
-	github.com/golang/protobuf v1.5.2
+	github.com/golang-jwt/jwt/v4 v4.5.0
+	github.com/golang/protobuf v1.5.3
 	github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c
 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
 	github.com/mattn/go-oci8 v0.1.1
@@ -18,8 +18,8 @@ require (
 	github.com/swaggo/gin-swagger v1.3.0
 	github.com/swaggo/swag v1.8.4
 	go.uber.org/zap v1.21.0
-	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
-	google.golang.org/protobuf v1.28.0
+	golang.org/x/sync v0.3.0
+	google.golang.org/protobuf v1.31.0
 	xorm.io/xorm v1.3.1
 )
 
@@ -37,9 +37,11 @@ require (
 	github.com/go-playground/validator/v10 v10.10.0 // indirect
 	github.com/goccy/go-json v0.9.7 // indirect
 	github.com/golang/snappy v0.0.4 // indirect
+	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/kr/pretty v0.3.1 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/lestrrat-go/strftime v1.0.6 // indirect
 	github.com/magiconair/properties v1.8.6 // indirect
@@ -55,16 +57,17 @@ require (
 	github.com/spf13/cast v1.5.0 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/stretchr/testify v1.8.4 // indirect
 	github.com/subosito/gotenv v1.3.0 // indirect
 	github.com/syndtr/goleveldb v1.0.0 // indirect
 	github.com/ugorji/go/codec v1.2.7 // indirect
 	go.uber.org/atomic v1.7.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
-	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
-	golang.org/x/net v0.0.0-20220811182439-13a9a731de15 // indirect
-	golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
-	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.12 // indirect
+	golang.org/x/crypto v0.12.0 // indirect
+	golang.org/x/net v0.12.0 // indirect
+	golang.org/x/sys v0.11.0 // indirect
+	golang.org/x/text v0.12.0 // indirect
+	golang.org/x/tools v0.6.0 // indirect
 	gopkg.in/ini.v1 v1.66.4 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect

+ 27 - 23
go.sum

@@ -183,8 +183,8 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
-github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -213,8 +213,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
@@ -232,7 +232,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -364,8 +365,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -502,8 +504,9 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
@@ -549,7 +552,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
 github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
@@ -626,8 +630,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
-golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
+golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -662,7 +666,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -705,8 +709,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20220811182439-13a9a731de15 h1:cik0bxZUSJVDyaHf1hZPSDsU8SZHGQZQMeueXCE7yBQ=
-golang.org/x/net v0.0.0-20220811182439-13a9a731de15/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
+golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -727,8 +731,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
+golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -788,8 +792,8 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -799,8 +803,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
+golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -864,8 +868,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -970,8 +974,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
-google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

+ 2 - 2
initialize/rabbitmq.go

@@ -272,7 +272,7 @@ func (t *MQProc) onNtf(funcode uint32, sessionId uint32, bytes *[]byte) {
 		}
 	}
 
-	global.M2A_LOG.Info("----------退出通知逻辑", zap.Any("funcode", funcode), zap.Any("sessionId", sessionId))
+	// global.M2A_LOG.Info("----------退出通知逻辑", zap.Any("funcode", funcode), zap.Any("sessionId", sessionId))
 }
 
 // getRspProtobuf 将总线回复的数据反序列化为Protobuf
@@ -293,7 +293,7 @@ func (t *MQProc) getRspProtobuf(msg *[]byte) (funcode uint32, sessionId uint32,
 		global.LogoutRsp,
 		global.ListingOrderChangeNtf: // 资金变化通知等
 
-		global.M2A_LOG.Info("进入通知逻辑----------", zap.Any("funcode", funcode), zap.Any("sessionId", sessionId))
+		// global.M2A_LOG.Info("进入通知逻辑----------", zap.Any("funcode", funcode), zap.Any("sessionId", sessionId))
 
 		bytes = &b
 	case global.ModifyPwdRsp: // 修改账户密码应答

+ 28 - 0
model/account/certification.go

@@ -0,0 +1,28 @@
+package account
+
+import "time"
+
+// Useresignrecord 用户电子签记录表
+type Useresignrecord struct {
+	RECORDID         int64     `json:"recordid" xorm:"RECORDID"`                 // 记录ID(SEQ_USERESIGNRECORD)
+	USERID           int64     `json:"userid" xorm:"USERID"`                     // 用户ID
+	TEMPLATECONFIGID int64     `json:"templateconfigid" xorm:"TEMPLATECONFIGID"` // 模板配置ID
+	TEMPLATENO       string    `json:"templateno" xorm:"TEMPLATENO"`             // 模板编号(电子签类型对应的模板编号)
+	TEMPLATETYPE     int32     `json:"templatetype" xorm:"TEMPLATETYPE"`         // 模板类型 - 1:实名认证 2:开户协议
+	TEMPLATENAME     string    `json:"templatename" xorm:"TEMPLATENAME"`         // 模板名称
+	ORDERINDEX       int32     `json:"orderindex" xorm:"ORDERINDEX"`             // 显示顺序
+	RECORDSTATUS     int32     `json:"recordstatus" xorm:"RECORDSTATUS"`         // 记录状态 - 1:未签署 2:签署中 3:已签署 4:签署拒绝
+	CONTRACTNO       string    `json:"contractno" xorm:"CONTRACTNO"`             // 合同编号
+	CONTRACTFILEADDR string    `json:"contractfileaddr" xorm:"CONTRACTFILEADDR"` // 合同签署文件地址
+	SIGNREMARK       string    `json:"signremark" xorm:"SIGNREMARK"`             // 签署备注
+	CREATORID        int64     `json:"creatorid" xorm:"CREATORID"`               // 创建人
+	CREATETIME       time.Time `json:"createtime" xorm:"CREATETIME"`             // 创建时间
+	UPDATETIME       time.Time `json:"updatetime" xorm:"UPDATETIME"`             // 更新时间
+	AUTHINFO         string    `json:"authinfo" xorm:"AUTHINFO"`                 // 认证信息
+	SIGNURL          string    `json:"signurl" xorm:"SIGNURL"`                   // 合同签署URL(三方URL)
+}
+
+// TableName is USERESIGNRECORD
+func (r *Useresignrecord) TableName() string {
+	return "USERESIGNRECORD"
+}

+ 11 - 0
model/account/request/certification.go

@@ -0,0 +1,11 @@
+package request
+
+// AddUserReq 实名认证添加用户请求参数
+type AddUserReq struct {
+	Name               string `json:"name" binding:"required"`    // 用户姓名
+	IdCard             string `json:"idCard"  binding:"required"` // 证件号码
+	IdCardType         *int   `json:"idCardType"`                 // 证件类型(默认1):1-居民身份证 2-台湾居民来往内地通行证 3-港澳居民往来内地通行证 10-武装警察身份证 11-军人身份证 15-警察(警官)证 21-外国人永久居留证 23-护照
+	IdCardPhotoURL     string `json:"idCardPhoto"`                // 证件照正面
+	IdCardPhotoBackURL string `json:"idCardPhotoBackURL"`         // 证件照背面
+	Mobile             string `json:"mobile"`                     // 手机号码
+}

+ 1 - 0
model/account/request/login.go

@@ -5,4 +5,5 @@ type LoginReq struct {
 	UserName   string `from:"userName" binding:"required"`   // 用户名,可以是LoginID/LoginCode/手机号码
 	Password   string `from:"password" binding:"required"`   // 密码
 	ClientType int    `from:"clientType" binding:"required"` // 客户端类型,2-PC交易端 3-手机客户端_安卓 4-网页客户端 5-微信客户端 6-手机客户端_苹果
+	// ClientVersion string `form:"clientVersion" binding:"required"` // 客户端版本号
 }

+ 1 - 1
model/common/request/jwt.go

@@ -14,5 +14,5 @@ type BaseClaims struct {
 	LoginID   int
 	Group     int
 	SessionID int
-	// FIXME: 要不要这里存UserID?
+	UserID    int
 }

+ 7 - 8
publish/publish.go

@@ -1,7 +1,6 @@
 package publish
 
 import (
-	"fmt"
 	"sync"
 )
 
@@ -47,17 +46,17 @@ func (p *Publisher) Unsubscribe(topic Topic, ch chan interface{}) {
 }
 
 func (p *Publisher) Publish(topic Topic, data interface{}) {
-	fmt.Printf("Publish++++++++++++ 1 \n")
+	// fmt.Printf("Publish++++++++++++ 1 \n")
 
 	p.mu.RLock()
 	defer p.mu.RUnlock()
 
-	fmt.Printf("Publish++++++++++++ 2 \n")
+	// fmt.Printf("Publish++++++++++++ 2 \n")
 
 	if subs, ok := p.subscribers[topic]; ok {
-		if topic == Topic_Trading {
-			fmt.Printf("Publish-Topic_Trading 获取sub, topic: %v sub: %v \n", topic, subs)
-		}
+		// if topic == Topic_Trading {
+		// 	fmt.Printf("Publish-Topic_Trading 获取sub, topic: %v sub: %v \n", topic, subs)
+		// }
 		for _, ch := range subs {
 			select {
 			case ch <- data:
@@ -66,8 +65,8 @@ func (p *Publisher) Publish(topic Topic, data interface{}) {
 			}
 		}
 	} else {
-		fmt.Printf("Publish 获取订阅目标失败, topic: %v \n", topic)
+		// fmt.Printf("Publish 获取订阅目标失败, topic: %v \n", topic)
 	}
 
-	fmt.Printf("Publish++++++++++++ 3 \n")
+	// fmt.Printf("Publish++++++++++++ 3 \n")
 }

+ 58 - 0
py/Api.py

@@ -0,0 +1,58 @@
+#coding=utf-8
+import HttpUtils
+
+# 添加用户 https://{host}/user/addPersonalUser
+def addPerson(inputData):
+    # 从输入数据中获取调用接口
+    account = inputData["account"]
+    name = inputData["name"]
+    mobile = inputData["mobile"]
+    idCard = inputData["idCard"]
+    idCardType = inputData["idCardType"]
+    
+    appId = inputData["appId"]
+    appKey = inputData["appKey"]
+    apiUrl = inputData["apiUrl"]
+
+    # 组装参数
+    reqBodyData = {
+        "account": account, 
+        "idCard": idCard, 
+        "idCardType": idCardType,
+        "identifyMobile": mobile, 
+        "identifyType": 1,
+        "mobile": mobile, 
+        "name": name, 
+        "isNotice": 1
+    }
+    #请求地址
+    # url = "https://prev.asign.cn/user/addPersonalUser"
+    Result = HttpUtils.HttpUtils.doPOST(apiUrl, reqBodyData, appId, appKey)
+
+    return Result
+
+# 上传待签署文件 https://{host}/contract/createContract
+def createContract(inputData):
+    # 从输入数据中获取调用接口
+    contractNo = inputData["contractNo"]
+    contractName = inputData["contractName"]
+    signOrder = inputData["signOrder"]
+    templates = inputData["templates"]
+    notifyUrl = inputData["notifyUrl"]
+    
+    appId = inputData["appId"]
+    appKey = inputData["appKey"]
+    apiUrl = inputData["apiUrl"]
+
+    # 组装参数
+    reqBodyData = {
+        "contractNo": contractNo, 
+        "contractName": contractName, 
+        "signOrder": signOrder,
+        "templates": templates, 
+        "notifyUrl": notifyUrl
+    }
+    #请求地址
+    Result = HttpUtils.HttpUtils.doPOST(apiUrl, reqBodyData, appId, appKey)
+
+    return Result

+ 23 - 0
py/Enter.py

@@ -0,0 +1,23 @@
+import sys
+import json
+import Api
+import io
+
+def main():
+    # 创建一个使用UTF-8编码的包装器
+    sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding='utf-8')
+    # 从标准输入读取JSON字符串
+    input_json = sys.stdin.read().strip()
+    # 解析JSON数据
+    inputData = json.loads(input_json)
+    # 从输入数据中获取调用接口
+    api = inputData["api"]
+    if (api == "addPerson"):
+        result = Api.addPerson(inputData)
+
+    # 输出结果
+    print(result)
+
+
+if __name__ == "__main__":
+    main()

+ 89 - 0
py/HttpUtils.py

@@ -0,0 +1,89 @@
+#coding=utf-8
+import hashlib
+import json
+import random
+import requests
+import time
+from collections import OrderedDict
+from Crypto.PublicKey import RSA
+from Crypto.Hash import MD5, SHA1, SHA256
+from Crypto.Signature import PKCS1_v1_5 as Signature_PKC
+import base64
+from requests_toolbelt.multipart.encoder import MultipartEncoder
+
+# 接口调用地址,
+# 正式环境:https://oapi.acsign.cn 
+# 沙箱环境域名:https://prev.acsign.cn
+
+#构建post请求
+class HttpUtils(object):
+    # 构建请求头
+    @staticmethod
+    def doPOST(url, reqBodyData, appId, appKey, pathstr=None, file_key=None, file_name=None):
+        accept = "*/*"
+        # 请求的与实体对应的MIME信息
+        contentType = "multipart/form-data; charset=UTF-8"
+            
+        # 对请求的url进行签名
+        accountsApiUrl = url
+        # 毫米时间戳获取
+        t = time.time()
+        # 毫秒级时间戳
+        _timestamp = str(round(t * 1000) + 1000 * 60)
+
+        # 按照阿拉伯字符排序
+        jsonstr = json.dumps(reqBodyData, sort_keys=True,separators=(',',':'), ensure_ascii=False)
+        data = dict()
+        data["appId"] = appId
+        data["timestamp"] = _timestamp
+        data["bizData"] = jsonstr
+
+        #计算md5值
+        m = hashlib.md5()
+        m.update(jsonstr.encode('utf-8'))
+        # 二进制数据字符串值
+        md5_str = m.hexdigest()
+        sign_str = '%s%s%s%s' % (jsonstr, md5_str, appId, _timestamp)
+        # 打印签名值
+        private_keyBytes = base64.b64decode(appKey)
+        rsakey = RSA.importKey(private_keyBytes)
+        signer = Signature_PKC.new(rsakey)
+        # 根据SHA256算法处理签名内容data
+        sha_data = SHA1.new(sign_str.encode("utf-8"))
+        # 私钥进行签名
+        signature = base64.b64encode(signer.sign(sha_data))
+        
+        if(pathstr != None):
+           m = MultipartEncoder(
+	   	       fields={
+                   'appId': appId,
+                   'bizData': jsonstr,
+                   'timestamp': _timestamp,
+          	       'Content-Type': 'application/octet-stream',
+                   'type': 'application/octet-stream',
+                   file_key: (file_name, open(pathstr, 'rb'), 'application/octet-stream')
+               },
+               boundary='-----------------------------' + str(random.randint(1e28, 1e29 - 1)))
+        else:
+            m = MultipartEncoder(
+	   	       fields={
+                   'appId': appId,
+                   'bizData': jsonstr,
+                   'timestamp': _timestamp,
+          	       'Content-Type': 'application/octet-stream',
+                   'type': 'application/octet-stream'
+               },
+               boundary='-----------------------------' + str(random.randint(1e28, 1e29 - 1)))
+
+        #添加文件
+        # 构建请求头
+        headers = {
+            'Content-Type': contentType,
+            'Accept': accept,
+            'sign': signature
+        }
+
+        headers['Content-Type'] = m.content_type
+        r = requests.post(accountsApiUrl, data=m, headers=headers)
+
+        return r.text

+ 49 - 0
py/Sign.py

@@ -0,0 +1,49 @@
+import hashlib
+import json
+import base64
+import sys
+from collections import OrderedDict
+from Crypto.PublicKey import RSA
+from Crypto.Hash import MD5, SHA1, SHA256
+from Crypto.Signature import PKCS1_v1_5 as Signature_PKC
+
+def main():
+    # 从标准输入读取JSON字符串
+    input_json = sys.stdin.read().strip()
+    # 解析JSON数据
+    input_data = json.loads(input_json)
+    # 从输入数据中获取参数
+    req_body_data = input_data["reqBodyData"]
+    timestamp = input_data["timestamp"]
+    appId = input_data["appId"]
+    appKey = input_data["appKey"]
+
+    # 按照阿拉伯字符排序
+    jsonstr = json.dumps(req_body_data, sort_keys=True,separators=(',',':'), ensure_ascii=False)
+    data = dict()
+    data["appId"] = appId
+    data["timestamp"] = timestamp
+    data["bizData"] = jsonstr
+
+    #计算md5值
+    m = hashlib.md5()
+    m.update(jsonstr.encode('utf-8'))
+    # 二进制数据字符串值
+    md5_str = m.hexdigest()
+    sign_str = '%s%s%s%s' % (jsonstr, md5_str, appId, timestamp)
+    # 打印签名值
+    private_keyBytes = base64.b64decode(appKey)
+    rsakey = RSA.importKey(private_keyBytes)
+    signer = Signature_PKC.new(rsakey)
+    # 根据SHA256算法处理签名内容data
+    sha_data = SHA1.new(sign_str.encode("utf-8"))
+    #sha_data = SHA1.new(sign_str)
+    # 私钥进行签名
+    signature = base64.b64encode(signer.sign(sha_data))
+
+    # 输出签名结果
+    print(signature)
+
+
+if __name__ == "__main__":
+    main()

+ 28 - 0
res/pem/asign/prev/290912417/private_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCMxHDrE8t4gCxG
+zXWBg0b1+/h+knxlB++Qd9rHnlSE3I9EoMtGrdGNqYEC667beGiHnG0fu33FGwHE
+8lLljLHZKKHCP7ScX7PFU9WgtmJcGlkHgH9usMpEDrNld0d6N4WKsFPkPSAbXQJr
+KV4l0pJ+CqtG9/mRjmHMM2pH3Xa6SJ+pEJZrIXQ4KP/ktDJ6BLGUEYYyaLfroQr5
+g8moetnopK3XcYt045t5uUorEyyVZdTj2LoyorxahTort4J8Gqcr6DqIGnw5cZ0x
+2czYwf1Yv0zNotctUHPogeXKEDtKTg7AuTmKPpj5g4OuqZ6kb+kMLlQCbhq867De
+Ln1i2ChvAgMBAAECggEAPdzo6Q6qeXsOulD6BqBsU5dYFjwHShlKOZnKvraA22DY
+36awnw+ti83F4kQ0dMQ+X/apaGwr8i8TwHtkXfhOgqqeKDF6DMzsQti8hiQRJvv3
+S1o35qvwQCebxIg+k+IJoLEsNR592TivYuvrscCQW68RpZ8+AS9rIGQqHYog7u1r
+w8SYh6hcTt8d1yMAo31unHKQUDxv0Kcu3HmikoOdqIBJXaJ96yd+VT2YYQiROxnA
+gfKHw5h2UG58iePpB14b7FBRbOiTcYka65sB0ekWRQEZwkjGJq/Y7gcrUASsDfsx
+3ZvUU2mhJtjXfZ5MKRG24pliJMnrTyUbkQSd+xB05QKBgQDCLwL7Ngpmo+8cEKSr
+0SHD7CZld6XrnU+cTwAhqH7W1INmv7bAStdCTn/Lu88fnrjFYiEO/c4uCHUhW5dx
+n8w3ysXSZvy25nTHbs+RgIMnB4oHNKFw/5PnVRAiVJetxDD4J/lcu+nMtuOpUqi0
+5IQrANA7yjnlUzFKeVj4Tl7f2wKBgQC5lEVTPpMxjGRDfgNgpf9AGyV+7kxZty3n
+tjGWyB+ZiVMKbg0GP672UY56wPEp6WR1faDhpHVBSjrAs/+PxYB813WdFsrY+JZk
+UAUalk9JvdNob9fGdDjErklbr1Ehy3fBimbdAPpW/bL9vpv0s5AIMFmTvrLXV36H
+tOLTaXzX/QKBgQC8CdhCOY+S2zbFn5xGwYqH9hsscNOcOMaJ37qJ3WeOU1Y3Xnjy
+9sZCcE+yk1i8DvBwO1k18jMBQrFiLxaVMiAI+vPbjl00JDB7m2OnS6I1XkicN6wK
+B4RLJIUuKWqEG5MsC1GfKlz6HzSQl/nEyfDuz6fV7jtoRcpaiu3L7eBTtwKBgQCq
+oXvEeIqaSj1+GQU9/iLdbJhjRvQIoXah0FF0E/mKEEu8EUgEcNWMQZzBnVmgcaoe
+pS4hijcNmxS+TkIyjSQApZLMp0JHkPmmrjSjJ4UrhlA1fHBnVkFEBgQJOv9cFVq9
+J2ZA2j9TcNz8lh+LNPa/sMUMJZEGkroSSO0t+ZYCeQKBgQCEyaiqD+GX+KB1VZu9
+09DNJVA5boHYUKosiYgBoGrRiHI34xVbe+W3YNRhLWdqrA/+NbizbPGyxWxrJ9fh
+uaYxwpOl3vYVmUDK7HqnSQhwYrp7AvrfRtCs6e8esNH8F7oLY6h6FaiyE8BUhPdK
+zDwYuWJIW38SCv+epOiR22My6Q==
+-----END PRIVATE KEY-----

+ 28 - 0
res/pem/asign/prev/290912417/rsa_private_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCMxHDrE8t4gCxG
+zXWBg0b1+/h+knxlB++Qd9rHnlSE3I9EoMtGrdGNqYEC667beGiHnG0fu33FGwHE
+8lLljLHZKKHCP7ScX7PFU9WgtmJcGlkHgH9usMpEDrNld0d6N4WKsFPkPSAbXQJr
+KV4l0pJ+CqtG9/mRjmHMM2pH3Xa6SJ+pEJZrIXQ4KP/ktDJ6BLGUEYYyaLfroQr5
+g8moetnopK3XcYt045t5uUorEyyVZdTj2LoyorxahTort4J8Gqcr6DqIGnw5cZ0x
+2czYwf1Yv0zNotctUHPogeXKEDtKTg7AuTmKPpj5g4OuqZ6kb+kMLlQCbhq867De
+Ln1i2ChvAgMBAAECggEAPdzo6Q6qeXsOulD6BqBsU5dYFjwHShlKOZnKvraA22DY
+36awnw+ti83F4kQ0dMQ+X/apaGwr8i8TwHtkXfhOgqqeKDF6DMzsQti8hiQRJvv3
+S1o35qvwQCebxIg+k+IJoLEsNR592TivYuvrscCQW68RpZ8+AS9rIGQqHYog7u1r
+w8SYh6hcTt8d1yMAo31unHKQUDxv0Kcu3HmikoOdqIBJXaJ96yd+VT2YYQiROxnA
+gfKHw5h2UG58iePpB14b7FBRbOiTcYka65sB0ekWRQEZwkjGJq/Y7gcrUASsDfsx
+3ZvUU2mhJtjXfZ5MKRG24pliJMnrTyUbkQSd+xB05QKBgQDCLwL7Ngpmo+8cEKSr
+0SHD7CZld6XrnU+cTwAhqH7W1INmv7bAStdCTn/Lu88fnrjFYiEO/c4uCHUhW5dx
+n8w3ysXSZvy25nTHbs+RgIMnB4oHNKFw/5PnVRAiVJetxDD4J/lcu+nMtuOpUqi0
+5IQrANA7yjnlUzFKeVj4Tl7f2wKBgQC5lEVTPpMxjGRDfgNgpf9AGyV+7kxZty3n
+tjGWyB+ZiVMKbg0GP672UY56wPEp6WR1faDhpHVBSjrAs/+PxYB813WdFsrY+JZk
+UAUalk9JvdNob9fGdDjErklbr1Ehy3fBimbdAPpW/bL9vpv0s5AIMFmTvrLXV36H
+tOLTaXzX/QKBgQC8CdhCOY+S2zbFn5xGwYqH9hsscNOcOMaJ37qJ3WeOU1Y3Xnjy
+9sZCcE+yk1i8DvBwO1k18jMBQrFiLxaVMiAI+vPbjl00JDB7m2OnS6I1XkicN6wK
+B4RLJIUuKWqEG5MsC1GfKlz6HzSQl/nEyfDuz6fV7jtoRcpaiu3L7eBTtwKBgQCq
+oXvEeIqaSj1+GQU9/iLdbJhjRvQIoXah0FF0E/mKEEu8EUgEcNWMQZzBnVmgcaoe
+pS4hijcNmxS+TkIyjSQApZLMp0JHkPmmrjSjJ4UrhlA1fHBnVkFEBgQJOv9cFVq9
+J2ZA2j9TcNz8lh+LNPa/sMUMJZEGkroSSO0t+ZYCeQKBgQCEyaiqD+GX+KB1VZu9
+09DNJVA5boHYUKosiYgBoGrRiHI34xVbe+W3YNRhLWdqrA/+NbizbPGyxWxrJ9fh
+uaYxwpOl3vYVmUDK7HqnSQhwYrp7AvrfRtCs6e8esNH8F7oLY6h6FaiyE8BUhPdK
+zDwYuWJIW38SCv+epOiR22My6Q==
+-----END PRIVATE KEY-----

+ 9 - 0
res/pem/asign/prev/290912417/rsa_public_key.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjMRw6xPLeIAsRs11gYNG
+9fv4fpJ8ZQfvkHfax55UhNyPRKDLRq3RjamBAuuu23hoh5xtH7t9xRsBxPJS5Yyx
+2Sihwj+0nF+zxVPVoLZiXBpZB4B/brDKRA6zZXdHejeFirBT5D0gG10CayleJdKS
+fgqrRvf5kY5hzDNqR912ukifqRCWayF0OCj/5LQyegSxlBGGMmi366EK+YPJqHrZ
+6KSt13GLdOObeblKKxMslWXU49i6MqK8WoU6K7eCfBqnK+g6iBp8OXGdMdnM2MH9
+WL9MzaLXLVBz6IHlyhA7Sk4OwLk5ij6Y+YODrqmepG/pDC5UAm4avOuw3i59Ytgo
+bwIDAQAB
+-----END PUBLIC KEY-----

+ 2 - 0
router/account.go

@@ -18,5 +18,7 @@ func InitAccountPrivateRouter(r *gin.RouterGroup) {
 	{
 		accountR.GET("TokenCheck", account.TokenCheck)
 		accountR.GET("Loginout", account.Loginout)
+		accountR.GET("QueryUserESignRecord", account.QueryUserESignRecord)
+		accountR.POST("AddUser", account.AddUser)
 	}
 }

+ 2 - 1
service/account/login.go

@@ -64,7 +64,7 @@ func Login(req request.LoginReq, addr string) (loginaccount *accountModel.Logina
 	loginReq.ClientType = utils.SetPointValue(uint32(req.ClientType))
 	uid, _ := uuid.NewV4()
 	loginReq.GUID = utils.SetPointValue(uid.String())
-	loginReq.Version = utils.SetPointValue("10.0.0.1")
+	loginReq.Version = utils.SetPointValue("1.0.0")
 	uid, _ = uuid.NewV4()
 	loginReq.DeviceID = utils.SetPointValue(uid.String())
 	loginReq.ClientAppID = utils.SetPointValue("MTP20_GO_ACCESS")
@@ -174,6 +174,7 @@ func buildRedisLoginInfo(loginaccount accountModel.Loginaccount, addr string, gr
 		LoginID:   int(loginaccount.LOGINID),
 		Group:     group,
 		SessionID: sessionID,
+		UserID:    int(loginaccount.USERID),
 	})
 	token, err = j.CreateToken(claims)
 	if err != nil {

+ 279 - 0
service/asign/asign.go

@@ -0,0 +1,279 @@
+package asign
+
+import (
+	"bytes"
+	"crypto"
+	"crypto/md5"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"encoding/pem"
+	"errors"
+	"fmt"
+	"io"
+	"mtp20access/global"
+	"net/http"
+	"net/url"
+	"os/exec"
+	"strconv"
+	"strings"
+	"time"
+
+	"go.uber.org/zap"
+)
+
+// 签名规范:
+//
+//	1、表单提交方式:form-data
+//	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)中。
+func getSignature(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
+	timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
+
+	privateKeyBlock, _ := pem.Decode([]byte(privateKeyPEM))
+	// if privateKeyBlock == nil || privateKeyBlock.Type != "RSA PRIVATE KEY" {
+	if privateKeyBlock == nil {
+		err = errors.New("签名失败: Error decoding private key PEM")
+		return
+	}
+
+	// 解析 PKCS#8 格式的私钥
+	privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
+	if err != nil {
+		fmt.Println("Failed to parse private key:", err)
+		return
+	}
+
+	// md5(bizData)
+	m := md5.New()
+	m.Write([]byte(bizData))
+	bdMd5Hx := hex.EncodeToString(m.Sum(nil))
+
+	// 待签内容
+	message := bizData + bdMd5Hx + appId + timestamp
+
+	// 使用私钥进行签名
+	hashed := sha256.Sum256([]byte(message))
+	signature, err := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, hashed[:])
+	if err != nil {
+		fmt.Println("Error signing:", err)
+		return
+	}
+
+	// fmt.Println(signature)
+	signatureBase64 = base64.StdEncoding.EncodeToString(signature)
+
+	return
+}
+
+type pySignReqData struct {
+	ReqBodyData string `json:"reqBodyData"`
+	Timestamp   string `json:"timestamp"`
+	AppId       string `json:"appId"`
+	AppKey      string `json:"appKey"`
+}
+
+func getSignatureByPy(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
+	// md5(bizData)
+	m := md5.New()
+	m.Write([]byte(bizData))
+	bdMd5Hx := hex.EncodeToString(m.Sum(nil))
+	timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
+	// timestamp = "1691559290641"
+	// 待签内容
+	message := bizData + bdMd5Hx + appId + timestamp
+
+	// 构建请求数据结构
+	reqData := pySignReqData{
+		ReqBodyData: message,
+		Timestamp:   timestamp,
+		AppId:       appId,
+		AppKey:      privateKeyPEM,
+	}
+	// 将请求数据转换为JSON字符串
+	reqJSON, err := json.Marshal(reqData)
+	if err != nil {
+		global.M2A_LOG.Error("[getSignatureByPy] 构建请求参数失败", zap.Error(err))
+		return
+	}
+	// 要执行的Python脚本命令
+	pythonScriptPath := "./py/sign.py"
+	// 创建一个命令对象
+	cmd := exec.Command("py", pythonScriptPath)
+	// 设置标准输入为JSON字符串
+	cmd.Stdin = strings.NewReader(string(reqJSON))
+	// 获取标准输出
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		global.M2A_LOG.Error("[getSignatureByPy] 签名失败", zap.Error(err))
+		return
+	}
+	// 获取签名结果
+	signatureBase64 = string(output)
+
+	return
+}
+
+// addPersonalUser 响应参数
+type AddPersonalUserRsp struct {
+	Code int                 `json:"code"` // 响应码,100000表示成功,其他表示异常
+	Msg  string              `json:"msg"`  // 响应信息
+	Data AddPersonalUserData `json:"data"` // 	响应数据
+}
+
+type AddPersonalUserData struct {
+	SealNo string `json:"sealNo"` // 默认印章编号
+}
+
+// https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
+// 添加个人用户(https://{host}/user/addPersonalUser)
+// 错误码	错误描述
+// 100021	用户已存在
+// 100156	手机号码格式错误
+// 100157	邮箱格式错误
+// 100571	参数错误,唯一识别码Account为空
+// 100577	参数错误,{param}长度超过限制:{length}
+// 100579	参数错误,{param}不能为空
+// 100598	参数错误,身份证号码格式不正确
+// 100639	参数错误,名字点号格式不正确
+func AddPersonalUser(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) {
+	apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
+	appId := "290912417"
+	privateKey := `MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAkMD+72J6iAF0ZNV+3t628lsRHfJ80nKZWK5/C7Pg+AZmOIzJlwHsKhRzCvxoxqYHQprhiFzW9l73v9vD9l1JYwIDAQABAkBVijccr01JYdKuY5t9iI8D2NzcnZc1pZMI3NUmzT18Uyg7b9CUvGHlLeg/gdT4QtVd7wIzHYCY4letEcEMh54BAiEAwzNWusj5XiLmty7PI0Hbakx4HtcND1+P0UHLEWqWOuECIQC91zQuL7nStgGzT3HvaeBB5Ouapa39fHRm2nCjHaxwwwIgRR2XdvmUOj23XWMomr5F14SN/7V7fVcD0D8wjNElsmECIDYavV5kb7tj7/wgqkInlKhzC8rZaUsTS0F9BBkY/eptAiAQJ8Saz8YlMIESdHMxANGSog01fECbcZqLFMuNf8SorA==`
+
+	// 请求参数
+	params := make(map[string]interface{})
+	params["account"] = account
+	params["name"] = name
+	params["idCard"] = idCard
+	params["idCardType"] = 1
+	if idCardType != nil {
+		params["idCardType"] = *idCardType
+	}
+	params["mobile"] = mobile
+	// 用户实名认证模式为强制认证时,需要选择认证方法:
+	// 1:身份证二要素认证
+	// 2:运营商三要素认证
+	// 3:银行卡四要素认证
+	params["identifyType"] = 2
+	params["identifyMobile"] = mobile
+	params["isNotice"] = 1
+	bizData, err := json.Marshal(params)
+	if err != nil {
+		global.M2A_LOG.Error("[AddPersonalUser] 构建请求参数失败", zap.Error(err))
+		return
+	}
+	global.M2A_LOG.Info("[AddPersonalUser] 构建请求参数", zap.Any("params", string(bizData)))
+	// 签名
+	sign, timestamp, err := getSignatureByPy(string(bizData), appId, privateKey)
+	sign = strings.Replace(sign, "\r\n", "", -1)
+	sign = strings.Replace(sign, "\r", "", -1)
+	if err != nil {
+		global.M2A_LOG.Error("[AddPersonalUser] 签名失败", zap.Error(err))
+		return
+	}
+	global.M2A_LOG.Info("[AddPersonalUser] 签名", zap.Any("sign", sign))
+	// 构建form-data请求参数
+	formValues := url.Values{}
+	formValues.Set("appId", appId)
+	formValues.Set("timestamp", timestamp)
+	formValues.Set("bizData", string(bizData))
+	// 构建请求
+	req, err := http.NewRequest("POST", apiUrl, bytes.NewReader([]byte(formValues.Encode())))
+	// 设置请求头
+	req.Header.Set("sign", sign)
+	req.Header.Set("timestamp", timestamp)
+	req.Header.Set("Content-Type", "multipart/form-data; charset=UTF-8")
+	req.Header.Set("Accept", "*/*")
+
+	client := &http.Client{}
+	rsp, err := client.Do(req)
+	if err != nil {
+		global.M2A_LOG.Error("[AddPersonalUser] 请求失败", zap.Error(err))
+		return
+	}
+	defer rsp.Body.Close()
+	body, err := io.ReadAll(rsp.Body)
+	if err != nil {
+		global.M2A_LOG.Error("[AddPersonalUser] 获取body失败", zap.Error(err))
+		return
+	}
+	if err = json.Unmarshal(body, &rspData); err != nil {
+		global.M2A_LOG.Error("[AddPersonalUser] 反序列化body失败", zap.Error(err))
+		return
+	}
+
+	return
+}
+
+// https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
+// 添加个人用户(https://{host}/user/addPersonalUser)
+// 错误码	错误描述
+// 100021	用户已存在
+// 100156	手机号码格式错误
+// 100157	邮箱格式错误
+// 100571	参数错误,唯一识别码Account为空
+// 100577	参数错误,{param}长度超过限制:{length}
+// 100579	参数错误,{param}不能为空
+// 100598	参数错误,身份证号码格式不正确
+// 100639	参数错误,名字点号格式不正确
+func AddPersonalUserBy(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) {
+	appId := global.M2A_CONFIG.Asign.AppId
+	privateKey := global.M2A_CONFIG.Asign.PrivateKey
+	apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
+
+	// 构建请求数据结构
+	reqData := make(map[string]interface{})
+	reqData["account"] = account
+	reqData["name"] = name
+	reqData["mobile"] = mobile
+	reqData["idCard"] = idCard
+	reqData["idCardType"] = 1
+	if idCardType != nil {
+		reqData["idCardType"] = *idCardType
+	}
+
+	reqData["api"] = "addPerson"
+	reqData["appId"] = appId
+	reqData["appKey"] = privateKey
+	reqData["apiUrl"] = apiUrl
+
+	// 将请求数据转换为JSON字符串
+	reqJSON, err := json.Marshal(reqData)
+	if err != nil {
+		global.M2A_LOG.Error("[AddPersonalUserBy] 构建请求参数失败", zap.Error(err))
+		return
+	}
+	// 要执行的Python脚本命令
+	pythonScriptPath := "./py/Enter.py"
+	// 创建一个命令对象
+	cmd := exec.Command("py", pythonScriptPath)
+	// 设置标准输入为JSON字符串
+	cmd.Stdin = strings.NewReader(string(reqJSON))
+	// 获取标准输出
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		global.M2A_LOG.Error("[AddPersonalUserBy] 请求失败", zap.Error(err))
+		return
+	}
+
+	// 结果
+	rspBody := string(output)
+	if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
+		global.M2A_LOG.Error("[AddPersonalUserBy] 反序列化body失败", zap.Error(err))
+		return
+	}
+
+	return
+}

+ 0 - 74
service/asign/sign.go

@@ -1,74 +0,0 @@
-package asign
-
-import (
-	"crypto"
-	"crypto/md5"
-	"crypto/rand"
-	"crypto/rsa"
-	"crypto/sha1"
-	"crypto/x509"
-	"encoding/hex"
-	"encoding/pem"
-	"errors"
-	"fmt"
-	"strconv"
-	"time"
-)
-
-// 签名规范:
-//
-//	1、表单提交方式:form-data
-//	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)中。
-func GetSignature(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
-	timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
-	// Parse the privateKeyPEM into an RSA private key
-	privateKeyBlock, _ := pem.Decode([]byte(privateKeyPEM))
-	if privateKeyBlock == nil || privateKeyBlock.Type != "RSA PRIVATE KEY" {
-		err = errors.New("签名失败: Error decoding private key PEM")
-		return
-	}
-	privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
-	if err != nil {
-		return
-	}
-
-	// md5(bizData)
-	m := md5.New()
-	m.Write([]byte(bizData))
-	bdMd5Hx := hex.EncodeToString(m.Sum(nil))
-
-	// Message to be signed
-	message := bizData + bdMd5Hx + appId + timestamp
-
-	// Sign the message using SHA1withRSA
-	signature, err := signMessage(message, privateKey)
-	if err != nil {
-		fmt.Println("Error signing the message:", err)
-		return
-	}
-	fmt.Println(signature)
-
-	return
-}
-
-// signMessage signs the given message using SHA1withRSA
-func signMessage(message string, privateKey *rsa.PrivateKey) ([]byte, error) {
-	hashed := sha1.Sum([]byte(message))
-	return rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, hashed[:])
-}
-
-// verifySignature verifies the given signature for the message using SHA1withRSA
-// func verifySignature(message string, signature []byte, publicKey *rsa.PublicKey) bool {
-// 	hashed := sha1.Sum([]byte(message))
-// 	err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA1, hashed[:], signature)
-// 	return err == nil
-// }

+ 62 - 0
service/sign/sign.go

@@ -0,0 +1,62 @@
+package sign
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"mtp20access/global"
+	"mtp20access/model/account"
+	"mtp20access/model/account/request"
+	"mtp20access/service/asign"
+	"strconv"
+
+	"go.uber.org/zap"
+)
+
+// QueryUserESignRecord 查询用户电子签记录表
+func QueryUserESignRecord(userId int) (rsp []account.Useresignrecord, err error) {
+	rsp = make([]account.Useresignrecord, 0)
+	err = global.M2A_DB.Where("USERID = ?", userId).Find(&rsp)
+
+	return
+}
+
+// AddUser 添加用户
+func AddUser(req request.AddUserReq, userId int) (err error) {
+	// 调用爱签API-添加个人用户(https://{host}/user/addPersonalUser)
+	rsp, err := asign.AddPersonalUserBy(
+		strconv.Itoa(userId),
+		req.Name,
+		req.IdCard,
+		req.Mobile,
+		req.IdCardType,
+	)
+	if err != nil {
+		return
+	}
+	if rsp.Code != 100000 {
+		err = errors.New(strconv.Itoa(rsp.Code))
+		global.M2A_LOG.Error("【AddUser】 接口调用失败", zap.Error(err))
+		return
+	}
+
+	// 更新用户电子签记录-实名认证状态
+	authinfo, err := json.Marshal(req)
+	if err != nil {
+		global.M2A_LOG.Error("【AddUser】 构建AUTHINFO失败", zap.Error(err))
+		return
+	}
+	sql := fmt.Sprintf(`
+		UPDATE useresignrecord
+		SET RECORDSTATUS = 3,
+			UPDATETIME = SYSDATE,
+			AUTHINFO = '%v'
+		WHERE USERID = %v
+	`, string(authinfo), userId)
+	if _, err = global.M2A_DB.Exec(sql); err != nil {
+		global.M2A_LOG.Error("【AddUser】 添加用户电子签记录", zap.Error(err))
+		return
+	}
+
+	return
+}

+ 23 - 5
utils/gin.go

@@ -1,6 +1,8 @@
 package utils
 
 import (
+	"errors"
+	"mtp20access/model/common/request"
 	"mtp20access/model/common/response"
 
 	"github.com/gin-gonic/gin"
@@ -8,15 +10,15 @@ import (
 
 type GinUtils struct {
 	C   *gin.Context
-	err error
+	Err error
 }
 
 // BindFormReq 绑定Form入参的方法
 func (r *GinUtils) BindFormReq(req interface{}) {
-	if r.err != nil {
+	if r.Err != nil {
 		return
 	}
-	if r.err = r.C.ShouldBind(&req); r.err != nil {
+	if r.Err = r.C.ShouldBind(&req); r.Err != nil {
 		response.FailWithMessage("入参不正确", r.C)
 		return
 	}
@@ -24,11 +26,27 @@ func (r *GinUtils) BindFormReq(req interface{}) {
 
 // BindJsonReq 绑定Body入参的方法
 func (r *GinUtils) BindJsonReq(req interface{}) {
-	if r.err != nil {
+	if r.Err != nil {
 		return
 	}
-	if r.err = r.C.ShouldBindJSON(&req); r.err != nil {
+	if r.Err = r.C.ShouldBindJSON(&req); r.Err != nil {
 		response.FailWithMessage("入参不正确", r.C)
 		return
 	}
 }
+
+func (r *GinUtils) GetClaims() (claims *request.CustomClaims) {
+	if r.Err != nil {
+		return
+	}
+	// 获取请求账号信息
+	s, exists := r.C.Get("claims")
+	if !exists {
+		r.Err = errors.New("获取请求账号信息异常")
+		response.FailWithMessage("获取请求账号信息异常", r.C)
+		return
+	}
+	claims = s.(*request.CustomClaims)
+
+	return
+}