Files
cursor/plans/首充定格资金修复_ac2c16c6.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

10 KiB
Raw Blame History

name, overview, todos, isProject
name overview todos isProject
首充定格资金修复 本次仅改 slot_wallet 与 slot_console。wallet 删除 Recharge bus、首充发 free_credit_initconsole 消费该事件完成定格。pay 等其它服务不在本次范围。
id content status
wallet-remove-recharge-bus 删除 WalletLogic recharge/rechargeSign 中 sendConsoleBus('Recharge') completed
id content status
wallet-send-free-credit-init recharge + rechargeSign 首充时发送 free_credit_initinc/改账前采 balance_before_qf completed
id content status
wallet-remove-create-wager-first-recharge CreateWagerTask 删除 version1/version2 首充特殊打码分支,首充走普通充值打码 completed
id content status
console-event-bus-handler EventBus case free_credit_init + FreeCreditInitEvent completed
id content status
console-free-credit-init-logic handleFreeCreditInitRechargeEvent 移除首充定格,保留档位推进 completed
id content status
console-safe-freeze-order freezeFirstRecharge 先 RPC 后落库;上限与幂等 completed
id content status
mq-reliability free_credit_init 失败 nack/requeue仅 console EventBus completed
id content status
tests-and-repair wallet/console 单测与集成测FreeCreditsFreezeDev 补偿console completed
false

首充定格资金操作修复方案(修订 v4

变更范围(硬约束)

本次仅编辑以下仓库/服务,不修改任何其它服务(含 slot_pay、slot_lib 等):

在范围内 不在范围内
slot_wallet slot_pay
slot_console slot_libslot_agent、…

Recharge 总线消息假定由 pay 或其它既有链路 发送;本次不从 wallet 重复发送,也不改 pay 去补发。若线上 pay 未发 Recharge,统计/档位问题需另开 pay 任务,不纳入本 PR


设计原则

情愿用户定格失败,也不能让系统亏钱。

服务(本次) 职责
slot_wallet 入账;首充发 free_credit_init删除 Recharge bus移除 CreateWagerTask 旧首充打码拆分
slot_console 消费 free_credit_init → 定格扣款 + 活动落库;RechargeEvent 不再做首充定格

目标架构

sequenceDiagram
    participant Ext as 外部_pay等_本次不改
    participant Wallet as slot_wallet
    participant MQ as console_bus
    participant Console as slot_console

    Ext->>Wallet: recharge / rechargeSign
    Wallet->>Wallet: 采 balance_before_qf入账
    Wallet->>MQ: free_credit_init
    Ext->>MQ: Recharge本次不实现
    MQ->>Console: FreeCreditInitEvent
    Console->>Wallet: freeCreditsFreeze RPC
    Console->>Console: player/package 落库
    MQ->>Console: RechargeEvent既有逻辑无定格

实现步骤

1. slot_wallet

1.1 删除 Recharge bus

WalletLogic::recharge()rechargeSign() 移除:

$this->sendConsoleBus('Recharge', $this->requestDTO->recharge);

保留 sendConsoleBus('reward', ...) 及其它非 Recharge 类型(本次不动)。

1.2 maybeSendFreeCreditInit() + 扩展 sendConsoleBus

  • 改账/ inc() balance_before_qfis_first_rechargetotal_deposit == 0,在 statModel->inc 之前判断)。
  • 改账成功后:is_first_recharge && balance_before_qf > 0 时发送:
$this->sendConsoleBus('free_credit_init', 0, [
    'balance_before_qf' => $balanceBeforeQf,
    'recharge_amount' => $this->requestDTO->recharge,
]);

1.3 recharge()rechargeSign() 均接入

签到购买 rechargeSign() 与普通 recharge() 使用同一套 maybeSendFreeCreditInit() 逻辑。

1.4 移除 CreateWagerTask 旧首充打码逻辑(已废弃)

app/command/CreateWagerTask.phpFree Credits 上线前 于首充时本地拆分余额打码与现「console 定格 + 分档释放」重复且易冲突,本次删除

旧逻辑位置(条件均为 RechargeExchangeService::total 累计充值等于本笔 recharge_amount,即首充):

方法 行号(约) 行为
version1() L87140 SOURCE_TYPE_FIRST_RECHARGE 任务 + SOURCE_TYPE_FREE 免打码任务 + SOURCE_TYPE_FIRST_LEFT「首充剩余」打码
version2() L186244 首充充值/赠送打码 + SOURCE_TYPE_FIRST_LEFTfirst_recharge_left 系数)

与新方案关系

  • 充值前免费余额 → 由 console free_credit_initfreeCreditsFreeze 扣出活动池(不再在 wallet 侧拆 FREE / FIRST_LEFT 任务)。
  • 免打码第一档 / 后续释放 → 由 console FreeCreditsLogic + freeCreditsClaim 创建 Y1WalletLogic::freeCreditsClaimcreateTask)。
  • 首充本笔充值金额的打码 → 与其它充值相同,走 elseif ($dto->required_wager > 0) 通用分支即可。

改动要点

  1. 删除 version1 / version2 中整段 if ($entity->recharge > 0 && $entity->recharge == $dto->recharge_amount) { ... }
  2. 首充与普通充值统一落入后续 elseif ($dto->required_wager > 0)version1 L142+、version2 L246+)。
  3. 保留其中对 SOURCE_TYPE_BUY_SIGN(购买签到解锁额度为 0的处理——该逻辑在 elseif 分支内已有,无需首充专用块。
  4. 删除后确认无引用孤立的 SOURCE_TYPE_FIRST_RECHARGE / SOURCE_TYPE_FIRST_LEFT 首充专用路径(常量可保留供历史任务读)。

2. slot_console

2.1 EventBus 注册 free_credit_init

EventBus::deal() 增加显式分支(类名不能走 default 动态加载):

case 'free_credit_init':
    (new FreeCreditInitEvent())->handle($busEntity);
    break;

新建 app/command/event/FreeCreditInitEvent.php

2.2 FreeCreditsLogic::handleFreeCreditInit

  • 入参:uidbalance_before_qfwallet_amountorderId(来自 bus data)。
  • frozenAmount = max(balance_before_qf, 0)不以充值后再读余额反推为主路径。
  • 活动未开启 / 已定格 → 幂等 return。
  • 调用调整后的 freezeFirstRecharge()

2.3 调整 freezeFirstRecharge(先扣款、后落库)

freezeFirstRecharge()

  1. 幂等:已有 free_credits_freeze:{orderId} 流水则跳过 RPC。
  2. frozenAmount = min(事件金额, RPC 前当前可扣余额)≤0 不扣。
  3. freeCreditsFreeze RPC Db::transaction 写 player/packages。
  4. RPC 失败 → 不落库,抛异常

2.4 RechargeEvent 去掉首充定格

RechargeEvent::handle() 删除 L73-82 对 handleRecharge 的调用(首充定格改由 free_credit_init 触发)。

保留并可继续调用 仅档位推进 的逻辑,例如:

  • 新增 FreeCreditsLogic::advanceAfterRecharge($uid, $walletAmount),或
  • handleRecharge 内去掉首充 freezeFirstRecharge 分支,仅保留 advanceByRecharge(供既有 Recharge 消息使用)。

统计、黑名单、代理首充等 RechargeEvent 现有代码不动(本次范围外行为保持)。

2.5 MQ 可靠性(仅 console

  • FreeCreditInitEvent / free_credit_init:异常上抛;EventBus 对该 type 失败时 nack/requeue(需对齐现有 consumer
  • Recharge 路径统计块仍可独立 try/catch本次不改 pay 发消息前提)。

3. 测试(仅 wallet + console

用例 位置
wallet 删除 Recharge bus slot_wallet
wallet 首充发 free_credit_init(含 rechargeSign slot_wallet
首充不再走 CreateWagerTask 特殊分支 slot_wallet CreateWagerTask
FreeCreditInitEvent 定格成功 slot_console 集成测
RPC 失败不落库 / 先扣后落库 / 幂等 slot_console
RechargeEvent 不再触发定格 调整 FreeCreditsHandleRechargeTest

不新增 pay 侧联调用例。


关键改动文件(仅此两份)

slot_wallet

slot_console


验收标准(本 PR

  • walletrecharge / rechargeSign 不再发送 type=Recharge;首充且 inc 前有免费余额时发送 free_credit_initbalance_before_qf 正确。
  • wallet:首充不再触发 CreateWagerTaskFIRST_RECHARGE / FREE / FIRST_LEFT 拆分,仅按本笔充值/赠送金额走通用打码任务。
  • console:收到 free_credit_init 后完成定格扣款与落库;扣款 ≤ 事件金额且 ≤ 可扣余额;幂等。
  • consoleRechargeEvent 不再执行首充定格;已定格用户经 Recharge 仍可 advanceByRecharge(依赖外部 pay 发消息,本 PR 不验证 pay)。
  • 范围git diff 仅涉及 slot_walletslot_console 路径。