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

6.5 KiB
Raw Permalink Blame History

name, overview, todos, isProject
name overview todos isProject
第一档提现统计入账 第一档免打码提现成功后Pay 的 `EventWithdrawal` 在发 Console 总线之外,需经 slot_lib 调用 wallet 新增「仅累加 total_withdraw」接口把提现金额记入分片 `wallet_stat_xx`,与普通提现 `withdrawSuccess` 的统计口径对齐。
id content status
wallet-stat-method slot_walletWalletLogModel 常量 + WalletLogic::freeCreditsFirstCashoutStatinc total_withdraw + 幂等) completed
id content status
slot-lib-client slot_lib WalletService常量 + freeCreditsFirstCashoutStat() 封装 HTTP update completed
id content status
pay-event-hook slot_pay EventWithdrawal第一档成功分支调用 freeCreditsFirstCashoutStat(order_id, amount) completed
id content status
tests wallet 单测幂等与 stat incpay 单测/mock 验证调用链 completed
id content status
backfill-optional (可选)历史成功第一档订单回填 total_withdraw 脚本说明 completed
false

第一档提现成功wallet_stat 累计提现额补齐

背景与缺口

首充定格第一档(free_credit_first_cashout)在 EventWithdrawal.php 打款成功时已按设计 跳过 withdrawSuccess / withdrawFail,只发 Console 总线:

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()type=withdraw)里 WalletStatModel::inc(..., 'total_withdraw', fee),且会 finishWithdraw 扣减 withdraw_lock

第一档 不冻结 普通钱包(WithdrawalOrderEntity::apply 已跳过 withdrawFrozen),因此 不能 直接调 withdrawSuccess——会动 withdraw_lock 并可能失败。

遗留问题:wallet_stat_{xx}.total_withdraw 未累加,后台用户列表/盈亏等读 tw/total_withdraw 会偏小。

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/updateWalletLogic::run()type 分发,不改 wallet_account 余额与 withdraw_lock

1. slot_wallet

文件 改动
WalletLogModel.php 新增 BIZ_TYPE_FREE_CREDITS_FIRST_CASHOUT_STAT = 'freeCreditsFirstCashoutStat'
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(申请时未 incmanualRefund 对称性无关)

2. slot_lib

文件 改动
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 第一档成功分支:在发 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 或 mock EventWithdrawal::updateOrder:第一档成功应调用新方法、不调 withdrawSuccess

历史数据

已打款成功、但 total_withdraw 未记的第一档订单:可写一次性运维脚本(按 free_credits_package.withdraw_order_id + pay 订单金额)批量调新接口或 SQL incbiz_id 用原 order_id 保证幂等。


涉及文件小结

不改: FreeCreditsLogic::handleFirstCashoutResult档位状态已覆盖、Console EventBus 消费端。