386 lines
12 KiB
Markdown
386 lines
12 KiB
Markdown
# 房间背景图 Flutter 对接
|
||
|
||
本文描述 Flutter App 对接房间背景图的 HTTP 接口和腾讯云 IM 房间消息。房间背景图是房间维度素材:上传文件只拿 URL,保存接口把 URL 归档到房间素材列表,设置接口才会改变当前房间背景并广播房间 IM 消息。
|
||
|
||
## 基础地址
|
||
|
||
本地开发地址:
|
||
|
||
```text
|
||
http://127.0.0.1:13000
|
||
```
|
||
|
||
线上环境使用当前 App 配置里的 gateway base URL。下面所有路径都拼在 gateway base URL 后。
|
||
|
||
## 请求头
|
||
|
||
| Header | 必填 | 示例 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `Authorization` | 是 | `Bearer <access_token>` | 登录 access token;服务端从 token 读取当前用户 ID。 |
|
||
| `X-App-Code` | 否 | `lalu` | App 编码;登录态接口最终以 token 内的 `app_code` 为准。 |
|
||
| `Content-Type` | POST JSON 必填 | `application/json` | 保存和设置背景使用 JSON。 |
|
||
| `Content-Type` | 上传必填 | `multipart/form-data` | 上传文件使用 multipart。 |
|
||
|
||
统一响应 envelope:
|
||
|
||
```json
|
||
{
|
||
"code": "OK",
|
||
"message": "ok",
|
||
"request_id": "req_abc",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
客户端分支判断只看 `code`,`message` 只用于兜底展示,`request_id` 要写入日志。
|
||
|
||
## 调用顺序
|
||
|
||
```text
|
||
1. 房主选择本地图片
|
||
2. POST /api/v1/files/upload 上传图片,拿 data.url
|
||
3. POST /api/v1/rooms/backgrounds/save 保存到房间背景素材列表
|
||
4. GET /api/v1/rooms/{room_id}/backgrounds 刷新背景列表
|
||
5. POST /api/v1/rooms/backgrounds/set 设置当前背景
|
||
6. 当前房间内所有用户通过 IM 收到 room_background_changed
|
||
7. 房间页用 data.room.background_url 或 IM attributes.room_background_url 刷新背景
|
||
```
|
||
|
||
## 上传图片
|
||
|
||
背景图先走通用文件上传。该接口不改变房间状态,只返回对象存储 URL。
|
||
|
||
```http
|
||
POST /api/v1/files/upload
|
||
Authorization: Bearer <access_token>
|
||
X-App-Code: lalu
|
||
Content-Type: multipart/form-data
|
||
|
||
file=<binary>
|
||
```
|
||
|
||
请求字段:
|
||
|
||
| 字段 | 必填 | 类型 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `file` | 是 | multipart file | 单文件最大 20MB。背景建议传图片文件;服务端通用入口不绑定房间语义。 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": "OK",
|
||
"message": "ok",
|
||
"request_id": "req_upload_bg",
|
||
"data": {
|
||
"url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png",
|
||
"object_key": "app/files/10001/1778000000000_xxx.png",
|
||
"content_type": "image/png",
|
||
"size_bytes": 345678
|
||
}
|
||
}
|
||
```
|
||
|
||
客户端只需要把 `data.url` 传给保存背景接口。
|
||
|
||
## 保存背景图
|
||
|
||
保存背景图把 URL 写入当前房间的背景素材列表。保存成功不会改变当前房间背景,也不会发房间 IM 消息。
|
||
|
||
```http
|
||
POST /api/v1/rooms/backgrounds/save
|
||
Authorization: Bearer <access_token>
|
||
X-App-Code: lalu
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"room_id": "lalu_abc123",
|
||
"image_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png"
|
||
}
|
||
```
|
||
|
||
请求字段:
|
||
|
||
| 字段 | 必填 | 类型 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `room_id` | 是 | string | 房间 ID。 |
|
||
| `image_url` | 是 | string | 上传接口返回的 `data.url`;服务端 trim 后最长 1024 字符。 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": "OK",
|
||
"message": "ok",
|
||
"request_id": "req_bg_save",
|
||
"data": {
|
||
"background": {
|
||
"background_id": 101,
|
||
"room_id": "lalu_abc123",
|
||
"image_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png",
|
||
"created_by_user_id": "10001",
|
||
"created_at_ms": 1778000000123,
|
||
"updated_at_ms": 1778000000123
|
||
},
|
||
"server_time_ms": 1778000000123
|
||
}
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `background.background_id` | int64 | 设置当前背景时使用。Flutter 如果统一按字符串保存 ID,也可以转成字符串保存;提交时服务端支持数字或数字字符串。 |
|
||
| `background.image_url` | string | 背景图展示 URL。 |
|
||
| `background.created_by_user_id` | string | 保存素材的用户 ID。 |
|
||
| `server_time_ms` | int64 | 服务端时间,Unix epoch milliseconds。 |
|
||
|
||
## 获取背景图列表
|
||
|
||
房主打开房间背景编辑面板时调用。
|
||
|
||
```http
|
||
GET /api/v1/rooms/{room_id}/backgrounds
|
||
Authorization: Bearer <access_token>
|
||
X-App-Code: lalu
|
||
```
|
||
|
||
路径参数:
|
||
|
||
| 参数 | 必填 | 类型 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `room_id` | 是 | string | 房间 ID。 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": "OK",
|
||
"message": "ok",
|
||
"request_id": "req_bg_list",
|
||
"data": {
|
||
"backgrounds": [
|
||
{
|
||
"background_id": 101,
|
||
"room_id": "lalu_abc123",
|
||
"image_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png",
|
||
"created_by_user_id": "10001",
|
||
"created_at_ms": 1778000000123,
|
||
"updated_at_ms": 1778000000123
|
||
}
|
||
],
|
||
"selected_background_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png",
|
||
"server_time_ms": 1778000000456
|
||
}
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `backgrounds` | array | 房间保存过的背景图,按最近保存优先。 |
|
||
| `selected_background_url` | string | 当前生效背景;没有设置过时为空或缺省。 |
|
||
| `server_time_ms` | int64 | 服务端时间。 |
|
||
|
||
## 设置当前背景
|
||
|
||
设置当前背景会改变 Room Cell 快照,返回最新房间数据,并向房间 IM 群广播 `room_background_changed`。
|
||
|
||
```http
|
||
POST /api/v1/rooms/backgrounds/set
|
||
Authorization: Bearer <access_token>
|
||
X-App-Code: lalu
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"room_id": "lalu_abc123",
|
||
"command_id": "cmd_background_lalu_abc123_10001_1778000000000",
|
||
"background_id": "101"
|
||
}
|
||
```
|
||
|
||
请求字段:
|
||
|
||
| 字段 | 必填 | 类型 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `room_id` | 是 | string | 房间 ID。 |
|
||
| `command_id` | 是 | string | 房间命令幂等键;同一次点击超时重试必须复用。 |
|
||
| `background_id` | 是 | int64/string | 背景素材 ID,必须来自当前房间的背景列表。 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": "OK",
|
||
"message": "ok",
|
||
"request_id": "req_bg_set",
|
||
"data": {
|
||
"result": {
|
||
"applied": true,
|
||
"room_version": 18,
|
||
"server_time_ms": 1778000000789
|
||
},
|
||
"room": {
|
||
"room_id": "lalu_abc123",
|
||
"im_group_id": "lalu_abc123",
|
||
"room_short_id": "100001",
|
||
"title": "Live Room",
|
||
"cover_url": "https://cdn.example.com/room.png",
|
||
"background_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png",
|
||
"description": "welcome",
|
||
"owner_user_id": "10001",
|
||
"mode": "voice",
|
||
"status": "active",
|
||
"chat_enabled": true,
|
||
"locked": false,
|
||
"heat": 1000,
|
||
"online_count": 12,
|
||
"seat_count": 10,
|
||
"occupied_seat_count": 2,
|
||
"visible_region_id": 1001,
|
||
"version": 18
|
||
},
|
||
"background": {
|
||
"background_id": 101,
|
||
"room_id": "lalu_abc123",
|
||
"image_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png",
|
||
"created_by_user_id": "10001",
|
||
"created_at_ms": 1778000000123,
|
||
"updated_at_ms": 1778000000123
|
||
},
|
||
"server_time_ms": 1778000000789
|
||
}
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `result.applied` | bool | `true` 表示本次命令产生状态变更;同一 `command_id` 重试可能为 `false`。 |
|
||
| `result.room_version` | int64 | 命令完成后的房间版本。 |
|
||
| `room.background_url` | string | 当前生效背景图。客户端设置成功后直接用它刷新 UI。 |
|
||
| `background` | object | 被设置的背景素材。 |
|
||
|
||
## 房间详情返回
|
||
|
||
进房或详情页重建时不需要额外查背景列表。当前生效背景在房间详情里返回:
|
||
|
||
```http
|
||
GET /api/v1/rooms/{room_id}/detail
|
||
Authorization: Bearer <access_token>
|
||
X-App-Code: lalu
|
||
```
|
||
|
||
关键字段:
|
||
|
||
```json
|
||
{
|
||
"data": {
|
||
"room": {
|
||
"room_id": "lalu_abc123",
|
||
"background_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png",
|
||
"version": 18
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
客户端进入房间页时用 `data.room.background_url` 初始化背景。收到 IM 后如果 `room_version` 更大,再覆盖本地背景。
|
||
|
||
## 腾讯 IM 消息
|
||
|
||
设置背景成功后,room-service 会向房间 IM 群发送 TIMCustomElem。Flutter 需要监听群自定义消息并解析 JSON。
|
||
|
||
消息负载是 `tencentim.RoomEvent` JSON:
|
||
|
||
```json
|
||
{
|
||
"event_id": "evt_xxx",
|
||
"room_id": "lalu_abc123",
|
||
"event_type": "room_background_changed",
|
||
"actor_user_id": 10001,
|
||
"room_version": 18,
|
||
"attributes": {
|
||
"background_id": "101",
|
||
"room_background_url": "https://cdn.example.com/app/files/10001/1778000000000_xxx.png"
|
||
}
|
||
}
|
||
```
|
||
|
||
腾讯 IM 自定义消息字段:
|
||
|
||
| 字段 | 值 | 说明 |
|
||
| --- | --- | --- |
|
||
| `TIMCustomElem.data` | 上面的 JSON 字符串 | 业务负载。 |
|
||
| `TIMCustomElem.desc` | `room_background_changed` | 事件类型。 |
|
||
| `TIMCustomElem.extension` | `room_system_message` | 房间系统消息标记。 |
|
||
|
||
客户端处理规则:
|
||
|
||
| 条件 | 处理 |
|
||
| --- | --- |
|
||
| `event_type != room_background_changed` | 交给其他房间消息处理器。 |
|
||
| `room_id` 不是当前房间 | 忽略。 |
|
||
| `event_id` 已处理过 | 忽略,避免重放或 SDK 重复投递。 |
|
||
| `room_version` 小于本地房间版本 | 忽略旧消息。 |
|
||
| `room_version` 大于等于本地房间版本 | 设置房间背景为 `attributes.room_background_url`,并更新本地 `room_version`。 |
|
||
|
||
Flutter 伪代码:
|
||
|
||
```dart
|
||
void onRoomCustomMessage(String data) {
|
||
final event = jsonDecode(data) as Map<String, dynamic>;
|
||
if (event['event_type'] != 'room_background_changed') return;
|
||
if (event['room_id'] != currentRoomId) return;
|
||
|
||
final eventId = event['event_id'] as String? ?? '';
|
||
if (handledEventIds.contains(eventId)) return;
|
||
|
||
final version = (event['room_version'] as num?)?.toInt() ?? 0;
|
||
if (version < localRoomVersion) return;
|
||
|
||
final attrs = (event['attributes'] as Map?)?.cast<String, dynamic>() ?? {};
|
||
final backgroundUrl = attrs['room_background_url'] as String? ?? '';
|
||
handledEventIds.add(eventId);
|
||
localRoomVersion = version;
|
||
setRoomBackground(backgroundUrl);
|
||
}
|
||
```
|
||
|
||
## 错误处理
|
||
|
||
| code | HTTP | 常见场景 | Flutter 处理 |
|
||
| --- | --- | --- | --- |
|
||
| `INVALID_ARGUMENT` | 400 | `room_id` 格式不对、`image_url` 为空、`background_id` 为空、上传文件过大或格式不合法。 | 停止当前动作,提示重新选择图片或刷新页面后重试。 |
|
||
| `UNAUTHORIZED` | 401 | access token 缺失或过期。 | 回登录或刷新 token。 |
|
||
| `PROFILE_REQUIRED` | 403 | 用户未完成资料。 | 引导补全资料。 |
|
||
| `PERMISSION_DENIED` | 403 | 当前用户不是房主。 | 关闭编辑入口或提示无权限;不要重试。 |
|
||
| `NOT_FOUND` | 404 | 房间不存在,或设置的 `background_id` 不属于该房间。 | 刷新我的房间和背景列表。 |
|
||
| `CONFLICT` | 409 | 房间不是 active,或同一 `command_id` 携带了不同 payload。 | 刷新房间状态;如果是超时重试,确保复用同一个请求体。 |
|
||
| `UPSTREAM_ERROR` | 502 | gateway、room-service、对象存储或 IM 投递依赖临时不可用。 | 保留用户选择,提示稍后重试。 |
|
||
|
||
失败响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": "PERMISSION_DENIED",
|
||
"message": "permission denied",
|
||
"request_id": "req_bg_set_denied"
|
||
}
|
||
```
|
||
|
||
## UI 状态建议
|
||
|
||
| 场景 | 建议 |
|
||
| --- | --- |
|
||
| 房主打开背景设置面板 | 调 `GET /rooms/{room_id}/backgrounds`,用 `selected_background_url` 标记当前背景。 |
|
||
| 用户选择新图片 | 先本地预览,再上传,上传成功后调用保存接口。 |
|
||
| 保存成功 | 把返回的 `background` 插入列表顶部,但不切换房间背景。 |
|
||
| 点击应用背景 | 生成新的 `command_id` 调设置接口;按钮进入 loading,防止重复点击。 |
|
||
| 设置成功 | 用响应里的 `room.background_url` 立即刷新当前设备 UI。 |
|
||
| 其他用户在线 | 通过 `room_background_changed` IM 消息刷新背景。 |
|
||
| 断线重进 | 通过 `/rooms/{room_id}/detail` 的 `room.background_url` 恢复当前背景,不依赖历史 IM。 |
|