From e672f00b66acc8a927c4208b6c3cb4ebb65e7023 Mon Sep 17 00:00:00 2001 From: milky0217 Date: Wed, 10 Jun 2026 14:11:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=AF=E4=BB=98=E7=A1=AE=E8=AE=A4TOCT?= =?UTF-8?q?OU=E7=AB=9E=E6=80=81(AND=20status=3Dpending)+=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E5=99=A8=E8=A1=A5=E5=85=85=E8=BF=87=E6=9C=9F=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db.rs | 15 +++++++++++---- src/main.rs | 3 +++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/db.rs b/src/db.rs index ddb7ab9..04cc370 100644 --- a/src/db.rs +++ b/src/db.rs @@ -585,7 +585,7 @@ pub async fn confirm_payment_order( let new_expires: Option> = sqlx::query_scalar( r#" WITH updated_order AS ( - UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 RETURNING package_type + UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 AND status = 'pending' RETURNING package_type ) UPDATE users SET is_member = true, @@ -658,10 +658,11 @@ pub async fn confirm_payment_order_by_orderno( .flatten(); // 一次性完成:更新订单状态 + 累加计算新的到期时间 - sqlx::query( + // 加上 AND status = 'pending' 防止竞态覆盖已取消/退款的订单 + let result = sqlx::query( r#" WITH updated_order AS ( - UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 RETURNING package_type, user_id + UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 AND status = 'pending' RETURNING package_type, user_id ) UPDATE users SET is_member = true, @@ -686,6 +687,12 @@ pub async fn confirm_payment_order_by_orderno( .await .map_err(|e| AppError::Database(format!("支付确认失败: {}", e)))?; + // 0 行 update 说明订单已被取消/退款/已确认(竞态保护生效) + if result.rows_affected() == 0 { + tracing::warn!("支付确认: 订单 {} 状态已变更,跳过处理", order_no); + return Ok(()); + } + // 审计日志 if let Some(uid) = user_id { let _ = insert_payment_audit_log(pool, order_no, uid, "paid", None, None).await; @@ -1019,7 +1026,7 @@ pub async fn admin_force_confirm_order( let result = sqlx::query_scalar::<_, Option>>( r#" WITH updated_order AS ( - UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 RETURNING package_type + UPDATE payment_orders SET status = 'paid', paid_at = NOW() WHERE order_no = $1 AND status = 'pending' RETURNING package_type ) UPDATE users SET is_member = true, diff --git a/src/main.rs b/src/main.rs index f1e8bd0..4cc8ea0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -275,6 +275,9 @@ async fn main() -> std::io::Result<()> { // 会员到期前 7 天提醒 let _ = db::check_member_expiry_soon(&pool_clone).await; + // 清理超过 24 小时的过期待支付订单 + let _ = db::cleanup_expired_pending_orders(&pool_clone, None).await; + // 清理过期的 refresh_token let _ = db::cleanup_expired_refresh_tokens(&pool_clone).await; }