asign.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. package asign
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "mtp20access/global"
  6. "os/exec"
  7. "strings"
  8. "go.uber.org/zap"
  9. )
  10. // 签名规范:
  11. //
  12. // 1、表单提交方式:form-data
  13. // 2、请求头部参数
  14. // 参数1:sign(签名值,具体算法参考一下的前面算法)
  15. // 参数2:timestamp(时间戳,13位)
  16. // 3、请求体参数:
  17. // 参数1:appId(appId值,每个接入者唯一一个)
  18. // 参数2:timestamp(时间戳,13位,与上述一致)
  19. // 参数3:bizData(json字符串,举个例子,比方说要传合同编号如:{"contractNo":"0001"})
  20. // 4、签名算法:
  21. // 4.1、将上述3所属的bizData(json字符串),按照阿拉伯字母排序(如:{"ba":1,"ac":2}--->{"ac":2,"ba":1}),
  22. // 4.2、将4.1排序后的字符串,将【bizData+md5(bizData)+ appId + timestatmp】拼接后利用RSA非对称加密算法(SHA1withRSA),计算出最后的签名sign,对其base64编码,放入head的key(sign)中。
  23. // func getSignature(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
  24. // timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
  25. // privateKeyBlock, _ := pem.Decode([]byte(privateKeyPEM))
  26. // // if privateKeyBlock == nil || privateKeyBlock.Type != "RSA PRIVATE KEY" {
  27. // if privateKeyBlock == nil {
  28. // err = errors.New("签名失败: Error decoding private key PEM")
  29. // return
  30. // }
  31. // // 解析 PKCS#8 格式的私钥
  32. // privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
  33. // if err != nil {
  34. // fmt.Println("Failed to parse private key:", err)
  35. // return
  36. // }
  37. // // md5(bizData)
  38. // m := md5.New()
  39. // m.Write([]byte(bizData))
  40. // bdMd5Hx := hex.EncodeToString(m.Sum(nil))
  41. // // 待签内容
  42. // message := bizData + bdMd5Hx + appId + timestamp
  43. // // 使用私钥进行签名
  44. // hashed := sha256.Sum256([]byte(message))
  45. // signature, err := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, hashed[:])
  46. // if err != nil {
  47. // fmt.Println("Error signing:", err)
  48. // return
  49. // }
  50. // // fmt.Println(signature)
  51. // signatureBase64 = base64.StdEncoding.EncodeToString(signature)
  52. // return
  53. // }
  54. // type pySignReqData struct {
  55. // ReqBodyData string `json:"reqBodyData"`
  56. // Timestamp string `json:"timestamp"`
  57. // AppId string `json:"appId"`
  58. // AppKey string `json:"appKey"`
  59. // }
  60. // func getSignatureByPy(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
  61. // // md5(bizData)
  62. // m := md5.New()
  63. // m.Write([]byte(bizData))
  64. // bdMd5Hx := hex.EncodeToString(m.Sum(nil))
  65. // timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
  66. // // timestamp = "1691559290641"
  67. // // 待签内容
  68. // message := bizData + bdMd5Hx + appId + timestamp
  69. // // 构建请求数据结构
  70. // reqData := pySignReqData{
  71. // ReqBodyData: message,
  72. // Timestamp: timestamp,
  73. // AppId: appId,
  74. // AppKey: privateKeyPEM,
  75. // }
  76. // // 将请求数据转换为JSON字符串
  77. // reqJSON, err := json.Marshal(reqData)
  78. // if err != nil {
  79. // global.M2A_LOG.Error("[getSignatureByPy] 构建请求参数失败", zap.Error(err))
  80. // return
  81. // }
  82. // // 要执行的Python脚本命令
  83. // pythonScriptPath := "./py/sign.py"
  84. // // 创建一个命令对象
  85. // cmd := exec.Command(global.M2A_CONFIG.Asign.Py, pythonScriptPath)
  86. // // 设置标准输入为JSON字符串
  87. // cmd.Stdin = strings.NewReader(string(reqJSON))
  88. // // 获取标准输出
  89. // output, err := cmd.CombinedOutput()
  90. // if err != nil {
  91. // global.M2A_LOG.Error("[getSignatureByPy] 签名失败", zap.Error(err))
  92. // return
  93. // }
  94. // // 获取签名结果
  95. // signatureBase64 = string(output)
  96. // return
  97. // }
  98. type AsignData interface {
  99. AddPersonalUserData | CreateContractData | AddSignerData | DownloadContractData | ContractStatusData | WillFaceData
  100. }
  101. // AsignRsp 爱签响应参数
  102. type AsignRsp[T AsignData] struct {
  103. Code int `json:"code"` // 响应码,100000表示成功,其他表示异常
  104. Msg string `json:"msg"` // 响应信息
  105. Data T `json:"data"` // 响应数据
  106. }
  107. // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
  108. // 添加个人用户(https://{host}/user/addPersonalUser)
  109. // 错误码 错误描述
  110. // 100021 用户已存在
  111. // 100156 手机号码格式错误
  112. // 100157 邮箱格式错误
  113. // 100571 参数错误,唯一识别码Account为空
  114. // 100577 参数错误,{param}长度超过限制:{length}
  115. // 100579 参数错误,{param}不能为空
  116. // 100598 参数错误,身份证号码格式不正确
  117. // 100639 参数错误,名字点号格式不正确
  118. // func AddPersonalUser(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) {
  119. // apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
  120. // appId := "290912417"
  121. // privateKey := `MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAkMD+72J6iAF0ZNV+3t628lsRHfJ80nKZWK5/C7Pg+AZmOIzJlwHsKhRzCvxoxqYHQprhiFzW9l73v9vD9l1JYwIDAQABAkBVijccr01JYdKuY5t9iI8D2NzcnZc1pZMI3NUmzT18Uyg7b9CUvGHlLeg/gdT4QtVd7wIzHYCY4letEcEMh54BAiEAwzNWusj5XiLmty7PI0Hbakx4HtcND1+P0UHLEWqWOuECIQC91zQuL7nStgGzT3HvaeBB5Ouapa39fHRm2nCjHaxwwwIgRR2XdvmUOj23XWMomr5F14SN/7V7fVcD0D8wjNElsmECIDYavV5kb7tj7/wgqkInlKhzC8rZaUsTS0F9BBkY/eptAiAQJ8Saz8YlMIESdHMxANGSog01fECbcZqLFMuNf8SorA==`
  122. // // 请求参数
  123. // params := make(map[string]interface{})
  124. // params["account"] = account
  125. // params["name"] = name
  126. // params["idCard"] = idCard
  127. // params["idCardType"] = 1
  128. // if idCardType != nil {
  129. // params["idCardType"] = *idCardType
  130. // }
  131. // params["mobile"] = mobile
  132. // // 用户实名认证模式为强制认证时,需要选择认证方法:
  133. // // 1:身份证二要素认证
  134. // // 2:运营商三要素认证
  135. // // 3:银行卡四要素认证
  136. // params["identifyType"] = 2
  137. // params["identifyMobile"] = mobile
  138. // params["isNotice"] = 1
  139. // bizData, err := json.Marshal(params)
  140. // if err != nil {
  141. // global.M2A_LOG.Error("[AddPersonalUser] 构建请求参数失败", zap.Error(err))
  142. // return
  143. // }
  144. // global.M2A_LOG.Info("[AddPersonalUser] 构建请求参数", zap.Any("params", string(bizData)))
  145. // // 签名
  146. // sign, timestamp, err := getSignatureByPy(string(bizData), appId, privateKey)
  147. // sign = strings.Replace(sign, "\r\n", "", -1)
  148. // sign = strings.Replace(sign, "\r", "", -1)
  149. // if err != nil {
  150. // global.M2A_LOG.Error("[AddPersonalUser] 签名失败", zap.Error(err))
  151. // return
  152. // }
  153. // global.M2A_LOG.Info("[AddPersonalUser] 签名", zap.Any("sign", sign))
  154. // // 构建form-data请求参数
  155. // formValues := url.Values{}
  156. // formValues.Set("appId", appId)
  157. // formValues.Set("timestamp", timestamp)
  158. // formValues.Set("bizData", string(bizData))
  159. // // 构建请求
  160. // req, err := http.NewRequest("POST", apiUrl, bytes.NewReader([]byte(formValues.Encode())))
  161. // // 设置请求头
  162. // req.Header.Set("sign", sign)
  163. // req.Header.Set("timestamp", timestamp)
  164. // req.Header.Set("Content-Type", "multipart/form-data; charset=UTF-8")
  165. // req.Header.Set("Accept", "*/*")
  166. // client := &http.Client{}
  167. // rsp, err := client.Do(req)
  168. // if err != nil {
  169. // global.M2A_LOG.Error("[AddPersonalUser] 请求失败", zap.Error(err))
  170. // return
  171. // }
  172. // defer rsp.Body.Close()
  173. // body, err := io.ReadAll(rsp.Body)
  174. // if err != nil {
  175. // global.M2A_LOG.Error("[AddPersonalUser] 获取body失败", zap.Error(err))
  176. // return
  177. // }
  178. // if err = json.Unmarshal(body, &rspData); err != nil {
  179. // global.M2A_LOG.Error("[AddPersonalUser] 反序列化body失败", zap.Error(err))
  180. // return
  181. // }
  182. // return
  183. // }
  184. type AddPersonalUserData struct {
  185. SealNo string `json:"sealNo"` // 默认印章编号
  186. }
  187. // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
  188. // 添加个人用户(https://{host}/user/addPersonalUser)
  189. // 错误码 错误描述
  190. // 100020 认证记录尚未完成/失败
  191. // 100021 用户已存在
  192. // 100571 参数错误,唯一识别码Account为空
  193. // 100577 参数错误,{param}长度超过限制:{length}
  194. // 100579 参数错误,{param}不能为空
  195. // 100580 参数错误,{param1}、{param2}不能同时为空
  196. // 100598 参数错误,身份证号码格式不正确
  197. // 100721 参数错误,根据序列号未查询到对应认证记录
  198. // 100722 参数错误,该实名记录已核销,请重新实名认证
  199. // 100726 该条实名记录为核验记录,无法用于添加用户
  200. // 100727 实名认证类型和添加用户类型不匹配
  201. func AddPersonalUserBy(account, name, idCard, mobile string, idCardType *int) (rspData AsignRsp[AddPersonalUserData], err error) {
  202. apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
  203. // 构建请求数据结构
  204. reqData := make(map[string]interface{})
  205. reqData["account"] = account
  206. reqData["name"] = name
  207. reqData["mobile"] = mobile
  208. reqData["idCard"] = idCard
  209. reqData["idCardType"] = 1
  210. if idCardType != nil {
  211. reqData["idCardType"] = *idCardType
  212. }
  213. reqData["api"] = "addPerson"
  214. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  215. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  216. reqData["apiUrl"] = apiUrl
  217. // 将请求数据转换为JSON字符串
  218. reqJSON, err := json.Marshal(reqData)
  219. if err != nil {
  220. global.M2A_LOG.Error("[AddPersonalUserBy] 构建请求参数失败", zap.Error(err))
  221. return
  222. }
  223. // 要执行的Python脚本命令
  224. pythonScriptPath := "./py/Enter.py"
  225. // 创建一个命令对象
  226. cmd := exec.Command(global.M2A_CONFIG.Asign.Py, pythonScriptPath)
  227. // 设置标准输入为JSON字符串
  228. cmd.Stdin = strings.NewReader(string(reqJSON))
  229. // 创建一个字节缓冲区来捕获命令的输出
  230. var stdoutBuf, stderrBuf bytes.Buffer
  231. cmd.Stdout = &stdoutBuf
  232. cmd.Stderr = &stderrBuf
  233. // 执行命令
  234. err = cmd.Run()
  235. // 输出标准输出和标准错误
  236. global.M2A_LOG.Info("AddPersonalUserBy 标准输出:", zap.Any("stdoutBuf", stdoutBuf.String()))
  237. global.M2A_LOG.Info("AddPersonalUserBy 标准错误:", zap.Any("stderrBuf", stderrBuf.String()))
  238. if err != nil {
  239. global.M2A_LOG.Error("[AddPersonalUserBy] 请求失败", zap.Error(err))
  240. return
  241. }
  242. // 结果
  243. rspBody := stdoutBuf.String()
  244. // 正确返回
  245. // {"code":100000,"msg":"�ɹ�","data":{"id":null,"userId":null,"partnerId":null,"type":null,"sealType":null,"sealTypeOfUser":null,"sealName":null,"sealCode":null,"startTime":null,"endTime":null,"sealNo":"9a82d2da479b4430b1da6f7f7cd96aee","sealPassword":null,"sealFont":null,"version":null,"caType":null,"certNo":null,"path":null,"isDefault":null,"status":null,"color":null,"fontSize":null,"imageWidth":null,"imageHeight":null,"imageShape":null,"signTimes":null,"logicDel":null,"createTime":null,"modifyTime":null}}
  246. // 错误返回
  247. // {"code":100021,"msg":"�û��Ѵ��ڣ�����֤�����ظ�","data":{"id":null,"partnerId":null,"account":"100000007","companyAccount":null,"name":null,"companyName":null,"userType":null,"mobile":null,"bankCard":null,"email":null,"idCard":"360428200007287603","idCardType":null,"creditCode":null,"contactName":null,"contactPhone":null,"contactIdCard":null,"isNotice":null,"isAutoSign":null,"signPwd":null,"identifyStatus":null,"identifyMobile":null,"identifyType":null,"userStatus":null,"needSeal":null,"remark":null,"createTime":null,"modifyTime":null,"sealType":null,"authType":null,"serialNo":null}}
  248. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  249. global.M2A_LOG.Error("[AddPersonalUserBy] 反序列化body失败", zap.Error(err))
  250. return
  251. }
  252. return
  253. }
  254. type CreateContractData struct {
  255. PreviewUrl string `json:"previewUrl"` // 合同预览链接
  256. ContractFiles []interface{} `json:"contractFiles"` // 合同文件信息(文件名称,附件编号,页数)
  257. }
  258. /*
  259. *
  260. CreateContract 上传待签署文件
  261. contractNo 合同ID,合同唯一编号
  262. contractName 合同名称
  263. templateNo 合同模板编号
  264. *
  265. */
  266. func CreateContract(contractNo, contractName, templateNo string) (rspData AsignRsp[CreateContractData], err error) {
  267. apiUrl := global.M2A_CONFIG.Asign.URL + "contract/createContract"
  268. // 构建请求数据结构
  269. reqData := make(map[string]interface{})
  270. reqData["contractNo"] = contractNo
  271. reqData["contractName"] = contractName
  272. reqData["validityTime"] = 30
  273. reqData["signOrder"] = 1 // 1:无序签约(默认
  274. reqData["templates"] = []map[string]string{
  275. {"templateNo": templateNo}} // 合同模板编号 - 目前只支持一份合同签一份协议
  276. reqData["notifyUrl"] = global.M2A_CONFIG.Asign.NotifyUrl
  277. reqData["api"] = "createContract"
  278. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  279. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  280. reqData["apiUrl"] = apiUrl
  281. // 将请求数据转换为JSON字符串
  282. reqJSON, err := json.Marshal(reqData)
  283. if err != nil {
  284. global.M2A_LOG.Error("[CreateContract] 构建请求参数失败", zap.Error(err))
  285. return
  286. }
  287. // 要执行的Python脚本命令
  288. pythonScriptPath := "./py/Enter.py"
  289. // 创建一个命令对象
  290. cmd := exec.Command(global.M2A_CONFIG.Asign.Py, pythonScriptPath)
  291. // 设置标准输入为JSON字符串
  292. cmd.Stdin = strings.NewReader(string(reqJSON))
  293. // 创建一个字节缓冲区来捕获命令的输出
  294. var stdoutBuf, stderrBuf bytes.Buffer
  295. cmd.Stdout = &stdoutBuf
  296. cmd.Stderr = &stderrBuf
  297. // 执行命令
  298. err = cmd.Run()
  299. // 输出标准输出和标准错误
  300. // fmt.Println("标准输入:", string(reqJSON))
  301. global.M2A_LOG.Info("CreateContract 标准输出:", zap.Any("stdoutBuf", stdoutBuf.String()))
  302. global.M2A_LOG.Info("CreateContract 标准错误:", zap.Any("stderrBuf", stderrBuf.String()))
  303. if err != nil {
  304. global.M2A_LOG.Error("[CreateContract] 请求失败", zap.Error(err))
  305. return
  306. }
  307. // 结果
  308. rspBody := stdoutBuf.String()
  309. // 正确返回:CreateContract 标准输出: {"code":100000,"msg":" ɹ ","data":{"pageSizeMap":{},"signUser":[],"previewUrl":"https://pre.asign.cn/m/#/mobile/previewApiContract/QhUqHj669cOmXIPrJ7eOhSfWh6_oDGlmQ53lOD5OVAvJRvTVpenYICiMWnvbCi4swvNWUIVuO1zPsTvIvHPGuE1fTZ-AQyCeUyWWu7f56Jg?expired=10800000","contractFiles":[]}}
  310. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  311. global.M2A_LOG.Error("[CreateContract] 反序列化body失败", zap.Error(err))
  312. return
  313. }
  314. return
  315. }
  316. // 合同用户信息
  317. type SignUserData struct {
  318. Account string `json:"account"` // 用户唯一识别码
  319. SignUrl string `json:"signUrl"` // 合同签署链接
  320. PwdSignUrl string `json:"pwdSignUrl"` // 密码签署链接
  321. SignOrder int `json:"signOrder"` // 顺序签约的序号
  322. Name string `json:"name"` // 用户姓名
  323. IdCard string `json:"idCard"` // 用户身份证
  324. }
  325. // 添加签署方响应数据
  326. type AddSignerData struct {
  327. ContractNo string `json:"contractNo"` // 合同编号
  328. ContractName string `json:"contractName"` // 合同名称
  329. ValidityTime string `json:"validityTime"` // 合同有效期
  330. PreviewUrl string `json:"previewUrl"` // 合同预览链接
  331. SignUser []SignUserData `json:"signUser"` // 合同用户信息
  332. }
  333. /*
  334. *
  335. AddSigner 添加签署方
  336. contractNo 合同ID,合同唯一编号
  337. account 用户唯一识别码(UserID)
  338. *
  339. */
  340. func AddSigner(contractNo, account string) (rspData AsignRsp[AddSignerData], err error) {
  341. apiUrl := global.M2A_CONFIG.Asign.URL + "contract/addSigner"
  342. // 构建请求数据结构
  343. reqData := make(map[string]interface{})
  344. reqData["contractNo"] = contractNo
  345. reqData["account"] = account
  346. reqData["signType"] = 3
  347. // 签署方式指定:(从以下分类中指定一种)
  348. // 1:短信验证码签约(支持企业和个人)
  349. // 2:签约密码签约(支持企业和个人)
  350. // 3:人脸识别签约(支持企业和个人)
  351. // 4:手写签名(不推荐,仅限个人)
  352. // 6:手写识别签名+短信签约(仅限个人)
  353. // 7:手写签名+短信签约(仅限个人)
  354. // 8:手写签名+人脸识别签约(仅限个人)
  355. // 9:手写识别签名+人脸识别签约(仅限个人)
  356. reqData["validateType"] = 4
  357. reqData["signStrategyList"] = []map[string]interface{}{
  358. {"attachNo": 1, "locationMode": 4, "signKey": "key_sign_name"},
  359. {"attachNo": 1, "locationMode": 4, "signKey": "key_sign_date"},
  360. }
  361. reqData["api"] = "addSigner"
  362. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  363. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  364. reqData["apiUrl"] = apiUrl
  365. // 将请求数据转换为JSON字符串
  366. reqJSON, err := json.Marshal(reqData)
  367. if err != nil {
  368. global.M2A_LOG.Error("[AddSigner] 构建请求参数失败", zap.Error(err))
  369. return
  370. }
  371. // 要执行的Python脚本命令
  372. pythonScriptPath := "./py/Enter.py"
  373. // 创建一个命令对象
  374. cmd := exec.Command(global.M2A_CONFIG.Asign.Py, pythonScriptPath)
  375. // 设置标准输入为JSON字符串
  376. cmd.Stdin = strings.NewReader(string(reqJSON))
  377. // 创建一个字节缓冲区来捕获命令的输出
  378. var stdoutBuf, stderrBuf bytes.Buffer
  379. cmd.Stdout = &stdoutBuf
  380. cmd.Stderr = &stderrBuf
  381. // 执行命令
  382. err = cmd.Run()
  383. // 输出标准输出和标准错误
  384. global.M2A_LOG.Info("AddSigner 标准输出:", zap.Any("stdoutBuf", stdoutBuf.String()))
  385. global.M2A_LOG.Info("AddSigner 标准错误:", zap.Any("stderrBuf", stderrBuf.String()))
  386. if err != nil {
  387. global.M2A_LOG.Error("[AddSigner] 请求失败", zap.Error(err))
  388. return
  389. }
  390. // 结果
  391. // 正确返回:AddSigner 标准输出: {"code":100000,"msg":"�ɹ�","data":{"contractNo":"100000008_20230815145538_10","pageSizeMap":{},"contractName":"���ݲ�Ҷ������ʾ�飨10.31���հ棩","signOrder":1,"validityTime":"2023-09-14","signUser":[{"account":"100000008","signUrl":"https://h5.asign.cn/web/short/eE7zmq515512","signOrder":1,"name":"����̼��","idCard":"110101200007285605","mobile":"15914012153"}],"previewUrl":"https://pre.asign.cn/m/#/mobile/previewApiContract/QhUqHj669cOmXIPrJ7eOhSfWh6_oDGlmQ53lOD5OVAvJRvTVpenYICiMWnvbCi4sS_knh8JQ124Lwf_uxqXkF0-n1uuC18qLy5B8TfAF88g?expired=10800000","contractFiles":[]}}
  392. // 错误返回:AddSigner 标准输出: {"code":100617,"msg":"ģ IJ [key_sign_name] ","data":null}
  393. rspBody := stdoutBuf.String()
  394. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  395. global.M2A_LOG.Error("[AddSigner] 反序列化body失败", zap.Error(err))
  396. return
  397. }
  398. return
  399. }
  400. // 下载合同响应数据
  401. type DownloadContractData struct {
  402. FileName string `json:"fileName"` // 文件名
  403. MD5 string `json:"md5"` // 文件md5值
  404. FileType int `json:"fileType"` // 文件类型 0:PDF 1:ZIP
  405. Size int `json:"size"` // 文件大小
  406. Data string `json:"data"` // Base64字符串
  407. }
  408. /*
  409. DownloadContract 下载合同
  410. contractNo 合同ID,合同唯一编号
  411. */
  412. func DownloadContract(contractNo string) (rspData AsignRsp[DownloadContractData], err error) {
  413. apiUrl := global.M2A_CONFIG.Asign.URL + "contract/downloadContract"
  414. // 构建请求数据结构
  415. reqData := make(map[string]interface{})
  416. reqData["contractNo"] = contractNo
  417. reqData["api"] = "downloadContract"
  418. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  419. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  420. reqData["apiUrl"] = apiUrl
  421. // 将请求数据转换为JSON字符串
  422. reqJSON, err := json.Marshal(reqData)
  423. if err != nil {
  424. global.M2A_LOG.Error("[DownloadContract] 构建请求参数失败", zap.Error(err))
  425. return
  426. }
  427. // 要执行的Python脚本命令
  428. pythonScriptPath := "./py/Enter.py"
  429. // 创建一个命令对象
  430. cmd := exec.Command(global.M2A_CONFIG.Asign.Py, pythonScriptPath)
  431. // 设置标准输入为JSON字符串
  432. cmd.Stdin = strings.NewReader(string(reqJSON))
  433. // 创建一个字节缓冲区来捕获命令的输出
  434. var stdoutBuf, stderrBuf bytes.Buffer
  435. cmd.Stdout = &stdoutBuf
  436. cmd.Stderr = &stderrBuf
  437. // 执行命令
  438. err = cmd.Run()
  439. // 输出标准输出和标准错误
  440. global.M2A_LOG.Info("DownloadContract 标准输出:", zap.Any("stdoutBuf", stdoutBuf.String()))
  441. global.M2A_LOG.Info("DownloadContract 标准错误:", zap.Any("stderrBuf", stderrBuf.String()))
  442. if err != nil {
  443. global.M2A_LOG.Error("[DownloadContract] 请求失败", zap.Error(err))
  444. return
  445. }
  446. // 结果
  447. rspBody := stdoutBuf.String()
  448. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  449. global.M2A_LOG.Error("[DownloadContract] 反序列化body失败", zap.Error(err))
  450. return
  451. }
  452. return
  453. }
  454. // 查询合同状态响应数据
  455. type ContractStatusData struct {
  456. ContractNo string `json:"contractNo"` // 合同唯一编号
  457. ContractName string `json:"contractName"` // 合同名称
  458. Status int `json:"status"` // 合同状态:0:等待签约 1:签约中 2:已签约 3:过期 4:拒签 6:作废 -2:状态异常
  459. }
  460. /*
  461. ContractStatus 查询合同状态
  462. contractNo 合同ID,合同唯一编号
  463. */
  464. func ContractStatus(contractNo string) (rspData AsignRsp[ContractStatusData], err error) {
  465. apiUrl := global.M2A_CONFIG.Asign.URL + "contract/status"
  466. // 构建请求数据结构
  467. reqData := make(map[string]interface{})
  468. reqData["contractNo"] = contractNo
  469. reqData["api"] = "contractStatus"
  470. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  471. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  472. reqData["apiUrl"] = apiUrl
  473. // 将请求数据转换为JSON字符串
  474. reqJSON, err := json.Marshal(reqData)
  475. if err != nil {
  476. global.M2A_LOG.Error("[ContractStatus] 构建请求参数失败", zap.Error(err))
  477. return
  478. }
  479. // 要执行的Python脚本命令
  480. pythonScriptPath := "./py/Enter.py"
  481. // 创建一个命令对象
  482. cmd := exec.Command(global.M2A_CONFIG.Asign.Py, pythonScriptPath)
  483. // 设置标准输入为JSON字符串
  484. cmd.Stdin = strings.NewReader(string(reqJSON))
  485. // 创建一个字节缓冲区来捕获命令的输出
  486. var stdoutBuf, stderrBuf bytes.Buffer
  487. cmd.Stdout = &stdoutBuf
  488. cmd.Stderr = &stderrBuf
  489. // 执行命令
  490. err = cmd.Run()
  491. // 输出标准输出和标准错误
  492. global.M2A_LOG.Info("ContractStatus 标准输出:", zap.Any("stdoutBuf", stdoutBuf.String()))
  493. global.M2A_LOG.Info("ContractStatus 标准错误:", zap.Any("stderrBuf", stderrBuf.String()))
  494. if err != nil {
  495. global.M2A_LOG.Error("[ContractStatus] 请求失败", zap.Error(err))
  496. return
  497. }
  498. // 结果
  499. rspBody := stdoutBuf.String()
  500. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  501. global.M2A_LOG.Error("[ContractStatus] 反序列化body失败", zap.Error(err))
  502. return
  503. }
  504. return
  505. }
  506. // 个人意愿核身认证响应数据
  507. type WillFaceData struct {
  508. Result int `json:"result"` // 认证结果:0.暂无结果/认证中
  509. SerialNo string `json:"serialNo"` // 认证流水号
  510. Type string `json:"type"` // 认证类型
  511. FaceUrl string `json:"faceUrl"` // 意愿核身链接
  512. }
  513. /*
  514. WillFace 个人意愿核身认证
  515. */
  516. func WillFace(realName, idCardNo, question, answer, recordId string) (rspData AsignRsp[WillFaceData], err error) {
  517. apiUrl := global.M2A_CONFIG.Asign.URL + "auth/person/willFace"
  518. // 构建请求数据结构
  519. reqData := make(map[string]interface{})
  520. reqData["realName"] = realName
  521. reqData["idCardNo"] = idCardNo
  522. reqData["question"] = question
  523. reqData["answer"] = answer
  524. reqData["redirectUrl"] = global.M2A_CONFIG.Asign.WillFaceRedirectUrl + "?recordId=" + recordId
  525. reqData["api"] = "willFace"
  526. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  527. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  528. reqData["apiUrl"] = apiUrl
  529. // 将请求数据转换为JSON字符串
  530. reqJSON, err := json.Marshal(reqData)
  531. if err != nil {
  532. global.M2A_LOG.Error("[WillFace] 构建请求参数失败", zap.Error(err))
  533. return
  534. }
  535. // 要执行的Python脚本命令
  536. pythonScriptPath := "./py/Enter.py"
  537. // 创建一个命令对象
  538. cmd := exec.Command(global.M2A_CONFIG.Asign.Py, pythonScriptPath)
  539. // 设置标准输入为JSON字符串
  540. cmd.Stdin = strings.NewReader(string(reqJSON))
  541. // 创建一个字节缓冲区来捕获命令的输出
  542. var stdoutBuf, stderrBuf bytes.Buffer
  543. cmd.Stdout = &stdoutBuf
  544. cmd.Stderr = &stderrBuf
  545. // 执行命令
  546. err = cmd.Run()
  547. // 输出标准输出和标准错误
  548. global.M2A_LOG.Info("WillFace 标准输出:", zap.Any("stdoutBuf", stdoutBuf.String()))
  549. global.M2A_LOG.Info("WillFace 标准错误:", zap.Any("stderrBuf", stderrBuf.String()))
  550. if err != nil {
  551. global.M2A_LOG.Error("[WillFace] 请求失败", zap.Error(err))
  552. return
  553. }
  554. // 结果
  555. rspBody := stdoutBuf.String()
  556. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  557. global.M2A_LOG.Error("[WillFace] 反序列化body失败", zap.Error(err))
  558. return
  559. }
  560. return
  561. }