Files
tyapi-server/internal/domains/certification/services/state_machine/esign_callback_handler.go

391 lines
12 KiB
Go
Raw Normal View History

2025-07-21 15:13:26 +08:00
package state_machine
import (
"context"
"encoding/json"
"fmt"
"tyapi-server/internal/domains/certification/enums"
"go.uber.org/zap"
)
// EsignCallbackData e签宝回调数据结构
type EsignCallbackData struct {
// 基础信息
Action string `json:"action"` // 回调动作类型
FlowID string `json:"flow_id"` // 流程ID
AccountID string `json:"account_id"` // 账户ID
Status string `json:"status"` // 状态
Message string `json:"message"` // 消息
Timestamp int64 `json:"timestamp"` // 时间戳
// 扩展数据
Data map[string]interface{} `json:"data,omitempty"` // 扩展数据
OriginalData string `json:"original_data"` // 原始回调数据
}
// EsignCallbackHandler e签宝回调处理器
// 负责处理e签宝的异步回调将外部回调转换为内部状态转换
type EsignCallbackHandler struct {
stateMachine *CertificationStateMachine
logger *zap.Logger
}
// NewEsignCallbackHandler 创建e签宝回调处理器
func NewEsignCallbackHandler(
stateMachine *CertificationStateMachine,
logger *zap.Logger,
) *EsignCallbackHandler {
return &EsignCallbackHandler{
stateMachine: stateMachine,
logger: logger,
}
}
// HandleCallback 处理e签宝回调
func (h *EsignCallbackHandler) HandleCallback(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("接收到e签宝回调",
zap.String("certification_id", certificationID),
zap.String("action", callbackData.Action),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
// 根据动作类型分发处理
switch callbackData.Action {
case "auth_result":
return h.handleAuthResult(ctx, certificationID, callbackData)
case "sign_result":
return h.handleSignResult(ctx, certificationID, callbackData)
case "flow_status":
return h.handleFlowStatus(ctx, certificationID, callbackData)
default:
h.logger.Warn("未知的e签宝回调动作",
zap.String("certification_id", certificationID),
zap.String("action", callbackData.Action))
return fmt.Errorf("未知的回调动作: %s", callbackData.Action)
}
}
// handleAuthResult 处理企业认证结果回调
func (h *EsignCallbackHandler) handleAuthResult(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("处理企业认证结果回调",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
switch callbackData.Status {
case "success", "verified", "completed":
// 认证成功
_, err := h.stateMachine.TransitionToEnterpriseVerified(
ctx,
certificationID,
callbackData.FlowID,
)
return err
case "failed", "rejected", "error":
// 认证失败
failureReason := h.parseAuthFailureReason(callbackData)
_, err := h.stateMachine.TransitionToInfoRejected(
ctx,
certificationID,
failureReason,
callbackData.Message,
)
return err
default:
h.logger.Warn("未知的企业认证状态",
zap.String("certification_id", certificationID),
zap.String("status", callbackData.Status))
return fmt.Errorf("未知的认证状态: %s", callbackData.Status)
}
}
// handleSignResult 处理合同签署结果回调
func (h *EsignCallbackHandler) handleSignResult(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("处理合同签署结果回调",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
switch callbackData.Status {
case "signed", "completed", "success":
// 签署成功
contractURL := h.extractContractURL(callbackData)
_, err := h.stateMachine.TransitionToContractSigned(
ctx,
certificationID,
contractURL,
)
return err
case "rejected", "refused":
// 用户拒绝签署
_, err := h.stateMachine.TransitionToContractRejected(
ctx,
certificationID,
enums.FailureReasonContractRejectedByUser,
callbackData.Message,
)
return err
case "expired", "timeout":
// 签署超时
_, err := h.stateMachine.TransitionToContractExpired(
ctx,
certificationID,
fmt.Sprintf("合同签署超时: %s", callbackData.Message),
)
return err
case "failed", "error":
// 签署失败
failureReason := h.parseSignFailureReason(callbackData)
_, err := h.stateMachine.TransitionToContractRejected(
ctx,
certificationID,
failureReason,
callbackData.Message,
)
return err
default:
h.logger.Warn("未知的合同签署状态",
zap.String("certification_id", certificationID),
zap.String("status", callbackData.Status))
return fmt.Errorf("未知的签署状态: %s", callbackData.Status)
}
}
// handleFlowStatus 处理流程状态回调
func (h *EsignCallbackHandler) handleFlowStatus(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("处理流程状态回调",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
// 流程状态回调主要用于监控和日志记录
// 实际的状态转换由具体的auth_result和sign_result处理
switch callbackData.Status {
case "started", "processing", "in_progress":
h.logger.Info("e签宝流程进行中",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID))
case "paused", "suspended":
h.logger.Warn("e签宝流程被暂停",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("message", callbackData.Message))
case "cancelled", "terminated":
h.logger.Warn("e签宝流程被取消",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("message", callbackData.Message))
default:
h.logger.Info("收到其他流程状态",
zap.String("certification_id", certificationID),
zap.String("status", callbackData.Status))
}
return nil
}
// parseAuthFailureReason 解析企业认证失败原因
func (h *EsignCallbackHandler) parseAuthFailureReason(callbackData *EsignCallbackData) enums.FailureReason {
// 根据e签宝返回的错误信息解析失败原因
message := callbackData.Message
// 检查扩展数据中的错误码
if errorCode, exists := callbackData.Data["error_code"]; exists {
switch errorCode {
case "ENTERPRISE_NOT_FOUND", "ORG_NOT_EXISTS":
return enums.FailureReasonEnterpriseNotExists
case "INFO_MISMATCH", "ORG_INFO_ERROR":
return enums.FailureReasonEnterpriseInfoMismatch
case "STATUS_ABNORMAL", "ORG_STATUS_ERROR":
return enums.FailureReasonEnterpriseStatusAbnormal
case "LEGAL_PERSON_ERROR", "LEGAL_REP_ERROR":
return enums.FailureReasonLegalPersonMismatch
case "DOCUMENT_INVALID", "ID_CARD_ERROR":
return enums.FailureReasonInvalidDocument
}
}
// 根据错误消息文本判断
if message != "" {
if h.containsKeywords(message, []string{"企业不存在", "机构不存在", "not found"}) {
return enums.FailureReasonEnterpriseNotExists
}
if h.containsKeywords(message, []string{"信息不匹配", "信息错误", "mismatch"}) {
return enums.FailureReasonEnterpriseInfoMismatch
}
if h.containsKeywords(message, []string{"状态异常", "status abnormal"}) {
return enums.FailureReasonEnterpriseStatusAbnormal
}
if h.containsKeywords(message, []string{"法定代表人", "legal person", "法人"}) {
return enums.FailureReasonLegalPersonMismatch
}
if h.containsKeywords(message, []string{"证件", "身份证", "document", "id card"}) {
return enums.FailureReasonInvalidDocument
}
}
// 默认返回e签宝验证失败
return enums.FailureReasonEsignVerificationFailed
}
// parseSignFailureReason 解析合同签署失败原因
func (h *EsignCallbackHandler) parseSignFailureReason(callbackData *EsignCallbackData) enums.FailureReason {
// 根据e签宝返回的错误信息解析失败原因
message := callbackData.Message
// 检查扩展数据中的错误码
if errorCode, exists := callbackData.Data["error_code"]; exists {
switch errorCode {
case "USER_REJECTED", "SIGN_REJECTED":
return enums.FailureReasonContractRejectedByUser
case "FLOW_EXPIRED", "SIGN_EXPIRED":
return enums.FailureReasonContractExpired
case "FLOW_ERROR", "SIGN_PROCESS_ERROR":
return enums.FailureReasonSignProcessFailed
case "ESIGN_ERROR", "SYSTEM_ERROR":
return enums.FailureReasonEsignFlowError
}
}
// 根据错误消息文本判断
if message != "" {
if h.containsKeywords(message, []string{"拒绝", "rejected", "refused"}) {
return enums.FailureReasonContractRejectedByUser
}
if h.containsKeywords(message, []string{"过期", "超时", "expired", "timeout"}) {
return enums.FailureReasonContractExpired
}
if h.containsKeywords(message, []string{"流程错误", "process error", "flow error"}) {
return enums.FailureReasonSignProcessFailed
}
}
// 默认返回e签宝流程错误
return enums.FailureReasonEsignFlowError
}
// extractContractURL 提取合同URL
func (h *EsignCallbackHandler) extractContractURL(callbackData *EsignCallbackData) string {
// 优先从扩展数据中获取
if contractURL, exists := callbackData.Data["contract_url"]; exists {
if url, ok := contractURL.(string); ok && url != "" {
return url
}
}
if downloadURL, exists := callbackData.Data["download_url"]; exists {
if url, ok := downloadURL.(string); ok && url != "" {
return url
}
}
if fileURL, exists := callbackData.Data["file_url"]; exists {
if url, ok := fileURL.(string); ok && url != "" {
return url
}
}
// 如果没有找到URL返回空字符串
h.logger.Warn("未能从回调数据中提取合同URL",
zap.Any("callback_data", callbackData.Data))
return ""
}
// containsKeywords 检查文本是否包含关键词
func (h *EsignCallbackHandler) containsKeywords(text string, keywords []string) bool {
for _, keyword := range keywords {
if len(text) >= len(keyword) {
for i := 0; i <= len(text)-len(keyword); i++ {
if text[i:i+len(keyword)] == keyword {
return true
}
}
}
}
return false
}
// ValidateCallbackData 验证回调数据
func (h *EsignCallbackHandler) ValidateCallbackData(callbackData *EsignCallbackData) error {
if callbackData == nil {
return fmt.Errorf("回调数据不能为空")
}
if callbackData.Action == "" {
return fmt.Errorf("回调动作不能为空")
}
if callbackData.FlowID == "" {
return fmt.Errorf("流程ID不能为空")
}
if callbackData.Status == "" {
return fmt.Errorf("状态不能为空")
}
return nil
}
// ParseCallbackData 解析原始回调数据
func (h *EsignCallbackHandler) ParseCallbackData(rawData string) (*EsignCallbackData, error) {
var callbackData EsignCallbackData
if err := json.Unmarshal([]byte(rawData), &callbackData); err != nil {
h.logger.Error("解析e签宝回调数据失败", zap.Error(err), zap.String("raw_data", rawData))
return nil, fmt.Errorf("解析回调数据失败: %w", err)
}
// 保存原始数据
callbackData.OriginalData = rawData
// 验证数据完整性
if err := h.ValidateCallbackData(&callbackData); err != nil {
return nil, fmt.Errorf("回调数据验证失败: %w", err)
}
return &callbackData, nil
}
// GetCallbackType 获取回调类型描述
func (h *EsignCallbackHandler) GetCallbackType(action string) string {
types := map[string]string{
"auth_result": "企业认证结果",
"sign_result": "合同签署结果",
"flow_status": "流程状态更新",
}
if typeName, exists := types[action]; exists {
return typeName
}
return "未知类型"
}