fix: 添加后台定时重试 + 管理员强制确认 + 审计日志 + mock守卫
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use actix_web::{web, get, put, HttpResponse};
|
||||
use actix_web::{web, get, post, put, HttpResponse};
|
||||
use sqlx::postgres::PgPool;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
@@ -58,8 +58,42 @@ pub async fn admin_update_user_payment(
|
||||
|
||||
// 更新用户付费状态
|
||||
db::update_user_payment_status(pool.get_ref(), target_user_id, body.is_paid, paid_expires_at).await?;
|
||||
// 审计日志
|
||||
let _ = db::insert_payment_audit_log(
|
||||
pool.get_ref(), "ADMIN_MANUAL", target_user_id, "admin_revoke",
|
||||
Some(claims.user_id),
|
||||
Some(&format!("管理员手动更新付费状态: is_paid={}, expires_at={:?}", body.is_paid, body.paid_expires_at)),
|
||||
).await;
|
||||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||
"success": true,
|
||||
"message": "用户付费状态已更新"
|
||||
})))
|
||||
}
|
||||
|
||||
/// POST /api/admin/orders/{order_no}/force-confirm
|
||||
/// 管理员强制确认指定订单(用于回调失败后的手动恢复)
|
||||
#[post("/api/admin/orders/{order_no}/force-confirm")]
|
||||
pub async fn admin_force_confirm_order(
|
||||
path: web::Path<String>,
|
||||
pool: web::Data<PgPool>,
|
||||
claims: web::ReqData<Claims>,
|
||||
) -> Result<HttpResponse, AppError> {
|
||||
let order_no = path.into_inner();
|
||||
tracing::info!("管理员强制确认订单: {}", order_no);
|
||||
|
||||
// 验证当前用户是否为管理员
|
||||
let current_user = db::get_user_by_id(pool.get_ref(), claims.user_id).await?;
|
||||
if !current_user.is_admin {
|
||||
return Err(AppError::Forbidden("无权限执行此操作".to_string()));
|
||||
}
|
||||
|
||||
let new_expires = db::admin_force_confirm_order(pool.get_ref(), &order_no, claims.user_id).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||||
"success": true,
|
||||
"message": "订单已确认",
|
||||
"data": {
|
||||
"new_expires_at": new_expires,
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -590,13 +590,16 @@ pub async fn web_login_confirm(
|
||||
// 查询付费状态
|
||||
let (is_paid_active, paid_expires_at): (bool, Option<String>) =
|
||||
match sqlx::query_as::<_, (bool, Option<chrono::DateTime<chrono::Utc>>)>(
|
||||
"SELECT is_paid_active($1)",
|
||||
"SELECT is_paid, paid_expires_at FROM users WHERE id = $1",
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_optional(pool.get_ref())
|
||||
.await
|
||||
{
|
||||
Ok(Some((active, expires))) => (active, expires.map(|e| e.to_rfc3339())),
|
||||
Ok(Some((is_paid, expires))) => {
|
||||
let active = is_paid && expires.map_or(true, |e| e > Utc::now());
|
||||
(active, expires.map(|e| e.to_rfc3339()))
|
||||
}
|
||||
_ => (false, None),
|
||||
};
|
||||
|
||||
@@ -642,7 +645,9 @@ pub async fn web_login_auto_confirm(
|
||||
) -> impl Responder {
|
||||
let code = req.code.clone();
|
||||
|
||||
let openid = if code.starts_with("mock_") || code == "test_mock" {
|
||||
let openid = if (code.starts_with("mock_") || code == "test_mock")
|
||||
&& std::env::var("MOCK_LOGIN_ENABLED").ok() == Some("true".to_string())
|
||||
{
|
||||
format!("mock_openid_{}", Utc::now().timestamp_millis())
|
||||
} else {
|
||||
let url = format!(
|
||||
|
||||
@@ -18,6 +18,7 @@ pub static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates");
|
||||
// re-export handlers for convenient use in main.rs
|
||||
pub use admin::admin_get_user;
|
||||
pub use admin::admin_update_user_payment;
|
||||
pub use admin::admin_force_confirm_order;
|
||||
pub use auth::login;
|
||||
pub use auth::mock_login;
|
||||
pub use auth::refresh_token;
|
||||
|
||||
@@ -1173,12 +1173,17 @@ pub async fn payment_login_status(
|
||||
let user_id = user_id.unwrap();
|
||||
|
||||
let (is_paid_active, paid_expires_at): (bool, Option<String>) =
|
||||
match sqlx::query_as::<_, (bool, Option<chrono::DateTime<chrono::Utc>>)>("SELECT is_paid_active($1)")
|
||||
match sqlx::query_as::<_, (bool, Option<chrono::DateTime<chrono::Utc>>)>(
|
||||
"SELECT is_paid, paid_expires_at FROM users WHERE id = $1"
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_optional(pool.get_ref())
|
||||
.await
|
||||
{
|
||||
Ok(Some((active, expires))) => (active, expires.map(|e| e.to_rfc3339())),
|
||||
Ok(Some((is_paid, expires))) => {
|
||||
let active = is_paid && expires.map_or(true, |e| e > Utc::now());
|
||||
(active, expires.map(|e| e.to_rfc3339()))
|
||||
}
|
||||
_ => (false, None),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user