hyapp-server/docs/flutter对接/房间背景图Flutter对接.md
2026-05-28 17:57:29 +08:00

386 lines
12 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 对接
本文描述 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。 |