From 5087d77f23b13540d8ab2891aece9da239056441 Mon Sep 17 00:00:00 2001 From: sion123 <450702724@qq.com> Date: Fri, 8 May 2026 03:15:21 +0800 Subject: [PATCH] =?UTF-8?q?fix(chat):=20=E4=BF=AE=E5=A4=8D=E9=87=8D?= =?UTF-8?q?=E8=BF=9E=E5=90=8E=E5=AF=B9=E8=AF=9D=E7=8A=B6=E6=80=81=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E5=AF=BC=E8=87=B4=E6=B6=88=E6=81=AF=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 WebSocket 重连场景下,服务端可能丢失 `conversationId` 状态,导致后续消息发送被拒绝。通过在客户端消息中携带 `conversationId`,并在服务端添加 fallback 恢复逻辑,确保重连后仍能正常发送消息。 同时优化了 `pendingMessage` 类型定义,支持存储待发送的图片附件,修复了延迟发送场景下图片丢失的问题。 --- web/client/src/components/chat/ChatInput.tsx | 2 +- web/client/src/components/chat/ChatView.tsx | 10 +++++----- web/client/src/hooks/useChat.ts | 5 +++-- web/server/ws/chat.ts | 4 ++++ 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/web/client/src/components/chat/ChatInput.tsx b/web/client/src/components/chat/ChatInput.tsx index ca26459..c4c19d6 100644 --- a/web/client/src/components/chat/ChatInput.tsx +++ b/web/client/src/components/chat/ChatInput.tsx @@ -75,7 +75,7 @@ export function ChatInput({ onSend, disabled, connecting }: { onSend: (content: const handleSend = () => { const text = input.trim(); if ((!text && images.length === 0) || disabled) return; - const imgs = images.length > 0 ? images.map(({ data, mimeType }) => ({ data, mimeType })) : undefined; + const imgs = images.length > 0 ? images.map(({ data, mimeType }) => ({ type: 'image' as const, data, mimeType })) : undefined; onSend(text || '(图片)', imgs); setInput(''); setImages([]); diff --git a/web/client/src/components/chat/ChatView.tsx b/web/client/src/components/chat/ChatView.tsx index 00df73a..19e1c26 100644 --- a/web/client/src/components/chat/ChatView.tsx +++ b/web/client/src/components/chat/ChatView.tsx @@ -19,7 +19,7 @@ export function ChatView() { const [manifestPath, setManifestPath] = useState(null); const [accounts, setAccounts] = useState([]); const [quote, setQuote] = useState(null); - const [pendingMessage, setPendingMessage] = useState(null); + const [pendingMessage, setPendingMessage] = useState<{ content: string; images?: Array<{ type: 'image'; data: string; mimeType: string }> } | null>(null); const [showScrollBtn, setShowScrollBtn] = useState(false); const creatingRef = useRef(false); const scrollRef = useRef(null); @@ -72,7 +72,7 @@ export function ChatView() { useEffect(() => { if (conversationId && connected && pendingMessage) { const timer = setTimeout(() => { - send(pendingMessage); + send(pendingMessage.content, pendingMessage.images); setPendingMessage(null); }, 300); return () => clearTimeout(timer); @@ -108,10 +108,10 @@ export function ChatView() { }, [removeMessage]); // Delayed conversation creation - const handleSendNew = useCallback(async (content: string) => { + const handleSendNew = useCallback(async (content: string, images?: Array<{ data: string; mimeType: string }>) => { if (creatingRef.current) return; creatingRef.current = true; - setPendingMessage(content); + setPendingMessage({ content, images }); try { const newId = await createConversation(content.slice(0, 30), selectedAccountId || undefined); @@ -131,7 +131,7 @@ export function ChatView() { if (conversationId) { send(content, images); } else { - handleSendNew(content); + handleSendNew(content, images); } }, [send, quote, conversationId]); diff --git a/web/client/src/hooks/useChat.ts b/web/client/src/hooks/useChat.ts index 894c1c2..9178a04 100644 --- a/web/client/src/hooks/useChat.ts +++ b/web/client/src/hooks/useChat.ts @@ -156,8 +156,9 @@ export function useChat(conversationId: string | null) { } }, [conversationId, connected]); - const send = useCallback((content: string, images?: Array<{ data: string; mimeType: string }>) => { - chatSocket.send('chat', { content, images }); + const send = useCallback((content: string, images?: Array<{ type: 'image'; data: string; mimeType: string }>) => { + const convId = activeConvRef.current; + chatSocket.send('chat', { content, images, conversationId: convId }); }, []); const stop = useCallback(() => { diff --git a/web/server/ws/chat.ts b/web/server/ws/chat.ts index 29d2424..bf17f56 100644 --- a/web/server/ws/chat.ts +++ b/web/server/ws/chat.ts @@ -30,6 +30,10 @@ export function handleChat(ws: WebSocket) { } if (msg.type === 'chat') { + // Fallback: use conversationId from message if server state lost (e.g. after reconnect) + if (!conversationId && msg.conversationId) { + conversationId = msg.conversationId as string; + } if (!conversationId) { ws.send(JSON.stringify({ type: 'error', data: { message: '没有活跃对话,请先创建或选择一个对话' } })); return;