sign.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. package sign
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "mtp20access/global"
  10. "mtp20access/model/account"
  11. "mtp20access/model/account/request"
  12. "mtp20access/model/account/response"
  13. "mtp20access/service/asign"
  14. "mtp20access/utils"
  15. "net/http"
  16. "os"
  17. "strconv"
  18. "time"
  19. "github.com/gofrs/uuid"
  20. "go.uber.org/zap"
  21. )
  22. // QueryUserESignRecord 查询用户电子签记录表
  23. func QueryUserESignRecord(userId int) (rsp []account.Useresignrecord, err error) {
  24. rsp = make([]account.Useresignrecord, 0)
  25. err = global.M2A_DB.Where("USERID = ?", userId).Find(&rsp)
  26. return
  27. }
  28. // AddUser 添加用户
  29. func AddUser(req request.AddUserReq, userId int) (err error) {
  30. // 获取用户电子签记录
  31. useresignrecord := new(account.Useresignrecord)
  32. has, _ := global.M2A_DB.Where("USERID = ? AND TEMPLATETYPE = 1 AND RECORDSTATUS = 3", userId).Get(useresignrecord)
  33. if has {
  34. err = errors.New("用户已同步")
  35. return
  36. }
  37. // 调用爱签API-添加个人用户(https://{host}/user/addPersonalUser)
  38. rsp, err := asign.AddPersonalUserBy(
  39. strconv.Itoa(userId),
  40. req.Name,
  41. req.IdCard,
  42. req.Mobile,
  43. req.IdCardType,
  44. )
  45. if err != nil {
  46. return
  47. }
  48. if rsp.Code != 100000 {
  49. err = errors.New(strconv.Itoa(rsp.Code))
  50. global.M2A_LOG.Error("【AddUser】 接口调用失败", zap.Error(err))
  51. return
  52. }
  53. // 更新用户电子签记录-实名认证状态
  54. authinfo, err := json.Marshal(req)
  55. if err != nil {
  56. global.M2A_LOG.Error("【AddUser】 构建AUTHINFO失败", zap.Error(err))
  57. return
  58. }
  59. sql := fmt.Sprintf(`
  60. UPDATE useresignrecord
  61. SET RECORDSTATUS = 3,
  62. UPDATETIME = SYSDATE,
  63. AUTHINFO = '%v'
  64. WHERE USERID = %v AND TEMPLATETYPE = 1
  65. `, string(authinfo), userId)
  66. if _, err = global.M2A_DB.Exec(sql); err != nil {
  67. global.M2A_LOG.Error("【AddUser】 更新用户电子签记录失败", zap.Error(err))
  68. return
  69. }
  70. return
  71. }
  72. // CreateContractAndAddSigner 上传待签署文件和添加签署方
  73. func CreateContractAndAddSigner(req request.CreateContractAndAddSignerReq, userId int) (rsp response.CreateContractAndAddSignerRsp, err error) {
  74. // 获取用户电子签记录
  75. useresignrecord := new(account.Useresignrecord)
  76. has, err := global.M2A_DB.Where("USERID = ? AND TEMPLATENO = ?", userId, req.TemplateNo).Get(useresignrecord)
  77. if err != nil || !has {
  78. global.M2A_LOG.Error("【CreateContractAndAddSigner】 获取用户电子签记录失败", zap.Error(err))
  79. return
  80. }
  81. // 判断是否需要创建合同(上传待签署文件)
  82. if useresignrecord.CONTRACTNO == "" {
  83. // 生成合同编号
  84. // #{userid} || '_' || to_char(sysdate, 'yyyyMMddhh24miss') || '_' || seq_useresignrecord.currval,
  85. contractNo := fmt.Sprintf("%d_%s_%v", userId, time.Now().Format("20060102150405"), useresignrecord.RECORDID)
  86. // 调用爱签API-上传待签署文件(https://{host}/contract/createContract)
  87. r, e := asign.CreateContract(
  88. contractNo,
  89. useresignrecord.TEMPLATENAME,
  90. useresignrecord.TEMPLATENO,
  91. )
  92. if e != nil {
  93. err = e
  94. return
  95. }
  96. if r.Code != 100000 {
  97. err = errors.New(strconv.Itoa(r.Code))
  98. global.M2A_LOG.Error("【CreateContractAndAddSigner】 上传待签署文件接口调用失败", zap.Error(err))
  99. return
  100. }
  101. // 将返回的合同编号写入数据库
  102. useresignrecord.CONTRACTNO = contractNo
  103. sql := fmt.Sprintf(`
  104. UPDATE useresignrecord
  105. SET contractNo = '%v',
  106. UPDATETIME = SYSDATE
  107. WHERE RECORDID = %v
  108. `, useresignrecord.CONTRACTNO, useresignrecord.RECORDID)
  109. if _, err = global.M2A_DB.Exec(sql); err != nil {
  110. global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同编号失败", zap.Error(err))
  111. return
  112. }
  113. }
  114. // 判断是否需要添加签署方(获取合同签约地址)
  115. if useresignrecord.SIGNURL != "" {
  116. err = errors.New("合同签署链接已存在")
  117. } else {
  118. // 调用爱签API-添加签署方(https://{host}/contract/addSigner)
  119. r, e := asign.AddSigner(
  120. useresignrecord.CONTRACTNO,
  121. strconv.Itoa(userId),
  122. )
  123. if e != nil {
  124. err = e
  125. return
  126. }
  127. if r.Code != 100000 {
  128. err = errors.New(strconv.Itoa(r.Code))
  129. global.M2A_LOG.Error("【CreateContractAndAddSigner】 添加签署方接口调用失败", zap.Error(err))
  130. return
  131. }
  132. if len(r.Data.SignUser) > 0 {
  133. useresignrecord.SIGNURL = r.Data.SignUser[0].SignUrl
  134. // 将返回的合同编号写入数据库
  135. sql := fmt.Sprintf(`
  136. UPDATE useresignrecord
  137. SET RECORDSTATUS = 2,
  138. SIGNURL = '%v',
  139. UPDATETIME = SYSDATE
  140. WHERE RECORDID = %v
  141. `, useresignrecord.SIGNURL, useresignrecord.RECORDID)
  142. if _, err = global.M2A_DB.Exec(sql); err != nil {
  143. global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同签署链接失败", zap.Error(err))
  144. return
  145. }
  146. // 给客户端返回合同签署地址
  147. rsp.SignUrl = useresignrecord.SIGNURL
  148. }
  149. }
  150. return
  151. }
  152. // SignCompleted
  153. func SignCompleted(userId int) (err error) {
  154. // 获取用户电子签记录
  155. datas := make([]account.Useresignrecord, 0)
  156. err = global.M2A_DB.Where("USERID = ?", userId).Find(&datas)
  157. if err != nil {
  158. global.M2A_LOG.Error("SignCompleted 获取用户电子签记录失败", zap.Error(err))
  159. return
  160. }
  161. var record *account.Useresignrecord
  162. for i, item := range datas {
  163. if item.TEMPLATETYPE == 1 {
  164. record = &datas[i]
  165. }
  166. // 如果是签署中状态,则查询一下合同状态
  167. if item.RECORDSTATUS == 2 {
  168. rspCTStatus, e := asign.ContractStatus(item.CONTRACTNO)
  169. if e != nil {
  170. err = e
  171. global.M2A_LOG.Error("[SignCompleted] 查询合同状态失败", zap.Error(err))
  172. return
  173. }
  174. // 下载合同并修改合同状态
  175. err = modifyContractStatus(rspCTStatus.Data.ContractNo, strconv.Itoa(rspCTStatus.Data.Status))
  176. } else if item.RECORDSTATUS != 3 && item.RECORDSTATUS != 2 {
  177. err = errors.New("未完成所有合同签署")
  178. global.M2A_LOG.Error(err.Error())
  179. return
  180. }
  181. }
  182. // 获取临时存储的用户信息
  183. if record == nil || record.AUTHINFO == "" {
  184. global.M2A_LOG.Error("[SignCompleted] 获取实名认证信息失败", zap.Error(err))
  185. return
  186. }
  187. // {"name":"甘肃碳交","idCard":"360428200007287603","idCardType":1,"idCardPhoto":"./uploadFile/20230812/202308121518494929.png","idCardPhotoBackURL":"./uploadFile/20230812/202308121518529223.png","mobile":"15914012151"}
  188. cacheMap := make(map[string]interface{})
  189. err = json.Unmarshal([]byte(record.AUTHINFO), &cacheMap)
  190. if err != nil {
  191. global.M2A_LOG.Error("[SignCompleted] 反序列化临时存储用户信息失败", zap.Error(err))
  192. return
  193. }
  194. // 调用JAVA实名认证接口
  195. reqParam := make(map[string]interface{})
  196. reqParam["userid"] = userId
  197. reqParam["cardnum"] = cacheMap["idCard"]
  198. reqParam["username"] = cacheMap["name"]
  199. reqParam["cardtype"] = 0 // 目前写死证件类型为身份证 - 0
  200. reqParam["cardfrontphotourl"] = cacheMap["idCardPhoto"]
  201. reqParam["cardbackphotourl"] = cacheMap["idCardPhotoBackURL"]
  202. reqParam["userinfotype"] = 1 // 目前写死为个人 - 1
  203. jsonParam, err := json.Marshal(&reqParam)
  204. if err != nil {
  205. global.M2A_LOG.Error("[SignCompleted] 反序列化JAVA实名认证入参失败", zap.Error(err))
  206. return
  207. }
  208. // 构建请求
  209. javaUrl := global.M2A_CONFIG.Asign.OpenApiUrl + "/onlineopen/userInfo/addAuth"
  210. req, err := http.NewRequest("POST", javaUrl, bytes.NewReader(jsonParam))
  211. // 设置请求头
  212. req.Header.Set("Content-Type", "application/json; charset=utf-8")
  213. client := &http.Client{}
  214. rsp, err := client.Do(req)
  215. if err != nil {
  216. global.M2A_LOG.Error("[SignCompleted] 请求失败", zap.Error(err))
  217. return
  218. }
  219. defer rsp.Body.Close()
  220. body, err := io.ReadAll(rsp.Body)
  221. if err != nil {
  222. global.M2A_LOG.Error("[SignCompleted] 获取body失败", zap.Error(err))
  223. return
  224. }
  225. rspData := make(map[string]interface{})
  226. if err = json.Unmarshal(body, &rspData); err != nil {
  227. global.M2A_LOG.Error("[SignCompleted] 反序列化java body失败", zap.Error(err))
  228. return
  229. }
  230. fmt.Println("rspData:", rspData)
  231. return
  232. }
  233. /*
  234. handleASignCompleted 处理爱签合同签署完成后回调通知
  235. req 异步推送参数
  236. */
  237. func HandleASignCompleted(contractNo, status string) (err error) {
  238. /*
  239. // 合同签署完成后回调通知示例
  240. String publickey = "MFwwDQcccccxxxxmEz/nw27Ln6AP90ZCMPi+iNF1m9mhNECAwEAAQ==";
  241. String remark = ""; // 若被拒签则会返回拒签原因,拒签原因不参与签名
  242. Map <String, String> map = new HashMap<>();
  243. map.put("action", "signCompleted");
  244. map.put("contractNo", "20221114142140345");
  245. map.put("status", "2");
  246. map.put("signTime", "2022-11-14 14:22:00");
  247. map.put("timestamp", "1668406920005");
  248. map.put("validityTime", "2022-11-24 23:59:59");
  249. String json = JSONObject.toJSONString(map, SerializerFeature.MapSortField);
  250. System.out.println("数据:" + json);
  251. // 计算签名
  252. try {
  253. String sign = "feFfcprGjdmDDqRmxK5qlWlMncX0mc6LJ5agebOGIx2QiAern+6ZRg/SBHOgvHp/+1ywVRdyKNUKxPneETwKPw==";
  254. System.out.println(RSAUtils.rsaSignCheck(sign, json, publickey));
  255. } catch (Exception e) {
  256. e.printStackTrace();
  257. }
  258. */
  259. // 获取合同编号
  260. if contractNo == "" {
  261. global.M2A_LOG.Error("【HandleASignCompleted】 获取合同编号失败")
  262. return
  263. }
  264. // 获取合同状态
  265. if status == "" {
  266. global.M2A_LOG.Error("【HandleASignCompleted】 获取获取合同状态失败")
  267. return
  268. }
  269. // 下载合同并修改合同状态
  270. err = modifyContractStatus(contractNo, status)
  271. return
  272. }
  273. // modifyContractStatus 爱签异步推送和查询合同状态后调用
  274. // 合同状态:
  275. // 0:等待签约
  276. // 1:签约中
  277. // 2:已签约
  278. // 3:过期
  279. // 4:拒签
  280. // 6:作废
  281. // -2:状态异常
  282. func modifyContractStatus(contractNo, status string) (err error) {
  283. if status == "2" {
  284. // 获取用户电子签记录
  285. useresignrecord := new(account.Useresignrecord)
  286. has, e := global.M2A_DB.Where("CONTRACTNO = ?", contractNo).Get(useresignrecord)
  287. if e != nil || !has {
  288. err = e
  289. global.M2A_LOG.Error("【modifyContractStatus】 获取用户电子签记录失败", zap.Error(err))
  290. return
  291. }
  292. if useresignrecord.RECORDSTATUS == 3 {
  293. // 已完成签署,不需要再下载和修改数据库状态
  294. return
  295. }
  296. // 已签约
  297. // 下载合同
  298. r, e := asign.DownloadContract(contractNo)
  299. if e != nil {
  300. err = e
  301. return
  302. }
  303. if r.Code != 100000 {
  304. err = errors.New(strconv.Itoa(r.Code))
  305. global.M2A_LOG.Error("【modifyContractStatus】 下载合同接口调用失败", zap.Error(err))
  306. return
  307. }
  308. if r.Data.Data != "" {
  309. // 获取网上开户地址(用于客户端下载文件)
  310. openconfig := account.Wskhopenaccountconfig{CONFIGID: 6}
  311. has, e := openconfig.Get()
  312. if e != nil || !has {
  313. err = e
  314. global.M2A_LOG.Error("【modifyContractStatus】 获取网上开户地址失败", zap.Error(err))
  315. return
  316. }
  317. // 将Base64写入目标文件
  318. uid, _ := uuid.NewV4()
  319. fileName := fmt.Sprintf("%v.pdf", uid.String())
  320. // openconfig.CONFIGVALUE = "./" // FIXME: - 测试代码
  321. folderPath := "sign/" + time.Now().Format("20060102")
  322. savePath := openconfig.CONFIGVALUE + "/uploadFile/" + folderPath
  323. if exist, _ := utils.PathExists(savePath); !exist {
  324. os.MkdirAll(savePath, os.ModePerm)
  325. }
  326. fb, e := base64.StdEncoding.DecodeString(r.Data.Data)
  327. if e != nil {
  328. err = e
  329. global.M2A_LOG.Error("【modifyContractStatus】 解码pdf文件失败", zap.Error(err))
  330. return
  331. }
  332. if err = os.WriteFile(savePath+"/"+fileName, fb, 0666); err != nil {
  333. global.M2A_LOG.Error("【modifyContractStatus】 保存合同失败", zap.Error(err))
  334. return
  335. }
  336. // 更新数据库记录
  337. contractfileaddr := fmt.Sprintf("./uploadFile/%v/%v", folderPath, fileName)
  338. sql := fmt.Sprintf(`
  339. UPDATE useresignrecord
  340. SET RECORDSTATUS = 3,
  341. UPDATETIME = SYSDATE,
  342. CONTRACTFILEADDR = '%v'
  343. WHERE CONTRACTNO = '%v'
  344. `, contractfileaddr, contractNo)
  345. if _, err = global.M2A_DB.Exec(sql); err != nil {
  346. global.M2A_LOG.Error("【modifyContractStatus】 更新用户电子签记录失败", zap.Error(err))
  347. return
  348. }
  349. }
  350. } else if status == "0" || status == "1" {
  351. err = fmt.Errorf("合同签署状态不正确, status: %v", status)
  352. return
  353. } else {
  354. // 其它状态一律改为4(签署拒绝)
  355. // 更新数据库记录
  356. sql := fmt.Sprintf(`
  357. UPDATE useresignrecord
  358. SET RECORDSTATUS = 4,
  359. UPDATETIME = SYSDATE
  360. WHERE CONTRACTNO = '%v'
  361. `, contractNo)
  362. if _, err = global.M2A_DB.Exec(sql); err != nil {
  363. global.M2A_LOG.Error("【HandleASignCompleted】 更新用户电子签记录失败", zap.Error(err))
  364. return
  365. }
  366. err = fmt.Errorf("合同签署状态不正确, status: %v", status)
  367. }
  368. return
  369. }