hyapp-server/docs/voice-room-client-api-flow.md
2026-05-06 21:22:39 +08:00

14 KiB
Raw Blame History

Voice Room Client API Flow

本文定义用户进入语音房后的客户端接口调用顺序、触发条件和服务边界。本文只描述 App 侧要调用的 gateway HTTP 接口、腾讯云 IM/RTC SDK 动作,以及不能由客户端直接调用的内部守卫接口。

Scope

Area Owner Rule
HTTP 入口 gateway-service 鉴权、profile gate、协议转换、腾讯 IM/RTC token 签发
房间业务状态 room-service Room Cell、presence、麦位、房管、snapshot、outbox
房间长连接和公屏 腾讯云 IM SDK 客户端登录、房间群、公屏、系统消息接收
真实音频频道 腾讯云 RTC SDK 进 RTC 房、切换 audience/anchor、发音频
用户主数据 user-service 登录、资料完成状态、用户区域、push token

关键边界:

  • 客户端进入语音房必须先 JoinRoom 写入 room-service 业务 presence再进 IM 群和 RTC 房。
  • room-service 不保存客户端 socket也不保存腾讯云 RTC 连接态。
  • 腾讯云 IM 群成员关系不是业务 presence是否允许进群、发言、签 RTC token 仍以 room-service 守卫为准。
  • gateway 只使用 access token 中的 user_id 作为 actor客户端不能自报 actor_user_idowner_user_idhost_user_id
  • 所有 /api/v1 业务响应使用 {code,message,request_id,data} envelope。

Minimal Enter Room Flow

sequenceDiagram
  participant C as Client
  participant G as gateway-service
  participant R as room-service
  participant IM as Tencent IM SDK
  participant RTC as Tencent RTC SDK

  C->>G: POST /api/v1/rooms/join
  G->>R: JoinRoom(actor_user_id from token)
  R-->>G: room snapshot + user presence
  G-->>C: room snapshot

  C->>G: GET /api/v1/im/usersig
  G-->>C: IM user_sig
  C->>IM: login(user_id, user_sig)
  C->>IM: joinGroup(room_id)

  C->>G: POST /api/v1/rtc/token
  G->>R: VerifyRoomPresence(room_id, user_id)
  G-->>C: RTC user_sig, str_room_id, role=audience
  C->>RTC: enterRoom(strRoomId, audience)

  loop while room page is active
    C->>G: POST /api/v1/rooms/heartbeat
  end

Required Order

Step Interface Required Reason
1 POST /api/v1/rooms/join yes 建立业务 presence后续 IM/RTC 守卫依赖它
2 GET /api/v1/im/usersig yes 登录腾讯云 IM接收房间群消息和系统事件
3 Tencent IM SDK login + joinGroup yes 本仓库不承载房间长连接
4 POST /api/v1/rtc/token yes for voice room audio 签发 TRTC 票据;当前返回 role=audience
5 Tencent RTC SDK enterRoom yes for voice room audio 进入真实音频频道旁听
6 POST /api/v1/rooms/heartbeat yes 刷新业务 presence避免 stale timeout

如果产品允许“只看公屏不听语音”,第 4、5 步可以延迟到用户打开音频时。但语音房默认应进入 RTC audience以保证打开房间即可听到声音。

Core Interfaces

Join Room

POST /api/v1/rooms/join
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_join_<room_id>_<user_id>_<nonce>",
  "role": "audience"
}

Response data:

{
  "result": {
    "applied": true,
    "room_version": 2,
    "server_time_ms": 1778000000000
  },
  "user": {
    "user_id": 10001,
    "role": "audience",
    "joined_at_ms": 1778000000000,
    "last_seen_at_ms": 1778000000000
  },
  "room": {}
}

Rules:

  • room_id 必须来自房间列表、分享或恢复接口,不能由客户端拼业务含义。
  • command_id 用于房间命令幂等;客户端重试同一次进房必须复用同一个值。
  • role 只作为展示默认值,不能通过自报 admin/host/owner 获得权限。
  • JoinRoom 成功后才能登录 IM 进群和获取 RTC token。

IM UserSig

GET /api/v1/im/usersig
Authorization: Bearer <access_token>

Response data:

{
  "sdk_app_id": 20036101,
  "user_id": "10001",
  "user_sig": "xxx",
  "expire_at_ms": 1778086400000
}

Rules:

  • 当前 gateway 路由是 GET /api/v1/im/usersig
  • user_id 是内部不可变长 user_id 的十进制字符串,必须直接作为腾讯云 IM identifier。
  • UserSig 只用于腾讯云 IM SDK 登录,不用于本仓库鉴权。
  • 未完成资料用户会被 profile gate 拒绝。

Client SDK action:

Tencent IM SDK login(user_id, user_sig)
Tencent IM SDK joinGroup(room_id)

RTC Token

POST /api/v1/rtc/token
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx"
}

Response data:

{
  "sdk_app_id": 20036101,
  "user_id": "10001",
  "rtc_user_id": "10001",
  "user_sig": "xxx",
  "expire_at_ms": 1778007200000,
  "room_id": "lalu_xxx",
  "rtc_room_id": "lalu_xxx",
  "rtc_room_id_type": "string",
  "str_room_id": "lalu_xxx",
  "app_scene": "voice_chat_room",
  "role": "audience"
}

Rules:

  • gateway 会先调用 room-service VerifyRoomPresence,用户不在房间、被踢、被 ban 时不会签发 RTC token。
  • 当前只支持 TRTC strRoomId,客户端不能把 room_id 转为数字 roomId
  • 当前 token 返回 role=audience;上麦成功后由客户端切 anchor 并确认发流。
  • token 过期时重新调用该接口,不使用 refresh token。

Client SDK action:

Tencent RTC SDK enterRoom(strRoomId=resp.str_room_id, userId=resp.rtc_user_id, role=audience)

Heartbeat

POST /api/v1/rooms/heartbeat
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_heartbeat_<room_id>_<user_id>_<tick>"
}

Rules:

  • 只刷新已经存在的业务 presence不能替代 JoinRoom。
  • 用户已经离房、被踢或房间关闭时,客户端应停止心跳并清理本地房间态。
  • 心跳间隔由客户端配置控制,但必须小于 room-service 的 presence stale 窗口。

Leave Room

POST /api/v1/rooms/leave
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_leave_<room_id>_<user_id>_<nonce>"
}

Rules:

  • 主动关闭房间页、切换账号、被客户端确认退出时调用。
  • 如果用户在麦上LeaveRoom 会释放麦位。
  • LeaveRoom 后客户端必须停止 heartbeat、退出 RTC 房、退出或忽略 IM 房间群消息。

Mic Flow

Mic Up

POST /api/v1/rooms/mic/up
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_mic_up_<room_id>_<user_id>_<nonce>",
  "seat_no": 1
}

Response data:

{
  "result": {},
  "seat_no": 1,
  "mic_session_id": "mic_xxx",
  "publish_deadline_ms": 1778000015000,
  "room": {}
}

Client actions after success:

  1. 保存 mic_session_idroom.versionpublish_deadline_ms
  2. 确保已有 RTC token没有或过期则调用 POST /api/v1/rtc/token
  3. RTC SDK 切换 anchor
  4. RTC SDK 调 startLocalAudio
  5. SDK 本地发流成功后调用 POST /api/v1/rooms/mic/publishing/confirm

Rules:

  • MicUp 成功只代表业务麦位占用成功,不代表 RTC 已经发布音频成功。
  • 服务端状态先进入 pending_publish
  • 超过 publish_deadline_ms 未确认room-service 会自动 MicDownreason 为 publish_timeout

Confirm Mic Publishing

POST /api/v1/rooms/mic/publishing/confirm
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_confirm_publish_<room_id>_<user_id>_<nonce>",
  "target_user_id": 0,
  "mic_session_id": "mic_xxx",
  "room_version": 12,
  "event_time_ms": 1778000001000,
  "source": "client"
}

Rules:

  • target_user_id=0 表示确认当前 actorRTC webhook 路径才需要显式 target。
  • mic_session_id 必须等于当前麦位会话。
  • room_versionevent_time_ms 用于丢弃旧事件,避免旧 audience/leave 事件清理新会话。
  • 成功后麦位 publish_state=publishing

Mic Down

POST /api/v1/rooms/mic/down
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_mic_down_<room_id>_<user_id>_<nonce>",
  "target_user_id": 0,
  "reason": "user_action"
}

Rules:

  • 普通用户下自己麦时 target_user_id 可传 0 或自己 user_id。
  • 房管下别人麦时传目标用户 target_user_id
  • 客户端收到成功响应或房间系统消息后必须停止本地音频并切回 audience

Change Mic Seat

POST /api/v1/rooms/mic/change
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_change_mic_<room_id>_<actor_user_id>_<nonce>",
  "target_user_id": 10002,
  "seat_no": 2
}

Rules:

  • 换麦位只更新业务麦位和 UI不需要重新进入 RTC。
  • 如果 target 当前在发流,客户端继续保持 RTC 上行。

Room Management APIs

这些接口只允许有权限的 owner/host/admin 调用gateway 不判断权限,最终由 room-service Room Cell 判断。

Interface Body Result
POST /api/v1/rooms/mic/lock room_id, command_id, seat_no, locked 锁/解锁麦位
POST /api/v1/rooms/chat/enabled room_id, command_id, enabled 开/关公屏,影响发言守卫
POST /api/v1/rooms/admin/set room_id, command_id, target_user_id, is_admin 设置或取消房管
POST /api/v1/rooms/host/transfer room_id, command_id, target_user_id 转移主持人
POST /api/v1/rooms/user/mute room_id, command_id, target_user_id, muted, reason 禁言或解禁
POST /api/v1/rooms/user/kick room_id, command_id, target_user_id, reason 踢人并 ban释放 presence 和麦位
POST /api/v1/rooms/user/unban room_id, command_id, target_user_id, reason 解除房间 ban

Management event handling:

  • 管理动作成功后room-service 写 command log、snapshot 和 outbox。
  • 客户端应通过腾讯云 IM 房间系统消息更新 UI。
  • 如果本地 UI 状态和系统消息冲突,以最新 room snapshot 和 room_version 为准。

Gift And Interaction

POST /api/v1/rooms/gift/send
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "room_id": "lalu_xxx",
  "command_id": "cmd_send_gift_<room_id>_<user_id>_<nonce>",
  "target_user_id": 10002,
  "gift_id": "rose",
  "gift_count": 1
}

Rules:

  • SendGift 必须先由 wallet-service 扣费成功,再落房间表现。
  • 房间表现、热度、本地榜通过 room snapshot 和房间系统消息同步。
  • 公屏文本消息不走 gateway HTTP客户端使用腾讯云 IM 群消息。

Restore And Reconnect

Current Room

GET /api/v1/rooms/current
Authorization: Bearer <access_token>

Response data:

{
  "has_current_room": true,
  "room_id": "lalu_xxx",
  "room_version": 12,
  "role": "audience",
  "mic_session_id": "",
  "publish_state": "",
  "need_join_im_group": true,
  "need_rtc_token": false,
  "server_time_ms": 1778000000000
}

Client restore rules:

Response Client Action
has_current_room=false 清理本地房间态,不展示恢复入口
need_join_im_group=true GET /api/v1/im/usersig,重新登录 IM 并 join group
need_rtc_token=true POST /api/v1/rtc/token,重新进入 RTC
publish_state=pending_publish 重新执行 SDK 发流并确认当前 mic_session_id
publish_state=publishing 确认本地 RTC 上行是否仍存在;不确定时重新发流并确认

rooms/current 是只读恢复探测,不会隐式 JoinRoom不刷新 heartbeat也不改变 Room Cell。

Reconnect Order

  1. access token 有效时先调 GET /api/v1/rooms/current
  2. 如果 has_current_room=false,回房间列表或主界面。
  3. 如果 has_current_room=true,先恢复 IM再恢复 RTC。
  4. 如果用户原本在麦上,必须使用返回的 mic_session_id 继续确认或等待服务端超时下麦。
  5. 恢复成功后恢复 POST /api/v1/rooms/heartbeat 定时器。

Server Callback Boundaries

客户端不要直接调用这些接口或内部 RPC

Interface Caller Purpose
POST /api/v1/tencent-im/callback Tencent IM 入群、发言等服务端回调入口
POST /api/v1/tencent-rtc/callback Tencent RTC RTC 音频事件回调入口
room-service.VerifyRoomPresence gateway / callback 判断用户是否仍在房间、是否被 ban
room-service.CheckSpeakPermission gateway / callback 判断用户是否允许发公屏

客户端只消费这些回调产生的房间系统消息和最新 snapshot不直接操作内部守卫。

Error Handling

Error Meaning Client Action
UNAUTHORIZED / AUTH_REQUIRED access token 无效 refresh 成功后重试refresh 失败回登录
PROFILE_REQUIRED 资料未完成 跳资料补全页,不能进入房间
NOT_FOUND 房间或用户事实不存在 清本地目标房间,回列表
ROOM_CLOSED 房间已关闭 清房间态,回列表
PERMISSION_DENIED 被踢、被 ban、无权限 停止对应操作,按系统消息更新 UI
CONFLICT 麦位占用、命令冲突、状态冲突 拉最新 snapshot 或等待房间系统消息修正
UPSTREAM_ERROR 内部服务或腾讯云依赖异常 保留本地登录态,展示重试

不要因为普通网络失败或 5xx 清空登录态。只有 refresh 明确失败、session revoked 或用户主动 logout 才清 token。

Room Snapshot Sync

当前客户端拿完整房间状态的来源:

  • JoinRoom 响应里的 room snapshot。
  • 房间命令响应里的 room snapshot。
  • 腾讯云 IM 房间系统消息。
  • rooms/current 的恢复摘要。
  • GET /api/v1/rooms/snapshot?room_id=<room_id> 主动同步接口。
GET /api/v1/rooms/snapshot?room_id=<room_id>

该接口只读 Room Cell/snapshot不刷新 presence不隐式 JoinRoom不替代 heartbeat。gateway 使用 access token user_id 作为 viewerroom-service 要求 viewer 仍在该房间;未进房、被踢或被 ban 的用户不能读取完整在线用户和麦位快照。