You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

483 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package tencent
import (
common2 "base/app/common"
"encoding/base64"
errors_ "errors"
"fmt"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
essbasic "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/essbasic/v20210526"
"io"
"io/ioutil"
"log"
"net/http"
"regexp"
"time"
)
var Ess = &ess{}
type ess struct {
secretId string
secretKey string
appid string
organizationName string
proxyAppId string
proxyOrganizationOpenId string
proxyOperatorOpenId string
endPoint string
fileServiceEndPoint string
callbackUrl string
client *essbasic.Client
fileClient *essbasic.Client
agent *essbasic.Agent
}
// InitEss @Title 初始化ess-client
func InitEss(secretId, secretKey, appid, organizationName, proxyAppId, proxyOrganizationOpenId, proxyOperatorOpenId, endpoint, fileServiceEndPoint, callbackUrl string) (err error) {
credential := common.NewCredential(
secretId,
secretKey,
)
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = endpoint
// 实例化要请求产品的client对象,clientProfile是可选的
Ess.client, err = essbasic.NewClient(credential, "", cpf)
if err != nil {
return
}
fileCpf := profile.NewClientProfile()
fileCpf.HttpProfile.Endpoint = fileServiceEndPoint
Ess.fileClient, err = essbasic.NewClient(credential, "", fileCpf)
if err != nil {
return
}
Ess.agent = &essbasic.Agent{
AppId: &appid,
ProxyAppId: &proxyAppId,
ProxyOrganizationOpenId: &proxyOrganizationOpenId,
ProxyOperator: &essbasic.UserInfo{
OpenId: &proxyOperatorOpenId,
},
}
Ess.secretId = secretId
Ess.secretKey = secretKey
Ess.appid = appid
Ess.organizationName = organizationName
Ess.proxyAppId = proxyAppId
Ess.proxyOrganizationOpenId = proxyOrganizationOpenId
Ess.proxyOperatorOpenId = proxyOperatorOpenId
Ess.endPoint = endpoint
Ess.fileServiceEndPoint = fileServiceEndPoint
Ess.callbackUrl = callbackUrl
return
}
// 生成控制台链接
func (e *ess) CreateConsoleLoginUrl(organizationName, operatorName string) (url string, err error) {
var proxyOrganizationOpenId, proxyOperatorOpenId string
// 如果传入了企业和子客企业员信息,则为新建子客
if organizationName != "" {
proxyOrganizationOpenId = common2.MD5(fmt.Sprintf("%d%s", time.Now().UnixNano(), organizationName))
} else {
organizationName = e.organizationName
proxyOrganizationOpenId = e.proxyOrganizationOpenId
}
if operatorName != "" {
proxyOperatorOpenId = common2.MD5(fmt.Sprintf("%d%s", time.Now().UnixNano(), operatorName))
//proxyOperatorOpenId = "yDRGiUUqb8vqkUye7Sy4yo9oWeoFkK23"
} else {
proxyOperatorOpenId = e.proxyOperatorOpenId
}
request := essbasic.NewCreateConsoleLoginUrlRequest()
request.Agent = &essbasic.Agent{
AppId: &e.appid,
ProxyOrganizationOpenId: &proxyOrganizationOpenId,
ProxyOperator: &essbasic.UserInfo{
OpenId: &proxyOperatorOpenId,
},
}
request.ProxyOrganizationName = &organizationName
response, err := e.client.CreateConsoleLoginUrl(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
return url, err
}
re, _ := regexp.Compile("\u0026")
data := re.ReplaceAllString(*response.Response.ConsoleUrl, "&")
return data, nil
}
type UploadFileStruct struct {
FileUrl string
FileName string
}
// 文件上传
func (e *ess) UploadFile(files []UploadFileStruct) (fileIds []*string, err error) {
var uploadFiles []*essbasic.UploadFile
for _, file := range files {
var imgBase64 string
if imgBase64, err = GetUrlImgBase64(file.FileUrl); err != nil {
fmt.Printf("\r\n上传文件错误————%s\r\n", err.Error())
return fileIds, err
}
uploadFiles = append(uploadFiles, &essbasic.UploadFile{
FileBody: &imgBase64,
FileName: &file.FileName,
})
}
// 上传文件获取fileId
var response *essbasic.UploadFilesResponse
if response, err = e.UploadFiles(uploadFiles); err != nil {
return fileIds, err
}
return response.Response.FileIds, nil
}
// 使用多个模板批量创建签署流程
func (e *ess) CreateFlowsByTemplates(formFields []*essbasic.FormField, flowApproverInfos []*essbasic.FlowApproverInfo, customerData, flowName, templateId string) (flowIds []*string, err error) {
FlowType := "合同"
request := essbasic.NewCreateFlowsByTemplatesRequest()
request.Agent = e.agent
flowInfo := &essbasic.FlowInfo{
TemplateId: &templateId,
FlowName: &flowName,
FlowApprovers: flowApproverInfos,
FlowType: &FlowType,
FormFields: formFields,
CustomerData: &customerData,
CallbackUrl: &e.callbackUrl,
}
var flowInfos []*essbasic.FlowInfo
flowInfos = append(flowInfos, flowInfo)
request.FlowInfos = flowInfos
// 返回的resp是一个CreateFlowsByTemplatesResponse的实例与请求对象对应
response, err := e.client.CreateFlowsByTemplates(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
return nil, errors_.New("CreateFlowsByTemplates出错:" + err.Error())
}
// 输出json格式的字符串回包
fmt.Printf("\r\n模版创建合同返回值————%s\r\n", response.ToJsonString())
if response.Response == nil {
return nil, errors_.New("ess模版创建合同无响应")
}
hasErr, errText := parseEssErrMsg(response.Response.ErrorMessages)
if hasErr {
return nil, errors_.New("模版创建合同错误:" + errText)
}
flowIds = response.Response.FlowIds
return
}
// 获取跳转小程序查看或签署链接
func (e *ess) CreateSignUrls(endPoint uint, flowIds []*string) (signUrlInfos []*essbasic.SignUrlInfo, err error) {
request := essbasic.NewCreateSignUrlsRequest()
request.Agent = e.agent
request.FlowIds = flowIds
var Endpoint string
switch endPoint {
case 1: // 短链直接跳小程序
Endpoint = "WEIXINAPP"
case 2: // 跳转H5页面
Endpoint = "CHANNEL"
case 3: // 第三方APP或小程序跳转电子签小程序
Endpoint = "APP"
case 4: // 长链接跳转小程序
Endpoint = "LONGURL2WEIXINAPP"
default:
Endpoint = "WEIXINAPP"
}
request.Endpoint = &Endpoint
response, err := e.client.CreateSignUrls(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
return nil, err
}
fmt.Printf("\r\n查看签署链接返回————%s\r\n", response.ToJsonString())
if response.Response == nil {
return nil, errors_.New("ess获取签署链接无响应")
}
hasErr, errText := parseEssErrMsg(response.Response.ErrorMessages)
if response.Response != nil && len(response.Response.SignUrlInfos) != 0 {
signUrlInfos = response.Response.SignUrlInfos
}
if hasErr {
return signUrlInfos, errors_.New("获取签署链接错误:" + errText)
}
return
}
// 渠道版撤销签署流程
func (e *ess) ChannelCancelFlow(flowIds []*string) error {
request := essbasic.NewChannelBatchCancelFlowsRequest()
request.Agent = e.agent
request.FlowIds = flowIds
response, err := e.client.ChannelBatchCancelFlows(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
return err
}
fmt.Printf("\r\n渠道版撤销签署流程————%s\r\n", response.ToJsonString())
if response.Response == nil {
return errors_.New("ess撤销签署流程无响应")
}
return nil
}
// 渠道通过图片为子客代创建印章
func (e *ess) CreateSealByImage(proxyOrganizationOpenId, proxyOperatorOpenId, sealName, sealImage string) (sealId string, err error) {
request := essbasic.NewCreateSealByImageRequest()
request.Agent = &essbasic.Agent{
AppId: &e.appid,
ProxyAppId: &e.proxyAppId,
ProxyOrganizationOpenId: &proxyOrganizationOpenId,
ProxyOperator: &essbasic.UserInfo{
OpenId: &proxyOperatorOpenId,
},
}
request.SealName = &sealName
request.SealImage = &sealImage
response, err := e.client.CreateSealByImage(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
return sealId, err
}
fmt.Printf("\r\n%s\r\n", response.ToJsonString())
if response.Response == nil {
return sealId, errors_.New("ess创建印章无响应")
}
sealId = *response.Response.SealId
return
}
//根据签署流程信息批量获取资源下载链接
func (e *ess) DescribeResourceUrlsByFlows(flowIds []*string) (flowResourceUrlInfos []*essbasic.FlowResourceUrlInfo, err error) {
request := essbasic.NewDescribeResourceUrlsByFlowsRequest()
request.Agent = e.agent
request.FlowIds = flowIds
if len(flowIds) > 50 {
return nil, errors_.New("flow数量不可超过50个")
}
response, err := e.client.DescribeResourceUrlsByFlows(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
return nil, err
}
fmt.Printf("\r\n%s\r\n", response.ToJsonString())
if response.Response == nil {
return nil, errors_.New("ess获取资源下载链接无响应")
}
hasErr, errText := parseEssErrMsg(response.Response.ErrorMessages)
if hasErr {
return nil, errors_.New("获取合同下载链接错误:" + errText)
}
flowResourceUrlInfos = response.Response.FlowResourceUrlInfos
return
}
// 获取单个合同的下载链接
func (e *ess) GetUrlByFlowId(flowId string) (url, fileName string, err error) {
var flowIds []*string
flowIds = append(flowIds, &flowId)
flowResourceUrlInfos, err := e.DescribeResourceUrlsByFlows(flowIds)
if err != nil {
return
}
if len(flowResourceUrlInfos) == 0 {
err = errors_.New("流程对应资源链接信息数组不存在")
return
}
info := flowResourceUrlInfos[0].ResourceUrlInfos
if len(info) == 0 {
err = errors_.New("资源链接信息不存在")
return
}
url = *info[0].Url
fileName = *info[0].Name + flowId + "." + *info[0].Type
return
}
func (e *ess) GetRecipients(templateId string) (recipients []*essbasic.Recipient, err error) {
request := essbasic.NewDescribeTemplatesRequest()
request.Agent = e.agent
request.TemplateId = &templateId
response, err := e.client.DescribeTemplates(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
return nil, errors_.New("GetRecipients出错:" + err.Error())
}
if err != nil {
return nil, err
}
log.Println(response.ToJsonString())
return response.Response.Templates[0].Recipients, nil
}
// 签署人类型PERSON-个人;
// ORGANIZATION-企业;
// ENTERPRISESERVER-企业静默签;
// 注ENTERPRISESERVER 类型仅用于使用文件创建流程ChannelCreateFlowByFiles接口并且仅能指定发起方企业签署方为静默签署
// BuildPersonApprover 打包个人签署方参与者信息
func BuildPersonApprover(name, mobile, recipient string) *essbasic.FlowApproverInfo {
flowApproverInfo := &essbasic.FlowApproverInfo{}
approverType := "PERSON"
flowApproverInfo.ApproverType = &approverType
flowApproverInfo.Name = &name
flowApproverInfo.Mobile = &mobile
flowApproverInfo.RecipientId = &recipient
return flowApproverInfo
}
// BuildOrganizationApprover 打包企业签署方参与者信息
func BuildOrganizationApprover(organizationName, organizationOpenId, openId, recipient string) *essbasic.FlowApproverInfo {
flowApproverInfo := &essbasic.FlowApproverInfo{}
approverType := "ORGANIZATION"
flowApproverInfo.ApproverType = &approverType
flowApproverInfo.OrganizationName = &organizationName
flowApproverInfo.OrganizationOpenId = &organizationOpenId
flowApproverInfo.OpenId = &openId
flowApproverInfo.RecipientId = &recipient
return flowApproverInfo
}
// BuildNotChannelOrganizationApprover 打包渠道外企业签署方参与者信息
func BuildNotChannelOrganizationApprover(name, mobile, organizationName, recipient string) *essbasic.FlowApproverInfo {
flowApproverInfo := &essbasic.FlowApproverInfo{}
approverType := "ORGANIZATION"
notChannelOrganization := true
flowApproverInfo.Name = &name
flowApproverInfo.Mobile = &mobile
flowApproverInfo.ApproverType = &approverType
flowApproverInfo.OrganizationName = &organizationName
flowApproverInfo.NotChannelOrganization = &notChannelOrganization
flowApproverInfo.RecipientId = &recipient
return flowApproverInfo
}
// BuildServerSignApprover 打包企业静默签署方参与者信息
func BuildServerSignApprover() *essbasic.FlowApproverInfo {
flowApproverInfo := &essbasic.FlowApproverInfo{}
approverType := "ENTERPRISESERVER"
flowApproverInfo.ApproverType = &approverType
return flowApproverInfo
}
// BuildComponent 构建(签署)控件信息
func BuildComponent(componentPosX, componentPosY, componentWidth, componentHeight float64,
fileIndex, componentPage int64, componentType, componentValue string) *essbasic.Component {
var component = essbasic.Component{
// 参数控件X位置单位px
ComponentPosX: &componentPosX,
// 参数控件Y位置单位px
ComponentPosY: &componentPosY,
// 参数控件宽度默认100单位px表单域和关键字转换控件不用填
ComponentWidth: &componentWidth,
// 参数控件高度默认100单位px表单域和关键字转换控件不用填
ComponentHeight: &componentHeight,
// 控件所属文件的序号 (文档中文件的排列序号从0开始)
FileIndex: &fileIndex,
// 参数控件所在页码从1开始
ComponentPage: &componentPage,
// 如果是Component控件类型则可选的字段为
//TEXT - 普通文本控件;
//DATE - 普通日期控件跟TEXT相比会有校验逻辑
//DYNAMIC_TABLE- 动态表格控件
//如果是SignComponent控件类型则可选的字段为
//SIGN_SEAL - 签署印章控件;
//SIGN_DATE - 签署日期控件;
//SIGN_SIGNATURE - 用户签名控件;
//SIGN_PERSONAL_SEAL - 个人签署印章控件;
//表单域的控件不能作为印章和签名控件
ComponentType: &componentType,
// 印章 ID传参 DEFAULT_COMPANY_SEAL 表示使用默认印章。
// 控件填入内容印章控件里面如果是手写签名内容为PNG图片格式的base64编码。
ComponentValue: &componentValue,
}
return &component
}
func BuildFormFieldById(componentId, componentValue string) *essbasic.FormField {
return &essbasic.FormField{
ComponentId: common.StringPtr(componentId),
ComponentValue: common.StringPtr(componentValue),
}
}
func BuildFormFieldByName(componentName, componentValue string) *essbasic.FormField {
return &essbasic.FormField{
ComponentName: common.StringPtr(componentName),
ComponentValue: common.StringPtr(componentValue),
}
}
// 网络图片转base64
func GetUrlImgBase64(path string) (baseImg string, err error) {
//获取网络图片
client := &http.Client{
Timeout: time.Second * 5, //超时时间
}
var bodyImg io.Reader
request, err := http.NewRequest("GET", path, bodyImg)
if err != nil {
return "", err
}
respImg, err := client.Do(request)
if err != nil {
return "", err
}
defer respImg.Body.Close()
imgByte, err := ioutil.ReadAll(respImg.Body)
if err != nil {
return "", err
}
//// 判断文件类型生成一个前缀拼接base64后可以直接粘贴到浏览器打开不需要可以不用下面代码
////取图片类型
//mimeType := http.DetectContentType(imgByte)
//switch mimeType {
//case "image/jpeg":
// baseImg = "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(imgByte)
//case "image/png":
// baseImg = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgByte)
//}
baseImg = base64.StdEncoding.EncodeToString(imgByte)
return
}
// ess上传文件
func (e *ess) UploadFiles(uploadFiles []*essbasic.UploadFile) (response *essbasic.UploadFilesResponse, err error) {
request := essbasic.NewUploadFilesRequest()
request.Agent = e.agent
// 上传文件内容数组
request.FileInfos = uploadFiles
// 1. TEMPLATE - 模板; 文件类型:.pdf
// 2. DOCUMENT - 签署过程及签署后的合同文档/图片控件 文件类型:.pdf/.jpg/.png
BusinessType := "DOCUMENT"
request.BusinessType = &BusinessType
// 返回的resp是一个UploadFilesResponse的实例与请求对象对应
response, err = e.fileClient.UploadFiles(request)
if _, ok := err.(*errors.TencentCloudSDKError); ok {
fmt.Printf("An API error has returned: %s", err)
return response, err
}
if err != nil {
return response, err
}
return response, nil
}
// 返回ess的Response中errorMassage内容
func parseEssErrMsg(errMsgs []*string) (hasErr bool, errText string) {
if len(errMsgs) > 0 && *errMsgs[0] != "" {
for _, v := range errMsgs {
errText = errText + *v + ";"
}
return true, errText
}
return false, ""
}