asign.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. package asign
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "mtp20access/global"
  7. "os/exec"
  8. "strings"
  9. "go.uber.org/zap"
  10. )
  11. // 签名规范:
  12. //
  13. // 1、表单提交方式:form-data
  14. // 2、请求头部参数
  15. // 参数1:sign(签名值,具体算法参考一下的前面算法)
  16. // 参数2:timestamp(时间戳,13位)
  17. // 3、请求体参数:
  18. // 参数1:appId(appId值,每个接入者唯一一个)
  19. // 参数2:timestamp(时间戳,13位,与上述一致)
  20. // 参数3:bizData(json字符串,举个例子,比方说要传合同编号如:{"contractNo":"0001"})
  21. // 4、签名算法:
  22. // 4.1、将上述3所属的bizData(json字符串),按照阿拉伯字母排序(如:{"ba":1,"ac":2}--->{"ac":2,"ba":1}),
  23. // 4.2、将4.1排序后的字符串,将【bizData+md5(bizData)+ appId + timestatmp】拼接后利用RSA非对称加密算法(SHA1withRSA),计算出最后的签名sign,对其base64编码,放入head的key(sign)中。
  24. // func getSignature(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
  25. // timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
  26. // privateKeyBlock, _ := pem.Decode([]byte(privateKeyPEM))
  27. // // if privateKeyBlock == nil || privateKeyBlock.Type != "RSA PRIVATE KEY" {
  28. // if privateKeyBlock == nil {
  29. // err = errors.New("签名失败: Error decoding private key PEM")
  30. // return
  31. // }
  32. // // 解析 PKCS#8 格式的私钥
  33. // privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
  34. // if err != nil {
  35. // fmt.Println("Failed to parse private key:", err)
  36. // return
  37. // }
  38. // // md5(bizData)
  39. // m := md5.New()
  40. // m.Write([]byte(bizData))
  41. // bdMd5Hx := hex.EncodeToString(m.Sum(nil))
  42. // // 待签内容
  43. // message := bizData + bdMd5Hx + appId + timestamp
  44. // // 使用私钥进行签名
  45. // hashed := sha256.Sum256([]byte(message))
  46. // signature, err := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, hashed[:])
  47. // if err != nil {
  48. // fmt.Println("Error signing:", err)
  49. // return
  50. // }
  51. // // fmt.Println(signature)
  52. // signatureBase64 = base64.StdEncoding.EncodeToString(signature)
  53. // return
  54. // }
  55. // type pySignReqData struct {
  56. // ReqBodyData string `json:"reqBodyData"`
  57. // Timestamp string `json:"timestamp"`
  58. // AppId string `json:"appId"`
  59. // AppKey string `json:"appKey"`
  60. // }
  61. // func getSignatureByPy(bizData string, appId string, privateKeyPEM string) (signatureBase64 string, timestamp string, err error) {
  62. // // md5(bizData)
  63. // m := md5.New()
  64. // m.Write([]byte(bizData))
  65. // bdMd5Hx := hex.EncodeToString(m.Sum(nil))
  66. // timestamp = strconv.FormatInt(time.Now().UnixMilli(), 10)
  67. // // timestamp = "1691559290641"
  68. // // 待签内容
  69. // message := bizData + bdMd5Hx + appId + timestamp
  70. // // 构建请求数据结构
  71. // reqData := pySignReqData{
  72. // ReqBodyData: message,
  73. // Timestamp: timestamp,
  74. // AppId: appId,
  75. // AppKey: privateKeyPEM,
  76. // }
  77. // // 将请求数据转换为JSON字符串
  78. // reqJSON, err := json.Marshal(reqData)
  79. // if err != nil {
  80. // global.M2A_LOG.Error("[getSignatureByPy] 构建请求参数失败", zap.Error(err))
  81. // return
  82. // }
  83. // // 要执行的Python脚本命令
  84. // pythonScriptPath := "./py/sign.py"
  85. // // 创建一个命令对象
  86. // cmd := exec.Command("py", pythonScriptPath)
  87. // // 设置标准输入为JSON字符串
  88. // cmd.Stdin = strings.NewReader(string(reqJSON))
  89. // // 获取标准输出
  90. // output, err := cmd.CombinedOutput()
  91. // if err != nil {
  92. // global.M2A_LOG.Error("[getSignatureByPy] 签名失败", zap.Error(err))
  93. // return
  94. // }
  95. // // 获取签名结果
  96. // signatureBase64 = string(output)
  97. // return
  98. // }
  99. type AsignData interface {
  100. AddPersonalUserData | CreateContractData | AddSignerData
  101. }
  102. // AsignRsp 爱签响应参数
  103. type AsignRsp[T AsignData] struct {
  104. Code int `json:"code"` // 响应码,100000表示成功,其他表示异常
  105. Msg string `json:"msg"` // 响应信息
  106. Data T `json:"data"` // 响应数据
  107. }
  108. // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
  109. // 添加个人用户(https://{host}/user/addPersonalUser)
  110. // 错误码 错误描述
  111. // 100021 用户已存在
  112. // 100156 手机号码格式错误
  113. // 100157 邮箱格式错误
  114. // 100571 参数错误,唯一识别码Account为空
  115. // 100577 参数错误,{param}长度超过限制:{length}
  116. // 100579 参数错误,{param}不能为空
  117. // 100598 参数错误,身份证号码格式不正确
  118. // 100639 参数错误,名字点号格式不正确
  119. // func AddPersonalUser(account, name, idCard, mobile string, idCardType *int) (rspData AddPersonalUserRsp, err error) {
  120. // apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
  121. // appId := "290912417"
  122. // privateKey := `MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAkMD+72J6iAF0ZNV+3t628lsRHfJ80nKZWK5/C7Pg+AZmOIzJlwHsKhRzCvxoxqYHQprhiFzW9l73v9vD9l1JYwIDAQABAkBVijccr01JYdKuY5t9iI8D2NzcnZc1pZMI3NUmzT18Uyg7b9CUvGHlLeg/gdT4QtVd7wIzHYCY4letEcEMh54BAiEAwzNWusj5XiLmty7PI0Hbakx4HtcND1+P0UHLEWqWOuECIQC91zQuL7nStgGzT3HvaeBB5Ouapa39fHRm2nCjHaxwwwIgRR2XdvmUOj23XWMomr5F14SN/7V7fVcD0D8wjNElsmECIDYavV5kb7tj7/wgqkInlKhzC8rZaUsTS0F9BBkY/eptAiAQJ8Saz8YlMIESdHMxANGSog01fECbcZqLFMuNf8SorA==`
  123. // // 请求参数
  124. // params := make(map[string]interface{})
  125. // params["account"] = account
  126. // params["name"] = name
  127. // params["idCard"] = idCard
  128. // params["idCardType"] = 1
  129. // if idCardType != nil {
  130. // params["idCardType"] = *idCardType
  131. // }
  132. // params["mobile"] = mobile
  133. // // 用户实名认证模式为强制认证时,需要选择认证方法:
  134. // // 1:身份证二要素认证
  135. // // 2:运营商三要素认证
  136. // // 3:银行卡四要素认证
  137. // params["identifyType"] = 2
  138. // params["identifyMobile"] = mobile
  139. // params["isNotice"] = 1
  140. // bizData, err := json.Marshal(params)
  141. // if err != nil {
  142. // global.M2A_LOG.Error("[AddPersonalUser] 构建请求参数失败", zap.Error(err))
  143. // return
  144. // }
  145. // global.M2A_LOG.Info("[AddPersonalUser] 构建请求参数", zap.Any("params", string(bizData)))
  146. // // 签名
  147. // sign, timestamp, err := getSignatureByPy(string(bizData), appId, privateKey)
  148. // sign = strings.Replace(sign, "\r\n", "", -1)
  149. // sign = strings.Replace(sign, "\r", "", -1)
  150. // if err != nil {
  151. // global.M2A_LOG.Error("[AddPersonalUser] 签名失败", zap.Error(err))
  152. // return
  153. // }
  154. // global.M2A_LOG.Info("[AddPersonalUser] 签名", zap.Any("sign", sign))
  155. // // 构建form-data请求参数
  156. // formValues := url.Values{}
  157. // formValues.Set("appId", appId)
  158. // formValues.Set("timestamp", timestamp)
  159. // formValues.Set("bizData", string(bizData))
  160. // // 构建请求
  161. // req, err := http.NewRequest("POST", apiUrl, bytes.NewReader([]byte(formValues.Encode())))
  162. // // 设置请求头
  163. // req.Header.Set("sign", sign)
  164. // req.Header.Set("timestamp", timestamp)
  165. // req.Header.Set("Content-Type", "multipart/form-data; charset=UTF-8")
  166. // req.Header.Set("Accept", "*/*")
  167. // client := &http.Client{}
  168. // rsp, err := client.Do(req)
  169. // if err != nil {
  170. // global.M2A_LOG.Error("[AddPersonalUser] 请求失败", zap.Error(err))
  171. // return
  172. // }
  173. // defer rsp.Body.Close()
  174. // body, err := io.ReadAll(rsp.Body)
  175. // if err != nil {
  176. // global.M2A_LOG.Error("[AddPersonalUser] 获取body失败", zap.Error(err))
  177. // return
  178. // }
  179. // if err = json.Unmarshal(body, &rspData); err != nil {
  180. // global.M2A_LOG.Error("[AddPersonalUser] 反序列化body失败", zap.Error(err))
  181. // return
  182. // }
  183. // return
  184. // }
  185. type AddPersonalUserData struct {
  186. SealNo string `json:"sealNo"` // 默认印章编号
  187. }
  188. // https://preweb.asign.cn/platform/openDoc/docDetail?mid=addPersonalUser
  189. // 添加个人用户(https://{host}/user/addPersonalUser)
  190. // 错误码 错误描述
  191. // 100021 用户已存在
  192. // 100156 手机号码格式错误
  193. // 100157 邮箱格式错误
  194. // 100571 参数错误,唯一识别码Account为空
  195. // 100577 参数错误,{param}长度超过限制:{length}
  196. // 100579 参数错误,{param}不能为空
  197. // 100598 参数错误,身份证号码格式不正确
  198. // 100639 参数错误,名字点号格式不正确
  199. func AddPersonalUserBy(account, name, idCard, mobile string, idCardType *int) (rspData AsignRsp[AddPersonalUserData], err error) {
  200. apiUrl := global.M2A_CONFIG.Asign.URL + "user/addPersonalUser"
  201. // 构建请求数据结构
  202. reqData := make(map[string]interface{})
  203. reqData["account"] = account
  204. reqData["name"] = name
  205. reqData["mobile"] = mobile
  206. reqData["idCard"] = idCard
  207. reqData["idCardType"] = 1
  208. if idCardType != nil {
  209. reqData["idCardType"] = *idCardType
  210. }
  211. reqData["api"] = "addPerson"
  212. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  213. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  214. reqData["apiUrl"] = apiUrl
  215. // 将请求数据转换为JSON字符串
  216. reqJSON, err := json.Marshal(reqData)
  217. if err != nil {
  218. global.M2A_LOG.Error("[AddPersonalUserBy] 构建请求参数失败", zap.Error(err))
  219. return
  220. }
  221. // 要执行的Python脚本命令
  222. pythonScriptPath := "./py/Enter.py"
  223. // 创建一个命令对象
  224. cmd := exec.Command("py", pythonScriptPath)
  225. // 设置标准输入为JSON字符串
  226. cmd.Stdin = strings.NewReader(string(reqJSON))
  227. // 创建一个字节缓冲区来捕获命令的输出
  228. var stdoutBuf, stderrBuf bytes.Buffer
  229. cmd.Stdout = &stdoutBuf
  230. cmd.Stderr = &stderrBuf
  231. // 执行命令
  232. err = cmd.Run()
  233. // 输出标准输出和标准错误
  234. fmt.Println("标准输出:", stdoutBuf.String())
  235. fmt.Println("标准错误:", stderrBuf.String())
  236. if err != nil {
  237. global.M2A_LOG.Error("[AddPersonalUserBy] 请求失败", zap.Error(err))
  238. return
  239. }
  240. // 结果
  241. rspBody := stdoutBuf.String()
  242. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  243. global.M2A_LOG.Error("[AddPersonalUserBy] 反序列化body失败", zap.Error(err))
  244. return
  245. }
  246. return
  247. }
  248. type CreateContractData struct {
  249. PreviewUrl string `json:"previewUrl"` // 合同预览链接
  250. ContractFiles []interface{} `json:"contractFiles"` // 合同文件信息(文件名称,附件编号,页数)
  251. }
  252. /*
  253. *
  254. CreateContract 上传待签署文件
  255. contractNo 合同ID,合同唯一编号
  256. contractName 合同名称
  257. templateNo 合同模板编号
  258. *
  259. */
  260. func CreateContract(contractNo, contractName, templateNo string) (rspData AsignRsp[CreateContractData], err error) {
  261. apiUrl := global.M2A_CONFIG.Asign.URL + "contract/createContract"
  262. // 构建请求数据结构
  263. reqData := make(map[string]interface{})
  264. reqData["contractNo"] = contractNo
  265. reqData["contractName"] = contractName
  266. reqData["signOrder"] = 1 // 1:无序签约(默认
  267. reqData["templates"] = []map[string]string{
  268. {"templateNo": templateNo}} // 合同模板编号 - 目前只支持一份合同签一份协议
  269. reqData["notifyUrl"] = ""
  270. reqData["api"] = "createContract"
  271. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  272. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  273. reqData["apiUrl"] = apiUrl
  274. // 将请求数据转换为JSON字符串
  275. reqJSON, err := json.Marshal(reqData)
  276. if err != nil {
  277. global.M2A_LOG.Error("[AddPersonalUserBy] 构建请求参数失败", zap.Error(err))
  278. return
  279. }
  280. // 要执行的Python脚本命令
  281. pythonScriptPath := "./py/Enter.py"
  282. // 创建一个命令对象
  283. cmd := exec.Command("py", pythonScriptPath)
  284. // 设置标准输入为JSON字符串
  285. cmd.Stdin = strings.NewReader(string(reqJSON))
  286. // 获取标准输出
  287. output, err := cmd.CombinedOutput()
  288. if err != nil {
  289. global.M2A_LOG.Error("[AddPersonalUserBy] 请求失败", zap.Error(err))
  290. return
  291. }
  292. // 结果
  293. rspBody := string(output)
  294. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  295. global.M2A_LOG.Error("[AddPersonalUserBy] 反序列化body失败", zap.Error(err))
  296. return
  297. }
  298. return
  299. }
  300. // 合同用户信息
  301. type SignUserData struct {
  302. Account string `json:"account"` // 用户唯一识别码
  303. SignUrl string `json:"signUrl"` // 合同签署链接
  304. PwdSignUrl string `json:"pwdSignUrl"` // 密码签署链接
  305. SignOrder int `json:"signOrder"` // 顺序签约的序号
  306. Name string `json:"name"` // 用户姓名
  307. IdCard string `json:"idCard"` // 用户身份证
  308. }
  309. // 添加签署方响应数据
  310. type AddSignerData struct {
  311. ContractNo string `json:"contractNo"` // 合同编号
  312. ContractName string `json:"contractName"` // 合同名称
  313. ValidityTime string `json:"validityTime"` // 合同有效期
  314. PreviewUrl string `json:"previewUrl"` // 合同预览链接
  315. SignUser []SignUserData `json:"signUser"` // 合同用户信息
  316. }
  317. /*
  318. *
  319. AddSigner 添加签署方
  320. contractNo 合同ID,合同唯一编号
  321. account 用户唯一识别码(UserID)
  322. *
  323. */
  324. func AddSigner(contractNo, account string) (rspData AsignRsp[AddSignerData], err error) {
  325. apiUrl := global.M2A_CONFIG.Asign.URL + "contract/addSigner"
  326. // 构建请求数据结构
  327. reqData := make(map[string]interface{})
  328. reqData["contractNo"] = contractNo
  329. reqData["account"] = account
  330. reqData["signStrategyList"] = []map[string]int{
  331. {"attachNo": 1, "locationMode": 4}}
  332. reqData["api"] = "addSigner"
  333. reqData["appId"] = global.M2A_CONFIG.Asign.AppId
  334. reqData["appKey"] = global.M2A_CONFIG.Asign.PrivateKey
  335. reqData["apiUrl"] = apiUrl
  336. // 将请求数据转换为JSON字符串
  337. reqJSON, err := json.Marshal(reqData)
  338. if err != nil {
  339. global.M2A_LOG.Error("[AddSigner] 构建请求参数失败", zap.Error(err))
  340. return
  341. }
  342. // 要执行的Python脚本命令
  343. pythonScriptPath := "./py/Enter.py"
  344. // 创建一个命令对象
  345. cmd := exec.Command("py", pythonScriptPath)
  346. // 设置标准输入为JSON字符串
  347. cmd.Stdin = strings.NewReader(string(reqJSON))
  348. // 获取标准输出
  349. output, err := cmd.CombinedOutput()
  350. if err != nil {
  351. global.M2A_LOG.Error("[AddSigner] 请求失败", zap.Error(err))
  352. return
  353. }
  354. // 结果
  355. rspBody := string(output)
  356. if err = json.Unmarshal([]byte(rspBody), &rspData); err != nil {
  357. global.M2A_LOG.Error("[AddSigner] 反序列化body失败", zap.Error(err))
  358. return
  359. }
  360. return
  361. }