7.2 KiB
name, overview, todos, isProject
| name | overview | todos | isProject | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| firstCashout 合并代码评审 | 你对「merge post + 统一 WithdrawService::apply」的改法方向正确,但当前 WithdrawService 在 package_id>0 时仍执行 checkInfo/手续费/黑规则,且 Pay 失败无回滚,会导致第一档提现不可用或卡在 processing。 |
|
false |
firstCashout 合并改动 — 代码评审
你的改动(理解正确)
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:有package_id先 merge,再同一套validate($type)— 符合预期。mergeFirstCashoutIntoPost:补amount/bizType/package_id— 符合预期。firstCashout收窄为只markFirstCashoutProcessing+ 外部统一PayService::apply— 思路可行。
P0:FC 仍会走 checkInfo,大概率直接失败
WithdrawService::apply 当前顺序:
$bankInfo = $this->checkBankInfo($applyDTO);
$this->checkInfo($applyDTO->type, $applyDTO->amount); // 始终执行
// ...
if ($applyDTO->package_id > 0) {
firstCashout(...); // 永远走不到(checkInfo 已抛错)
}
checkInfo 会校验钱包可提现余额 ≥ amount(WithdrawService.php L333-335)。
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 L236 调 pay 没有 try/catch 回滚 → 申请失败时 package 会一直 processing,用户无法重试。
建议:
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 约定:
fee = 0auditType = 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 内再次查 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兼容路径与主入口一致:正确。- pay 层仍靠
bizType跳过withdrawFrozen:只要 bizType 确实传到 pay 就没问题(当前 L184 已设)。
推荐修复结构(最小 diff)
在 WithdrawService::apply 开头(绑卡、频控之后):
if ($applyDTO->package_id > 0) {
return $this->applyFreeCreditsFirstCashout($applyDTO, $bankInfo);
}
// 原有普通提现逻辑不变
applyFreeCreditsFirstCashout 内:
- 跳过
checkInfo、黑规则、getAmountAndFee orderId→firstCashout(mark)→ 组WithdrawalInfo(qf amount, fee=0, auditType=2, bizType)- 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,不冻结钱包