Files
nl-im-websocket-demo/README2.md
2025-07-05 15:57:28 +08:00

204 lines
5.5 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.
# 现在需要修复的问题
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
```