fix(websocket): 修复WebSocket连接和视频消息处理问题
- 显式指定WebSocket URL确保连接到正确服务器 - 添加本地地址连接拦截逻辑防止错误连接 - 优化视频消息处理逻辑,包括加载状态管理和下载响应处理 - 使用时间戳作为唯一序列号避免重复 - 修复消息订阅逻辑,仅在有待处理视频请求时监听
This commit is contained in:
@@ -116,6 +116,7 @@ const WebSocketExample: React.FC = () => {
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
connect({
|
||||
url: "wss://kf.quwanzhi.com:9993", // 显式指定WebSocket URL,确保使用正确的服务器地址
|
||||
client: "kefu-client",
|
||||
autoReconnect: true,
|
||||
})
|
||||
|
||||
@@ -47,7 +47,7 @@ import dayjs from "dayjs";
|
||||
import { ChatRecord, ContractData } from "@/pages/pc/ckbox/data";
|
||||
import { clearUnreadCount, getMessages } from "@/pages/pc/ckbox/api";
|
||||
import styles from "./ChatWindow.module.scss";
|
||||
import { useWebSocketStore } from "@/store/module/websocket";
|
||||
import { useWebSocketStore, WebSocketMessage } from "@/store/module/websocket";
|
||||
const { Header, Content, Footer, Sider } = Layout;
|
||||
const { TextArea } = Input;
|
||||
|
||||
@@ -76,6 +76,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
clearUnreadCount([contract.id]).then(() => {
|
||||
setLoading(true);
|
||||
getMessages({
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
wechatFriendId: contract.id,
|
||||
@@ -83,73 +84,106 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
To: +new Date() + 1000,
|
||||
Count: 100,
|
||||
olderData: true,
|
||||
}).then(msg => {
|
||||
setMessages(msg);
|
||||
});
|
||||
})
|
||||
.then(msg => {
|
||||
setMessages(msg);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
});
|
||||
}, [contract.id]);
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
// 只有在非视频加载操作时才自动滚动到底部
|
||||
// 检查是否有视频正在加载中
|
||||
const hasLoadingVideo = messages.some(msg => {
|
||||
try {
|
||||
const content =
|
||||
typeof msg.content === "string"
|
||||
? JSON.parse(msg.content)
|
||||
: msg.content;
|
||||
return content.isLoading === true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasLoadingVideo) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [messages]);
|
||||
|
||||
// 添加 WebSocket 消息订阅
|
||||
// 添加 WebSocket 消息订阅 - 监听视频下载响应消息
|
||||
useEffect(() => {
|
||||
// 只有当有待处理的视频请求时才订阅WebSocket消息
|
||||
if (Object.keys(pendingVideoRequests).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("开始监听视频下载响应,当前待处理请求:", pendingVideoRequests);
|
||||
|
||||
// 订阅 WebSocket 消息变化
|
||||
const unsubscribe = useWebSocketStore.subscribe(
|
||||
state => state.messages,
|
||||
(messages, previousMessages) => {
|
||||
// 只处理新消息
|
||||
if (messages.length > previousMessages.length) {
|
||||
// 获取最新的消息
|
||||
const newMessages = messages.slice(previousMessages.length);
|
||||
const unsubscribe = useWebSocketStore.subscribe(state => {
|
||||
// 只处理新增的消息
|
||||
const messages = state.messages as WebSocketMessage[];
|
||||
|
||||
// 处理新消息
|
||||
newMessages.forEach(message => {
|
||||
const content = message.content;
|
||||
// 筛选出视频下载响应消息
|
||||
messages.forEach(message => {
|
||||
if (message?.content?.cmdType === "CmdDownloadVideoResult") {
|
||||
console.log("收到视频下载响应:", message.content);
|
||||
|
||||
// 检查是否是视频下载响应
|
||||
if (content && content.cmdType === "CmdDownloadVideoResponse") {
|
||||
// 获取视频URL
|
||||
const videoUrl = content.videoUrl;
|
||||
const requestId = content.requestId || content.seq;
|
||||
// 检查是否是我们正在等待的视频响应
|
||||
const messageId = Object.keys(pendingVideoRequests).find(
|
||||
id => pendingVideoRequests[id] === message.content.friendMessageId,
|
||||
);
|
||||
|
||||
// 查找对应的消息ID
|
||||
const messageId = pendingVideoRequests[requestId];
|
||||
if (messageId && videoUrl) {
|
||||
// 更新消息内容,将预览图替换为实际视频
|
||||
setMessages(prevMessages => {
|
||||
return prevMessages.map(msg => {
|
||||
if (msg.id === messageId) {
|
||||
return {
|
||||
...msg,
|
||||
content: videoUrl,
|
||||
};
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
});
|
||||
if (messageId) {
|
||||
console.log("找到对应的消息ID:", messageId);
|
||||
|
||||
// 从待处理请求中移除
|
||||
setPendingVideoRequests(prev => {
|
||||
const newRequests = { ...prev };
|
||||
delete newRequests[requestId];
|
||||
return newRequests;
|
||||
});
|
||||
// 从待处理队列中移除
|
||||
setPendingVideoRequests(prev => {
|
||||
const newRequests = { ...prev };
|
||||
delete newRequests[messageId];
|
||||
return newRequests;
|
||||
});
|
||||
|
||||
messageApi.success("视频加载成功");
|
||||
}
|
||||
}
|
||||
});
|
||||
// 更新消息内容,将视频URL添加到对应的消息中
|
||||
setMessages(prevMessages => {
|
||||
return prevMessages.map(msg => {
|
||||
if (msg.id === Number(messageId)) {
|
||||
try {
|
||||
const msgContent =
|
||||
typeof msg.content === "string"
|
||||
? JSON.parse(msg.content)
|
||||
: msg.content;
|
||||
|
||||
// 更新消息内容,添加视频URL并移除加载状态
|
||||
return {
|
||||
...msg,
|
||||
content: JSON.stringify({
|
||||
...msgContent,
|
||||
videoUrl: message.content.url,
|
||||
isLoading: false,
|
||||
}),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("解析消息内容失败:", e);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// 组件卸载时取消订阅
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [pendingVideoRequests, messageApi]);
|
||||
}, [pendingVideoRequests]); // 依赖于pendingVideoRequests,当队列变化时重新设置订阅
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
@@ -237,21 +271,24 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
};
|
||||
|
||||
// 处理视频播放请求,发送socket请求获取真实视频地址
|
||||
const handleVideoPlayRequest = (tencentUrl: string, messageId: string) => {
|
||||
// 生成请求ID (可以使用 seq 或其他唯一标识)
|
||||
const handleVideoPlayRequest = (tencentUrl: string, messageId: number) => {
|
||||
// 生成请求ID (使用当前时间戳作为唯一标识)
|
||||
const requestSeq = `${+new Date()}`;
|
||||
console.log("发送视频下载请求:", { messageId, requestSeq });
|
||||
|
||||
// 构建socket请求数据
|
||||
useWebSocketStore.getState().sendCommand("CmdDownloadVideo", {
|
||||
chatroomMessageId: contract.chatroomId ? contract.chatroomId : 0,
|
||||
friendMessageId: contract.chatroomId ? 0 : contract.id,
|
||||
seq: 9, // 使用唯一的请求ID
|
||||
chatroomMessageId: contract.chatroomId ? messageId : 0,
|
||||
friendMessageId: contract.chatroomId ? 0 : messageId,
|
||||
seq: requestSeq, // 使用唯一的请求ID
|
||||
tencentUrl: tencentUrl,
|
||||
wechatAccountId: contract.wechatAccountId,
|
||||
});
|
||||
|
||||
// 记录待处理的视频请求
|
||||
// 将消息ID和请求序列号添加到待处理队列
|
||||
setPendingVideoRequests(prev => ({
|
||||
...prev,
|
||||
[messageId]: messageId,
|
||||
}));
|
||||
|
||||
// 更新消息状态为加载中
|
||||
@@ -313,6 +350,31 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
handleVideoPlayRequest(videoData.tencentUrl, msg.id);
|
||||
};
|
||||
|
||||
// 检查是否已下载视频URL
|
||||
if (videoData.videoUrl) {
|
||||
// 已获取到视频URL,显示视频播放器
|
||||
return (
|
||||
<div className={styles.videoMessage}>
|
||||
<div className={styles.videoContainer}>
|
||||
<video
|
||||
controls
|
||||
src={videoData.videoUrl}
|
||||
style={{ maxWidth: "100%", borderRadius: "8px" }}
|
||||
/>
|
||||
<a
|
||||
href={videoData.videoUrl}
|
||||
download
|
||||
className={styles.downloadButton}
|
||||
style={{ display: "flex" }}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<DownloadOutlined style={{ fontSize: "18px" }} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 检查是否处于加载状态
|
||||
if (videoData.isLoading) {
|
||||
return (
|
||||
@@ -336,6 +398,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
// 默认显示预览图和播放按钮
|
||||
return (
|
||||
<div className={styles.videoMessage}>
|
||||
<div className={styles.videoContainer} onClick={handlePlayClick}>
|
||||
@@ -709,7 +772,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
};
|
||||
|
||||
const renderMessage = (msg: ChatRecord) => {
|
||||
const isOwn = msg.senderId === "me";
|
||||
const isOwn = msg.isSend;
|
||||
return (
|
||||
<div
|
||||
key={msg.id}
|
||||
|
||||
@@ -25,13 +25,19 @@ export const chatInitAPIdata = async () => {
|
||||
const accountId = getAccountId();
|
||||
|
||||
//发起链接
|
||||
connect({
|
||||
accessToken: String(Token),
|
||||
accountId: accountId,
|
||||
client: "kefu-client",
|
||||
cmdType: "CmdSignIn",
|
||||
seq: 1,
|
||||
});
|
||||
if (Token && accountId) {
|
||||
connect({
|
||||
url: "wss://kf.quwanzhi.com:9993", // 显式指定WebSocket URL,确保使用正确的服务器地址
|
||||
accessToken: String(Token),
|
||||
accountId: accountId,
|
||||
client: "kefu-client",
|
||||
cmdType: "CmdSignIn",
|
||||
seq: +new Date(),
|
||||
});
|
||||
console.log("WebSocket连接已初始化");
|
||||
} else {
|
||||
console.error("WebSocket连接初始化失败:缺少Token或accountId");
|
||||
}
|
||||
//获取联系人列表
|
||||
const contractList = await getAllContactList();
|
||||
//获取群列表
|
||||
@@ -137,7 +143,7 @@ export const getChatInfo = () => {
|
||||
//获取UserId
|
||||
sendCommand("CmdRequestWechatAccountsAliveStatus", {
|
||||
wechatAccountIds: ["300745", "4880930", "32686452"],
|
||||
seq: 2,
|
||||
seq: +new Date(),
|
||||
});
|
||||
console.log("发送链接信息");
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface WebSocketMessage {
|
||||
cmdType?: string;
|
||||
seq?: number;
|
||||
wechatAccountIds?: string[];
|
||||
content?: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ const DEFAULT_CONFIG: WebSocketConfig = {
|
||||
accessToken: "",
|
||||
autoReconnect: true,
|
||||
cmdType: "", // 添加默认的命令类型
|
||||
seq: 0, // 添加默认的序列号
|
||||
seq: +new Date(), // 添加默认的序列号
|
||||
reconnectInterval: 3000,
|
||||
maxReconnectAttempts: 5,
|
||||
};
|
||||
@@ -123,7 +124,17 @@ export const useWebSocketStore = createPersistStore<WebSocketState>(
|
||||
t: Date.now().toString(),
|
||||
});
|
||||
|
||||
// 检查URL是否为localhost,如果是则不连接
|
||||
const wsUrl = fullConfig.url + "?" + params;
|
||||
if (wsUrl.includes("localhost") || wsUrl.includes("127.0.0.1")) {
|
||||
console.error("WebSocket连接被拦截:不允许连接到本地地址", wsUrl);
|
||||
Toast.show({
|
||||
content: "WebSocket连接被拦截:不允许连接到本地地址",
|
||||
position: "top",
|
||||
});
|
||||
set({ status: WebSocketStatus.ERROR });
|
||||
return;
|
||||
}
|
||||
|
||||
set({
|
||||
status: WebSocketStatus.CONNECTING,
|
||||
@@ -260,12 +271,12 @@ export const useWebSocketStore = createPersistStore<WebSocketState>(
|
||||
accessToken: token2,
|
||||
accountId: Number(getAccountId()),
|
||||
client: currentState.config?.client || "kefu-client",
|
||||
seq: currentState.config?.seq || 1,
|
||||
seq: +new Date(),
|
||||
});
|
||||
//获取UserId
|
||||
currentState.sendCommand("CmdRequestWechatAccountsAliveStatus", {
|
||||
wechatAccountIds: ["300745", "4880930", "32686452"],
|
||||
seq: 2,
|
||||
seq: +new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user