Files
shz-backend/docs/app-interview-rtc-api.md
2026-07-31 15:06:14 +08:00

219 lines
5.8 KiB
Markdown
Raw 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.

# 面试视频房间接口
## 1. 接口说明
本组接口用于创建一场面试视频通话,并分别获取面试官和候选人的 MyRTC 连接凭据。
后端会在一次请求中完成以下操作:
1. 自动生成一个唯一的 `roomId`
2. 生成面试官专用的 `peerId`
3. 生成候选人专用的 `peerId`
4. 使用与 MyRTC 服务一致的 JWT 密钥,分别签发两枚一次性 token
5. 保证两枚 token 绑定同一个 `roomId`
前端不需要调用房间详情查询和房间销毁接口。房间在 MyRTC 客户端成功执行 `join` 后创建;双方离开后由 MyRTC 自动回收空房间。
## 2. 创建房间并获取双方凭据
### 请求
```http
POST /app/interview/rtc/token
Authorization: Bearer <业务系统登录Token>
Content-Type: application/json
```
请求体为空即可,不需要传入 `roomId``peerId` 或 token。
```json
{}
```
### 成功响应
接口遵循后端统一的 `AjaxResult` 返回格式:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"roomId": "room-0a4c3fbd8f9e4b70a0e2de99f6b5f2a8",
"expiresIn": "5m",
"interviewer": {
"role": "interviewer",
"peerId": "interviewer-4a78d8f0c4a5468fb0d8d05a8199b3bb",
"token": "eyJhbGciOiJIUzI1NiJ9..."
},
"candidate": {
"role": "candidate",
"peerId": "candidate-1d2f7e2e2ae8462da3bb514d2f39b8e3",
"token": "eyJhbGciOiJIUzI1NiJ9..."
}
}
}
```
字段说明:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | number | `200` 表示成功 |
| `msg` | string | 接口提示信息 |
| `data.roomId` | string | 本次面试房间 ID双方必须使用同一个值 |
| `data.expiresIn` | string | token 有效期,当前通常为 `5m` |
| `data.interviewer.role` | string | 固定为 `interviewer` |
| `data.interviewer.peerId` | string | 面试官在该房间内的唯一 ID |
| `data.interviewer.token` | string | 面试官的一次性 MyRTC JWT |
| `data.candidate.role` | string | 固定为 `candidate` |
| `data.candidate.peerId` | string | 候选人在该房间内的唯一 ID |
| `data.candidate.token` | string | 候选人的一次性 MyRTC JWT |
## 3. 前端使用流程
### 3.1 调用后端接口
```javascript
const response = await request.post('/app/interview/rtc/token', {})
if (response.code !== 200) {
throw new Error(response.msg || '创建面试视频房间失败')
}
const session = response.data
const roomId = session.roomId
const interviewer = session.interviewer
const candidate = session.candidate
```
注意:`roomId` 在响应的 `data.roomId` 中,不是在响应最外层。
### 3.2 面试官加入房间
面试官客户端使用:
```javascript
{
roomId: session.roomId,
peerId: session.interviewer.peerId,
token: session.interviewer.token
}
```
### 3.3 候选人加入房间
候选人客户端使用:
```javascript
{
roomId: session.roomId,
peerId: session.candidate.peerId,
token: session.candidate.token
}
```
两端的 `roomId` 必须完全相同,但 `peerId``token` 必须使用各自对应角色的数据,不能交叉使用。
## 4. MyRTC WebSocket 连接参数
MyRTC 的 WebSocket 地址由 MyRTC 服务部署地址决定。当前服务的信令路径是 `/signaling`JWT 通过 WebSocket 握手阶段的 `token` 查询参数传入,例如:
```text
wss://rtc.zhaopinzao8dian.com/signaling?token=<url-encoded-token>
```
如果使用 `WebSocket` 原生 API建议使用 `URL` 对 token 做编码,不要直接拼接未编码的 token
```javascript
const wsUrl = new URL('wss://rtc.zhaopinzao8dian.com/signaling')
wsUrl.searchParams.set('token', token)
const ws = new WebSocket(wsUrl.toString())
```
加入房间时,发送的业务消息需要包含 `roomId``peerId` 和 token。协议形式如下
```json
{
"type": "request",
"action": "join",
"requestId": "join-001",
"payload": {
"roomId": "room-0a4c3fbd8f9e4b70a0e2de99f6b5f2a8",
"peerId": "interviewer-4a78d8f0c4a5468fb0d8d05a8199b3bb"
}
}
```
token 通常在建立 WebSocket 连接时通过 SDK 配置或连接参数传入,不能把面试官 token 和候选人 token 互换。
如果使用项目已有 MyRTC SDK前端只需要将以下三个字段传给 SDK
```javascript
await rtcClient.connect({
roomId,
peerId,
token
})
```
## 5. token 注意事项
- token 是一次性的,成功 `join` 后即会被 MyRTC 消费;
- token 默认有效期为 `5m`,过期后不能继续使用;
- 页面刷新或连接断开后重新连接,不能继续复用已经成功 `join` 的 token需要重新调用后端接口签发
- 面试官和候选人的 token 绑定不同的 `peerId`,不能让两个客户端使用同一组凭据;
- `roomId``peerId` 和 token 应保存在当前面试页面的内存状态中,不建议写入长期本地存储;
- 前端不需要保存或传递 MyRTC 的 `ADMIN_TOKEN`,该令牌只由业务后端访问 MyRTC 控制面时使用。
## 6. 错误响应
### 未登录
```json
{
"code": 500,
"msg": "未登录"
}
```
### MyRTC JWT 密钥未配置或配置不一致
后端可能返回类似以下错误:
```json
{
"code": 500,
"msg": "MyRTC JWT_SECRET 未配置"
}
```
如果后端签发 token 使用的密钥和 MyRTC 服务配置不一致,前端会在连接或 `join` 阶段收到 token 签名错误。此时需要检查业务后端和 MyRTC 的 `JWT_SECRET` 是否完全一致。
### MyRTC 服务不可用
```json
{
"code": 500,
"msg": "无法连接 MyRTC"
}
```
## 7. 房间查询和销毁接口
这两个接口不是前端视频通话流程的一部分,前端无需调用:
```text
GET /app/interview/rtc/room/{roomId}
DELETE /app/interview/rtc/room/{roomId}
```
它们仅供后端管理或运维场景使用。正常情况下,前端只需要:
```text
POST /app/interview/rtc/token
```
然后使用响应中的双方凭据加入同一个 `roomId` 即可。