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

12 KiB
Raw Permalink Blame History

房间背景图 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": {}
}

客户端分支判断只看 codemessage 只用于兜底展示,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}/detailroom.background_url 恢复当前背景,不依赖历史 IM。