Files
cursor/plans/第一档提现统计入账_b97ec4a2.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

121 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: 第一档提现统计入账
overview: 第一档免打码提现成功后Pay 的 `EventWithdrawal` 在发 Console 总线之外,需经 slot_lib 调用 wallet 新增「仅累加 total_withdraw」接口把提现金额记入分片 `wallet_stat_xx`,与普通提现 `withdrawSuccess` 的统计口径对齐。
todos:
- id: wallet-stat-method
content: slot_walletWalletLogModel 常量 + WalletLogic::freeCreditsFirstCashoutStatinc 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 incpay 单测/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 消费端。