hyapp-server/docs/用户装扮Flutter对接文档.md
2026-06-16 17:00:06 +08:00

529 lines
13 KiB
Markdown
Raw Permalink 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.

# 用户装扮 Flutter 对接文档
## 通用说明
所有需要登录的接口都需要带:
```http
Authorization: Bearer <token>
```
统一返回外壳:
```json
{
"code": "OK",
"message": "OK",
"request_id": "xxx",
"data": {}
}
```
下文只描述 `data` 内部字段。
## 1. 获取我的装扮聚合
地址:
```http
GET /api/v1/appearance
GET /api/v1/users/me/appearance
```
参数:无。
返回值:
```json
{
"user_id": "42",
"avatar_frame": {
"resource_id": 8101,
"resource_code": "frame_gold",
"resource_type": "avatar_frame",
"name": "Gold Frame",
"asset_url": "https://cdn.example/frame.png",
"preview_url": "",
"animation_url": "https://cdn.example/frame.svga",
"metadata_json": "{}",
"entitlement_id": "ent-frame",
"expires_at_ms": 1999999999999
},
"profile_card": {},
"vehicle": {
"resource_id": 8102,
"resource_code": "vehicle_gold",
"resource_type": "vehicle",
"name": "Gold Vehicle",
"asset_url": "https://cdn.example/vehicle.png",
"preview_url": "",
"animation_url": "https://cdn.example/vehicle.svga",
"metadata_json": "{}",
"entitlement_id": "ent-vehicle",
"expires_at_ms": 1999999999999
},
"mic_seat_animation": {
"resource_id": 8106,
"resource_code": "mic_seat_wave_gold",
"resource_type": "mic_seat_animation",
"name": "Gold Mic Wave",
"asset_url": "https://cdn.example/mic-seat.png",
"preview_url": "",
"animation_url": "https://cdn.example/mic-seat.svga",
"metadata_json": "{}",
"entitlement_id": "ent-mic-seat-wave",
"expires_at_ms": 1999999999999
},
"equipped_badges": [],
"badges": [
{
"slot": "strip",
"position": 1,
"badge_form": "icon",
"resource_id": 8103,
"resource": {
"resource_id": 8103,
"resource_code": "badge_host",
"resource_type": "badge",
"name": "Host Badge",
"asset_url": "https://cdn.example/badge.png",
"preview_url": "",
"animation_url": "",
"metadata_json": "{}",
"entitlement_id": "",
"expires_at_ms": 0
},
"entitlement_id": "ent-badge",
"source_type": "achievement",
"source_id": "ach-host",
"pin_mode": "auto",
"updated_at_ms": 1778256000000
}
],
"badge_profile": {
"strip_badges": [],
"profile_tile_badges": [],
"honor_badges": [],
"server_time_ms": 1778256000000
},
"resources": [
{
"resource_id": 8106,
"resource_code": "mic_seat_wave_gold",
"resource_type": "mic_seat_animation",
"name": "Gold Mic Wave",
"asset_url": "https://cdn.example/mic-seat.png",
"preview_url": "",
"animation_url": "https://cdn.example/mic-seat.svga",
"metadata_json": "{}",
"entitlement_id": "ent-mic-seat-wave",
"expires_at_ms": 1999999999999
}
],
"server_time_ms": 1778256000000
}
```
字段说明:
| 字段 | 说明 |
|---|---|
| user_id | 用户 ID字符串 |
| avatar_frame | 当前佩戴头像框,未佩戴时为 `{}` |
| profile_card | 当前佩戴背景卡,未佩戴时为 `{}` |
| vehicle | 当前佩戴入场座驾,未佩戴时为 `{}` |
| mic_seat_animation | 当前佩戴麦位声波,未佩戴时为 `{}` |
| equipped_badges | wallet 当前佩戴的 badge 权益 |
| badges | 房间、公屏等高频展示用徽章,取 `strip_badges` |
| badge_profile | 完整徽章展示配置 |
| resources | 当前佩戴资源的完整列表 |
| server_time_ms | 服务端时间戳 |
说明:
- 过期佩戴不会返回。
- 头像框、背景卡、入场座驾、麦位声波来自 wallet 当前佩戴。
- 徽章展示配置来自 activity。
- 客户端渲染动画优先使用 `animation_url`,没有动画时用 `asset_url``preview_url` 兜底。
## 2. 获取我的资源背包
地址:
```http
GET /api/v1/users/me/resources
```
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| resource_type | string | 否 | `avatar_frame` / `profile_card` / `vehicle` / `badge` / `chat_bubble` |
示例:
```http
GET /api/v1/users/me/resources?resource_type=vehicle
```
返回值:
```json
{
"items": [
{
"entitlement_id": "ent-vehicle",
"resource_id": 8102,
"resource": {
"resource_id": 8102,
"resource_code": "vehicle_gold",
"resource_type": "vehicle",
"name": "Gold Vehicle",
"status": "active",
"usage_scopes": ["room_entry"],
"asset_url": "https://cdn.example/vehicle.png",
"preview_url": "",
"animation_url": "https://cdn.example/vehicle.svga",
"metadata_json": "{}"
},
"status": "active",
"quantity": 1,
"remaining_quantity": 1,
"effective_at_ms": 1778256000000,
"expires_at_ms": 1999999999999,
"source_grant_id": "grant-1",
"created_at_ms": 1778256000000,
"updated_at_ms": 1778256000000,
"equipped": true
}
],
"total": 1
}
```
字段说明:
| 字段 | 说明 |
|---|---|
| entitlement_id | 用户权益 ID佩戴时建议传这个值 |
| resource_id | 资源 ID |
| resource | 资源素材和基础信息 |
| status | 权益状态 |
| quantity | 总数量 |
| remaining_quantity | 剩余数量 |
| effective_at_ms | 生效时间 |
| expires_at_ms | 过期时间0 表示不过期 |
| equipped | 是否当前佩戴 |
## 3. 佩戴资源
地址:
```http
POST /api/v1/users/me/resources/{resource_id}/equip
```
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| resource_id | int64 | 是 | 资源 ID |
Body
```json
{
"entitlement_id": "ent-vehicle"
}
```
Body 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| entitlement_id | string | 否 | 用户权益 ID建议传 |
返回值:
```json
{
"entitlement_id": "ent-vehicle",
"resource_id": 8102,
"resource": {},
"status": "active",
"quantity": 1,
"remaining_quantity": 1,
"effective_at_ms": 1778256000000,
"expires_at_ms": 1999999999999,
"equipped": true
}
```
说明:
- 可佩戴类型:`avatar_frame``profile_card``vehicle``chat_bubble``badge``mic_seat_animation`
- `badge``mic_seat_animation` 发放成功后服务端默认佩戴。
- 过期资源不能佩戴。
- 不传 `entitlement_id` 时,服务端会选择当前有效权益。
## 4. 取消佩戴
地址:
```http
DELETE /api/v1/users/me/resources/{resource_type}/unequip
```
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| resource_type | string | 是 | `avatar_frame` / `profile_card` / `vehicle` / `chat_bubble` / `badge` |
返回值:
```json
{
"resource_type": "vehicle",
"unequipped": true,
"updated_at_ms": 1778256000000
}
```
说明:
- 重复取消不会报错。
- 用户本来没佩戴该类型时,`unequipped=false`
## 5. 批量获取房间展示资料
地址:
```http
GET /api/v1/users/room-display-profiles:batch
```
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| user_ids | string | 是 | 多个用户 ID逗号分隔 |
| user_id | string | 否 | 也支持重复传多个 `user_id` |
示例:
```http
GET /api/v1/users/room-display-profiles:batch?user_ids=42,43
```
返回值:
```json
{
"profiles": [
{
"user_id": "42",
"username": "Tom",
"avatar": "https://cdn.example/avatar.png",
"display_user_id": "100001",
"gender": "male",
"age": 22,
"country": "CN",
"country_name": "China",
"country_display_name": "China",
"country_flag": "CN",
"vip": {},
"level": {},
"badges": [],
"avatar_frame": {
"resource_id": 8101,
"resource_code": "frame_gold",
"resource_type": "avatar_frame",
"name": "Gold Frame",
"asset_url": "https://cdn.example/frame.png",
"preview_url": "",
"animation_url": "https://cdn.example/frame.svga",
"metadata_json": "{}",
"entitlement_id": "ent-frame",
"expires_at_ms": 1999999999999
},
"profile_card": {
"resource_id": 8104,
"resource_code": "profile_card_gold",
"resource_type": "profile_card",
"name": "Gold Profile Card",
"asset_url": "https://cdn.example/profile-card.png",
"preview_url": "",
"animation_url": "https://cdn.example/profile-card.mp4",
"metadata_json": "{\"profile_card_layout\":{\"source_width\":1136,\"source_height\":1680,\"content_top\":143,\"content_height\":1525}}",
"entitlement_id": "ent-profile-card",
"expires_at_ms": 1999999999999
},
"vehicle": {
"resource_id": 8102,
"resource_code": "vehicle_gold",
"resource_type": "vehicle",
"name": "Gold Vehicle",
"asset_url": "https://cdn.example/vehicle.png",
"preview_url": "",
"animation_url": "https://cdn.example/vehicle.svga",
"metadata_json": "{}",
"entitlement_id": "ent-vehicle",
"expires_at_ms": 1999999999999
},
"mic_seat_animation": {
"resource_id": 8106,
"resource_code": "mic_seat_wave_gold",
"resource_type": "mic_seat_animation",
"name": "Gold Mic Wave",
"asset_url": "https://cdn.example/mic-seat.png",
"preview_url": "",
"animation_url": "https://cdn.example/mic-seat.svga",
"metadata_json": "{}",
"entitlement_id": "ent-mic-seat-wave",
"expires_at_ms": 1999999999999
},
"charm": 0
}
]
}
```
说明:
- 房间、公屏、在线列表展示头像框用 `avatar_frame`
- 房间资料卡展示用 `profile_card`,从 `metadata_json.profile_card_layout` 解析 `content_top``content_height`
- 入场座驾展示用 `vehicle`
- 麦位声波展示用 `mic_seat_animation`
- 徽章条展示用 `badges`
- 过期佩戴不会返回。
## 6. 进房
地址:
```http
POST /api/v1/rooms/join
```
Body
```json
{
"room_id": "room_1001",
"command_id": "cmd-join-1001",
"role": "audience",
"password": "1234"
}
```
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| room_id | string | 是 | 房间 ID |
| command_id | string | 是 | 幂等 IDFlutter 每次进房动作生成一个 |
| role | string | 否 | 默认 `audience` |
| password | string | 否 | 锁房密码 |
返回值:
```json
{
"result": {
"applied": true,
"room_version": 9,
"server_time_ms": 1778256000000
},
"room": {},
"viewer": {},
"seats": [],
"contribution_rank": [],
"profiles": [
{
"user_id": "42",
"display_user_id": "100001",
"username": "Tom",
"avatar": "https://cdn.example/avatar.png"
}
],
"im": {
"group_id": "room_1001",
"need_join_group": true
},
"rtc": {
"need_token": true,
"available": true,
"token": {}
},
"server_time_ms": 1778256000000
}
```
说明:
- Flutter 不需要传入场座驾。
- 服务端会在进房时自动读取当前佩戴的 `vehicle`,写入房间 IM。
- `join` 返回里的 `profiles` 是基础资料。
- 房间增强资料用 `/users/room-display-profiles:batch`,或使用房间详情、在线用户列表里内嵌的 `profile` 字段。
## 7. 相关 IM
### 用户进房
腾讯 IM 群自定义消息:
```json
{
"event_id": "evt_xxx",
"room_id": "room_1001",
"event_type": "room_user_joined",
"actor_user_id": 42,
"target_user_id": 42,
"room_version": 9,
"entry_vehicle": {
"resource_id": 8102,
"resource_code": "vehicle_gold",
"name": "Gold Vehicle",
"asset_url": "https://cdn.example/vehicle.png",
"preview_url": "",
"animation_url": "https://cdn.example/vehicle.svga",
"metadata_json": "{}",
"entitlement_id": "ent-vehicle",
"expires_at_ms": 1999999999999
},
"attributes": {
"role": "audience"
}
}
```
Flutter 处理:
| 字段 | 说明 |
|---|---|
| event_type | `room_user_joined` 表示用户进入房间 |
| actor_user_id | 进房用户 ID |
| target_user_id | 进房用户 ID |
| room_version | 房间版本 |
| entry_vehicle | 入场座驾快照 |
| attributes.role | 用户进房角色,仅用于展示 |
处理规则:
- `entry_vehicle.resource_id > 0`:播放入场座驾。
- `entry_vehicle.animation_url`:优先用于动画播放。
- `entry_vehicle.asset_url` / `entry_vehicle.preview_url`:动画不可用时兜底展示。
- `entry_vehicle` 不存在或为空:普通进房,不播放座驾。
- 不要收到进房 IM 后再实时请求 `/appearance` 查询座驾,直接使用 IM 中的 `entry_vehicle` 快照。
## 8. Flutter 调用建议
1. 个人装扮页进入时调 `/api/v1/appearance`
2. 背包页调 `/api/v1/users/me/resources?resource_type=xxx`
3. 点击佩戴调 `POST /api/v1/users/me/resources/{resource_id}/equip`,成功后刷新 `/appearance`
4. 点击取消佩戴调 `DELETE /api/v1/users/me/resources/{resource_type}/unequip`,成功后刷新 `/appearance`
5. 个人主页信息卡背景使用个人资料返回的 `profile_bg_img`;该字段是用户资料字段,和 `profile_card` 装扮资源无关。
6. 房间、公屏、在线用户资料优先使用 `room-display-profiles:batch``avatar_frame``profile_card``vehicle``badges`
7. 收到 `room_user_joined` IM 时,只按 `entry_vehicle` 播放入场座驾。