From 357a479571dd0f6926a1aa014af92fb18b83e66c Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Wed, 10 Dec 2025 14:21:52 +0800 Subject: [PATCH] =?UTF-8?q?fix(addon/aikefu):=20=E4=BF=AE=E5=A4=8DEventSou?= =?UTF-8?q?rce=E6=9C=89=E9=A2=9D=E5=A4=96=E7=9A=84=E7=A9=BA=E8=A1=A8?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=BE=93=E5=87=BA=EF=BC=8C=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E5=89=8D=E7=AB=AFEventSource=E6=8D=95=E6=8D=89=E5=88=B0error?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/addon/aikefu/api/controller/Kefu.php | 11 +++++++- src/addon/aikefu/docs/stream_chat_demo.html | 30 +++++++++++++++------ src/app/api/controller/Kefu.php | 2 ++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/addon/aikefu/api/controller/Kefu.php b/src/addon/aikefu/api/controller/Kefu.php index 1248f2ef0..8e23b44dd 100644 --- a/src/addon/aikefu/api/controller/Kefu.php +++ b/src/addon/aikefu/api/controller/Kefu.php @@ -213,6 +213,8 @@ class Kefu extends BaseApi if ($stream) { // 处理流式响应 $this->handleStreamingResponse($url, $requestData, $headers, $message, $user_id); + // 流式响应处理完成后必须退出脚本,避免返回额外内容 + exit; } else { // 处理非流式响应 return $this->handleBlockingResponse($url, $requestData, $headers, $message, $user_id, $conversation_id); @@ -617,12 +619,16 @@ class Kefu extends BaseApi 'content' => $assistant_content ]; echo "event: done\ndata: " . json_encode($done_data) . "\n\n"; + + // 发送连接关闭事件,让客户端知道连接已正常结束 + echo "event: close\ndata: {\"status\":\"completed\"}\n\n"; + // 只在有输出缓冲时才刷新 if (ob_get_level() > 0) { ob_flush(); } flush(); - $this->log('发送done事件,会话ID:' . $real_conversation_id, 'info'); + $this->log('发送done和close事件,会话ID:' . $real_conversation_id, 'info'); // 保存助手的完整回复 if (!empty($real_conversation_id) && !empty($real_assistant_message_id) && !empty($assistant_content)) { @@ -637,6 +643,9 @@ class Kefu extends BaseApi $error_msg = 'AI客服请求异常:' . $e->getMessage() . ',错误行:' . $e->getLine() . ',错误文件:' . $e->getFile(); $this->log($error_msg, 'error'); } + + // 流式响应处理完成后必须退出脚本,避免EventSource协议错误 + exit; } /** diff --git a/src/addon/aikefu/docs/stream_chat_demo.html b/src/addon/aikefu/docs/stream_chat_demo.html index e6326508a..34b37ce4f 100644 --- a/src/addon/aikefu/docs/stream_chat_demo.html +++ b/src/addon/aikefu/docs/stream_chat_demo.html @@ -231,7 +231,7 @@ if (data.event === 'message_end') { statusText.textContent = '对话完成'; - sendBtn.disabled = false; + // 不要在这里关闭连接,等待done和close事件 } if (data.conversation_id) { @@ -257,16 +257,35 @@ } }); + // 监听连接关闭事件 + es.addEventListener('close', (event) => { + console.log('收到连接关闭事件:', event); + try { + const data = JSON.parse(event.data); + console.log('连接正常结束:', data); + if (es) { + es.close(); + es = null; + } + } catch (error) { + console.error('解析关闭事件失败:', error); + } + }); + // 监听错误事件 es.addEventListener('error', (error) => { console.error('EventSource 错误:', error); // 添加更详细的错误信息 if (es.readyState === EventSource.CLOSED) { - console.error('EventSource 连接已关闭'); + console.log('EventSource 连接已正常关闭'); + statusText.textContent = '连接已关闭'; } else if (es.readyState === EventSource.CONNECTING) { console.error('EventSource 连接中出现错误'); + statusText.textContent = '连接错误'; + } else { + console.error('EventSource 未知错误'); + statusText.textContent = '连接错误'; } - statusText.textContent = '连接错误'; sendBtn.disabled = false; if (es) { es.close(); @@ -274,11 +293,6 @@ } }); - // 监听关闭事件 - es.addEventListener('close', () => { - console.log('EventSource 连接关闭'); - }); - } catch (error) { console.error('创建 EventSource 失败:', error); statusText.textContent = '请求失败'; diff --git a/src/app/api/controller/Kefu.php b/src/app/api/controller/Kefu.php index 49f0d80f3..637facc53 100644 --- a/src/app/api/controller/Kefu.php +++ b/src/app/api/controller/Kefu.php @@ -365,6 +365,8 @@ class Kefu extends BaseApi // 触发事件,让监听器处理流式响应 event('KefuChat', $event_data); + // 流式响应需要终止脚本执行,避免输出额外内容导致EventSource错误 + exit; } else { // 触发智能客服聊天事件(非流式) $result = event('KefuChat', $event_data);