fix: 支付后增加会员时长验证(verify_membership_after_payment)防止ELSE静默错误
This commit is contained in:
77
src/db.rs
77
src/db.rs
@@ -615,6 +615,12 @@ pub async fn confirm_payment_order(
|
|||||||
// 发送支付成功通知
|
// 发送支付成功通知
|
||||||
let _ = insert_payment_notification(pool, user_id, order_no).await;
|
let _ = insert_payment_notification(pool, user_id, order_no).await;
|
||||||
|
|
||||||
|
// 验证会员到期时间是否正常
|
||||||
|
if let Err(e) = verify_membership_after_payment(pool, order_no).await {
|
||||||
|
tracing::warn!("会员到期时间验证异常 (订单 {}): {}", order_no, e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Ok(new_expires)
|
Ok(new_expires)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,6 +691,11 @@ pub async fn confirm_payment_order_by_orderno(
|
|||||||
let _ = insert_payment_audit_log(pool, order_no, uid, "paid", None, None).await;
|
let _ = insert_payment_audit_log(pool, order_no, uid, "paid", None, None).await;
|
||||||
// 发送支付成功通知
|
// 发送支付成功通知
|
||||||
let _ = insert_payment_notification(pool, uid, order_no).await;
|
let _ = insert_payment_notification(pool, uid, order_no).await;
|
||||||
|
|
||||||
|
// 验证会员到期时间是否正常
|
||||||
|
if let Err(e) = verify_membership_after_payment(pool, order_no).await {
|
||||||
|
tracing::warn!("会员到期时间验证异常 (订单 {}): {}", order_no, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1046,6 +1057,11 @@ pub async fn admin_force_confirm_order(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
// 验证会员到期时间是否正常
|
||||||
|
if let Err(e) = verify_membership_after_payment(pool, order_no).await {
|
||||||
|
tracing::warn!("会员到期时间验证异常 (订单 {}): {}", order_no, e);
|
||||||
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
// 订单已处理,只写日志
|
// 订单已处理,只写日志
|
||||||
@@ -1185,3 +1201,64 @@ pub async fn admin_refund_order(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 支付确认后验证会员到期时间是否正常
|
||||||
|
/// 用于捕获 ELSE INTERVAL '0 days' 的静默错误
|
||||||
|
async fn verify_membership_after_payment(
|
||||||
|
pool: &PgPool,
|
||||||
|
order_no: &str,
|
||||||
|
) -> Result<(), AppError> {
|
||||||
|
#[derive(Debug, sqlx::FromRow)]
|
||||||
|
struct PaymentResult {
|
||||||
|
package_type: String,
|
||||||
|
paid_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
membership_expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = sqlx::query_as::<_, PaymentResult>(
|
||||||
|
r#"
|
||||||
|
SELECT po.package_type, po.paid_at, u.membership_expires_at
|
||||||
|
FROM payment_orders po
|
||||||
|
JOIN users u ON u.id = po.user_id
|
||||||
|
WHERE po.order_no = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(order_no)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| AppError::Database(format!("验证查询失败: {}", e)))?;
|
||||||
|
|
||||||
|
let r = match row {
|
||||||
|
Some(r) => r,
|
||||||
|
None => return Err(AppError::NotFound("验证查询:订单不存在".to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
if r.package_type == "permanent" {
|
||||||
|
return Ok(()); // 永久会员不累计
|
||||||
|
}
|
||||||
|
|
||||||
|
let expires = match r.membership_expires_at {
|
||||||
|
Some(e) => e,
|
||||||
|
None => {
|
||||||
|
let msg = format!("支付验证失败:{} 非永久套餐但会员到期为 NULL", r.package_type);
|
||||||
|
tracing::error!("{}", msg);
|
||||||
|
return Err(AppError::Internal(msg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let actual_days = (expires - r.paid_at).num_days();
|
||||||
|
|
||||||
|
// 预期至少 25 天(最短月卡 30 天,允许 5 天误差)
|
||||||
|
if actual_days < 25 {
|
||||||
|
let msg = format!(
|
||||||
|
"支付验证失败:套餐 {} 仅增加 {} 天会员时长(预期 >= 25 天),可能触发了 ELSE 分支",
|
||||||
|
r.package_type, actual_days
|
||||||
|
);
|
||||||
|
tracing::error!("{}", msg);
|
||||||
|
return Err(AppError::Internal(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!("支付验证通过:{} 增加 {} 天", r.package_type, actual_days);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user