first commit

This commit is contained in:
ray zhou
2026-05-21 18:16:26 +08:00
commit 033aa0ae69
324 changed files with 24705 additions and 0 deletions

View File

@@ -0,0 +1,223 @@
---
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` 路径。