12 KiB
房间背景图 Flutter 对接
本文描述 Flutter App 对接房间背景图的 HTTP 接口和腾讯云 IM 房间消息。房间背景图是房间维度素材:上传文件只拿 URL,保存接口把 URL 归档到房间素材列表,设置接口才会改变当前房间背景并广播房间 IM 消息。
基础地址
本地开发地址:
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:
{
"code": "OK",
"message": "ok",
"request_id": "req_abc",
"data": {}
}
客户端分支判断只看 code,message 只用于兜底展示,request_id 要写入日志。
调用顺序
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。
POST /api/v1/files/upload
Authorization: Bearer <access_token>
X-App-Code: lalu
Content-Type: multipart/form-data
file=<binary>
请求字段:
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
file |
是 | multipart file | 单文件最大 20MB。背景建议传图片文件;服务端通用入口不绑定房间语义。 |
成功响应:
{
"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 消息。
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 字符。 |
成功响应:
{
"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。 |
获取背景图列表
房主打开房间背景编辑面板时调用。
GET /api/v1/rooms/{room_id}/backgrounds
Authorization: Bearer <access_token>
X-App-Code: lalu
路径参数:
| 参数 | 必填 | 类型 | 说明 |
|---|---|---|---|
room_id |
是 | string | 房间 ID。 |
成功响应:
{
"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。
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,必须来自当前房间的背景列表。 |
成功响应:
{
"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 | 被设置的背景素材。 |
房间详情返回
进房或详情页重建时不需要额外查背景列表。当前生效背景在房间详情里返回:
GET /api/v1/rooms/{room_id}/detail
Authorization: Bearer <access_token>
X-App-Code: lalu
关键字段:
{
"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:
{
"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 伪代码:
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 投递依赖临时不可用。 | 保留用户选择,提示稍后重试。 |
失败响应示例:
{
"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。 |