449 lines
13 KiB
Markdown
449 lines
13 KiB
Markdown
# 语音房房间用户列表前端接入文档
|
||
|
||
## 1. 接入范围
|
||
|
||
|
||
- 房间在线用户列表接口。
|
||
- 设置或取消房间管理员接口。
|
||
- 相关腾讯云 IM 房间系统消息。
|
||
- 前端错误处理建议。
|
||
|
||
## 2. 通用约定
|
||
|
||
### 2.1 鉴权
|
||
|
||
所有接口都需要登录态:
|
||
|
||
```http
|
||
Authorization: Bearer <access_token>
|
||
```
|
||
|
||
### 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` 去重。
|