Files
cursor/plans/Plan-0abba57d.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

8.1 KiB
Raw Blame History


todos:

  • id: "expiry-service-mark-only" content: "TrialBalanceExpiryService新增 markTrialExpiredIfNeeded / isTrialPlayBlocked / ensureCanLaunchGame停止对外清零编排" status: pending
  • id: "ws-connect" content: "TrialBalanceExpiryConnectService仅打 tag + 弹窗,移除 clearExpiredTrialBalanceIfNeeded" status: pending
  • id: "entry-logic" content: "TrialRewardEntryLogic移除全路径清零修正 trial_balance_expired 判定" status: pending
  • id: "game-launch-guard" content: "GameLogic + GameController::login体验过期未充值时 BusinessException 拦截" status: pending
  • id: "constants-copy" content: "TrialRewardEntryConstants更新弹窗与 launch 拒绝文案" status: pending
  • id: "verify" content: "docker php -l + slot-backend-completion-report 自检" status: pending isProject: false

体验到期:仅标识、不清余额、禁止进游戏

背景与现状

当前 TrialBalanceExpiryConnectService 在 WS 连接时:

  1. 调用 clearExpiredTrialBalanceIfNeeded → wallet expireTrialBalance + activity 删 trial_reward_record
  2. 打 user tag trial_bonus_expired
  3. 推送弹窗文案写「Trial balance has been cleared」

TrialRewardEntryLogic 在 entry/status/withdraw-click 三条路径同样调用清零。

这与需求文档 03_wallet_fund_flow.md「不再产生 TRIAL_BONUS_EXPIRE」不一致。wallet 侧 isTrialPhase 在超期后已返回 false,体验金留在 frozen_bonus 但无法用于试玩下注;需在 console 启动接口 显式拦截。

目标行为

flowchart TD
    subgraph wsConnect [WS连接事件]
        A[未充值且体验期已过] --> B[打 user tag trial_bonus_expired]
        B --> C[推送 WS 弹窗一次]
        A --> D[不调用 wallet expireTrialBalance]
        A --> E[不调用 activity trial-balance/expire]
    end
    subgraph gameLaunch [game/launch 与 game/login]
        F[未充值且体验期已过] --> G[拒绝启动 BusinessException]
        H[已充值或仍在体验期内] --> I[正常 launch]
    end
场景 余额 activity 记录 user tag 进游戏
体验期内、未充值 保留 保留 允许
体验过期、未充值 保留 frozen_bonus 保留 标记过期 拒绝
已充值 按首充规则 按首充规则 不处理 允许

实现步骤

1. 收敛「到期处理」到仅标识(TrialBalanceExpiryService

文件:slot-console/app/service/trial/TrialBalanceExpiryService.php

  • 新增 markTrialExpiredIfNeeded(int $uid, string $source, string $currency, int $organization): bool
    • 已充值 → 直接返回 false
    • resolveWalletSnapshot + resolveTrialBalanceExpiry 未过期 → false
    • 已过期 → 仅 markTrialBonusExpiredUserTag($uid),返回 true
    • 调用 expireTrialBalance调用 markActivityTrialBalanceExpired
  • 新增 isTrialPlayBlocked(int $uid, string $source, string $currency, int $organization): bool(供 launch 守卫复用):
    • 逻辑:!has_recharged && resolveTrialBalanceExpiry(...).expired
  • 新增 ensureCanLaunchGame(...):当 isTrialPlayBlocked 为真时 throw new BusinessException(...)
  • clearExpiredTrialBalanceIfNeeded 保留方法体但 删除所有调用方(避免误用);类注释改为「到期判定与标识」,去掉「清零」表述
  • markActivityTrialBalanceExpired 可保留 private 方法(历史兼容),但本需求下不再被调用

2. 改写 WS 连接处理

文件:slot-console/app/service/trial/TrialBalanceExpiryConnectService.php

  • 删除 clearExpiredTrialBalanceIfNeededwasBalanceCleared / hadNoTrialBalance 分支
  • 流程简化为:
    1. user tag 已标记 → return幂等防重复弹窗
    2. 已充值 → return
    3. 未过期 → return
    4. markTrialBonusExpiredUserTag + WsService::notifyClientPOP + info 日志
  • 更新类 PHPDoc「检测试玩到期打 user tag、推送弹窗不清零余额

3. 入口 Logic 停止清零并修正 trial_balance_expired

文件:slot-console/app/api/logic/TrialRewardEntryLogic.php

  • buildEntryPayload / buildStatusPayload / resolveWithdrawClick 中:
    • 移除 clearExpiredTrialBalanceIfNeeded 调用及 trialBalanceBeforeClear 相关刷新逻辑
    • trial_balance_expired 改为:$trialExpiry['expired'] && !$walletSnapshot['has_recharged'](不再依赖 bonus_amount <= 0
  • 删除 private clearExpiredTrialBalanceIfNeeded 方法
  • 移除对 TrialExpiredBalanceClearCommand 的 useDTO 可保留文件以免大范围删除,但无引用)

4. game/launch 与 game/login 拦截

文件:slot-console/app/api/logic/GameLogic.php

  • 构造注入或懒加载 TrialBalanceExpiryService
  • launch() 开头调用 ensureCanLaunchGame($dto->uid, source, currency, organization)
    • source/currency/organization 从 GameLaunchDTOuserEntity 读取(与 wallet 快照现有参数对齐)
  • GameController::login 在调用 SlotPlatformService::login 前同样守卫deprecated 路径仍可能被 launchByGameId 使用)

错误处理:抛 Webman\Exception\BusinessException,消息使用新常量;api/exception/Handler 已注册,前端 useGameLaunch 会 toast 该 message。

5. 常量与文案

文件:slot-console/app/constants/TrialRewardEntryConstants.php

  • 更新 POPUP_MESSAGE_TRIAL_BALANCE_EXPIRED去掉「balance has been cleared」改为体验期结束、需充值后才能继续游戏英文与产品口径一致
  • 新增 GAME_LAUNCH_BLOCKED_MESSAGElaunch 拒绝文案,可与弹窗语义一致)

CLEAR_SOURCE_* 常量暂保留activity 接口仍存在,仅本链路不再调用)。

6. 不改动的服务

  • slot-wallet:不删 expireTrialBalance API历史兼容本需求不再调用
  • slot-activitytrial-balance/expire innerapi 保留,本需求不再调用
  • slot-pwa:按你的选择不拦截
  • lobby:依赖 launch API 错误 message 即可,无需必改

关键代码对照

WS 连接当前清零调用(将删除):

        $clearOutcome = $this->trialBalanceExpiryService->clearExpiredTrialBalanceIfNeeded(
            new TrialExpiredBalanceClearCommand(
                $uid,
                $channelSource,
                $currency,
                $organization,
                $walletSnapshot,
                []
            ),
            TrialRewardEntryConstants::CLEAR_SOURCE_WS_CONNECT
        );

入口当前过期判定(将修改):

            'trial_balance_expired' => $walletSnapshot['bonus_amount'] <= 0 && $trialExpiry['expired'],

验证要点

  1. 模拟未充值用户、wallet.created_at 超过配置 validity_hoursWS 连接一次 → user tag 写入、收到弹窗、bonus 余额不变、无 TRIAL_BONUS_EXPIRE 流水
  2. 再次 WS 连接 → 无重复弹窗tag 幂等)
  3. 同用户调用 POST /api/game/launch → BusinessException不返回 url
  4. 同用户 GET /api/trial-reward/entrytrial_balance_expired=truetrial_balance 仍 > 0
  5. 已充值用户超体验窗口 → launch 正常、WS 不弹窗
  6. 收尾跑 slot-backend-completion-report 与 verify 脚本