204 lines
5.5 KiB
Markdown
204 lines
5.5 KiB
Markdown
# 现在需要修复的问题
|
||
1. 媒体预览的组件有问题:Uncaught TypeError: $setup.previewMedia is not a function
|
||
at _createElementBlock.onClick._cache.<computed>._cache.<computed> (MessageBubble.vue:27:21)
|
||
2. 点击视频聊天或者语音聊天后应该请求弹框(等待对方接听,对方传输接收则开始聊天,对方拒绝则给出消息提示然后存在聊天记录中),对方也要弹出视频/语音通话邀请,可以接收或者拒绝。
|
||
3. 根据{前端获取信令示例}和{后端交互代码}来完成整个聊天功能,并且要考虑用户掉线、断网、网络异常等情况,以及用户退出登录、用户被删除等情况。
|
||
4. 在好友管理点击聊天后要吧侧边导航变成聊天
|
||
5. 聊天记录列表和好友管理中中,点击对方的头像要有对方的信息
|
||
6. 完整的基于WebRTC实现语音/视频通话功能
|
||
|
||
|
||
|
||
|
||
# 前端获取信令示例
|
||
```javascript
|
||
// 发起通话邀请
|
||
function startCall(targetUserId, isVideo) {
|
||
const callId = generateUniqueId();
|
||
const callType = isVideo ? 6 : 7; // 6=视频,7=语音
|
||
|
||
const callSignal = {
|
||
action: "invite",
|
||
call_type: callType,
|
||
call_id: callId,
|
||
caller_id: currentUserId,
|
||
callee_id: targetUserId,
|
||
data: JSON.stringify(offer) // SDP offer
|
||
};
|
||
|
||
const payload = {
|
||
sender_user_id: currentUserId,
|
||
receiver_user_id: targetUserId,
|
||
message_type: callType,
|
||
message_content: JSON.stringify(callSignal)
|
||
};
|
||
|
||
// 调用/send-to-user接口
|
||
fetch('/send-to-user', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(payload)
|
||
}).then(response => {
|
||
if (!response.ok) {
|
||
throw new Error('Failed to send call invite');
|
||
}
|
||
return response.json();
|
||
}).then(data => {
|
||
console.log('Call invite sent:', data);
|
||
}).catch(error => {
|
||
console.error('Error sending call invite:', error);
|
||
});
|
||
}
|
||
|
||
// 接受通话
|
||
function acceptCall(callId, callType) {
|
||
const callSignal = {
|
||
action: "accept",
|
||
call_id: callId,
|
||
data: JSON.stringify(answer) // SDP answer
|
||
};
|
||
|
||
const payload = {
|
||
sender_user_id: currentUserId,
|
||
receiver_user_id: callerUserId,
|
||
message_type: callType,
|
||
message_content: JSON.stringify(callSignal)
|
||
};
|
||
|
||
fetch('/send-to-user', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(payload)
|
||
});
|
||
}
|
||
|
||
// 拒绝通话
|
||
function rejectCall(callId, callType, reason) {
|
||
const callSignal = {
|
||
action: "reject",
|
||
call_id: callId,
|
||
data: reason || "用户忙"
|
||
};
|
||
|
||
const payload = {
|
||
sender_user_id: currentUserId,
|
||
receiver_user_id: callerUserId,
|
||
message_type: callType,
|
||
message_content: JSON.stringify(callSignal)
|
||
};
|
||
|
||
fetch('/send-to-user', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(payload)
|
||
});
|
||
}
|
||
|
||
// 结束通话
|
||
function endCall(callId, callType) {
|
||
const callSignal = {
|
||
action: "end",
|
||
call_id: callId
|
||
};
|
||
|
||
const payload = {
|
||
sender_user_id: currentUserId,
|
||
receiver_user_id: otherUserId,
|
||
message_type: callType,
|
||
message_content: JSON.stringify(callSignal)
|
||
};
|
||
|
||
fetch('/send-to-user', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(payload)
|
||
});
|
||
}
|
||
|
||
// 发送ICE候选
|
||
function sendCandidate(callId, callType, candidate, targetUserId) {
|
||
const callSignal = {
|
||
action: "candidate",
|
||
call_id: callId,
|
||
data: JSON.stringify(candidate)
|
||
};
|
||
|
||
const payload = {
|
||
sender_user_id: currentUserId,
|
||
receiver_user_id: targetUserId,
|
||
message_type: callType,
|
||
message_content: JSON.stringify(callSignal)
|
||
};
|
||
|
||
fetch('/send-to-user', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(payload)
|
||
});
|
||
}
|
||
```
|
||
|
||
## 后端交互代码
|
||
1. {websocket_controller.go}
|
||
|
||
## 消息结构
|
||
1. {messages.go}
|
||
|
||
## 通话流程图
|
||
```mermaid
|
||
graph TD
|
||
A[用户A点击通话按钮] --> B{选择通话类型}
|
||
B --> |语音通话| C1[设置 message_type=7]
|
||
B --> |视频通话| C2[设置 message_type=6]
|
||
|
||
C1 --> D[生成唯一CallID]
|
||
D --> E[创建PeerConnection对象]
|
||
E --> F[获取本地媒体流]
|
||
F --> G[创建SDP offer]
|
||
G --> H[构造信令对象]
|
||
|
||
H --> I[发送邀请信令]
|
||
I --> J[后端转发给用户B]
|
||
|
||
J --> K[用户B收到来电通知]
|
||
K --> L{用户B选择}
|
||
L --> |接听| M[创建SDP answer]
|
||
L --> |拒绝| N[发送拒绝信令]
|
||
|
||
M --> O[发送接受信令]
|
||
O --> P[后端转发给用户A]
|
||
|
||
P --> Q[用户A设置远程描述]
|
||
Q --> R[交换ICE候选]
|
||
|
||
R --> S[建立P2P连接]
|
||
S --> T[开始语音通话]
|
||
|
||
N --> U[用户A收到拒绝通知]
|
||
U --> V[结束通话流程]
|
||
|
||
subgraph 信令交换
|
||
I[发送邀请信令]
|
||
O[发送接受信令]
|
||
R[交换ICE候选]
|
||
end
|
||
|
||
subgraph WebRTC连接
|
||
E[创建PeerConnection]
|
||
F[获取本地媒体流]
|
||
G[创建SDP offer]
|
||
M[创建SDP answer]
|
||
Q[设置远程描述]
|
||
S[建立P2P连接]
|
||
end
|
||
``` |