feat: 添加PAYMENT_MAINTENANCE_MODE维护模式 + ServiceUnavailable错误
This commit is contained in:
@@ -9,6 +9,9 @@ use crate::error::AppError;
|
|||||||
|
|
||||||
// 用于插入weather_data的数据
|
// 用于插入weather_data的数据
|
||||||
pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user_id: i32) -> Result<i32, AppError> {
|
pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user_id: i32) -> Result<i32, AppError> {
|
||||||
|
// 维护模式:跳过所有配额检查
|
||||||
|
let is_maintenance = std::env::var("PAYMENT_MAINTENANCE_MODE").ok() == Some("true".to_string());
|
||||||
|
if !is_maintenance {
|
||||||
// 配额检查:非付费用户数据条数限制
|
// 配额检查:非付费用户数据条数限制
|
||||||
let user = get_user_by_id(pool, user_id).await?;
|
let user = get_user_by_id(pool, user_id).await?;
|
||||||
let is_paid_active = user.is_paid && user.paid_expires_at.map_or(true, |expires| expires > Utc::now());
|
let is_paid_active = user.is_paid && user.paid_expires_at.map_or(true, |expires| expires > Utc::now());
|
||||||
@@ -24,6 +27,7 @@ pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user
|
|||||||
return Err(AppError::Forbidden("数据条数已达上限,请升级为付费用户".to_string()));
|
return Err(AppError::Forbidden("数据条数已达上限,请升级为付费用户".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 准备插入数据的 SQL 语句
|
// 准备插入数据的 SQL 语句
|
||||||
let insert_query = r#"
|
let insert_query = r#"
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ pub enum AppError {
|
|||||||
Internal(String),
|
Internal(String),
|
||||||
Database(String),
|
Database(String),
|
||||||
TooManyRequests(String),
|
TooManyRequests(String),
|
||||||
|
ServiceUnavailable(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for AppError {
|
impl fmt::Display for AppError {
|
||||||
@@ -65,6 +66,7 @@ impl fmt::Display for AppError {
|
|||||||
AppError::Internal(msg) => write!(f, "{}", msg),
|
AppError::Internal(msg) => write!(f, "{}", msg),
|
||||||
AppError::Database(msg) => write!(f, "{}", msg),
|
AppError::Database(msg) => write!(f, "{}", msg),
|
||||||
AppError::TooManyRequests(msg) => write!(f, "{}", msg),
|
AppError::TooManyRequests(msg) => write!(f, "{}", msg),
|
||||||
|
AppError::ServiceUnavailable(msg) => write!(f, "{}", msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,6 +80,7 @@ impl ResponseError for AppError {
|
|||||||
AppError::BadRequest(_) => StatusCode::BAD_REQUEST,
|
AppError::BadRequest(_) => StatusCode::BAD_REQUEST,
|
||||||
AppError::Internal(_) | AppError::Database(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
AppError::Internal(_) | AppError::Database(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
AppError::TooManyRequests(_) => StatusCode::TOO_MANY_REQUESTS,
|
AppError::TooManyRequests(_) => StatusCode::TOO_MANY_REQUESTS,
|
||||||
|
AppError::ServiceUnavailable(_) => StatusCode::SERVICE_UNAVAILABLE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +172,7 @@ impl ResponseError for AppError {
|
|||||||
AppError::BadRequest(_) => "请求无效",
|
AppError::BadRequest(_) => "请求无效",
|
||||||
AppError::Internal(_) | AppError::Database(_) => "服务器错误",
|
AppError::Internal(_) | AppError::Database(_) => "服务器错误",
|
||||||
AppError::TooManyRequests(_) => "请求过于频繁",
|
AppError::TooManyRequests(_) => "请求过于频繁",
|
||||||
|
AppError::ServiceUnavailable(_) => "服务暂不可用",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,16 @@ fn build_alipay_form_html(
|
|||||||
|
|
||||||
// ===== 提取 JWT token =====
|
// ===== 提取 JWT token =====
|
||||||
|
|
||||||
|
/// 维护模式守卫:PAYMENT_MAINTENANCE_MODE=true 时所有支付接口返回 503
|
||||||
|
fn check_payment_maintenance() -> Result<(), AppError> {
|
||||||
|
if std::env::var("PAYMENT_MAINTENANCE_MODE").ok() == Some("true".to_string()) {
|
||||||
|
return Err(AppError::ServiceUnavailable(
|
||||||
|
"支付系统维护中,请稍后再试".to_string()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_token(req: &HttpRequest) -> Option<String> {
|
fn extract_token(req: &HttpRequest) -> Option<String> {
|
||||||
req.headers()
|
req.headers()
|
||||||
.get("Authorization")?
|
.get("Authorization")?
|
||||||
@@ -272,6 +282,7 @@ fn get_jwt_secret() -> String {
|
|||||||
|
|
||||||
#[get("/payment")]
|
#[get("/payment")]
|
||||||
pub async fn payment_index() -> Result<HttpResponse, AppError> {
|
pub async fn payment_index() -> Result<HttpResponse, AppError> {
|
||||||
|
check_payment_maintenance()?;
|
||||||
let html = r##"<!DOCTYPE html>
|
let html = r##"<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
@@ -594,6 +605,7 @@ pub async fn payment_page(
|
|||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
query: web::Query<PaymentPageQuery>,
|
query: web::Query<PaymentPageQuery>,
|
||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
check_payment_maintenance()?;
|
||||||
let token = extract_token(&req)
|
let token = extract_token(&req)
|
||||||
.or_else(|| query.jwt.clone())
|
.or_else(|| query.jwt.clone())
|
||||||
.ok_or_else(|| AppError::Unauthorized("未登录".to_string()))?;
|
.ok_or_else(|| AppError::Unauthorized("未登录".to_string()))?;
|
||||||
@@ -665,6 +677,7 @@ pub async fn alipay_pay_page(
|
|||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
query: web::Query<AlipayPayQuery>,
|
query: web::Query<AlipayPayQuery>,
|
||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
check_payment_maintenance()?;
|
||||||
let token = extract_token(&req).ok_or_else(|| AppError::Unauthorized("未登录".to_string()))?;
|
let token = extract_token(&req).ok_or_else(|| AppError::Unauthorized("未登录".to_string()))?;
|
||||||
crate::auth::verify_token(&token, &get_jwt_secret())
|
crate::auth::verify_token(&token, &get_jwt_secret())
|
||||||
.map_err(|_| AppError::Unauthorized("Token 无效".to_string()))?;
|
.map_err(|_| AppError::Unauthorized("Token 无效".to_string()))?;
|
||||||
@@ -908,6 +921,7 @@ pub async fn create_order(
|
|||||||
claims: web::ReqData<Claims>,
|
claims: web::ReqData<Claims>,
|
||||||
body: web::Json<CreateOrderRequest>,
|
body: web::Json<CreateOrderRequest>,
|
||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
check_payment_maintenance()?;
|
||||||
let user_id = claims.user_id;
|
let user_id = claims.user_id;
|
||||||
|
|
||||||
let pkg = match get_package_info(&body.package_type) {
|
let pkg = match get_package_info(&body.package_type) {
|
||||||
@@ -950,6 +964,7 @@ pub async fn mock_confirm(
|
|||||||
claims: web::ReqData<Claims>,
|
claims: web::ReqData<Claims>,
|
||||||
body: web::Json<MockConfirmRequest>,
|
body: web::Json<MockConfirmRequest>,
|
||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
check_payment_maintenance()?;
|
||||||
let user_id = claims.user_id;
|
let user_id = claims.user_id;
|
||||||
|
|
||||||
let expires_at =
|
let expires_at =
|
||||||
@@ -973,6 +988,7 @@ pub async fn sync_order(
|
|||||||
claims: web::ReqData<Claims>,
|
claims: web::ReqData<Claims>,
|
||||||
body: web::Json<MockConfirmRequest>,
|
body: web::Json<MockConfirmRequest>,
|
||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
check_payment_maintenance()?;
|
||||||
let user_id = claims.user_id;
|
let user_id = claims.user_id;
|
||||||
|
|
||||||
// 尝试确认订单(幂等),失败时记录日志
|
// 尝试确认订单(幂等),失败时记录日志
|
||||||
@@ -1015,6 +1031,7 @@ pub async fn cancel_order(
|
|||||||
claims: web::ReqData<Claims>,
|
claims: web::ReqData<Claims>,
|
||||||
body: web::Json<CancelOrderRequest>,
|
body: web::Json<CancelOrderRequest>,
|
||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
check_payment_maintenance()?;
|
||||||
db::cancel_payment_order(pool.get_ref(), &body.order_id, claims.user_id).await?;
|
db::cancel_payment_order(pool.get_ref(), &body.order_id, claims.user_id).await?;
|
||||||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -1052,6 +1069,8 @@ pub async fn get_user_quota(
|
|||||||
) -> Result<HttpResponse, AppError> {
|
) -> Result<HttpResponse, AppError> {
|
||||||
let user_id = claims.user_id;
|
let user_id = claims.user_id;
|
||||||
|
|
||||||
|
let is_maintenance = std::env::var("PAYMENT_MAINTENANCE_MODE").ok() == Some("true".to_string());
|
||||||
|
|
||||||
let (used, is_paid_active, paid_expires_at) =
|
let (used, is_paid_active, paid_expires_at) =
|
||||||
db::get_user_quota(pool.get_ref(), user_id).await?;
|
db::get_user_quota(pool.get_ref(), user_id).await?;
|
||||||
let limit: i64 = std::env::var("FREE_USER_DATA_LIMIT")
|
let limit: i64 = std::env::var("FREE_USER_DATA_LIMIT")
|
||||||
@@ -1059,14 +1078,21 @@ pub async fn get_user_quota(
|
|||||||
.and_then(|v| v.parse().ok())
|
.and_then(|v| v.parse().ok())
|
||||||
.unwrap_or(20);
|
.unwrap_or(20);
|
||||||
|
|
||||||
|
let (unlimited, active) = if is_maintenance {
|
||||||
|
(true, true)
|
||||||
|
} else {
|
||||||
|
(is_paid_active, is_paid_active)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
"data": {
|
"data": {
|
||||||
"used": used,
|
"used": used,
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
"unlimited": is_paid_active,
|
"unlimited": unlimited,
|
||||||
"is_paid_active": is_paid_active,
|
"is_paid_active": active,
|
||||||
"paid_expires_at": paid_expires_at,
|
"paid_expires_at": paid_expires_at,
|
||||||
|
"maintenance_mode": is_maintenance,
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,13 @@ pub async fn get_current_user_profile(
|
|||||||
info!("获取当前用户信息, 用户ID: {}", user_id);
|
info!("获取当前用户信息, 用户ID: {}", user_id);
|
||||||
|
|
||||||
let user = db::get_user_by_id(pool.get_ref(), user_id).await?;
|
let user = db::get_user_by_id(pool.get_ref(), user_id).await?;
|
||||||
let is_paid_active = user.is_paid &&
|
let is_maintenance = std::env::var("PAYMENT_MAINTENANCE_MODE").ok() == Some("true".to_string());
|
||||||
user.paid_expires_at.map_or(true, |expires| expires > chrono::Utc::now());
|
let is_paid_active = if is_maintenance {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
user.is_paid &&
|
||||||
|
user.paid_expires_at.map_or(true, |expires| expires > chrono::Utc::now())
|
||||||
|
};
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user