14 KiB
App Entry State Machine
本文定义用户从打开 App 到进入主界面的完整状态机。它只描述 App 入口流程、接口调用顺序、跳转条件和异常恢复边界;登录注册细节见 Login And Registration Technical Design,邀请码细节见 Registration Invite Code Architecture,房间重连细节见 Room Entry Reconnect Development Requirements。
Goals
- 客户端启动时用确定的状态机决定去登录页、资料补全页、主界面或维护/升级页。
- 未完成资料的用户不能进入房间、IM、RTC、钱包和付费流程。
- access token 过期时优先 refresh,refresh 失败才回登录页。
- 主界面首屏只调用轻量一级接口:房间、消息、我的。
- 腾讯云 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;可选提交 invite_code |
| 注册国家 | 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
启动顺序:
- App 启动,生成或读取 stable
device_id。 - 调
GET /api/v1/app/bootstrap,以 bootstrap 返回的版本、维护、feature flags 和配置版本作为入口决策事实。 - 如果 bootstrap 返回强更,进入强更页。
- 如果 bootstrap 返回维护中,进入维护页。
- 检查本地是否有 access token。
- 没有 token,进入登录页。
- 有 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_tokendevice_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
请求体包含 username/avatar/gender/country,可以选填 invite_code。如果用户填写邀请码但服务端无法解析到有效邀请人,返回 INVALID_INVITE_CODE,客户端停留在资料补全页让用户修改或清空邀请码。
成功后:
- 替换响应里的新 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 |
默认 hot;new 用同一接口 |
| 消息 | 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 |
已登录但资料未完成 | 资料补全 |
INVALID_INVITE_CODE |
注册页邀请码无效 | 停留资料补全页,提示修改或清空邀请码 |
USER_DISABLED |
用户被禁用 | 账号异常页 |
USER_BANNED |
用户被封禁 | 账号异常页 |
UPSTREAM_ERROR |
上游不可用 | 重试,不清 token |
| network timeout | 网络异常 | 重试,不清 token |
不要因为普通 5xx、网络失败或 bootstrap 失败清空登录态。
Client Local State
客户端本地至少保存:
access_tokenrefresh_tokensession_iduser_idapp_codeprofile_completedonboarding_statusdevice_idlast_room_id,只做恢复提示,不能作为 presence 事实- config version cache
服务端事实优先级高于本地缓存。只要服务端返回 profile required、disabled、banned、session revoked,客户端必须更新本地状态。
Implementation Status
- 服务端已固定 App 入口状态机文档,客户端入口必须按本文状态转移。
- gateway 已实现
GET /api/v1/app/bootstrap,返回版本、维护、feature flags、bottom tabs 和配置版本。 - gateway 已实现
GET /api/v1/rooms/current,只用 token user_id 做当前用户房间恢复探测,不修改房间状态。 - user-service 已实现 push token 绑定/解绑 RPC,gateway 已暴露
POST/DELETE /api/v1/devices/push-token。 - App 端下一步按本文状态机接入:bootstrap -> token probe -> refresh/login/onboarding -> main tabs -> IM login -> room restore。
- 端到端 smoke 必须覆盖:新用户注册、老用户启动、access token 过期 refresh、profile required、IM login、房间恢复。
Verification Matrix
| Scenario | Expected |
|---|---|
| 首次安装无 token | bootstrap 后进入登录页 |
| 新用户三方登录 | 创建用户,进入资料补全页 |
| 新用户资料补全填写有效邀请码 | 注册完成,邀请关系绑定到邀请码 owner |
| 新用户资料补全填写无效邀请码 | 返回 INVALID_INVITE_CODE,仍在资料补全页 |
| 资料补全成功 | 替换 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 只返回配置摘要和版本,不返回大列表。