hyapp-server/docs/flutter对接/房间锁房接口Flutter对接.md
2026-06-04 18:31:06 +08:00

333 lines
7.7 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 对接
## 地址
本地:
```text
http://127.0.0.1:13000
```
接口:
| 方法 | 地址 | 说明 |
| --- | --- | --- |
| `POST` | `/api/v1/rooms/password` | 房主设置或清空房间密码 |
| `POST` | `/api/v1/rooms/join` | 进入房间;锁房时必须传密码 |
| `GET` | `/api/v1/rooms` | 房间列表和搜索,返回 `locked` |
| `GET` | `/api/v1/rooms/feeds` | Mine 页房间流,返回 `locked` |
请求头:
| Header | 必填 | 说明 |
| --- | --- | --- |
| `Authorization` | 是 | `Bearer <access_token>` |
| `X-App-Code` | 否 | App 编码,例如 `lalu` |
## 设置房间密码
`POST /api/v1/rooms/password`
参数:
| 字段 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- |
| `room_id` | 是 | string | 房间 ID |
| `command_id` | 是 | string | 本次设置动作的幂等键,同一次重试复用 |
| `locked` | 是 | bool | `true` 设置密码,`false` 清空密码 |
| `password` | `locked=true` 必填 | string | 房间密码,服务端 trim 后最长 64 字符;`locked=false` 时忽略 |
请求:
```json
{
"room_id": "room_10001",
"command_id": "cmd_room_password_room_10001_1710000000",
"locked": true,
"password": "1234"
}
```
返回:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_abc",
"data": {
"result": {
"applied": true,
"room_version": 12,
"server_time_ms": 1710000000456
},
"room": {
"room_id": "room_10001",
"im_group_id": "room_10001",
"room_short_id": "100001",
"title": "Live Room",
"cover_url": "https://cdn.example/room.png",
"owner_user_id": "10001",
"mode": "voice",
"status": "active",
"chat_enabled": true,
"locked": true,
"heat": 1000,
"online_count": 12,
"seat_count": 15,
"occupied_seat_count": 2,
"version": 12
},
"seats": [],
"server_time_ms": 1710000000456
}
}
```
返回字段:
| 字段 | 说明 |
| --- | --- |
| `data.result.applied` | 本次命令是否改变房间状态 |
| `data.result.room_version` | 房间最新版本 |
| `data.room.locked` | 最新锁房状态 |
| `data.room.version` | 房间快照版本 |
| `data.seats` | 当前麦位列表 |
## 清空房间密码
`POST /api/v1/rooms/password`
参数:
| 字段 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- |
| `room_id` | 是 | string | 房间 ID |
| `command_id` | 是 | string | 本次清空动作的幂等键 |
| `locked` | 是 | bool | 固定传 `false` |
请求:
```json
{
"room_id": "room_10001",
"command_id": "cmd_room_unlock_room_10001_1710000100",
"locked": false
}
```
返回同设置密码接口,`data.room.locked=false`
## 进入锁房房间
`POST /api/v1/rooms/join`
参数:
| 字段 | 必填 | 类型 | 说明 |
| --- | --- | --- | --- |
| `room_id` | 是 | string | 房间 ID |
| `command_id` | 是 | string | 本次进房动作的幂等键,同一次重试复用 |
| `role` | 否 | string | 当前传 `audience` |
| `password` | 锁房时必填 | string | 本次入房密码 |
请求:
```json
{
"room_id": "room_10001",
"command_id": "cmd_join_room_10001_20001_1710000000",
"role": "audience",
"password": "1234"
}
```
返回:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_abc",
"data": {
"result": {
"applied": true,
"room_version": 13,
"server_time_ms": 1710000001456
},
"room": {
"room_id": "room_10001",
"im_group_id": "room_10001",
"locked": true,
"version": 13
},
"viewer": {
"user_id": "20001",
"role": "audience",
"joined_at_ms": 1710000001456,
"last_seen_at_ms": 1710000001456
},
"im": {
"group_id": "room_10001",
"need_join_group": true
},
"rtc": {
"need_token": true,
"available": true
},
"server_time_ms": 1710000001456
}
}
```
返回字段:
| 字段 | 说明 |
| --- | --- |
| `data.room.locked` | 当前房间是否锁房 |
| `data.viewer.user_id` | 当前用户 ID |
| `data.im.group_id` | JoinRoom 成功后需要加入的腾讯 IM 房间群 |
| `data.im.need_join_group` | `true` 时 Flutter 调腾讯 IM `joinGroup` |
| `data.rtc.available` | 是否拿到 RTC token |
进房规则:
| 场景 | 结果 |
| --- | --- |
| 未锁房 | `password` 可不传 |
| 已锁房且密码正确 | JoinRoom 成功,再加入 IM 房间群 |
| 已锁房且密码错误或缺失 | 返回 `403 PERMISSION_DENIED`,不要加入 IM 房间群 |
| 房主进自己房间 | 不需要密码 |
| 已在房间内重复 JoinRoom | 非房主仍要按当前最新密码校验;密码错误返回 `403 PERMISSION_DENIED` |
## 房间列表 locked 字段
`GET /api/v1/rooms?tab=hot&query=100001`
`GET /api/v1/rooms/feeds?tab=followed`
返回:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_abc",
"data": {
"rooms": [
{
"room_id": "room_10001",
"im_group_id": "room_10001",
"title": "Live Room",
"locked": true,
"online_count": 12,
"visible_region_id": 1,
"room_short_id": "100001"
}
],
"next_cursor": ""
}
}
```
字段:
| 字段 | 说明 |
| --- | --- |
| `rooms[].locked` | 是否锁房;`true` 时进房前弹密码输入 |
| `rooms[].im_group_id` | 房间 IM 群 ID只能在 JoinRoom 成功后使用 |
| `rooms[].visible_region_id` | 房间区域 |
## 相关 IM
### JoinRoom 成功后加入房间群
接口 `POST /api/v1/rooms/join` 成功后Flutter 使用:
```text
data.im.group_id
```
调用腾讯 IM SDK 加入房间群。
规则:
| 场景 | Flutter 行为 |
| --- | --- |
| JoinRoom 成功 | 调腾讯 IM `joinGroup(data.im.group_id)` |
| JoinRoom 返回 `PERMISSION_DENIED` | 不调用 `joinGroup` |
| JoinRoom 返回 `ROOM_CLOSED``NOT_FOUND` | 不调用 `joinGroup`,刷新列表 |
### 房间密码变更区域通知
房主设置或清空密码后,服务端会发区域播报 IM不再发房间群 IM。
Flutter 需要监听用户所在区域播报群的自定义消息:
```json
{
"event_id": "room_password_changed:evt_room_xxx",
"broadcast_type": "room_password_changed",
"scope": "region",
"app_code": "lalu",
"region_id": 1,
"room_id": "room_10001",
"actor_user_id": 10001,
"locked": true,
"sent_at_ms": 1710000000456,
"room_version": 12,
"source_event_id": "evt_room_xxx",
"action": {
"type": "refresh_room",
"room_id": "room_10001"
}
}
```
字段:
| 字段 | 说明 |
| --- | --- |
| `broadcast_type` | 固定 `room_password_changed` |
| `scope` | 固定 `region` |
| `region_id` | 区域 ID |
| `room_id` | 发生变化的房间 |
| `locked` | 最新锁房状态 |
| `room_version` | 房间版本;本地只用更大的版本覆盖旧状态 |
| `action.type` | 固定 `refresh_room` |
处理:
| 当前页面 | 行为 |
| --- | --- |
| 房间列表页 | 更新对应房间卡片 `locked` |
| 房间详情页 | 如果 `room_id` 相同,刷新房间快照或更新本地 `locked` |
| 密码弹窗打开中 | 收到同房间更新后,按最新 `locked` 状态决定是否关闭弹窗 |
### 房间群系统消息
`room_password_changed` 不走房间群 IM。房间内用户刷新锁房状态依赖区域播报或接口返回的 `room.locked`
## 错误返回
失败统一返回:
```json
{
"code": "PERMISSION_DENIED",
"message": "permission denied",
"request_id": "req_abc"
}
```
常见错误:
| HTTP | code | 说明 |
| --- | --- | --- |
| `400` | `INVALID_ARGUMENT` | 参数缺失、设置密码时密码为空或超过 64 字符 |
| `401` | `UNAUTHORIZED` | token 无效或过期 |
| `403` | `PERMISSION_DENIED` | 非房主设置密码、进房密码错误、用户被 ban |
| `404` | `NOT_FOUND` | 房间不存在 |
| `409` | `ROOM_CLOSED` | 房间已关闭 |
| `409` | `CONFLICT` | 同一个 `command_id` 被不同请求体复用 |