78 lines
2.4 KiB
Rust
78 lines
2.4 KiB
Rust
use actix_web::{HttpResponse, ResponseError};
|
||
use serde::Serialize;
|
||
use thiserror::Error;
|
||
|
||
/// 统一错误响应体(不泄露内部细节)
|
||
#[derive(Serialize)]
|
||
pub struct ErrorResponse {
|
||
pub code: u32,
|
||
pub message: String,
|
||
}
|
||
|
||
/// 应用错误类型
|
||
#[derive(Debug, Error)]
|
||
pub enum AppError {
|
||
#[error("参数验证失败: {0}")]
|
||
Validation(String),
|
||
|
||
#[error("未授权访问")]
|
||
Unauthorized,
|
||
|
||
#[error("资源不存在")]
|
||
NotFound,
|
||
|
||
#[error("钱包已存在")]
|
||
WalletAlreadyExists,
|
||
|
||
#[error("余额不足")]
|
||
InsufficientBalance,
|
||
|
||
#[error("手续费不足")]
|
||
InsufficientFee,
|
||
|
||
#[error("内部服务错误")]
|
||
Internal,
|
||
|
||
#[error("数据库错误")]
|
||
Database,
|
||
}
|
||
|
||
impl ResponseError for AppError {
|
||
fn error_response(&self) -> HttpResponse {
|
||
// 注意:所有错误响应都使用通用描述,不泄露内部实现细节
|
||
match self {
|
||
AppError::Validation(msg) => HttpResponse::BadRequest().json(ErrorResponse {
|
||
code: 4001,
|
||
message: msg.clone(),
|
||
}),
|
||
AppError::Unauthorized => HttpResponse::Unauthorized().json(ErrorResponse {
|
||
code: 4010,
|
||
message: "未授权访问".to_string(),
|
||
}),
|
||
AppError::NotFound => HttpResponse::NotFound().json(ErrorResponse {
|
||
code: 4040,
|
||
message: "资源不存在".to_string(),
|
||
}),
|
||
AppError::WalletAlreadyExists => HttpResponse::Conflict().json(ErrorResponse {
|
||
code: 4091,
|
||
message: "该用户钱包已存在".to_string(),
|
||
}),
|
||
AppError::InsufficientBalance => HttpResponse::BadRequest().json(ErrorResponse {
|
||
code: 4002,
|
||
message: "余额不足".to_string(),
|
||
}),
|
||
AppError::InsufficientFee => HttpResponse::BadRequest().json(ErrorResponse {
|
||
code: 4003,
|
||
message: "XIC手续费余额不足".to_string(),
|
||
}),
|
||
// 内部错误统一返回500,不暴露细节
|
||
AppError::Internal | AppError::Database => {
|
||
HttpResponse::InternalServerError().json(ErrorResponse {
|
||
code: 5000,
|
||
message: "服务暂时不可用,请稍后重试".to_string(),
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|