package state_machine import ( "context" "fmt" "time" "tyapi-server/internal/domains/certification/entities" "tyapi-server/internal/domains/certification/enums" "tyapi-server/internal/domains/certification/repositories" "go.uber.org/zap" ) // CertificationStateMachine 认证状态机 // 负责管理认证流程的状态转换、业务规则验证和事件发布 type CertificationStateMachine struct { configManager *StateConfigManager repository repositories.CertificationCommandRepository eventPublisher interface{} // TODO: 使用 interfaces.EventPublisher logger *zap.Logger } // NewCertificationStateMachine 创建认证状态机 func NewCertificationStateMachine( repository repositories.CertificationCommandRepository, eventPublisher interface{}, // TODO: 使用 interfaces.EventPublisher logger *zap.Logger, ) *CertificationStateMachine { return &CertificationStateMachine{ configManager: NewStateConfigManager(), repository: repository, eventPublisher: eventPublisher, logger: logger, } } // StateTransitionRequest 状态转换请求 type StateTransitionRequest struct { CertificationID string `json:"certification_id"` TargetStatus enums.CertificationStatus `json:"target_status"` Actor enums.ActorType `json:"actor"` ActorID string `json:"actor_id"` Reason string `json:"reason"` Context map[string]interface{} `json:"context"` AllowRollback bool `json:"allow_rollback"` } // StateTransitionResult 状态转换结果 type StateTransitionResult struct { Success bool `json:"success"` OldStatus enums.CertificationStatus `json:"old_status"` NewStatus enums.CertificationStatus `json:"new_status"` Message string `json:"message"` TransitionedAt time.Time `json:"transitioned_at"` Events []interface{} `json:"events,omitempty"` } // CanTransition 检查是否可以执行状态转换 func (sm *CertificationStateMachine) CanTransition( cert *entities.Certification, targetStatus enums.CertificationStatus, actor enums.ActorType, ) (bool, string) { // 1. 检查基本状态转换规则 canTransition, message := sm.configManager.CanTransition(cert.Status, targetStatus, actor) if !canTransition { return false, message } // 2. 检查认证实体的业务规则 if canTransition, message := cert.CanTransitionTo(targetStatus, actor); !canTransition { return false, message } // 3. 检查是否为最终状态 if cert.IsFinalStatus() { return false, "认证已完成,无法进行状态转换" } return true, "" } // ExecuteTransition 执行状态转换 func (sm *CertificationStateMachine) ExecuteTransition( ctx context.Context, req *StateTransitionRequest, ) (*StateTransitionResult, error) { sm.logger.Info("开始执行状态转换", zap.String("certification_id", req.CertificationID), zap.String("target_status", string(req.TargetStatus)), zap.String("actor", string(req.Actor)), zap.String("actor_id", req.ActorID)) // 1. 加载认证聚合根 cert, err := sm.loadCertification(ctx, req.CertificationID) if err != nil { return sm.createFailureResult(cert.Status, req.TargetStatus, fmt.Sprintf("加载认证信息失败: %s", err.Error())), err } oldStatus := cert.Status // 2. 验证转换合法性 if canTransition, message := sm.CanTransition(cert, req.TargetStatus, req.Actor); !canTransition { return sm.createFailureResult(oldStatus, req.TargetStatus, message), fmt.Errorf("状态转换验证失败: %s", message) } // 3. 验证业务规则 if err := sm.validateBusinessRules(cert, req); err != nil { return sm.createFailureResult(oldStatus, req.TargetStatus, fmt.Sprintf("业务规则验证失败: %s", err.Error())), err } // 4. 执行状态转换 if err := cert.TransitionTo(req.TargetStatus, req.Actor, req.ActorID, req.Reason); err != nil { return sm.createFailureResult(oldStatus, req.TargetStatus, fmt.Sprintf("状态转换执行失败: %s", err.Error())), err } // 5. 保存到数据库 if err := sm.repository.Update(ctx, *cert); err != nil { // 如果保存失败,需要回滚状态 sm.logger.Error("状态转换保存失败,尝试回滚", zap.String("certification_id", req.CertificationID), zap.Error(err)) if req.AllowRollback { if rollbackErr := sm.rollbackStateTransition(ctx, cert, oldStatus, req.Actor, req.ActorID); rollbackErr != nil { sm.logger.Error("状态回滚失败", zap.Error(rollbackErr)) } } return sm.createFailureResult(oldStatus, req.TargetStatus, fmt.Sprintf("保存状态转换失败: %s", err.Error())), err } // 6. 发布领域事件 events := cert.GetDomainEvents() for _, event := range events { // TODO: 实现事件发布 // if err := sm.eventPublisher.PublishEvent(ctx, event); err != nil { // sm.logger.Error("发布领域事件失败", // zap.String("certification_id", req.CertificationID), // zap.Error(err)) // } sm.logger.Info("领域事件待发布", zap.String("certification_id", req.CertificationID), zap.Any("event", event)) } // 7. 清理领域事件 cert.ClearDomainEvents() // 8. 记录成功日志 sm.logger.Info("状态转换执行成功", zap.String("certification_id", req.CertificationID), zap.String("from_status", string(oldStatus)), zap.String("to_status", string(req.TargetStatus)), zap.String("actor", string(req.Actor))) // 9. 返回成功结果 return &StateTransitionResult{ Success: true, OldStatus: oldStatus, NewStatus: req.TargetStatus, Message: "状态转换成功", TransitionedAt: time.Now(), Events: events, }, nil } // GetValidTransitions 获取有效的状态转换 func (sm *CertificationStateMachine) GetValidTransitions( cert *entities.Certification, actor enums.ActorType, ) []*StateTransitionRule { return sm.configManager.GetAllowedTransitions(cert.Status, actor) } // GetStateInfo 获取状态信息 func (sm *CertificationStateMachine) GetStateInfo(status enums.CertificationStatus) *StateConfig { return sm.configManager.GetStateConfig(status) } // ValidateBusinessRules 验证业务规则 func (sm *CertificationStateMachine) ValidateBusinessRules( cert *entities.Certification, req *StateTransitionRequest, ) error { return sm.validateBusinessRules(cert, req) } // IsUserActionRequired 检查是否需要用户操作 func (sm *CertificationStateMachine) IsUserActionRequired(status enums.CertificationStatus) bool { return sm.configManager.IsUserActionRequired(status) } // GetProgressPercentage 获取进度百分比 func (sm *CertificationStateMachine) GetProgressPercentage(status enums.CertificationStatus) int { return sm.configManager.GetStateProgress(status) } // ================ 私有方法 ================ // loadCertification 加载认证聚合根 func (sm *CertificationStateMachine) loadCertification(ctx context.Context, certificationID string) (*entities.Certification, error) { // 这里需要通过查询仓储获取认证信息 // 由于当前只有命令仓储,这里使用简单的方法 // 在实际实现中,应该使用查询仓储 cert := &entities.Certification{ID: certificationID} // TODO: 实现从查询仓储加载认证信息 // cert, err := sm.queryRepository.GetByID(ctx, certificationID) // if err != nil { // return nil, fmt.Errorf("认证信息不存在: %w", err) // } return cert, nil } // validateBusinessRules 验证业务规则 func (sm *CertificationStateMachine) validateBusinessRules( cert *entities.Certification, req *StateTransitionRequest, ) error { // 获取转换规则 rule := sm.configManager.GetTransitionRule(cert.Status, req.TargetStatus) if rule == nil { return fmt.Errorf("找不到状态转换规则") } // 如果不需要验证,直接返回 if !rule.RequiresValidation { return nil } // 构建验证上下文 context := make(map[string]interface{}) // 添加认证基本信息 context["certification_id"] = cert.ID context["user_id"] = cert.UserID context["current_status"] = string(cert.Status) context["retry_count"] = cert.RetryCount context["auth_flow_id"] = cert.AuthFlowID // 添加请求中的上下文信息 for key, value := range req.Context { context[key] = value } // 执行业务规则验证 return sm.configManager.ValidateBusinessRules(rule, context) } // rollbackStateTransition 回滚状态转换 func (sm *CertificationStateMachine) rollbackStateTransition( ctx context.Context, cert *entities.Certification, originalStatus enums.CertificationStatus, actor enums.ActorType, actorID string, ) error { sm.logger.Info("开始回滚状态转换", zap.String("certification_id", cert.ID), zap.String("original_status", string(originalStatus)), zap.String("current_status", string(cert.Status))) // 直接设置回原状态(跳过业务规则验证) cert.Status = originalStatus // 更新审计信息 now := time.Now() cert.LastTransitionAt = &now cert.LastTransitionBy = actor cert.LastTransitionActor = actorID // 保存回滚结果 if err := sm.repository.Update(ctx, *cert); err != nil { return fmt.Errorf("保存回滚状态失败: %w", err) } sm.logger.Info("状态转换回滚成功", zap.String("certification_id", cert.ID), zap.String("rollback_to_status", string(originalStatus))) return nil } // createFailureResult 创建失败结果 func (sm *CertificationStateMachine) createFailureResult( oldStatus, targetStatus enums.CertificationStatus, message string, ) *StateTransitionResult { return &StateTransitionResult{ Success: false, OldStatus: oldStatus, NewStatus: targetStatus, Message: message, TransitionedAt: time.Now(), Events: []interface{}{}, } } // ================ 状态转换快捷方法 ================ // TransitionToInfoSubmitted 转换到已提交企业信息状态 func (sm *CertificationStateMachine) TransitionToInfoSubmitted( ctx context.Context, certificationID string, actor enums.ActorType, actorID string, enterpriseInfo interface{}, ) (*StateTransitionResult, error) { req := &StateTransitionRequest{ CertificationID: certificationID, TargetStatus: enums.StatusInfoSubmitted, Actor: actor, ActorID: actorID, Reason: "用户提交企业信息", Context: map[string]interface{}{ "enterprise_info": enterpriseInfo, }, AllowRollback: true, } return sm.ExecuteTransition(ctx, req) } // TransitionToEnterpriseVerified 转换到已企业认证状态 func (sm *CertificationStateMachine) TransitionToEnterpriseVerified( ctx context.Context, certificationID string, authFlowID string, ) (*StateTransitionResult, error) { req := &StateTransitionRequest{ CertificationID: certificationID, TargetStatus: enums.StatusEnterpriseVerified, Actor: enums.ActorTypeEsign, ActorID: "esign_system", Reason: "e签宝企业认证成功", Context: map[string]interface{}{ "auth_flow_id": authFlowID, }, AllowRollback: false, } return sm.ExecuteTransition(ctx, req) } // TransitionToInfoRejected 转换到企业信息被拒绝状态 func (sm *CertificationStateMachine) TransitionToInfoRejected( ctx context.Context, certificationID string, failureReason enums.FailureReason, failureMessage string, ) (*StateTransitionResult, error) { req := &StateTransitionRequest{ CertificationID: certificationID, TargetStatus: enums.StatusInfoRejected, Actor: enums.ActorTypeEsign, ActorID: "esign_system", Reason: "e签宝企业认证失败", Context: map[string]interface{}{ "failure_reason": failureReason, "failure_message": failureMessage, }, AllowRollback: false, } return sm.ExecuteTransition(ctx, req) } // TransitionToContractApplied 转换到已申请合同状态 func (sm *CertificationStateMachine) TransitionToContractApplied( ctx context.Context, certificationID string, actor enums.ActorType, actorID string, ) (*StateTransitionResult, error) { req := &StateTransitionRequest{ CertificationID: certificationID, TargetStatus: enums.StatusContractApplied, Actor: actor, ActorID: actorID, Reason: "用户申请合同签署", Context: map[string]interface{}{}, AllowRollback: true, } return sm.ExecuteTransition(ctx, req) } // TransitionToContractSigned 转换到已签署合同状态(认证完成) func (sm *CertificationStateMachine) TransitionToContractSigned( ctx context.Context, certificationID string, contractURL string, ) (*StateTransitionResult, error) { req := &StateTransitionRequest{ CertificationID: certificationID, TargetStatus: enums.StatusContractSigned, Actor: enums.ActorTypeEsign, ActorID: "esign_system", Reason: "e签宝合同签署成功,认证完成", Context: map[string]interface{}{ "contract_url": contractURL, }, AllowRollback: false, } return sm.ExecuteTransition(ctx, req) } // TransitionToContractRejected 转换到合同被拒签状态 func (sm *CertificationStateMachine) TransitionToContractRejected( ctx context.Context, certificationID string, failureReason enums.FailureReason, failureMessage string, ) (*StateTransitionResult, error) { req := &StateTransitionRequest{ CertificationID: certificationID, TargetStatus: enums.StatusContractRejected, Actor: enums.ActorTypeEsign, ActorID: "esign_system", Reason: "合同签署失败", Context: map[string]interface{}{ "failure_reason": failureReason, "failure_message": failureMessage, }, AllowRollback: false, } return sm.ExecuteTransition(ctx, req) } // TransitionToContractExpired 转换到合同签署超时状态 func (sm *CertificationStateMachine) TransitionToContractExpired( ctx context.Context, certificationID string, failureMessage string, ) (*StateTransitionResult, error) { req := &StateTransitionRequest{ CertificationID: certificationID, TargetStatus: enums.StatusContractExpired, Actor: enums.ActorTypeSystem, ActorID: "timeout_monitor", Reason: "合同签署超时", Context: map[string]interface{}{ "failure_reason": enums.FailureReasonContractExpired, "failure_message": failureMessage, }, AllowRollback: false, } return sm.ExecuteTransition(ctx, req) }