Files
tyapi-server/internal/infrastructure/external/esign/certification_esign_service.go
2025-07-28 01:46:39 +08:00

301 lines
8.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package esign
import (
"context"
"fmt"
"time"
"go.uber.org/zap"
"tyapi-server/internal/domains/certification/entities/value_objects"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
"tyapi-server/internal/shared/esign"
)
// ================ 常量定义 ================
const (
// 企业认证超时时间
EnterpriseAuthTimeout = 30 * time.Minute
// 合同签署超时时间
ContractSignTimeout = 7 * 24 * time.Hour // 7天
// 回调重试次数
MaxCallbackRetries = 3
)
// ================ 服务实现 ================
// CertificationEsignService 认证e签宝服务实现
//
// 业务职责:
// - 处理企业认证流程
// - 处理合同生成和签署
// - 处理e签宝回调
// - 管理认证状态更新
type CertificationEsignService struct {
esignClient *esign.Client
commandRepo repositories.CertificationCommandRepository
queryRepo repositories.CertificationQueryRepository
logger *zap.Logger
}
// NewCertificationEsignService 创建认证e签宝服务
func NewCertificationEsignService(
esignClient *esign.Client,
commandRepo repositories.CertificationCommandRepository,
queryRepo repositories.CertificationQueryRepository,
logger *zap.Logger,
) *CertificationEsignService {
return &CertificationEsignService{
esignClient: esignClient,
commandRepo: commandRepo,
queryRepo: queryRepo,
logger: logger,
}
}
// ================ 企业认证流程 ================
// StartEnterpriseAuth 开始企业认证
//
// 业务流程:
// 1. 调用e签宝企业认证API
// 2. 更新认证记录的auth_flow_id
// 3. 更新状态为企业认证中
//
// 参数:
// - ctx: 上下文
// - certificationID: 认证ID
// - enterpriseInfo: 企业信息
//
// 返回:
// - authURL: 认证URL
// - error: 错误信息
func (s *CertificationEsignService) StartEnterpriseAuth(
ctx context.Context,
certificationID string,
enterpriseInfo *value_objects.EnterpriseInfo,
) (string, error) {
s.logger.Info("开始企业认证",
zap.String("certification_id", certificationID),
zap.String("company_name", enterpriseInfo.CompanyName))
// TODO: 实现e签宝企业认证API调用
// 暂时使用模拟响应
authFlowID := fmt.Sprintf("auth_%s_%d", certificationID, time.Now().Unix())
authURL := fmt.Sprintf("https://esign.example.com/auth/%s", authFlowID)
s.logger.Info("模拟调用e签宝企业认证API",
zap.String("auth_flow_id", authFlowID),
zap.String("auth_url", authURL))
// 更新认证记录
if err := s.commandRepo.UpdateAuthFlowID(ctx, certificationID, authFlowID); err != nil {
s.logger.Error("更新认证流程ID失败", zap.Error(err))
return "", fmt.Errorf("更新认证流程ID失败: %w", err)
}
s.logger.Info("企业认证启动成功",
zap.String("certification_id", certificationID),
zap.String("auth_flow_id", authFlowID))
return authURL, nil
}
// HandleEnterpriseAuthCallback 处理企业认证回调
//
// 业务流程:
// 1. 根据回调信息查找认证记录
// 2. 根据回调状态更新认证状态
// 3. 如果成功,继续合同生成流程
//
// 参数:
// - ctx: 上下文
// - authFlowID: 认证流程ID
// - success: 是否成功
// - message: 回调消息
//
// 返回:
// - error: 错误信息
func (s *CertificationEsignService) HandleEnterpriseAuthCallback(
ctx context.Context,
authFlowID string,
success bool,
message string,
) error {
s.logger.Info("处理企业认证回调",
zap.String("auth_flow_id", authFlowID),
zap.Bool("success", success))
// 查找认证记录
cert, err := s.queryRepo.FindByAuthFlowID(ctx, authFlowID)
if err != nil {
s.logger.Error("根据认证流程ID查找认证记录失败", zap.Error(err))
return fmt.Errorf("查找认证记录失败: %w", err)
}
if success {
// 企业认证成功,更新状态
if err := s.commandRepo.UpdateStatus(ctx, cert.ID, enums.StatusEnterpriseVerified); err != nil {
s.logger.Error("更新认证状态失败", zap.Error(err))
return fmt.Errorf("更新认证状态失败: %w", err)
}
s.logger.Info("企业认证成功", zap.String("certification_id", cert.ID))
} else {
// 企业认证失败,更新状态
if err := s.commandRepo.UpdateStatus(ctx, cert.ID, enums.StatusInfoRejected); err != nil {
s.logger.Error("更新认证状态失败", zap.Error(err))
return fmt.Errorf("更新认证状态失败: %w", err)
}
s.logger.Info("企业认证失败", zap.String("certification_id", cert.ID), zap.String("reason", message))
}
return nil
}
// ================ 合同管理流程 ================
// GenerateContract 生成认证合同
//
// 业务流程:
// 1. 调用e签宝合同生成API
// 2. 更新认证记录的合同信息
// 3. 更新状态为合同已生成
//
// 参数:
// - ctx: 上下文
// - certificationID: 认证ID
//
// 返回:
// - contractSignURL: 合同签署URL
// - error: 错误信息
func (s *CertificationEsignService) GenerateContract(
ctx context.Context,
certificationID string,
) (string, error) {
s.logger.Info("生成认证合同", zap.String("certification_id", certificationID))
// TODO: 实现e签宝合同生成API调用
// 暂时使用模拟响应
contractFileID := fmt.Sprintf("contract_%s_%d", certificationID, time.Now().Unix())
esignFlowID := fmt.Sprintf("flow_%s_%d", certificationID, time.Now().Unix())
contractURL := fmt.Sprintf("https://esign.example.com/contract/%s", contractFileID)
contractSignURL := fmt.Sprintf("https://esign.example.com/sign/%s", esignFlowID)
s.logger.Info("模拟调用e签宝合同生成API",
zap.String("contract_file_id", contractFileID),
zap.String("esign_flow_id", esignFlowID))
// 更新认证记录
if err := s.commandRepo.UpdateContractInfo(
ctx,
certificationID,
contractFileID,
esignFlowID,
contractURL,
contractSignURL,
); err != nil {
s.logger.Error("更新合同信息失败", zap.Error(err))
return "", fmt.Errorf("更新合同信息失败: %w", err)
}
// 更新状态
if err := s.commandRepo.UpdateStatus(ctx, certificationID, enums.StatusContractApplied); err != nil {
s.logger.Error("更新认证状态失败", zap.Error(err))
return "", fmt.Errorf("更新认证状态失败: %w", err)
}
s.logger.Info("认证合同生成成功",
zap.String("certification_id", certificationID),
zap.String("contract_file_id", contractFileID))
return contractSignURL, nil
}
// HandleContractSignCallback 处理合同签署回调
//
// 业务流程:
// 1. 根据回调信息查找认证记录
// 2. 根据回调状态更新认证状态
// 3. 如果成功,认证流程完成
//
// 参数:
// - ctx: 上下文
// - esignFlowID: e签宝流程ID
// - success: 是否成功
// - signedFileURL: 已签署文件URL
//
// 返回:
// - error: 错误信息
func (s *CertificationEsignService) HandleContractSignCallback(
ctx context.Context,
esignFlowID string,
success bool,
signedFileURL string,
) error {
s.logger.Info("处理合同签署回调",
zap.String("esign_flow_id", esignFlowID),
zap.Bool("success", success))
// 查找认证记录
cert, err := s.queryRepo.FindByEsignFlowID(ctx, esignFlowID)
if err != nil {
s.logger.Error("根据e签宝流程ID查找认证记录失败", zap.Error(err))
return fmt.Errorf("查找认证记录失败: %w", err)
}
if success {
// 合同签署成功更新合同URL
if err := s.commandRepo.UpdateContractInfo(ctx, cert.ID, cert.ContractFileID, cert.EsignFlowID, signedFileURL, cert.ContractSignURL); err != nil {
s.logger.Error("更新合同URL失败", zap.Error(err))
return fmt.Errorf("更新合同URL失败: %w", err)
}
// 更新状态到合同已签署
if err := s.commandRepo.UpdateStatus(ctx, cert.ID, enums.StatusContractSigned); err != nil {
s.logger.Error("更新认证状态失败", zap.Error(err))
return fmt.Errorf("更新认证状态失败: %w", err)
}
s.logger.Info("合同签署成功", zap.String("certification_id", cert.ID))
} else {
// 合同签署失败
if err := s.commandRepo.UpdateStatus(ctx, cert.ID, enums.StatusContractRejected); err != nil {
s.logger.Error("更新认证状态失败", zap.Error(err))
return fmt.Errorf("更新认证状态失败: %w", err)
}
s.logger.Info("合同签署失败", zap.String("certification_id", cert.ID))
}
return nil
}
// ================ 辅助方法 ================
// GetContractSignURL 获取合同签署URL
//
// 参数:
// - ctx: 上下文
// - certificationID: 认证ID
//
// 返回:
// - signURL: 签署URL
// - error: 错误信息
func (s *CertificationEsignService) GetContractSignURL(ctx context.Context, certificationID string) (string, error) {
cert, err := s.queryRepo.GetByID(ctx, certificationID)
if err != nil {
return "", fmt.Errorf("获取认证信息失败: %w", err)
}
if cert.ContractSignURL == "" {
return "", fmt.Errorf("合同签署URL尚未生成")
}
return cert.ContractSignURL, nil
}