zhibo/模块文档/18-通话功能模块.md

527 lines
12 KiB
Markdown
Raw Normal View History

2025-12-30 11:11:11 +08:00
# 通话功能模块
## 模块概述
通话功能模块负责处理用户之间的语音和视频通话功能,包括发起通话、接听/拒绝通话、通话记录等。
## 相关文件
- `CallActivity.java` - 通话界面
- `IncomingCallActivity.java` - 来电界面
- `CallHistoryActivity.java` - 通话记录界面
- `CallManager.java` - 通话管理器
- `CallSignalingClient.java` - 信令客户端
- `CallApiService.java` - 通话API接口
- `InitiateCallRequest.java` - 发起通话请求模型
- `InitiateCallResponse.java` - 发起通话响应模型
- `CallRecordResponse.java` - 通话记录响应模型
- `CallHistoryResponse.java` - 通话历史响应模型
---
## 接口列表
### 1. 发起通话
**接口地址**: `POST /api/front/call/initiate`
**请求参数** (InitiateCallRequest):
```json
{
"calleeId": "integer", // 被叫用户ID必填
"callType": "string" // 通话类型: voice/video必填
}
```
**返回数据** (InitiateCallResponse):
```json
{
"code": 200,
"msg": "success",
"data": {
"callId": "string", // 通话ID
"callType": "string", // 通话类型
"calleeId": "integer", // 被叫用户ID
"calleeName": "string", // 被叫用户昵称
"calleeAvatar": "string", // 被叫用户头像
"status": "string", // 通话状态: calling
"signalingUrl": "string" // 信令服务器URL
}
}
```
---
### 2. 接听通话
**接口地址**: `POST /api/front/call/accept/{callId}`
**路径参数**:
```
callId: string // 通话ID
```
**请求参数**: 无
**返回数据**:
```json
{
"code": 200,
"msg": "接听成功",
"data": true
}
```
---
### 3. 拒绝通话
**接口地址**: `POST /api/front/call/reject/{callId}`
**路径参数**:
```
callId: string // 通话ID
```
**请求参数**: 无
**返回数据**:
```json
{
"code": 200,
"msg": "已拒绝",
"data": true
}
```
---
### 4. 取消通话
**接口地址**: `POST /api/front/call/cancel/{callId}`
**路径参数**:
```
callId: string // 通话ID
```
**请求参数**: 无
**返回数据**:
```json
{
"code": 200,
"msg": "已取消",
"data": true
}
```
---
### 5. 结束通话
**接口地址**: `POST /api/front/call/end/{callId}`
**路径参数**:
```
callId: string // 通话ID
```
**请求参数**:
```
endReason: string // 结束原因(可选)
```
**返回数据**:
```json
{
"code": 200,
"msg": "通话已结束",
"data": true
}
```
---
### 6. 获取通话记录
**接口地址**: `GET /api/front/call/history`
**请求参数**:
```
page: integer // 页码默认1
limit: integer // 每页数量默认20
```
**返回数据** (CallHistoryResponse):
```json
{
"code": 200,
"msg": "success",
"data": {
"records": [
{
"id": "long", // 记录ID
"callId": "string", // 通话ID
"callType": "string", // 通话类型: voice/video
"callDirection": "string", // 通话方向: outgoing/incoming
"otherUserId": "integer", // 对方用户ID
"otherUserName": "string", // 对方用户昵称
"otherUserAvatar": "string",// 对方用户头像
"status": "string", // 状态: completed/missed/rejected/cancelled
"duration": "integer", // 通话时长(秒)
"startTime": "long", // 开始时间戳
"endTime": "long" // 结束时间戳
}
],
"total": "integer",
"page": "integer",
"limit": "integer"
}
}
```
---
### 7. 删除通话记录
**接口地址**: `DELETE /api/front/call/record/{recordId}`
**路径参数**:
```
recordId: long // 记录ID
```
**请求参数**: 无
**返回数据**:
```json
{
"code": 200,
"msg": "删除成功",
"data": true
}
```
---
### 8. 获取未接来电数量
**接口地址**: `GET /api/front/call/missed/count`
**请求参数**: 无
**返回数据**:
```json
{
"code": 200,
"msg": "success",
"data": 5 // 未接来电数量
}
```
---
### 9. 获取通话状态
**接口地址**: `GET /api/front/call/status`
**请求参数**: 无
**返回数据**:
```json
{
"code": 200,
"msg": "success",
"data": {
"inCall": "boolean", // 是否正在通话中
"callId": "string", // 当前通话ID
"callType": "string", // 通话类型
"otherUserId": "integer", // 对方用户ID
"startTime": "long" // 通话开始时间
}
}
```
---
### 10. 获取通话详情
**接口地址**: `GET /api/front/call/detail/{callId}`
**路径参数**:
```
callId: string // 通话ID
```
**请求参数**: 无
**返回数据** (CallRecordResponse):
```json
{
"code": 200,
"msg": "success",
"data": {
"id": "long",
"callId": "string",
"callType": "string",
"callDirection": "string",
"callerId": "integer",
"callerName": "string",
"callerAvatar": "string",
"calleeId": "integer",
"calleeName": "string",
"calleeAvatar": "string",
"status": "string",
"duration": "integer",
"startTime": "long",
"endTime": "long",
"endReason": "string"
}
}
```
---
## 功能说明
### 通话类型
| 类型 | 值 | 说明 |
|------|-----|------|
| 语音通话 | voice | 仅语音通话 |
| 视频通话 | video | 视频+语音通话 |
### 通话状态
| 状态 | 值 | 说明 |
|------|-----|------|
| 呼叫中 | calling | 正在呼叫对方 |
| 振铃中 | ringing | 对方正在振铃 |
| 通话中 | connected | 通话已接通 |
| 已完成 | completed | 通话正常结束 |
| 未接听 | missed | 对方未接听 |
| 已拒绝 | rejected | 对方拒绝接听 |
| 已取消 | cancelled | 主叫方取消 |
| 已结束 | ended | 通话结束 |
### 通话方向
| 方向 | 值 | 说明 |
|------|-----|------|
| 呼出 | outgoing | 我发起的通话 |
| 呼入 | incoming | 对方发起的通话 |
---
## 通话流程
### 发起通话流程
```
1. 用户A点击通话按钮
2. 调用发起通话接口
3. 获取callId和信令服务器地址
4. 连接信令服务器
5. 等待对方响应
6. 对方接听 → 建立P2P连接 → 开始通话
对方拒绝/超时 → 结束流程
```
### 接听通话流程
```
1. 用户B收到来电通知WebSocket推送
2. 显示来电界面
3. 用户点击接听按钮
4. 调用接听通话接口
5. 连接信令服务器
6. 建立P2P连接
7. 开始通话
```
### 结束通话流程
```
1. 用户点击挂断按钮
2. 调用结束通话接口
3. 断开P2P连接
4. 断开信令连接
5. 保存通话记录
6. 返回上一页面
```
---
## WebSocket推送
通话相关的实时通知通过WebSocket推送
### 来电通知
```json
{
"type": "incoming_call",
"callId": "call123",
"callType": "video",
"callerId": 123,
"callerName": "张三",
"callerAvatar": "https://..."
}
```
### 通话状态变化
```json
{
"type": "call_status",
"callId": "call123",
"status": "connected",
"timestamp": 1703001234567
}
```
### 对方挂断
```json
{
"type": "call_ended",
"callId": "call123",
"endReason": "normal",
"duration": 120
}
```
---
## 使用示例
### 1. 发起语音通话
```java
private void initiateVoiceCall(int calleeId) {
CallApiService callApiService = // 获取CallApiService实例
InitiateCallRequest request = new InitiateCallRequest(calleeId, "voice");
Call<CallApiResponse<InitiateCallResponse>> call =
callApiService.initiateCall(request);
call.enqueue(new Callback<CallApiResponse<InitiateCallResponse>>() {
@Override
public void onResponse(Call<CallApiResponse<InitiateCallResponse>> call,
Response<CallApiResponse<InitiateCallResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
CallApiResponse<InitiateCallResponse> apiResponse = response.body();
if (apiResponse.getCode() == 200) {
InitiateCallResponse data = apiResponse.getData();
String callId = data.getCallId();
String signalingUrl = data.getSignalingUrl();
// 跳转到通话界面
Intent intent = new Intent(this, CallActivity.class);
intent.putExtra("callId", callId);
intent.putExtra("callType", "voice");
intent.putExtra("signalingUrl", signalingUrl);
startActivity(intent);
}
}
}
@Override
public void onFailure(Call<CallApiResponse<InitiateCallResponse>> call, Throwable t) {
Toast.makeText(this, "发起通话失败", Toast.LENGTH_SHORT).show();
}
});
}
```
### 2. 接听通话
```java
private void acceptCall(String callId) {
CallApiService callApiService = // 获取CallApiService实例
Call<CallApiResponse<Boolean>> call = callApiService.acceptCall(callId);
call.enqueue(new Callback<CallApiResponse<Boolean>>() {
@Override
public void onResponse(Call<CallApiResponse<Boolean>> call,
Response<CallApiResponse<Boolean>> response) {
if (response.isSuccessful() && response.body() != null) {
CallApiResponse<Boolean> apiResponse = response.body();
if (apiResponse.getCode() == 200) {
// 跳转到通话界面
Intent intent = new Intent(this, CallActivity.class);
intent.putExtra("callId", callId);
startActivity(intent);
finish();
}
}
}
@Override
public void onFailure(Call<CallApiResponse<Boolean>> call, Throwable t) {
Toast.makeText(this, "接听失败", Toast.LENGTH_SHORT).show();
}
});
}
```
### 3. 获取通话记录
```java
private void loadCallHistory(int page) {
CallApiService callApiService = // 获取CallApiService实例
Call<CallApiResponse<CallHistoryResponse>> call =
callApiService.getCallHistory(page, 20);
call.enqueue(new Callback<CallApiResponse<CallHistoryResponse>>() {
@Override
public void onResponse(Call<CallApiResponse<CallHistoryResponse>> call,
Response<CallApiResponse<CallHistoryResponse>> response) {
if (response.isSuccessful() && response.body() != null) {
CallApiResponse<CallHistoryResponse> apiResponse = response.body();
if (apiResponse.getCode() == 200) {
CallHistoryResponse data = apiResponse.getData();
List<CallRecordResponse> records = data.getRecords();
// 更新UI显示通话记录
updateCallHistoryList(records);
}
}
}
@Override
public void onFailure(Call<CallApiResponse<CallHistoryResponse>> call, Throwable t) {
Toast.makeText(this, "加载失败", Toast.LENGTH_SHORT).show();
}
});
}
```
---
## 技术实现
### WebRTC集成
通话功能基于WebRTC技术实现
- 使用信令服务器交换SDP和ICE候选
- 建立P2P连接进行音视频传输
- 支持NAT穿透
### 权限要求
- 语音通话:`RECORD_AUDIO`
- 视频通话:`RECORD_AUDIO` + `CAMERA`
- 网络访问:`INTERNET`
---
## 注意事项
1. 所有接口都需要登录认证
2. 通话前需要检查对方是否在线
3. 需要申请音频和摄像头权限
4. 通话过程中保持屏幕常亮
5. 通话时禁用锁屏
6. 建议使用耳机以获得更好的通话质量
7. 通话记录保存30天
8. 未接来电会显示红点提示
9. 通话过程中网络断开需要自动重连
10. 需要集成WebRTC库实现音视频通话