hyapp-server/docs/app-entry-state-machine.md
2026-05-06 21:22:39 +08:00

13 KiB
Raw Blame History

App Entry State Machine

本文定义用户从打开 App 到进入主界面的完整状态机。它只描述 App 入口流程、接口调用顺序、跳转条件和异常恢复边界;登录注册细节见 Login And Registration Technical Design,房间重连细节见 Room Entry Reconnect Development Requirements

Goals

  • 客户端启动时用确定的状态机决定去登录页、资料补全页、主界面或维护/升级页。
  • 未完成资料的用户不能进入房间、IM、RTC、钱包和付费流程。
  • access token 过期时优先 refreshrefresh 失败才回登录页。
  • 主界面首屏只调用轻量一级接口:房间、消息、我的。
  • 腾讯云 IM 登录必须在 profile_completed=true 后发生,避免未完成资料用户进入实时系统。
  • 房间恢复必须独立于房间列表,不能靠列表猜测用户是否还在某个房间。

Current Interface Inventory

Capability Endpoint Status Rule
三方登录即注册 POST /api/v1/auth/third-party/login DONE 返回 is_new_user/profile_completed/onboarding_status
refresh token POST /api/v1/auth/token/refresh DONE 成功后轮换 refresh token
资料补全 POST /api/v1/users/me/onboarding/complete DONE 成功后返回新的 access token不轮换 refresh token
注册国家 GET /api/v1/countries DONE 登录前可用
我的页首屏 GET /api/v1/users/me/overview DONE 主界面轻聚合
房间列表 GET /api/v1/rooms?tab=hot/new DONE 需要 completed profile
消息 tab GET /api/v1/messages/tabs DONE 需要 completed profile
腾讯 IM UserSig GET /api/v1/im/usersig DONE 需要 completed profile
App 启动配置 GET /api/v1/app/bootstrap DONE 版本、维护、feature flags、配置版本
当前房间恢复 GET /api/v1/rooms/current DONE 判断是否有可恢复房间
推送 token 绑定 POST /api/v1/devices/push-token / DELETE /api/v1/devices/push-token DONE 离线消息触达

Top Level State Machine

stateDiagram-v2
  [*] --> Launch
  Launch --> Bootstrap
  Bootstrap --> ForceUpgrade
  Bootstrap --> Maintenance
  Bootstrap --> TokenCheck

  TokenCheck --> LoginRequired: no local token
  TokenCheck --> AuthenticatedProbe: access token exists

  AuthenticatedProbe --> MainReady: overview ok and profile completed
  AuthenticatedProbe --> Refreshing: access token expired or 401
  AuthenticatedProbe --> ProfileRequired: PROFILE_REQUIRED or token says profile incomplete

  Refreshing --> AuthenticatedProbe: refresh ok
  Refreshing --> LoginRequired: refresh failed

  LoginRequired --> LoginOrRegister
  LoginOrRegister --> ProfileRequired: login ok but profile incomplete
  LoginOrRegister --> MainReady: login ok and profile completed

  ProfileRequired --> CompleteOnboarding
  CompleteOnboarding --> MainReady: success with replacement access token

  MainReady --> MainTabs
  MainTabs --> IMLogin
  MainTabs --> RoomRestoreProbe

Launch Sequence

启动顺序:

  1. App 启动,生成或读取 stable device_id
  2. GET /api/v1/app/bootstrap,以 bootstrap 返回的版本、维护、feature flags 和配置版本作为入口决策事实。
  3. 如果 bootstrap 返回强更,进入强更页。
  4. 如果 bootstrap 返回维护中,进入维护页。
  5. 检查本地是否有 access token。
  6. 没有 token进入登录页。
  7. 有 token调用 GET /api/v1/users/me/overview 做认证探测和首屏数据读取。

不要只靠本地缓存判断是否进入主界面。用户可能被禁用、token 可能过期、资料完成状态可能已经变化。

Bootstrap Contract

当前接口:

GET /api/v1/app/bootstrap

登录前也可调用。请求必须携带 App 解析头:

Header Meaning
X-App-Code / X-HY-App-Code 已知内部 app_code
X-App-Package / X-Package-Name 包名或 Bundle ID
X-App-Platform / X-Platform android or ios
X-App-Version 客户端版本
X-Build-Number 构建号

响应字段:

{
  "app_code": "lalu",
  "server_time_ms": 1777996800000,
  "force_upgrade": false,
  "maintenance": false,
  "minimum_version": "1.0.0",
  "latest_version": "1.2.0",
  "feature_flags": {
    "wallet_recharge": true,
    "wallet_withdraw": true,
    "diamond_exchange": true,
    "message_tab": true,
    "host_center": true,
    "room_create": true
  },
  "bottom_tabs": [
    {"key": "rooms", "title": "房间", "enabled": true},
    {"key": "messages", "title": "消息", "enabled": true},
    {"key": "me", "title": "我的", "enabled": true}
  ],
  "config_versions": {
    "countries": "v1",
    "banners": "v1",
    "h5_links": "v1",
    "resources": "v1",
    "gifts": "v1"
  }
}

bootstrap 只返回配置摘要和版本号,不返回完整礼物、背包资源或 banner 列表。客户端发现版本变化后再拉对应配置接口。

Authentication Probe

有 access token 时,首选调用:

GET /api/v1/users/me/overview

结果处理:

Result Client Action
200 OK 使用 overview 进入主界面
401 AUTH_REQUIRED / token expired 调 refresh
403 PROFILE_REQUIRED 进入资料补全页
403 USER_DISABLED/BANNED 进入账号异常页
5xx/502 展示重试页,不清 token

overview 失败时不要直接清空登录态。只有 refresh 明确失败、session revoked 或用户主动 logout 才清本地 token。

Refresh State

POST /api/v1/auth/token/refresh

请求必须带:

  • refresh_token
  • device_id

处理规则:

Result Client Action
refresh ok 替换 access token 和 refresh token重试 overview
SESSION_EXPIRED 清 token进登录页
SESSION_REVOKED 清 token进登录页
AUTH_FAILED 清 token进登录页
network/5xx 不清 token展示重试

refresh 成功会轮换 refresh token。客户端必须原子保存新 access token 和新 refresh token避免只保存一半导致下次启动失效。

Login State

三方登录:

POST /api/v1/auth/third-party/login

状态跳转:

Response Client Action
is_new_user=true 进入资料补全页
profile_completed=false 进入资料补全页
profile_completed=true 进入主界面

服务端不会返回 NOT_REGISTERED。三方身份不存在时,该接口原子创建用户、三方绑定、默认短号和 session。

Profile Required State

资料补全页需要先拉:

GET /api/v1/countries

提交:

POST /api/v1/users/me/onboarding/complete

成功后:

  • 替换响应里的新 access token。
  • 保留原 refresh token该接口不轮换 refresh token。
  • 重新调用 GET /api/v1/users/me/overview
  • overview 成功后进入主界面。

未完成资料的 access token 会在 gateway profile gate 被拒绝。客户端不能绕过资料补全去请求房间、IM、RTC、钱包和付费接口。

Main Tabs Entry

进入主界面后,底部三个一级页使用这些接口:

Tab Primary Endpoint Notes
房间 GET /api/v1/rooms?tab=hot&limit=20 默认 hotnew 用同一接口
消息 GET /api/v1/messages/tabs 用户私聊列表走腾讯云 IM SDK
我的 GET /api/v1/users/me/overview 轻聚合,不查流水、成员、工资

主界面首屏可以并行:

sequenceDiagram
  participant C as Client
  participant G as gateway-service
  participant TIM as Tencent IM

  par room tab
    C->>G: GET /api/v1/rooms?tab=hot
  and message tab
    C->>G: GET /api/v1/messages/tabs
  and me tab
    C->>G: GET /api/v1/users/me/overview
  and IM
    C->>G: GET /api/v1/im/usersig
    C->>TIM: SDK login
  end

如果当前默认落在“房间” tab可以优先请求房间列表消息和我的延后或并行低优先级请求。

IM Login State

腾讯云 IM 登录必须发生在 profile_completed=true 后。

GET /api/v1/im/usersig

规则:

  • UserSig 只用于腾讯云 IM SDK 登录。
  • 用户私聊会话列表由 SDK 拉取。
  • 系统/活动消息由 /api/v1/messages/tabs/api/v1/messages 拉取。
  • UserSig 过期时重新调用该接口,不使用 refresh token。

未完成资料用户请求 /api/v1/im/usersig 必须返回 PROFILE_REQUIRED

Room Restore State

当前接口:

GET /api/v1/rooms/current

用途App 从后台回前台、进程重启、网络恢复时,判断是否有需要恢复的房间。不能通过房间列表猜测当前房间,因为列表是发现页,不表达当前用户 presence。

响应字段:

{
  "has_current_room": true,
  "room_id": "lalu_room_01",
  "room_version": 12,
  "role": "audience",
  "mic_session_id": "",
  "publish_state": "",
  "need_join_im_group": true,
  "need_rtc_token": false,
  "server_time_ms": 1777996800000
}

恢复规则:

State Client Action
has_current_room=false 不展示恢复入口
audience 重新登录 IM 并 join group不申请 RTC token
on mic pending/publishing /api/v1/rtc/token,恢复 RTC随后按麦位状态确认发流
room closed / kicked / banned 清本地房间态,展示原因或静默回房间列表

rooms/current 由 room-service 的用户 presence 读模型和 Room Cell 快照二次校验共同决定。该接口只做恢复探测,不写命令日志、不刷新心跳、不隐式 JoinRoom。

Push Token State

当前接口:

POST /api/v1/devices/push-token
DELETE /api/v1/devices/push-token

绑定时机:

  • 资料完成后。
  • 用户允许系统通知后。
  • token 变化、App 重装、语言/时区变化时重新上报。

该能力服务系统/活动消息离线触达,不影响 App 能否进入主界面。

Error Handling Rules

Error Meaning Client Action
AUTH_REQUIRED access token 缺失或无效 有 refresh 则 refresh否则登录
SESSION_EXPIRED refresh session 过期 清 token登录
SESSION_REVOKED refresh session 已吊销 清 token登录
PROFILE_REQUIRED 已登录但资料未完成 资料补全
USER_DISABLED 用户被禁用 账号异常页
USER_BANNED 用户被封禁 账号异常页
UPSTREAM_ERROR 上游不可用 重试,不清 token
network timeout 网络异常 重试,不清 token

不要因为普通 5xx、网络失败或 bootstrap 失败清空登录态。

Client Local State

客户端本地至少保存:

  • access_token
  • refresh_token
  • session_id
  • user_id
  • app_code
  • profile_completed
  • onboarding_status
  • device_id
  • last_room_id,只做恢复提示,不能作为 presence 事实
  • config version cache

服务端事实优先级高于本地缓存。只要服务端返回 profile required、disabled、banned、session revoked客户端必须更新本地状态。

Implementation Status

  1. 服务端已固定 App 入口状态机文档,客户端入口必须按本文状态转移。
  2. gateway 已实现 GET /api/v1/app/bootstrap返回版本、维护、feature flags、bottom tabs 和配置版本。
  3. gateway 已实现 GET /api/v1/rooms/current,只用 token user_id 做当前用户房间恢复探测,不修改房间状态。
  4. user-service 已实现 push token 绑定/解绑 RPCgateway 已暴露 POST/DELETE /api/v1/devices/push-token
  5. App 端下一步按本文状态机接入bootstrap -> token probe -> refresh/login/onboarding -> main tabs -> IM login -> room restore。
  6. 端到端 smoke 必须覆盖新用户注册、老用户启动、access token 过期 refresh、profile required、IM login、房间恢复。

Verification Matrix

Scenario Expected
首次安装无 token bootstrap 后进入登录页
新用户三方登录 创建用户,进入资料补全页
资料补全成功 替换 access token进入主界面
access token 过期 refresh 成功后重试 overview
refresh token 过期 清 token进入登录页
未完成资料请求房间列表 返回 PROFILE_REQUIRED
未完成资料请求 IM UserSig 返回 PROFILE_REQUIRED
主界面打开房间 tab GET /api/v1/rooms?tab=hot
主界面打开消息 tab GET /api/v1/messages/tabs,用户私聊走 IM SDK
主界面打开我的 tab GET /api/v1/users/me/overview
App 回前台且有房间 presence rooms/current 返回恢复信息
普通 5xx 或网络失败 展示重试,不清本地 token

Critical Rules

  • 资料未完成不能进入实时系统、房间、钱包和付费流程。
  • request_id 只做链路追踪,不做幂等键。
  • 登录和 refresh 响应里的 token 必须原子保存。
  • CompleteOnboarding 只替换 access token不轮换 refresh token。
  • IM 登录在主资料完成之后,用户私聊状态以腾讯云 IM SDK 为准。
  • 房间恢复用 rooms/current,不要用房间列表代替。
  • bootstrap 只返回配置摘要和版本,不返回大列表。