# 语音房房间用户列表前端接入文档 ## 1. 接入范围 - 房间在线用户列表接口。 - 设置或取消房间管理员接口。 - 相关腾讯云 IM 房间系统消息。 - 前端错误处理建议。 ## 2. 通用约定 ### 2.1 鉴权 所有接口都需要登录态: ```http Authorization: Bearer ``` ### 2.2 ID 类型 HTTP 返回里的用户 ID 统一按字符串处理,避免 JavaScript 大整数精度问题。 示例: ```json { "user_id": "10001" } ``` ### 2.3 时间类型 所有时间字段都是毫秒时间戳: ```json { "server_time_ms": 1760000006000 } ``` ### 2.4 房间身份 房间用户身份拆成两个字段: | 字段 | 说明 | | --- | --- | | `is_owner` | 是否是当前房间房主 | | `room_role` | 是否是当前房间管理员,只表示管理员集合 | `room_role` 只会返回: | 值 | 说明 | | --- | --- | | `normal` | 当前房间普通用户 | | `admin` | 当前房间管理员 | 注意: - 房主不是管理员,房主用户返回 `is_owner = true`。 - 房主如果不在管理员集合里,`room_role` 返回 `normal`。 - 前端展示“房主”标识时看 `is_owner`,展示“管理员”标识时看 `room_role = admin`。 - 当前登录用户是否展示管理员操作入口,优先看房间详情里的 `permissions.is_owner`;如果只在用户列表内判断,则看当前登录用户对应 item 的 `is_owner`。 - `role` 是进房 presence 角色,例如 `audience`,不要用于判断房主或管理员。 ## 3. 查询房间在线用户列表 ### 3.1 接口定义 ```http GET /api/v1/rooms/{room_id}/online-users ``` ### 3.2 Path 参数 | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `room_id` | string | 是 | 房间 ID | ### 3.3 Query 参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | `page` | int | 否 | `1` | 页码,从 `1` 开始 | | `page_size` | int | 否 | `50` | 每页数量,最大 `100` | | `sort` | string | 否 | `online` | `online` 按在线活跃排序;`gift_value` 按礼物价值排序 | ### 3.4 请求示例 ```bash curl -H "Authorization: Bearer $TOKEN" \ "https://api.global-interaction.com/api/v1/rooms/room_10001/online-users?page=1&page_size=50" ``` ### 3.5 返回示例 ```json { "items": [ { "user_id": "10001", "role": "audience", "is_owner": true, "room_role": "normal", "gift_value": 188800, "joined_at_ms": 1760000000000, "last_seen_at_ms": 1760000005000, "profile": { "user_id": "10001", "username": "Alice", "avatar": "https://example.com/a.png", "display_user_id": "163001", "gender": "female", "age": 26, "country": "US", "country_name": "United States", "country_display_name": "United States", "country_flag": "🇺🇸" } }, { "user_id": "10002", "role": "audience", "is_owner": false, "room_role": "admin", "gift_value": 0, "joined_at_ms": 1760000001000, "last_seen_at_ms": 1760000004000, "profile": { "user_id": "10002", "username": "Bob", "avatar": "https://example.com/b.png", "display_user_id": "163002", "gender": "male", "age": 24, "country": "SG", "country_name": "Singapore", "country_display_name": "Singapore", "country_flag": "🇸🇬" } } ], "total": 2, "page": 1, "page_size": 50, "server_time_ms": 1760000006000 } ``` ### 3.6 返回字段 | 字段 | 类型 | 说明 | | --- | --- | --- | | `items` | array | 当前页在线用户 | | `items[].user_id` | string | 用户 ID | | `items[].role` | string | 进房 presence 角色 | | `items[].is_owner` | bool | 是否是当前房间房主 | | `items[].room_role` | string | 当前房间管理员身份:`normal` 或 `admin` | | `items[].gift_value` | int64 | 当前房间内累计送礼价值,未送礼返回 `0` | | `items[].joined_at_ms` | int64 | 进房时间 | | `items[].last_seen_at_ms` | int64 | 最近房间业务心跳时间 | | `items[].profile.user_id` | string | 用户 ID | | `items[].profile.username` | string | 用户昵称 | | `items[].profile.avatar` | string | 用户头像 | | `items[].profile.display_user_id` | string | 展示 ID | | `items[].profile.gender` | string | 用户性别,来自 user-service | | `items[].profile.age` | int | 用户年龄,由后端按生日计算;生日为空或非法时返回 `0` | | `items[].profile.country` | string | 用户国家码,ISO Alpha-2,例如 `US` | | `items[].profile.country_name` | string | 国家名称,来自国家主数据 | | `items[].profile.country_display_name` | string | 国家展示名称,来自国家主数据 | | `items[].profile.country_flag` | string | 国家旗帜 emoji,由国家码生成;国家码为空或非法时返回空字符串 | | `total` | int64 | 在线用户总数 | | `page` | int | 当前页 | | `page_size` | int | 当前每页数量 | | `server_time_ms` | int64 | 服务端时间 | ### 3.7 前端展示建议 - `gift_value` 展示的是该用户在当前房间内“送出”的礼物价值,不是收到的礼物价值。 - `gift_value = 0` 时仍然展示该用户。 - 房主标识看 `is_owner = true`。 - 管理员标识看 `room_role = admin`。 - 房主不是管理员,不要因为 `is_owner = true` 就把管理员标识点亮。 - 在线列表只返回 `gender` 和 `age`,不会返回用户生日 `birth`。 - `age = 0` 表示用户未填写生日或生日格式异常,前端可隐藏年龄。 - 国家展示优先使用 `country_display_name`,没有时可降级使用 `country_name` 或 `country`。 - `country_flag` 是 emoji 字符串,前端无需自行按国家码换算。 - 用户列表弹窗打开时,可以先拉第一页;滚动到底再拉下一页。 - 收到相关 IM 后,如果列表弹窗正在展示,可以局部更新;状态不确定时重新拉接口。 ## 4. 设置或取消管理员,只有房主才显示 ### 4.1 接口定义 ```http POST /api/v1/rooms/admin/set Content-Type: application/json ``` ### 4.2 请求参数 | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `room_id` | string | 是 | 房间 ID | | `command_id` | string | 是 | 客户端生成的幂等 ID,重试同一次操作时保持不变 | | `target_user_id` | string/int64 | 是 | 被设置或取消管理员的用户 ID | | `enabled` | bool | 是 | `true` 设置管理员;`false` 取消管理员 | ### 4.3 设置管理员请求示例 ```json { "room_id": "room_10001", "command_id": "cmd-10001", "target_user_id": "10002", "enabled": true } ``` ### 4.4 取消管理员请求示例 ```json { "room_id": "room_10001", "command_id": "cmd-10002", "target_user_id": "10002", "enabled": false } ``` ### 4.5 返回示例 建议后端返回: ```json { "result": { "applied": true, "room_version": 12 }, "target_user_id": "10002", "room_role": "admin", "server_time_ms": 1760000006000 } ``` 字段说明: | 字段 | 类型 | 说明 | | --- | --- | --- | | `result.applied` | bool | 本次请求是否实际改变了房间状态 | | `result.room_version` | int64 | 房间状态版本 | | `target_user_id` | string | 被操作用户 | | `room_role` | string | 操作后的身份,`admin` 或 `normal` | | `server_time_ms` | int64 | 服务端时间 | 如果后端第一版仍直接返回完整 room-service gRPC 结构,前端只需要关注命令是否成功;最终用户列表状态以接口重拉或 IM 刷新为准。 ### 4.6 前端操作边界 - 只有房主可以设置或取消管理员。 - 房主可以操作所有非房主用户的管理员身份。 - 管理员之间不可互相设置或取消管理员。 - 房主不是管理员,不允许对房主执行设置或取消管理员。 - 目标用户必须当前在房间内。 - 前端重试同一次操作时,必须复用同一个 `command_id`。 - 操作成功后,可以等待 `room_admin_changed` IM,也可以直接刷新用户列表。 ## 5. 错误处理 ### 5.1 通用错误 | HTTP 状态 | 场景 | 前端处理 | | --- | --- | --- | | `401` | 未登录或 token 失效 | 跳登录或刷新登录态 | | `403` | 无权限,例如用户不在房间或不是房主 | 提示无权限,并刷新房间状态 | | `404` | 房间不存在或目标用户不在房间 | 提示目标不存在或用户已离开 | | `409` | 状态冲突,例如目标用户是房主 | 提示当前状态不允许操作,并刷新用户列表 | | `5xx` | 服务端异常 | 保留当前 UI,提示稍后重试 | ### 5.2 查询用户列表错误 | 场景 | 前端处理 | | --- | --- | | 当前用户不在房间 | 关闭用户列表弹窗,刷新当前房间状态 | | 分页为空 | 正常展示空列表或结束加载更多 | | `page_size` 超过上限 | 使用服务端返回的 `page_size` 为准 | ### 5.3 设置管理员错误 | 场景 | 前端处理 | | --- | --- | | 非房主操作 | 隐藏或禁用管理入口,提示无权限 | | 操作目标是房主 | 不展示管理员操作入口;如果服务端返回错误,刷新列表 | | 目标用户已离房 | 提示用户已离开,刷新列表 | | 重复点击 | 前端按钮进入 loading;同一次请求复用同一个 `command_id` | | 请求超时 | 可以用同一个 `command_id` 重试 | ## 6. 相关 IM ### 6.1 IM 消息形态 房间系统消息通过腾讯云 IM `TIMCustomElem` 下发。 前端解析入口: ```text TIMCustomElem.MsgContent.Data ``` `Data` 是 JSON 字符串。 示例: ```json { "event_id": "evt-admin-10001", "room_id": "room_10001", "event_type": "room_admin_changed", "actor_user_id": 10001, "target_user_id": 10002, "room_version": 12, "attributes": { "enabled": "true" } } ``` 通用字段: | 字段 | 类型 | 说明 | | --- | --- | --- | | `event_id` | string | 事件唯一 ID,前端用于去重 | | `room_id` | string | 房间 ID | | `event_type` | string | 事件类型 | | `actor_user_id` | int64 | 操作人 | | `target_user_id` | int64 | 被操作人 | | `room_version` | int64 | 房间版本 | | `attributes` | object | 字符串键值扩展字段 | ### 6.2 管理员变更 IM 事件类型: ```text room_admin_changed ``` 消息示例: ```json { "event_id": "evt-admin-10001", "room_id": "room_10001", "event_type": "room_admin_changed", "actor_user_id": 10001, "target_user_id": 10002, "room_version": 12, "attributes": { "enabled": "true" } } ``` 字段说明: | 字段 | 说明 | | --- | --- | | `actor_user_id` | 发起操作的房主 | | `target_user_id` | 被设置或取消管理员的用户 | | `attributes.enabled` | `"true"` 设置管理员;`"false"` 取消管理员 | 前端处理: - 使用 `event_id` 去重。 - 如果当前用户是 `target_user_id`,更新当前用户管理权限 UI。 - 如果用户列表已打开,更新对应用户的 `room_role`。 - 如果列表数据不完整,重新请求在线用户列表接口。 ### 6.3 送礼成功 IM 事件类型: ```text room_gift_sent ``` 消息示例: ```json { "event_id": "evt-gift-10001", "room_id": "room_10001", "event_type": "room_gift_sent", "actor_user_id": 10002, "target_user_id": 10003, "gift_value": 5200, "room_heat": 999999, "room_version": 13, "attributes": { "gift_id": "rose", "pool_id": "", "gift_count": "10", "billing_receipt_id": "receipt-10001", "coin_spent": "5200", "gift_point_added": "5200", "price_version": "v1" } } ``` 字段说明: | 字段 | 说明 | | --- | --- | | `actor_user_id` | 送礼用户,用户列表里需要累加 `gift_value` 的用户 | | `target_user_id` | 收礼用户 | | `gift_value` | 本次送礼价值增量 | | `room_heat` | 送礼后的房间热度 | | `attributes.gift_id` | 礼物 ID | | `attributes.gift_count` | 礼物数量,字符串 | | `attributes.billing_receipt_id` | 钱包结算流水 | 前端处理: - 如果用户列表已打开,并且当前页包含 `actor_user_id`,可以执行 `gift_value += event.gift_value`。 - 如果当前列表按 `gift_value` 排序,建议重新拉接口,避免分页顺序错乱。 - 如果当前页不包含 `actor_user_id`,不要手动插入用户,分页完整性以接口为准。 ### 6.4 进出房 IM 用户列表也会受进出房影响: | 事件 | 建议处理 | | --- | --- | | `room_user_joined` | 用户列表打开时重新拉列表 | | `room_user_left` | 用户列表打开时移除用户或重新拉列表 | ## 7. 前端刷新策略 推荐策略: 1. 打开用户列表弹窗时,请求 `GET /api/v1/rooms/{room_id}/online-users`。 2. 收到 `room_admin_changed` 时,局部更新 `room_role`;不确定时重拉。 3. 收到 `room_gift_sent` 时,局部累加 `gift_value`;如果当前排序依赖礼物价值,重拉。 4. 收到 `room_user_joined` 或 `room_user_left` 时,列表打开状态下重拉。 5. 所有 IM 使用 `event_id` 去重。