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: // 验证企业信息是否完整 // 这里应该检查用户是否有企业信息,通过用户域的企业服务验证 // 暂时跳过验证,由应用服务层协调 break 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 "" } // GetTransitionHistory 获取状态转换历史 func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, certificationID string) ([]map[string]interface{}, error) { cert, err := sm.certRepo.GetByID(ctx, certificationID) if err != nil { return nil, fmt.Errorf("获取认证记录失败: %w", err) } history := []map[string]interface{}{} // 添加创建时间 history = append(history, map[string]interface{}{ "status": "CREATED", "timestamp": cert.CreatedAt, "action": "create", "performer": "system", "metadata": map[string]interface{}{}, }) // 添加各个时间节点的状态转换 if cert.InfoSubmittedAt != nil { history = append(history, map[string]interface{}{ "status": string(enums.StatusInfoSubmitted), "timestamp": *cert.InfoSubmittedAt, "action": "submit_info", "performer": "user", "metadata": map[string]interface{}{}, }) } if cert.FaceVerifiedAt != nil { history = append(history, map[string]interface{}{ "status": string(enums.StatusFaceVerified), "timestamp": *cert.FaceVerifiedAt, "action": "face_verify", "performer": "system", "metadata": map[string]interface{}{}, }) } if cert.ContractAppliedAt != nil { history = append(history, map[string]interface{}{ "status": string(enums.StatusContractApplied), "timestamp": *cert.ContractAppliedAt, "action": "apply_contract", "performer": "user", "metadata": map[string]interface{}{}, }) } if cert.ContractApprovedAt != nil { metadata := map[string]interface{}{} if cert.AdminID != nil { metadata["admin_id"] = *cert.AdminID } if cert.ApprovalNotes != "" { metadata["approval_notes"] = cert.ApprovalNotes } if cert.SigningURL != "" { metadata["signing_url"] = cert.SigningURL } history = append(history, map[string]interface{}{ "status": string(enums.StatusContractApproved), "timestamp": *cert.ContractApprovedAt, "action": "admin_approve", "performer": "admin", "metadata": metadata, }) } if cert.ContractSignedAt != nil { metadata := map[string]interface{}{} if cert.ContractURL != "" { metadata["contract_url"] = cert.ContractURL } history = append(history, map[string]interface{}{ "status": string(enums.StatusContractSigned), "timestamp": *cert.ContractSignedAt, "action": "user_sign", "performer": "user", "metadata": metadata, }) } if cert.CompletedAt != nil { history = append(history, map[string]interface{}{ "status": string(enums.StatusCompleted), "timestamp": *cert.CompletedAt, "action": "system_complete", "performer": "system", "metadata": map[string]interface{}{}, }) } return history, nil } // ValidateCertificationFlow 验证认证流程的完整性 func (sm *CertificationStateMachine) ValidateCertificationFlow(ctx context.Context, certificationID string) (map[string]interface{}, error) { cert, err := sm.certRepo.GetByID(ctx, certificationID) if err != nil { return nil, fmt.Errorf("获取认证记录失败: %w", err) } validation := map[string]interface{}{ "certification_id": certificationID, "current_status": cert.Status, "is_valid": true, "issues": []string{}, "warnings": []string{}, } // 检查必要的时间节点 if cert.Status != enums.StatusPending { if cert.InfoSubmittedAt == nil { validation["is_valid"] = false validation["issues"] = append(validation["issues"].([]string), "缺少企业信息提交时间") } } if cert.Status == enums.StatusFaceVerified || cert.Status == enums.StatusContractApplied || cert.Status == enums.StatusContractPending || cert.Status == enums.StatusContractApproved || cert.Status == enums.StatusContractSigned || cert.Status == enums.StatusCompleted { if cert.FaceVerifiedAt == nil { validation["is_valid"] = false validation["issues"] = append(validation["issues"].([]string), "缺少人脸识别完成时间") } } if cert.Status == enums.StatusContractApproved || cert.Status == enums.StatusContractSigned || cert.Status == enums.StatusCompleted { if cert.ContractApprovedAt == nil { validation["is_valid"] = false validation["issues"] = append(validation["issues"].([]string), "缺少合同审核时间") } if cert.SigningURL == "" { validation["warnings"] = append(validation["warnings"].([]string), "缺少合同签署链接") } } if cert.Status == enums.StatusContractSigned || cert.Status == enums.StatusCompleted { if cert.ContractSignedAt == nil { validation["is_valid"] = false validation["issues"] = append(validation["issues"].([]string), "缺少合同签署时间") } if cert.ContractURL == "" { validation["warnings"] = append(validation["warnings"].([]string), "缺少合同文件链接") } } if cert.Status == enums.StatusCompleted { if cert.CompletedAt == nil { validation["is_valid"] = false validation["issues"] = append(validation["issues"].([]string), "缺少认证完成时间") } } return validation, nil }