# 通话功能模块 ## 模块概述 通话功能模块负责处理用户之间的语音和视频通话功能,包括发起通话、接听/拒绝通话、通话记录等。 ## 相关文件 - `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> call = callApiService.initiateCall(request); call.enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { if (response.isSuccessful() && response.body() != null) { CallApiResponse 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> call, Throwable t) { Toast.makeText(this, "发起通话失败", Toast.LENGTH_SHORT).show(); } }); } ``` ### 2. 接听通话 ```java private void acceptCall(String callId) { CallApiService callApiService = // 获取CallApiService实例 Call> call = callApiService.acceptCall(callId); call.enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { if (response.isSuccessful() && response.body() != null) { CallApiResponse 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> call, Throwable t) { Toast.makeText(this, "接听失败", Toast.LENGTH_SHORT).show(); } }); } ``` ### 3. 获取通话记录 ```java private void loadCallHistory(int page) { CallApiService callApiService = // 获取CallApiService实例 Call> call = callApiService.getCallHistory(page, 20); call.enqueue(new Callback>() { @Override public void onResponse(Call> call, Response> response) { if (response.isSuccessful() && response.body() != null) { CallApiResponse apiResponse = response.body(); if (apiResponse.getCode() == 200) { CallHistoryResponse data = apiResponse.getData(); List records = data.getRecords(); // 更新UI显示通话记录 updateCallHistoryList(records); } } } @Override public void onFailure(Call> 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库实现音视频通话