sign.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. if err = global.M2A_DB.Where("USERID = ?", userId).OrderBy("ORDERINDEX").Find(&rsp); err != nil {
  26. return
  27. }
  28. for i := range rsp {
  29. data := &rsp[i]
  30. if data.TEMPLATETYPE == 2 && data.RECORDSTATUS == 2 {
  31. // 去爱签同步合同状态
  32. rspCTStatus, e := asign.ContractStatus(data.CONTRACTNO)
  33. if e != nil {
  34. err = e
  35. global.M2A_LOG.Error("【QueryUserESignRecord】 查询合同状态失败", zap.Error(err))
  36. return
  37. }
  38. // 下载合同并修改合同状态
  39. e = modifyContractStatus(data.CONTRACTNO, strconv.Itoa(rspCTStatus.Data.Status))
  40. if e != nil {
  41. // 从新获取记录
  42. recordId := data.RECORDID
  43. data = new(account.Useresignrecord)
  44. has, e := global.M2A_DB.Where("RECORDID = ?", recordId).Get(data)
  45. if e != nil || !has {
  46. err = e
  47. global.M2A_LOG.Error("【QueryUserESignRecord】 获取用户电子签记录失败", zap.Error(err))
  48. return
  49. }
  50. }
  51. }
  52. }
  53. return
  54. }
  55. // AddUser 添加用户
  56. func AddUser(req request.AddUserReq, userId int) (err error) {
  57. // 获取用户电子签记录
  58. useresignrecord := new(account.Useresignrecord)
  59. has, _ := global.M2A_DB.Where("USERID = ? AND TEMPLATETYPE = 1 AND RECORDSTATUS = 3", userId).Get(useresignrecord)
  60. if has {
  61. err = errors.New("用户已同步")
  62. return
  63. }
  64. // 调用爱签API-添加个人用户(https://{host}/user/addPersonalUser)
  65. rsp, err := asign.AddPersonalUserBy(
  66. strconv.Itoa(userId),
  67. req.Name,
  68. req.IdCard,
  69. req.Mobile,
  70. req.IdCardType,
  71. )
  72. if err != nil {
  73. return
  74. }
  75. if rsp.Code != 100000 {
  76. err = errors.New(strconv.Itoa(rsp.Code))
  77. global.M2A_LOG.Error("【AddUser】 接口调用失败", zap.Error(err))
  78. return
  79. }
  80. // 更新用户电子签记录-实名认证状态
  81. authinfo, err := json.Marshal(req)
  82. if err != nil {
  83. global.M2A_LOG.Error("【AddUser】 构建AUTHINFO失败", zap.Error(err))
  84. return
  85. }
  86. sql := fmt.Sprintf(`
  87. UPDATE useresignrecord
  88. SET RECORDSTATUS = 3,
  89. UPDATETIME = SYSDATE,
  90. AUTHINFO = '%v'
  91. WHERE USERID = %v AND TEMPLATETYPE = 1
  92. `, string(authinfo), userId)
  93. if _, err = global.M2A_DB.Exec(sql); err != nil {
  94. global.M2A_LOG.Error("【AddUser】 更新用户电子签记录失败", zap.Error(err))
  95. return
  96. }
  97. return
  98. }
  99. // CreateContractAndAddSigner 上传待签署文件和添加签署方
  100. func CreateContractAndAddSigner(req request.CreateContractAndAddSignerReq, userId int) (rsp response.CreateContractAndAddSignerRsp, err error) {
  101. // 获取用户电子签记录
  102. useresignrecord := new(account.Useresignrecord)
  103. has, err := global.M2A_DB.Where("USERID = ? AND TEMPLATENO = ?", userId, req.TemplateNo).Get(useresignrecord)
  104. if err != nil || !has {
  105. global.M2A_LOG.Error("【CreateContractAndAddSigner】 获取用户电子签记录失败", zap.Error(err))
  106. return
  107. }
  108. // 判断是否需要创建合同(上传待签署文件)
  109. if useresignrecord.CONTRACTNO == "" {
  110. // 生成合同编号
  111. // #{userid} || '_' || to_char(sysdate, 'yyyyMMddhh24miss') || '_' || seq_useresignrecord.currval,
  112. contractNo := fmt.Sprintf("%d_%s_%v", userId, time.Now().Format("20060102150405"), useresignrecord.RECORDID)
  113. // 调用爱签API-上传待签署文件(https://{host}/contract/createContract)
  114. r, e := asign.CreateContract(
  115. contractNo,
  116. useresignrecord.TEMPLATENAME,
  117. useresignrecord.TEMPLATENO,
  118. )
  119. if e != nil {
  120. err = e
  121. return
  122. }
  123. if r.Code != 100000 {
  124. err = errors.New(strconv.Itoa(r.Code))
  125. global.M2A_LOG.Error("【CreateContractAndAddSigner】 上传待签署文件接口调用失败", zap.Error(err))
  126. return
  127. }
  128. // 将返回的合同编号写入数据库
  129. useresignrecord.CONTRACTNO = contractNo
  130. sql := fmt.Sprintf(`
  131. UPDATE useresignrecord
  132. SET contractNo = '%v',
  133. UPDATETIME = SYSDATE
  134. WHERE RECORDID = %v
  135. `, useresignrecord.CONTRACTNO, useresignrecord.RECORDID)
  136. if _, err = global.M2A_DB.Exec(sql); err != nil {
  137. global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同编号失败", zap.Error(err))
  138. return
  139. }
  140. }
  141. // 判断是否需要添加签署方(获取合同签约地址)
  142. if useresignrecord.SIGNURL != "" {
  143. err = errors.New("合同签署链接已存在")
  144. } else {
  145. // 调用爱签API-添加签署方(https://{host}/contract/addSigner)
  146. r, e := asign.AddSigner(
  147. useresignrecord.CONTRACTNO,
  148. strconv.Itoa(userId),
  149. )
  150. if e != nil {
  151. err = e
  152. return
  153. }
  154. if r.Code != 100000 {
  155. err = errors.New(strconv.Itoa(r.Code))
  156. global.M2A_LOG.Error("【CreateContractAndAddSigner】 添加签署方接口调用失败", zap.Error(err))
  157. return
  158. }
  159. if len(r.Data.SignUser) > 0 {
  160. useresignrecord.SIGNURL = r.Data.SignUser[0].SignUrl
  161. // 将返回的合同编号写入数据库
  162. sql := fmt.Sprintf(`
  163. UPDATE useresignrecord
  164. SET RECORDSTATUS = 2,
  165. SIGNURL = '%v',
  166. UPDATETIME = SYSDATE
  167. WHERE RECORDID = %v
  168. `, useresignrecord.SIGNURL, useresignrecord.RECORDID)
  169. if _, err = global.M2A_DB.Exec(sql); err != nil {
  170. global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同签署链接失败", zap.Error(err))
  171. return
  172. }
  173. // 给客户端返回合同签署地址
  174. rsp.SignUrl = useresignrecord.SIGNURL
  175. }
  176. }
  177. return
  178. }
  179. // SignCompleted
  180. func SignCompleted(userId int) (err error) {
  181. // 获取用户电子签记录
  182. datas := make([]account.Useresignrecord, 0)
  183. err = global.M2A_DB.Where("USERID = ?", userId).Find(&datas)
  184. if err != nil {
  185. global.M2A_LOG.Error("[SignCompleted] 获取用户电子签记录失败", zap.Error(err))
  186. return
  187. }
  188. var record *account.Useresignrecord
  189. for i, item := range datas {
  190. if item.TEMPLATETYPE == 1 {
  191. record = &datas[i]
  192. }
  193. switch item.TEMPLATETYPE {
  194. case 1: // 实名认证
  195. if item.RECORDSTATUS != 3 {
  196. err = errors.New("未完成用户添加")
  197. return
  198. }
  199. case 2: // 合同签署
  200. // 如果是签署中状态,则查询一下合同状态
  201. if item.RECORDSTATUS == 2 {
  202. rspCTStatus, e := asign.ContractStatus(item.CONTRACTNO)
  203. if e != nil {
  204. err = e
  205. global.M2A_LOG.Error("[SignCompleted] 查询合同状态失败", zap.Error(err))
  206. return
  207. }
  208. // 下载合同并修改合同状态
  209. err = modifyContractStatus(rspCTStatus.Data.ContractNo, strconv.Itoa(rspCTStatus.Data.Status))
  210. return
  211. } else if (item.RECORDSTATUS != 3 && item.RECORDSTATUS != 2) && item.TEMPLATETYPE == 2 {
  212. err = errors.New("未完成所有合同签署")
  213. global.M2A_LOG.Error(err.Error())
  214. return
  215. }
  216. case 3: // 个人意愿
  217. if item.RECORDSTATUS != 3 {
  218. err = errors.New("未完成个人意愿核身认证")
  219. return
  220. }
  221. }
  222. }
  223. // 获取临时存储的用户信息
  224. if record == nil || record.AUTHINFO == "" {
  225. global.M2A_LOG.Error("[SignCompleted] 获取实名认证信息失败", zap.Error(err))
  226. return
  227. }
  228. // {"name":"甘肃碳交","idCard":"360428200007287603","idCardType":1,"idCardPhoto":"./uploadFile/20230812/202308121518494929.png","idCardPhotoBackURL":"./uploadFile/20230812/202308121518529223.png","mobile":"15914012151"}
  229. cacheMap := make(map[string]interface{})
  230. err = json.Unmarshal([]byte(record.AUTHINFO), &cacheMap)
  231. if err != nil {
  232. global.M2A_LOG.Error("[SignCompleted] 反序列化临时存储用户信息失败", zap.Error(err))
  233. return
  234. }
  235. // 调用JAVA实名认证接口
  236. reqParam := make(map[string]interface{})
  237. reqParam["userid"] = userId
  238. reqParam["cardnum"] = cacheMap["idCard"]
  239. reqParam["username"] = cacheMap["name"]
  240. reqParam["cardtype"] = 0 // 目前写死证件类型为身份证 - 0
  241. reqParam["cardfrontphotourl"] = cacheMap["idCardPhoto"]
  242. reqParam["cardbackphotourl"] = cacheMap["idCardPhotoBackURL"]
  243. reqParam["userinfotype"] = 1 // 目前写死为个人 - 1
  244. jsonParam, err := json.Marshal(&reqParam)
  245. if err != nil {
  246. global.M2A_LOG.Error("[SignCompleted] 反序列化JAVA实名认证入参失败", zap.Error(err))
  247. return
  248. }
  249. // 构建请求
  250. javaUrl := global.M2A_CONFIG.Asign.OpenApiUrl + "/onlineopen/userInfo/addAuth"
  251. req, err := http.NewRequest("POST", javaUrl, bytes.NewReader(jsonParam))
  252. // 设置请求头
  253. req.Header.Set("Content-Type", "application/json; charset=utf-8")
  254. client := &http.Client{}
  255. rsp, err := client.Do(req)
  256. if err != nil {
  257. global.M2A_LOG.Error("[SignCompleted] 请求失败", zap.Error(err))
  258. return
  259. }
  260. defer rsp.Body.Close()
  261. body, err := io.ReadAll(rsp.Body)
  262. if err != nil {
  263. global.M2A_LOG.Error("[SignCompleted] 获取body失败", zap.Error(err))
  264. return
  265. }
  266. // rspData: map[code:0 hasAuth:1 message:认证成功]
  267. rspData := make(map[string]interface{})
  268. if err = json.Unmarshal(body, &rspData); err != nil {
  269. global.M2A_LOG.Error("[SignCompleted] 反序列化java body失败", zap.Error(err))
  270. return
  271. }
  272. global.M2A_LOG.Info("[SignCompleted] 调用JAVA实名认证接口返回", zap.Any("rspData", rspData))
  273. code, ok := rspData["code"]
  274. if !ok {
  275. err = errors.New("实名认证失败,请稍后重试")
  276. return
  277. }
  278. if code != "0" {
  279. err = fmt.Errorf("实名认证失败,%v", rspData["message"])
  280. global.M2A_LOG.Error("[SignCompleted]", zap.Error(err))
  281. return
  282. }
  283. return
  284. }
  285. /*
  286. handleASignCompleted 处理爱签合同签署完成后回调通知
  287. req 异步推送参数
  288. */
  289. func HandleASignCompleted(contractNo, status string) (err error) {
  290. /*
  291. // 合同签署完成后回调通知示例
  292. String publickey = "MFwwDQcccccxxxxmEz/nw27Ln6AP90ZCMPi+iNF1m9mhNECAwEAAQ==";
  293. String remark = ""; // 若被拒签则会返回拒签原因,拒签原因不参与签名
  294. Map <String, String> map = new HashMap<>();
  295. map.put("action", "signCompleted");
  296. map.put("contractNo", "20221114142140345");
  297. map.put("status", "2");
  298. map.put("signTime", "2022-11-14 14:22:00");
  299. map.put("timestamp", "1668406920005");
  300. map.put("validityTime", "2022-11-24 23:59:59");
  301. String json = JSONObject.toJSONString(map, SerializerFeature.MapSortField);
  302. System.out.println("数据:" + json);
  303. // 计算签名
  304. try {
  305. String sign = "feFfcprGjdmDDqRmxK5qlWlMncX0mc6LJ5agebOGIx2QiAern+6ZRg/SBHOgvHp/+1ywVRdyKNUKxPneETwKPw==";
  306. System.out.println(RSAUtils.rsaSignCheck(sign, json, publickey));
  307. } catch (Exception e) {
  308. e.printStackTrace();
  309. }
  310. */
  311. // 获取合同编号
  312. if contractNo == "" {
  313. global.M2A_LOG.Error("【HandleASignCompleted】 获取合同编号失败")
  314. return
  315. }
  316. // 获取合同状态
  317. if status == "" {
  318. global.M2A_LOG.Error("【HandleASignCompleted】 获取获取合同状态失败")
  319. return
  320. }
  321. // 下载合同并修改合同状态
  322. err = modifyContractStatus(contractNo, status)
  323. return
  324. }
  325. // modifyContractStatus 爱签异步推送和查询合同状态后调用
  326. // 注意传入的status是爱签的合同状态
  327. // 合同状态:
  328. // 0:等待签约
  329. // 1:签约中
  330. // 2:已签约
  331. // 3:过期
  332. // 4:拒签
  333. // 6:作废
  334. // -2:状态异常
  335. func modifyContractStatus(contractNo, status string) (err error) {
  336. if status == "2" {
  337. // 获取用户电子签记录
  338. useresignrecord := new(account.Useresignrecord)
  339. has, e := global.M2A_DB.Where("CONTRACTNO = ?", contractNo).Get(useresignrecord)
  340. if e != nil || !has {
  341. err = e
  342. global.M2A_LOG.Error("【modifyContractStatus】 获取用户电子签记录失败", zap.Error(err))
  343. return
  344. }
  345. if useresignrecord.RECORDSTATUS == 3 {
  346. // 已完成签署,不需要再下载和修改数据库状态
  347. return
  348. }
  349. // 已签约
  350. // 下载合同
  351. r, e := asign.DownloadContract(contractNo)
  352. if e != nil {
  353. err = e
  354. return
  355. }
  356. if r.Code != 100000 {
  357. err = errors.New(strconv.Itoa(r.Code))
  358. global.M2A_LOG.Error("【modifyContractStatus】 下载合同接口调用失败", zap.Error(err))
  359. return
  360. }
  361. if r.Data.Data != "" {
  362. // 获取网上开户地址(用于客户端下载文件)
  363. openconfig := account.Wskhopenaccountconfig{CONFIGID: 6}
  364. has, e := openconfig.Get()
  365. if e != nil || !has {
  366. err = e
  367. global.M2A_LOG.Error("【modifyContractStatus】 获取网上开户地址失败", zap.Error(err))
  368. return
  369. }
  370. // 将Base64写入目标文件
  371. uid, _ := uuid.NewV4()
  372. fileName := fmt.Sprintf("%v.pdf", uid.String())
  373. // openconfig.CONFIGVALUE = "./" // FIXME: - 测试代码
  374. folderPath := "sign/" + time.Now().Format("20060102")
  375. savePath := openconfig.CONFIGVALUE + "/uploadFile/" + folderPath
  376. if exist, _ := utils.PathExists(savePath); !exist {
  377. os.MkdirAll(savePath, os.ModePerm)
  378. }
  379. fb, e := base64.StdEncoding.DecodeString(r.Data.Data)
  380. if e != nil {
  381. err = e
  382. global.M2A_LOG.Error("【modifyContractStatus】 解码pdf文件失败", zap.Error(err))
  383. return
  384. }
  385. if err = os.WriteFile(savePath+"/"+fileName, fb, 0666); err != nil {
  386. global.M2A_LOG.Error("【modifyContractStatus】 保存合同失败", zap.Error(err))
  387. return
  388. }
  389. // 更新数据库记录
  390. contractfileaddr := fmt.Sprintf("./uploadFile/%v/%v", folderPath, fileName)
  391. sql := fmt.Sprintf(`
  392. UPDATE useresignrecord
  393. SET RECORDSTATUS = 3,
  394. UPDATETIME = SYSDATE,
  395. CONTRACTFILEADDR = '%v'
  396. WHERE CONTRACTNO = '%v'
  397. `, contractfileaddr, contractNo)
  398. if _, err = global.M2A_DB.Exec(sql); err != nil {
  399. global.M2A_LOG.Error("【modifyContractStatus】 更新用户电子签记录失败", zap.Error(err))
  400. return
  401. }
  402. }
  403. } else if status == "0" || status == "1" {
  404. err = fmt.Errorf("合同签署状态不正确, status: %v", status)
  405. return
  406. } else {
  407. // 其它状态一律改为4(签署拒绝)
  408. // 更新数据库记录
  409. sql := fmt.Sprintf(`
  410. UPDATE useresignrecord
  411. SET RECORDSTATUS = 4,
  412. UPDATETIME = SYSDATE
  413. WHERE CONTRACTNO = '%v'
  414. `, contractNo)
  415. if _, err = global.M2A_DB.Exec(sql); err != nil {
  416. global.M2A_LOG.Error("【HandleASignCompleted】 更新用户电子签记录失败", zap.Error(err))
  417. return
  418. }
  419. err = fmt.Errorf("合同签署状态不正确, status: %v", status)
  420. }
  421. return
  422. }
  423. // WillFace 个人意愿核身认证
  424. func WillFace(req request.WillFaceReq, userId int) (wrsp response.WillFaceRsp, err error) {
  425. // 获取用户电子签记录
  426. useresignrecord := new(account.Useresignrecord)
  427. has, err := global.M2A_DB.Where("USERID = ? AND TEMPLATETYPE = 3", userId).Get(useresignrecord)
  428. if err != nil || !has {
  429. global.M2A_LOG.Error("【WillFace】 获取用户电子签记录失败", zap.Error(err))
  430. return
  431. }
  432. if useresignrecord.RECORDSTATUS == 3 {
  433. err = errors.New("不可重复进行个人意愿核身认证")
  434. return
  435. }
  436. // 调用爱签API-个人意愿核身认证(https://{host}/auth/person/willFace)
  437. rsp, err := asign.WillFace(
  438. req.RealName,
  439. req.IdCardNo,
  440. useresignrecord.TEMPLATENO,
  441. useresignrecord.TEMPLATENAME,
  442. strconv.Itoa(int(useresignrecord.RECORDID)))
  443. if err != nil {
  444. return
  445. }
  446. if rsp.Code != 100000 {
  447. err = errors.New(strconv.Itoa(rsp.Code))
  448. global.M2A_LOG.Error("【WillFace】 接口调用失败", zap.Error(err))
  449. return
  450. }
  451. wrsp.FaceUrl = rsp.Data.FaceUrl
  452. // 更新用户电子签记录
  453. sql := fmt.Sprintf(`
  454. UPDATE useresignrecord
  455. SET RECORDSTATUS = 2,
  456. UPDATETIME = SYSDATE,
  457. SIGNURL = '%v'
  458. WHERE RECORDID = %v
  459. `, rsp.Data.FaceUrl, useresignrecord.RECORDID)
  460. if _, err = global.M2A_DB.Exec(sql); err != nil {
  461. global.M2A_LOG.Error("【WillFace】 更新用户电子签记录失败", zap.Error(err))
  462. return
  463. }
  464. return
  465. }
  466. // 个人意愿核身认证爱签回调通知处理
  467. func HandleWillFace(sign, result, msg, recordId string) (err error) {
  468. // 获取用户电子签记录
  469. useresignrecord := new(account.Useresignrecord)
  470. has, e := global.M2A_DB.Where("RECORDID = ?", recordId).Get(useresignrecord)
  471. if e != nil || !has {
  472. err = e
  473. global.M2A_LOG.Error("【HandleWillFace】 获取用户电子签记录失败", zap.Error(err))
  474. return
  475. }
  476. // 人脸认证结果
  477. // 1:认证成功
  478. // 2:认证失败
  479. if result == "1" {
  480. // FIXME: - 这里应该校验签名(姓名和身份证号码)
  481. // 更新数据库记录
  482. sql := fmt.Sprintf(`
  483. UPDATE useresignrecord
  484. SET RECORDSTATUS = 3,
  485. UPDATETIME = SYSDATE
  486. WHERE RECORDID = %v
  487. `, recordId)
  488. if _, err = global.M2A_DB.Exec(sql); err != nil {
  489. global.M2A_LOG.Error("【HandleWillFace】 更新用户电子签记录失败", zap.Error(err))
  490. return
  491. }
  492. }
  493. return
  494. }