775 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			775 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package entities
 | ||
| 
 | ||
| import (
 | ||
| 	"errors"
 | ||
| 	"fmt"
 | ||
| 	"time"
 | ||
| 
 | ||
| 	"tyapi-server/internal/domains/certification/entities/value_objects"
 | ||
| 	"tyapi-server/internal/domains/certification/enums"
 | ||
| 
 | ||
| 	"github.com/google/uuid"
 | ||
| 	"gorm.io/gorm"
 | ||
| )
 | ||
| 
 | ||
| // Certification 认证聚合根
 | ||
| // 这是企业认证流程的核心聚合根,封装了完整的认证业务逻辑和状态管理
 | ||
| type Certification struct {
 | ||
| 	// === 基础信息 ===
 | ||
| 	ID     string                    `gorm:"primaryKey;type:varchar(64)" json:"id" comment:"认证申请唯一标识"`
 | ||
| 	UserID string                    `gorm:"type:varchar(36);not null;unique" json:"user_id" comment:"申请用户ID"`
 | ||
| 	Status enums.CertificationStatus `gorm:"type:varchar(50);not null;index" json:"status" comment:"当前认证状态"`
 | ||
| 
 | ||
| 	// === 流程时间戳 - 记录每个关键步骤的完成时间 ===
 | ||
| 	InfoSubmittedAt       *time.Time `json:"info_submitted_at,omitempty" comment:"企业信息提交时间"`
 | ||
| 	EnterpriseVerifiedAt  *time.Time `json:"enterprise_verified_at,omitempty" comment:"企业认证完成时间"`
 | ||
| 	ContractAppliedAt     *time.Time `json:"contract_applied_at,omitempty" comment:"合同申请时间"`
 | ||
| 	ContractSignedAt      *time.Time `json:"contract_signed_at,omitempty" comment:"合同签署完成时间"`
 | ||
| 	CompletedAt           *time.Time `json:"completed_at,omitempty" comment:"认证完成时间"`
 | ||
| 	ContractFileCreatedAt *time.Time `json:"contract_file_created_at,omitempty" comment:"合同文件生成时间"`
 | ||
| 
 | ||
| 	// === e签宝相关信息 ===
 | ||
| 	AuthFlowID      string `gorm:"type:varchar(500)" json:"auth_flow_id,omitempty" comment:"企业认证流程ID"`
 | ||
| 	AuthURL         string `gorm:"type:varchar(500)" json:"auth_url,omitempty" comment:"企业认证链接"`
 | ||
| 	ContractFileID  string `gorm:"type:varchar(500)" json:"contract_file_id,omitempty" comment:"合同文件ID"`
 | ||
| 	EsignFlowID     string `gorm:"type:varchar(500)" json:"esign_flow_id,omitempty" comment:"签署流程ID"`
 | ||
| 	ContractURL     string `gorm:"type:varchar(500)" json:"contract_url,omitempty" comment:"合同文件访问链接"`
 | ||
| 	ContractSignURL string `gorm:"type:varchar(500)" json:"contract_sign_url,omitempty" comment:"合同签署链接"`
 | ||
| 
 | ||
| 	// === 失败信息 ===
 | ||
| 	FailureReason  enums.FailureReason `gorm:"type:varchar(100)" json:"failure_reason,omitempty" comment:"失败原因"`
 | ||
| 	FailureMessage string              `gorm:"type:text" json:"failure_message,omitempty" comment:"失败详细信息"`
 | ||
| 	RetryCount     int                 `gorm:"default:0" json:"retry_count" comment:"重试次数"`
 | ||
| 
 | ||
| 	// === 审计信息 ===
 | ||
| 	LastTransitionAt    *time.Time      `json:"last_transition_at,omitempty" comment:"最后状态转换时间"`
 | ||
| 	LastTransitionBy    enums.ActorType `gorm:"type:varchar(20)" json:"last_transition_by,omitempty" comment:"最后操作者类型"`
 | ||
| 	LastTransitionActor string          `gorm:"type:varchar(100)" json:"last_transition_actor,omitempty" comment:"最后操作者ID"`
 | ||
| 
 | ||
| 	// === 系统字段 ===
 | ||
| 	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
 | ||
| 	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
 | ||
| 	DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
 | ||
| 
 | ||
| 	// === 领域事件 (不持久化) ===
 | ||
| 	domainEvents []interface{} `gorm:"-" json:"-"`
 | ||
| }
 | ||
| 
 | ||
| // TableName 指定数据库表名
 | ||
| func (Certification) TableName() string {
 | ||
| 	return "certifications"
 | ||
| }
 | ||
| 
 | ||
| // BeforeCreate GORM钩子:创建前自动生成UUID
 | ||
| func (c *Certification) BeforeCreate(tx *gorm.DB) error {
 | ||
| 	if c.ID == "" {
 | ||
| 		c.ID = uuid.New().String()
 | ||
| 	}
 | ||
| 
 | ||
| 	// 设置初始状态
 | ||
| 	if c.Status == "" {
 | ||
| 		c.Status = enums.StatusPending
 | ||
| 	}
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 工厂方法 ================
 | ||
| 
 | ||
| // NewCertification 创建新的认证申请
 | ||
| func NewCertification(userID string) (*Certification, error) {
 | ||
| 	if userID == "" {
 | ||
| 		return nil, errors.New("用户ID不能为空")
 | ||
| 	}
 | ||
| 
 | ||
| 	certification := &Certification{
 | ||
| 		ID:           uuid.New().String(),
 | ||
| 		UserID:       userID,
 | ||
| 		Status:       enums.StatusPending,
 | ||
| 		RetryCount:   0,
 | ||
| 		domainEvents: make([]interface{}, 0),
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加领域事件
 | ||
| 	certification.addDomainEvent(&CertificationCreatedEvent{
 | ||
| 		CertificationID: certification.ID,
 | ||
| 		UserID:          userID,
 | ||
| 		CreatedAt:       time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return certification, nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 状态转换方法 ================
 | ||
| 
 | ||
| // CanTransitionTo 检查是否可以转换到目标状态
 | ||
| func (c *Certification) CanTransitionTo(targetStatus enums.CertificationStatus, actor enums.ActorType) (bool, string) {
 | ||
| 	// 检查状态转换规则
 | ||
| 	if !enums.CanTransitionTo(c.Status, targetStatus) {
 | ||
| 		return false, fmt.Sprintf("不允许从 %s 转换到 %s", enums.GetStatusName(c.Status), enums.GetStatusName(targetStatus))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 检查操作者权限
 | ||
| 	if !c.validateActorPermission(targetStatus, actor) {
 | ||
| 		return false, fmt.Sprintf("%s 无权执行此状态转换", enums.GetActorTypeName(actor))
 | ||
| 	}
 | ||
| 	return true, ""
 | ||
| }
 | ||
| 
 | ||
| // TransitionTo 执行状态转换
 | ||
| func (c *Certification) TransitionTo(targetStatus enums.CertificationStatus, actor enums.ActorType, actorID string, reason string) error {
 | ||
| 	// 验证转换合法性
 | ||
| 	canTransition, message := c.CanTransitionTo(targetStatus, actor)
 | ||
| 	if !canTransition {
 | ||
| 		return fmt.Errorf("状态转换失败: %s", message)
 | ||
| 	}
 | ||
| 
 | ||
| 	oldStatus := c.Status
 | ||
| 
 | ||
| 	// 执行状态转换
 | ||
| 	c.Status = targetStatus
 | ||
| 	c.updateTimestampByStatus(targetStatus)
 | ||
| 	c.updateTransitionAudit(actor, actorID)
 | ||
| 
 | ||
| 	// 清除失败信息(如果转换到成功状态)
 | ||
| 	if !enums.IsFailureStatus(targetStatus) {
 | ||
| 		c.clearFailureInfo()
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加状态转换事件
 | ||
| 	c.addDomainEvent(&CertificationStatusChangedEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		FromStatus:      oldStatus,
 | ||
| 		ToStatus:        targetStatus,
 | ||
| 		Actor:           actor,
 | ||
| 		ActorID:         actorID,
 | ||
| 		Reason:          reason,
 | ||
| 		TransitionedAt:  time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 业务操作方法 ================
 | ||
| 
 | ||
| // SubmitEnterpriseInfo 提交企业信息
 | ||
| func (c *Certification) SubmitEnterpriseInfo(enterpriseInfo *value_objects.EnterpriseInfo, authURL string, authFlowID string) error {
 | ||
| 	// 验证当前状态
 | ||
| 	if c.Status != enums.StatusPending && c.Status != enums.StatusInfoRejected {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许提交企业信息", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 验证企业信息
 | ||
| 	if err := enterpriseInfo.Validate(); err != nil {
 | ||
| 		return fmt.Errorf("企业信息验证失败: %w", err)
 | ||
| 	}
 | ||
| 	if authURL != "" {
 | ||
| 		c.AuthURL = authURL
 | ||
| 	}
 | ||
| 	if authFlowID != "" {
 | ||
| 		c.AuthFlowID = authFlowID
 | ||
| 	}
 | ||
| 	// 状态转换
 | ||
| 	if err := c.TransitionTo(enums.StatusInfoSubmitted, enums.ActorTypeUser, c.UserID, "用户提交企业信息"); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加业务事件
 | ||
| 	c.addDomainEvent(&EnterpriseInfoSubmittedEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		EnterpriseInfo:  enterpriseInfo,
 | ||
| 		SubmittedAt:     time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // 完成企业认证
 | ||
| func (c *Certification) CompleteEnterpriseVerification() error {
 | ||
| 	if c.Status != enums.StatusInfoSubmitted {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许完成企业认证", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	if err := c.TransitionTo(enums.StatusEnterpriseVerified, enums.ActorTypeSystem, "system", "企业认证成功"); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 
 | ||
| 	c.addDomainEvent(&EnterpriseVerificationSuccessEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		AuthFlowID:      "",
 | ||
| 		VerifiedAt:      time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // HandleEnterpriseVerificationCallback 处理企业认证回调
 | ||
| func (c *Certification) HandleEnterpriseVerificationCallback(success bool, authFlowID string, failureReason enums.FailureReason, message string) error {
 | ||
| 	// 验证当前状态
 | ||
| 	if c.Status != enums.StatusInfoSubmitted {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许处理企业认证回调", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	c.AuthFlowID = authFlowID
 | ||
| 
 | ||
| 	if success {
 | ||
| 		// 认证成功
 | ||
| 		if err := c.TransitionTo(enums.StatusEnterpriseVerified, enums.ActorTypeEsign, "esign_system", "企业认证成功"); err != nil {
 | ||
| 			return err
 | ||
| 		}
 | ||
| 
 | ||
| 		c.addDomainEvent(&EnterpriseVerificationSuccessEvent{
 | ||
| 			CertificationID: c.ID,
 | ||
| 			UserID:          c.UserID,
 | ||
| 			AuthFlowID:      authFlowID,
 | ||
| 			VerifiedAt:      time.Now(),
 | ||
| 		})
 | ||
| 	} else {
 | ||
| 		// 认证失败
 | ||
| 		c.setFailureInfo(failureReason, message)
 | ||
| 
 | ||
| 		if err := c.TransitionTo(enums.StatusInfoRejected, enums.ActorTypeEsign, "esign_system", "企业认证失败"); err != nil {
 | ||
| 			return err
 | ||
| 		}
 | ||
| 
 | ||
| 		c.addDomainEvent(&EnterpriseVerificationFailedEvent{
 | ||
| 			CertificationID: c.ID,
 | ||
| 			UserID:          c.UserID,
 | ||
| 			AuthFlowID:      authFlowID,
 | ||
| 			FailureReason:   failureReason,
 | ||
| 			FailureMessage:  message,
 | ||
| 			FailedAt:        time.Now(),
 | ||
| 		})
 | ||
| 	}
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // ApplyContract 申请合同签署
 | ||
| func (c *Certification) ApplyContract(EsignFlowID string, ContractSignURL string) error {
 | ||
| 	// 验证当前状态
 | ||
| 	if c.Status != enums.StatusEnterpriseVerified {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许申请合同", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 	// 状态转换
 | ||
| 	if err := c.TransitionTo(enums.StatusContractApplied, enums.ActorTypeUser, c.UserID, "用户申请合同签署"); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 	c.EsignFlowID = EsignFlowID
 | ||
| 	c.ContractSignURL = ContractSignURL
 | ||
| 	now := time.Now()
 | ||
| 	c.ContractFileCreatedAt = &now
 | ||
| 	// 添加业务事件
 | ||
| 	c.addDomainEvent(&ContractAppliedEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		AppliedAt:       time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // AddContractFileID 生成合同文件
 | ||
| func (c *Certification) AddContractFileID(contractFileID string, contractURL string) error {
 | ||
| 	c.ContractFileID = contractFileID
 | ||
| 	c.ContractURL = contractURL
 | ||
| 	now := time.Now()
 | ||
| 	c.ContractFileCreatedAt = &now
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // UpdateContractInfo 更新合同信息
 | ||
| func (c *Certification) UpdateContractInfo(contractInfo *value_objects.ContractInfo) error {
 | ||
| 	// 验证合同信息
 | ||
| 	if err := contractInfo.Validate(); err != nil {
 | ||
| 		return fmt.Errorf("合同信息验证失败: %w", err)
 | ||
| 	}
 | ||
| 
 | ||
| 	// 更新合同相关字段
 | ||
| 	c.ContractFileID = contractInfo.ContractFileID
 | ||
| 	c.EsignFlowID = contractInfo.EsignFlowID
 | ||
| 	c.ContractURL = contractInfo.ContractURL
 | ||
| 	c.ContractSignURL = contractInfo.ContractSignURL
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // SignSuccess 签署成功
 | ||
| func (c *Certification) SignSuccess() error {
 | ||
| 	// 验证当前状态
 | ||
| 	if c.Status != enums.StatusContractApplied {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许处理合同签署回调", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	if err := c.TransitionTo(enums.StatusContractSigned, enums.ActorTypeEsign, "esign_system", "合同签署成功"); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 
 | ||
| 	c.addDomainEvent(&ContractSignedEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		SignedAt:        time.Now(),
 | ||
| 	})
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // ContractRejection 处理合同拒签
 | ||
| func (c *Certification) ContractRejection(message string) error {
 | ||
| 	// 验证当前状态
 | ||
| 	if c.Status != enums.StatusContractApplied {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许处理合同拒签", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 设置失败信息
 | ||
| 	c.setFailureInfo(enums.FailureReasonContractRejectedByUser, message)
 | ||
| 
 | ||
| 	// 状态转换
 | ||
| 	if err := c.TransitionTo(enums.StatusContractRejected, enums.ActorTypeEsign, "esign_system", "合同签署被拒绝"); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加业务事件
 | ||
| 	c.addDomainEvent(&ContractSignFailedEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		FailureReason:   enums.FailureReasonContractRejectedByUser,
 | ||
| 		FailureMessage:  message,
 | ||
| 		FailedAt:        time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // ContractExpiration 处理合同过期
 | ||
| func (c *Certification) ContractExpiration() error {
 | ||
| 	// 验证当前状态
 | ||
| 	if c.Status != enums.StatusContractApplied {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许处理合同过期", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 设置失败信息
 | ||
| 	c.setFailureInfo(enums.FailureReasonContractExpired, "合同签署已超时")
 | ||
| 
 | ||
| 	// 状态转换
 | ||
| 	if err := c.TransitionTo(enums.StatusContractExpired, enums.ActorTypeSystem, "system", "合同签署超时"); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加业务事件
 | ||
| 	c.addDomainEvent(&ContractSignFailedEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		FailureReason:   enums.FailureReasonContractExpired,
 | ||
| 		FailureMessage:  "合同签署已超时",
 | ||
| 		FailedAt:        time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // RetryFromFailure 从失败状态重试
 | ||
| func (c *Certification) RetryFromFailure(actor enums.ActorType, actorID string) error {
 | ||
| 	if !enums.IsFailureStatus(c.Status) {
 | ||
| 		return errors.New("当前状态不是失败状态,无需重试")
 | ||
| 	}
 | ||
| 
 | ||
| 	// 检查重试次数限制
 | ||
| 	if c.RetryCount >= 3 {
 | ||
| 		return errors.New("已达到最大重试次数限制")
 | ||
| 	}
 | ||
| 
 | ||
| 	// 检查失败原因是否可重试
 | ||
| 	if !enums.IsRetryable(c.FailureReason) {
 | ||
| 		return fmt.Errorf("失败原因 %s 不支持重试", enums.GetFailureReasonName(c.FailureReason))
 | ||
| 	}
 | ||
| 
 | ||
| 	var targetStatus enums.CertificationStatus
 | ||
| 	var reason string
 | ||
| 
 | ||
| 	switch c.Status {
 | ||
| 	case enums.StatusInfoRejected:
 | ||
| 		targetStatus = enums.StatusInfoSubmitted
 | ||
| 		reason = "重新提交企业信息"
 | ||
| 	case enums.StatusContractRejected, enums.StatusContractExpired:
 | ||
| 		targetStatus = enums.StatusEnterpriseVerified
 | ||
| 		reason = "重置状态,准备重新申请合同"
 | ||
| 	default:
 | ||
| 		return fmt.Errorf("不支持从状态 %s 重试", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 增加重试次数
 | ||
| 	c.RetryCount++
 | ||
| 
 | ||
| 	// 状态转换
 | ||
| 	if err := c.TransitionTo(targetStatus, actor, actorID, reason); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加重试事件
 | ||
| 	c.addDomainEvent(&CertificationRetryEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		FromStatus:      c.Status,
 | ||
| 		ToStatus:        targetStatus,
 | ||
| 		RetryCount:      c.RetryCount,
 | ||
| 		RetriedAt:       time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // CompleteCertification 完成认证
 | ||
| func (c *Certification) CompleteCertification() error {
 | ||
| 	// 验证当前状态
 | ||
| 	if c.Status != enums.StatusContractSigned {
 | ||
| 		return fmt.Errorf("当前状态 %s 不允许完成认证", enums.GetStatusName(c.Status))
 | ||
| 	}
 | ||
| 
 | ||
| 	// 验证合同信息完整性
 | ||
| 	if c.ContractFileID == "" || c.EsignFlowID == "" || c.ContractURL == "" {
 | ||
| 		return errors.New("合同信息不完整,无法完成认证")
 | ||
| 	}
 | ||
| 
 | ||
| 	// 状态转换
 | ||
| 	if err := c.TransitionTo(enums.StatusCompleted, enums.ActorTypeSystem, "system", "系统处理完成,认证成功"); err != nil {
 | ||
| 		return err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 添加业务事件
 | ||
| 	c.addDomainEvent(&CertificationCompletedEvent{
 | ||
| 		CertificationID: c.ID,
 | ||
| 		UserID:          c.UserID,
 | ||
| 		CompletedAt:     time.Now(),
 | ||
| 	})
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // ================ 查询方法 ================
 | ||
| // GetDataByStatus 根据当前状态获取对应的数据
 | ||
| func (c *Certification) GetDataByStatus() map[string]interface{} {
 | ||
| 	data := map[string]interface{}{}
 | ||
| 	switch c.Status {
 | ||
| 	case enums.StatusInfoSubmitted:
 | ||
| 		data["auth_url"] = c.AuthURL
 | ||
| 	case enums.StatusInfoRejected:
 | ||
| 		data["failure_reason"] = c.FailureReason
 | ||
| 		data["failure_message"] = c.FailureMessage
 | ||
| 	case enums.StatusEnterpriseVerified:
 | ||
| 		data["ContractURL"] = c.ContractURL
 | ||
| 	case enums.StatusContractApplied:
 | ||
| 		data["contract_sign_url"] = c.ContractSignURL
 | ||
| 	case enums.StatusContractSigned:
 | ||
| 	case enums.StatusCompleted:
 | ||
| 		data["completed_at"] = c.CompletedAt
 | ||
| 	case enums.StatusContractRejected:
 | ||
| 		data["failure_reason"] = c.FailureReason
 | ||
| 		data["failure_message"] = c.FailureMessage
 | ||
| 	}
 | ||
| 	return data
 | ||
| }
 | ||
| 
 | ||
| // GetProgress 获取认证进度百分比
 | ||
| func (c *Certification) GetProgress() int {
 | ||
| 	return enums.GetProgressPercentage(c.Status)
 | ||
| }
 | ||
| 
 | ||
| // IsUserActionRequired 是否需要用户操作
 | ||
| func (c *Certification) IsUserActionRequired() bool {
 | ||
| 	return enums.IsUserActionRequired(c.Status)
 | ||
| }
 | ||
| 
 | ||
| // GetCurrentStatusName 获取当前状态名称
 | ||
| func (c *Certification) GetCurrentStatusName() string {
 | ||
| 	return enums.GetStatusName(c.Status)
 | ||
| }
 | ||
| 
 | ||
| // GetUserActionHint 获取用户操作提示
 | ||
| func (c *Certification) GetUserActionHint() string {
 | ||
| 	return enums.GetUserActionHint(c.Status)
 | ||
| }
 | ||
| 
 | ||
| // GetAvailableActions 获取当前可执行的操作
 | ||
| func (c *Certification) GetAvailableActions() []string {
 | ||
| 	actions := make([]string, 0)
 | ||
| 
 | ||
| 	switch c.Status {
 | ||
| 	case enums.StatusPending:
 | ||
| 		actions = append(actions, "submit_enterprise_info")
 | ||
| 	case enums.StatusEnterpriseVerified:
 | ||
| 		actions = append(actions, "apply_contract")
 | ||
| 	case enums.StatusInfoRejected, enums.StatusContractRejected, enums.StatusContractExpired:
 | ||
| 		if enums.IsRetryable(c.FailureReason) && c.RetryCount < 3 {
 | ||
| 			actions = append(actions, "retry")
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	return actions
 | ||
| }
 | ||
| 
 | ||
| // IsFinalStatus 是否为最终状态
 | ||
| func (c *Certification) IsFinalStatus() bool {
 | ||
| 	return enums.IsFinalStatus(c.Status)
 | ||
| }
 | ||
| 
 | ||
| // IsCompleted 是否已完成
 | ||
| func (c *Certification) IsCompleted() bool {
 | ||
| 	return c.Status == enums.StatusCompleted
 | ||
| }
 | ||
| 
 | ||
| // GetNextValidStatuses 获取下一个有效状态
 | ||
| func (c *Certification) GetNextValidStatuses() []enums.CertificationStatus {
 | ||
| 	return enums.GetNextValidStatuses(c.Status)
 | ||
| }
 | ||
| 
 | ||
| // GetFailureInfo 获取失败信息
 | ||
| func (c *Certification) GetFailureInfo() (enums.FailureReason, string) {
 | ||
| 	return c.FailureReason, c.FailureMessage
 | ||
| }
 | ||
| 
 | ||
| // IsContractFileExpired 判断合同文件是否过期(生成后50分钟过期)
 | ||
| func (c *Certification) IsContractFileExpired() bool {
 | ||
| 	if c.ContractFileCreatedAt == nil && c.Status == enums.StatusEnterpriseVerified {
 | ||
| 		// 60分钟前
 | ||
| 		t := time.Now().Add(-60 * time.Minute)
 | ||
| 		c.ContractFileCreatedAt = &t
 | ||
| 		return true
 | ||
| 	}
 | ||
| 	if c.ContractFileCreatedAt != nil {
 | ||
| 		return time.Since(*c.ContractFileCreatedAt) > 50*time.Minute
 | ||
| 	}
 | ||
| 	return false
 | ||
| }
 | ||
| 
 | ||
| // IsContractFileNeedUpdate 是否需要更新合同文件
 | ||
| func (c *Certification) IsContractFileNeedUpdate() bool {
 | ||
| 	if c.IsContractFileExpired() && c.Status == enums.StatusEnterpriseVerified {
 | ||
| 		return true
 | ||
| 	}
 | ||
| 	return false
 | ||
| }
 | ||
| 
 | ||
| // ================ 业务规则验证 ================
 | ||
| 
 | ||
| // ValidateBusinessRules 验证业务规则
 | ||
| func (c *Certification) ValidateBusinessRules() error {
 | ||
| 	// 基础验证
 | ||
| 	if c.UserID == "" {
 | ||
| 		return errors.New("用户ID不能为空")
 | ||
| 	}
 | ||
| 
 | ||
| 	if !enums.IsValidStatus(c.Status) {
 | ||
| 		return fmt.Errorf("无效的认证状态: %s", c.Status)
 | ||
| 	}
 | ||
| 
 | ||
| 	// 状态相关验证
 | ||
| 	switch c.Status {
 | ||
| 	case enums.StatusEnterpriseVerified:
 | ||
| 		if c.ContractURL == "" {
 | ||
| 			return errors.New("企业认证成功后,合同文件ID和合同URL不能为空")
 | ||
| 		}
 | ||
| 	case enums.StatusContractSigned:
 | ||
| 		if c.ContractFileID == "" || c.EsignFlowID == "" {
 | ||
| 			return errors.New("合同签署状态下必须有完整的合同信息")
 | ||
| 		}
 | ||
| 	case enums.StatusCompleted:
 | ||
| 		if c.ContractFileID == "" || c.EsignFlowID == "" || c.ContractURL == "" {
 | ||
| 			return errors.New("认证完成状态下必须有完整的合同信息")
 | ||
| 		}
 | ||
| 		if c.CompletedAt == nil {
 | ||
| 			return errors.New("认证完成状态下必须有完成时间")
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	// 失败状态验证
 | ||
| 	if enums.IsFailureStatus(c.Status) {
 | ||
| 		if c.FailureReason == "" {
 | ||
| 			return errors.New("失败状态下必须有失败原因")
 | ||
| 		}
 | ||
| 		if !enums.IsValidFailureReason(c.FailureReason) {
 | ||
| 			return fmt.Errorf("无效的失败原因: %s", c.FailureReason)
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| // validateActorPermission 验证操作者权限
 | ||
| func (c *Certification) validateActorPermission(targetStatus enums.CertificationStatus, actor enums.ActorType) bool {
 | ||
| 	// 定义状态转换的权限规则
 | ||
| 	permissions := map[enums.CertificationStatus][]enums.ActorType{
 | ||
| 		enums.StatusInfoSubmitted:      {enums.ActorTypeUser, enums.ActorTypeAdmin},
 | ||
| 		enums.StatusEnterpriseVerified: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
 | ||
| 		enums.StatusInfoRejected:       {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
 | ||
| 		enums.StatusContractApplied:    {enums.ActorTypeUser, enums.ActorTypeAdmin},
 | ||
| 		enums.StatusContractSigned:     {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
 | ||
| 		enums.StatusContractRejected:   {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
 | ||
| 		enums.StatusContractExpired:    {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
 | ||
| 		enums.StatusCompleted:          {enums.ActorTypeSystem, enums.ActorTypeAdmin},
 | ||
| 	}
 | ||
| 
 | ||
| 	allowedActors, exists := permissions[targetStatus]
 | ||
| 	if !exists {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 
 | ||
| 	for _, allowedActor := range allowedActors {
 | ||
| 		if actor == allowedActor {
 | ||
| 			return true
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	return false
 | ||
| }
 | ||
| 
 | ||
| // ================ 辅助方法 ================
 | ||
| 
 | ||
| // updateTimestampByStatus 根据状态更新对应的时间戳
 | ||
| func (c *Certification) updateTimestampByStatus(status enums.CertificationStatus) {
 | ||
| 	now := time.Now()
 | ||
| 
 | ||
| 	switch status {
 | ||
| 	case enums.StatusInfoSubmitted:
 | ||
| 		c.InfoSubmittedAt = &now
 | ||
| 	case enums.StatusEnterpriseVerified:
 | ||
| 		c.EnterpriseVerifiedAt = &now
 | ||
| 	case enums.StatusContractApplied:
 | ||
| 		c.ContractAppliedAt = &now
 | ||
| 	case enums.StatusContractSigned:
 | ||
| 		c.ContractSignedAt = &now
 | ||
| 	case enums.StatusCompleted:
 | ||
| 		c.CompletedAt = &now
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // updateTransitionAudit 更新状态转换审计信息
 | ||
| func (c *Certification) updateTransitionAudit(actor enums.ActorType, actorID string) {
 | ||
| 	now := time.Now()
 | ||
| 	c.LastTransitionAt = &now
 | ||
| 	c.LastTransitionBy = actor
 | ||
| 	c.LastTransitionActor = actorID
 | ||
| }
 | ||
| 
 | ||
| // setFailureInfo 设置失败信息
 | ||
| func (c *Certification) setFailureInfo(reason enums.FailureReason, message string) {
 | ||
| 	c.FailureReason = reason
 | ||
| 	c.FailureMessage = message
 | ||
| }
 | ||
| 
 | ||
| // clearFailureInfo 清除失败信息
 | ||
| func (c *Certification) clearFailureInfo() {
 | ||
| 	c.FailureReason = ""
 | ||
| 	c.FailureMessage = ""
 | ||
| }
 | ||
| 
 | ||
| // ================ 领域事件管理 ================
 | ||
| 
 | ||
| // addDomainEvent 添加领域事件
 | ||
| func (c *Certification) addDomainEvent(event interface{}) {
 | ||
| 	if c.domainEvents == nil {
 | ||
| 		c.domainEvents = make([]interface{}, 0)
 | ||
| 	}
 | ||
| 	c.domainEvents = append(c.domainEvents, event)
 | ||
| }
 | ||
| 
 | ||
| // GetDomainEvents 获取领域事件
 | ||
| func (c *Certification) GetDomainEvents() []interface{} {
 | ||
| 	return c.domainEvents
 | ||
| }
 | ||
| 
 | ||
| // ClearDomainEvents 清除领域事件
 | ||
| func (c *Certification) ClearDomainEvents() {
 | ||
| 	c.domainEvents = make([]interface{}, 0)
 | ||
| }
 | ||
| 
 | ||
| // ================ 领域事件定义 ================
 | ||
| 
 | ||
| // CertificationCreatedEvent 认证创建事件
 | ||
| type CertificationCreatedEvent struct {
 | ||
| 	CertificationID string    `json:"certification_id"`
 | ||
| 	UserID          string    `json:"user_id"`
 | ||
| 	CreatedAt       time.Time `json:"created_at"`
 | ||
| }
 | ||
| 
 | ||
| // CertificationStatusChangedEvent 认证状态变更事件
 | ||
| type CertificationStatusChangedEvent struct {
 | ||
| 	CertificationID string                    `json:"certification_id"`
 | ||
| 	UserID          string                    `json:"user_id"`
 | ||
| 	FromStatus      enums.CertificationStatus `json:"from_status"`
 | ||
| 	ToStatus        enums.CertificationStatus `json:"to_status"`
 | ||
| 	Actor           enums.ActorType           `json:"actor"`
 | ||
| 	ActorID         string                    `json:"actor_id"`
 | ||
| 	Reason          string                    `json:"reason"`
 | ||
| 	TransitionedAt  time.Time                 `json:"transitioned_at"`
 | ||
| }
 | ||
| 
 | ||
| // EnterpriseInfoSubmittedEvent 企业信息提交事件
 | ||
| type EnterpriseInfoSubmittedEvent struct {
 | ||
| 	CertificationID string                        `json:"certification_id"`
 | ||
| 	UserID          string                        `json:"user_id"`
 | ||
| 	EnterpriseInfo  *value_objects.EnterpriseInfo `json:"enterprise_info"`
 | ||
| 	SubmittedAt     time.Time                     `json:"submitted_at"`
 | ||
| }
 | ||
| 
 | ||
| // EnterpriseVerificationSuccessEvent 企业认证成功事件
 | ||
| type EnterpriseVerificationSuccessEvent struct {
 | ||
| 	CertificationID string    `json:"certification_id"`
 | ||
| 	UserID          string    `json:"user_id"`
 | ||
| 	AuthFlowID      string    `json:"auth_flow_id"`
 | ||
| 	VerifiedAt      time.Time `json:"verified_at"`
 | ||
| }
 | ||
| 
 | ||
| // EnterpriseVerificationFailedEvent 企业认证失败事件
 | ||
| type EnterpriseVerificationFailedEvent struct {
 | ||
| 	CertificationID string              `json:"certification_id"`
 | ||
| 	UserID          string              `json:"user_id"`
 | ||
| 	AuthFlowID      string              `json:"auth_flow_id"`
 | ||
| 	FailureReason   enums.FailureReason `json:"failure_reason"`
 | ||
| 	FailureMessage  string              `json:"failure_message"`
 | ||
| 	FailedAt        time.Time           `json:"failed_at"`
 | ||
| }
 | ||
| 
 | ||
| // ContractAppliedEvent 合同申请事件
 | ||
| type ContractAppliedEvent struct {
 | ||
| 	CertificationID string    `json:"certification_id"`
 | ||
| 	UserID          string    `json:"user_id"`
 | ||
| 	AppliedAt       time.Time `json:"applied_at"`
 | ||
| }
 | ||
| 
 | ||
| // ContractSignedEvent 合同签署成功事件
 | ||
| type ContractSignedEvent struct {
 | ||
| 	CertificationID string    `json:"certification_id"`
 | ||
| 	UserID          string    `json:"user_id"`
 | ||
| 	ContractURL     string    `json:"contract_url"`
 | ||
| 	SignedAt        time.Time `json:"signed_at"`
 | ||
| }
 | ||
| 
 | ||
| // ContractSignFailedEvent 合同签署失败事件
 | ||
| type ContractSignFailedEvent struct {
 | ||
| 	CertificationID string              `json:"certification_id"`
 | ||
| 	UserID          string              `json:"user_id"`
 | ||
| 	FailureReason   enums.FailureReason `json:"failure_reason"`
 | ||
| 	FailureMessage  string              `json:"failure_message"`
 | ||
| 	FailedAt        time.Time           `json:"failed_at"`
 | ||
| }
 | ||
| 
 | ||
| // CertificationCompletedEvent 认证完成事件
 | ||
| type CertificationCompletedEvent struct {
 | ||
| 	CertificationID string    `json:"certification_id"`
 | ||
| 	UserID          string    `json:"user_id"`
 | ||
| 	CompletedAt     time.Time `json:"completed_at"`
 | ||
| }
 | ||
| 
 | ||
| // CertificationRetryEvent 认证重试事件
 | ||
| type CertificationRetryEvent struct {
 | ||
| 	CertificationID string                    `json:"certification_id"`
 | ||
| 	UserID          string                    `json:"user_id"`
 | ||
| 	FromStatus      enums.CertificationStatus `json:"from_status"`
 | ||
| 	ToStatus        enums.CertificationStatus `json:"to_status"`
 | ||
| 	RetryCount      int                       `json:"retry_count"`
 | ||
| 	RetriedAt       time.Time                 `json:"retried_at"`
 | ||
| }
 |