hyapp-server/docs/新版VIP权限策略_Flutter客户端对接文档.md
2026-07-15 13:20:33 +08:00

1053 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 新版 VIP 权限策略 Flutter 客户端对接文档
## 1. VIP 权限策略
### 1.1 权限来源
- VIP 等级只用于套餐名称、价格、有效期和等级展示。
- Flutter 只使用 `GET /api/v1/vip/me` 返回的 `data.state.effective_benefits` 判断当前用户是否具有某项权益。
- Flutter 不得使用 `VIP等级 >= N``unlock_level``app_code` 或本地权益表推导权限。
- `/api/v1/vip/packages` 中的 `packages[].benefits` 是该套餐当前配置的完整权益集合,只用于套餐页和升级引导,不能作为用户已拥有权益。
- 写操作最终以对应业务接口的服务端鉴权结果为准。Flutter 的权益判断只用于入口显隐、锁态和交互预判。
### 1.2 Flutter 权益状态
```dart
class VipEntitlementState {
final VipProgramConfig program;
final VipIdentity effectiveVip;
final String effectiveSource; // paid / trial / none
final Map<String, VipBenefit> benefitsByCode;
final VipUserSettings settings;
final int evaluatedAtMs;
bool has(String benefitCode) {
return program.status == 'active' &&
effectiveVip.active &&
benefitsByCode[benefitCode]?.status == 'active';
}
bool allowsRoomEntryNotice() {
return has('room_entry_notice') && settings.roomEntryNoticeEnabled;
}
bool allowsOnlineGlobalNotice() {
return has('online_global_notice') &&
settings.onlineGlobalNoticeEnabled;
}
}
```
`benefitsByCode` 必须由 `state.effective_benefits` 转换。未知 `benefit_code` 保留在状态中但不自动开放功能,不得导致解析失败或页面崩溃。
### 1.3 权益刷新
| 场景 | Flutter 处理 |
|---|---|
| 登录成功、切换账号、切换 App | 清空原 VIP 状态,调用 `/vip/me` |
| App 冷启动、回到前台 | 调用 `/vip/me` 校准过期、体验卡回落和后台配置变更 |
| 进入 VIP 页面 | 并发调用 `/vip/packages``/vip/me` |
| 购买、佩戴体验卡、卸下体验卡成功 | 使用响应中的 `state` 原子替换本地状态 |
| 收到 `vip_notice` | 按 `event_id` 去重,再调用 `/vip/me` |
| `config_version` 不一致 | 用户权限以 `/vip/me.state` 为准,重新请求 `/vip/packages`,不得合并不同版本的权益数组 |
| VIP 到期倒计时结束 | 可更新页面显示,但执行功能前仍以服务端接口结果为准 |
### 1.4 Fami 初始等级权益
下表只用于识别初始配置。Flutter 实际必须渲染 `/vip/packages` 返回的数据。
| 等级 | 本级初始新增权益 |
|---|---|
| VIP1 | `vip_badge_identity``vip_medal``avatar_frame``vip_title` |
| VIP2 | `chat_bubble``vip_gift``entry_effect``visitor_history` |
| VIP3 | `voice_wave``profile_card``custom_room_background``room_image_message` |
| VIP4 | `animated_avatar``animated_room_cover``mic_skin``room_border``vehicle` |
| VIP5 | `colored_room_name``colored_nickname``colored_id``gift_tray_skin` |
| VIP6 | `hide_profile_data``custom_avatar_frame``leaderboard_invisible``daily_coin_rebate` |
| VIP7 | `custom_profile_card``anonymous_profile_visit``custom_gift` |
| VIP8 | `room_entry_notice``custom_pretty_id``custom_vehicle` |
| VIP9 | `anti_kick``anti_mute``online_global_notice` |
## 2. 通用协议
- API 前缀:`/api/v1`
- 请求头:`Authorization: Bearer <access_token>`
- `app_code``user_id` 从登录态解析Flutter 不传。
- 时间字段统一使用 Unix epoch milliseconds。
- 业务切日使用 UTCFlutter 仅在展示时转换本地时间。
- 成功响应统一格式:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_xxx",
"data": {}
}
```
- `request_id` 只用于链路追踪。
-`command_id` 的接口,网络重试必须复用原 `command_id`;新的业务动作必须生成新值。
## 3. VIP 套餐
### 接口地址
`GET /api/v1/vip/packages`
### 参数
无。
### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_packages",
"data": {
"current_vip": {
"level": 3,
"name": "VIP3",
"active": true,
"started_at_ms": 1780000000000,
"expires_at_ms": 1782592000000,
"program_type": "tiered_privilege_v1",
"config_version": 7
},
"packages": [
{
"level": 4,
"name": "VIP4",
"status": "active",
"price_coin": 4000,
"duration_ms": 2592000000,
"reward_resource_group_id": 0,
"reward_items": [],
"can_purchase": true,
"sort_order": 40,
"recharge_gate_required": false,
"required_recharge_coin_amount": 0,
"user_recharge_coin_amount": 0,
"purchase_locked_reason": "",
"benefits": [
{
"benefit_code": "custom_room_background",
"name": "自定义房间背景",
"benefit_type": "function",
"unlock_level": 3,
"status": "active",
"trial_enabled": true,
"resource_id": 0,
"resource_type": "",
"execution_scope": "room",
"auto_equip": false,
"sort_order": 110,
"metadata_json": ""
}
],
"config_version": 7
}
],
"program_config": {
"app_code": "fami",
"program_type": "tiered_privilege_v1",
"level_count": 9,
"same_level_expiry_policy": "extend_remaining",
"upgrade_expiry_policy": "replace_from_now",
"downgrade_purchase_policy": "reject",
"benefit_inheritance_policy": "target_only",
"grant_mode": "trial_card",
"trial_card_enabled": true,
"status": "active",
"config_version": 7
},
"state": {
"paid_vip": {
"level": 3,
"name": "VIP3",
"active": true,
"expires_at_ms": 1782592000000
},
"equipped_trial_card": null,
"effective_vip": {
"level": 3,
"name": "VIP3",
"active": true,
"expires_at_ms": 1782592000000
},
"effective_source": "paid",
"effective_benefits": [
{
"benefit_code": "custom_room_background",
"status": "active"
}
],
"evaluated_at_ms": 1780000000000,
"user_settings": {
"room_entry_notice_enabled": true,
"online_global_notice_enabled": true,
"updated_at_ms": 0
}
}
}
}
```
## 4. 当前 VIP 权限状态
### 接口地址
`GET /api/v1/vip/me`
### 参数
无。
### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_vip_me",
"data": {
"level": 8,
"name": "VIP8",
"active": true,
"started_at_ms": 1780100000000,
"expires_at_ms": 1782692000000,
"program_type": "tiered_privilege_v1",
"config_version": 7,
"program_config": {
"app_code": "fami",
"program_type": "tiered_privilege_v1",
"level_count": 9,
"same_level_expiry_policy": "extend_remaining",
"upgrade_expiry_policy": "replace_from_now",
"downgrade_purchase_policy": "reject",
"benefit_inheritance_policy": "target_only",
"grant_mode": "trial_card",
"trial_card_enabled": true,
"status": "active",
"config_version": 7
},
"state": {
"paid_vip": {
"level": 3,
"name": "VIP3",
"active": true,
"started_at_ms": 1780000000000,
"expires_at_ms": 1782592000000,
"program_type": "tiered_privilege_v1",
"config_version": 7
},
"equipped_trial_card": {
"trial_card_id": "vip_trial_card_xxx",
"entitlement_id": "ent_xxx",
"resource_id": 108,
"user_id": 10001,
"level": 8,
"name": "VIP8",
"status": "active",
"equipped": true,
"duration_ms": 2592000000,
"effective_at_ms": 1780100000000,
"expires_at_ms": 1782692000000,
"remaining_duration_ms": 2591000000
},
"effective_vip": {
"level": 8,
"name": "VIP8",
"active": true,
"started_at_ms": 1780100000000,
"expires_at_ms": 1782692000000,
"program_type": "tiered_privilege_v1",
"config_version": 7
},
"effective_source": "trial",
"effective_benefits": [
{
"benefit_code": "room_entry_notice",
"name": "进房高亮通知",
"benefit_type": "function",
"status": "active",
"trial_enabled": true,
"resource_id": 0,
"resource_type": "",
"execution_scope": "room",
"auto_equip": false,
"sort_order": 290,
"metadata_json": ""
}
],
"evaluated_at_ms": 1780101000000,
"program_config": {
"app_code": "fami",
"program_type": "tiered_privilege_v1",
"config_version": 7
},
"user_settings": {
"app_code": "fami",
"user_id": 10001,
"room_entry_notice_enabled": true,
"online_global_notice_enabled": true,
"updated_at_ms": 0
}
}
}
}
```
- `effective_source``paid``trial``none`
- 当前展示身份只读 `state.effective_vip`
- 当前权限只读 `state.effective_benefits`
- `daily_coin_rebate` 不会因体验卡进入 `effective_benefits`
## 5. 购买、续期、升级
### 接口地址
`POST /api/v1/vip/purchase`
### 参数
```json
{
"command_id": "vip_purchase_10001_4_1780100000000",
"level": 4
}
```
| 字段 | 必填 | 说明 |
|---|---|---|
| `command_id` | 是 | 最长 128 字节,同一次购买重试必须复用 |
| `level` | 是 | 必须来自 `/vip/packages` |
### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_purchase",
"data": {
"order_id": "vip_order_xxx",
"transaction_id": "tx_xxx",
"vip": {
"level": 4,
"name": "VIP4",
"active": true,
"started_at_ms": 1780100000000,
"expires_at_ms": 1782692000000,
"program_type": "tiered_privilege_v1",
"config_version": 7
},
"coin_spent": 4000,
"coin_balance_after": 6000,
"reward_items": [],
"program_config": {
"app_code": "fami",
"program_type": "tiered_privilege_v1",
"upgrade_expiry_policy": "replace_from_now",
"config_version": 7
},
"state": {
"effective_source": "paid",
"paid_vip": {
"level": 4,
"name": "VIP4",
"active": true,
"expires_at_ms": 1782692000000
},
"equipped_trial_card": null,
"effective_vip": {
"level": 4,
"name": "VIP4",
"active": true,
"expires_at_ms": 1782692000000
},
"effective_benefits": [
{
"benefit_code": "custom_room_background",
"status": "active"
}
],
"evaluated_at_ms": 1780100000000
}
}
}
```
- 同级购买:从原截止时间继续累加。
- Fami 高等级购买:立即替换低等级,从购买时间重新计时,低等级剩余时间丢弃。
- 低等级购买:服务端拒绝。
- 购买时佩戴体验卡:付费 VIP 会更新,但 `effective_vip` 仍可能来自体验卡,必须使用响应 `state`
## 6. VIP 体验卡
### 6.1 体验卡列表
#### 接口地址
`GET /api/v1/users/me/resources?resource_type=vip_trial_card`
#### 参数
| 参数 | 位置 | 必填 | 值 |
|---|---|---|---|
| `resource_type` | query | 是 | `vip_trial_card` |
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_trial_cards",
"data": {
"items": [
{
"entitlement_id": "ent_card_a",
"resource_id": 101,
"resource": {
"resource_id": 101,
"resource_code": "vip_trial_card_3",
"resource_type": "vip_trial_card",
"name": "VIP3体验卡",
"status": "active",
"asset_url": "",
"preview_url": "",
"animation_url": "",
"metadata_json": "{\"vip_level\":3}",
"sort_order": 30
},
"status": "active",
"quantity": 1,
"remaining_quantity": 1,
"effective_at_ms": 1780100000000,
"expires_at_ms": 1781309600000,
"source_grant_id": "grant_a",
"created_at_ms": 1780100000000,
"updated_at_ms": 1780100000000,
"equipped": false
}
],
"total": 1
}
}
```
### 6.2 佩戴体验卡
#### 接口地址
`POST /api/v1/vip/trial-cards/{entitlement_id}/equip`
#### 参数
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
| `entitlement_id` | path | 是 | 使用体验卡列表中的实例 ID |
无 body。
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_trial_equip",
"data": {
"trial_card": {
"trial_card_id": "vip_trial_card_b",
"entitlement_id": "ent_card_b",
"resource_id": 102,
"user_id": 10001,
"level": 4,
"name": "VIP4",
"status": "active",
"equipped": true,
"duration_ms": 1728000000,
"effective_at_ms": 1780200000000,
"expires_at_ms": 1781928000000,
"remaining_duration_ms": 1700000000
},
"program_config": {
"app_code": "fami",
"program_type": "tiered_privilege_v1",
"trial_card_enabled": true
},
"state": {
"effective_source": "trial",
"effective_vip": {
"level": 4,
"name": "VIP4",
"active": true,
"expires_at_ms": 1781928000000
},
"effective_benefits": []
},
"server_time_ms": 1780228000000
}
}
```
### 6.3 卸下体验卡
#### 接口地址
`DELETE /api/v1/vip/trial-cards/equipped`
#### 参数
无。
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_trial_unequip",
"data": {
"unequipped": true,
"program_config": {
"app_code": "fami",
"program_type": "tiered_privilege_v1"
},
"state": {
"effective_source": "paid",
"paid_vip": {
"level": 3,
"name": "VIP3",
"active": true
},
"equipped_trial_card": null,
"effective_vip": {
"level": 3,
"name": "VIP3",
"active": true
},
"effective_benefits": []
},
"server_time_ms": 1780229000000
}
}
```
- 体验卡可自由切换,不比较等级高低。
- 切换和卸下不会暂停、延长或删除体验卡。
- 每张卡的剩余时间按绝对 `expires_at_ms` 计算。
- 佩戴和卸下成功后立即使用响应 `state` 替换本地 VIP 状态。
## 7. VIP 通知开关
### 7.1 查询开关
#### 接口地址
`GET /api/v1/vip/settings`
#### 参数
无。
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_settings_get",
"data": {
"settings": {
"app_code": "fami",
"user_id": 10001,
"room_entry_notice_enabled": true,
"online_global_notice_enabled": false,
"updated_at_ms": 1780300000000
},
"evaluated_at_ms": 1780300001000
}
}
```
### 7.2 修改开关
#### 接口地址
`PATCH /api/v1/vip/settings`
#### 参数
局部更新,至少传一个字段:
```json
{
"room_entry_notice_enabled": false,
"online_global_notice_enabled": true
}
```
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_settings_patch",
"data": {
"settings": {
"app_code": "fami",
"user_id": 10001,
"room_entry_notice_enabled": false,
"online_global_notice_enabled": true,
"updated_at_ms": 1780300010000
},
"server_time_ms": 1780300010000
}
}
```
## 8. 每日 VIP 金币返现
### 8.1 查询当前返现
#### 接口地址
`GET /api/v1/vip/coin-rebates/current`
#### 参数
无。
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_rebate_current",
"data": {
"found": true,
"rebate": {
"rebate_id": "vip_rebate_xxx",
"user_id": "10001",
"task_day": "2026-07-15",
"vip_level": 6,
"vip_name": "VIP6",
"coin_amount": 100,
"status": "claimable",
"available_at_ms": 1784073600000,
"expires_at_ms": 1784160000000,
"config_version": 8,
"claimed_at_ms": 0,
"wallet_transaction_id": "",
"created_at_ms": 1784073605000,
"updated_at_ms": 1784073605000
},
"server_time_ms": 1784073610000
}
}
```
无资格时 `found=false``rebate=null`
### 8.2 批量查询返现状态
#### 接口地址
`POST /api/v1/vip/coin-rebates/statuses`
#### 参数
```json
{
"rebate_ids": ["vip_rebate_xxx", "vip_rebate_yyy"],
"page": 1,
"page_size": 20
}
```
- `rebate_ids` 最多 100 个。
- 不传 `rebate_ids` 时按 `page/page_size` 查询历史。
- `page_size` 最大 100。
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_rebate_statuses",
"data": {
"rebates": [
{
"rebate_id": "vip_rebate_xxx",
"task_day": "2026-07-15",
"vip_level": 6,
"vip_name": "VIP6",
"coin_amount": 100,
"status": "claimable",
"available_at_ms": 1784073600000,
"expires_at_ms": 1784160000000,
"claimed_at_ms": 0,
"wallet_transaction_id": ""
}
],
"total": 1,
"server_time_ms": 1784073610000
}
}
```
### 8.3 领取返现
#### 接口地址
`POST /api/v1/vip/coin-rebates/{rebate_id}/claim`
#### 参数
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
| `rebate_id` | path | 是 | 从 current、statuses 或系统消息取得 |
```json
{
"command_id": "vip_rebate_claim_10001_20260715"
}
```
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_rebate_claim",
"data": {
"rebate": {
"rebate_id": "vip_rebate_xxx",
"task_day": "2026-07-15",
"vip_level": 6,
"vip_name": "VIP6",
"coin_amount": 100,
"status": "claimed",
"claimed_at_ms": 1784073700000,
"wallet_transaction_id": "wallet_tx_xxx"
},
"transaction_id": "wallet_tx_xxx",
"coin_balance": {
"asset_type": "COIN",
"available_amount": 20100,
"frozen_amount": 0,
"version": 18
},
"server_time_ms": 1784073700000
}
}
```
## 9. 上线全服通知
### 接口地址
`POST /api/v1/vip/online-notice`
### 参数
```json
{
"command_id": "vip_online_<process_boot_id>_2026-07-15"
}
```
- `command_id` 最长 128 字节。
- 同一进程同一 UTC 日复用同一个 `command_id`
- `process_boot_id` 只保存在内存中,不写磁盘。
### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_online_notice",
"data": {
"event_id": "vip_online_notice:10001:sha256_digest",
"group_id": "hy_fami_bc_g_v2",
"status": "pending",
"created": true,
"evaluated_at_ms": 1784073610000
}
}
```
- `status=pending` 表示事件已进入异步投递链路。
- 幂等命中时 `created=false`,不是失败。
- 同一进程每天触发一次;杀掉进程后新进程当天可以再次触发。
## 10. 进房 VIP 通知
### 接口地址
`POST /api/v1/rooms/join`
### 参数
```json
{
"room_id": "room_1001",
"command_id": "join_10001_room_1001_1784073600000",
"role": "audience",
"password": "",
"response_mode": "full"
}
```
### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_join",
"data": {
"result": {
"applied": true,
"room_version": 18,
"server_time_ms": 1784073600000
},
"room": {},
"viewer": {},
"seats": [],
"profiles": [],
"im": {
"group_id": "room_1001",
"need_join_group": true
},
"effective_vip": {
"program_type": "tiered_privilege_v1",
"level": 8,
"name": "VIP8",
"room_entry_notice_enabled": true
},
"server_time_ms": 1784073600000
}
}
```
本人进房首屏使用 HTTP 返回的 `data.effective_vip`。其他用户的进房效果使用当前房间 IM 消息。
## 11. VIP 装扮资源
### 11.1 我的资源列表
#### 接口地址
`GET /api/v1/users/me/resources?resource_type={resource_type}`
#### 参数
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
| `resource_type` | query | 否 | `avatar_frame``profile_card``vehicle``chat_bubble` 等资源类型 |
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_resources",
"data": {
"items": [
{
"entitlement_id": "ent_xxx",
"resource_id": 108,
"resource": {
"resource_id": 108,
"resource_code": "frame_vip9",
"resource_type": "avatar_frame",
"name": "VIP头像框",
"status": "active",
"asset_url": "https://cdn.example/frame.png",
"preview_url": "https://cdn.example/frame-preview.png",
"animation_url": "https://cdn.example/frame.svga",
"metadata_json": "{}",
"sort_order": 10
},
"status": "active",
"expires_at_ms": 1786665600000,
"equipped": true
}
],
"total": 1
}
}
```
### 11.2 佩戴资源
#### 接口地址
`POST /api/v1/users/me/resources/{resource_id}/equip`
#### 参数
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
| `resource_id` | path | 是 | 资源 ID |
无 body。
### 11.3 卸下资源
#### 接口地址
`DELETE /api/v1/users/me/resources/{resource_type}/unequip`
#### 参数
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
| `resource_type` | path | 是 | 资源类型 |
无 body。
### 11.4 当前装扮
#### 接口地址
- `GET /api/v1/appearance`
- `GET /api/v1/users/by-id/{user_id}/appearance`
#### 参数
第二个接口使用 path 参数 `user_id`
#### 返回值
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_appearance",
"data": {
"user_id": "10001",
"avatar_frame": {},
"profile_card": {},
"vehicle": {},
"mic_seat_animation": {},
"equipped_badges": [],
"badges": [],
"resources": [],
"server_time_ms": 1784073600000
}
}
```
## 12. 相关 IM
### 12.1 VIP 激活通知
通道:腾讯云 IM C2C `TIMCustomElem`
```json
{
"Ext": "vip_notice",
"Desc": "VipActivated",
"Data": "{\"event_type\":\"VipActivated\",\"event_id\":\"evt_xxx\",\"app_code\":\"fami\",\"transaction_id\":\"tx_xxx\",\"command_id\":\"vip_purchase_10001_4_1780100000000\",\"user_id\":\"10001\",\"source\":\"vip_purchase\",\"action\":\"upgrade\",\"previous_level\":3,\"previous_expires_at_ms\":1781396000000,\"level\":4,\"vip_name\":\"VIP4\",\"started_at_ms\":1780100000000,\"expires_at_ms\":1782692000000,\"program_type\":\"tiered_privilege_v1\",\"config_version\":7,\"expiry_policy\":\"replace_from_now\",\"created_at_ms\":1780100000000}"
}
```
Flutter 按 `event_id` 去重,然后调用 `GET /api/v1/vip/me`,不能直接使用 IM 内容生成权限。
### 12.2 进房通知
通道:当前房间腾讯云 IM 群 `TIMCustomElem`,不发送到全服群。
```json
{
"Ext": "room_system_message",
"Desc": "room_user_joined",
"Data": "{\"event_id\":\"evt_join_xxx\",\"room_id\":\"room_1001\",\"event_type\":\"room_user_joined\",\"actor_user_id\":10001,\"target_user_id\":10001,\"room_version\":18,\"role\":\"audience\",\"nickname\":\"Nina\",\"avatar\":\"https://cdn.example/avatar.png\",\"display_user_id\":\"100042\",\"pretty_display_user_id\":\"888042\",\"vip_program_type\":\"tiered_privilege_v1\",\"effective_vip_level\":\"8\",\"effective_vip_name\":\"VIP8\",\"room_entry_notice_enabled\":\"true\"}"
}
```
仅当 `event_type=room_user_joined``room_entry_notice_enabled=true` 时展示 VIP 进房效果。使用 `event_id` 去重。
### 12.3 上线全服通知
通道:全局播报群 `TIMCustomElem`
| 字段 | 值 |
|---|---|
| `Ext` | `im_broadcast` |
| `Desc` | `vip_online_notice` |
| `Data` | 下方 JSON 字符串 |
| `CloudCustomData` | 与 `Data` 相同 |
```json
{
"event_id": "vip_online_notice:10001:sha256_digest",
"broadcast_type": "vip_online_notice",
"scope": "global",
"app_code": "fami",
"user_id": 10001,
"sender_user_id": 10001,
"nickname": "Nina",
"avatar": "https://cdn.example/avatar.png",
"avatar_frame_url": "https://cdn.example/frame.png",
"avatar_frame_resource_id": 108,
"avatar_frame_code": "frame_vip9",
"avatar_frame_asset_url": "https://cdn.example/frame.png",
"avatar_frame_preview_url": "https://cdn.example/frame-preview.png",
"avatar_frame_animation_url": "https://cdn.example/frame-animation.svga",
"display_user_id": "888042",
"raw_display_user_id": "100042",
"pretty_display_user_id": "888042",
"vip_program_type": "tiered_privilege_v1",
"vip_level": 9,
"vip_name": "VIP9",
"message": "欢迎进入Fami祝你有美好的一天",
"action": {
"type": "none"
},
"sent_at_ms": 1784073610500
}
```
Flutter 使用 `event_id` 去重直接使用事件内的用户、VIP 和头像框快照渲染。
### 12.4 金币返现系统消息
接口:`GET /api/v1/messages?section=system&page_size=20&page_token=`
```json
{
"message_id": "msg_vip_rebate_xxx",
"title": "VIP金币返现",
"summary": "尊敬的VIP用户平台今日返现100金币请及时领取。",
"body": "尊敬的VIP用户平台今日返现100金币请及时领取。",
"icon_url": "",
"image_url": "",
"action_type": "vip_coin_rebate_claim",
"action_param": "{\"coin_amount\":100,\"expires_at_ms\":1784160000000,\"rebate_id\":\"vip_rebate_xxx\",\"task_day\":\"2026-07-15\"}",
"read": false,
"sent_at_ms": 1784073600000
}
```
Flutter 将 `action_param` 解析为 JSON打开页面后调用 `/vip/coin-rebates/statuses` 获取真实领取状态。
### 12.5 IM 登录与全局群
接口:`GET /api/v1/im/usersig`
Flutter 使用返回的 UserSig 登录腾讯云 IM并加入响应 `join_groups``type=global_broadcast` 的群组后,再消费 `vip_online_notice`