Skip to main content

fbc_starter/
error.rs

1use axum::{
2    http::StatusCode,
3    response::{IntoResponse, Response},
4    Json,
5};
6use serde_json::json;
7use thiserror::Error;
8
9/// 错误分类(业务/通用/自定义)
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ErrorCategory {
12    /// 业务逻辑错误
13    Biz,
14    /// 通用错误
15    Common,
16    /// 自定义错误
17    Custom,
18}
19
20impl std::fmt::Display for ErrorCategory {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            ErrorCategory::Biz => write!(f, "业务错误"),
24            ErrorCategory::Common => write!(f, "通用错误"),
25            ErrorCategory::Custom => write!(f, "自定义错误"),
26        }
27    }
28}
29
30/// 应用错误类型
31#[derive(Error, Debug)]
32pub enum AppError {
33    #[error("内部服务器错误: {0}")]
34    Internal(#[from] anyhow::Error),
35
36    #[error("未找到资源")]
37    NotFound,
38
39    #[error("未授权访问")]
40    Unauthorized,
41
42    #[error("禁止访问")]
43    Forbidden,
44
45    #[error("请求参数错误: {0}")]
46    BadRequest(String),
47
48    #[error("配置错误: {0}")]
49    Config(#[from] config::ConfigError),
50
51    /// 服务不可用(无可用实例)
52    #[error("服务不可用: {0}")]
53    ServiceUnavailable(String),
54
55    /// 数据库错误(需要启用 mysql/postgres/sqlite 任一特性)
56    #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
57    #[error("数据库错误: {0}")]
58    Database(#[from] sqlx::Error),
59
60    /// 数据库连接池未初始化错误
61    #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
62    #[error("数据库连接池未初始化")]
63    DatabaseNotInitialized,
64
65    /// Redis 错误(需要启用 redis 特性)
66    #[cfg(feature = "redis")]
67    #[error("Redis 错误: {0}")]
68    Redis(#[from] redis::RedisError),
69
70    /// Redis 客户端未初始化错误
71    #[cfg(feature = "redis")]
72    #[error("Redis 客户端未初始化")]
73    RedisNotInitialized,
74
75    /// 分类错误(合并原有的 BizError / CommonError / CustomerError)
76    ///
77    /// # 参数
78    /// - `0`: 业务状态码
79    /// - `1`: 错误消息
80    /// - `2`: 错误分类
81    #[error("{2}: [{0}] {1}")]
82    Categorized(i32, String, ErrorCategory),
83
84    /// IO 错误
85    #[error("IO 错误: {0}")]
86    Io(#[from] std::io::Error),
87
88    /// 地址解析错误
89    #[error("地址解析错误: {0}")]
90    Addr(#[from] std::net::AddrParseError),
91
92    /// Redis 连接池错误
93    #[cfg(feature = "redis")]
94    #[error("Redis 连接池错误: {0}")]
95    RedisPool(#[from] deadpool_redis::PoolError),
96
97    /// Kafka 错误
98    #[cfg(feature = "kafka")]
99    #[error("Kafka 错误: {0}")]
100    Kafka(#[from] rdkafka::error::KafkaError),
101}
102
103/// 应用结果类型
104pub type AppResult<T> = Result<T, AppError>;
105
106impl AppError {
107    /// 创建业务错误(向后兼容 API)
108    pub fn biz_error(status: i32, message: String) -> Self {
109        Self::Categorized(status, message, ErrorCategory::Biz)
110    }
111
112    /// 创建通用错误(向后兼容 API)
113    pub fn common_error(status: i32, message: String) -> Self {
114        Self::Categorized(status, message, ErrorCategory::Common)
115    }
116
117    /// 创建自定义错误(向后兼容 API)
118    pub fn customer_error(status: i32, message: String) -> Self {
119        Self::Categorized(status, message, ErrorCategory::Custom)
120    }
121}
122
123impl IntoResponse for AppError {
124    fn into_response(self) -> Response {
125        let (status, error_message) = match self {
126            AppError::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
127            AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
128            AppError::Forbidden => (StatusCode::FORBIDDEN, self.to_string()),
129            AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
130            AppError::Config(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
131            AppError::ServiceUnavailable(msg) => {
132                tracing::warn!("服务不可用: {}", msg);
133                (StatusCode::SERVICE_UNAVAILABLE, format!("服务不可用: {}", msg))
134            }
135            #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
136            AppError::Database(e) => {
137                tracing::error!("数据库错误: {:?}", e);
138                (
139                    StatusCode::INTERNAL_SERVER_ERROR,
140                    format!("数据库错误: {}", e),
141                )
142            }
143            #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
144            AppError::DatabaseNotInitialized => {
145                tracing::error!("数据库连接池未初始化");
146                (
147                    StatusCode::INTERNAL_SERVER_ERROR,
148                    "数据库连接池未初始化".to_string(),
149                )
150            }
151            #[cfg(feature = "redis")]
152            AppError::Redis(e) => {
153                tracing::error!("Redis 错误: {:?}", e);
154                (
155                    StatusCode::INTERNAL_SERVER_ERROR,
156                    format!("Redis 错误: {}", e),
157                )
158            }
159            #[cfg(feature = "redis")]
160            AppError::RedisNotInitialized => {
161                tracing::error!("Redis 客户端未初始化");
162                (
163                    StatusCode::INTERNAL_SERVER_ERROR,
164                    "Redis 客户端未初始化".to_string(),
165                )
166            }
167            AppError::Internal(e) => {
168                tracing::error!("内部错误: {:?}", e);
169                (
170                    StatusCode::INTERNAL_SERVER_ERROR,
171                    "内部服务器错误".to_string(),
172                )
173            }
174            AppError::Categorized(status, msg, category) => {
175                let display_msg = format!("{}: [{}] {}", category, status, msg);
176                tracing::warn!("{}", display_msg);
177                (StatusCode::OK, display_msg)
178            }
179            AppError::Io(e) => {
180                tracing::error!("IO 错误: {:?}", e);
181                (StatusCode::INTERNAL_SERVER_ERROR, format!("IO 错误: {}", e))
182            }
183            AppError::Addr(e) => {
184                tracing::error!("地址解析错误: {:?}", e);
185                (StatusCode::BAD_REQUEST, format!("地址解析错误: {}", e))
186            }
187            #[cfg(feature = "redis")]
188            AppError::RedisPool(e) => {
189                tracing::error!("Redis 连接池错误: {:?}", e);
190                (
191                    StatusCode::INTERNAL_SERVER_ERROR,
192                    format!("Redis 连接池错误: {}", e),
193                )
194            }
195            #[cfg(feature = "kafka")]
196            AppError::Kafka(e) => {
197                tracing::error!("Kafka 错误: {:?}", e);
198                (
199                    StatusCode::INTERNAL_SERVER_ERROR,
200                    format!("Kafka 错误: {}", e),
201                )
202            }
203        };
204
205        let body = Json(json!({
206            "error": error_message,
207            "status": status.as_u16(),
208        }));
209
210        (status, body).into_response()
211    }
212}