1use axum::{
2 http::StatusCode,
3 response::{IntoResponse, Response},
4 Json,
5};
6use serde_json::json;
7use thiserror::Error;
8
9#[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 #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
32 #[error("数据库错误: {0}")]
33 Database(#[from] sqlx::Error),
34
35 #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
37 #[error("数据库连接池未初始化")]
38 DatabaseNotInitialized,
39
40 #[cfg(feature = "redis")]
42 #[error("Redis 错误: {0}")]
43 Redis(#[from] redis::RedisError),
44
45 #[cfg(feature = "redis")]
47 #[error("Redis 客户端未初始化")]
48 RedisNotInitialized,
49
50 #[error("业务错误: {1}")]
56 BizError(StatusCode, String),
57
58 #[error("通用错误: {1}")]
64 CommonError(StatusCode, String),
65
66 #[error("自定义错误: {1}")]
72 CustomerError(StatusCode, String),
73}
74
75pub type AppResult<T> = Result<T, AppError>;
77
78impl AppError {
79 pub fn biz_error(status: StatusCode, message: String) -> Self {
95 Self::BizError(status, message)
96 }
97
98 pub fn common_error(status: StatusCode, message: String) -> Self {
114 Self::CommonError(status, message)
115 }
116
117 pub fn customer_error(status: StatusCode, message: String) -> Self {
133 Self::CustomerError(status, message)
134 }
135}
136
137impl IntoResponse for AppError {
138 fn into_response(self) -> Response {
139 let (status, error_message) = match self {
140 AppError::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
141 AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
142 AppError::Forbidden => (StatusCode::FORBIDDEN, self.to_string()),
143 AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
144 AppError::Config(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
145 #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
146 AppError::Database(e) => {
147 tracing::error!("数据库错误: {:?}", e);
148 (
149 StatusCode::INTERNAL_SERVER_ERROR,
150 format!("数据库错误: {}", e),
151 )
152 }
153 #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
154 AppError::DatabaseNotInitialized => {
155 tracing::error!("数据库连接池未初始化");
156 (
157 StatusCode::INTERNAL_SERVER_ERROR,
158 "数据库连接池未初始化".to_string(),
159 )
160 }
161 #[cfg(feature = "redis")]
162 AppError::Redis(e) => {
163 tracing::error!("Redis 错误: {:?}", e);
164 (
165 StatusCode::INTERNAL_SERVER_ERROR,
166 format!("Redis 错误: {}", e),
167 )
168 }
169 #[cfg(feature = "redis")]
170 AppError::RedisNotInitialized => {
171 tracing::error!("Redis 客户端未初始化");
172 (
173 StatusCode::INTERNAL_SERVER_ERROR,
174 "Redis 客户端未初始化".to_string(),
175 )
176 }
177 AppError::Internal(e) => {
178 tracing::error!("内部错误: {:?}", e);
179 (
180 StatusCode::INTERNAL_SERVER_ERROR,
181 "内部服务器错误".to_string(),
182 )
183 }
184 AppError::BizError(status, msg) => {
185 tracing::warn!("业务错误: {} (状态码: {})", msg, status);
186 (status, msg)
187 }
188 AppError::CommonError(status, msg) => {
189 tracing::warn!("通用错误: {} (状态码: {})", msg, status);
190 (status, msg)
191 }
192 AppError::CustomerError(status, msg) => {
193 tracing::warn!("自定义错误: {} (状态码: {})", msg, status);
194 (status, msg)
195 }
196 };
197
198 let body = Json(json!({
199 "error": error_message,
200 "status": status.as_u16(),
201 }));
202
203 (status, body).into_response()
204 }
205}