1use axum::{
2 http::StatusCode,
3 response::{IntoResponse, Response},
4 Json,
5};
6use serde_json::json;
7use thiserror::Error;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ErrorCategory {
12 Biz,
14 Common,
16 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#[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 #[error("服务不可用: {0}")]
53 ServiceUnavailable(String),
54
55 #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
57 #[error("数据库错误: {0}")]
58 Database(#[from] sqlx::Error),
59
60 #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
62 #[error("数据库连接池未初始化")]
63 DatabaseNotInitialized,
64
65 #[cfg(feature = "redis")]
67 #[error("Redis 错误: {0}")]
68 Redis(#[from] redis::RedisError),
69
70 #[cfg(feature = "redis")]
72 #[error("Redis 客户端未初始化")]
73 RedisNotInitialized,
74
75 #[error("{2}: [{0}] {1}")]
82 Categorized(i32, String, ErrorCategory),
83
84 #[error("IO 错误: {0}")]
86 Io(#[from] std::io::Error),
87
88 #[error("地址解析错误: {0}")]
90 Addr(#[from] std::net::AddrParseError),
91
92 #[cfg(feature = "redis")]
94 #[error("Redis 连接池错误: {0}")]
95 RedisPool(#[from] deadpool_redis::PoolError),
96
97 #[cfg(feature = "kafka")]
99 #[error("Kafka 错误: {0}")]
100 Kafka(#[from] rdkafka::error::KafkaError),
101}
102
103pub type AppResult<T> = Result<T, AppError>;
105
106impl AppError {
107 pub fn biz_error(status: i32, message: String) -> Self {
109 Self::Categorized(status, message, ErrorCategory::Biz)
110 }
111
112 pub fn common_error(status: i32, message: String) -> Self {
114 Self::Categorized(status, message, ErrorCategory::Common)
115 }
116
117 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}