Files
cursor/plans/第一档提现审核拒绝_843062ca.plan.md
ray zhou f71a5c59af ok
2026-05-29 11:21:40 +08:00

8.4 KiB
Raw Blame History

name, overview, todos, isProject
name overview todos isProject
第一档提现审核拒绝 补齐管理后台「拒绝退钱/拒绝扣钱」对 Free Credits 第一档独立提现free_credit_first_cashout的处理Pay 侧识别业务类型、避免误动钱包,并通过已有 Console 总线回调恢复档位状态。
id content status
persist-biz-type WithdrawalOrderEntity::creatOrder 写入 data_snapshot.biz_type新增 isFreeCreditFirstCashout 判断 in_progress
id content status
apply-skip-freeze apply() 第一档跳过 withdrawFrozen 与 incTodayWithdrawalInfo pending
id content status
audit-reject-branch audit() 拒绝退钱/扣钱:第一档跳过钱包,发 Fail/Rejected 总线;普通单保持原逻辑 pending
id content status
mq-constants slot_lib MQBusEntity 增加三条 FreeCredits 事件常量 pending
id content status
event-withdrawal EventWithdrawal 成功/失败按 biz_type 发 Success/Fail 总线,避免误 withdrawSuccess/Fail pending
id content status
tests 补充 slot_pay 单测 + 手工验证后台拒绝退钱/扣钱 pending
false

第一档提现:后台审核拒绝处理

问题确认

管理后台 slot_admin_vue/.../withdrawal/index.vue 点「拒绝退钱」(audit_status=2) / 「拒绝扣钱」(audit_status=3) → WithdrawalOrderController::auditPayService::withdrawalAuditWithdrawalOrderEntity::audit

当前 audit() 一律对普通提现调用 withdrawFail / withdrawDone通知 slot_consoleFreeCreditsLogic::handleFirstCashoutResult

第一档提现(FreeCreditsLogic::applyFirstCashoutWithdrawbizType=free_credit_first_cashout)在提交时已 markFirstCashoutProcessing,审核拒绝后档位会卡在 processingC 端无法再次提现。

需求文档约定(§20.2

后台操作 产品语义 应对 handleFirstCashoutResult
拒绝退钱 失败,可重试 success=false, rejected=false → 档位 ready
拒绝扣钱 风控拒绝 success=false, rejected=true → 档位 rejected

slot_console 已有消费端(EventBus.php L133140全库无生产端 发送 FreeCreditsFirstCashoutFail / Rejected / Success

flowchart TB
  subgraph today [现状]
    adminReject[后台拒绝退钱]
    payAudit[Pay audit]
    walletFail[withdrawFail]
    adminReject --> payAudit --> walletFail
  end
  subgraph target [目标]
    adminReject2[后台拒绝退钱]
    payAudit2[Pay audit 识别 bizType]
    skipWallet[跳过钱包退冻]
    mqFail[MQ FreeCreditsFirstCashoutFail]
    fcLogic[handleFirstCashoutResult ready]
    adminReject2 --> payAudit2 --> skipWallet
    payAudit2 --> mqFail --> fcLogic
  end

改造范围(主改 slot_pay,常量补 slot_lib

1. 订单落库时持久化 biz_type

文件:slot_pay/app/entity/WithdrawalOrderEntity.phpcreatOrder()

  • WithdrawalInfo::$bizType 非空,写入 data_snapshot.biz_type(表无独立字段,用现有 JSON 即可)。
  • 常量与 C 端一致:free_credit_first_cashout(见 FreeCreditsLogic::BIZ_TYPE_FIRST_CASHOUT)。

新增私有方法(同文件):

private function isFreeCreditFirstCashout(?WithdrawalInfo $info = null, ?self $order = null): bool
  • 优先读 $info->bizType;审核/回调阶段读 $order->data_snapshot['biz_type']

2. 申请提现:第一档不冻普通钱包

文件:同上 — apply()

  • isFreeCreditFirstCashout($info) 为 true 时:
    • 跳过 withdrawFrozenUserTagService::incTodayWithdrawalInfo
    • creatOrder、人工审核分支、WebSocketMqService::sendWithdrawalUrl 保持不变。
  • catch 中仅当曾冻结时才 withdrawFail(与现逻辑一致,第一档不会进入)。

对齐 WithdrawalInfo 注释 与需求「第一档不进入普通钱包 withdraw」。

3. 审核拒绝/扣钱:分支 + 发总线(核心)

文件:同上 — audit()

audit_status 设为 2 或 3 后:

条件 钱包 Console 总线 type handleFirstCashoutResult
普通提现 + 拒绝退钱 withdrawFail
普通提现 + 拒绝扣钱 withdrawDone
第一档 + 拒绝退钱 不调钱包;不调 decTodayWithdrawalInfo FreeCreditsFirstCashoutFail ready可重提
第一档 + 拒绝扣钱 不调钱包 FreeCreditsFirstCashoutRejected rejected

实现方式Pay 依赖已有 ConsoleMqService

use slotLib\common\mq\ConsoleMqService;
// audit 拒绝分支内:
if ($this->isFreeCreditFirstCashout(null, $order)) {
    $event = $type === self::AUDIT_STATUS_REVIEW
        ? MQBusEntity::TYPE_FREE_CREDITS_FIRST_CASHOUT_FAIL
        : MQBusEntity::TYPE_FREE_CREDITS_FIRST_CASHOUT_REJECTED;
    ConsoleMqService::getInstance()->sendConsoleBusEvent($order->uid, $event, [
        'order_id' => $order->order_id,
    ]);
} else {
    // 现有 withdrawFail / withdrawDone + decTodayWithdrawalInfo
}

站内信逻辑可保留(第一档同样通知用户)。

4. 总线常量集中到 slot_lib

文件:slot_lib/src/entity/mq/MQBusEntity.php

新增(与 console 现有字符串一致):

  • TYPE_FREE_CREDITS_FIRST_CASHOUT_SUCCESS = 'FreeCreditsFirstCashoutSuccess'
  • TYPE_FREE_CREDITS_FIRST_CASHOUT_FAIL = 'FreeCreditsFirstCashoutFail'
  • TYPE_FREE_CREDITS_FIRST_CASHOUT_REJECTED = 'FreeCreditsFirstCashoutRejected'

slot_console/app/entity/mq/MQBusEntity.php 可改为 use slotLib\entity\mq\MQBusEntity as LibMQBusEntity 引用常量(可选,避免双份字符串)。

5. 顺带补齐:打款结果回调(建议同 PR

EventWithdrawal::updateOrder() 在成功/失败时同样未区分第一档,会误调 withdrawSuccess / withdrawFail

在同一 PR 中按 data_snapshot.biz_type 分支:

  • 成功 → 发 FreeCreditsFirstCashoutSuccess withdrawSuccess
  • 失败 → 发 FreeCreditsFirstCashoutFail withdrawFail
  • 普通单保持现状

否则后台「通过」后渠道失败/成功,档位状态仍会错乱。

不改动的部分

  • 管理后台 UI:无需改,仍调 audit(record, 2|3)
  • FreeCreditsLogic::handleFirstCashoutResult:逻辑已满足需求,只补 Pay 侧触发。
  • 数据库 DDL:用 data_snapshot,无需加列。

历史订单

已产生、且 data_snapshotbiz_type 的第一档单:

  • 拒绝时仍会走旧 withdrawFail(若当时已冻钱包则退钱正确,但档位仍卡 processing
  • 运维可对已知 order_id 在 console 手工调用 handleFirstCashoutResult,或一次性 SQL 回填 data_snapshot.biz_type(按 free_credits_package.withdraw_order_id 关联)。

测试

层级 内容
slot_pay 单测 creatOrder 写入 biz_typeisFreeCreditFirstCashoutaudit(2) 第一档 mock 不发 withdrawFail、发 MQmock ConsoleMqService
slot_console 已有 FreeCreditsLogicCashoutResultTest 覆盖 handleFirstCashoutResult
手工 后台对第一档人工单:拒绝退钱 → C 端档位回 ready 可再提;拒绝扣钱 → rejected

涉及文件小结