11 KiB
My Page Overview API
本文定义 App 底部 我的 页首屏聚合接口。该接口只返回首屏渲染需要的轻量摘要:用户资料、我的邀请码、核心资产余额、身份开关、消息红点和入口显隐。流水、成员列表、背包完整列表、工资明细、邀请列表和团队统计必须放到二级页接口。
Goals
我的页首屏一次请求拿到稳定摘要,减少 App 冷启动后的接口数量。- 聚合已有轻量能力:
/users/me、/wallet/me/balances、/users/me/host-identity、/messages/tabs。 - 避免实时
COUNT(*)、扫流水、扫成员、扫背包、现算工资。 - 后端控制入口显隐,App 不硬编码复杂身份组合。
- 所有数据按
app_code和当前 JWTuser_id隔离。
Endpoint
GET /api/v1/users/me/overview
Authorization: Bearer <access_token>
响应仍使用 gateway /api/v1 envelope:
{
"code": "OK",
"message": "ok",
"request_id": "req_01",
"data": {
"profile": {},
"wallet": {},
"invite": {},
"roles": {},
"badges": {},
"entries": [],
"server_time_ms": 1777996800000
}
}
Response Shape
{
"profile": {
"user_id": "10001",
"display_user_id": "163000",
"default_display_user_id": "163000",
"display_user_id_kind": "default",
"display_user_id_expires_at_ms": 0,
"username": "Alice",
"avatar": "https://cdn.example.com/avatar.png",
"gender": "female",
"birth": "2000-01-01",
"country": "US",
"country_id": 1,
"country_name": "United States",
"country_display_name": "United States",
"region_id": 10,
"region_code": "north_america",
"region_name": "North America",
"language": "en",
"profile_completed": true,
"onboarding_status": "completed"
},
"wallet": {
"balances": [
{
"asset_type": "COIN",
"available_amount": 12000,
"frozen_amount": 0,
"version": 8
},
{
"asset_type": "DIAMOND",
"available_amount": 300,
"frozen_amount": 0,
"version": 3
},
{
"asset_type": "USD_BALANCE",
"available_amount": 2500,
"frozen_amount": 500,
"version": 2
}
],
"recharge_enabled": true,
"diamond_exchange_enabled": true,
"withdraw_enabled": true
},
"invite": {
"my_invite_code": "A1B2C3",
"invite_enabled": true,
"invite_count": 12,
"valid_invite_count": 3,
"valid_invite_threshold_coin": 80000
},
"roles": {
"is_host": true,
"is_agent": false,
"is_bd": false,
"is_bd_leader": false,
"is_coin_seller": false
},
"badges": {
"unread_system_messages": 2,
"unread_activity_messages": 1,
"pending_role_invitations": 0
},
"entries": [
{
"key": "wallet",
"title": "Wallet",
"visible": true,
"badge_count": 0
},
{
"key": "host_center",
"title": "Host Center",
"visible": true,
"badge_count": 0
},
{
"key": "coin_seller_center",
"title": "Coin Seller",
"visible": false,
"badge_count": 0
},
{
"key": "backpack",
"title": "Backpack",
"visible": true,
"badge_count": 0
},
{
"key": "settings",
"title": "Settings",
"visible": true,
"badge_count": 0
}
],
"server_time_ms": 1777996800000
}
Field Sources
| Field | Source | Query Pattern |
|---|---|---|
profile |
user-service.GetUser |
single row by (app_code, user_id) |
wallet.balances |
wallet-service.GetBalances |
point read for COIN、DIAMOND、USD_BALANCE |
invite.my_invite_code |
user-service invite code module |
point read active primary code by (app_code, user_id) |
invite.invite_count |
invite read model | users that successfully filled this invite code; without read model return 0 or omit |
invite.valid_invite_count |
invite read model | invited users whose cumulative eligible recharge reaches the configured threshold |
invite.valid_invite_threshold_coin |
invite validity policy | current configured threshold, default 80000 COIN |
roles |
user-service host identity module |
point read role facts or future role summary read model |
badges.unread_* |
message inbox owner | counter/read model, not message list scan |
entries |
gateway composition rules | computed from roles and feature flags |
server_time_ms |
gateway | current server clock |
当前实现已有 /users/me、/wallet/me/balances、/users/me/host-identity、/messages/tabs。/users/me/overview 可以先在 gateway-service 聚合这些能力;后续如果角色判断的三次 not-found RPC 变慢,再在 user-service 增加 GetUserRoleSummary 内部 RPC。
Asset Rules
首屏只返回三类资产:
| Asset | Show Rule |
|---|---|
COIN |
所有用户展示 |
DIAMOND |
所有用户展示,但文案不能暗示可直接消费 |
USD_BALANCE |
有余额、冻结金额、host/agent/bd/coin_seller 身份时展示 |
不要返回 GIFT_POINT。积分是后台工资/活动计算输入,不是普通用户钱包可直接操作资产。
Entry Rules
入口显隐由后端返回,App 只负责渲染。
| Entry Key | Visible Rule |
|---|---|
wallet |
always true |
host_center |
roles.is_host == true |
apply_host |
roles.is_host == false |
agency_center |
roles.is_agent == true |
bd_center |
roles.is_bd == true |
bd_leader_center |
roles.is_bd_leader == true |
coin_seller_center |
roles.is_coin_seller == true |
backpack |
always true |
settings |
always true |
support |
always true |
同一个用户可能同时是 BD 和 host。不要用互斥枚举表达角色,必须使用 bool flags。
Expensive Fields To Exclude
不要在 overview 中实时查询这些字段:
| Field | Reason | Replacement |
|---|---|---|
| 邀请用户列表 | 需要分页,可能扫 user_invite_relations |
二级页 GET /api/v1/invites/users |
实时邀请人数 COUNT(*) |
高活跃邀请人会让首页变慢 | read model counter 或返回 0 |
| 邀请奖励流水 | 属于钱包/活动明细 | 二级页分页 |
这些字段不要放进首屏 overview:
| Field | Reason | Replacement |
|---|---|---|
period_mic_minutes |
需要统计聚合,可能扫 stats | Host Center 二级页 |
period_gift_points |
需要统计聚合,可能跨日汇总 | Host Center 二级页 |
estimated_earning_usd_minor |
不能首屏现算工资 | 后台工资 read model 或收益页 |
agency_member_count |
容易 COUNT(*) members |
Agency Center 二级页或 summary table |
bd_host_count |
团队规模统计昂贵 | BD Center 二级页或 summary table |
pending_withdrawals |
扫提现/流水表 | wallet badge counter |
recharge_count |
扫充值记录 | wallet 二级页 |
resource_count |
扫背包权益表 | backpack 二级页 |
latest_transactions |
扫交易流水 | wallet 二级页 |
inbox_items |
扫消息列表 | messages 二级页 |
首屏接口禁止为了红点实时扫大表。需要红点就维护 counter/read model;没有 counter 时先不展示该红点。
Performance Budget
目标:
- P95 backend latency:
< 120msin normal region deployment. - Gateway fanout RPC count: max 4.
- No list scan.
- No transaction history query.
- No member
COUNT(*). - No resource entitlement list.
- No salary calculation.
推荐并行 fanout:
sequenceDiagram
participant C as Client
participant G as gateway-service
participant U as user-service
participant W as wallet-service
participant M as message inbox owner
C->>G: GET /api/v1/users/me/overview
par profile and roles
G->>U: GetUser(user_id)
G->>U: GetUserRoleSummary(user_id)
and wallet
G->>W: GetBalances(COIN, DIAMOND, USD_BALANCE)
and badges
G->>M: ListMessageTabs(user_id)
end
G->>G: compose entries and feature flags
G-->>C: overview
MVP 可以复用已有 GetHostProfile、GetBDProfile、GetCoinSellerProfile 聚合角色;优化版应增加单个 GetUserRoleSummary,减少 not-found RPC 和错误映射成本。
Degraded Responses
首屏接口可以部分降级,但必须清楚标记。
| Upstream Failure | Response Rule |
|---|---|
user-service.GetUser fail |
整体失败,资料是首屏主数据 |
wallet-service.GetBalances fail |
返回空 balances,并加 wallet.unavailable=true |
| role summary fail | 返回 all false,并加 roles.unavailable=true,不要误展示特权入口 |
| message tabs fail | unread 置 0,并加 badges.unavailable=true |
如果某个上游超时,gateway 不应无限等待。overview 是首屏接口,应该有比普通操作接口更短的聚合超时预算。
HTTP Handler Shape
新增路由:
mux.Handle(apiV1Prefix+"/users/me/overview", h.profileAPIHandler(jwtVerifier, h.getMyOverview))
handler 只做协议聚合:
- 从 JWT context 取
user_id和app_code。 - 并行请求 profile、balances、roles、message tabs。
- 把资产补齐为固定三类,缺失资产返回 0。
- 根据 roles 和 feature flags 生成 entries。
- 返回 envelope data。
不要在 gateway 写 SQL,也不要在 gateway 做工资、成员、资源统计。
Verification Matrix
| Scenario | Expected |
|---|---|
| 普通用户打开我的页 | 返回 profile、COIN/DIAMOND/USD_BALANCE、普通入口 |
| host 打开我的页 | roles.is_host=true,展示 host_center |
| BD Leader 打开我的页 | is_bd=true 且 is_bd_leader=true,展示 leader 入口 |
| 币商打开我的页 | is_coin_seller=true,展示 coin seller 入口 |
| 钱包没有账户行 | 三类资产都返回 0 投影 |
| 消息服务失败 | 我的页仍可展示,消息红点降级为 0 |
| 用户资料服务失败 | overview 整体失败 |
| 大量成员/流水存在 | overview 延迟不受影响,因为不查成员和流水 |
Implementation Order
- 新增本文档,确认字段边界和慢查询排除规则。
- 在
gateway-service增加GET /api/v1/users/me/overviewhandler。 - 复用现有
GetUser、GetBalances、GetHostProfile/GetBDProfile/GetCoinSellerProfile、ListMessageTabs。 - 增加 handler 单元测试,覆盖普通用户、host、BD Leader、币商、钱包缺失、消息降级。
- 如果角色 RPC 成本偏高,再在
user-service增加GetUserRoleSummary。 - 如果红点需要更多类型,先补 counter/read model,再把 badge 加进 overview。
Critical Rules
我的页 overview 是轻聚合接口,不是用户全量详情接口。- 首屏不做
COUNT(*)、不扫流水、不扫成员、不查完整背包、不现算工资。 - 钱包余额以
wallet-service为准。 - 用户资料以
user-service为准。 - 角色入口以服务端返回的
roles和entries为准。 - 消息红点以 message inbox counter/read model 为准,用户私聊未读仍以腾讯云 IM SDK 为准。