121 lines
6.5 KiB
Markdown
121 lines
6.5 KiB
Markdown
---
|
||
name: 第一档提现统计入账
|
||
overview: 第一档免打码提现成功后,Pay 的 `EventWithdrawal` 在发 Console 总线之外,需经 slot_lib 调用 wallet 新增「仅累加 total_withdraw」接口,把提现金额记入分片 `wallet_stat_xx`,与普通提现 `withdrawSuccess` 的统计口径对齐。
|
||
todos:
|
||
- id: wallet-stat-method
|
||
content: slot_wallet:WalletLogModel 常量 + WalletLogic::freeCreditsFirstCashoutStat(inc total_withdraw + 幂等)
|
||
status: completed
|
||
- id: slot-lib-client
|
||
content: slot_lib WalletService:常量 + freeCreditsFirstCashoutStat() 封装 HTTP update
|
||
status: completed
|
||
- id: pay-event-hook
|
||
content: slot_pay EventWithdrawal:第一档成功分支调用 freeCreditsFirstCashoutStat(order_id, amount)
|
||
status: completed
|
||
- id: tests
|
||
content: wallet 单测幂等与 stat inc;pay 单测/mock 验证调用链
|
||
status: completed
|
||
- id: backfill-optional
|
||
content: (可选)历史成功第一档订单回填 total_withdraw 脚本说明
|
||
status: completed
|
||
isProject: false
|
||
---
|
||
|
||
# 第一档提现成功:wallet_stat 累计提现额补齐
|
||
|
||
## 背景与缺口
|
||
|
||
首充定格第一档(`free_credit_first_cashout`)在 [EventWithdrawal.php](slot_pay/app/command/EventWithdrawal.php) 打款成功时已按设计 **跳过** `withdrawSuccess` / `withdrawFail`,只发 Console 总线:
|
||
|
||
```117:122:slot_pay/app/command/EventWithdrawal.php
|
||
if (WithdrawalOrderEntity::isFreeCreditFirstCashoutOrder($order)) {
|
||
ConsoleMqService::getInstance()->sendConsoleBusEvent($order->uid, MQBusEntity::TYPE_FREE_CREDITS_FIRST_CASHOUT_SUCCESS, [
|
||
'order_id' => $order->order_id,
|
||
]);
|
||
} else {
|
||
$walletService->withdrawSuccess($withdrawalAmount, $order->order_id);
|
||
```
|
||
|
||
普通提现的 `total_withdraw` 仅在 [WalletLogic::withdraw()](slot_wallet/app/api/logic/WalletLogic.php)(`type=withdraw`)里 `WalletStatModel::inc(..., 'total_withdraw', fee)`,且会 `finishWithdraw` 扣减 `withdraw_lock`。
|
||
|
||
第一档 **不冻结** 普通钱包([WithdrawalOrderEntity::apply](slot_pay/app/entity/WithdrawalOrderEntity.php) 已跳过 `withdrawFrozen`),因此 **不能** 直接调 `withdrawSuccess`——会动 `withdraw_lock` 并可能失败。
|
||
|
||
遗留问题:**`wallet_stat_{xx}.total_withdraw` 未累加**,后台用户列表/盈亏等读 `tw`/`total_withdraw` 会偏小。
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Pay as slot_pay EventWithdrawal
|
||
participant Console as slot_console EventBus
|
||
participant Wallet as slot_wallet WalletLogic
|
||
|
||
Note over Pay: 现状(成功)
|
||
Pay->>Console: FreeCreditsFirstCashoutSuccess
|
||
Note over Wallet: total_withdraw 未更新
|
||
|
||
Note over Pay: 目标(成功)
|
||
Pay->>Console: FreeCreditsFirstCashoutSuccess
|
||
Pay->>Wallet: freeCreditsFirstCashoutStat(order_id, amount)
|
||
Wallet->>Wallet: inc total_withdraw + wallet_log
|
||
```
|
||
|
||
---
|
||
|
||
## 推荐方案:wallet 新增「仅统计」类型
|
||
|
||
与现有 `freeCreditsFreeze` / `freeCreditsFirstCashKeep` 一致:走 `POST api/wallet/update`,`WalletLogic::run()` 按 `type` 分发,**不改** `wallet_account` 余额与 `withdraw_lock`。
|
||
|
||
### 1. slot_wallet
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| [WalletLogModel.php](slot_wallet/app/model/multi/WalletLogModel.php) | 新增 `BIZ_TYPE_FREE_CREDITS_FIRST_CASHOUT_STAT = 'freeCreditsFirstCashoutStat'` |
|
||
| [WalletLogic.php](slot_wallet/app/api/logic/WalletLogic.php) | 新增 `freeCreditsFirstCashoutStat()`:`fee>0` 时事务内 `WalletStatModel::inc(currency, 'total_withdraw', fee)` + `addLog(0, balance, 新 biz_type, WALLET_TYPE_GIFT)`(流水 amount=0 或记负向备注,与 first_keep 风格一致) |
|
||
| 幂等 | `biz_id` 使用 **提现订单号** `order_id`(与 `withdrawSuccess` 一致);`wallet_log` 表已有 `uniq_uid_bizid_type (uid, biz_id, biz_type)`。重复 MQ 消费时 `addLog` 唯一键冲突应 **视为成功**(查已有流水则直接返回当前 wallet 快照,不二次 `inc`)——可参考项目内其它 Free Credits 写法的异常处理,若无统一模式则在方法开头 `findOne(['biz_id','biz_type'])` 短路 |
|
||
|
||
**刻意不做:**
|
||
|
||
- 不调 `finishWithdraw` / `doneWithdraw`
|
||
- 不 `sendConsoleBus('Withdrawal')`(避免与普通提现总线混淆)
|
||
- 失败/拒绝路径 **不** dec `total_withdraw`(申请时未 inc,与 `manualRefund` 对称性无关)
|
||
|
||
### 2. slot_lib
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| [WalletService.php](slot_lib/src/services/WalletService.php) | 常量 `WALLET_TYPE_FREE_CREDITS_FIRST_CASHOUT_STAT = 'freeCreditsFirstCashoutStat'` + 方法 `freeCreditsFirstCashoutStat($amount, $bizId = '')` |
|
||
|
||
Pay 继续用现有 `slotLib\services\WalletService`(与 `withdrawSuccess` 同路径),**不**在本需求引入 `slot_sdk`(与当前 pay→wallet 一致)。
|
||
|
||
### 3. slot_pay
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| [EventWithdrawal.php](slot_pay/app/command/EventWithdrawal.php) | 第一档成功分支:在发 Console 总线 **之后**(或之前,顺序无关)调用 `$walletService->freeCreditsFirstCashoutStat($withdrawalAmount, $order->order_id)` |
|
||
| 可选对齐 | 普通成功会 `UserTagService::incSuccessWithdrawalInfo`;若产品希望第一档也计入「成功提现次数/金额」标签,可同分支补上;**若仅关心 wallet_stat,可不加**(需你确认时可单独加) |
|
||
|
||
失败/拒绝分支 **保持现状**(只发 Fail/Rejected 总线,不调 wallet)。
|
||
|
||
### 4. 测试
|
||
|
||
| 仓库 | 内容 |
|
||
|------|------|
|
||
| `slot_wallet` | 单测:`freeCreditsFirstCashoutStat` 累加 `total_withdraw`、相同 `biz_id` 幂等 |
|
||
| `slot_pay` | 扩展 [WithdrawalOrderFreeCreditsTest.php](slot_pay/tests/Unit/WithdrawalOrderFreeCreditsTest.php) 或 mock `EventWithdrawal::updateOrder`:第一档成功应调用新方法、不调 `withdrawSuccess` |
|
||
|
||
---
|
||
|
||
## 历史数据
|
||
|
||
已打款成功、但 `total_withdraw` 未记的第一档订单:可写一次性运维脚本(按 `free_credits_package.withdraw_order_id` + pay 订单金额)批量调新接口或 SQL `inc`,`biz_id` 用原 `order_id` 保证幂等。
|
||
|
||
---
|
||
|
||
## 涉及文件小结
|
||
|
||
- [slot_wallet/app/api/logic/WalletLogic.php](slot_wallet/app/api/logic/WalletLogic.php)
|
||
- [slot_wallet/app/model/multi/WalletLogModel.php](slot_wallet/app/model/multi/WalletLogModel.php)
|
||
- [slot_lib/src/services/WalletService.php](slot_lib/src/services/WalletService.php)
|
||
- [slot_pay/app/command/EventWithdrawal.php](slot_pay/app/command/EventWithdrawal.php)
|
||
- 测试:`slot_wallet/tests/Unit/...`、`slot_pay/tests/Unit/WithdrawalOrderFreeCreditsTest.php`
|
||
|
||
**不改:** `FreeCreditsLogic::handleFirstCashoutResult`(档位状态已覆盖)、Console EventBus 消费端。
|