# 用户装扮 Flutter 对接文档 ## 通用说明 所有需要登录的接口都需要带: ```http Authorization: Bearer ``` 统一返回外壳: ```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 | 是 | 幂等 ID,Flutter 每次进房动作生成一个 | | 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` 播放入场座驾。