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(Error, Debug)]
11pub enum AppError {
12    #[error("内部服务器错误: {0}")]
13    Internal(#[from] anyhow::Error),
14
15    #[error("未找到资源")]
16    NotFound,
17
18    #[error("未授权访问")]
19    Unauthorized,
20
21    #[error("禁止访问")]
22    Forbidden,
23
24    #[error("请求参数错误: {0}")]
25    BadRequest(String),
26
27    #[error("配置错误: {0}")]
28    Config(#[from] config::ConfigError),
29
30    /// 数据库错误(需要启用 mysql/postgres/sqlite 任一特性)
31    #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
32    #[error("数据库错误: {0}")]
33    Database(#[from] sqlx::Error),
34
35    /// 数据库连接池未初始化错误
36    #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
37    #[error("数据库连接池未初始化")]
38    DatabaseNotInitialized,
39
40    /// Redis 错误(需要启用 redis 特性)
41    #[cfg(feature = "redis")]
42    #[error("Redis 错误: {0}")]
43    Redis(#[from] redis::RedisError),
44
45    /// Redis 客户端未初始化错误
46    #[cfg(feature = "redis")]
47    #[error("Redis 客户端未初始化")]
48    RedisNotInitialized,
49
50    /// 业务错误
51    ///
52    /// # 参数
53    /// - `status`: HTTP 状态码
54    /// - `message`: 错误消息
55    #[error("业务错误: {1}")]
56    BizError(i32, String),
57
58    /// 通用错误
59    ///
60    /// # 参数
61    /// - `status`: HTTP 状态码
62    /// - `message`: 错误消息
63    #[error("通用错误: {1}")]
64    CommonError(i32, String),
65
66    /// 自定义错误
67    ///
68    /// # 参数
69    /// - `status`: HTTP 状态码
70    /// - `message`: 错误消息
71    #[error("自定义错误: {1}")]
72    CustomerError(i32, String),
73
74    /// IO 错误
75    #[error("IO 错误: {0}")]
76    Io(#[from] std::io::Error),
77
78    /// 地址解析错误
79    #[error("地址解析错误: {0}")]
80    Addr(#[from] std::net::AddrParseError),
81
82    /// Redis 连接池错误
83    #[cfg(feature = "redis")]
84    #[error("Redis 连接池错误: {0}")]
85    RedisPool(#[from] deadpool_redis::PoolError),
86
87    /// Kafka 错误
88    #[cfg(feature = "kafka")]
89    #[error("Kafka 错误: {0}")]
90    Kafka(#[from] rdkafka::error::KafkaError),
91}
92
93/// 应用结果类型
94pub type AppResult<T> = Result<T, AppError>;
95
96impl AppError {
97    /// 创建业务错误
98    ///
99    /// # 参数
100    /// - `status`: HTTP 状态码
101    /// - `message`: 错误消息
102    ///
103    /// # 示例
104    /// ```rust,no_run
105    /// use fbc_starter::error::{AppError, AppResult};
106    /// use axum::http::StatusCode;
107    ///
108    /// fn example() -> AppResult<()> {
109    ///     Err(AppError::biz_error(StatusCode::BAD_REQUEST, "业务逻辑错误".to_string()))
110    /// }
111    /// ```
112    pub fn biz_error(status: i32, message: String) -> Self {
113        Self::BizError(status, message)
114    }
115
116    /// 创建通用错误
117    ///
118    /// # 参数
119    /// - `status`: HTTP 状态码
120    /// - `message`: 错误消息
121    ///
122    /// # 示例
123    /// ```rust,no_run
124    /// use fbc_starter::error::{AppError, AppResult};
125    /// use axum::http::StatusCode;
126    ///
127    /// fn example() -> AppResult<()> {
128    ///     Err(AppError::common_error(StatusCode::INTERNAL_SERVER_ERROR, "通用错误".to_string()))
129    /// }
130    /// ```
131    pub fn common_error(status: i32, message: String) -> Self {
132        Self::CommonError(status, message)
133    }
134
135    /// 创建自定义错误
136    ///
137    /// # 参数
138    /// - `status`: HTTP 状态码
139    /// - `message`: 错误消息
140    ///
141    /// # 示例
142    /// ```rust,no_run
143    /// use fbc_starter::error::{AppError, AppResult};
144    /// use axum::http::StatusCode;
145    ///
146    /// fn example() -> AppResult<()> {
147    ///     Err(AppError::customer_error(StatusCode::FORBIDDEN, "自定义错误".to_string()))
148    /// }
149    /// ```
150    pub fn customer_error(status: i32, message: String) -> Self {
151        Self::CustomerError(status, message)
152    }
153}
154
155impl IntoResponse for AppError {
156    fn into_response(self) -> Response {
157        let (status, error_message) = match self {
158            AppError::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
159            AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
160            AppError::Forbidden => (StatusCode::FORBIDDEN, self.to_string()),
161            AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
162            AppError::Config(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
163            #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
164            AppError::Database(e) => {
165                tracing::error!("数据库错误: {:?}", e);
166                (
167                    StatusCode::INTERNAL_SERVER_ERROR,
168                    format!("数据库错误: {}", e),
169                )
170            }
171            #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
172            AppError::DatabaseNotInitialized => {
173                tracing::error!("数据库连接池未初始化");
174                (
175                    StatusCode::INTERNAL_SERVER_ERROR,
176                    "数据库连接池未初始化".to_string(),
177                )
178            }
179            #[cfg(feature = "redis")]
180            AppError::Redis(e) => {
181                tracing::error!("Redis 错误: {:?}", e);
182                (
183                    StatusCode::INTERNAL_SERVER_ERROR,
184                    format!("Redis 错误: {}", e),
185                )
186            }
187            #[cfg(feature = "redis")]
188            AppError::RedisNotInitialized => {
189                tracing::error!("Redis 客户端未初始化");
190                (
191                    StatusCode::INTERNAL_SERVER_ERROR,
192                    "Redis 客户端未初始化".to_string(),
193                )
194            }
195            AppError::Internal(e) => {
196                tracing::error!("内部错误: {:?}", e);
197                (
198                    StatusCode::INTERNAL_SERVER_ERROR,
199                    "内部服务器错误".to_string(),
200                )
201            }
202            AppError::BizError(status, msg) => {
203                let msg = format!("业务错误: [{}] {}", status, msg);
204                tracing::warn!(msg);
205                (StatusCode::OK, msg)
206            }
207            AppError::CommonError(status, msg) => {
208                let msg = format!("通用错误: [{}] {}", status, msg);
209                tracing::warn!(msg);
210                (StatusCode::OK, msg)
211            }
212            AppError::CustomerError(status, msg) => {
213                let msg = format!("自定义错误: [{}] {}", status, msg);
214                tracing::warn!(msg);
215                (StatusCode::OK, msg)
216            }
217            AppError::Io(e) => {
218                tracing::error!("IO 错误: {:?}", e);
219                (StatusCode::INTERNAL_SERVER_ERROR, format!("IO 错误: {}", e))
220            }
221            AppError::Addr(e) => {
222                tracing::error!("地址解析错误: {:?}", e);
223                (StatusCode::BAD_REQUEST, format!("地址解析错误: {}", e))
224            }
225            #[cfg(feature = "redis")]
226            AppError::RedisPool(e) => {
227                tracing::error!("Redis 连接池错误: {:?}", e);
228                (
229                    StatusCode::INTERNAL_SERVER_ERROR,
230                    format!("Redis 连接池错误: {}", e),
231                )
232            }
233            #[cfg(feature = "kafka")]
234            AppError::Kafka(e) => {
235                tracing::error!("Kafka 错误: {:?}", e);
236                (
237                    StatusCode::INTERNAL_SERVER_ERROR,
238                    format!("Kafka 错误: {}", e),
239                )
240            }
241        };
242
243        let body = Json(json!({
244            "error": error_message,
245            "status": status.as_u16(),
246        }));
247
248        (status, body).into_response()
249    }
250}