feat(phase2e-2): 落地 HTTPMediaOrchestrator 打通 Go↔Node 媒体链路(Task 7)
- 新增 MediaServerConfig + config.{dev,docker}.yaml 的 media_server 段
(base_url / internal_token / timeout_ms / close_timeout_ms / close_retry)
- 新建 HTTPMediaOrchestrator(8 方法 + sync.Map 缓存 roomCode→routerID
+ 关闭类指数退避重试 200/500ms + ErrMediaResourceNotFound/ErrMediaServerError
两类错误)实现 MediaOrchestrator 接口
- wire 绑定由 NoopMediaOrchestrator 切换到 HTTPMediaOrchestrator;
MeetingService / MeetingSignalService 调用侧零改动
- E2E 脚本 docs/verify/meeting_t7_verify.mjs 证明 16/16 PASS:
健康检查/错token 401/REST 创房加入/WS room.join/真实 mediasoup
transport.create(ICE/DTLS 指纹均由 Node 返回非占位)/404 幂等关 producer
/host 结束会议触发 CloseRouter
- 同步更新 13 份文档:CURRENT_STATUS / implementation.plan / design
变更记录 / project-context / api 导览 / api websocket / api frontend meeting
/ system-architecture / media-server README / 顶层 README 等
- go build ./... 全绿
Made-with: Cursor
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
| [frontend/websocket.md](frontend/websocket.md) | WebSocket | ✅ Phase 2a | 前端 WebSocket 连接管理、事件协议、心跳、重连 |
|
||||
| [frontend/im.md](frontend/im.md) | 即时通讯 | ✅ Phase 2b | 7 个 API:会话列表/置顶/删除/清空、历史消息、全局搜索、未读数 |
|
||||
| [frontend/group.md](frontend/group.md) | 群聊管理 | ✅ Phase 2c | 16 个 API:建群/管理/成员/角色/禁言/公告/搜索/入群审批 |
|
||||
| [frontend/meeting.md](frontend/meeting.md) | 会议 | ✅ Phase 2e-2 Task 5/6 | 12 个 REST 接口(创建/加入/离开/结束/详情/列表/邀请/踢人/转让主持人/发起聊天/拉聊天历史/邀请链接兑换)+ 13 个 WebSocket 信令事件(8 C→S + 5 核心 S→C 广播 + 会议内聊天/被踢定向推送),Redis 媒体资源追踪 + host 权限校验 |
|
||||
| [frontend/meeting.md](frontend/meeting.md) | 会议 | ✅ Phase 2e-2 Task 5/6/7 | 12 个 REST 接口(创建/加入/离开/结束/详情/列表/邀请/踢人/转让主持人/发起聊天/拉聊天历史/邀请链接兑换)+ 13 个 WebSocket 信令事件(8 C→S + 5 核心 S→C 广播 + 会议内聊天/被踢定向推送),Redis 媒体资源追踪 + host 权限校验;Task 7 (2026-04-21) 已接入真实 mediasoup,返回值为 Node media-server 真实 Transport/Router/DTLS 参数 |
|
||||
| [frontend/notify.md](frontend/notify.md) | 通知中心 | ✅ Phase 2e-1 | 5 个 API:通知列表(游标分页)/未读数/标记已读/全部已读/管理员广播 + 2 个 WS 事件(notify.new/notify.unread.total) |
|
||||
|
||||
### 后台管理端 (`admin/`)
|
||||
@@ -199,7 +199,7 @@ docs/api/
|
||||
│ ├── websocket.md # WebSocket 事件协议 ✅ Phase 2a
|
||||
│ ├── im.md # 即时通讯(8 个 API) ✅ Phase 2b/2c
|
||||
│ ├── group.md # 群聊管理(16 个 API) ✅ Phase 2c
|
||||
│ ├── meeting.md # 会议(12 REST + 13 WS 事件) ✅ Phase 2e-2 (Task 5/6)
|
||||
│ ├── meeting.md # 会议(12 REST + 13 WS 事件) ✅ Phase 2e-2 (Task 5/6/7,真实 mediasoup)
|
||||
│ └── notify.md # 通知(5 API + 2 WS 事件) ✅ Phase 2e-1
|
||||
├── admin/ # 后台管理端 API
|
||||
│ ├── auth.md # 管理员认证 ✅ Phase 1
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
> 通用规范(认证、响应包络、通用错误码)见 [README.md](../README.md)
|
||||
> 会议内实时信令(Transport / Producer / Consumer / 控制事件)通过 WebSocket 完成,见 [websocket.md](../websocket.md)
|
||||
|
||||
**实施状态**:本文档对应 Phase 2e-2 Task 5 / Task 6 已落地的 12 个 REST 接口 + 13 个 WebSocket 信令事件,统一前缀 `/api/v1/meeting`(REST)与 `/ws`(WebSocket),全部需要 JWT 认证。Task 5 完成时间:2026-04-21;Task 6 完成时间:2026-04-21。
|
||||
**实施状态**:本文档对应 Phase 2e-2 Task 5 / Task 6 / Task 7 已落地的 12 个 REST 接口 + 13 个 WebSocket 信令事件,统一前缀 `/api/v1/meeting`(REST)与 `/ws`(WebSocket),全部需要 JWT 认证。Task 5/6/7 完成时间:2026-04-21。自 Task 7 起 **Go 后端直连 Node media-server**,`transport.id` / `iceCandidates` / `dtlsParameters.fingerprints` 等字段均由真实 mediasoup 返回(不再是 stub 占位)。
|
||||
|
||||
**设计口径**:以 [`docs/plans/2026-04-21-phase2e-2-design.md`](../../plans/2026-04-21-phase2e-2-design.md) §6.2 为单一事实来源(SSOT)。
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
`router_id` 当前为 Noop 占位,Task 7 接入 Node media-server 后改为真实 mediasoup Router ID,前端据此建立 WebSocket 订阅。
|
||||
`router_id` 自 Task 7 起为 Node media-server 返回的真实 mediasoup Router ID(Phase 2e-2 Task 7 完成,2026-04-21)。前端可据此建立 WebSocket 订阅,也可忽略,仅依赖 WS `meeting.transport.create` 的返回值创建 mediasoup-client Transport。
|
||||
|
||||
---
|
||||
|
||||
@@ -606,7 +606,7 @@
|
||||
- **MeetingWSHandler**(controller 层):thin adapter,仅负责 ws.Hub 事件注册 + JSON 反序列化 + ACK 回写;位于 `app/meeting/controller/meeting_ws_handler.go`。
|
||||
- **MeetingSignalService**(service 层):承载 8 个 C→S 事件的业务逻辑(活跃参会校验、host 权限校验、mediaOrchestrator 调用、Redis 资源追踪、广播),位于 `app/meeting/service/meeting_signal_service.go`。
|
||||
- **MeetingBroadcaster**(service 层):封装 `BroadcastToMeeting`(查询活跃 participant 列表 → 逐个 `PubSub.PublishToUser`)与 `PublishToUser`,供 REST / WS 两个入口统一使用,位于 `app/meeting/service/meeting_broadcaster.go`。
|
||||
- **MediaOrchestrator**(interface):定义 9 个 mediasoup 操作方法;Task 6 使用 `NoopMediaOrchestrator` 占位(返回 stub IDs),Task 7 替换为 `HTTPMediaOrchestrator` 对接 Node media-server。
|
||||
- **MediaOrchestrator**(interface):定义 8 个 mediasoup 操作方法(第 9 个 `ResumeConsumer` 推迟到 Task 9);Task 6 曾用 `NoopMediaOrchestrator` 占位,**Task 7 (2026-04-21) 已替换为 `HTTPMediaOrchestrator`**,通过 `X-Internal-Token` 鉴权直连 Node media-server 的 `/internal/v1/*` 接口;错误类型 `ErrMediaResourceNotFound`(Node 404 → 关闭类幂等转 nil)与 `ErrMediaServerError`(5xx / 超时 / 网络错)可供上层 `errors.Is` 区分;关闭类操作指数退避 200ms→500ms 最多 `CloseRetry+1` 次。
|
||||
|
||||
### 错误处理
|
||||
|
||||
@@ -632,11 +632,18 @@
|
||||
- `meeting.producer.close` + `meeting.member.producer.new closed=true` 广播
|
||||
- `meeting.room.leave` + `meeting.member.left` 广播
|
||||
- 不存在会议号 `meeting.room.join` → ACK `code=-1`
|
||||
- **Task 7** 端到端验证脚本(`docs/verify/meeting_t7_verify.mjs`)结果:**16/16 PASS**,覆盖:
|
||||
- media-server `/healthz` + `/internal/info`(正确 token 200、错误 token 401)
|
||||
- 注册登录 2 用户 → host 创建会议(HTTP 201 + 内部调 `CreateRouter`)→ 第二用户 REST 加入(再次 `CreateRouter`)
|
||||
- 双端 WS `meeting.room.join`
|
||||
- `meeting.transport.create`(direction=send)返回**真实** mediasoup `id`(非 `noop-` 前缀)+ `iceParameters` 对象 + `iceCandidates[]` 非空 + `dtlsParameters.fingerprints[]` 非空
|
||||
- 虚构 producer_id 调 `meeting.producer.close` → Node 返回 404 → Go 幂等转 `code=0`
|
||||
- host `POST /rooms/:code/end` 触发 `CloseRouter`,media-server 日志确认 `router closed explicitly`
|
||||
|
||||
---
|
||||
|
||||
## 后续任务关联
|
||||
|
||||
- **Task 7**:Go → Node HTTP 客户端,将 `NoopMediaOrchestrator` 替换为 `HTTPMediaOrchestrator`,接入真实 mediasoup Router,此时 WS 白名单事件契约与本文档完全不变,仅 `iceParameters / producer_id` 等字段由 stub 变为真实值。
|
||||
- **Task 8**:Vue 前端 mediasoup-client 接入,按本文 WS 契约实现 `mediasoup.Transport` 的 `connect / produce` 回调。
|
||||
- **Task 8**:会议生命周期状态机(host 宽限期 + 自动转让 + 空房 TTL),同时修复"CreateRoom + JoinRoom 重复调 `CreateRouter`"遗留行为。
|
||||
- **Task 9**:Vue 前端 mediasoup-client 接入,按本文 WS 契约实现 `mediasoup.Transport` 的 `connect / produce` 回调;补齐 `ResumeConsumer`(Node REST 已就绪)或在 Go 侧改为创建 Consumer 后自动 resume。
|
||||
- **Task 13**:通知卡片 UI 补齐 `meeting_invite` 内联按钮。
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 会议信令事件(Phase 2e-2 Task 6 已落地)
|
||||
## 会议信令事件(Phase 2e-2 Task 6 已落地;Task 7 起 mediasoup 返回值为真实值)
|
||||
|
||||
> **SSOT**:会议相关的全部 WS 事件详细契约(请求/ACK/广播载荷、权限、错误处理、资源追踪)见 [`docs/api/frontend/meeting.md`](./frontend/meeting.md) §WebSocket 信令协议。本节仅列事件总览。
|
||||
|
||||
@@ -232,6 +232,7 @@
|
||||
- **ACK 规则**:每个 C→S 事件服务端必回 `<event>.ack`;成功 `code=0`,业务失败 `code=-1` + 中文 `message`(与 REST 领域错误口径一致)。
|
||||
- **错误码示例**:`会议不存在` / `你当前未在会议中` / `仅主持人可执行此操作` / `会议已结束`。
|
||||
- **资源追踪**:服务端对每用户在每会议的 transport/producer/consumer 用 Redis Set `echo:meeting:resources:{room_id}:{user_id}` 记录,WS 断开或 `room.leave` 时自动清理。
|
||||
- **真实 mediasoup(Task 7 起)**:`meeting.transport.create` ACK 的 `id` / `iceParameters` / `iceCandidates` / `dtlsParameters`,以及 `meeting.produce.start` / `meeting.consume.start` 的 ID 均来自 Node media-server 真实 mediasoup Worker;Node 404(如关闭不存在 producer)在 Go 侧幂等转 `code=0`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user