2025-07-20 20:53:26 +08:00
|
|
|
|
package esign
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// SignFlowService 签署流程服务
|
|
|
|
|
|
// 处理签署流程创建、链接获取等操作
|
|
|
|
|
|
type SignFlowService struct {
|
|
|
|
|
|
httpClient *HTTPClient
|
|
|
|
|
|
config *Config
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewSignFlowService 创建签署流程服务
|
|
|
|
|
|
func NewSignFlowService(httpClient *HTTPClient, config *Config) *SignFlowService {
|
|
|
|
|
|
return &SignFlowService{
|
|
|
|
|
|
httpClient: httpClient,
|
|
|
|
|
|
config: config,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateConfig 更新配置
|
|
|
|
|
|
func (s *SignFlowService) UpdateConfig(config *Config) {
|
|
|
|
|
|
s.config = config
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create 创建签署流程
|
|
|
|
|
|
// 创建包含多个签署人的签署流程,支持自动盖章和手动签署
|
|
|
|
|
|
func (s *SignFlowService) Create(req *CreateSignFlowRequest) (string, error) {
|
|
|
|
|
|
fmt.Println("开始创建签署流程...")
|
|
|
|
|
|
fmt.Println("(将创建包含甲方自动盖章和乙方手动签署的流程)")
|
|
|
|
|
|
|
|
|
|
|
|
// 构建甲方签署人信息(自动盖章)
|
|
|
|
|
|
partyASigner := s.buildPartyASigner(req.FileID)
|
|
|
|
|
|
|
|
|
|
|
|
// 构建乙方签署人信息(手动签署)
|
|
|
|
|
|
partyBSigner := s.buildPartyBSigner(req.FileID, req.SignerAccount, req.SignerName, req.TransactorPhone, req.TransactorName, req.TransactorIDCardNum)
|
|
|
|
|
|
|
|
|
|
|
|
signers := []SignerInfo{partyASigner, partyBSigner}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建请求数据
|
|
|
|
|
|
requestData := CreateSignFlowByFileRequest{
|
|
|
|
|
|
Docs: []DocInfo{
|
|
|
|
|
|
{
|
|
|
|
|
|
FileId: req.FileID,
|
2025-07-28 01:46:39 +08:00
|
|
|
|
FileName: s.config.Contract.Name,
|
2025-07-20 20:53:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
SignFlowConfig: s.buildSignFlowConfig(),
|
|
|
|
|
|
Signers: signers,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 序列化请求数据
|
|
|
|
|
|
jsonData, err := MarshalRequest(requestData)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Printf("发起签署请求数据: %s\n", string(jsonData))
|
|
|
|
|
|
|
|
|
|
|
|
// 发送API请求
|
|
|
|
|
|
responseBody, err := s.httpClient.Request("POST", "/v3/sign-flow/create-by-file", jsonData)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", fmt.Errorf("发起签署失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析响应
|
|
|
|
|
|
var response CreateSignFlowByFileResponse
|
|
|
|
|
|
if err := UnmarshalResponse(responseBody, &response); err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := CheckResponseCode(response.Code, response.Message); err != nil {
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Printf("签署流程创建成功,流程ID: %s\n", response.Data.SignFlowId)
|
|
|
|
|
|
return response.Data.SignFlowId, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetSignURL 获取签署页面链接
|
|
|
|
|
|
// 为指定的签署人获取签署页面链接
|
|
|
|
|
|
func (s *SignFlowService) GetSignURL(signFlowID, psnAccount, orgName string) (string, string, error) {
|
|
|
|
|
|
fmt.Println("开始获取签署页面链接...")
|
|
|
|
|
|
|
|
|
|
|
|
// 构建请求数据
|
|
|
|
|
|
requestData := GetSignUrlRequest{
|
|
|
|
|
|
NeedLogin: false,
|
|
|
|
|
|
UrlType: UrlTypeSign,
|
|
|
|
|
|
Operator: &Operator{
|
|
|
|
|
|
PsnAccount: psnAccount,
|
|
|
|
|
|
},
|
|
|
|
|
|
Organization: &Organization{
|
|
|
|
|
|
OrgName: orgName,
|
|
|
|
|
|
},
|
|
|
|
|
|
ClientType: ClientTypeAll,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 序列化请求数据
|
|
|
|
|
|
jsonData, err := MarshalRequest(requestData)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Printf("获取签署页面链接请求数据: %s\n", string(jsonData))
|
|
|
|
|
|
|
|
|
|
|
|
// 发送API请求
|
|
|
|
|
|
urlPath := fmt.Sprintf("/v3/sign-flow/%s/sign-url", signFlowID)
|
|
|
|
|
|
responseBody, err := s.httpClient.Request("POST", urlPath, jsonData)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return "", "", fmt.Errorf("获取签署页面链接失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析响应
|
|
|
|
|
|
var response GetSignUrlResponse
|
|
|
|
|
|
if err := UnmarshalResponse(responseBody, &response); err != nil {
|
|
|
|
|
|
return "", "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := CheckResponseCode(response.Code, response.Message); err != nil {
|
|
|
|
|
|
return "", "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fmt.Printf("签署页面链接获取成功!\n")
|
|
|
|
|
|
fmt.Printf("完整链接: %s\n", response.Data.Url)
|
|
|
|
|
|
fmt.Printf("短链接: %s\n", response.Data.ShortUrl)
|
|
|
|
|
|
|
|
|
|
|
|
return response.Data.Url, response.Data.ShortUrl, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// buildPartyASigner 构建甲方签署人信息(自动盖章)
|
|
|
|
|
|
func (s *SignFlowService) buildPartyASigner(fileID string) SignerInfo {
|
|
|
|
|
|
return SignerInfo{
|
|
|
|
|
|
SignConfig: &SignConfig{SignOrder: 1},
|
|
|
|
|
|
SignerType: SignerTypeOrg,
|
|
|
|
|
|
SignFields: []SignField{
|
|
|
|
|
|
{
|
|
|
|
|
|
CustomBizNum: "甲方签章",
|
|
|
|
|
|
FileId: fileID,
|
|
|
|
|
|
NormalSignFieldConfig: &NormalSignFieldConfig{
|
|
|
|
|
|
AutoSign: true,
|
|
|
|
|
|
SignFieldStyle: SignFieldStyleNormal,
|
|
|
|
|
|
SignFieldPosition: &SignFieldPosition{
|
2025-07-28 01:46:39 +08:00
|
|
|
|
PositionPage: "8",
|
2025-07-20 20:53:26 +08:00
|
|
|
|
PositionX: 200,
|
2025-07-28 01:46:39 +08:00
|
|
|
|
PositionY: 430,
|
2025-07-20 20:53:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// buildPartyBSigner 构建乙方签署人信息(手动签署)
|
|
|
|
|
|
func (s *SignFlowService) buildPartyBSigner(fileID, signerAccount, signerName, transactorPhone, transactorName, transactorIDCardNum string) SignerInfo {
|
|
|
|
|
|
return SignerInfo{
|
|
|
|
|
|
SignConfig: &SignConfig{
|
|
|
|
|
|
SignOrder: 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
AuthConfig: &AuthConfig{
|
|
|
|
|
|
PsnAvailableAuthModes: []string{AuthModeMobile3},
|
|
|
|
|
|
WillingnessAuthModes: []string{WillingnessAuthSMS},
|
|
|
|
|
|
},
|
|
|
|
|
|
SignerType: SignerTypeOrg,
|
|
|
|
|
|
OrgSignerInfo: &OrgSignerInfo{
|
|
|
|
|
|
OrgName: signerName,
|
|
|
|
|
|
OrgInfo: &OrgInfo{
|
|
|
|
|
|
LegalRepName: transactorName,
|
|
|
|
|
|
LegalRepIDCardNum: transactorIDCardNum,
|
|
|
|
|
|
LegalRepIDCardType: IDCardTypeChina,
|
|
|
|
|
|
OrgIDCardNum: signerAccount,
|
|
|
|
|
|
OrgIDCardType: OrgCardTypeUSCC,
|
|
|
|
|
|
},
|
|
|
|
|
|
TransactorInfo: &TransactorInfo{
|
|
|
|
|
|
PsnAccount: transactorPhone,
|
|
|
|
|
|
PsnInfo: &PsnInfo{
|
|
|
|
|
|
PsnName: transactorName,
|
|
|
|
|
|
PsnIDCardNum: transactorIDCardNum,
|
|
|
|
|
|
PsnIDCardType: IDCardTypeChina,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
SignFields: []SignField{
|
|
|
|
|
|
{
|
|
|
|
|
|
CustomBizNum: "乙方签章",
|
|
|
|
|
|
FileId: fileID,
|
|
|
|
|
|
NormalSignFieldConfig: &NormalSignFieldConfig{
|
|
|
|
|
|
AutoSign: false,
|
|
|
|
|
|
SignFieldStyle: SignFieldStyleNormal,
|
|
|
|
|
|
SignFieldPosition: &SignFieldPosition{
|
2025-07-28 01:46:39 +08:00
|
|
|
|
PositionPage: "8",
|
|
|
|
|
|
PositionX: 450,
|
|
|
|
|
|
PositionY: 430,
|
2025-07-20 20:53:26 +08:00
|
|
|
|
},
|
2025-07-28 22:35:13 +08:00
|
|
|
|
OrgSealBizTypes: "PUBLIC",
|
2025-07-20 20:53:26 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// buildSignFlowConfig 构建签署流程配置
|
|
|
|
|
|
func (s *SignFlowService) buildSignFlowConfig() SignFlowConfig {
|
|
|
|
|
|
return SignFlowConfig{
|
2025-07-28 01:46:39 +08:00
|
|
|
|
SignFlowTitle: s.config.Contract.Name,
|
|
|
|
|
|
SignFlowExpireTime: calculateExpireTime(s.config.Contract.ExpireDays),
|
|
|
|
|
|
AutoFinish: s.config.Sign.AutoFinish,
|
2025-07-20 20:53:26 +08:00
|
|
|
|
AuthConfig: &AuthConfig{
|
|
|
|
|
|
PsnAvailableAuthModes: []string{AuthModeMobile3},
|
|
|
|
|
|
WillingnessAuthModes: []string{WillingnessAuthSMS},
|
|
|
|
|
|
},
|
|
|
|
|
|
ContractConfig: &ContractConfig{
|
|
|
|
|
|
AllowToRescind: false,
|
|
|
|
|
|
},
|
2025-07-28 01:46:39 +08:00
|
|
|
|
RedirectConfig: &RedirectConfig{
|
|
|
|
|
|
RedirectUrl: s.config.Sign.RedirectUrl,
|
|
|
|
|
|
},
|
2025-07-20 20:53:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|