package sign import ( "bytes" "encoding/base64" "encoding/json" "errors" "fmt" "io" "mtp20access/global" "mtp20access/model/account" "mtp20access/model/account/request" "mtp20access/model/account/response" "mtp20access/service/asign" "mtp20access/utils" "net/http" "os" "strconv" "time" "github.com/gofrs/uuid" "go.uber.org/zap" ) // QueryUserESignRecord 查询用户电子签记录表 func QueryUserESignRecord(userId int) (rsp []account.Useresignrecord, err error) { rsp = make([]account.Useresignrecord, 0) if err = global.M2A_DB.Where("USERID = ?", userId).Find(&rsp); err != nil { return } for i := range rsp { data := &rsp[i] if data.TEMPLATETYPE == 2 && data.RECORDSTATUS == 2 { // 去爱签同步合同状态 rspCTStatus, e := asign.ContractStatus(data.CONTRACTNO) if e != nil { err = e global.M2A_LOG.Error("【QueryUserESignRecord】 查询合同状态失败", zap.Error(err)) return } // 下载合同并修改合同状态 e = modifyContractStatus(data.CONTRACTNO, strconv.Itoa(rspCTStatus.Data.Status)) if e != nil { // 从新获取记录 recordId := data.RECORDID data = new(account.Useresignrecord) has, e := global.M2A_DB.Where("RECORDID = ?", recordId).Get(data) if e != nil || !has { err = e global.M2A_LOG.Error("【QueryUserESignRecord】 获取用户电子签记录失败", zap.Error(err)) return } } } } return } // AddUser 添加用户 func AddUser(req request.AddUserReq, userId int) (err error) { // 获取用户电子签记录 useresignrecord := new(account.Useresignrecord) has, _ := global.M2A_DB.Where("USERID = ? AND TEMPLATETYPE = 1 AND RECORDSTATUS = 3", userId).Get(useresignrecord) if has { err = errors.New("用户已同步") return } // 调用爱签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 AND TEMPLATETYPE = 1 `, string(authinfo), userId) if _, err = global.M2A_DB.Exec(sql); err != nil { global.M2A_LOG.Error("【AddUser】 更新用户电子签记录失败", zap.Error(err)) return } return } // CreateContractAndAddSigner 上传待签署文件和添加签署方 func CreateContractAndAddSigner(req request.CreateContractAndAddSignerReq, userId int) (rsp response.CreateContractAndAddSignerRsp, err error) { // 获取用户电子签记录 useresignrecord := new(account.Useresignrecord) has, err := global.M2A_DB.Where("USERID = ? AND TEMPLATENO = ?", userId, req.TemplateNo).Get(useresignrecord) if err != nil || !has { global.M2A_LOG.Error("【CreateContractAndAddSigner】 获取用户电子签记录失败", zap.Error(err)) return } // 判断是否需要创建合同(上传待签署文件) if useresignrecord.CONTRACTNO == "" { // 生成合同编号 // #{userid} || '_' || to_char(sysdate, 'yyyyMMddhh24miss') || '_' || seq_useresignrecord.currval, contractNo := fmt.Sprintf("%d_%s_%v", userId, time.Now().Format("20060102150405"), useresignrecord.RECORDID) // 调用爱签API-上传待签署文件(https://{host}/contract/createContract) r, e := asign.CreateContract( contractNo, useresignrecord.TEMPLATENAME, useresignrecord.TEMPLATENO, ) if e != nil { err = e return } if r.Code != 100000 { err = errors.New(strconv.Itoa(r.Code)) global.M2A_LOG.Error("【CreateContractAndAddSigner】 上传待签署文件接口调用失败", zap.Error(err)) return } // 将返回的合同编号写入数据库 useresignrecord.CONTRACTNO = contractNo sql := fmt.Sprintf(` UPDATE useresignrecord SET contractNo = '%v', UPDATETIME = SYSDATE WHERE RECORDID = %v `, useresignrecord.CONTRACTNO, useresignrecord.RECORDID) if _, err = global.M2A_DB.Exec(sql); err != nil { global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同编号失败", zap.Error(err)) return } } // 判断是否需要添加签署方(获取合同签约地址) if useresignrecord.SIGNURL != "" { err = errors.New("合同签署链接已存在") } else { // 调用爱签API-添加签署方(https://{host}/contract/addSigner) r, e := asign.AddSigner( useresignrecord.CONTRACTNO, strconv.Itoa(userId), ) if e != nil { err = e return } if r.Code != 100000 { err = errors.New(strconv.Itoa(r.Code)) global.M2A_LOG.Error("【CreateContractAndAddSigner】 添加签署方接口调用失败", zap.Error(err)) return } if len(r.Data.SignUser) > 0 { useresignrecord.SIGNURL = r.Data.SignUser[0].SignUrl // 将返回的合同编号写入数据库 sql := fmt.Sprintf(` UPDATE useresignrecord SET RECORDSTATUS = 2, SIGNURL = '%v', UPDATETIME = SYSDATE WHERE RECORDID = %v `, useresignrecord.SIGNURL, useresignrecord.RECORDID) if _, err = global.M2A_DB.Exec(sql); err != nil { global.M2A_LOG.Error("【CreateContractAndAddSigner】 写入合同签署链接失败", zap.Error(err)) return } // 给客户端返回合同签署地址 rsp.SignUrl = useresignrecord.SIGNURL } } return } // SignCompleted func SignCompleted(userId int) (err error) { // 获取用户电子签记录 datas := make([]account.Useresignrecord, 0) err = global.M2A_DB.Where("USERID = ?", userId).Find(&datas) if err != nil { global.M2A_LOG.Error("[SignCompleted] 获取用户电子签记录失败", zap.Error(err)) return } var record *account.Useresignrecord for i, item := range datas { if item.TEMPLATETYPE == 1 { record = &datas[i] } switch item.TEMPLATETYPE { case 1: // 实名认证 if item.RECORDSTATUS != 3 { err = errors.New("未完成用户添加") return } case 2: // 合同签署 // 如果是签署中状态,则查询一下合同状态 if item.RECORDSTATUS == 2 { rspCTStatus, e := asign.ContractStatus(item.CONTRACTNO) if e != nil { err = e global.M2A_LOG.Error("[SignCompleted] 查询合同状态失败", zap.Error(err)) return } // 下载合同并修改合同状态 err = modifyContractStatus(rspCTStatus.Data.ContractNo, strconv.Itoa(rspCTStatus.Data.Status)) return } else if (item.RECORDSTATUS != 3 && item.RECORDSTATUS != 2) && item.TEMPLATETYPE == 2 { err = errors.New("未完成所有合同签署") global.M2A_LOG.Error(err.Error()) return } case 3: // 个人意愿 if item.RECORDSTATUS != 3 { err = errors.New("未完成个人意愿核身认证") return } } } // 获取临时存储的用户信息 if record == nil || record.AUTHINFO == "" { global.M2A_LOG.Error("[SignCompleted] 获取实名认证信息失败", zap.Error(err)) return } // {"name":"甘肃碳交","idCard":"360428200007287603","idCardType":1,"idCardPhoto":"./uploadFile/20230812/202308121518494929.png","idCardPhotoBackURL":"./uploadFile/20230812/202308121518529223.png","mobile":"15914012151"} cacheMap := make(map[string]interface{}) err = json.Unmarshal([]byte(record.AUTHINFO), &cacheMap) if err != nil { global.M2A_LOG.Error("[SignCompleted] 反序列化临时存储用户信息失败", zap.Error(err)) return } // 调用JAVA实名认证接口 reqParam := make(map[string]interface{}) reqParam["userid"] = userId reqParam["cardnum"] = cacheMap["idCard"] reqParam["username"] = cacheMap["name"] reqParam["cardtype"] = 0 // 目前写死证件类型为身份证 - 0 reqParam["cardfrontphotourl"] = cacheMap["idCardPhoto"] reqParam["cardbackphotourl"] = cacheMap["idCardPhotoBackURL"] reqParam["userinfotype"] = 1 // 目前写死为个人 - 1 jsonParam, err := json.Marshal(&reqParam) if err != nil { global.M2A_LOG.Error("[SignCompleted] 反序列化JAVA实名认证入参失败", zap.Error(err)) return } // 构建请求 javaUrl := global.M2A_CONFIG.Asign.OpenApiUrl + "/onlineopen/userInfo/addAuth" req, err := http.NewRequest("POST", javaUrl, bytes.NewReader(jsonParam)) // 设置请求头 req.Header.Set("Content-Type", "application/json; charset=utf-8") client := &http.Client{} rsp, err := client.Do(req) if err != nil { global.M2A_LOG.Error("[SignCompleted] 请求失败", zap.Error(err)) return } defer rsp.Body.Close() body, err := io.ReadAll(rsp.Body) if err != nil { global.M2A_LOG.Error("[SignCompleted] 获取body失败", zap.Error(err)) return } // rspData: map[code:0 hasAuth:1 message:认证成功] rspData := make(map[string]interface{}) if err = json.Unmarshal(body, &rspData); err != nil { global.M2A_LOG.Error("[SignCompleted] 反序列化java body失败", zap.Error(err)) return } global.M2A_LOG.Info("[SignCompleted] 调用JAVA实名认证接口返回", zap.Any("rspData", rspData)) code, ok := rspData["code"] if !ok { err = errors.New("实名认证失败,请稍后重试") return } if code != "0" { err = fmt.Errorf("实名认证失败,%v", rspData["message"]) global.M2A_LOG.Error("[SignCompleted]", zap.Error(err)) return } return } /* handleASignCompleted 处理爱签合同签署完成后回调通知 req 异步推送参数 */ func HandleASignCompleted(contractNo, status string) (err error) { /* // 合同签署完成后回调通知示例 String publickey = "MFwwDQcccccxxxxmEz/nw27Ln6AP90ZCMPi+iNF1m9mhNECAwEAAQ=="; String remark = ""; // 若被拒签则会返回拒签原因,拒签原因不参与签名 Map map = new HashMap<>(); map.put("action", "signCompleted"); map.put("contractNo", "20221114142140345"); map.put("status", "2"); map.put("signTime", "2022-11-14 14:22:00"); map.put("timestamp", "1668406920005"); map.put("validityTime", "2022-11-24 23:59:59"); String json = JSONObject.toJSONString(map, SerializerFeature.MapSortField); System.out.println("数据:" + json); // 计算签名 try { String sign = "feFfcprGjdmDDqRmxK5qlWlMncX0mc6LJ5agebOGIx2QiAern+6ZRg/SBHOgvHp/+1ywVRdyKNUKxPneETwKPw=="; System.out.println(RSAUtils.rsaSignCheck(sign, json, publickey)); } catch (Exception e) { e.printStackTrace(); } */ // 获取合同编号 if contractNo == "" { global.M2A_LOG.Error("【HandleASignCompleted】 获取合同编号失败") return } // 获取合同状态 if status == "" { global.M2A_LOG.Error("【HandleASignCompleted】 获取获取合同状态失败") return } // 下载合同并修改合同状态 err = modifyContractStatus(contractNo, status) return } // modifyContractStatus 爱签异步推送和查询合同状态后调用 // 注意传入的status是爱签的合同状态 // 合同状态: // 0:等待签约 // 1:签约中 // 2:已签约 // 3:过期 // 4:拒签 // 6:作废 // -2:状态异常 func modifyContractStatus(contractNo, status string) (err error) { if status == "2" { // 获取用户电子签记录 useresignrecord := new(account.Useresignrecord) has, e := global.M2A_DB.Where("CONTRACTNO = ?", contractNo).Get(useresignrecord) if e != nil || !has { err = e global.M2A_LOG.Error("【modifyContractStatus】 获取用户电子签记录失败", zap.Error(err)) return } if useresignrecord.RECORDSTATUS == 3 { // 已完成签署,不需要再下载和修改数据库状态 return } // 已签约 // 下载合同 r, e := asign.DownloadContract(contractNo) if e != nil { err = e return } if r.Code != 100000 { err = errors.New(strconv.Itoa(r.Code)) global.M2A_LOG.Error("【modifyContractStatus】 下载合同接口调用失败", zap.Error(err)) return } if r.Data.Data != "" { // 获取网上开户地址(用于客户端下载文件) openconfig := account.Wskhopenaccountconfig{CONFIGID: 6} has, e := openconfig.Get() if e != nil || !has { err = e global.M2A_LOG.Error("【modifyContractStatus】 获取网上开户地址失败", zap.Error(err)) return } // 将Base64写入目标文件 uid, _ := uuid.NewV4() fileName := fmt.Sprintf("%v.pdf", uid.String()) // openconfig.CONFIGVALUE = "./" // FIXME: - 测试代码 folderPath := "sign/" + time.Now().Format("20060102") savePath := openconfig.CONFIGVALUE + "/uploadFile/" + folderPath if exist, _ := utils.PathExists(savePath); !exist { os.MkdirAll(savePath, os.ModePerm) } fb, e := base64.StdEncoding.DecodeString(r.Data.Data) if e != nil { err = e global.M2A_LOG.Error("【modifyContractStatus】 解码pdf文件失败", zap.Error(err)) return } if err = os.WriteFile(savePath+"/"+fileName, fb, 0666); err != nil { global.M2A_LOG.Error("【modifyContractStatus】 保存合同失败", zap.Error(err)) return } // 更新数据库记录 contractfileaddr := fmt.Sprintf("./uploadFile/%v/%v", folderPath, fileName) sql := fmt.Sprintf(` UPDATE useresignrecord SET RECORDSTATUS = 3, UPDATETIME = SYSDATE, CONTRACTFILEADDR = '%v' WHERE CONTRACTNO = '%v' `, contractfileaddr, contractNo) if _, err = global.M2A_DB.Exec(sql); err != nil { global.M2A_LOG.Error("【modifyContractStatus】 更新用户电子签记录失败", zap.Error(err)) return } } } else if status == "0" || status == "1" { err = fmt.Errorf("合同签署状态不正确, status: %v", status) return } else { // 其它状态一律改为4(签署拒绝) // 更新数据库记录 sql := fmt.Sprintf(` UPDATE useresignrecord SET RECORDSTATUS = 4, UPDATETIME = SYSDATE WHERE CONTRACTNO = '%v' `, contractNo) if _, err = global.M2A_DB.Exec(sql); err != nil { global.M2A_LOG.Error("【HandleASignCompleted】 更新用户电子签记录失败", zap.Error(err)) return } err = fmt.Errorf("合同签署状态不正确, status: %v", status) } return } // WillFace 个人意愿核身认证 func WillFace(req request.WillFaceReq, userId int) (wrsp response.WillFaceRsp, err error) { // 获取用户电子签记录 useresignrecord := new(account.Useresignrecord) has, err := global.M2A_DB.Where("USERID = ? AND TEMPLATETYPE = 3", userId).Get(useresignrecord) if err != nil || !has { global.M2A_LOG.Error("【WillFace】 获取用户电子签记录失败", zap.Error(err)) return } if useresignrecord.RECORDSTATUS == 3 { err = errors.New("不可重复进行个人意愿核身认证") return } // 调用爱签API-个人意愿核身认证(https://{host}/auth/person/willFace) rsp, err := asign.WillFace( req.RealName, req.IdCardNo, useresignrecord.TEMPLATENO, useresignrecord.TEMPLATENAME, strconv.Itoa(int(useresignrecord.RECORDID))) if err != nil { return } if rsp.Code != 100000 { err = errors.New(strconv.Itoa(rsp.Code)) global.M2A_LOG.Error("【WillFace】 接口调用失败", zap.Error(err)) return } wrsp.FaceUrl = rsp.Data.FaceUrl // 更新用户电子签记录 sql := fmt.Sprintf(` UPDATE useresignrecord SET RECORDSTATUS = 2, UPDATETIME = SYSDATE, SIGNURL = '%v' WHERE RECORDID = %v `, rsp.Data.FaceUrl, useresignrecord.RECORDID) if _, err = global.M2A_DB.Exec(sql); err != nil { global.M2A_LOG.Error("【WillFace】 更新用户电子签记录失败", zap.Error(err)) return } return } // 个人意愿核身认证爱签回调通知处理 func HandleWillFace(sign, result, msg, recordId string) (err error) { // 获取用户电子签记录 useresignrecord := new(account.Useresignrecord) has, e := global.M2A_DB.Where("RECORDID = ?", recordId).Get(useresignrecord) if e != nil || !has { err = e global.M2A_LOG.Error("【HandleWillFace】 获取用户电子签记录失败", zap.Error(err)) return } // 人脸认证结果 // 1:认证成功 // 2:认证失败 if result == "1" { // FIXME: - 这里应该校验签名(姓名和身份证号码) // 更新数据库记录 sql := fmt.Sprintf(` UPDATE useresignrecord SET RECORDSTATUS = 3, UPDATETIME = SYSDATE WHERE RECORDID = %v `, recordId) if _, err = global.M2A_DB.Exec(sql); err != nil { global.M2A_LOG.Error("【HandleWillFace】 更新用户电子签记录失败", zap.Error(err)) return } } return }