Record coin seller transfers without recharge policy

This commit is contained in:
zhx 2026-06-04 23:06:51 +08:00
parent d79c877ec0
commit 90ea9d706d
4 changed files with 33 additions and 39 deletions

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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,
)

View File

@ -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()