Files
cursor/plans/第一档提现或保留余额_181f2d15.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

177 lines
8.2 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: 为 Free Credits 第一档定格后首笔金额增加用户二选一沿用现有独立提现或新增「保留到可提现余额」withdraw_balance、不创建打码任务。涉及 slot_wallet 新钱包原子能力、slot_lib RPC、slot_console 新 C 端接口与档位状态同步。
todos:
- id: wallet-first-keep
content: slot_walletWalletLogModel + WalletLogic::freeCreditsFirstCashKeepwithdraw 入账、无 createTask+ 单测
status: completed
- id: slot-lib-rpc
content: slot_lib WalletService 增加 freeCreditsFirstCashKeep RPC
status: completed
- id: console-keep-api
content: slot_consoleFreeCreditsLogic::keepFirstCash + Validator + Controller + 路由
status: completed
- id: console-tests
content: slot_console 单测 FreeCreditsKeepFirstCashTest必要时调整 broadcast action
status: completed
- id: run-phpunit
content: php82 容器跑 console + wallet 相关单测
status: completed
isProject: false
---
# 第一档:提现或保留到可提现余额
## 背景与现状
当前第一档(`package_type=TYPE_FIRST_CASH`)在累计充值解锁后**仅支持**走 [`WithdrawService::applyFreeCreditsFirstCashout`](slot_console/app/service/WithdrawService.php) → Pay 独立提现(`bizType=free_credit_first_cashout`**不经过钱包余额**(需求文档 §15.3)。
后续释放档走 [`FreeCreditsLogic::claim`](slot_console/app/api/logic/FreeCreditsLogic.php) → [`WalletLogic::freeCreditsClaim`](slot_wallet/app/api/logic/WalletLogic.php):入账 **deposit_balance****创建 Y1 打码任务**
新需求:第一档金额用户可二选一:
| 选项 | 行为 |
| --- | --- |
| **Withdraw** | 保持现有 `/api/withdraw/apply` + `package_id` |
| **Keep to balance** | 入账 **withdraw_balance****不** `createTask`,金额立即可参与普通提现规则(无其它未完成打码任务时) |
已确认:保留余额入账 **withdraw_balance**(非 deposit
```mermaid
flowchart TD
firstReady[第一档 status=ready]
firstReady --> withdrawPath["POST /api/withdraw/apply"]
firstReady --> keepPath["POST /api/free-credits/keep-first-cash"]
withdrawPath --> payOrder[Pay 独立提现单]
payOrder --> busCallback[EventBus 提现结果]
busCallback --> pkgDone[package completed]
keepPath --> walletKeep[wallet freeCreditsFirstCashKeep]
walletKeep --> pkgDone
pkgDone --> playerDone[player STATUS_FIRST_CASH_DONE]
```
## 目标契约
### 新接口slot_console
- **路径**`POST /api/free-credits/keep-first-cash`(与现有 `claim` 命名风格一致)
- **入参**`package_id`(必填,`free_credits_package.id`,须为第一档且 `status=ready`
- **成功**`data` 结构同 `status()` / `claim``buildStatus`
- **幂等**`bizId = free_credits_first_keep:{packageId}`;重复请求不重复入账
- **互斥**:同一第一档 `processing/completed` 后不可再提现或 keep提现处理中不可 keep
### 档位与玩家状态(与提现成功对齐)
完成后与 [`handleFirstCashoutResult(success)`](slot_console/app/api/logic/FreeCreditsLogic.php) 一致:
- package → `STATUS_COMPLETED`,写 `completed_time``claim_biz_id` 存幂等键(复用字段,无需改表)
- player → `STATUS_FIRST_CASH_DONE`
- 调用 `refreshPlayerCompletion`
- 首页状态条关闭逻辑与「第一档提现成功」相同C 端仍用 `status` / packages[0] completed
### 钱包slot_wallet
新增 **`freeCreditsFirstCashKeep`**`WalletLogic::run``type` 分发):
- `inc(withdraw=fee, deposit=0, bonus=0)` 增加 **withdraw_balance**
- **不**调用 `createTask`
- 流水:`BIZ_TYPE_FREE_CREDITS_FIRST_KEEP = 'freeCreditsFirstCashKeep'``WALLET_TYPE_GIFT`(与 freeze/claim 一致)
- 事务内完成入账 + `addLog`;成功后 `sendConsoleBus`(可选,用于统计/通知,类型建议 `freeCreditsFirstCashKeep`
对比现有实现:
| 能力 | 入账 | 打码任务 |
| --- | --- | --- |
| `freeCreditsClaim`(后续档) | deposit | Y1 `createTask` |
| **`freeCreditsFirstCashKeep`(新)** | **withdraw** | **无** |
### slot_lib
[`WalletService`](slot_lib/src/services/WalletService.php) 增加常量与方法:
```php
const WALLET_TYPE_FREE_CREDITS_FIRST_KEEP = 'freeCreditsFirstCashKeep';
public function freeCreditsFirstCashKeep($amount, $bizId = ''): Wallet
```
## 实现步骤
### 1. slot_wallet
| 文件 | 改动 |
| --- | --- |
| [`WalletLogModel.php`](slot_wallet/app/model/multi/WalletLogModel.php) | `BIZ_TYPE_FREE_CREDITS_FIRST_KEEP` |
| [`WalletLogic.php`](slot_wallet/app/api/logic/WalletLogic.php) | `freeCreditsFirstCashKeep()`:镜像 `freeCreditsClaim` 事务结构,改为 `inc(fee,0,0)`**删除** `createTask` 调用 |
| 单测 | 新增 `WalletFreeCreditsFirstKeepTest`(或扩展现有 wallet 单测):断言 withdraw 增加、无 task 创建(可 mock `createTask` 不被调用) |
### 2. slot_lib
- [`WalletService.php`](slot_lib/src/services/WalletService.php):常量 + `freeCreditsFirstCashKeep($amount, $bizId)`
### 3. slot_console — Logic
[`FreeCreditsLogic.php`](slot_console/app/api/logic/FreeCreditsLogic.php) 新增 `keepFirstCash(int $uid, int $packageId): array`
1. `assertEligibleParticipant`
2. 查询 package`uid``TYPE_FIRST_CASH``STATUS_READY`
3. `bizId = 'free_credits_first_keep:' . $packageId`
4. package → `STATUS_PROCESSING`,写 `claim_biz_id`
5. `walletService->freeCreditsFirstCashKeep($package->amount_qf, $bizId)`
6. 成功 → package `COMPLETED` + 时间戳player `STATUS_FIRST_CASH_DONE``refreshPlayerCompletion`
7. 失败 → package 回滚 `READY`(同 `claim`
8. 返回 `buildStatus`
**不**改 `advanceByRecharge` / 定格冻结逻辑。
### 4. slot_console — API 层
| 文件 | 改动 |
| --- | --- |
| [`FreeCreditsValidator.php`](slot_console/app/api/validator/FreeCreditsValidator.php) | `SCENE_KEEP_FIRST_CASH``package_id` require\|integer |
| [`FreeCreditsController.php`](slot_console/app/api/controller/FreeCreditsController.php) | `keepFirstCash()` + PHPDoc类注释补充第二路径 |
| 路由配置 | 注册 `POST /api/free-credits/keep-first-cash`(查项目现有 `route` / `config``free-credits` 注册方式,与 `claim` 并列) |
### 5. 广播 / 统计(小改)
- [`buildBroadcastList`](slot_console/app/api/logic/FreeCreditsLogic.php):第一档 completed 若走 keep广播 `action` 可新增 `keep`(或复用 `claim`);与产品确认文案前可先 `keep`
- [`FreeCreditsStatsLogic`](slot_console/app/innerapi/logic/FreeCreditsStatsLogic.php)`first_cashout_*` 按「第一档 completed」统计**keep 与 withdraw 均计入**(无需区分,除非运营后续要拆指标)
### 6. 单测slot_console
| 文件 | 内容 |
| --- | --- |
| 新增 `FreeCreditsKeepFirstCashTest` | harness 注入 mock `WalletService`;断言状态迁移、幂等、非 ready 抛错 |
| 可选集成测 | keep 后 package completed + player `FIRST_CASH_DONE` |
运行:
```bash
docker exec -w /app/www/slot/slot_console php82 ./vendor/bin/phpunit tests/Unit/FreeCreditsKeepFirstCashTest.php
docker exec -w /app/www/slot/slot_wallet php82 ./vendor/bin/phpunit tests/Unit/WalletFreeCreditsFirstKeepTest.php
```
## C 端约定(供联调,本次可不改 PWA
第一档 `packages[0].status === 1` 时展示两个入口:
- **Withdraw** → 现有 `POST /api/withdraw/apply` + `package_id`
- **Keep to Balance** → `POST /api/free-credits/keep-first-cash` + `package_id`
完成后 `packages[0].status === 3`,与提现成功 UI 一致(按钮隐藏 / 成功态)。
## 不在本次范围
- slot_pwa / gateway 前端页面与文案
- 需求文档 §1011 全文修订可后续补「Add to Balance」分支
- 修改后续档 `claim` 的打码规则
- 第一档金额改由后台配置动态展示(仍从 `packages[0].amount` 读取)
## 验收清单
1. 第一档 readykeep 成功 → withdraw_balance 增加对应千分位,**无**新 WagerTask
2. 同一 package 重复 keep幂等不重复加钱
3. keep 成功后package/player 状态与提现成功一致;`status` 接口 packages[0] 为 completed
4. 第一档 processing提现中keep 拒绝
5. 第一档 completedwithdraw / keep 均拒绝
6. 相关单测通过