mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 10:27:46 +08:00
fix(admin): floor batch deductions at zero
This commit is contained in:
@@ -226,7 +226,11 @@ impl AppState {
|
|||||||
let before_gift = wallet.gift_balance;
|
let before_gift = wallet.gift_balance;
|
||||||
let before_total = before_recharge + before_gift;
|
let before_total = before_recharge + before_gift;
|
||||||
let amount_usd = if clamp_deduction_to_available_balance && amount_usd < 0.0 {
|
let amount_usd = if clamp_deduction_to_available_balance && amount_usd < 0.0 {
|
||||||
-(-amount_usd).min(before_total.max(0.0))
|
if before_total < 0.0 {
|
||||||
|
-before_total
|
||||||
|
} else {
|
||||||
|
-(-amount_usd).min(before_total)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
amount_usd
|
amount_usd
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ async fn gateway_reports_wallet_limit_lookup_failure_as_unprocessed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn gateway_reports_missing_wallet_and_skips_zero_delta_for_non_positive_balance() {
|
async fn gateway_reports_missing_wallet_and_floors_negative_balance_on_deduction() {
|
||||||
let state = AppState::new()
|
let state = AppState::new()
|
||||||
.expect("gateway should build")
|
.expect("gateway should build")
|
||||||
.with_auth_users_for_tests([sample_user("user-negative"), sample_user("user-no-wallet")])
|
.with_auth_users_for_tests([sample_user("user-negative"), sample_user("user-no-wallet")])
|
||||||
@@ -607,8 +607,8 @@ async fn gateway_reports_missing_wallet_and_skips_zero_delta_for_non_positive_ba
|
|||||||
assert_eq!(result["failures"][0]["reason"], "用户钱包不可用");
|
assert_eq!(result["failures"][0]["reason"], "用户钱包不可用");
|
||||||
|
|
||||||
let wallet = wallet_detail(&client, &gateway_url, "user-negative").await;
|
let wallet = wallet_detail(&client, &gateway_url, "user-negative").await;
|
||||||
assert_eq!(wallet["balance"], -1.0);
|
assert_eq!(wallet["balance"], 0.0);
|
||||||
assert_eq!(wallet["total_adjusted"], 0.0);
|
assert_eq!(wallet["total_adjusted"], 1.0);
|
||||||
|
|
||||||
gateway_handle.abort();
|
gateway_handle.abort();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -812,7 +812,12 @@ impl SqlxWalletRepository {
|
|||||||
|
|
||||||
fn effective_wallet_adjustment_amount(input: &AdjustWalletBalanceInput, before_total: f64) -> f64 {
|
fn effective_wallet_adjustment_amount(input: &AdjustWalletBalanceInput, before_total: f64) -> f64 {
|
||||||
if input.clamp_deduction_to_available_balance && input.amount_usd < 0.0 {
|
if input.clamp_deduction_to_available_balance && input.amount_usd < 0.0 {
|
||||||
-(-input.amount_usd).min(before_total.max(0.0))
|
if before_total < 0.0 {
|
||||||
|
// Clear legacy negative totals to zero and record the actual ledger delta.
|
||||||
|
-before_total
|
||||||
|
} else {
|
||||||
|
-(-input.amount_usd).min(before_total)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
input.amount_usd
|
input.amount_usd
|
||||||
}
|
}
|
||||||
@@ -9598,7 +9603,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(effective_wallet_adjustment_amount(&input, 13.0), -13.0);
|
assert_eq!(effective_wallet_adjustment_amount(&input, 13.0), -13.0);
|
||||||
assert_eq!(effective_wallet_adjustment_amount(&input, 0.0), -0.0);
|
assert_eq!(effective_wallet_adjustment_amount(&input, 0.0), -0.0);
|
||||||
assert_eq!(effective_wallet_adjustment_amount(&input, -1.0), -0.0);
|
assert_eq!(effective_wallet_adjustment_amount(&input, -1.0), 1.0);
|
||||||
|
|
||||||
let legacy_input = AdjustWalletBalanceInput {
|
let legacy_input = AdjustWalletBalanceInput {
|
||||||
clamp_deduction_to_available_balance: false,
|
clamp_deduction_to_available_balance: false,
|
||||||
@@ -9681,22 +9686,25 @@ mod tests {
|
|||||||
amount_usd: -1.0,
|
amount_usd: -1.0,
|
||||||
balance_type: "recharge".to_string(),
|
balance_type: "recharge".to_string(),
|
||||||
operator_id: Some("admin-user".to_string()),
|
operator_id: Some("admin-user".to_string()),
|
||||||
description: Some("bulk deduction from negative balance".to_string()),
|
description: Some("bulk deduction floors a negative balance".to_string()),
|
||||||
clamp_deduction_to_available_balance: true,
|
clamp_deduction_to_available_balance: true,
|
||||||
batch_context: None,
|
batch_context: None,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("legacy negative wallet should remain usable")
|
.expect("legacy negative wallet should be floored at zero")
|
||||||
.expect("wallet should still exist");
|
.expect("wallet should still exist");
|
||||||
assert_eq!(wallet.balance + wallet.gift_balance, -1.0);
|
assert_eq!(wallet.balance + wallet.gift_balance, 0.0);
|
||||||
assert!(transaction.is_none());
|
let transaction = transaction.expect("negative balance correction should be ledgered");
|
||||||
|
assert_eq!(transaction.amount, 1.0);
|
||||||
|
assert_eq!(transaction.balance_before, -1.0);
|
||||||
|
assert_eq!(transaction.balance_after, 0.0);
|
||||||
let transaction_count: i64 =
|
let transaction_count: i64 =
|
||||||
sqlx::query_scalar("SELECT COUNT(*) FROM wallet_transactions WHERE wallet_id = $1")
|
sqlx::query_scalar("SELECT COUNT(*) FROM wallet_transactions WHERE wallet_id = $1")
|
||||||
.bind(&wallet_id)
|
.bind(&wallet_id)
|
||||||
.fetch_one(&pool)
|
.fetch_one(&pool)
|
||||||
.await
|
.await
|
||||||
.expect("ledger row count should be readable");
|
.expect("ledger row count should be readable");
|
||||||
assert_eq!(transaction_count, 1);
|
assert_eq!(transaction_count, 2);
|
||||||
pool.close().await;
|
pool.close().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user