fix(websocket): 修复WebSocket连接和视频消息处理问题

- 显式指定WebSocket URL确保连接到正确服务器
- 添加本地地址连接拦截逻辑防止错误连接
- 优化视频消息处理逻辑,包括加载状态管理和下载响应处理
- 使用时间戳作为唯一序列号避免重复
- 修复消息订阅逻辑,仅在有待处理视频请求时监听
This commit is contained in:
2025-08-26 16:18:37 +08:00
parent a26b465ff6
commit 3645075473
4 changed files with 148 additions and 67 deletions

View File

@@ -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,
})

View File

@@ -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}

View File

@@ -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("发送链接信息");
};

View File

@@ -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(),
});
}