diff --git a/services/gateway-service/internal/transport/http/response_test.go b/services/gateway-service/internal/transport/http/response_test.go index 7295e608..efe68243 100644 --- a/services/gateway-service/internal/transport/http/response_test.go +++ b/services/gateway-service/internal/transport/http/response_test.go @@ -5590,12 +5590,12 @@ func TestCoinSellerTransferChecksIdentityAndPropagatesWalletCommand(t *testing.T SellerBalanceAfter: 80000, TargetBalanceAfter: 80000, Amount: 80000, - RechargeUsdMinor: 100, - RechargeCurrencyCode: "USD", - RechargePolicyId: 7, - RechargePolicyVersion: "recharge-v1", - RechargePolicyCoinAmount: 80000, - RechargePolicyUsdMinorAmount: 100, + RechargeUsdMinor: 0, + RechargeCurrencyCode: "", + RechargePolicyId: 0, + RechargePolicyVersion: "", + RechargePolicyCoinAmount: 0, + RechargePolicyUsdMinorAmount: 0, }} hostClient := &fakeUserHostClient{profile: &userv1.CoinSellerProfile{ UserId: 42, @@ -5642,7 +5642,7 @@ func TestCoinSellerTransferChecksIdentityAndPropagatesWalletCommand(t *testing.T t.Fatalf("decode response failed: %v", err) } data, ok := response.Data.(map[string]any) - if response.Code != httpkit.CodeOK || !ok || data["transaction_id"] != "wtx-coin-seller" || data["seller_balance_after"] != float64(80000) || data["recharge_usd_minor"] != float64(100) { + if response.Code != httpkit.CodeOK || !ok || data["transaction_id"] != "wtx-coin-seller" || data["seller_balance_after"] != float64(80000) || data["recharge_usd_minor"] != float64(0) || data["recharge_policy_id"] != float64(0) { t.Fatalf("coin seller transfer response mismatch: %+v", response) } } diff --git a/services/wallet-service/internal/service/wallet/service_test.go b/services/wallet-service/internal/service/wallet/service_test.go index 58dd535d..f312ba9a 100644 --- a/services/wallet-service/internal/service/wallet/service_test.go +++ b/services/wallet-service/internal/service/wallet/service_test.go @@ -1568,7 +1568,6 @@ func TestCreditLuckyGiftRewardIsIdempotent(t *testing.T) { func TestTransferCoinFromSellerMovesDedicatedAssetToPlayerCoin(t *testing.T) { repository := mysqltest.NewRepository(t) svc := walletservice.New(repository) - repository.SetRechargePolicy(1001, "recharge-v1", 80000, 100) _, _, err := svc.AdminCreditAsset(context.Background(), ledger.AdminCreditAssetCommand{ CommandID: "cmd-credit-seller", TargetUserID: 30001, @@ -1599,7 +1598,7 @@ func TestTransferCoinFromSellerMovesDedicatedAssetToPlayerCoin(t *testing.T) { if err != nil { t.Fatalf("TransferCoinFromSeller retry failed: %v", err) } - if first.TransactionID == "" || first.TransactionID != second.TransactionID || first.SellerBalanceAfter != 80000 || first.TargetBalanceAfter != 80000 || first.RechargeUSDMinor != 100 { + if first.TransactionID == "" || first.TransactionID != second.TransactionID || first.SellerBalanceAfter != 80000 || first.TargetBalanceAfter != 80000 || first.RechargeUSDMinor != 0 { t.Fatalf("transfer receipt mismatch: first=%+v second=%+v", first, second) } @@ -1635,7 +1634,6 @@ func TestTransferCoinFromSellerMovesDedicatedAssetToPlayerCoin(t *testing.T) { func TestTransferCoinFromSellerAllowsSelfTransfer(t *testing.T) { repository := mysqltest.NewRepository(t) svc := walletservice.New(repository) - repository.SetRechargePolicy(1001, "recharge-v1", 30000, 100) _, _, err := svc.AdminCreditAsset(context.Background(), ledger.AdminCreditAssetCommand{ CommandID: "cmd-credit-self-seller", TargetUserID: 30003, @@ -1666,7 +1664,7 @@ func TestTransferCoinFromSellerAllowsSelfTransfer(t *testing.T) { if err != nil { t.Fatalf("TransferCoinFromSeller self transfer retry failed: %v", err) } - if first.TransactionID == "" || first.TransactionID != second.TransactionID || first.SellerBalanceAfter != 70000 || first.TargetBalanceAfter != 30000 || first.RechargeUSDMinor != 100 { + if first.TransactionID == "" || first.TransactionID != second.TransactionID || first.SellerBalanceAfter != 70000 || first.TargetBalanceAfter != 30000 || first.RechargeUSDMinor != 0 { t.Fatalf("self transfer receipt mismatch: first=%+v second=%+v", first, second) } @@ -1691,11 +1689,10 @@ func TestTransferCoinFromSellerAllowsSelfTransfer(t *testing.T) { } } -// TestTransferCoinFromSellerAllowsSubCentRechargeAmount 验证小额金币也必须到账,美元统计按最小货币单位向下取整。 -func TestTransferCoinFromSellerAllowsSubCentRechargeAmount(t *testing.T) { +// TestTransferCoinFromSellerRecordsCoinsWithoutUSDConversion 验证币商转账只按金币记录充值事实,不再走 USD 换算。 +func TestTransferCoinFromSellerRecordsCoinsWithoutUSDConversion(t *testing.T) { repository := mysqltest.NewRepository(t) svc := walletservice.New(repository) - repository.SetRechargePolicy(1001, "recharge-v1", 1000, 100) _, _, err := svc.AdminCreditAsset(context.Background(), ledger.AdminCreditAssetCommand{ CommandID: "cmd-credit-small-seller", TargetUserID: 30004, @@ -1731,7 +1728,7 @@ func TestTransferCoinFromSellerAllowsSubCentRechargeAmount(t *testing.T) { if balanceAmount(balances, ledger.AssetCoinSellerCoin) != 10 || balanceAmount(balances, ledger.AssetCoin) != 1 { t.Fatalf("small transfer balances mismatch: %+v", balances) } - if got := repository.CountRows("wallet_recharge_records", "transaction_id = ? AND coin_amount = ? AND usd_minor_amount = ?", receipt.TransactionID, int64(1), int64(0)); got != 1 { + if got := repository.CountRows("wallet_recharge_records", "transaction_id = ? AND coin_amount = ? AND usd_minor_amount = ? AND policy_id = ? AND policy_version = ? AND exchange_coin_amount = ? AND exchange_usd_minor_amount = ?", receipt.TransactionID, int64(1), int64(0), int64(0), "", int64(1), int64(0)); got != 1 { t.Fatalf("small transfer should write recharge record with zero USD minor, got %d", got) } } @@ -1758,8 +1755,8 @@ func TestTransferCoinFromSellerInsufficientBalance(t *testing.T) { } } -// TestTransferCoinFromSellerRequiresRechargePolicy 验证缺少区域充值政策时不会产生半成品充值事实。 -func TestTransferCoinFromSellerRequiresRechargePolicy(t *testing.T) { +// TestTransferCoinFromSellerDoesNotRequireRechargePolicy 验证币商转账不依赖外部充值定价策略。 +func TestTransferCoinFromSellerDoesNotRequireRechargePolicy(t *testing.T) { repository := mysqltest.NewRepository(t) svc := walletservice.New(repository) _, _, err := svc.AdminCreditAsset(context.Background(), ledger.AdminCreditAssetCommand{ @@ -1775,7 +1772,7 @@ func TestTransferCoinFromSellerRequiresRechargePolicy(t *testing.T) { t.Fatalf("seed seller asset failed: %v", err) } - _, err = svc.TransferCoinFromSeller(context.Background(), ledger.CoinSellerTransferCommand{ + receipt, err := svc.TransferCoinFromSeller(context.Background(), ledger.CoinSellerTransferCommand{ CommandID: "cmd-seller-no-policy", SellerUserID: 32001, TargetUserID: 32002, @@ -1784,14 +1781,17 @@ func TestTransferCoinFromSellerRequiresRechargePolicy(t *testing.T) { Amount: 80000, Reason: "player recharge", }) - if !xerr.IsCode(err, xerr.NotFound) { - t.Fatalf("expected NOT_FOUND when recharge policy is missing, got %v", err) + if err != nil { + t.Fatalf("TransferCoinFromSeller without recharge policy failed: %v", err) } - if got := repository.CountRows("wallet_transactions", "command_id = ?", "cmd-seller-no-policy"); got != 0 { - t.Fatalf("missing recharge policy must not write transaction, got %d", got) + if receipt.RechargeUSDMinor != 0 || receipt.RechargePolicyID != 0 || receipt.RechargePolicyVersion != "" { + t.Fatalf("policy-free transfer receipt mismatch: %+v", receipt) } - if got := repository.CountRows("wallet_recharge_records", "transaction_id LIKE ?", "wtx_%"); got != 0 { - t.Fatalf("missing recharge policy must not write recharge record, got %d", got) + if got := repository.CountRows("wallet_transactions", "command_id = ?", "cmd-seller-no-policy"); got != 1 { + t.Fatalf("policy-free seller transfer should write transaction, got %d", got) + } + if got := repository.CountRows("wallet_recharge_records", "transaction_id = ? AND coin_amount = ? AND usd_minor_amount = ? AND policy_id = ?", receipt.TransactionID, int64(80000), int64(0), int64(0)); got != 1 { + t.Fatalf("policy-free seller transfer should write coin-only recharge record, got %d", got) } } diff --git a/services/wallet-service/internal/storage/mysql/repository.go b/services/wallet-service/internal/storage/mysql/repository.go index a1810858..6e87b2c8 100644 --- a/services/wallet-service/internal/storage/mysql/repository.go +++ b/services/wallet-service/internal/storage/mysql/repository.go @@ -1214,14 +1214,8 @@ func (r *Repository) TransferCoinFromSeller(ctx context.Context, command ledger. } transactionID := transactionID(command.AppCode, command.CommandID) - policy, err := r.resolveRechargePolicy(ctx, tx, command.TargetRegionID, nowMs) - if err != nil { - return ledger.CoinSellerTransferReceipt{}, err - } - rechargeUSDMinor, err := calculateRechargeUSDMinor(command.Amount, policy) - if err != nil { - return ledger.CoinSellerTransferReceipt{}, err - } + // 币商转账的事实口径只记录金币数量;USD 价格策略属于外部充值定价,不参与币商库存转普通金币。 + rechargeUSDMinor := int64(0) rechargeSequence, err := r.reserveUserRechargeSequence(ctx, tx, command.AppCode, command.TargetUserID, transactionID, command.Amount, rechargeUSDMinor, nowMs) if err != nil { return ledger.CoinSellerTransferReceipt{}, err @@ -1242,11 +1236,11 @@ func (r *Repository) TransferCoinFromSeller(ctx context.Context, command ledger. TargetBalanceAfter: targetAfter, RechargeSequence: rechargeSequence, RechargeUSDMinor: rechargeUSDMinor, - RechargeCurrencyCode: policy.CurrencyCode, - RechargePolicyID: policy.PolicyID, - RechargePolicyVersion: policy.PolicyVersion, - RechargePolicyCoinAmount: policy.CoinAmount, - RechargePolicyUSDMinorAmount: policy.USDMinorAmount, + RechargeCurrencyCode: "", + RechargePolicyID: 0, + RechargePolicyVersion: "", + RechargePolicyCoinAmount: 0, + RechargePolicyUSDMinorAmount: 0, } if err := r.insertTransaction(ctx, tx, transactionID, command.CommandID, bizTypeCoinSellerTransfer, requestHash, fmt.Sprintf("coin_seller:%d:%d", command.SellerUserID, command.TargetUserID), metadata, nowMs); err != nil { return ledger.CoinSellerTransferReceipt{}, err @@ -1917,7 +1911,7 @@ func (r *Repository) insertRechargeRecord(ctx context.Context, tx *sql.Tx, trans metadata.RechargeCurrencyCode, metadata.Amount, metadata.RechargeUSDMinor, - metadata.RechargePolicyCoinAmount, + metadata.Amount, metadata.RechargePolicyUSDMinorAmount, nowMs, ) diff --git a/services/wallet-service/internal/testutil/mysqltest/mysqltest.go b/services/wallet-service/internal/testutil/mysqltest/mysqltest.go index ab36ea93..a5ece288 100644 --- a/services/wallet-service/internal/testutil/mysqltest/mysqltest.go +++ b/services/wallet-service/internal/testutil/mysqltest/mysqltest.go @@ -449,7 +449,7 @@ func (r *Repository) SetGiftDiamondRatio(regionID int64, giftTypeCode string, ra } } -// SetRechargePolicy 配置区域充值汇率,用于币商转账充值口径测试。 +// SetRechargePolicy 配置区域充值定价,用于外部充值类事实测试。 func (r *Repository) SetRechargePolicy(regionID int64, policyVersion string, coinAmount int64, usdMinorAmount int64) { r.t.Helper()