7.4 KiB
7.4 KiB
name, overview, todos, isProject
| name | overview | todos | isProject | |||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| firstCashout 合并提现 | 可以合并:推荐在 Withdraw 入口与 Pay 下单层统一,用 package_id 区分;活动档位状态机仍留在 FreeCreditsLogic,不能把 FC 逻辑硬塞进 WithdrawService::apply 主流程中间。 |
|
false |
firstCashout 能否合并进现有提现逻辑
结论(后端)
可以合并,且推荐合并「入口 + 校验 + 绑卡 + Pay 下单」;不应把 Free Credits 编排塞进 WithdrawService::apply 主流程中间。
当前仓库状态:尚未合并——仍是双入口:
| 入口 | 现状 |
|---|---|
POST /api/free-credits/first-cashout |
FreeCreditsLogic::firstCashout 完整编排 |
POST /api/withdraw/apply |
WithdrawService::apply,无 package_id |
Pay 层已经统一:两类提现最终都走 WithdrawalOrderEntity::apply,靠 bizType=free_credit_first_cashout 跳过 withdrawFrozen 并走独立 MQ 回调。
为什么「可以」合并
C 端参数与需求 §11 一致:独立提现页 = 普通提现页精简版,收款字段相同,仅差:
| 字段 | 普通提现 | Free Credits 第一档 |
|---|---|---|
amount |
用户输入 | 不传,服务端取 package |
package_id |
无 | 必填 |
因此用 同一 POST /api/withdraw/apply + 可选 package_id 区分业务是合理契约。
为什么不能「整段并入」WithdrawService::apply
WithdrawService::apply 当前顺序(L164–219):
checkBankInfocheckInfo(校验钱包 withdraw 余额 ≥ amount)- 黑规则、
getAmountAndFee(手续费) PayService::apply
Free Credits 第一档资金在活动池,不在普通 withdraw 余额。若在中间插入 firstCashout 而不 early return,会先被 checkInfo 打成 Insufficient balance(评审计划已记录为 P0)。
此外 FC 固定规则与普通提现不同:
| 项 | 普通提现 | FC 第一档 |
|---|---|---|
| 金额来源 | 用户 amount |
package->amount_qf |
| 手续费 | getAmountAndFee |
0 |
| 审核 | 动态 getAuditType + 黑规则 |
auditType=2 |
| 钱包冻结 | 有 | 无(pay 侧 bizType 分支) |
| 活动状态 | 无 | markFirstCashoutProcessing / 失败回滚 |
| Pay 失败回滚 | 钱包解冻 | handleFirstCashoutResult 恢复 ready |
这些差异属于 Logic 编排,符合 backend-layering:不应把 FreeCreditsLogic 整段搬进 WithdrawService 当「又一层 Service」。
推荐合并结构
flowchart TB
Client["POST /api/withdraw/apply"]
Client --> Branch{package_id 存在?}
Branch -->|否| Normal["WithdrawService::apply 原逻辑"]
Branch -->|是| FC["WithdrawService::applyFreeCreditsFirstCashout"]
FC --> Mark["FreeCreditsLogic::markFirstCashoutProcessing"]
FC --> Pay["PayService::apply bizType=free_credit_first_cashout"]
Normal --> Pay2["PayService::apply 普通"]
Pay --> PayEntity["slot_pay WithdrawalOrderEntity"]
Pay2 --> PayEntity
合并层(推荐做)
-
- 有
package_id→ 走 FC 分支(或 DTO 带package_id后交给 Service early return) - 无
package_id→ 现有普通提现
- 有
-
- 新增 FC scene:与普通 apply 对称,把
amount换成package_id(SCENE_APPLY_CASH_FC等)
- 新增 FC scene:与普通 apply 对称,把
-
- 增加可选
package_id
- 增加可选
-
绑卡
- FC 复用
WithdrawService::checkBankInfo(当前firstCashout只读绑卡、不写库,合并后应对齐普通提现)
- FC 复用
-
FreeCreditsController::firstCashout- 保留为 兼容 alias(内部转调 withdraw apply),或标记 deprecated
保持分离(必须)
FreeCreditsLogic:markFirstCashoutProcessing、handleFirstCashoutResult、package 状态机、assertEligibleParticipantWithdrawService::applyFreeCreditsFirstCashout(新建私有方法):- 跳过
checkInfo、黑规则、getAmountAndFee amount = package->amount_qf,fee = 0,auditType = 2- try/catch Pay,失败时
handleFirstCashoutResult($orderId, false)
- 跳过
已统一、无需再改
- slot_pay:
bizType/remark分支(跳过冻结、Success/Fail/Rejected → Console MQ) - slot_console EventBus:
FreeCreditsFirstCashoutSuccess/Fail/Rejected→handleFirstCashoutResult
不推荐的做法
在 WithdrawService::apply 中间写:
$this->checkInfo(...);
// ...
if ($package_id > 0) {
(new FreeCreditsLogic())->firstCashout(...);
}
$amountInfo = $this->getAmountAndFee(...);
PayService::apply(...);
会导致:余额校验失败、手续费错误、Pay 失败不回滚档位(processing 卡死)。
与「单独提现」需求的关系
| 需求 | 合并后是否满足 |
|---|---|
| 不进入普通钱包(§15.3) | 是,仍靠 pay bizType |
| 固定第一档金额(§11.3) | 是,服务端取 package |
| 处理中不可重复提交(§5.6) | 是,仍由 package status 控制 |
| 成功/失败/拒绝状态(§20.2) | 是,回调逻辑不变 |
合并的是 HTTP 入口与收款参数校验,不是改掉「独立提现」的账务语义。
实施要点(若执行)
WithdrawApplyDTO增加package_idWithdrawService::apply在绑卡 + Redis 频控后 early return 到applyFreeCreditsFirstCashout- FC 分支内:先
markFirstCashoutProcessing,再 Pay,catch 回滚 - 单测:FC 在
withdraw=0时可提交;Pay 失败 package 回 ready;普通 apply 无回归 - 文档:
/api/free-credits/first-cashout→ 指向/api/withdraw/apply+package_id
验收清单
package_id>0且钱包可提现余额为 0:可成功提交 pay- pay 申请失败:package 回到
ready,可重试 - pay 回调成功:package
completed,playerFIRST_CASH_DONE - 不传
package_id:普通提现与现网一致 - pay 订单
remark仍为free_credit_first_cashout,不触发withdrawFrozen