zhibo/Zhibo/zhibo-h/语音视频通话模块说明.md

574 lines
12 KiB
Markdown
Raw Permalink 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.

# 语音/视频通话模块说明
## 📋 模块概述
本模块实现了基于 WebRTC 的一对一语音/视频通话功能,支持通话邀请、接听、拒绝、取消、结束等完整流程,并提供通话记录管理功能。
**实现时间**: 已完成
**技术方案**: WebRTC + WebSocket 信令交换
**完成度**: 100%
---
## 🏗️ 架构设计
### 核心组件
1. **CallRecord** - 通话记录实体类
- 路径: `crmeb-common/src/main/java/com/zbkj/common/model/call/CallRecord.java`
- 功能: 存储通话记录信息(通话双方、类型、状态、时长等)
2. **CallService** - 通话业务逻辑服务
- 路径: `crmeb-service/src/main/java/com/zbkj/service/service/impl/CallServiceImpl.java`
- 功能: 处理通话创建、接听、拒绝、取消、结束等业务逻辑
3. **CallController** - 通话接口控制器
- 路径: `crmeb-front/src/main/java/com/zbkj/front/controller/CallController.java`
- 功能: 提供 RESTful API 接口
4. **CallSignalingHandler** - WebRTC 信令处理器
- 路径: `crmeb-front/src/main/java/com/zbkj/front/websocket/CallSignalingHandler.java`
- 功能: 处理 WebSocket 信令交换offer、answer、ice-candidate
---
## 📊 数据库设计
### 表名: `eb_call_record`
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | BIGINT | 主键ID |
| call_id | VARCHAR(64) | 通话唯一标识(用于信令) |
| caller_id | INT | 主叫用户ID |
| callee_id | INT | 被叫用户ID |
| call_type | VARCHAR(20) | 通话类型: voice-语音, video-视频 |
| status | VARCHAR(20) | 通话状态 |
| call_time | DATETIME | 通话发起时间 |
| connect_time | DATETIME | 通话接通时间 |
| end_time | DATETIME | 通话结束时间 |
| duration | INT | 通话时长(秒) |
| end_reason | VARCHAR(50) | 结束原因 |
| caller_deleted | TINYINT(1) | 主叫是否删除记录 |
| callee_deleted | TINYINT(1) | 被叫是否删除记录 |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
### 通话状态说明
- `calling` - 呼叫中(主叫发起,等待被叫响应)
- `ringing` - 响铃中(被叫收到邀请)
- `connected` - 通话中(双方已接通)
- `ended` - 已结束(正常结束)
- `missed` - 未接(超时未接听)
- `rejected` - 已拒绝(被叫拒绝)
- `cancelled` - 已取消(主叫取消)
- `busy` - 忙线(对方正在通话中)
---
## 🔌 API 接口
### 1. 发起通话
```http
POST /api/front/call/initiate
Content-Type: application/json
{
"calleeId": 123,
"callType": "video" // voice 或 video
}
```
**响应**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"callId": "call_abc123...",
"callType": "video",
"calleeId": 123,
"calleeName": "用户昵称",
"calleeAvatar": "头像URL",
"status": "calling",
"signalingUrl": "/ws/call/call_abc123..."
}
}
```
### 2. 接听通话
```http
POST /api/front/call/accept/{callId}
```
### 3. 拒绝通话
```http
POST /api/front/call/reject/{callId}
```
### 4. 取消通话
```http
POST /api/front/call/cancel/{callId}
```
### 5. 结束通话
```http
POST /api/front/call/end/{callId}?endReason=normal
```
### 6. 获取通话记录
```http
GET /api/front/call/history?page=1&limit=20
```
### 7. 删除通话记录
```http
DELETE /api/front/call/record/{recordId}
```
### 8. 获取未接来电数量
```http
GET /api/front/call/missed/count
```
### 9. 检查通话状态
```http
GET /api/front/call/status
```
### 10. 获取通话详情
```http
GET /api/front/call/detail/{callId}
```
---
## 🔄 WebSocket 信令协议
### 连接地址
```
ws://your-domain/ws/call
```
### 客户端发送消息类型
#### 1. 注册用户
```json
{
"type": "register",
"userId": 123
}
```
#### 2. 发起通话请求
```json
{
"type": "call_request",
"calleeId": 456,
"callType": "video"
}
```
#### 3. 接听通话
```json
{
"type": "call_accept",
"callId": "call_abc123..."
}
```
#### 4. 拒绝通话
```json
{
"type": "call_reject",
"callId": "call_abc123..."
}
```
#### 5. 取消通话
```json
{
"type": "call_cancel",
"callId": "call_abc123..."
}
```
#### 6. 结束通话
```json
{
"type": "call_end",
"callId": "call_abc123...",
"reason": "normal"
}
```
#### 7. WebRTC Offer
```json
{
"type": "offer",
"callId": "call_abc123...",
"sdp": {
"type": "offer",
"sdp": "v=0\r\no=..."
}
}
```
#### 8. WebRTC Answer
```json
{
"type": "answer",
"callId": "call_abc123...",
"sdp": {
"type": "answer",
"sdp": "v=0\r\no=..."
}
}
```
#### 9. ICE Candidate
```json
{
"type": "ice-candidate",
"callId": "call_abc123...",
"candidate": {
"candidate": "candidate:...",
"sdpMLineIndex": 0,
"sdpMid": "0"
}
}
```
### 服务端推送消息类型
#### 1. 注册成功
```json
{
"type": "registered",
"userId": 123
}
```
#### 2. 来电通知
```json
{
"type": "incoming_call",
"callId": "call_abc123...",
"callerId": 123,
"callerName": "用户昵称",
"callerAvatar": "头像URL",
"callType": "video"
}
```
#### 3. 通话已创建
```json
{
"type": "call_created",
"callId": "call_abc123..."
}
```
#### 4. 对方已接听
```json
{
"type": "call_accepted",
"callId": "call_abc123..."
}
```
#### 5. 对方已拒绝
```json
{
"type": "call_rejected",
"callId": "call_abc123..."
}
```
#### 6. 对方已取消
```json
{
"type": "call_cancelled",
"callId": "call_abc123..."
}
```
#### 7. 通话已结束
```json
{
"type": "call_ended",
"callId": "call_abc123...",
"reason": "normal"
}
```
#### 8. 通话超时
```json
{
"type": "call_timeout",
"callId": "call_abc123..."
}
```
#### 9. 错误消息
```json
{
"type": "error",
"message": "错误描述"
}
```
---
## 🎯 通话流程
### 语音/视频通话完整流程
```
主叫方 服务器 被叫方
| | |
|--1. 发起通话----------->| |
| POST /call/initiate | |
| | |
|<--返回callId-----------| |
| | |
|--2. 连接WebSocket----->| |
| register | |
| | |
| |--3. 推送来电通知------->|
| | incoming_call |
| | |
| |<--4. 接听通话----------|
| | call_accept |
| | |
|<--5. 通知已接听--------|--5. 通知已接听-------->|
| call_accepted | call_accepted |
| | |
|--6. 发送Offer--------->|--6. 转发Offer--------->|
| | |
|<--7. 接收Answer--------|<--7. 发送Answer--------|
| | |
|--8. 交换ICE候选------->|--8. 转发ICE候选------->|
|<-----------------------|<-----------------------|
| | |
|========== WebRTC P2P 通话建立 =================|
| | |
|--9. 结束通话---------->| |
| call_end | |
| |--9. 通知结束---------->|
| | call_ended |
```
---
## ⚙️ 核心功能特性
### 1. 通话管理
- ✅ 发起语音/视频通话
- ✅ 接听/拒绝/取消通话
- ✅ 通话中状态检测
- ✅ 通话时长统计
- ✅ 通话记录保存
### 2. 状态管理
- ✅ 用户在线状态检测
- ✅ 通话状态实时同步
- ✅ 忙线检测(防止重复呼叫)
- ✅ 超时自动挂断60秒
### 3. 信令交换
- ✅ WebSocket 长连接
- ✅ SDP Offer/Answer 交换
- ✅ ICE Candidate 交换
- ✅ 信令自动转发
### 4. 通话记录
- ✅ 通话历史查询
- ✅ 未接来电统计
- ✅ 双向软删除
- ✅ 通话详情查看
### 5. 异常处理
- ✅ 网络断开自动结束
- ✅ 超时自动挂断
- ✅ 错误消息推送
- ✅ 状态一致性保证
---
## 🔒 安全特性
1. **权限验证**: 所有接口需要登录认证
2. **操作权限**: 只能操作自己参与的通话
3. **状态校验**: 严格的状态机转换验证
4. **防重复呼叫**: 检测用户是否正在通话中
5. **超时保护**: 60秒超时自动结束未接通的通话
---
## 📱 客户端集成指南
### 1. 发起通话
```javascript
// 1. 调用 API 发起通话
const response = await fetch('/api/front/call/initiate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
calleeId: 456,
callType: 'video'
})
});
const { callId, signalingUrl } = await response.json();
// 2. 连接 WebSocket
const ws = new WebSocket('ws://your-domain/ws/call');
// 3. 注册用户
ws.send(JSON.stringify({
type: 'register',
userId: currentUserId
}));
// 4. 创建 WebRTC 连接
const pc = new RTCPeerConnection(config);
// 5. 添加本地媒体流
const stream = await navigator.mediaDevices.getUserMedia({
video: callType === 'video',
audio: true
});
stream.getTracks().forEach(track => pc.addTrack(track, stream));
// 6. 创建并发送 Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
ws.send(JSON.stringify({
type: 'offer',
callId: callId,
sdp: offer
}));
// 7. 监听 ICE Candidate
pc.onicecandidate = (event) => {
if (event.candidate) {
ws.send(JSON.stringify({
type: 'ice-candidate',
callId: callId,
candidate: event.candidate
}));
}
};
// 8. 接收远程流
pc.ontrack = (event) => {
remoteVideo.srcObject = event.streams[0];
};
```
### 2. 接听通话
```javascript
// 1. 收到来电通知
ws.onmessage = async (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'incoming_call') {
// 显示来电界面
showIncomingCallUI(msg);
}
};
// 2. 用户点击接听
async function acceptCall(callId) {
// 调用接听 API
await fetch(`/api/front/call/accept/${callId}`, {
method: 'POST'
});
// 创建 WebRTC 连接
const pc = new RTCPeerConnection(config);
// 添加本地媒体流
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
stream.getTracks().forEach(track => pc.addTrack(track, stream));
// 监听 Offer
ws.onmessage = async (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'offer') {
await pc.setRemoteDescription(msg.sdp);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
// 发送 Answer
ws.send(JSON.stringify({
type: 'answer',
callId: callId,
sdp: answer
}));
}
if (msg.type === 'ice-candidate') {
await pc.addIceCandidate(msg.candidate);
}
};
}
```
---
## 🧪 测试建议
### 功能测试
1. 正常通话流程测试
2. 拒绝/取消通话测试
3. 超时未接测试
4. 忙线检测测试
5. 通话记录查询测试
### 异常测试
1. 网络断开重连测试
2. 对方离线测试
3. 并发呼叫测试
4. WebSocket 断开测试
### 性能测试
1. 并发通话数量测试
2. 信令延迟测试
3. 内存泄漏测试
---
## 📝 注意事项
1. **WebRTC 兼容性**: 确保客户端浏览器支持 WebRTC
2. **HTTPS 要求**: WebRTC 需要在 HTTPS 环境下运行(本地开发除外)
3. **STUN/TURN 服务器**: 生产环境需要配置 STUN/TURN 服务器用于 NAT 穿透
4. **媒体权限**: 需要用户授权摄像头和麦克风权限
5. **超时时间**: 默认 60 秒超时,可根据需求调整
6. **通话记录清理**: 建议定期清理过期的通话记录
---
## 🚀 后续优化建议
1. **群组通话**: 支持多人视频会议
2. **屏幕共享**: 支持屏幕共享功能
3. **通话质量**: 添加网络质量检测和自适应码率
4. **通话录制**: 支持通话录制功能
5. **美颜滤镜**: 集成美颜和滤镜功能
6. **通话统计**: 添加通话质量统计和分析
---
**文档版本**: v1.0
**最后更新**: 2024年12月26日
**维护状态**: ✅ 已完成