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

224 lines
10 KiB
Markdown
Raw Permalink 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: 本次仅改 slot_wallet 与 slot_console。wallet 删除 Recharge bus、首充发 free_credit_initconsole 消费该事件完成定格。pay 等其它服务不在本次范围。
todos:
- id: wallet-remove-recharge-bus
content: 删除 WalletLogic recharge/rechargeSign 中 sendConsoleBus('Recharge')
status: completed
- id: wallet-send-free-credit-init
content: recharge + rechargeSign 首充时发送 free_credit_initinc/改账前采 balance_before_qf
status: completed
- id: wallet-remove-create-wager-first-recharge
content: CreateWagerTask 删除 version1/version2 首充特殊打码分支,首充走普通充值打码
status: completed
- id: console-event-bus-handler
content: EventBus case free_credit_init + FreeCreditInitEvent
status: completed
- id: console-free-credit-init-logic
content: handleFreeCreditInitRechargeEvent 移除首充定格,保留档位推进
status: completed
- id: console-safe-freeze-order
content: freezeFirstRecharge 先 RPC 后落库;上限与幂等
status: completed
- id: mq-reliability
content: free_credit_init 失败 nack/requeue仅 console EventBus
status: completed
- id: tests-and-repair
content: wallet/console 单测与集成测FreeCreditsFreezeDev 补偿console
status: completed
isProject: false
---
# 首充定格资金操作修复方案(修订 v4
## 变更范围(硬约束)
**本次仅编辑以下仓库/服务,不修改任何其它服务(含 slot_pay、slot_lib 等):**
| 在范围内 | 不在范围内 |
|----------|------------|
| `slot_wallet` | `slot_pay` |
| `slot_console` | `slot_lib``slot_agent`、… |
> `Recharge` 总线消息假定由 **pay 或其它既有链路** 发送;本次不从 wallet 重复发送,也**不改 pay** 去补发。若线上 pay 未发 `Recharge`,统计/档位问题需另开 pay 任务,**不纳入本 PR**。
---
## 设计原则
> **情愿用户定格失败,也不能让系统亏钱。**
| 服务(本次) | 职责 |
|--------------|------|
| **slot_wallet** | 入账;首充发 **`free_credit_init`****删除** `Recharge` bus**移除** `CreateWagerTask` 旧首充打码拆分 |
| **slot_console** | 消费 `free_credit_init` → 定格扣款 + 活动落库;`RechargeEvent` **不再**做首充定格 |
---
## 目标架构
```mermaid
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()`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/api/logic/WalletLogic.php)、[`rechargeSign()`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/api/logic/WalletLogic.php) 移除:
```php
$this->sendConsoleBus('Recharge', $this->requestDTO->recharge);
```
保留 `sendConsoleBus('reward', ...)` 及其它非 Recharge 类型(本次不动)。
#### 1.2 `maybeSendFreeCreditInit()` + 扩展 `sendConsoleBus`
- 改账/ `inc()` **前**`balance_before_qf``is_first_recharge``total_deposit == 0`,在 `statModel->inc` 之前判断)。
- 改账成功后:`is_first_recharge && balance_before_qf > 0` 时发送:
```php
$this->sendConsoleBus('free_credit_init', 0, [
'balance_before_qf' => $balanceBeforeQf,
'recharge_amount' => $this->requestDTO->recharge,
]);
```
- [`sendConsoleBus()`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/api/logic/WalletLogic.php) 增加可选参数 `array $extraData = []`
#### 1.3 `recharge()` 与 `rechargeSign()` 均接入
签到购买 [`rechargeSign()`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/api/logic/WalletLogic.php) 与普通 [`recharge()`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/api/logic/WalletLogic.php) 使用同一套 `maybeSendFreeCreditInit()` 逻辑。
#### 1.4 移除 `CreateWagerTask` 旧首充打码逻辑(已废弃)
[`app/command/CreateWagerTask.php`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/command/CreateWagerTask.php) 在 **Free Credits 上线前** 于首充时本地拆分余额打码与现「console 定格 + 分档释放」重复且易冲突,**本次删除**。
**旧逻辑位置**(条件均为 `RechargeExchangeService::total` 累计充值等于本笔 `recharge_amount`,即首充):
| 方法 | 行号(约) | 行为 |
|------|-----------|------|
| `version1()` | L87140 | `SOURCE_TYPE_FIRST_RECHARGE` 任务 + `SOURCE_TYPE_FREE` 免打码任务 + `SOURCE_TYPE_FIRST_LEFT`「首充剩余」打码 |
| `version2()` | L186244 | 首充充值/赠送打码 + `SOURCE_TYPE_FIRST_LEFT``first_recharge_left` 系数) |
**与新方案关系**
- 充值前免费余额 → 由 console `free_credit_init``freeCreditsFreeze` 扣出活动池(不再在 wallet 侧拆 `FREE` / `FIRST_LEFT` 任务)。
- 免打码第一档 / 后续释放 → 由 console `FreeCreditsLogic` + `freeCreditsClaim` 创建 Y1`WalletLogic::freeCreditsClaim``createTask`)。
- 首充**本笔充值金额**的打码 → 与其它充值相同,走 `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()`](file:///Users/ray/Documents/project/www/slot/slot_console/app/command/EventBus.php) 增加显式分支(类名不能走 default 动态加载):
```php
case 'free_credit_init':
(new FreeCreditInitEvent())->handle($busEntity);
break;
```
新建 [`app/command/event/FreeCreditInitEvent.php`](file:///Users/ray/Documents/project/www/slot/slot_console/app/command/event/FreeCreditInitEvent.php)。
#### 2.2 `FreeCreditsLogic::handleFreeCreditInit`
- 入参:`uid``balance_before_qf``wallet_amount``orderId`(来自 bus `data`)。
- `frozenAmount = max(balance_before_qf, 0)`**不以**充值后再读余额反推为主路径。
- 活动未开启 / 已定格 → 幂等 return。
- 调用调整后的 `freezeFirstRecharge()`
#### 2.3 调整 `freezeFirstRecharge`(先扣款、后落库)
[`freezeFirstRecharge()`](file:///Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php)
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()`](file:///Users/ray/Documents/project/www/slot/slot_console/app/command/event/RechargeEvent.php) **删除** 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`](file:///Users/ray/Documents/project/www/slot/slot_console/tests/Unit/FreeCreditsHandleRechargeTest.php) 等 |
**不新增** pay 侧联调用例。
---
## 关键改动文件(仅此两份)
**slot_wallet**
- [`app/api/logic/WalletLogic.php`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/api/logic/WalletLogic.php)
- [`app/command/CreateWagerTask.php`](file:///Users/ray/Documents/project/www/slot/slot_wallet/app/command/CreateWagerTask.php)
**slot_console**
- [`app/command/EventBus.php`](file:///Users/ray/Documents/project/www/slot/slot_console/app/command/EventBus.php)
- `app/command/event/FreeCreditInitEvent.php`(新建)
- [`app/api/logic/FreeCreditsLogic.php`](file:///Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php)
- [`app/command/event/RechargeEvent.php`](file:///Users/ray/Documents/project/www/slot/slot_console/app/command/event/RechargeEvent.php)
- 相关 testsconsole 仓内)
---
## 验收标准(本 PR
- **wallet**`recharge` / `rechargeSign` 不再发送 `type=Recharge`;首充且 inc 前有免费余额时发送 `free_credit_init``balance_before_qf` 正确。
- **wallet**:首充不再触发 `CreateWagerTask``FIRST_RECHARGE` / `FREE` / `FIRST_LEFT` 拆分,仅按本笔充值/赠送金额走通用打码任务。
- **console**:收到 `free_credit_init` 后完成定格扣款与落库;扣款 ≤ 事件金额且 ≤ 可扣余额;幂等。
- **console**`RechargeEvent` 不再执行首充定格;已定格用户经 `Recharge` 仍可 `advanceByRecharge`(依赖外部 pay 发消息,**本 PR 不验证 pay**)。
- **范围**`git diff` 仅涉及 `slot_wallet``slot_console` 路径。