temp
This commit is contained in:
293
internal/shared/esign/README.md
Normal file
293
internal/shared/esign/README.md
Normal file
@@ -0,0 +1,293 @@
|
||||
# e签宝 SDK - 重构版本
|
||||
|
||||
这是重构后的e签宝Go SDK,提供了更清晰、更易用的API接口。
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 主要组件
|
||||
|
||||
1. **Client (client.go)** - 统一的客户端入口
|
||||
2. **Config (config.go)** - 配置管理
|
||||
3. **HTTPClient (http.go)** - HTTP请求处理
|
||||
4. **服务模块**:
|
||||
- **TemplateService** - 模板操作服务
|
||||
- **SignFlowService** - 签署流程服务
|
||||
- **OrgAuthService** - 机构认证服务
|
||||
- **FileOpsService** - 文件操作服务
|
||||
|
||||
### 设计特点
|
||||
|
||||
- ✅ **模块化设计**:功能按模块分离,职责清晰
|
||||
- ✅ **统一入口**:通过Client提供统一的API
|
||||
- ✅ **易于使用**:提供高级业务接口和底层操作接口
|
||||
- ✅ **配置管理**:集中的配置验证和管理
|
||||
- ✅ **错误处理**:统一的错误处理和响应验证
|
||||
- ✅ **类型安全**:完整的类型定义和结构体
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 创建客户端
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/your-org/tyapi-server-gin/internal/shared/esign"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建配置
|
||||
config, err := esign.NewConfig(
|
||||
"your_app_id",
|
||||
"your_app_secret",
|
||||
"https://smlopenapi.esign.cn",
|
||||
"your_template_id",
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 创建客户端
|
||||
client := esign.NewClient(config)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 基础用法 - 一键合同签署
|
||||
|
||||
```go
|
||||
// 最简单的合同签署
|
||||
result, err := client.GenerateContractSigning(&esign.ContractSigningRequest{
|
||||
CompanyName: "我的公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
LegalPersonPhone: "13800138000",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("签署失败:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("请访问链接进行签署: %s\n", result.SignURL)
|
||||
```
|
||||
|
||||
### 3. 企业认证
|
||||
|
||||
```go
|
||||
// 企业认证
|
||||
authResult, err := client.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
CompanyName: "我的公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
TransactorName: "李四",
|
||||
TransactorPhone: "13800138001",
|
||||
TransactorID: "123456789012345679",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("企业认证失败:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("请访问链接进行企业认证: %s\n", authResult.AuthURL)
|
||||
```
|
||||
|
||||
## 高级用法
|
||||
|
||||
### 分步操作
|
||||
|
||||
如果需要更精细的控制,可以使用分步操作:
|
||||
|
||||
```go
|
||||
// 1. 填写模板
|
||||
templateData := map[string]string{
|
||||
"JFQY": "甲方公司",
|
||||
"JFFR": "甲方法人",
|
||||
"YFQY": "乙方公司",
|
||||
"YFFR": "乙方法人",
|
||||
"QDRQ": "2024年01月01日",
|
||||
}
|
||||
|
||||
fileID, err := client.FillTemplate(templateData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 创建签署流程
|
||||
signFlowReq := &esign.CreateSignFlowRequest{
|
||||
FileID: fileID,
|
||||
SignerAccount: "123456789012345678",
|
||||
SignerName: "乙方公司",
|
||||
TransactorPhone: "13800138000",
|
||||
TransactorName: "乙方法人",
|
||||
TransactorIDCardNum: "123456789012345678",
|
||||
TransactorMobile: "13800138000",
|
||||
}
|
||||
|
||||
signFlowID, err := client.CreateSignFlow(signFlowReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 获取签署链接
|
||||
signURL, shortURL, err := client.GetSignURL(signFlowID, "13800138000", "乙方公司")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. 查询签署状态
|
||||
status, err := client.GetSignFlowStatus(signFlowID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. 检查是否完成
|
||||
completed, err := client.IsSignFlowCompleted(signFlowID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义模板数据
|
||||
|
||||
```go
|
||||
customData := map[string]string{
|
||||
"custom_field_1": "自定义值1",
|
||||
"custom_field_2": "自定义值2",
|
||||
"contract_date": "2024年01月01日",
|
||||
}
|
||||
|
||||
result, err := client.GenerateContractSigning(&esign.ContractSigningRequest{
|
||||
CompanyName: "我的公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
LegalPersonPhone: "13800138000",
|
||||
CustomData: customData, // 使用自定义数据
|
||||
})
|
||||
```
|
||||
|
||||
## API参考
|
||||
|
||||
### 主要接口
|
||||
|
||||
#### 合同签署
|
||||
- `GenerateContractSigning(req *ContractSigningRequest) (*ContractSigningResult, error)` - 一键生成合同签署
|
||||
|
||||
#### 企业认证
|
||||
- `GenerateEnterpriseAuth(req *EnterpriseAuthRequest) (*EnterpriseAuthResult, error)` - 一键企业认证
|
||||
|
||||
#### 模板操作
|
||||
- `FillTemplate(components map[string]string) (string, error)` - 填写模板
|
||||
- `FillTemplateWithDefaults(partyA, legalRepA, partyB, legalRepB string) (string, error)` - 使用默认数据填写模板
|
||||
|
||||
#### 签署流程
|
||||
- `CreateSignFlow(req *CreateSignFlowRequest) (string, error)` - 创建签署流程
|
||||
- `GetSignURL(signFlowID, psnAccount, orgName string) (string, string, error)` - 获取签署链接
|
||||
- `QuerySignFlowDetail(signFlowID string) (*QuerySignFlowDetailResponse, error)` - 查询流程详情
|
||||
- `IsSignFlowCompleted(signFlowID string) (bool, error)` - 检查是否完成
|
||||
|
||||
#### 机构认证
|
||||
- `GetOrgAuthURL(req *OrgAuthRequest) (string, string, string, error)` - 获取认证链接
|
||||
- `ValidateOrgAuthInfo(req *OrgAuthRequest) error` - 验证认证信息
|
||||
|
||||
#### 文件操作
|
||||
- `DownloadSignedFile(signFlowID string) (*DownloadSignedFileResponse, error)` - 下载已签署文件
|
||||
- `GetSignFlowStatus(signFlowID string) (string, error)` - 获取流程状态
|
||||
|
||||
## 配置管理
|
||||
|
||||
### 配置结构
|
||||
|
||||
```go
|
||||
type Config struct {
|
||||
AppID string `json:"app_id"` // 应用ID
|
||||
AppSecret string `json:"app_secret"` // 应用密钥
|
||||
ServerURL string `json:"server_url"` // 服务器URL
|
||||
TemplateID string `json:"template_id"` // 模板ID
|
||||
}
|
||||
```
|
||||
|
||||
### 配置验证
|
||||
|
||||
SDK会自动验证配置的完整性:
|
||||
|
||||
```go
|
||||
config, err := esign.NewConfig("", "", "", "")
|
||||
// 返回错误:应用ID不能为空
|
||||
|
||||
// 手动验证
|
||||
err := config.Validate()
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
SDK提供统一的错误处理:
|
||||
|
||||
```go
|
||||
result, err := client.GenerateContractSigning(req)
|
||||
if err != nil {
|
||||
// 错误包含详细的错误信息
|
||||
log.Printf("签署失败: %v", err)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## 迁移指南
|
||||
|
||||
### 从旧版本迁移
|
||||
|
||||
旧版本:
|
||||
```go
|
||||
service := service.NewEQService(config)
|
||||
result, err := service.ExecuteSignProcess(req)
|
||||
```
|
||||
|
||||
新版本:
|
||||
```go
|
||||
client := esign.NewClient(config)
|
||||
result, err := client.GenerateContractSigning(req)
|
||||
```
|
||||
|
||||
### 主要变化
|
||||
|
||||
1. **包名变更**:`service` → `esign`
|
||||
2. **入口简化**:`EQService` → `Client`
|
||||
3. **方法重命名**:更语义化的方法名
|
||||
4. **结构重组**:按功能模块划分
|
||||
5. **类型优化**:更简洁的请求/响应结构
|
||||
|
||||
## 示例代码
|
||||
|
||||
完整的示例代码请参考 `example.go` 文件。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **配置安全**:请妥善保管AppID和AppSecret
|
||||
2. **网络超时**:默认HTTP超时为30秒
|
||||
3. **并发安全**:Client实例是并发安全的
|
||||
4. **错误重试**:建议实现适当的重试机制
|
||||
5. **日志记录**:SDK会输出调试信息,生产环境请注意日志级别
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何更新配置?
|
||||
```go
|
||||
newConfig, _ := esign.NewConfig("new_app_id", "new_secret", "new_url", "new_template")
|
||||
client.UpdateConfig(newConfig)
|
||||
```
|
||||
|
||||
### Q: 如何处理网络错误?
|
||||
```go
|
||||
result, err := client.GenerateContractSigning(req)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "timeout") {
|
||||
// 处理超时
|
||||
} else if strings.Contains(err.Error(), "API调用失败") {
|
||||
// 处理API错误
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何自定义HTTP客户端?
|
||||
当前版本使用内置的HTTP客户端,如需自定义,可以修改`http.go`中的客户端配置。
|
||||
269
internal/shared/esign/client.go
Normal file
269
internal/shared/esign/client.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Client e签宝客户端
|
||||
// 提供统一的e签宝服务接口,整合所有功能模块
|
||||
type Client struct {
|
||||
config *Config // 配置信息
|
||||
httpClient *HTTPClient // HTTP客户端
|
||||
template *TemplateService // 模板服务
|
||||
signFlow *SignFlowService // 签署流程服务
|
||||
orgAuth *OrgAuthService // 机构认证服务
|
||||
fileOps *FileOpsService // 文件操作服务
|
||||
}
|
||||
|
||||
// NewClient 创建e签宝客户端
|
||||
// 使用配置信息初始化客户端及所有服务模块
|
||||
//
|
||||
// 参数:
|
||||
// - config: e签宝配置信息
|
||||
//
|
||||
// 返回: 客户端实例
|
||||
func NewClient(config *Config) *Client {
|
||||
httpClient := NewHTTPClient(config)
|
||||
|
||||
client := &Client{
|
||||
config: config,
|
||||
httpClient: httpClient,
|
||||
}
|
||||
|
||||
// 初始化各个服务模块
|
||||
client.template = NewTemplateService(httpClient, config)
|
||||
client.signFlow = NewSignFlowService(httpClient, config)
|
||||
client.orgAuth = NewOrgAuthService(httpClient, config)
|
||||
client.fileOps = NewFileOpsService(httpClient, config)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// GetConfig 获取当前配置
|
||||
func (c *Client) GetConfig() *Config {
|
||||
return c.config
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (c *Client) UpdateConfig(config *Config) {
|
||||
c.config = config
|
||||
c.httpClient.UpdateConfig(config)
|
||||
|
||||
// 更新各服务模块的配置
|
||||
c.template.UpdateConfig(config)
|
||||
c.signFlow.UpdateConfig(config)
|
||||
c.orgAuth.UpdateConfig(config)
|
||||
c.fileOps.UpdateConfig(config)
|
||||
}
|
||||
|
||||
// ==================== 模板操作 ====================
|
||||
|
||||
// FillTemplate 填写模板
|
||||
// 使用自定义数据填写模板生成文件
|
||||
func (c *Client) FillTemplate(components map[string]string) (*FillTemplate, error) {
|
||||
return c.template.FillWithCustomData(components)
|
||||
}
|
||||
|
||||
// FillTemplateWithDefaults 使用默认数据填写模板
|
||||
func (c *Client) FillTemplateWithDefaults(partyA, legalRepA, partyB, legalRepB string) (*FillTemplate, error) {
|
||||
return c.template.FillWithDefaults(partyA, legalRepA, partyB, legalRepB)
|
||||
}
|
||||
|
||||
// ==================== 签署流程 ====================
|
||||
|
||||
// CreateSignFlow 创建签署流程
|
||||
func (c *Client) CreateSignFlow(req *CreateSignFlowRequest) (string, error) {
|
||||
return c.signFlow.Create(req)
|
||||
}
|
||||
|
||||
// GetSignURL 获取签署链接
|
||||
func (c *Client) GetSignURL(signFlowID, psnAccount, orgName string) (string, string, error) {
|
||||
return c.signFlow.GetSignURL(signFlowID, psnAccount, orgName)
|
||||
}
|
||||
|
||||
// QuerySignFlowDetail 查询签署流程详情
|
||||
func (c *Client) QuerySignFlowDetail(signFlowID string) (*QuerySignFlowDetailResponse, error) {
|
||||
return c.fileOps.QuerySignFlowDetail(signFlowID)
|
||||
}
|
||||
|
||||
// IsSignFlowCompleted 检查签署流程是否完成
|
||||
func (c *Client) IsSignFlowCompleted(signFlowID string) (bool, error) {
|
||||
result, err := c.QuerySignFlowDetail(signFlowID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// 状态码2表示已完成
|
||||
return result.Data.SignFlowStatus == 2, nil
|
||||
}
|
||||
|
||||
// ==================== 机构认证 ====================
|
||||
|
||||
// GetOrgAuthURL 获取机构认证链接
|
||||
func (c *Client) GetOrgAuthURL(req *OrgAuthRequest) (string, string, string, error) {
|
||||
return c.orgAuth.GetAuthURL(req)
|
||||
}
|
||||
|
||||
// ValidateOrgAuthInfo 验证机构认证信息
|
||||
func (c *Client) ValidateOrgAuthInfo(req *OrgAuthRequest) error {
|
||||
return c.orgAuth.ValidateAuthInfo(req)
|
||||
}
|
||||
|
||||
// ==================== 文件操作 ====================
|
||||
|
||||
// DownloadSignedFile 下载已签署文件
|
||||
func (c *Client) DownloadSignedFile(signFlowID string) (*DownloadSignedFileResponse, error) {
|
||||
return c.fileOps.DownloadSignedFile(signFlowID)
|
||||
}
|
||||
|
||||
// GetSignFlowStatus 获取签署流程状态
|
||||
func (c *Client) GetSignFlowStatus(signFlowID string) (string, error) {
|
||||
detail, err := c.QuerySignFlowDetail(signFlowID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return GetSignFlowStatusText(detail.Data.SignFlowStatus), nil
|
||||
}
|
||||
|
||||
// ==================== 业务集成接口 ====================
|
||||
|
||||
// ContractSigningRequest 合同签署请求
|
||||
type ContractSigningRequest struct {
|
||||
// 企业信息
|
||||
CompanyName string `json:"companyName"` // 企业名称
|
||||
UnifiedSocialCode string `json:"unifiedSocialCode"` // 统一社会信用代码
|
||||
LegalPersonName string `json:"legalPersonName"` // 法人姓名
|
||||
LegalPersonID string `json:"legalPersonId"` // 法人身份证号
|
||||
LegalPersonPhone string `json:"legalPersonPhone"` // 法人手机号
|
||||
|
||||
// 经办人信息(可选,如果与法人不同)
|
||||
TransactorName string `json:"transactorName,omitempty"` // 经办人姓名
|
||||
TransactorPhone string `json:"transactorPhone,omitempty"` // 经办人手机号
|
||||
TransactorID string `json:"transactorId,omitempty"` // 经办人身份证号
|
||||
|
||||
// 模板数据(可选)
|
||||
CustomData map[string]string `json:"customData,omitempty"` // 自定义模板数据
|
||||
}
|
||||
|
||||
// ContractSigningResult 合同签署结果
|
||||
type ContractSigningResult struct {
|
||||
FileID string `json:"fileId"` // 文件ID
|
||||
SignFlowID string `json:"signFlowId"` // 签署流程ID
|
||||
SignURL string `json:"signUrl"` // 签署链接
|
||||
ShortURL string `json:"shortUrl"` // 短链接
|
||||
}
|
||||
|
||||
// GenerateContractSigning 生成合同签署
|
||||
// 一站式合同签署服务:填写模板 -> 创建签署流程 -> 获取签署链接
|
||||
func (c *Client) GenerateContractSigning(req *ContractSigningRequest) (*ContractSigningResult, error) {
|
||||
// 1. 准备模板数据
|
||||
var err error
|
||||
var fillTemplate *FillTemplate
|
||||
if len(req.CustomData) > 0 {
|
||||
// 使用自定义数据
|
||||
fillTemplate, err = c.FillTemplate(req.CustomData)
|
||||
} else {
|
||||
// 使用默认数据
|
||||
fillTemplate, err = c.FillTemplateWithDefaults(
|
||||
"海南省学宇思网络科技有限公司",
|
||||
"刘福思",
|
||||
req.CompanyName,
|
||||
req.LegalPersonName,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("填写模板失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 确定签署人信息
|
||||
signerName := req.LegalPersonName
|
||||
transactorName := req.LegalPersonName
|
||||
transactorPhone := req.LegalPersonPhone
|
||||
transactorID := req.LegalPersonID
|
||||
|
||||
if req.TransactorName != "" {
|
||||
signerName = req.TransactorName
|
||||
transactorName = req.TransactorName
|
||||
transactorPhone = req.TransactorPhone
|
||||
transactorID = req.TransactorID
|
||||
}
|
||||
|
||||
// 3. 创建签署流程
|
||||
signFlowReq := &CreateSignFlowRequest{
|
||||
FileID: fillTemplate.FileID,
|
||||
SignerAccount: req.UnifiedSocialCode,
|
||||
SignerName: signerName,
|
||||
TransactorPhone: transactorPhone,
|
||||
TransactorName: transactorName,
|
||||
TransactorIDCardNum: transactorID,
|
||||
}
|
||||
|
||||
signFlowID, err := c.CreateSignFlow(signFlowReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建签署流程失败: %w", err)
|
||||
}
|
||||
|
||||
// 4. 获取签署链接
|
||||
signURL, shortURL, err := c.GetSignURL(signFlowID, transactorPhone, signerName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取签署链接失败: %w", err)
|
||||
}
|
||||
|
||||
return &ContractSigningResult{
|
||||
FileID: fillTemplate.FileID,
|
||||
SignFlowID: signFlowID,
|
||||
SignURL: signURL,
|
||||
ShortURL: shortURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EnterpriseAuthRequest 企业认证请求
|
||||
type EnterpriseAuthRequest struct {
|
||||
// 企业信息
|
||||
CompanyName string `json:"companyName"` // 企业名称
|
||||
UnifiedSocialCode string `json:"unifiedSocialCode"` // 统一社会信用代码
|
||||
LegalPersonName string `json:"legalPersonName"` // 法人姓名
|
||||
LegalPersonID string `json:"legalPersonId"` // 法人身份证号
|
||||
|
||||
// 经办人信息
|
||||
TransactorName string `json:"transactorName"` // 经办人姓名
|
||||
TransactorMobile string `json:"transactorMobile"` // 经办人手机号
|
||||
TransactorID string `json:"transactorId"` // 经办人身份证号
|
||||
}
|
||||
|
||||
// EnterpriseAuthResult 企业认证结果
|
||||
type EnterpriseAuthResult struct {
|
||||
AuthFlowID string `json:"authFlowId"` // 认证流程ID
|
||||
AuthURL string `json:"authUrl"` // 认证链接
|
||||
AuthShortURL string `json:"authShortUrl"` // 短链接
|
||||
}
|
||||
|
||||
// GenerateEnterpriseAuth 生成企业认证
|
||||
// 一站式企业认证服务
|
||||
func (c *Client) GenerateEnterpriseAuth(req *EnterpriseAuthRequest) (*EnterpriseAuthResult, error) {
|
||||
authReq := &OrgAuthRequest{
|
||||
OrgName: req.CompanyName,
|
||||
OrgIDCardNum: req.UnifiedSocialCode,
|
||||
LegalRepName: req.LegalPersonName,
|
||||
LegalRepIDCardNum: req.LegalPersonID,
|
||||
TransactorName: req.TransactorName,
|
||||
TransactorIDCardNum: req.TransactorID,
|
||||
TransactorMobile: req.TransactorMobile,
|
||||
}
|
||||
|
||||
// 验证信息
|
||||
if err := c.ValidateOrgAuthInfo(authReq); err != nil {
|
||||
return nil, fmt.Errorf("认证信息验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 获取认证链接
|
||||
authFlowID, authURL, shortURL, err := c.GetOrgAuthURL(authReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取认证链接失败: %w", err)
|
||||
}
|
||||
|
||||
return &EnterpriseAuthResult{
|
||||
AuthFlowID: authFlowID,
|
||||
AuthURL: authURL,
|
||||
AuthShortURL: shortURL,
|
||||
}, nil
|
||||
}
|
||||
83
internal/shared/esign/config.go
Normal file
83
internal/shared/esign/config.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package esign
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Config e签宝服务配置结构体
|
||||
// 包含应用ID、密钥、服务器URL和模板ID等基础配置信息
|
||||
type Config struct {
|
||||
AppID string `json:"appId"` // 应用ID
|
||||
AppSecret string `json:"appSecret"` // 应用密钥
|
||||
ServerURL string `json:"serverUrl"` // 服务器URL
|
||||
TemplateID string `json:"templateId"` // 模板ID
|
||||
}
|
||||
|
||||
// NewConfig 创建新的配置实例
|
||||
// 提供配置验证和默认值设置
|
||||
func NewConfig(appID, appSecret, serverURL, templateID string) (*Config, error) {
|
||||
if appID == "" {
|
||||
return nil, fmt.Errorf("应用ID不能为空")
|
||||
}
|
||||
if appSecret == "" {
|
||||
return nil, fmt.Errorf("应用密钥不能为空")
|
||||
}
|
||||
if serverURL == "" {
|
||||
return nil, fmt.Errorf("服务器URL不能为空")
|
||||
}
|
||||
if templateID == "" {
|
||||
return nil, fmt.Errorf("模板ID不能为空")
|
||||
}
|
||||
|
||||
return &Config{
|
||||
AppID: appID,
|
||||
AppSecret: appSecret,
|
||||
ServerURL: serverURL,
|
||||
TemplateID: templateID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Validate 验证配置的完整性
|
||||
func (c *Config) Validate() error {
|
||||
if c.AppID == "" {
|
||||
return fmt.Errorf("应用ID不能为空")
|
||||
}
|
||||
if c.AppSecret == "" {
|
||||
return fmt.Errorf("应用密钥不能为空")
|
||||
}
|
||||
if c.ServerURL == "" {
|
||||
return fmt.Errorf("服务器URL不能为空")
|
||||
}
|
||||
if c.TemplateID == "" {
|
||||
return fmt.Errorf("模板ID不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 认证模式常量
|
||||
const (
|
||||
// 个人认证模式
|
||||
AuthModeMobile3 = "PSN_MOBILE3" // 手机号三要素认证
|
||||
AuthModeIDCard = "PSN_IDCARD" // 身份证认证
|
||||
AuthModeBank = "PSN_BANK" // 银行卡认证
|
||||
|
||||
// 意愿认证模式
|
||||
WillingnessAuthSMS = "CODE_SMS" // 短信验证码
|
||||
WillingnessAuthEmail = "CODE_EMAIL" // 邮箱验证码
|
||||
|
||||
// 证件类型常量
|
||||
IDCardTypeChina = "CRED_PSN_CH_IDCARD" // 中国大陆居民身份证
|
||||
OrgCardTypeUSCC = "CRED_ORG_USCC" // 统一社会信用代码
|
||||
|
||||
// 签署区样式常量
|
||||
SignFieldStyleNormal = 1 // 普通签章
|
||||
SignFieldStyleSeam = 2 // 骑缝签章
|
||||
|
||||
// 签署人类型常量
|
||||
SignerTypePerson = 0 // 个人
|
||||
SignerTypeOrg = 1 // 机构
|
||||
|
||||
// URL类型常量
|
||||
UrlTypeSign = 2 // 签署链接
|
||||
|
||||
// 客户端类型常量
|
||||
ClientTypeAll = "ALL" // 所有客户端
|
||||
)
|
||||
193
internal/shared/esign/example.go
Normal file
193
internal/shared/esign/example.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Example 展示如何使用重构后的e签宝SDK
|
||||
func Example() {
|
||||
// 1. 创建配置
|
||||
config, err := NewConfig(
|
||||
"your_app_id",
|
||||
"your_app_secret",
|
||||
"https://smlopenapi.esign.cn",
|
||||
"your_template_id",
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal("配置创建失败:", err)
|
||||
}
|
||||
|
||||
// 2. 创建客户端
|
||||
client := NewClient(config)
|
||||
|
||||
// 示例1: 简单合同签署流程
|
||||
fmt.Println("=== 示例1: 简单合同签署流程 ===")
|
||||
contractReq := &ContractSigningRequest{
|
||||
CompanyName: "测试公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
LegalPersonPhone: "13800138000",
|
||||
}
|
||||
|
||||
result, err := client.GenerateContractSigning(contractReq)
|
||||
if err != nil {
|
||||
log.Printf("合同签署失败: %v", err)
|
||||
} else {
|
||||
fmt.Printf("合同签署成功: %+v\n", result)
|
||||
}
|
||||
|
||||
// 示例2: 企业认证流程
|
||||
fmt.Println("\n=== 示例2: 企业认证流程 ===")
|
||||
authReq := &EnterpriseAuthRequest{
|
||||
CompanyName: "测试公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
TransactorName: "李四",
|
||||
TransactorMobile: "13800138001",
|
||||
TransactorID: "123456789012345679",
|
||||
}
|
||||
|
||||
authResult, err := client.GenerateEnterpriseAuth(authReq)
|
||||
if err != nil {
|
||||
log.Printf("企业认证失败: %v", err)
|
||||
} else {
|
||||
fmt.Printf("企业认证成功: %+v\n", authResult)
|
||||
}
|
||||
|
||||
// 示例3: 分步操作
|
||||
fmt.Println("\n=== 示例3: 分步操作 ===")
|
||||
|
||||
// 3.1 填写模板
|
||||
templateData := map[string]string{
|
||||
"JFQY": "甲方公司",
|
||||
"JFFR": "甲方法人",
|
||||
"YFQY": "乙方公司",
|
||||
"YFFR": "乙方法人",
|
||||
"QDRQ": "2024年01月01日",
|
||||
}
|
||||
|
||||
fileID, err := client.FillTemplate(templateData)
|
||||
if err != nil {
|
||||
log.Printf("模板填写失败: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("模板填写成功,文件ID: %s\n", fileID)
|
||||
|
||||
// 3.2 创建签署流程
|
||||
signFlowReq := &CreateSignFlowRequest{
|
||||
FileID: fileID.FileID,
|
||||
SignerAccount: "123456789012345678",
|
||||
SignerName: "乙方公司",
|
||||
TransactorPhone: "13800138000",
|
||||
TransactorName: "乙方法人",
|
||||
TransactorIDCardNum: "123456789012345678",
|
||||
}
|
||||
|
||||
signFlowID, err := client.CreateSignFlow(signFlowReq)
|
||||
if err != nil {
|
||||
log.Printf("创建签署流程失败: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("签署流程创建成功,流程ID: %s\n", signFlowID)
|
||||
|
||||
// 3.3 获取签署链接
|
||||
signURL, shortURL, err := client.GetSignURL(signFlowID, "13800138000", "乙方公司")
|
||||
if err != nil {
|
||||
log.Printf("获取签署链接失败: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("签署链接: %s\n", signURL)
|
||||
fmt.Printf("短链接: %s\n", shortURL)
|
||||
|
||||
// 3.4 查询签署状态
|
||||
status, err := client.GetSignFlowStatus(signFlowID)
|
||||
if err != nil {
|
||||
log.Printf("查询签署状态失败: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("签署状态: %s\n", status)
|
||||
|
||||
// 3.5 检查是否完成
|
||||
completed, err := client.IsSignFlowCompleted(signFlowID)
|
||||
if err != nil {
|
||||
log.Printf("检查签署状态失败: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("签署是否完成: %t\n", completed)
|
||||
}
|
||||
|
||||
// ExampleBasicUsage 基础用法示例
|
||||
func ExampleBasicUsage() {
|
||||
// 最简单的用法 - 一行代码完成合同签署
|
||||
config, _ := NewConfig("app_id", "app_secret", "server_url", "template_id")
|
||||
client := NewClient(config)
|
||||
|
||||
// 快速合同签署
|
||||
result, err := client.GenerateContractSigning(&ContractSigningRequest{
|
||||
CompanyName: "我的公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
LegalPersonPhone: "13800138000",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("签署失败:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("请访问以下链接进行签署: %s\n", result.SignURL)
|
||||
}
|
||||
|
||||
// ExampleWithCustomData 自定义数据示例
|
||||
func ExampleWithCustomData() {
|
||||
config, _ := NewConfig("app_id", "app_secret", "server_url", "template_id")
|
||||
client := NewClient(config)
|
||||
|
||||
// 使用自定义模板数据
|
||||
customData := map[string]string{
|
||||
"custom_field_1": "自定义值1",
|
||||
"custom_field_2": "自定义值2",
|
||||
"contract_date": "2024年01月01日",
|
||||
}
|
||||
|
||||
result, err := client.GenerateContractSigning(&ContractSigningRequest{
|
||||
CompanyName: "我的公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
LegalPersonPhone: "13800138000",
|
||||
CustomData: customData,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("签署失败:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("自定义合同签署链接: %s\n", result.SignURL)
|
||||
}
|
||||
|
||||
// ExampleEnterpriseAuth 企业认证示例
|
||||
func ExampleEnterpriseAuth() {
|
||||
config, _ := NewConfig("app_id", "app_secret", "server_url", "template_id")
|
||||
client := NewClient(config)
|
||||
|
||||
// 企业认证
|
||||
authResult, err := client.GenerateEnterpriseAuth(&EnterpriseAuthRequest{
|
||||
CompanyName: "我的公司",
|
||||
UnifiedSocialCode: "123456789012345678",
|
||||
LegalPersonName: "张三",
|
||||
LegalPersonID: "123456789012345678",
|
||||
TransactorName: "李四",
|
||||
TransactorMobile: "13800138001",
|
||||
TransactorID: "123456789012345679",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("企业认证失败:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("请访问以下链接进行企业认证: %s\n", authResult.AuthURL)
|
||||
}
|
||||
208
internal/shared/esign/fileops_service.go
Normal file
208
internal/shared/esign/fileops_service.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// FileOpsService 文件操作服务
|
||||
// 处理文件下载、流程查询等操作
|
||||
type FileOpsService struct {
|
||||
httpClient *HTTPClient
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewFileOpsService 创建文件操作服务
|
||||
func NewFileOpsService(httpClient *HTTPClient, config *Config) *FileOpsService {
|
||||
return &FileOpsService{
|
||||
httpClient: httpClient,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (s *FileOpsService) UpdateConfig(config *Config) {
|
||||
s.config = config
|
||||
}
|
||||
|
||||
// DownloadSignedFile 下载已签署文件及附属材料
|
||||
// 获取签署完成后的文件下载链接和证书下载链接
|
||||
//
|
||||
// 参数说明:
|
||||
// - signFlowId: 签署流程ID
|
||||
//
|
||||
// 返回: 下载文件响应和错误信息
|
||||
func (s *FileOpsService) DownloadSignedFile(signFlowId string) (*DownloadSignedFileResponse, error) {
|
||||
fmt.Println("开始下载已签署文件及附属材料...")
|
||||
|
||||
// 发送API请求
|
||||
urlPath := fmt.Sprintf("/v3/sign-flow/%s/attachments", signFlowId)
|
||||
responseBody, err := s.httpClient.Request("GET", urlPath, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("下载已签署文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var response DownloadSignedFileResponse
|
||||
if err := UnmarshalResponse(responseBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := CheckResponseCode(response.Code, response.Message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("已签署文件下载信息获取成功!\n")
|
||||
fmt.Printf("文件数量: %d\n", len(response.Data.Files))
|
||||
fmt.Printf("附属材料数量: %d\n", len(response.Data.Attachments))
|
||||
if response.Data.CertificateDownloadUrl != "" {
|
||||
fmt.Printf("证书下载链接: %s\n", response.Data.CertificateDownloadUrl)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// QuerySignFlowDetail 查询签署流程详情
|
||||
// 获取签署流程的详细状态和参与方信息
|
||||
//
|
||||
// 参数说明:
|
||||
// - signFlowId: 签署流程ID
|
||||
//
|
||||
// 返回: 流程详情响应和错误信息
|
||||
func (s *FileOpsService) QuerySignFlowDetail(signFlowId string) (*QuerySignFlowDetailResponse, error) {
|
||||
fmt.Println("开始查询签署流程详情...")
|
||||
|
||||
// 发送API请求
|
||||
urlPath := fmt.Sprintf("/v3/sign-flow/%s/detail", signFlowId)
|
||||
responseBody, err := s.httpClient.Request("GET", urlPath, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询签署流程详情失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var response QuerySignFlowDetailResponse
|
||||
if err := UnmarshalResponse(responseBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := CheckResponseCode(response.Code, response.Message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("查询签署流程详情响应: %+v\n", response)
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// GetSignedFileDownloadUrls 获取已签署文件的下载链接
|
||||
// 从下载响应中提取所有文件的下载链接
|
||||
//
|
||||
// 参数说明:
|
||||
// - downloadResponse: 下载文件响应
|
||||
//
|
||||
// 返回: 文件下载链接映射
|
||||
func GetSignedFileDownloadUrls(downloadResponse *DownloadSignedFileResponse) map[string]string {
|
||||
urls := make(map[string]string)
|
||||
|
||||
// 添加已签署文件
|
||||
for _, file := range downloadResponse.Data.Files {
|
||||
urls[file.FileName] = file.DownloadUrl
|
||||
}
|
||||
|
||||
// 添加附属材料
|
||||
for _, attachment := range downloadResponse.Data.Attachments {
|
||||
urls[attachment.FileName] = attachment.DownloadUrl
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
// GetSignFlowStatusText 获取签署流程状态文本
|
||||
// 从流程详情中提取状态信息
|
||||
//
|
||||
// 参数说明:
|
||||
// - status: 流程状态码
|
||||
//
|
||||
// 返回: 流程状态描述
|
||||
func GetSignFlowStatusText(status int32) string {
|
||||
switch status {
|
||||
case 1:
|
||||
return "草稿"
|
||||
case 2:
|
||||
return "签署中"
|
||||
case 3:
|
||||
return "已完成"
|
||||
case 4:
|
||||
return "已撤销"
|
||||
case 5:
|
||||
return "已过期"
|
||||
case 6:
|
||||
return "已拒绝"
|
||||
default:
|
||||
return fmt.Sprintf("未知状态(%d)", status)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSignerStatus 获取签署人状态
|
||||
// 从流程详情中提取指定签署人的状态
|
||||
//
|
||||
// 参数说明:
|
||||
// - detailResponse: 流程详情响应
|
||||
// - signerName: 签署人姓名
|
||||
//
|
||||
// 返回: 签署人状态描述
|
||||
func GetSignerStatus(detailResponse *QuerySignFlowDetailResponse, signerName string) string {
|
||||
for _, signer := range detailResponse.Data.Signers {
|
||||
var name string
|
||||
if signer.PsnSigner != nil {
|
||||
name = signer.PsnSigner.PsnName
|
||||
} else if signer.OrgSigner != nil {
|
||||
name = signer.OrgSigner.OrgName
|
||||
}
|
||||
|
||||
if name == signerName {
|
||||
switch signer.SignStatus {
|
||||
case 1:
|
||||
return "待签署"
|
||||
case 2:
|
||||
return "已签署"
|
||||
case 3:
|
||||
return "已拒绝"
|
||||
case 4:
|
||||
return "已过期"
|
||||
default:
|
||||
return fmt.Sprintf("未知状态(%d)", signer.SignStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
return "未找到签署人"
|
||||
}
|
||||
|
||||
// IsSignFlowCompleted 检查签署流程是否完成
|
||||
// 根据状态码判断签署流程是否已完成
|
||||
//
|
||||
// 参数说明:
|
||||
// - detailResponse: 流程详情响应
|
||||
//
|
||||
// 返回: 是否完成
|
||||
func IsSignFlowCompleted(detailResponse *QuerySignFlowDetailResponse) bool {
|
||||
// 状态码2表示已完成
|
||||
return detailResponse.Data.SignFlowStatus == 2
|
||||
}
|
||||
|
||||
// GetFileList 获取文件列表
|
||||
// 从下载响应中获取所有文件信息
|
||||
//
|
||||
// 参数说明:
|
||||
// - downloadResponse: 下载文件响应
|
||||
//
|
||||
// 返回: 文件信息列表
|
||||
func GetFileList(downloadResponse *DownloadSignedFileResponse) []SignedFileInfo {
|
||||
var files []SignedFileInfo
|
||||
|
||||
// 添加已签署文件
|
||||
files = append(files, downloadResponse.Data.Files...)
|
||||
|
||||
// 添加附属材料
|
||||
files = append(files, downloadResponse.Data.Attachments...)
|
||||
|
||||
return files
|
||||
}
|
||||
199
internal/shared/esign/http.go
Normal file
199
internal/shared/esign/http.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPClient e签宝HTTP客户端
|
||||
// 处理所有e签宝API的HTTP请求,包括签名生成、请求头设置等
|
||||
type HTTPClient struct {
|
||||
config *Config
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewHTTPClient 创建HTTP客户端
|
||||
func NewHTTPClient(config *Config) *HTTPClient {
|
||||
return &HTTPClient{
|
||||
config: config,
|
||||
client: &http.Client{Timeout: 30 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (h *HTTPClient) UpdateConfig(config *Config) {
|
||||
h.config = config
|
||||
}
|
||||
|
||||
// Request e签宝通用请求函数
|
||||
// 处理所有e签宝API的HTTP请求,包括签名生成、请求头设置等
|
||||
//
|
||||
// 参数说明:
|
||||
// - method: HTTP方法(GET、POST等)
|
||||
// - urlPath: API路径
|
||||
// - body: 请求体字节数组
|
||||
//
|
||||
// 返回: 响应体字节数组和错误信息
|
||||
func (h *HTTPClient) Request(method, urlPath string, body []byte) ([]byte, error) {
|
||||
// 生成签名所需参数
|
||||
timestamp := getCurrentTimestamp()
|
||||
nonce := generateNonce()
|
||||
date := getCurrentDate()
|
||||
|
||||
// 计算Content-MD5
|
||||
contentMD5 := ""
|
||||
if len(body) > 0 {
|
||||
contentMD5 = getContentMD5(body)
|
||||
}
|
||||
|
||||
// 根据Java示例,Headers为空字符串
|
||||
headers := ""
|
||||
|
||||
// 生成签名
|
||||
signature := generateSignature(h.config.AppSecret, method, "*/*", contentMD5, "application/json", date, headers, urlPath)
|
||||
|
||||
// 创建HTTP请求
|
||||
url := h.config.ServerURL + urlPath
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建HTTP请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Content-MD5", contentMD5)
|
||||
req.Header.Set("Date", date)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
req.Header.Set("X-Tsign-Open-App-Id", h.config.AppID)
|
||||
req.Header.Set("X-Tsign-Open-Auth-Mode", "Signature")
|
||||
req.Header.Set("X-Tsign-Open-Ca-Timestamp", timestamp)
|
||||
req.Header.Set("X-Tsign-Open-Nonce", nonce)
|
||||
req.Header.Set("X-Tsign-Open-Ca-Signature", signature)
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送HTTP请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
// 打印响应内容用于调试
|
||||
fmt.Printf("API响应状态码: %d\n", resp.StatusCode)
|
||||
fmt.Printf("API响应内容: %s\n", string(responseBody))
|
||||
|
||||
// 检查响应状态码
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("API请求失败,状态码: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return responseBody, nil
|
||||
}
|
||||
|
||||
// MarshalRequest 序列化请求数据为JSON
|
||||
//
|
||||
// 参数:
|
||||
// - data: 要序列化的数据
|
||||
//
|
||||
// 返回: JSON字节数组和错误信息
|
||||
func MarshalRequest(data interface{}) ([]byte, error) {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化请求数据失败: %v", err)
|
||||
}
|
||||
return jsonData, nil
|
||||
}
|
||||
|
||||
// UnmarshalResponse 反序列化响应数据
|
||||
//
|
||||
// 参数:
|
||||
// - responseBody: 响应体字节数组
|
||||
// - response: 目标响应结构体指针
|
||||
//
|
||||
// 返回: 错误信息
|
||||
func UnmarshalResponse(responseBody []byte, response interface{}) error {
|
||||
if err := json.Unmarshal(responseBody, response); err != nil {
|
||||
return fmt.Errorf("解析响应失败: %v,响应内容: %s", err, string(responseBody))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckResponseCode 检查API响应码
|
||||
//
|
||||
// 参数:
|
||||
// - code: 响应码
|
||||
// - message: 响应消息
|
||||
//
|
||||
// 返回: 错误信息
|
||||
func CheckResponseCode(code int, message string) error {
|
||||
if code != 0 {
|
||||
return fmt.Errorf("API调用失败: %s", message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortURLQueryParams 对URL查询参数按字典序(ASCII码)升序排序
|
||||
//
|
||||
// 参数:
|
||||
// - urlPath: 包含查询参数的URL路径
|
||||
//
|
||||
// 返回: 排序后的URL路径
|
||||
func sortURLQueryParams(urlPath string) string {
|
||||
// 检查是否包含查询参数
|
||||
if !strings.Contains(urlPath, "?") {
|
||||
return urlPath
|
||||
}
|
||||
|
||||
// 分离路径和查询参数
|
||||
parts := strings.SplitN(urlPath, "?", 2)
|
||||
if len(parts) != 2 {
|
||||
return urlPath
|
||||
}
|
||||
|
||||
basePath := parts[0]
|
||||
queryString := parts[1]
|
||||
|
||||
// 解析查询参数
|
||||
values, err := url.ParseQuery(queryString)
|
||||
if err != nil {
|
||||
// 如果解析失败,返回原始路径
|
||||
return urlPath
|
||||
}
|
||||
|
||||
// 获取所有参数键并排序
|
||||
var keys []string
|
||||
for key := range values {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// 重新构建查询字符串
|
||||
var sortedPairs []string
|
||||
for _, key := range keys {
|
||||
for _, value := range values[key] {
|
||||
sortedPairs = append(sortedPairs, key+"="+value)
|
||||
}
|
||||
}
|
||||
|
||||
// 组合排序后的查询参数
|
||||
sortedQueryString := strings.Join(sortedPairs, "&")
|
||||
|
||||
// 返回完整的URL路径
|
||||
if sortedQueryString != "" {
|
||||
return basePath + "?" + sortedQueryString
|
||||
}
|
||||
return basePath
|
||||
}
|
||||
63
internal/shared/esign/org_identity.go
Normal file
63
internal/shared/esign/org_identity.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// QueryOrgIdentityInfo 查询机构认证信息
|
||||
// 根据orgId、orgName或orgIDCardNum查询机构实名认证信息
|
||||
func (s *OrgAuthService) QueryOrgIdentityInfo(req *QueryOrgIdentityRequest) (*QueryOrgIdentityResponse, error) {
|
||||
// 构建查询参数
|
||||
params := url.Values{}
|
||||
if req.OrgID != "" {
|
||||
params.Add("orgId", req.OrgID)
|
||||
} else if req.OrgName != "" {
|
||||
params.Add("orgName", req.OrgName)
|
||||
} else if req.OrgIDCardNum != "" {
|
||||
params.Add("orgIDCardNum", req.OrgIDCardNum)
|
||||
if req.OrgIDCardType != "" {
|
||||
params.Add("orgIDCardType", string(req.OrgIDCardType))
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("至少提供orgId, orgName或orgIDCardNum之一")
|
||||
}
|
||||
|
||||
// 构建urlPath带query - 不使用URL编码,保持原始参数值
|
||||
urlPath := "/v3/organizations/identity-info"
|
||||
if len(params) > 0 {
|
||||
var queryParts []string
|
||||
for key, values := range params {
|
||||
for _, value := range values {
|
||||
queryParts = append(queryParts, key+"="+value)
|
||||
}
|
||||
}
|
||||
urlPath += "?" + strings.Join(queryParts, "&")
|
||||
}
|
||||
|
||||
// 发送API请求
|
||||
responseBody, err := s.httpClient.Request("GET", urlPath, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询机构认证信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var response QueryOrgIdentityResponse
|
||||
if err := UnmarshalResponse(responseBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := CheckResponseCode(int(response.Code), response.Message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("查询机构认证信息成功!\n")
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// QueryOrgIdentityInfo 查询机构认证信息(客户端方法)
|
||||
// 通过Client提供的便捷方法
|
||||
func (c *Client) QueryOrgIdentityInfo(req *QueryOrgIdentityRequest) (*QueryOrgIdentityResponse, error) {
|
||||
return c.orgAuth.QueryOrgIdentityInfo(req)
|
||||
}
|
||||
205
internal/shared/esign/orgauth_service.go
Normal file
205
internal/shared/esign/orgauth_service.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// OrgAuthService 机构认证服务
|
||||
// 处理机构认证和授权相关操作
|
||||
type OrgAuthService struct {
|
||||
httpClient *HTTPClient
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewOrgAuthService 创建机构认证服务
|
||||
func NewOrgAuthService(httpClient *HTTPClient, config *Config) *OrgAuthService {
|
||||
return &OrgAuthService{
|
||||
httpClient: httpClient,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (s *OrgAuthService) UpdateConfig(config *Config) {
|
||||
s.config = config
|
||||
}
|
||||
|
||||
// OrgAuthRequest 机构认证请求
|
||||
type OrgAuthRequest struct {
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
OrgIDCardNum string `json:"orgIdCardNum"` // 机构证件号
|
||||
LegalRepName string `json:"legalRepName"` // 法定代表人姓名
|
||||
LegalRepIDCardNum string `json:"legalRepIdCardNum"` // 法定代表人身份证号
|
||||
TransactorName string `json:"transactorName"` // 经办人姓名
|
||||
TransactorIDCardNum string `json:"transactorIdCardNum"` // 经办人身份证号
|
||||
TransactorMobile string `json:"transactorMobile"` // 经办人手机号
|
||||
}
|
||||
|
||||
// GetAuthURL 获取机构认证&授权页面链接
|
||||
// 为机构用户获取认证和授权页面链接,用于机构身份认证
|
||||
func (s *OrgAuthService) GetAuthURL(req *OrgAuthRequest) (string, string, string, error) {
|
||||
// 构建请求数据
|
||||
requestData := GetOrgAuthUrlRequest{
|
||||
OrgAuthConfig: &OrgAuthConfig{
|
||||
OrgName: req.OrgName,
|
||||
OrgInfo: &OrgAuthInfo{
|
||||
OrgIDCardNum: req.OrgIDCardNum,
|
||||
OrgIDCardType: OrgCardTypeUSCC,
|
||||
LegalRepName: req.LegalRepName,
|
||||
LegalRepIDCardNum: req.LegalRepIDCardNum,
|
||||
LegalRepIDCardType: IDCardTypeChina,
|
||||
},
|
||||
TransactorAuthPageConfig: &TransactorAuthPageConfig{
|
||||
PsnAvailableAuthModes: []string{AuthModeMobile3},
|
||||
PsnDefaultAuthMode: AuthModeMobile3,
|
||||
PsnEditableFields: []string{},
|
||||
},
|
||||
TransactorInfo: &TransactorAuthInfo{
|
||||
PsnAccount: req.TransactorMobile,
|
||||
PsnInfo: &PsnAuthInfo{
|
||||
PsnName: req.TransactorName,
|
||||
PsnIDCardNum: req.TransactorIDCardNum,
|
||||
PsnIDCardType: IDCardTypeChina,
|
||||
PsnMobile: req.TransactorMobile,
|
||||
PsnIdentityVerify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
ClientType: ClientTypeAll,
|
||||
}
|
||||
|
||||
// 序列化请求数据
|
||||
jsonData, err := MarshalRequest(requestData)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
fmt.Printf("获取机构认证&授权页面链接请求数据: %s\n", string(jsonData))
|
||||
|
||||
// 发送API请求
|
||||
responseBody, err := s.httpClient.Request("POST", "/v3/org-auth-url", jsonData)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("获取机构认证&授权页面链接失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var response GetOrgAuthUrlResponse
|
||||
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("认证流程ID: %s\n", response.Data.AuthFlowId)
|
||||
fmt.Printf("完整链接: %s\n", response.Data.AuthUrl)
|
||||
fmt.Printf("短链接: %s\n", response.Data.AuthShortUrl)
|
||||
|
||||
return response.Data.AuthFlowId, response.Data.AuthUrl, response.Data.AuthShortUrl, nil
|
||||
}
|
||||
|
||||
// CreateAuthConfig 创建机构认证配置
|
||||
// 构建机构认证所需的配置信息
|
||||
func (s *OrgAuthService) CreateAuthConfig(req *OrgAuthRequest) *OrgAuthConfig {
|
||||
return &OrgAuthConfig{
|
||||
OrgName: req.OrgName,
|
||||
OrgInfo: &OrgAuthInfo{
|
||||
OrgIDCardNum: req.OrgIDCardNum,
|
||||
OrgIDCardType: OrgCardTypeUSCC,
|
||||
LegalRepName: req.LegalRepName,
|
||||
LegalRepIDCardNum: req.LegalRepIDCardNum,
|
||||
LegalRepIDCardType: IDCardTypeChina,
|
||||
},
|
||||
TransactorAuthPageConfig: &TransactorAuthPageConfig{
|
||||
PsnAvailableAuthModes: []string{AuthModeMobile3},
|
||||
PsnDefaultAuthMode: AuthModeMobile3,
|
||||
PsnEditableFields: []string{},
|
||||
},
|
||||
TransactorInfo: &TransactorAuthInfo{
|
||||
PsnAccount: req.TransactorMobile,
|
||||
PsnInfo: &PsnAuthInfo{
|
||||
PsnName: req.TransactorName,
|
||||
PsnIDCardNum: req.TransactorIDCardNum,
|
||||
PsnIDCardType: IDCardTypeChina,
|
||||
PsnMobile: req.TransactorMobile,
|
||||
PsnIdentityVerify: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateAuthInfo 验证机构认证信息
|
||||
// 检查机构认证信息的完整性和格式
|
||||
func (s *OrgAuthService) ValidateAuthInfo(req *OrgAuthRequest) error {
|
||||
if req.OrgName == "" {
|
||||
return fmt.Errorf("机构名称不能为空")
|
||||
}
|
||||
if req.OrgIDCardNum == "" {
|
||||
return fmt.Errorf("机构证件号不能为空")
|
||||
}
|
||||
if req.LegalRepName == "" {
|
||||
return fmt.Errorf("法定代表人姓名不能为空")
|
||||
}
|
||||
if req.LegalRepIDCardNum == "" {
|
||||
return fmt.Errorf("法定代表人身份证号不能为空")
|
||||
}
|
||||
if req.TransactorName == "" {
|
||||
return fmt.Errorf("经办人姓名不能为空")
|
||||
}
|
||||
if req.TransactorIDCardNum == "" {
|
||||
return fmt.Errorf("经办人身份证号不能为空")
|
||||
}
|
||||
if req.TransactorMobile == "" {
|
||||
return fmt.Errorf("经办人手机号不能为空")
|
||||
}
|
||||
|
||||
// 验证统一社会信用代码格式(18位)
|
||||
if len(req.OrgIDCardNum) != 18 {
|
||||
return fmt.Errorf("机构证件号(统一社会信用代码)必须是18位")
|
||||
}
|
||||
|
||||
// 验证身份证号格式(18位)
|
||||
if len(req.LegalRepIDCardNum) != 18 {
|
||||
return fmt.Errorf("法定代表人身份证号必须是18位")
|
||||
}
|
||||
if len(req.TransactorIDCardNum) != 18 {
|
||||
return fmt.Errorf("经办人身份证号必须是18位")
|
||||
}
|
||||
|
||||
// 验证手机号格式(11位)
|
||||
if len(req.TransactorMobile) != 11 {
|
||||
return fmt.Errorf("经办人手机号必须是11位")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueryOrgIdentity 查询机构认证信息
|
||||
// 查询机构的实名认证状态和信息
|
||||
func (s *OrgAuthService) QueryOrgIdentity(req *QueryOrgIdentityRequest) (*QueryOrgIdentityResponse, error) {
|
||||
// 序列化请求数据
|
||||
jsonData, err := MarshalRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 发送API请求
|
||||
responseBody, err := s.httpClient.Request("POST", "/v3/organizations/identity", jsonData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询机构认证信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var response QueryOrgIdentityResponse
|
||||
if err := UnmarshalResponse(responseBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := CheckResponseCode(int(response.Code), response.Message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
215
internal/shared/esign/signflow_service.go
Normal file
215
internal/shared/esign/signflow_service.go
Normal file
@@ -0,0 +1,215 @@
|
||||
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,
|
||||
FileName: "天远数据API合作协议.pdf",
|
||||
},
|
||||
},
|
||||
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{
|
||||
PositionPage: "1",
|
||||
PositionX: 200,
|
||||
PositionY: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 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{
|
||||
PositionPage: "1",
|
||||
PositionX: 458,
|
||||
PositionY: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// buildSignFlowConfig 构建签署流程配置
|
||||
func (s *SignFlowService) buildSignFlowConfig() SignFlowConfig {
|
||||
return SignFlowConfig{
|
||||
SignFlowTitle: "天远数据API合作协议签署",
|
||||
SignFlowExpireTime: calculateExpireTime(7), // 7天后过期
|
||||
AutoFinish: true, // 所有签署方完成后自动完结
|
||||
AuthConfig: &AuthConfig{
|
||||
PsnAvailableAuthModes: []string{AuthModeMobile3},
|
||||
WillingnessAuthModes: []string{WillingnessAuthSMS},
|
||||
},
|
||||
ContractConfig: &ContractConfig{
|
||||
AllowToRescind: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
167
internal/shared/esign/template_service.go
Normal file
167
internal/shared/esign/template_service.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TemplateService 模板服务
|
||||
// 处理模板填写和文件生成相关操作
|
||||
type TemplateService struct {
|
||||
httpClient *HTTPClient
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewTemplateService 创建模板服务
|
||||
func NewTemplateService(httpClient *HTTPClient, config *Config) *TemplateService {
|
||||
return &TemplateService{
|
||||
httpClient: httpClient,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (s *TemplateService) UpdateConfig(config *Config) {
|
||||
s.config = config
|
||||
}
|
||||
|
||||
// Fill 填写模板生成文件
|
||||
// 根据模板ID和填写内容生成包含填写内容的文档
|
||||
//
|
||||
// 参数说明:
|
||||
// - components: 需要填写的组件列表,包含字段键名和值
|
||||
//
|
||||
// 返回: 生成的文件ID和错误信息
|
||||
func (s *TemplateService) Fill(components []Component) (*FillTemplate, error) {
|
||||
fmt.Println("开始填写模板生成文件...")
|
||||
|
||||
// 生成带时间戳的文件名
|
||||
fileName := generateFileName("天远数据API合作协议", "pdf")
|
||||
|
||||
// 构建请求数据
|
||||
requestData := FillTemplateRequest{
|
||||
DocTemplateID: s.config.TemplateID,
|
||||
FileName: fileName,
|
||||
Components: components,
|
||||
}
|
||||
|
||||
// 序列化请求数据
|
||||
jsonData, err := MarshalRequest(requestData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 发送API请求
|
||||
responseBody, err := s.httpClient.Request("POST", "/v3/files/create-by-doc-template", jsonData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("填写模板失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var response FillTemplateResponse
|
||||
if err := UnmarshalResponse(responseBody, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查响应状态
|
||||
if err := CheckResponseCode(response.Code, response.Message); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("模板填写成功,文件ID: %s\n", response.Data.FileID)
|
||||
return &FillTemplate{
|
||||
FileID: response.Data.FileID,
|
||||
FileDownloadUrl: response.Data.FileDownloadUrl,
|
||||
FileName: fileName,
|
||||
TemplateID: s.config.TemplateID,
|
||||
FillTime: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FillWithDefaults 使用默认数据填写模板
|
||||
// 使用预设的默认数据填写模板,适用于测试或标准流程
|
||||
//
|
||||
// 参数说明:
|
||||
// - partyA: 甲方企业名称
|
||||
// - legalRepA: 甲方法人姓名
|
||||
// - partyB: 乙方企业名称
|
||||
// - legalRepB: 乙方法人姓名
|
||||
//
|
||||
// 返回: 生成的文件ID和错误信息
|
||||
func (s *TemplateService) FillWithDefaults(partyA, legalRepA, partyB, legalRepB string) (*FillTemplate, error) {
|
||||
// 构建默认填写组件
|
||||
components := []Component{
|
||||
{
|
||||
ComponentKey: "JFQY",
|
||||
ComponentValue: partyA,
|
||||
},
|
||||
{
|
||||
ComponentKey: "JFFR",
|
||||
ComponentValue: legalRepA,
|
||||
},
|
||||
{
|
||||
ComponentKey: "YFQY",
|
||||
ComponentValue: partyB,
|
||||
},
|
||||
{
|
||||
ComponentKey: "YFFR",
|
||||
ComponentValue: legalRepB,
|
||||
},
|
||||
{
|
||||
ComponentKey: "QDRQ",
|
||||
ComponentValue: formatDateForTemplate(),
|
||||
},
|
||||
}
|
||||
|
||||
return s.Fill(components)
|
||||
}
|
||||
|
||||
// FillWithCustomData 使用自定义数据填写模板
|
||||
// 允许传入自定义的组件数据来填写模板
|
||||
//
|
||||
// 参数说明:
|
||||
// - customComponents: 自定义组件数据
|
||||
//
|
||||
// 返回: 生成的文件ID和错误信息
|
||||
func (s *TemplateService) FillWithCustomData(customComponents map[string]string) (*FillTemplate, error) {
|
||||
var components []Component
|
||||
|
||||
// 将map转换为Component切片
|
||||
for key, value := range customComponents {
|
||||
components = append(components, Component{
|
||||
ComponentKey: key,
|
||||
ComponentValue: value,
|
||||
})
|
||||
}
|
||||
|
||||
return s.Fill(components)
|
||||
}
|
||||
|
||||
// CreateDefaultComponents 创建默认模板数据
|
||||
// 返回用于测试的默认模板填写数据
|
||||
//
|
||||
// 返回: 默认组件数据
|
||||
func CreateDefaultComponents() []Component {
|
||||
return []Component{
|
||||
{
|
||||
ComponentKey: "JFQY",
|
||||
ComponentValue: "海南省学宇思网络科技有限公司",
|
||||
},
|
||||
{
|
||||
ComponentKey: "JFFR",
|
||||
ComponentValue: "刘福思",
|
||||
},
|
||||
{
|
||||
ComponentKey: "YFQY",
|
||||
ComponentValue: "测试企业",
|
||||
},
|
||||
{
|
||||
ComponentKey: "YFFR",
|
||||
ComponentValue: "测试法人",
|
||||
},
|
||||
{
|
||||
ComponentKey: "QDRQ",
|
||||
ComponentValue: time.Now().Format("2006年01月02日"),
|
||||
},
|
||||
}
|
||||
}
|
||||
571
internal/shared/esign/types.go
Normal file
571
internal/shared/esign/types.go
Normal file
@@ -0,0 +1,571 @@
|
||||
package esign
|
||||
|
||||
import "time"
|
||||
|
||||
// ==================== 模板填写相关结构体 ====================
|
||||
|
||||
// FillTemplateRequest 模板填写请求结构体
|
||||
// 用于根据模板ID生成包含填写内容的文档
|
||||
type FillTemplateRequest struct {
|
||||
DocTemplateID string `json:"docTemplateId"` // 文档模板ID
|
||||
FileName string `json:"fileName"` // 生成的文件名
|
||||
Components []Component `json:"components"` // 填写组件列表
|
||||
}
|
||||
|
||||
// Component 控件结构体
|
||||
// 定义模板中需要填写的字段信息
|
||||
type Component struct {
|
||||
ComponentID string `json:"componentId,omitempty"` // 控件ID(可选)
|
||||
ComponentKey string `json:"componentKey,omitempty"` // 控件键名(可选)
|
||||
ComponentValue string `json:"componentValue"` // 控件值
|
||||
}
|
||||
|
||||
// FillTemplateResponse 模板填写响应结构体
|
||||
type FillTemplateResponse struct {
|
||||
Code int `json:"code"` // 响应码
|
||||
Message string `json:"message"` // 响应消息
|
||||
Data struct {
|
||||
FileID string `json:"fileId"` // 生成的文件ID
|
||||
FileDownloadUrl string `json:"fileDownloadUrl"` // 文件下载URL
|
||||
} `json:"data"`
|
||||
}
|
||||
type FillTemplate struct {
|
||||
FileID string `json:"fileId"` // 生成的文件ID
|
||||
FileDownloadUrl string `json:"fileDownloadUrl"` // 文件下载URL
|
||||
FileName string `json:"fileName"` // 文件名
|
||||
TemplateID string `json:"templateId"` // 模板ID
|
||||
FillTime time.Time `json:"fillTime"` // 填写时间
|
||||
}
|
||||
|
||||
// ==================== 签署流程相关结构体 ====================
|
||||
|
||||
// CreateSignFlowByFileRequest 发起签署请求结构体
|
||||
// 用于创建基于文件的签署流程
|
||||
type CreateSignFlowByFileRequest struct {
|
||||
Docs []DocInfo `json:"docs"` // 文档信息列表
|
||||
SignFlowConfig SignFlowConfig `json:"signFlowConfig"` // 签署流程配置
|
||||
Signers []SignerInfo `json:"signers"` // 签署人列表
|
||||
}
|
||||
|
||||
// CreateSignFlowByFileResponse 发起签署响应结构体
|
||||
type CreateSignFlowByFileResponse struct {
|
||||
Code int `json:"code"` // 响应码
|
||||
Message string `json:"message"` // 响应消息
|
||||
Data struct {
|
||||
SignFlowId string `json:"signFlowId"` // 签署流程ID
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// DocInfo 文档信息
|
||||
type DocInfo struct {
|
||||
FileId string `json:"fileId"` // 文件ID
|
||||
FileName string `json:"fileName"` // 文件名
|
||||
}
|
||||
|
||||
// SignFlowConfig 签署流程配置
|
||||
type SignFlowConfig struct {
|
||||
SignFlowTitle string `json:"signFlowTitle"` // 签署流程标题
|
||||
SignFlowExpireTime int64 `json:"signFlowExpireTime,omitempty"` // 签署流程过期时间
|
||||
AutoFinish bool `json:"autoFinish"` // 是否自动完结
|
||||
NotifyUrl string `json:"notifyUrl,omitempty"` // 回调通知URL
|
||||
RedirectConfig *RedirectConfig `json:"redirectConfig,omitempty"` // 重定向配置
|
||||
AuthConfig *AuthConfig `json:"authConfig,omitempty"` // 认证配置
|
||||
ContractConfig *ContractConfig `json:"contractConfig,omitempty"` // 合同配置
|
||||
}
|
||||
|
||||
// RedirectConfig 重定向配置
|
||||
type RedirectConfig struct {
|
||||
RedirectUrl string `json:"redirectUrl"` // 重定向URL
|
||||
}
|
||||
|
||||
// AuthConfig 认证配置
|
||||
type AuthConfig struct {
|
||||
PsnAvailableAuthModes []string `json:"psnAvailableAuthModes"` // 个人可用认证模式
|
||||
OrgAvailableAuthModes []string `json:"orgAvailableAuthModes"` // 机构可用认证模式
|
||||
WillingnessAuthModes []string `json:"willingnessAuthModes"` // 意愿认证模式
|
||||
AudioVideoTemplateId string `json:"audioVideoTemplateId"` // 音视频模板ID
|
||||
}
|
||||
|
||||
// ContractConfig 合同配置
|
||||
type ContractConfig struct {
|
||||
AllowToRescind bool `json:"allowToRescind"` // 是否允许撤销
|
||||
}
|
||||
|
||||
// ==================== 签署人相关结构体 ====================
|
||||
|
||||
// SignerInfo 签署人信息结构体
|
||||
type SignerInfo struct {
|
||||
SignConfig *SignConfig `json:"signConfig"` // 签署配置
|
||||
AuthConfig *AuthConfig `json:"authConfig"` // 认证配置
|
||||
NoticeConfig *NoticeConfig `json:"noticeConfig"` // 通知配置
|
||||
SignerType int `json:"signerType"` // 签署人类型:0-个人,1-机构
|
||||
PsnSignerInfo *PsnSignerInfo `json:"psnSignerInfo,omitempty"` // 个人签署人信息
|
||||
OrgSignerInfo *OrgSignerInfo `json:"orgSignerInfo,omitempty"` // 机构签署人信息
|
||||
SignFields []SignField `json:"signFields"` // 签署区列表
|
||||
}
|
||||
|
||||
// SignConfig 签署配置
|
||||
type SignConfig struct {
|
||||
SignOrder int `json:"signOrder"` // 签署顺序
|
||||
}
|
||||
|
||||
// NoticeConfig 通知配置
|
||||
type NoticeConfig struct {
|
||||
NoticeTypes string `json:"noticeTypes"` // 通知类型:1-短信,2-邮件,3-短信+邮件
|
||||
}
|
||||
|
||||
// PsnSignerInfo 个人签署人信息
|
||||
type PsnSignerInfo struct {
|
||||
PsnAccount string `json:"psnAccount"` // 个人账号
|
||||
PsnInfo *PsnInfo `json:"psnInfo"` // 个人信息
|
||||
}
|
||||
|
||||
// PsnInfo 个人基本信息
|
||||
type PsnInfo struct {
|
||||
PsnName string `json:"psnName"` // 个人姓名
|
||||
PsnIDCardNum string `json:"psnIDCardNum,omitempty"` // 身份证号
|
||||
PsnIDCardType string `json:"psnIDCardType,omitempty"` // 证件类型
|
||||
BankCardNum string `json:"bankCardNum,omitempty"` // 银行卡号
|
||||
}
|
||||
|
||||
// OrgSignerInfo 机构签署人信息
|
||||
type OrgSignerInfo struct {
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
OrgInfo *OrgInfo `json:"orgInfo"` // 机构信息
|
||||
TransactorInfo *TransactorInfo `json:"transactorInfo"` // 经办人信息
|
||||
}
|
||||
|
||||
// OrgInfo 机构信息
|
||||
type OrgInfo struct {
|
||||
LegalRepName string `json:"legalRepName"` // 法定代表人姓名
|
||||
LegalRepIDCardNum string `json:"legalRepIDCardNum"` // 法定代表人身份证号
|
||||
LegalRepIDCardType string `json:"legalRepIDCardType"` // 法定代表人证件类型
|
||||
OrgIDCardNum string `json:"orgIDCardNum"` // 机构证件号
|
||||
OrgIDCardType string `json:"orgIDCardType"` // 机构证件类型
|
||||
}
|
||||
|
||||
// TransactorInfo 经办人信息
|
||||
type TransactorInfo struct {
|
||||
PsnAccount string `json:"psnAccount"` // 经办人账号
|
||||
PsnInfo *PsnInfo `json:"psnInfo"` // 经办人信息
|
||||
}
|
||||
|
||||
// ==================== 签署区相关结构体 ====================
|
||||
|
||||
// SignField 签署区信息
|
||||
type SignField struct {
|
||||
CustomBizNum string `json:"customBizNum"` // 自定义业务号
|
||||
FileId string `json:"fileId"` // 文件ID
|
||||
NormalSignFieldConfig *NormalSignFieldConfig `json:"normalSignFieldConfig"` // 普通签署区配置
|
||||
}
|
||||
|
||||
// NormalSignFieldConfig 普通签署区配置
|
||||
type NormalSignFieldConfig struct {
|
||||
AutoSign bool `json:"autoSign,omitempty"` // 是否自动签署
|
||||
SignFieldStyle int `json:"signFieldStyle"` // 签署区样式:1-普通签章,2-骑缝签章
|
||||
SignFieldPosition *SignFieldPosition `json:"signFieldPosition"` // 签署区位置
|
||||
}
|
||||
|
||||
// SignFieldPosition 签署区位置
|
||||
type SignFieldPosition struct {
|
||||
PositionPage string `json:"positionPage"` // 页码
|
||||
PositionX float64 `json:"positionX"` // X坐标
|
||||
PositionY float64 `json:"positionY"` // Y坐标
|
||||
}
|
||||
|
||||
// ==================== 签署页面链接相关结构体 ====================
|
||||
|
||||
// GetSignUrlRequest 获取签署页面链接请求结构体
|
||||
type GetSignUrlRequest struct {
|
||||
NeedLogin bool `json:"needLogin,omitempty"` // 是否需要登录
|
||||
UrlType int `json:"urlType,omitempty"` // URL类型
|
||||
Operator *Operator `json:"operator"` // 操作人信息
|
||||
Organization *Organization `json:"organization,omitempty"` // 机构信息
|
||||
RedirectConfig *RedirectConfig `json:"redirectConfig,omitempty"` // 重定向配置
|
||||
ClientType string `json:"clientType,omitempty"` // 客户端类型
|
||||
AppScheme string `json:"appScheme,omitempty"` // 应用协议
|
||||
}
|
||||
|
||||
// Operator 操作人信息
|
||||
type Operator struct {
|
||||
PsnAccount string `json:"psnAccount,omitempty"` // 个人账号
|
||||
PsnId string `json:"psnId,omitempty"` // 个人ID
|
||||
}
|
||||
|
||||
// Organization 机构信息
|
||||
type Organization struct {
|
||||
OrgId string `json:"orgId,omitempty"` // 机构ID
|
||||
OrgName string `json:"orgName,omitempty"` // 机构名称
|
||||
}
|
||||
|
||||
// GetSignUrlResponse 获取签署页面链接响应结构体
|
||||
type GetSignUrlResponse struct {
|
||||
Code int `json:"code"` // 响应码
|
||||
Message string `json:"message"` // 响应消息
|
||||
Data struct {
|
||||
ShortUrl string `json:"shortUrl"` // 短链接
|
||||
Url string `json:"url"` // 完整链接
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ==================== 文件下载相关结构体 ====================
|
||||
|
||||
// DownloadSignedFileResponse 下载已签署文件响应结构体
|
||||
type DownloadSignedFileResponse struct {
|
||||
Code int `json:"code"` // 响应码
|
||||
Message string `json:"message"` // 响应消息
|
||||
Data struct {
|
||||
Files []SignedFileInfo `json:"files"` // 已签署文件列表
|
||||
Attachments []SignedFileInfo `json:"attachments"` // 附属材料列表
|
||||
CertificateDownloadUrl string `json:"certificateDownloadUrl"` // 证书下载链接
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// SignedFileInfo 已签署文件信息
|
||||
type SignedFileInfo struct {
|
||||
FileId string `json:"fileId"` // 文件ID
|
||||
FileName string `json:"fileName"` // 文件名
|
||||
DownloadUrl string `json:"downloadUrl"` // 下载链接
|
||||
}
|
||||
|
||||
// ==================== 流程查询相关结构体 ====================
|
||||
|
||||
// QuerySignFlowDetailResponse 查询签署流程详情响应结构体
|
||||
type QuerySignFlowDetailResponse struct {
|
||||
Code int `json:"code"` // 响应码
|
||||
Message string `json:"message"` // 响应消息
|
||||
Data struct {
|
||||
SignFlowStatus int32 `json:"signFlowStatus"` // 签署流程状态
|
||||
SignFlowDescription string `json:"signFlowDescription"` // 签署流程描述
|
||||
RescissionStatus int32 `json:"rescissionStatus"` // 撤销状态
|
||||
RescissionSignFlowIds []string `json:"rescissionSignFlowIds"` // 撤销的签署流程ID列表
|
||||
RevokeReason string `json:"revokeReason"` // 撤销原因
|
||||
SignFlowCreateTime int64 `json:"signFlowCreateTime"` // 签署流程创建时间
|
||||
SignFlowStartTime int64 `json:"signFlowStartTime"` // 签署流程开始时间
|
||||
SignFlowFinishTime int64 `json:"signFlowFinishTime"` // 签署流程完成时间
|
||||
SignFlowInitiator *SignFlowInitiator `json:"signFlowInitiator"` // 签署流程发起方
|
||||
SignFlowConfig *SignFlowConfigDetail `json:"signFlowConfig"` // 签署流程配置详情
|
||||
Docs []DocDetail `json:"docs"` // 文档详情列表
|
||||
Attachments []AttachmentDetail `json:"attachments"` // 附属材料详情列表
|
||||
Signers []SignerDetail `json:"signers"` // 签署人详情列表
|
||||
Copiers []CopierDetail `json:"copiers"` // 抄送方详情列表
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// SignFlowInitiator 签署流程发起方
|
||||
type SignFlowInitiator struct {
|
||||
PsnInitiator *PsnInitiator `json:"psnInitiator"` // 个人发起方
|
||||
OrgInitiator *OrgInitiator `json:"orgInitiator"` // 机构发起方
|
||||
}
|
||||
|
||||
// PsnInitiator 个人发起方
|
||||
type PsnInitiator struct {
|
||||
PsnId string `json:"psnId"` // 个人ID
|
||||
PsnName string `json:"psnName"` // 个人姓名
|
||||
}
|
||||
|
||||
// OrgInitiator 机构发起方
|
||||
type OrgInitiator struct {
|
||||
OrgId string `json:"orgId"` // 机构ID
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
Transactor *Transactor `json:"transactor"` // 经办人
|
||||
}
|
||||
|
||||
// Transactor 经办人
|
||||
type Transactor struct {
|
||||
PsnId string `json:"psnId"` // 个人ID
|
||||
PsnName string `json:"psnName"` // 个人姓名
|
||||
}
|
||||
|
||||
// SignFlowConfigDetail 签署流程配置详情
|
||||
type SignFlowConfigDetail struct {
|
||||
SignFlowTitle string `json:"signFlowTitle"` // 签署流程标题
|
||||
ContractGroupIds []string `json:"contractGroupIds"` // 合同组ID列表
|
||||
AutoFinish bool `json:"autoFinish"` // 是否自动完结
|
||||
SignFlowExpireTime int64 `json:"signFlowExpireTime"` // 签署流程过期时间
|
||||
NotifyUrl string `json:"notifyUrl"` // 回调通知URL
|
||||
ChargeConfig *ChargeConfig `json:"chargeConfig"` // 计费配置
|
||||
NoticeConfig *NoticeConfig `json:"noticeConfig"` // 通知配置
|
||||
SignConfig *SignConfigDetail `json:"signConfig"` // 签署配置详情
|
||||
AuthConfig *AuthConfig `json:"authConfig"` // 认证配置
|
||||
}
|
||||
|
||||
// ChargeConfig 计费配置
|
||||
type ChargeConfig struct {
|
||||
ChargeMode int `json:"chargeMode"` // 计费模式
|
||||
OrderType string `json:"orderType"` // 订单类型
|
||||
BarrierCode string `json:"barrierCode"` // 障碍码
|
||||
}
|
||||
|
||||
// SignConfigDetail 签署配置详情
|
||||
type SignConfigDetail struct {
|
||||
AvailableSignClientTypes string `json:"availableSignClientTypes"` // 可用签署客户端类型
|
||||
ShowBatchDropSealButton bool `json:"showBatchDropSealButton"` // 是否显示批量盖章按钮
|
||||
SignTipsTitle string `json:"signTipsTitle"` // 签署提示标题
|
||||
SignTipsContent string `json:"signTipsContent"` // 签署提示内容
|
||||
SignMode string `json:"signMode"` // 签署模式
|
||||
DedicatedCloudId string `json:"dedicatedCloudId"` // 专属云ID
|
||||
}
|
||||
|
||||
// DocDetail 文档详情
|
||||
type DocDetail struct {
|
||||
FileId string `json:"fileId"` // 文件ID
|
||||
FileName string `json:"fileName"` // 文件名
|
||||
FileEditPwd string `json:"fileEditPwd"` // 文件编辑密码
|
||||
ContractNum string `json:"contractNum"` // 合同编号
|
||||
ContractBizTypeId string `json:"contractBizTypeId"` // 合同业务类型ID
|
||||
}
|
||||
|
||||
// AttachmentDetail 附属材料详情
|
||||
type AttachmentDetail struct {
|
||||
FileId string `json:"fileId"` // 文件ID
|
||||
FileName string `json:"fileName"` // 文件名
|
||||
SignerUpload bool `json:"signerUpload"` // 是否签署人上传
|
||||
}
|
||||
|
||||
// CopierDetail 抄送方详情
|
||||
type CopierDetail struct {
|
||||
CopierPsnInfo *CopierPsnInfo `json:"copierPsnInfo"` // 个人抄送方
|
||||
CopierOrgInfo *CopierOrgInfo `json:"copierOrgInfo"` // 机构抄送方
|
||||
}
|
||||
|
||||
// CopierPsnInfo 个人抄送方
|
||||
type CopierPsnInfo struct {
|
||||
PsnId string `json:"psnId"` // 个人ID
|
||||
PsnAccount string `json:"psnAccount"` // 个人账号
|
||||
}
|
||||
|
||||
// CopierOrgInfo 机构抄送方
|
||||
type CopierOrgInfo struct {
|
||||
OrgId string `json:"orgId"` // 机构ID
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
}
|
||||
|
||||
// SignerDetail 签署人详情
|
||||
type SignerDetail struct {
|
||||
PsnSigner *PsnSignerDetail `json:"psnSigner,omitempty"` // 个人签署人详情
|
||||
OrgSigner *OrgSignerDetail `json:"orgSigner,omitempty"` // 机构签署人详情
|
||||
SignerType int `json:"signerType"` // 签署人类型
|
||||
SignOrder int `json:"signOrder"` // 签署顺序
|
||||
SignStatus int `json:"signStatus"` // 签署状态
|
||||
SignFields []SignFieldDetail `json:"signFields"` // 签署区详情列表
|
||||
}
|
||||
|
||||
// PsnSignerDetail 个人签署人详情
|
||||
type PsnSignerDetail struct {
|
||||
PsnId string `json:"psnId"` // 个人ID
|
||||
PsnName string `json:"psnName"` // 个人姓名
|
||||
PsnAccount *PsnAccount `json:"psnAccount"` // 个人账号信息
|
||||
}
|
||||
|
||||
// PsnAccount 个人账号信息
|
||||
type PsnAccount struct {
|
||||
AccountMobile string `json:"accountMobile"` // 账号手机号
|
||||
AccountEmail string `json:"accountEmail"` // 账号邮箱
|
||||
}
|
||||
|
||||
// OrgSignerDetail 机构签署人详情
|
||||
type OrgSignerDetail struct {
|
||||
OrgId string `json:"orgId"` // 机构ID
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
OrgAccount string `json:"orgAccount"` // 机构账号
|
||||
}
|
||||
|
||||
// SignFieldDetail 签署区详情
|
||||
type SignFieldDetail struct {
|
||||
SignFieldId string `json:"signFieldId"` // 签署区ID
|
||||
SignFieldStatus string `json:"signFieldStatus"` // 签署区状态
|
||||
SealApprovalFlowId string `json:"sealApprovalFlowId"` // 印章审批流程ID
|
||||
StatusUpdateTime int64 `json:"statusUpdateTime"` // 状态更新时间
|
||||
FailReason string `json:"failReason"` // 失败原因
|
||||
CustomBizNum string `json:"customBizNum"` // 自定义业务号
|
||||
FileId string `json:"fileId"` // 文件ID
|
||||
SignFieldType int `json:"signFieldType"` // 签署区类型
|
||||
MustSign bool `json:"mustSign"` // 是否必须签署
|
||||
SignFieldSealType int `json:"signFieldSealType"` // 签署区印章类型
|
||||
NormalSignFieldConfig *NormalSignFieldDetail `json:"normalSignFieldConfig"` // 普通签署区配置详情
|
||||
}
|
||||
|
||||
// NormalSignFieldDetail 普通签署区配置详情
|
||||
type NormalSignFieldDetail struct {
|
||||
FreeMode bool `json:"freeMode"` // 是否自由模式
|
||||
SignFieldStyle int `json:"signFieldStyle"` // 签署区样式
|
||||
SignFieldPosition *SignFieldPosition `json:"signFieldPosition"` // 签署区位置
|
||||
MovableSignField bool `json:"movableSignField"` // 是否可移动签署区
|
||||
AutoSign bool `json:"autoSign"` // 是否自动签署
|
||||
SealStyle string `json:"sealStyle"` // 印章样式
|
||||
SealId string `json:"sealId"` // 印章ID
|
||||
}
|
||||
|
||||
// ==================== 机构认证相关结构体 ====================
|
||||
|
||||
// GetOrgAuthUrlRequest 获取机构认证&授权页面链接请求结构体
|
||||
type GetOrgAuthUrlRequest struct {
|
||||
OrgAuthConfig *OrgAuthConfig `json:"orgAuthConfig"` // 机构认证配置
|
||||
AuthorizeConfig *AuthorizeConfig `json:"authorizeConfig,omitempty"` // 授权配置
|
||||
RedirectConfig *RedirectConfig `json:"redirectConfig,omitempty"` // 重定向配置
|
||||
ClientType string `json:"clientType,omitempty"` // 客户端类型
|
||||
NotifyUrl string `json:"notifyUrl,omitempty"` // 回调通知URL
|
||||
AppScheme string `json:"appScheme,omitempty"` // 应用协议
|
||||
}
|
||||
|
||||
// OrgAuthConfig 机构认证授权相关结构体
|
||||
type OrgAuthConfig struct {
|
||||
OrgName string `json:"orgName,omitempty"` // 机构名称
|
||||
OrgId string `json:"orgId,omitempty"` // 机构ID
|
||||
OrgInfo *OrgAuthInfo `json:"orgInfo,omitempty"` // 机构信息
|
||||
TransactorAuthPageConfig *TransactorAuthPageConfig `json:"transactorAuthPageConfig,omitempty"` // 经办人认证页面配置
|
||||
TransactorInfo *TransactorAuthInfo `json:"transactorInfo,omitempty"` // 经办人信息
|
||||
}
|
||||
|
||||
// OrgAuthInfo 机构认证信息
|
||||
type OrgAuthInfo struct {
|
||||
OrgIDCardNum string `json:"orgIDCardNum,omitempty"` // 机构证件号
|
||||
OrgIDCardType string `json:"orgIDCardType,omitempty"` // 机构证件类型
|
||||
LegalRepName string `json:"legalRepName,omitempty"` // 法定代表人姓名
|
||||
LegalRepIDCardNum string `json:"legalRepIDCardNum,omitempty"` // 法定代表人身份证号
|
||||
LegalRepIDCardType string `json:"legalRepIDCardType,omitempty"` // 法定代表人证件类型
|
||||
OrgBankAccountNum string `json:"orgBankAccountNum,omitempty"` // 机构银行账号
|
||||
}
|
||||
|
||||
// TransactorAuthPageConfig 经办人认证页面配置
|
||||
type TransactorAuthPageConfig struct {
|
||||
PsnAvailableAuthModes []string `json:"psnAvailableAuthModes,omitempty"` // 个人可用认证模式
|
||||
PsnDefaultAuthMode string `json:"psnDefaultAuthMode,omitempty"` // 个人默认认证模式
|
||||
PsnEditableFields []string `json:"psnEditableFields,omitempty"` // 个人可编辑字段
|
||||
}
|
||||
|
||||
// TransactorAuthInfo 经办人认证信息
|
||||
type TransactorAuthInfo struct {
|
||||
PsnAccount string `json:"psnAccount,omitempty"` // 经办人账号
|
||||
PsnInfo *PsnAuthInfo `json:"psnInfo,omitempty"` // 经办人信息
|
||||
}
|
||||
|
||||
// PsnAuthInfo 个人认证信息
|
||||
type PsnAuthInfo struct {
|
||||
PsnName string `json:"psnName,omitempty"` // 个人姓名
|
||||
PsnIDCardNum string `json:"psnIDCardNum,omitempty"` // 身份证号
|
||||
PsnIDCardType string `json:"psnIDCardType,omitempty"` // 证件类型
|
||||
PsnMobile string `json:"psnMobile,omitempty"` // 手机号
|
||||
PsnIdentityVerify bool `json:"psnIdentityVerify,omitempty"` // 是否身份验证
|
||||
}
|
||||
|
||||
// AuthorizeConfig 授权配置
|
||||
type AuthorizeConfig struct {
|
||||
AuthorizedScopes []string `json:"authorizedScopes,omitempty"` // 授权范围
|
||||
}
|
||||
|
||||
// GetOrgAuthUrlResponse 获取机构认证&授权页面链接响应结构体
|
||||
type GetOrgAuthUrlResponse struct {
|
||||
Code int `json:"code"` // 响应码
|
||||
Message string `json:"message"` // 响应消息
|
||||
Data struct {
|
||||
AuthFlowId string `json:"authFlowId"` // 认证流程ID
|
||||
AuthUrl string `json:"authUrl"` // 认证链接
|
||||
AuthShortUrl string `json:"authShortUrl"` // 认证短链接
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ==================== 机构认证查询相关结构体 ====================
|
||||
type OrgIDCardType string
|
||||
|
||||
const (
|
||||
OrgIDCardTypeUSCC OrgIDCardType = "CRED_ORG_USCC" // 统一社会信用代码
|
||||
OrgIDCardTypeREGCODE OrgIDCardType = "CRED_ORG_REGCODE" // 工商注册号
|
||||
)
|
||||
|
||||
// QueryOrgIdentityRequest 查询机构认证信息请求
|
||||
type QueryOrgIdentityRequest struct {
|
||||
OrgID string `json:"orgId,omitempty"` // 机构账号ID
|
||||
OrgName string `json:"orgName,omitempty"` // 组织机构名称
|
||||
OrgIDCardNum string `json:"orgIDCardNum,omitempty"` // 组织机构证件号
|
||||
OrgIDCardType OrgIDCardType `json:"orgIDCardType,omitempty"` // 组织机构证件类型,只能为OrgIDCardTypeUSCC或OrgIDCardTypeREGCODE
|
||||
}
|
||||
|
||||
// QueryOrgIdentityResponse 查询机构认证信息响应
|
||||
type QueryOrgIdentityResponse struct {
|
||||
Code int32 `json:"code"` // 业务码,0表示成功
|
||||
Message string `json:"message"` // 业务信息
|
||||
Data struct {
|
||||
RealnameStatus int32 `json:"realnameStatus"` // 实名认证状态 (0-未实名, 1-已实名)
|
||||
AuthorizeUserInfo bool `json:"authorizeUserInfo"` // 是否授权身份信息给当前应用
|
||||
OrgID string `json:"orgId"` // 机构账号ID
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
OrgAuthMode string `json:"orgAuthMode"` // 机构实名认证方式
|
||||
OrgInfo struct {
|
||||
OrgIDCardNum string `json:"orgIDCardNum"` // 组织机构证件号
|
||||
OrgIDCardType string `json:"orgIDCardType"` // 组织机构证件号类型
|
||||
LegalRepName string `json:"legalRepName"` // 法定代表人姓名
|
||||
LegalRepIDCardNum string `json:"legalRepIDCardNum"` // 法定代表人证件号
|
||||
LegalRepIDCardType string `json:"legalRepIDCardType"` // 法定代表人证件类型
|
||||
CorporateAccount string `json:"corporateAccount"` // 机构对公账户名称
|
||||
OrgBankAccountNum string `json:"orgBankAccountNum"` // 机构对公打款银行卡号
|
||||
CnapsCode string `json:"cnapsCode"` // 机构对公打款银行联行号
|
||||
AuthorizationDownloadUrl string `json:"authorizationDownloadUrl"` // 授权委托书下载地址
|
||||
LicenseDownloadUrl string `json:"licenseDownloadUrl"` // 营业执照照片下载地址
|
||||
AdminName string `json:"adminName"` // 机构管理员姓名(脱敏)
|
||||
AdminAccount string `json:"adminAccount"` // 机构管理员联系方式(脱敏)
|
||||
} `json:"orgInfo"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// ==================== 结果结构体 ====================
|
||||
|
||||
// SignResult 签署结果结构体
|
||||
// 包含签署流程的完整结果信息
|
||||
type SignResult struct {
|
||||
FileID string `json:"fileId"` // 文件ID
|
||||
SignFlowID string `json:"signFlowId"` // 签署流程ID
|
||||
SignUrl string `json:"signUrl"` // 签署链接
|
||||
ShortUrl string `json:"shortUrl"` // 短链接
|
||||
DownloadSignedFileResult *DownloadSignedFileResponse `json:"downloadSignedFileResult,omitempty"` // 下载已签署文件结果
|
||||
QuerySignFlowDetailResult *QuerySignFlowDetailResponse `json:"querySignFlowDetailResult,omitempty"` // 查询签署流程详情结果
|
||||
}
|
||||
|
||||
// ==================== 请求结构体优化 ====================
|
||||
|
||||
// SignProcessRequest 签署流程请求结构体
|
||||
type SignProcessRequest struct {
|
||||
SignerAccount string `json:"signerAccount"` // 签署人账号(统一社会信用代码)
|
||||
SignerName string `json:"signerName"` // 签署人名称
|
||||
TransactorPhone string `json:"transactorPhone"` // 经办人手机号
|
||||
TransactorName string `json:"transactorName"` // 经办人姓名
|
||||
TransactorIDCardNum string `json:"transactorIdCardNum"` // 经办人身份证号
|
||||
TransactorMobile string `json:"transactorMobile"` // 经办人手机号
|
||||
IncludeDownloadAndQuery bool `json:"includeDownloadAndQuery"` // 是否包含下载和查询步骤
|
||||
CustomComponents map[string]string `json:"customComponents,omitempty"` // 自定义模板组件数据
|
||||
}
|
||||
|
||||
// OrgAuthUrlRequest 机构认证链接请求结构体
|
||||
type OrgAuthUrlRequest struct {
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
OrgIDCardNum string `json:"orgIdCardNum"` // 机构证件号
|
||||
LegalRepName string `json:"legalRepName"` // 法定代表人姓名
|
||||
LegalRepIDCardNum string `json:"legalRepIdCardNum"` // 法定代表人身份证号
|
||||
TransactorPhone string `json:"transactorPhone"` // 经办人手机号
|
||||
TransactorName string `json:"transactorName"` // 经办人姓名
|
||||
TransactorIDCardNum string `json:"transactorIdCardNum"` // 经办人身份证号
|
||||
TransactorMobile string `json:"transactorMobile"` // 经办人手机号
|
||||
}
|
||||
|
||||
// CreateSignFlowRequest 创建签署流程请求结构体
|
||||
type CreateSignFlowRequest struct {
|
||||
FileID string `json:"fileId"` // 文件ID
|
||||
SignerAccount string `json:"signerAccount"` // 签署人账号
|
||||
SignerName string `json:"signerName"` // 签署人名称
|
||||
TransactorPhone string `json:"transactorPhone"` // 经办人手机号
|
||||
TransactorName string `json:"transactorName"` // 经办人姓名
|
||||
TransactorIDCardNum string `json:"transactorIdCardNum"` // 经办人身份证号
|
||||
}
|
||||
|
||||
// SimplifiedGetSignUrlRequest 简化获取签署链接请求结构体 (避免与现有冲突)
|
||||
type SimplifiedGetSignUrlRequest struct {
|
||||
SignFlowID string `json:"signFlowId"` // 签署流程ID
|
||||
PsnAccount string `json:"psnAccount"` // 个人账号(手机号)
|
||||
OrgName string `json:"orgName"` // 机构名称
|
||||
}
|
||||
|
||||
// SimplifiedFillTemplateRequest 简化填写模板请求结构体
|
||||
type SimplifiedFillTemplateRequest struct {
|
||||
Components []Component `json:"components"` // 填写组件列表
|
||||
}
|
||||
104
internal/shared/esign/utils.go
Normal file
104
internal/shared/esign/utils.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// generateSignature 生成e签宝API请求签名
|
||||
// 使用HMAC-SHA256算法对请求参数进行签名
|
||||
//
|
||||
// 参数说明:
|
||||
// - appSecret: 应用密钥
|
||||
// - httpMethod: HTTP方法(GET、POST等)
|
||||
// - accept: Accept头值
|
||||
// - contentMD5: 请求体MD5值
|
||||
// - contentType: Content-Type头值
|
||||
// - date: Date头值
|
||||
// - headers: 自定义头部信息
|
||||
// - pathAndParameters: 请求路径和参数
|
||||
//
|
||||
// 返回: Base64编码的签名字符串
|
||||
func generateSignature(appSecret, httpMethod, accept, contentMD5, contentType, date, headers, pathAndParameters string) string {
|
||||
// 构建待签名字符串,按照e签宝API规范拼接
|
||||
signStr := httpMethod + "\n" + accept + "\n" + contentMD5 + "\n" + contentType + "\n" + date + "\n" + headers + pathAndParameters
|
||||
|
||||
// 使用HMAC-SHA256计算签名
|
||||
h := hmac.New(sha256.New, []byte(appSecret))
|
||||
h.Write([]byte(signStr))
|
||||
digestBytes := h.Sum(nil)
|
||||
|
||||
// 对摘要结果进行Base64编码
|
||||
signature := base64.StdEncoding.EncodeToString(digestBytes)
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
// generateNonce 生成随机字符串
|
||||
// 使用当前时间的纳秒数作为随机字符串
|
||||
//
|
||||
// 返回: 纳秒时间戳字符串
|
||||
func generateNonce() string {
|
||||
return strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
}
|
||||
|
||||
// getContentMD5 计算请求体的MD5值
|
||||
// 对请求体进行MD5哈希计算,然后进行Base64编码
|
||||
//
|
||||
// 参数:
|
||||
// - body: 请求体字节数组
|
||||
//
|
||||
// 返回: Base64编码的MD5值
|
||||
func getContentMD5(body []byte) string {
|
||||
md5Sum := md5.Sum(body)
|
||||
return base64.StdEncoding.EncodeToString(md5Sum[:])
|
||||
}
|
||||
|
||||
// getCurrentTimestamp 获取当前时间戳(毫秒)
|
||||
//
|
||||
// 返回: 毫秒级时间戳字符串
|
||||
func getCurrentTimestamp() string {
|
||||
return strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
|
||||
}
|
||||
|
||||
// getCurrentDate 获取当前UTC时间字符串
|
||||
// 格式: "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||
//
|
||||
// 返回: RFC1123格式的UTC时间字符串
|
||||
func getCurrentDate() string {
|
||||
return time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
||||
}
|
||||
|
||||
// formatDateForTemplate 格式化日期用于模板填写
|
||||
// 格式: "2006年01月02日"
|
||||
//
|
||||
// 返回: 中文格式的日期字符串
|
||||
func formatDateForTemplate() string {
|
||||
return time.Now().Format("2006年01月02日")
|
||||
}
|
||||
|
||||
// generateFileName 生成带时间戳的文件名
|
||||
//
|
||||
// 参数:
|
||||
// - baseName: 基础文件名
|
||||
// - extension: 文件扩展名
|
||||
//
|
||||
// 返回: 带时间戳的文件名
|
||||
func generateFileName(baseName, extension string) string {
|
||||
timestamp := time.Now().Format("20060102_150405")
|
||||
return baseName + "_" + timestamp + "." + extension
|
||||
}
|
||||
|
||||
// calculateExpireTime 计算过期时间戳
|
||||
//
|
||||
// 参数:
|
||||
// - days: 过期天数
|
||||
//
|
||||
// 返回: 毫秒级时间戳
|
||||
func calculateExpireTime(days int) int64 {
|
||||
return time.Now().AddDate(0, 0, days).UnixMilli()
|
||||
}
|
||||
Reference in New Issue
Block a user