Files
cursor/plans/is_new_user recommend-game-09c08dbd.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

12 KiB
Raw Permalink Blame History


todos:

  • id: "user-is-new-user" content: "slot-userAbstractRegisterService 增加 isNewUser 标志Mobile/Imei/Name RegisterService 赋值UserController::register 返回 is_new_user" status: pending
  • id: "console-ggame-query" content: "slot-consoleGGame 新增按 game_code 查可展示单条游戏方法" status: pending
  • id: "console-recommend-logic" content: "slot-consoleOnboardingRecommendLogiccenter 取池 → 随机 → g_game 组装 → 降级)" status: pending
  • id: "console-recommend-api" content: "slot-consoleOnboardingController + GET /api/onboarding/recommend-game" status: pending
  • id: "update-docs" content: "更新 register_popup_game_entry 文档:前端 is_new_user 条件请求 recommend-game 链路" status: pending
  • id: "lobby-auth-types" content: "lobbyAuthResponse 增加 is_new_userauth.service 注册后按标志决定是否拉 recommend-game" status: pending
  • id: "lobby-onboarding-api" content: "lobby新增 api/console/onboarding.api.ts 调用 GET /slot-console/api/onboarding/recommend-game" status: pending
  • id: "lobby-onboarding-popup" content: "lobby新用户注册成功后展示 OnboardingGamePopup倒计时 + LET'S PLAY + 关闭)" status: pending isProject: false

is_new_user + console recommend-game 方案

方案评价

该方案推荐采用,理由:

  • 符合 SSOT register_popup_game_entry.md 服务边界user 管身份console 管展示聚合
  • 前端仍直连 slot-user 注册,无需改注册入口
  • MobileRegisterService / ImeiRegisterService 已有「账号存在则走登录」逻辑,正好需要 is_new_user 区分
  • 老用户误走 register 接口时不会触发 onboarding 弹窗
  • 注册主链路不受 center/console 降级影响(推荐游戏失败只影响弹窗)
sequenceDiagram
    participant FE as lobby_PWA
    participant User as slot_user
    participant Console as slot_console
    participant Center as slot_center
    participant GGame as g_game

    FE->>User: POST register
    alt 新用户创建
        User-->>FE: token + uid + is_new_user=true
        FE->>Console: GET recommend-game (Bearer token)
        Console->>Center: innerapi onboarding/channel-game
        Center-->>Console: game_codes
        Console->>GGame: 按 game_code 查可展示游戏
        Console-->>FE: onboarding payload
    else 老用户登录回退
        User-->>FE: token + uid + is_new_user=false
        Note over FE: 不请求 recommend-game
    end

Part 1slot-user 增加 is_new_user

涉及仓库/Users/ray/Documents/project/www/slot/slot_user

现状

MobileRegisterService.phpImeiRegisterService.phpcheck() 返回 false 时会走 MobileLoginService / ImeiLoginService,但 Controller 无法区分「新建」与「登录回退」。

NameRegisterService.php 账号已存在直接返回 null报错成功则一定是新用户。

实现方式(最小改动)

AbstractRegisterService.php 增加:

/** 本次 register 请求是否真正创建了新用户false 表示走了登录回退) */
public bool $isNewUser = false;

各 RegisterService 在对应分支赋值:

类型 新用户创建 登录回退
mobile isNewUser = true isNewUser = false
imei isNewUser = true isNewUser = false
name isNewUser = true N/A失败返回 null

UserController::register() 响应中追加:

$data['is_new_user'] = $registerService->isNewUser;

login 接口不改(老用户走 login 本就不弹窗)。

注册响应示例

{
  "token": "...",
  "uid": 1387459,
  "expires_in": 1234567890,
  "is_new_user": true
}

Part 2slot-console 实现 recommend-game BFF03 子需求)

涉及仓库/Users/ray/Documents/project/www/ray/slot-console

路由

沿用 Webman 自动路由(参照 RewardGrantController

GET /api/onboarding/recommend-game

需登录JWT$request->userEntity->uid / $request->userEntity->source 取用户上下文。

核心 Logic新建

app/api/logic/OnboardingRecommendLogic.php 编排步骤(对齐需求 §5.1

  1. uidsourcesource 为空按 DEFAULT 交给 center 回退)
  2. CenterClientonboardingChannelGame($source)SDK 已有:CenterService.php
  3. enabled=falsegame_codes 为空 → 返回降级 payload
  4. game_codes 简单随机选一个;查 GGame 可展示游戏
  5. 不可用则剔除后继续随机;全部不可用 → enabled=false
  6. 组装返回(字段见需求 §6.1

GGame 扩展

GGame.php 新增按 game_code 查单条可展示游戏方法(复用 applyLobbyListableJoinsAndWhere,返回 id/name/icon_url/provider_code/game_code/category)。

可参照 slot-admin OnboardingChannelGameLogic::resolveGamesByGameCodes 的字段映射,但用户侧只返回需求文档定义的 game 结构。

GatewayService可选薄封装

app/service/center/CenterGatewayService.php:封装 CenterClient 初始化host 来自 ShareConfigService::get('centerApiHost')header server-nameLogic 不直接拼 HTTP。

Controller新建

app/api/controller/OnboardingController.php

  • recommendGame(Request $request) → 调 Logic → success($payload)
  • center 调用失败 / 异常:记录日志,返回 enabled=false不抛错阻断

展示字段来源

字段 来源
uid / player_number JWT uid
player_number_display number_format($uid)
platform_name ShareConfigService::sourceInfo($source) 或 center 渠道配置(首版可用 config/常量,与运营名对齐)
trial_bonus_amount 活动配置常量 20000$20 × 1000对齐 03_wallet_fund_flow.md
countdown_seconds 有游戏时 3,无游戏时 0
game GGame 查询结果

Part 3lobby 前端接入(游戏大厅 PWA

涉及仓库/Users/ray/Documents/project/www/ray/lobby

这就是你的前端应用:Vue 3 + Vite + Vant,网关前缀通过 VITE_APP_API_BASE_URL 代理到各 slot 服务。

现有注册链路(已确认)

位置
注册 API lobby/src/api/user/auth.api.tsPOST /slot-user/api/user/register
注册编排 lobby/src/services/user/auth.service.tssetAuth(result) 存 token
类型定义 lobby/src/types/user/auth.d.ts(目前只有 token 字段, is_new_user
Console API 范例 lobby/src/api/console/console.ts/slot-console/api/...
注册入口(需统一改) telregisterbox.vueemailregisterbox.vuelogin/index.vueauth/telegram-callback/index.vueregister/index.vue

典型流程(以手机注册为例):

userAuthService.register(...) → setAuth → initializeUserSession() → router.push('/')

弹窗现有模式可参考 WS Notify.popupnotifyPopupPayload.ts + DailySignInBox.vue),但 onboarding 不走 WS,注册成功后 HTTP 主动拉取

lobby 改动点

1. 类型与 API

  • AuthResponse 增加 is_new_user?: booleanuid?: number
  • 新建 lobby/src/types/console/onboarding.d.ts(对齐需求 §6.1 payload
  • 新建 lobby/src/api/console/onboarding.api.ts
GET /slot-console/api/onboarding/recommend-game

2. 注册编排(集中在一处,避免各页面重复)

扩展 auth.service.ts

register(data):
  1. result = registerAPI(data)
  2. setAuth(result)
  3. if result.is_new_user === true:
       onboarding = await fetchRecommendGame()  // 已 setAuthrequest 拦截器会自动带 Authorization
       return { ...result, onboarding }
  4. return result

recommend-game 失败时 不阻断注册onboarding.enabled = false,正常进首页。

3. 弹窗 UI

新建组件如 lobby/src/components/OnboardingGamePopup.vue(或 pages/home/components/

  • enabled=true 时展示:玩家编号、trial_bonus_amount、游戏封面、countdown_seconds 倒计时
  • LET'S PLAY → 用 game.id 走现有进游戏链路(game.api.ts / 首页 launch 逻辑)
  • 关闭 → 仅关弹窗,不调后端

4. 触发时机

在注册成功且 is_new_user 的分支里,跳转首页前或首页 onMounted 展示弹窗(推荐:注册页拿到 onboarding 后 router.push('/') + 通过 pinia/commonStore 或 route state 传给首页展示,避免路由切换丢状态)。

各注册入口最终都应走 userAuthService.register,无需每个页面单独写 recommend 逻辑。

前端调用链路lobby

1. lobby → POST /slot-user/api/user/register
2. is_new_user === true
     lobby → GET /slot-console/api/onboarding/recommend-game
     enabled=true → OnboardingGamePopup
3. is_new_user === false → 正常登录,无弹窗
4. initializeUserSession() → 进大厅首页 /

Part 4文档更新

更新 03_console_recommend_game_bff.md 与父文档 §3.1:明确前端为 lobby PWA,注册后条件请求 recommend-game。

注意:后端不记录弹窗状态;刷新后是否再弹由 lobby 本地逻辑决定(需求 §8.3)。


Part 5不在本次范围

  • user 不调 center / console innerapi按你确认的方案
  • 不新增 user_onboarding_popup
  • 不实现 mark_shown / mark_entered
  • login 接口不加 is_new_user(非必须)

验收要点

后端

  1. 新手机号/新 IMEI 注册 → is_new_user=true;同账号再次 register → is_new_user=false
  2. is_new_user=true 时 recommend-game 返回渠道游戏池随机一款(或 DEFAULT 回退)
  3. center 无配置 / 调用失败 → enabled=false,注册仍成功
  4. game_codes 含下架游戏 → 跳过,从剩余随机

lobby 前端

  1. 老用户走 register 登录回退(is_new_user=false)→ 不请求 recommend-game无弹窗
  2. 新用户注册成功 → 自动请求 recommend-gameenabled=true 时展示 onboarding 弹窗
  3. 点击 LET'S PLAY → 进入推荐游戏;点击关闭 → 留在大厅,不影响 Trial Balance
  4. 各注册入口(手机/邮箱/Telegram 等)行为一致(均经 userAuthService.register