package services 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" ) // StateTransition 状态转换规则 type StateTransition struct { From enums.CertificationStatus To enums.CertificationStatus Action string AllowUser bool // 是否允许用户操作 AllowAdmin bool // 是否允许管理员操作 RequiresValidation bool // 是否需要额外验证 } // CertificationStateMachine 认证状态机 type CertificationStateMachine struct { transitions map[enums.CertificationStatus][]StateTransition certRepo repositories.CertificationRepository logger *zap.Logger } // NewCertificationStateMachine 创建认证状态机 func NewCertificationStateMachine( certRepo repositories.CertificationRepository, logger *zap.Logger, ) *CertificationStateMachine { sm := &CertificationStateMachine{ transitions: make(map[enums.CertificationStatus][]StateTransition), certRepo: certRepo, logger: logger, } // 初始化状态转换规则 sm.initializeTransitions() return sm } // initializeTransitions 初始化状态转换规则 func (sm *CertificationStateMachine) initializeTransitions() { transitions := []StateTransition{ // 正常流程转换 {enums.StatusPending, enums.StatusInfoSubmitted, "submit_info", true, false, true}, {enums.StatusInfoSubmitted, enums.StatusFaceVerified, "face_verify", true, false, true}, {enums.StatusFaceVerified, enums.StatusContractApplied, "apply_contract", true, false, false}, {enums.StatusContractApplied, enums.StatusContractPending, "system_process", false, false, false}, {enums.StatusContractPending, enums.StatusContractApproved, "admin_approve", false, true, true}, {enums.StatusContractApproved, enums.StatusContractSigned, "user_sign", true, false, true}, {enums.StatusContractSigned, enums.StatusCompleted, "system_complete", false, false, false}, // 失败和重试转换 {enums.StatusInfoSubmitted, enums.StatusFaceFailed, "face_fail", false, false, false}, {enums.StatusFaceFailed, enums.StatusFaceVerified, "retry_face", true, false, true}, {enums.StatusContractPending, enums.StatusRejected, "admin_reject", false, true, true}, {enums.StatusRejected, enums.StatusInfoSubmitted, "restart_process", true, false, false}, {enums.StatusContractApproved, enums.StatusSignFailed, "sign_fail", false, false, false}, {enums.StatusSignFailed, enums.StatusContractSigned, "retry_sign", true, false, true}, } // 构建状态转换映射 for _, transition := range transitions { sm.transitions[transition.From] = append(sm.transitions[transition.From], transition) } } // CanTransition 检查是否可以转换到指定状态 func (sm *CertificationStateMachine) CanTransition( from enums.CertificationStatus, to enums.CertificationStatus, isUser bool, isAdmin bool, ) (bool, string) { validTransitions, exists := sm.transitions[from] if !exists { return false, "当前状态不支持任何转换" } for _, transition := range validTransitions { if transition.To == to { if isUser && !transition.AllowUser { return false, "用户不允许执行此操作" } if isAdmin && !transition.AllowAdmin { return false, "管理员不允许执行此操作" } if !isUser && !isAdmin && (transition.AllowUser || transition.AllowAdmin) { return false, "此操作需要用户或管理员权限" } return true, "" } } return false, "不支持的状态转换" } // TransitionTo 执行状态转换 func (sm *CertificationStateMachine) TransitionTo( ctx context.Context, certificationID string, targetStatus enums.CertificationStatus, isUser bool, isAdmin bool, metadata map[string]interface{}, ) error { // 获取当前认证记录 cert, err := sm.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("获取认证记录失败: %w", err) } // 检查是否可以转换 canTransition, reason := sm.CanTransition(cert.Status, targetStatus, isUser, isAdmin) if !canTransition { return fmt.Errorf("状态转换失败: %s", reason) } // 执行状态转换前的验证 if err := sm.validateTransition(ctx, cert, targetStatus, metadata); err != nil { return fmt.Errorf("状态转换验证失败: %w", err) } // 更新状态和时间戳 oldStatus := cert.Status cert.Status = targetStatus sm.updateTimestamp(cert, targetStatus) // 更新其他字段 sm.updateCertificationFields(cert, targetStatus, metadata) // 保存到数据库 if err := sm.certRepo.Update(ctx, cert); err != nil { return fmt.Errorf("保存状态转换失败: %w", err) } sm.logger.Info("认证状态转换成功", zap.String("certification_id", certificationID), zap.String("from_status", string(oldStatus)), zap.String("to_status", string(targetStatus)), zap.Bool("is_user", isUser), zap.Bool("is_admin", isAdmin), ) return nil } // updateTimestamp 更新对应的时间戳字段 func (sm *CertificationStateMachine) updateTimestamp(cert *entities.Certification, status enums.CertificationStatus) { now := time.Now() switch status { case enums.StatusInfoSubmitted: cert.InfoSubmittedAt = &now case enums.StatusFaceVerified: cert.FaceVerifiedAt = &now case enums.StatusContractApplied: cert.ContractAppliedAt = &now case enums.StatusContractApproved: cert.ContractApprovedAt = &now case enums.StatusContractSigned: cert.ContractSignedAt = &now case enums.StatusCompleted: cert.CompletedAt = &now } } // updateCertificationFields 根据状态更新认证记录的其他字段 func (sm *CertificationStateMachine) updateCertificationFields( cert *entities.Certification, status enums.CertificationStatus, metadata map[string]interface{}, ) { switch status { case enums.StatusContractApproved: if adminID, ok := metadata["admin_id"].(string); ok { cert.AdminID = &adminID } if approvalNotes, ok := metadata["approval_notes"].(string); ok { cert.ApprovalNotes = approvalNotes } if signingURL, ok := metadata["signing_url"].(string); ok { cert.SigningURL = signingURL } case enums.StatusRejected: if adminID, ok := metadata["admin_id"].(string); ok { cert.AdminID = &adminID } if rejectReason, ok := metadata["reject_reason"].(string); ok { cert.RejectReason = rejectReason } case enums.StatusContractSigned: if contractURL, ok := metadata["contract_url"].(string); ok { cert.ContractURL = contractURL } } } // validateTransition 验证状态转换的有效性 func (sm *CertificationStateMachine) validateTransition( ctx context.Context, cert *entities.Certification, targetStatus enums.CertificationStatus, metadata map[string]interface{}, ) error { switch targetStatus { case enums.StatusInfoSubmitted: // 验证企业信息是否完整 if cert.EnterpriseID == nil { return fmt.Errorf("企业信息未提交") } case enums.StatusFaceVerified: // 验证人脸识别是否成功 // 这里可以添加人脸识别结果的验证逻辑 case enums.StatusContractApproved: // 验证管理员审核信息 if metadata["signing_url"] == nil || metadata["signing_url"].(string) == "" { return fmt.Errorf("缺少合同签署链接") } case enums.StatusRejected: // 验证拒绝原因 if metadata["reject_reason"] == nil || metadata["reject_reason"].(string) == "" { return fmt.Errorf("缺少拒绝原因") } case enums.StatusContractSigned: // 验证合同签署信息 if cert.SigningURL == "" { return fmt.Errorf("缺少合同签署链接") } } return nil } // GetValidNextStatuses 获取当前状态可以转换到的下一个状态列表 func (sm *CertificationStateMachine) GetValidNextStatuses( currentStatus enums.CertificationStatus, isUser bool, isAdmin bool, ) []enums.CertificationStatus { var validStatuses []enums.CertificationStatus transitions, exists := sm.transitions[currentStatus] if !exists { return validStatuses } for _, transition := range transitions { if (isUser && transition.AllowUser) || (isAdmin && transition.AllowAdmin) { validStatuses = append(validStatuses, transition.To) } } return validStatuses } // GetTransitionAction 获取状态转换对应的操作名称 func (sm *CertificationStateMachine) GetTransitionAction( from enums.CertificationStatus, to enums.CertificationStatus, ) string { transitions, exists := sm.transitions[from] if !exists { return "" } for _, transition := range transitions { if transition.To == to { return transition.Action } } return "" }