Files
cursor/plans/修复第一档绑卡类型_1c86ae8f.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

5.5 KiB
Raw Blame History

name, overview, todos, isProject
name overview todos isProject
修复第一档绑卡类型 `WithdrawService::apply` 在 Free Credits 第一档(`package_id > 0`)分支把 `checkBankInfo` 返回的收款配置数组传给了需要 `UserBankCardModel` 的 `applyFreeCreditsFirstCashout`,导致 PHP 8 类型错误且 Pay 载荷取不到 `btc/usdt/email` 等字段。
id content status
refactor-checkBankInfo-return WithdrawService::checkBankInfo 返回 ['model' => UserBankCardModel, 'config' => itemConfig] 并补 PHPDoc cancelled
id content status
fix-apply-branch WithdrawService::apply 解构返回值FC 传 model普通提现用 config cancelled
id content status
run-integration-test php82 跑 FreeCreditsFirstCashoutApplyTest 验证无 TypeError completed
false

修复第一档提现绑卡类型不匹配

问题

WithdrawService::apply 中:

        $bankInfo = $this->checkBankInfo($applyDTO);

        if ($applyDTO->package_id > 0) {
            return $this->applyFreeCreditsFirstCashout($applyDTO, $bankInfo);
        }

checkBankInfo 在持久化 UserBankCardModel返回的是按支付类型整理的 $itemConfig 数组(约 353 行 return $itemConfig),供普通提现填 WithdrawalInfo

        $withdrawalInfo->account = $bankInfo['account'];
        $withdrawalInfo->address = $bankInfo['account'];
        $withdrawalInfo->userName = $bankInfo['user_name'] ?? $userTag->uid;
        $withdrawalInfo->card_number = $bankInfo['card_number'] ?? '';

applyFreeCreditsFirstCashout / FreeCreditsLogic::buildFirstCashoutWithdrawalInfo 签名要求 UserBankCardModel,并读取模型字段:

            'userName' => $post['user_name'] ?? $bank->user_name ?? strval($uid),
            'account' => empty($bank->card_number) ? strval($uid) : $bank->card_number,
            ...
        if ($payType === 2) {
            $withdrawalInfo->address = $bank->btc ?? '';
        } elseif ($payType === 3) {
            $withdrawalInfo->address = $bank->usdt ?? '';
        } elseif ($payType === 6) {
            $withdrawalInfo->userName = trim($bank->paypal_first_name . ' ' . $bank->paypal_last_name);
            $withdrawalInfo->account = $bank->email ?? '';

因此第一档路径在 PHP 8 下会触发 TypeError;即便未开严格类型,用数组当对象也会导致 Pay 下单字段错误。

flowchart LR
  apply[WithdrawService.apply]
  checkBank[checkBankInfo]
  saveModel[save UserBankCardModel]
  returnConfig["return itemConfig array"]
  fcBranch[package_id greater than 0]
  normalBranch[normal withdraw]
  fcMethod[applyFreeCreditsFirstCashout]
  logic[buildFirstCashoutWithdrawalInfo]
  apply --> checkBank
  checkBank --> saveModel --> returnConfig
  returnConfig --> fcBranch
  returnConfig --> normalBranch
  fcBranch -->|"wrong: array"| fcMethod
  fcMethod --> logic
  logic -->|"expects UserBankCardModel"| modelFields[bank.card_number btc usdt]

推荐改法(单文件、无二次查询)

WithdrawService.php 调整 checkBankInfo 返回值,使 apply 能同时满足两条路径:

  1. checkBankInfo 返回结构体(关联数组即可,无需新 DTO

    • model → 已 save()UserBankCardModel
    • config → 现有 $itemConfig(普通提现继续用)
  2. apply 解构

    • package_id > 0applyFreeCreditsFirstCashout($applyDTO, $bank['model'])
    • 否则 → $bankConfig = $bank['config'],后续 account / user_name / card_number 逻辑不变
  3. 补 PHPDoc(符合 php-doc.mdc

    • checkBankInfo: @return array{model: UserBankCardModel, config: array<string, mixed>}
    • applyFreeCreditsFirstCashout: 保持现有 @param UserBankCardModel

不改动 FreeCreditsLogic:其契约(绑卡已由 WithdrawService 写入后传入模型)是正确的。

备选(更小 diff多一次查询

若不想改 checkBankInfo 签名,可在 package_id > 0 分支先调用 checkBankInfo 落库,再 find($uid) 取模型。可行但冗余,不推荐。

测试

  • 现有集成测 FreeCreditsFirstCashoutApplyTest 已走 WithdrawService::apply + package_id;修复后应能越过 TypeError,继续表现为 Pay 不可用时的 Throwable / 余额校验未触发。
  • 修复后在 php82 容器执行:
docker exec -w /app/www/slot/slot_console php82 ./vendor/bin/phpunit tests/Integration/FreeCreditsFirstCashoutApplyTest.php

(需 RUN_DB_TESTS=1 时按项目惯例配置环境变量。)

可选:在 tests/Unit 增加轻量测试,用反射调用 checkBankInfo 断言返回含 modelconfig 键——非必须,集成测已覆盖主路径。

验收

  1. 第一档 POST /api/withdraw/apply + package_id 不再出现 UserBankCardModel 类型错误。
  2. 普通提现(无 package_id)行为与字段映射不变。
  3. Pay 下单能正确带上 card_number / btc / usdt / PayPal 等模型字段(与 checkBankInfo 写入一致)。