--- name: 第一档提现独立接口 overview: 将 Free Credits 第一档免打码提现从 `POST /api/withdraw/apply` 拆出,新增 `POST /api/free-credits/withdraw-first-cash`(与 `keep-first-cash` 并列);普通提现接口保持原样,不再识别 `package_id`。 todos: - id: validator-migrate content: FreeCreditsValidator 增加 withdraw-first-cash 各支付 scene;WithdrawValidator 移除 *_fc status: completed - id: controller-endpoint content: FreeCreditsController::withdrawFirstCash + 注释更新 status: completed - id: withdraw-service-split content: WithdrawService 抽出 public applyFreeCreditsFirstCash;apply() 去掉 package_id 分支 status: completed - id: withdraw-controller-clean content: WithdrawController::apply 仅保留普通提现校验与文案 status: completed - id: tests-update content: 迁移/更新 Validator 与 FreeCreditsFirstCashoutApplyTest 单测 status: completed isProject: false --- # 第一档提现独立接口 ## 现状 第一档独立提现与**普通钱包提现**共用同一入口: ```mermaid flowchart LR client[C端] withdrawApply["POST /api/withdraw/apply"] withdrawSvc[WithdrawService::apply] fcLogic[FreeCreditsLogic::applyFirstCashoutWithdraw] pay[PayService::apply] client --> withdrawApply withdrawApply -->|"package_id > 0"| withdrawSvc withdrawApply -->|"无 package_id"| withdrawSvc withdrawSvc --> fcLogic --> pay ``` 关键代码: - [`WithdrawController::apply`](slot_console/app/api/controller/WithdrawController.php):`package_id` 非空时走 `WithdrawValidator::firstCashoutScene` - [`WithdrawService::apply`](slot_console/app/service/WithdrawService.php) L167–168:`package_id > 0` 时调用 `applyFreeCreditsFirstCashout` → [`FreeCreditsLogic::applyFirstCashoutWithdraw`](slot_console/app/api/logic/FreeCreditsLogic.php) - 第一档「保留余额」已是独立接口:`POST /api/free-credits/keep-first-cash`([`FreeCreditsController`](slot_console/app/api/controller/FreeCreditsController.php)) 目标形态: ```mermaid flowchart LR normal["POST /api/withdraw/apply\namount + 绑卡"] fcWithdraw["POST /api/free-credits/withdraw-first-cash\npackage_id + 绑卡"] keep["POST /api/free-credits/keep-first-cash\npackage_id"] normal --> walletWithdraw[余额/手续费/黑规则] fcWithdraw --> fcLogic[FreeCreditsLogic] keep --> walletKeep[freeCreditsFirstCashKeep] ``` ## 新接口契约 | 项 | 约定 | | --- | --- | | 路径 | `POST /api/free-credits/withdraw-first-cash`(Webman 默认路由 → `FreeCreditsController::withdrawFirstCash`) | | 入参 | `package_id`(必填)、`type`(1/2/3/6)、各支付方式绑卡字段(与现 `WithdrawValidator` 的 `*_fc` scene 一致)、可选 `pay_net` | | 成功 `data` | `{ order_id, amount }`(与现第一档走 `withdraw/apply` 的返回一致,**不**改 `buildStatus`) | | 成功 `msg` | 沿用现第一档文案:`Submitted successfully! Your order is under review...` | | 业务逻辑 | **复用** `FreeCreditsLogic::applyFirstCashoutWithdraw`;Pay 回调、`handleFirstCashoutResult`、档位状态机**不变** | 与 [`keep-first-cash`](slot_console/app/api/controller/FreeCreditsController.php) 对称:同属 Free Credits 活动域,第一档二选一: - **Withdraw** → `POST /api/free-credits/withdraw-first-cash` - **Keep** → `POST /api/free-credits/keep-first-cash` ## 实现步骤(仅 slot_console) ### 1. 新 Controller 方法 文件:[`FreeCreditsController.php`](slot_console/app/api/controller/FreeCreditsController.php) - 新增 `withdrawFirstCash(Request $request)` - 校验 → 构建 `WithdrawApplyDTO`(复用现有 DTO,含 `package_id` + 绑卡字段)→ 调用提现编排(见下) - 更新类注释:第一档提现改指向新路径 ### 2. 校验迁移到 FreeCreditsValidator 文件:[`FreeCreditsValidator.php`](slot_console/app/api/validator/FreeCreditsValidator.php) 从 [`WithdrawValidator`](slot_console/app/api/validator/WithdrawValidator.php) **迁入**: - `SCENE_WITHDRAW_FIRST_CASH_CASH` / `_BTC` / `_USDT` / `_PAYPAL`(命名可与原 `*_fc` 对齐或重命名) - `type`、`user_name`、`cash_tag`、`btc`、`usdt`、`paypal_*`、`email` 等 rule(原 `*_fc` scene 字段集) - 静态方法 `firstCashoutScene(int $type): string`(原 `firstCashoutScene`) [`WithdrawValidator`](slot_console/app/api/validator/WithdrawValidator.php):**删除** `SCENE_APPLY_*_FC`、`firstCashoutScene` 及对应 scene 配置。 ### 3. 提现编排仍放 WithdrawService(绑卡 + 锁) 文件:[`WithdrawService.php`](slot_console/app/service/WithdrawService.php) - 将现有 `applyFreeCreditsFirstCashout` 提升为 **`public function applyFreeCreditsFirstCash(WithdrawApplyDTO $applyDTO): array`**,内容包含: - `is_bind_name` 校验 - Redis 防重复提交锁(与 `apply` 相同 key) - `checkBankInfo` + `FreeCreditsLogic::applyFirstCashoutWithdraw` - [`apply()`](slot_console/app/service/WithdrawService.php):**删除** `if ($applyDTO->package_id > 0)` 分支,仅保留普通提现路径 说明:绑卡逻辑仍在 `WithdrawService` 私有方法中,Logic 层不重复实现,避免违反分层「不中转 Service」时仍复用已有绑卡能力。 ### 4. 精简 WithdrawController 文件:[`WithdrawController.php`](slot_console/app/api/controller/WithdrawController.php) - `apply()` 仅按 `type` 选普通 scene(`(string) $type`) - 移除 `package_id` 分支与差异化 `msg` **不对**旧接口传 `package_id` 做拒绝或转发(按你的确认:无需处理)。 ### 5. DTO / 字段 [`WithdrawApplyDTO`](slot_console/app/api/dto/request/WithdrawApplyDTO.php):`package_id` 字段可保留供新接口使用;注释改为「仅 free-credits/withdraw-first-cash 使用」。普通 `withdraw/apply` 不再读取该字段。 ### 6. 单测与文档注释 | 文件 | 改动 | | --- | --- | | [`WithdrawValidatorFreeCreditsTest.php`](slot_console/tests/Unit/WithdrawValidatorFreeCreditsTest.php) | 迁至 `FreeCreditsValidatorWithdrawFirstCashTest`(或合并进 `FreeCreditsValidatorTest`),断言新 scene | | [`FreeCreditsFirstCashoutApplyTest.php`](slot_console/tests/Integration/FreeCreditsFirstCashoutApplyTest.php) | 改为调用 `WithdrawService::applyFreeCreditsFirstCash`(或经新 Controller harness);断言仍是不走余额校验、Pay 失败回滚 ready | | [`FreeCreditsController.php`](slot_console/app/api/controller/FreeCreditsController.php) / [`FreeCreditsValidator.php`](slot_console/app/api/validator/FreeCreditsValidator.php) | 注释同步新路径 | 运行: ```bash docker exec -w /app/www/slot/slot_console php82 ./vendor/bin/phpunit \ tests/Unit/FreeCreditsValidatorTest.php \ tests/Unit/WithdrawValidatorFreeCreditsTest.php \ tests/Integration/FreeCreditsFirstCashoutApplyTest.php ``` (迁移后调整具体测试文件名。) ## 不在本次范围 - slot_pwa / gateway 前端改 URL(联调时 C 端将 Withdraw 按钮从 `/api/withdraw/apply` 改为 `/api/free-credits/withdraw-first-cash`) - slot_pay / wallet / EventBus 回调逻辑 - `keep-first-cash`、`claim`、`status` 行为 ## C 端联调要点 第一档 `packages[0].status === 1` 时: - **Withdraw** → `POST /api/free-credits/withdraw-first-cash` + `package_id` + 支付方式字段 - **Keep** → 现有 `POST /api/free-credits/keep-first-cash` 普通提现页仍用 `POST /api/withdraw/apply`(`amount` 必填,**不传** `package_id`)。 ## 验收 1. 新接口:第一档 ready + 合法绑卡 → 返回 `order_id`/`amount`,package → `processing`,Pay 失败回滚 `ready` 2. 新接口:非 ready / 无资格 / processing 中 → 业务异常与现逻辑一致 3. `POST /api/withdraw/apply`:仅普通提现;带 `amount` 走余额/手续费校验;**不再**因 `package_id` 进入 Free Credits 分支 4. `keep-first-cash` / `claim` / 提现结果回调回归不受影响 5. 相关单测通过