# 新版 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。
- 业务切日使用 UTC，Flutter 仅在展示时转换本地时间。
- 成功响应统一格式：

```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`。
