ok
This commit is contained in:
181
plans/firstcashout_合并代码评审_b7afdfc6.plan.md
Normal file
181
plans/firstcashout_合并代码评审_b7afdfc6.plan.md
Normal file
@@ -0,0 +1,181 @@
|
||||
---
|
||||
name: firstCashout 合并代码评审
|
||||
overview: 你对「merge post + 统一 WithdrawService::apply」的改法方向正确,但当前 WithdrawService 在 package_id>0 时仍执行 checkInfo/手续费/黑规则,且 Pay 失败无回滚,会导致第一档提现不可用或卡在 processing。
|
||||
todos:
|
||||
- id: fc-early-return
|
||||
content: WithdrawService::apply 中 package_id>0 走独立 applyFreeCreditsFirstCashout 并 early return
|
||||
status: completed
|
||||
- id: skip-checkinfo-fc
|
||||
content: FC 分支跳过 checkInfo、getAmountAndFee、黑规则(或产品确认保留项)
|
||||
status: completed
|
||||
- id: pay-fail-rollback
|
||||
content: Pay apply 失败时 handleFirstCashoutResult 回滚 processing
|
||||
status: completed
|
||||
- id: fc-withdrawal-info
|
||||
content: FC 使用 package amount_qf、fee=0、auditType=2 组 WithdrawalInfo
|
||||
status: completed
|
||||
isProject: false
|
||||
---
|
||||
|
||||
# firstCashout 合并改动 — 代码评审
|
||||
|
||||
## 你的改动(理解正确)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Ctrl as WithdrawController
|
||||
participant FC as FreeCreditsLogic
|
||||
participant WS as WithdrawService
|
||||
participant Pay as slot_pay
|
||||
|
||||
Ctrl->>FC: mergeFirstCashoutIntoPost
|
||||
Note over FC: amount + bizType + package_id
|
||||
Ctrl->>WS: apply(DTO)
|
||||
WS->>FC: firstCashout(uid, packageId, orderId)
|
||||
Note over FC: markFirstCashoutProcessing
|
||||
WS->>Pay: apply(WithdrawalInfo)
|
||||
```
|
||||
|
||||
- [`WithdrawController::apply`](slot_console/app/api/controller/WithdrawController.php):有 `package_id` 先 merge,再**同一套** `validate($type)` — 符合预期。
|
||||
- [`mergeFirstCashoutIntoPost`](slot_console/app/api/logic/FreeCreditsLogic.php):补 `amount` / `bizType` / `package_id` — 符合预期。
|
||||
- [`firstCashout`](slot_console/app/api/logic/FreeCreditsLogic.php) 收窄为只 `markFirstCashoutProcessing` + 外部统一 `PayService::apply` — 思路可行。
|
||||
|
||||
---
|
||||
|
||||
## P0:FC 仍会走 `checkInfo`,大概率直接失败
|
||||
|
||||
[`WithdrawService::apply`](slot_console/app/service/WithdrawService.php) 当前顺序:
|
||||
|
||||
```php
|
||||
$bankInfo = $this->checkBankInfo($applyDTO);
|
||||
$this->checkInfo($applyDTO->type, $applyDTO->amount); // 始终执行
|
||||
// ...
|
||||
if ($applyDTO->package_id > 0) {
|
||||
firstCashout(...); // 永远走不到(checkInfo 已抛错)
|
||||
}
|
||||
```
|
||||
|
||||
`checkInfo` 会校验**钱包可提现余额** ≥ amount([`WithdrawService.php` L333-335](slot_console/app/service/WithdrawService.php))。
|
||||
Free Credits 第一档资金在**活动池**,不在普通 `withdraw` 余额里 → 典型报错 **`Insufficient balance`**。
|
||||
|
||||
**结论**:与需求「不进入普通钱包」冲突,第一档线上基本提不了现。
|
||||
|
||||
**建议**:`package_id > 0`(或 `bizType === free_credit_first_cashout`)时 **跳过** `checkInfo`(及与之绑定的首提 min/max 规则)。
|
||||
|
||||
---
|
||||
|
||||
## P0:Pay 失败时档位已置为 processing,无回滚
|
||||
|
||||
你现在在 **Pay 之前** 调用 `firstCashout` → `markFirstCashoutProcessing`(status=2)。
|
||||
|
||||
原实现是:mark → `PayService::apply` → **catch 时** `handleFirstCashoutResult($orderId, false)` 恢复 ready。
|
||||
|
||||
当前 [`WithdrawService::apply`](slot_console/app/service/WithdrawService.php) L236 调 pay **没有** try/catch 回滚 → 申请失败时 package 会一直 **processing**,用户无法重试。
|
||||
|
||||
**建议**:
|
||||
|
||||
```php
|
||||
if ($applyDTO->package_id > 0) {
|
||||
$orderId = CommonFn::generateOrderId(4, $userTag->uid);
|
||||
(new FreeCreditsLogic())->firstCashout($userTag->uid, $applyDTO->package_id, $orderId);
|
||||
$withdrawalInfo->orderId = $orderId;
|
||||
$withdrawalInfo->bizType = FreeCreditsLogic::BIZ_TYPE_FIRST_CASHOUT;
|
||||
try {
|
||||
$res = PayService::getInstance()->apply($withdrawalInfo->toArray());
|
||||
return $res;
|
||||
} catch (\Throwable $e) {
|
||||
(new FreeCreditsLogic())->handleFirstCashoutResult($orderId, false);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
(FC 分支应 **early return**,不要继续走下面黑规则 + `getAmountAndFee`。)
|
||||
|
||||
---
|
||||
|
||||
## P1:FC 仍走普通手续费 / 黑规则 / auditType
|
||||
|
||||
合并后 FC 路径仍执行:
|
||||
|
||||
- `getAuditType($applyDTO->amount)`、`BlackApiService`、提现倍数检测
|
||||
- `getAmountAndFee($applyDTO->amount, $this->withdrawal)` — 可能扣手续费、按钱包余额改金额
|
||||
|
||||
原 [`firstCashout`](slot_console/app/api/logic/FreeCreditsLogic.php) 约定:
|
||||
|
||||
- `fee = 0`
|
||||
- `auditType = 2`(人工审核)
|
||||
- `amount = package->amount_qf`(千分位,不经手续费逻辑)
|
||||
|
||||
`merge` 写入的 `amount` 是 **展示大单位**(`getNumberFormat(qf)`),再经 `getAmountAndFee` 可能与 pay 侧期望的千分位不一致。
|
||||
|
||||
**建议**:FC 分支单独组 `WithdrawalInfo`:
|
||||
|
||||
| 字段 | FC 取值 |
|
||||
|------|---------|
|
||||
| amount | 从 package 读 `amount_qf`(或 merge 时额外缓存 `_amount_qf`) |
|
||||
| fee | 0 |
|
||||
| auditType | 2 |
|
||||
| bizType | `free_credit_first_cashout` |
|
||||
| 黑规则 | 跳过(或产品确认是否要对 FC 也做) |
|
||||
|
||||
---
|
||||
|
||||
## P1:`firstCashout` 与 `markFirstCashoutProcessing` 重复校验
|
||||
|
||||
[`firstCashout`](slot_console/app/api/logic/FreeCreditsLogic.php) 内再次查 `STATUS_READY` 后调用 `markFirstCashoutProcessing`,而 `markFirstCashoutProcessing` ** again** 要求 `STATUS_READY`(L343)。
|
||||
逻辑重复但无害;可简化为只调 `markFirstCashoutProcessing`,或只保留一处校验。
|
||||
|
||||
注意:`mergeFirstCashoutIntoPost` 已校验 ready,到 `firstCashout` 时若并发重复提交,第二次会在 mark 阶段失败 — 符合「处理中不可重复提交」。
|
||||
|
||||
---
|
||||
|
||||
## P2:行为变化(需产品确认)
|
||||
|
||||
| 项 | 原 FC | 现统一 apply |
|
||||
|----|--------|----------------|
|
||||
| `is_bind_name` | 不校验 | **校验**(L157) |
|
||||
| Redis 5s 频控 | 无 | **有** |
|
||||
| 返回值 | `{order_id, amount}` | pay `apply` 原始结构 |
|
||||
|
||||
若 C 端依赖 `data.order_id` / `data.amount` 展示,需确认 pay 返回是否一致。
|
||||
|
||||
---
|
||||
|
||||
## 做得好的部分
|
||||
|
||||
- Controller 单入口 + merge post:**正确**。
|
||||
- 去掉独立 FC validator scene:**正确**。
|
||||
- [`FreeCreditsController::firstCashout`](slot_console/app/api/controller/FreeCreditsController.php) 兼容路径与主入口一致:**正确**。
|
||||
- pay 层仍靠 `bizType` 跳过 `withdrawFrozen`:**只要 bizType 确实传到 pay 就没问题**(当前 L184 已设)。
|
||||
|
||||
---
|
||||
|
||||
## 推荐修复结构(最小 diff)
|
||||
|
||||
在 [`WithdrawService::apply`](slot_console/app/service/WithdrawService.php) 开头(绑卡、频控之后):
|
||||
|
||||
```php
|
||||
if ($applyDTO->package_id > 0) {
|
||||
return $this->applyFreeCreditsFirstCashout($applyDTO, $bankInfo);
|
||||
}
|
||||
// 原有普通提现逻辑不变
|
||||
```
|
||||
|
||||
`applyFreeCreditsFirstCashout` 内:
|
||||
|
||||
1. 跳过 `checkInfo`、黑规则、`getAmountAndFee`
|
||||
2. `orderId` → `firstCashout`(mark)→ 组 `WithdrawalInfo`(qf amount, fee=0, auditType=2, bizType)
|
||||
3. try/catch pay + `handleFirstCashoutResult` 回滚
|
||||
|
||||
**不要**在普通流程中间用 `if ($package_id > 0) { firstCashout; }` 再接着跑普通逻辑 — 这是当前问题的根源。
|
||||
|
||||
---
|
||||
|
||||
## 验收清单
|
||||
|
||||
- [ ] `package_id>0` 且钱包 withdraw=0:能成功提交 pay(不报 Insufficient balance)
|
||||
- [ ] pay 申请失败:package 回到 ready,可再次提现
|
||||
- [ ] pay 成功 + 回调:package completed,player `FIRST_CASH_DONE`
|
||||
- [ ] 普通提现无 `package_id`:行为与改前一致
|
||||
- [ ] `remark`/bizType 在 pay 侧仍为 `free_credit_first_cashout`,不冻结钱包
|
||||
Reference in New Issue
Block a user