Files
udmin/docs/ERROR_HANDLING.md
ayou a3f2f99a68 docs: 添加项目文档包括总览、架构、流程引擎和服务层
新增以下文档文件:
- PROJECT_OVERVIEW.md 项目总览文档
- BACKEND_ARCHITECTURE.md 后端架构文档
- FRONTEND_ARCHITECTURE.md 前端架构文档
- FLOW_ENGINE.md 流程引擎文档
- SERVICES.md 服务层文档
- ERROR_HANDLING.md 错误处理模块文档

文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
2025-09-24 20:21:45 +08:00

28 KiB
Raw Permalink Blame History

UdminAI 错误处理模块文档

概述

UdminAI 项目的错误处理模块提供了统一的错误定义、处理和响应机制。该模块基于 Rust 的 Result 类型和 thiserror 库构建,确保错误信息的一致性、可追踪性和用户友好性。

设计原则

核心理念

  • 统一性: 所有模块使用统一的错误类型
  • 可追踪性: 错误包含足够的上下文信息
  • 用户友好: 面向用户的错误消息清晰易懂
  • 开发友好: 面向开发者的错误信息详细准确
  • 类型安全: 编译时错误类型检查

错误分层

  1. 应用层错误: 业务逻辑错误
  2. 服务层错误: 服务调用错误
  3. 数据层错误: 数据库和缓存错误
  4. 网络层错误: HTTP 和网络通信错误
  5. 系统层错误: 系统资源和配置错误

错误类型定义 (error.rs)

主要错误类型

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use serde::{Deserialize, Serialize};
use std::fmt;
use thiserror::Error;
use tracing::error;

/// 应用主错误类型
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum AppError {
    // 认证和授权错误
    #[error("认证失败: {message}")]
    AuthenticationFailed { message: String },
    
    #[error("授权失败: {message}")]
    AuthorizationFailed { message: String },
    
    #[error("令牌无效: {message}")]
    InvalidToken { message: String },
    
    #[error("令牌已过期")]
    TokenExpired,
    
    // 验证错误
    #[error("验证失败: {field} - {message}")]
    ValidationFailed { field: String, message: String },
    
    #[error("请求参数无效: {message}")]
    InvalidRequest { message: String },
    
    #[error("必需字段缺失: {field}")]
    MissingField { field: String },
    
    // 资源错误
    #[error("资源未找到: {resource}")]
    NotFound(String),
    
    #[error("资源已存在: {resource}")]
    AlreadyExists(String),
    
    #[error("资源冲突: {message}")]
    Conflict { message: String },
    
    // 业务逻辑错误
    #[error("业务规则违反: {message}")]
    BusinessRuleViolation { message: String },
    
    #[error("操作不被允许: {message}")]
    OperationNotAllowed { message: String },
    
    #[error("状态无效: 当前状态 {current}, 期望状态 {expected}")]
    InvalidState { current: String, expected: String },
    
    // 数据库错误
    #[error("数据库错误: {0}")]
    DatabaseError(String),
    
    #[error("数据库连接失败: {message}")]
    DatabaseConnectionFailed { message: String },
    
    #[error("事务失败: {message}")]
    TransactionFailed { message: String },
    
    // 缓存错误
    #[error("缓存错误: {0}")]
    CacheError(String),
    
    #[error("缓存连接失败: {message}")]
    CacheConnectionFailed { message: String },
    
    // 网络和外部服务错误
    #[error("网络错误: {message}")]
    NetworkError { message: String },
    
    #[error("外部服务错误: {service} - {message}")]
    ExternalServiceError { service: String, message: String },
    
    #[error("HTTP 请求失败: {status} - {message}")]
    HttpRequestFailed { status: u16, message: String },
    
    // 文件和 I/O 错误
    #[error("文件操作失败: {message}")]
    FileOperationFailed { message: String },
    
    #[error("文件未找到: {path}")]
    FileNotFound { path: String },
    
    #[error("文件权限不足: {path}")]
    FilePermissionDenied { path: String },
    
    // 配置和环境错误
    #[error("配置错误: {message}")]
    ConfigurationError { message: String },
    
    #[error("环境变量缺失: {variable}")]
    MissingEnvironmentVariable { variable: String },
    
    // 序列化和反序列化错误
    #[error("序列化失败: {message}")]
    SerializationFailed { message: String },
    
    #[error("反序列化失败: {message}")]
    DeserializationFailed { message: String },
    
    #[error("JSON 格式错误: {message}")]
    JsonFormatError { message: String },
    
    // 流程引擎错误
    #[error("流程执行失败: {flow_id} - {message}")]
    FlowExecutionFailed { flow_id: String, message: String },
    
    #[error("流程解析失败: {message}")]
    FlowParsingFailed { message: String },
    
    #[error("节点执行失败: {node_id} - {message}")]
    NodeExecutionFailed { node_id: String, message: String },
    
    // 调度任务错误
    #[error("任务调度失败: {job_id} - {message}")]
    JobSchedulingFailed { job_id: String, message: String },
    
    #[error("Cron 表达式无效: {expression}")]
    InvalidCronExpression { expression: String },
    
    #[error("任务执行超时: {job_id}")]
    JobExecutionTimeout { job_id: String },
    
    // 系统错误
    #[error("内部服务器错误: {message}")]
    InternalServerError { message: String },
    
    #[error("服务不可用: {message}")]
    ServiceUnavailable { message: String },
    
    #[error("请求超时: {message}")]
    RequestTimeout { message: String },
    
    #[error("资源耗尽: {resource}")]
    ResourceExhausted { resource: String },
    
    // 限流和安全错误
    #[error("请求频率过高: {message}")]
    RateLimitExceeded { message: String },
    
    #[error("请求体过大: 当前大小 {current}, 最大允许 {max}")]
    PayloadTooLarge { current: usize, max: usize },
    
    #[error("不支持的媒体类型: {media_type}")]
    UnsupportedMediaType { media_type: String },
}

/// 错误响应结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
    pub error: ErrorDetail,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub request_id: Option<String>,
}

/// 错误详情
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorDetail {
    pub code: String,
    pub message: String,
    pub details: Option<serde_json::Value>,
    pub field: Option<String>,
}

impl AppError {
    /// 获取错误代码
    pub fn error_code(&self) -> &'static str {
        match self {
            // 认证和授权
            AppError::AuthenticationFailed { .. } => "AUTH_FAILED",
            AppError::AuthorizationFailed { .. } => "AUTHORIZATION_FAILED",
            AppError::InvalidToken { .. } => "INVALID_TOKEN",
            AppError::TokenExpired => "TOKEN_EXPIRED",
            
            // 验证
            AppError::ValidationFailed { .. } => "VALIDATION_FAILED",
            AppError::InvalidRequest { .. } => "INVALID_REQUEST",
            AppError::MissingField { .. } => "MISSING_FIELD",
            
            // 资源
            AppError::NotFound(_) => "NOT_FOUND",
            AppError::AlreadyExists(_) => "ALREADY_EXISTS",
            AppError::Conflict { .. } => "CONFLICT",
            
            // 业务逻辑
            AppError::BusinessRuleViolation { .. } => "BUSINESS_RULE_VIOLATION",
            AppError::OperationNotAllowed { .. } => "OPERATION_NOT_ALLOWED",
            AppError::InvalidState { .. } => "INVALID_STATE",
            
            // 数据库
            AppError::DatabaseError(_) => "DATABASE_ERROR",
            AppError::DatabaseConnectionFailed { .. } => "DATABASE_CONNECTION_FAILED",
            AppError::TransactionFailed { .. } => "TRANSACTION_FAILED",
            
            // 缓存
            AppError::CacheError(_) => "CACHE_ERROR",
            AppError::CacheConnectionFailed { .. } => "CACHE_CONNECTION_FAILED",
            
            // 网络
            AppError::NetworkError { .. } => "NETWORK_ERROR",
            AppError::ExternalServiceError { .. } => "EXTERNAL_SERVICE_ERROR",
            AppError::HttpRequestFailed { .. } => "HTTP_REQUEST_FAILED",
            
            // 文件
            AppError::FileOperationFailed { .. } => "FILE_OPERATION_FAILED",
            AppError::FileNotFound { .. } => "FILE_NOT_FOUND",
            AppError::FilePermissionDenied { .. } => "FILE_PERMISSION_DENIED",
            
            // 配置
            AppError::ConfigurationError { .. } => "CONFIGURATION_ERROR",
            AppError::MissingEnvironmentVariable { .. } => "MISSING_ENV_VAR",
            
            // 序列化
            AppError::SerializationFailed { .. } => "SERIALIZATION_FAILED",
            AppError::DeserializationFailed { .. } => "DESERIALIZATION_FAILED",
            AppError::JsonFormatError { .. } => "JSON_FORMAT_ERROR",
            
            // 流程引擎
            AppError::FlowExecutionFailed { .. } => "FLOW_EXECUTION_FAILED",
            AppError::FlowParsingFailed { .. } => "FLOW_PARSING_FAILED",
            AppError::NodeExecutionFailed { .. } => "NODE_EXECUTION_FAILED",
            
            // 调度任务
            AppError::JobSchedulingFailed { .. } => "JOB_SCHEDULING_FAILED",
            AppError::InvalidCronExpression { .. } => "INVALID_CRON_EXPRESSION",
            AppError::JobExecutionTimeout { .. } => "JOB_EXECUTION_TIMEOUT",
            
            // 系统
            AppError::InternalServerError { .. } => "INTERNAL_SERVER_ERROR",
            AppError::ServiceUnavailable { .. } => "SERVICE_UNAVAILABLE",
            AppError::RequestTimeout { .. } => "REQUEST_TIMEOUT",
            AppError::ResourceExhausted { .. } => "RESOURCE_EXHAUSTED",
            
            // 限流和安全
            AppError::RateLimitExceeded { .. } => "RATE_LIMIT_EXCEEDED",
            AppError::PayloadTooLarge { .. } => "PAYLOAD_TOO_LARGE",
            AppError::UnsupportedMediaType { .. } => "UNSUPPORTED_MEDIA_TYPE",
        }
    }

    /// 获取 HTTP 状态码
    pub fn status_code(&self) -> StatusCode {
        match self {
            // 4xx 客户端错误
            AppError::AuthenticationFailed { .. } => StatusCode::UNAUTHORIZED,
            AppError::AuthorizationFailed { .. } => StatusCode::FORBIDDEN,
            AppError::InvalidToken { .. } => StatusCode::UNAUTHORIZED,
            AppError::TokenExpired => StatusCode::UNAUTHORIZED,
            
            AppError::ValidationFailed { .. } => StatusCode::BAD_REQUEST,
            AppError::InvalidRequest { .. } => StatusCode::BAD_REQUEST,
            AppError::MissingField { .. } => StatusCode::BAD_REQUEST,
            
            AppError::NotFound(_) => StatusCode::NOT_FOUND,
            AppError::AlreadyExists(_) => StatusCode::CONFLICT,
            AppError::Conflict { .. } => StatusCode::CONFLICT,
            
            AppError::BusinessRuleViolation { .. } => StatusCode::BAD_REQUEST,
            AppError::OperationNotAllowed { .. } => StatusCode::FORBIDDEN,
            AppError::InvalidState { .. } => StatusCode::BAD_REQUEST,
            
            AppError::FileNotFound { .. } => StatusCode::NOT_FOUND,
            AppError::FilePermissionDenied { .. } => StatusCode::FORBIDDEN,
            
            AppError::JsonFormatError { .. } => StatusCode::BAD_REQUEST,
            AppError::InvalidCronExpression { .. } => StatusCode::BAD_REQUEST,
            
            AppError::RateLimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
            AppError::PayloadTooLarge { .. } => StatusCode::PAYLOAD_TOO_LARGE,
            AppError::UnsupportedMediaType { .. } => StatusCode::UNSUPPORTED_MEDIA_TYPE,
            
            // 5xx 服务器错误
            AppError::DatabaseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::DatabaseConnectionFailed { .. } => StatusCode::SERVICE_UNAVAILABLE,
            AppError::TransactionFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            
            AppError::CacheError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::CacheConnectionFailed { .. } => StatusCode::SERVICE_UNAVAILABLE,
            
            AppError::NetworkError { .. } => StatusCode::BAD_GATEWAY,
            AppError::ExternalServiceError { .. } => StatusCode::BAD_GATEWAY,
            AppError::HttpRequestFailed { .. } => StatusCode::BAD_GATEWAY,
            
            AppError::FileOperationFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            
            AppError::ConfigurationError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::MissingEnvironmentVariable { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            
            AppError::SerializationFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::DeserializationFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            
            AppError::FlowExecutionFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::FlowParsingFailed { .. } => StatusCode::BAD_REQUEST,
            AppError::NodeExecutionFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            
            AppError::JobSchedulingFailed { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::JobExecutionTimeout { .. } => StatusCode::REQUEST_TIMEOUT,
            
            AppError::InternalServerError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::ServiceUnavailable { .. } => StatusCode::SERVICE_UNAVAILABLE,
            AppError::RequestTimeout { .. } => StatusCode::REQUEST_TIMEOUT,
            AppError::ResourceExhausted { .. } => StatusCode::SERVICE_UNAVAILABLE,
        }
    }

    /// 获取错误字段(如果适用)
    pub fn error_field(&self) -> Option<String> {
        match self {
            AppError::ValidationFailed { field, .. } => Some(field.clone()),
            AppError::MissingField { field } => Some(field.clone()),
            _ => None,
        }
    }

    /// 是否为客户端错误
    pub fn is_client_error(&self) -> bool {
        self.status_code().is_client_error()
    }

    /// 是否为服务器错误
    pub fn is_server_error(&self) -> bool {
        self.status_code().is_server_error()
    }

    /// 创建错误响应
    pub fn to_error_response(&self, request_id: Option<String>) -> ErrorResponse {
        ErrorResponse {
            error: ErrorDetail {
                code: self.error_code().to_string(),
                message: self.to_string(),
                details: None,
                field: self.error_field(),
            },
            timestamp: chrono::Utc::now(),
            request_id,
        }
    }

    /// 记录错误日志
    pub fn log_error(&self, request_id: Option<&str>) {
        let level = if self.is_server_error() {
            tracing::Level::ERROR
        } else {
            tracing::Level::WARN
        };

        match level {
            tracing::Level::ERROR => {
                error!(
                    target = "udmin",
                    error_code = %self.error_code(),
                    error_message = %self,
                    request_id = ?request_id,
                    "application.error.server"
                );
            }
            _ => {
                tracing::warn!(
                    target = "udmin",
                    error_code = %self.error_code(),
                    error_message = %self,
                    request_id = ?request_id,
                    "application.error.client"
                );
            }
        }
    }
}

/// 实现 IntoResponse使错误可以直接作为 HTTP 响应返回
impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        // 从请求上下文获取 request_id实际实现中可能需要通过中间件传递
        let request_id = None; // 这里应该从上下文获取
        
        // 记录错误日志
        self.log_error(request_id.as_deref());
        
        // 创建错误响应
        let error_response = self.to_error_response(request_id);
        let status_code = self.status_code();
        
        (status_code, Json(error_response)).into_response()
    }
}

/// 应用结果类型别名
pub type AppResult<T> = Result<T, AppError>;

/// 错误转换实现
impl From<sea_orm::DbErr> for AppError {
    fn from(err: sea_orm::DbErr) -> Self {
        match err {
            sea_orm::DbErr::RecordNotFound(_) => AppError::NotFound("记录不存在".to_string()),
            sea_orm::DbErr::Custom(msg) => AppError::DatabaseError(msg),
            sea_orm::DbErr::Conn(msg) => AppError::DatabaseConnectionFailed { message: msg },
            sea_orm::DbErr::Exec(msg) => AppError::DatabaseError(msg),
            sea_orm::DbErr::Query(msg) => AppError::DatabaseError(msg),
            _ => AppError::DatabaseError("未知数据库错误".to_string()),
        }
    }
}

impl From<redis::RedisError> for AppError {
    fn from(err: redis::RedisError) -> Self {
        match err.kind() {
            redis::ErrorKind::IoError => AppError::CacheConnectionFailed {
                message: err.to_string(),
            },
            redis::ErrorKind::AuthenticationFailed => AppError::CacheConnectionFailed {
                message: "Redis 认证失败".to_string(),
            },
            _ => AppError::CacheError(err.to_string()),
        }
    }
}

impl From<reqwest::Error> for AppError {
    fn from(err: reqwest::Error) -> Self {
        if err.is_timeout() {
            AppError::RequestTimeout {
                message: "HTTP 请求超时".to_string(),
            }
        } else if err.is_connect() {
            AppError::NetworkError {
                message: "网络连接失败".to_string(),
            }
        } else if let Some(status) = err.status() {
            AppError::HttpRequestFailed {
                status: status.as_u16(),
                message: err.to_string(),
            }
        } else {
            AppError::NetworkError {
                message: err.to_string(),
            }
        }
    }
}

impl From<serde_json::Error> for AppError {
    fn from(err: serde_json::Error) -> Self {
        if err.is_syntax() {
            AppError::JsonFormatError {
                message: "JSON 语法错误".to_string(),
            }
        } else if err.is_data() {
            AppError::DeserializationFailed {
                message: err.to_string(),
            }
        } else {
            AppError::SerializationFailed {
                message: err.to_string(),
            }
        }
    }
}

impl From<std::io::Error> for AppError {
    fn from(err: std::io::Error) -> Self {
        match err.kind() {
            std::io::ErrorKind::NotFound => AppError::FileNotFound {
                path: "未知路径".to_string(),
            },
            std::io::ErrorKind::PermissionDenied => AppError::FilePermissionDenied {
                path: "未知路径".to_string(),
            },
            _ => AppError::FileOperationFailed {
                message: err.to_string(),
            },
        }
    }
}

impl From<tokio::time::error::Elapsed> for AppError {
    fn from(_: tokio::time::error::Elapsed) -> Self {
        AppError::RequestTimeout {
            message: "操作超时".to_string(),
        }
    }
}

/// 错误构建器
pub struct ErrorBuilder {
    error: AppError,
}

impl ErrorBuilder {
    pub fn new(error: AppError) -> Self {
        Self { error }
    }

    pub fn with_details(mut self, details: serde_json::Value) -> Self {
        // 这里可以扩展错误以包含更多详情
        self
    }

    pub fn with_field(mut self, field: String) -> Self {
        // 设置错误字段
        self
    }

    pub fn build(self) -> AppError {
        self.error
    }
}

/// 错误宏
#[macro_export]
macro_rules! app_error {
    ($error_type:ident, $($field:ident = $value:expr),*) => {
        AppError::$error_type {
            $($field: $value.into()),*
        }
    };
}

/// 结果扩展 trait
pub trait ResultExt<T> {
    /// 将错误转换为 AppError
    fn map_app_error<F>(self, f: F) -> AppResult<T>
    where
        F: FnOnce() -> AppError;
    
    /// 添加错误上下文
    fn with_context<F>(self, f: F) -> AppResult<T>
    where
        F: FnOnce() -> String;
}

impl<T, E> ResultExt<T> for Result<T, E>
where
    E: Into<AppError>,
{
    fn map_app_error<F>(self, f: F) -> AppResult<T>
    where
        F: FnOnce() -> AppError,
    {
        self.map_err(|_| f())
    }

    fn with_context<F>(self, f: F) -> AppResult<T>
    where
        F: FnOnce() -> String,
    {
        self.map_err(|e| {
            let context = f();
            match e.into() {
                AppError::InternalServerError { message } => AppError::InternalServerError {
                    message: format!("{}: {}", context, message),
                },
                other => other,
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_codes() {
        let error = AppError::NotFound("用户".to_string());
        assert_eq!(error.error_code(), "NOT_FOUND");
        assert_eq!(error.status_code(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_error_response() {
        let error = AppError::ValidationFailed {
            field: "email".to_string(),
            message: "格式无效".to_string(),
        };
        
        let response = error.to_error_response(Some("req-123".to_string()));
        assert_eq!(response.error.code, "VALIDATION_FAILED");
        assert_eq!(response.error.field, Some("email".to_string()));
        assert_eq!(response.request_id, Some("req-123".to_string()));
    }

    #[test]
    fn test_error_conversion() {
        let db_error = sea_orm::DbErr::RecordNotFound("test".to_string());
        let app_error: AppError = db_error.into();
        
        match app_error {
            AppError::NotFound(_) => assert!(true),
            _ => assert!(false, "Expected NotFound error"),
        }
    }

    #[test]
    fn test_error_macro() {
        let error = app_error!(ValidationFailed, 
            field = "username", 
            message = "用户名已存在"
        );
        
        match error {
            AppError::ValidationFailed { field, message } => {
                assert_eq!(field, "username");
                assert_eq!(message, "用户名已存在");
            }
            _ => assert!(false, "Expected ValidationFailed error"),
        }
    }

    #[test]
    fn test_result_ext() {
        let result: Result<i32, &str> = Err("test error");
        let app_result = result.map_app_error(|| AppError::InternalServerError {
            message: "转换错误".to_string(),
        });
        
        assert!(app_result.is_err());
    }
}

错误处理策略

错误传播

/// 错误传播示例
pub async fn create_user(req: CreateUserReq) -> AppResult<UserDoc> {
    // 验证请求
    validate_create_user_request(&req)?;
    
    // 检查用户是否已存在
    if user_exists(&req.email).await? {
        return Err(AppError::AlreadyExists("用户邮箱已存在".to_string()));
    }
    
    // 创建用户
    let user = User::create(req).await
        .with_context(|| "创建用户失败")?;
    
    Ok(user.into())
}

错误恢复

/// 错误恢复示例
pub async fn get_user_with_fallback(id: &str) -> AppResult<UserDoc> {
    // 首先尝试从缓存获取
    match get_user_from_cache(id).await {
        Ok(user) => return Ok(user),
        Err(AppError::CacheError(_)) => {
            // 缓存错误,尝试从数据库获取
            tracing::warn!("缓存获取用户失败,尝试数据库");
        }
        Err(e) => return Err(e),
    }
    
    // 从数据库获取
    let user = get_user_from_db(id).await?;
    
    // 尝试更新缓存(忽略错误)
    if let Err(e) = set_user_cache(id, &user).await {
        tracing::warn!(error = %e, "更新用户缓存失败");
    }
    
    Ok(user)
}

错误聚合

/// 错误聚合示例
pub struct ValidationErrors {
    pub errors: Vec<AppError>,
}

impl ValidationErrors {
    pub fn new() -> Self {
        Self { errors: Vec::new() }
    }
    
    pub fn add(&mut self, error: AppError) {
        self.errors.push(error);
    }
    
    pub fn is_empty(&self) -> bool {
        self.errors.is_empty()
    }
    
    pub fn into_result(self) -> AppResult<()> {
        if self.errors.is_empty() {
            Ok(())
        } else {
            // 返回第一个错误,或者可以创建一个聚合错误类型
            Err(self.errors.into_iter().next().unwrap())
        }
    }
}

pub fn validate_user_data(data: &CreateUserReq) -> AppResult<()> {
    let mut errors = ValidationErrors::new();
    
    if data.email.is_empty() {
        errors.add(AppError::MissingField { field: "email".to_string() });
    }
    
    if data.password.len() < 8 {
        errors.add(AppError::ValidationFailed {
            field: "password".to_string(),
            message: "密码长度至少8位".to_string(),
        });
    }
    
    errors.into_result()
}

中间件集成

错误处理中间件

use axum::{
    extract::Request,
    middleware::Next,
    response::Response,
};
use uuid::Uuid;

/// 错误处理中间件
pub async fn error_handler_middleware(
    mut request: Request,
    next: Next,
) -> Response {
    // 生成请求 ID
    let request_id = Uuid::new_v4().to_string();
    request.extensions_mut().insert(request_id.clone());
    
    // 执行请求
    let response = next.run(request).await;
    
    // 如果响应是错误,添加请求 ID
    if response.status().is_client_error() || response.status().is_server_error() {
        // 这里可以修改响应头,添加请求 ID
        tracing::info!(
            target = "udmin",
            request_id = %request_id,
            status = %response.status(),
            "request.completed.error"
        );
    }
    
    response
}

监控和告警

错误指标收集

use prometheus::{Counter, Histogram, Registry};
use std::sync::Arc;

/// 错误指标收集器
#[derive(Clone)]
pub struct ErrorMetrics {
    error_counter: Counter,
    error_duration: Histogram,
}

impl ErrorMetrics {
    pub fn new(registry: &Registry) -> Self {
        let error_counter = Counter::new(
            "app_errors_total",
            "Total number of application errors"
        ).unwrap();
        
        let error_duration = Histogram::new(
            "error_handling_duration_seconds",
            "Time spent handling errors"
        ).unwrap();
        
        registry.register(Box::new(error_counter.clone())).unwrap();
        registry.register(Box::new(error_duration.clone())).unwrap();
        
        Self {
            error_counter,
            error_duration,
        }
    }
    
    pub fn record_error(&self, error: &AppError) {
        self.error_counter.inc();
        
        // 可以根据错误类型添加标签
        tracing::info!(
            target = "udmin",
            error_code = %error.error_code(),
            error_type = %std::any::type_name::<AppError>(),
            "metrics.error.recorded"
        );
    }
}

最佳实践

错误设计原则

  1. 明确性: 错误消息应该清楚地说明发生了什么
  2. 可操作性: 错误消息应该告诉用户如何解决问题
  3. 一致性: 相同类型的错误应该有一致的格式和处理方式
  4. 安全性: 不要在错误消息中泄露敏感信息

错误处理模式

  1. 快速失败: 尽早检测和报告错误
  2. 优雅降级: 在可能的情况下提供备选方案
  3. 错误隔离: 防止错误在系统中传播
  4. 错误恢复: 在适当的时候尝试从错误中恢复

日志记录

  1. 结构化日志: 使用结构化格式记录错误信息
  2. 上下文信息: 包含足够的上下文信息用于调试
  3. 敏感信息: 避免在日志中记录敏感信息
  4. 日志级别: 根据错误严重程度选择合适的日志级别

总结

UdminAI 的错误处理模块提供了完整的错误管理解决方案,具有以下特点:

  • 类型安全: 编译时错误类型检查
  • 统一处理: 所有错误使用统一的类型和格式
  • 用户友好: 清晰的错误消息和适当的 HTTP 状态码
  • 可观测性: 完整的错误日志和指标收集
  • 可扩展性: 易于添加新的错误类型和处理逻辑

通过合理的错误处理设计,确保了系统的稳定性、可维护性和用户体验。