From aa03abf651fb2c18380eda81114e06920d554c9c Mon Sep 17 00:00:00 2001 From: jinhhanhan <1683105490@qq.com> Date: Fri, 12 Dec 2025 10:48:26 +0800 Subject: [PATCH] =?UTF-8?q?chore=EF=BC=9Aai=E5=AE=A2=E6=9C=8D=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E6=B5=81=E5=BC=8F=E6=95=B0=E6=8D=AE=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/js/ai-service.js | 269 ++++++++++++++++------------------------ 1 file changed, 106 insertions(+), 163 deletions(-) diff --git a/common/js/ai-service.js b/common/js/ai-service.js index e46355f..2a48d26 100644 --- a/common/js/ai-service.js +++ b/common/js/ai-service.js @@ -11,19 +11,12 @@ export default { */ async sendMessage(message, options = {}) { try { - // 获取AI配置 const aiConfig = store.getters.globalAIKefuConfig - - // const new_conversationId = await this.generateConversationId() - - // 构建Dify API请求参数 const params = { - url: '/api/kefu/chat', // 后端代理接口 + url: '/api/kefu/chat', data: { - // conversation_id: options.conversationId ?? new_conversationId, user_id: store.state.memberInfo?.id || 'anonymous', stream: false, - // Dify API参数 inputs: {}, query: message, response_mode: options.stream ? 'streaming' : 'blocking', @@ -33,18 +26,14 @@ export default { 'Content-Type': 'application/json' } } - // ✅ 发送请求前打印日志,用于调试 - console.log('Sending request to:', params.url); - console.log('Request data:', params.data); + console.log('Sending request to:', params.url); + console.log('Request data:', params.data); - // 发送请求 const response = await http.sendRequest({ ...params, - async: false // 使用Promise方式 + async: false }) - return this.handleResponse(response, options) - } catch (error) { console.error('Dify API请求失败:', error) throw new Error('AI服务暂时不可用,请稍后重试') @@ -53,15 +42,10 @@ export default { /** * 流式消息处理 - * @param {string} message 用户消息 - * @param {Function} onChunk 流式数据回调 - * @param {Function} onComplete 完成回调 */ async sendStreamMessage(message, onChunk, onComplete) { try { - // 使用HTTP流式请求 - return this.sendHttpStream(message, onChunk, onComplete) - + return this.sendHttpStream(message, onChunk, onComplete) } catch (error) { console.error('流式消息发送失败:', error) throw error @@ -70,20 +54,11 @@ export default { /** * EventSource 流式聊天 - * @param {string} message 用户消息 - * @param {string} conversationId 会话ID(可选) - * @param {Function} onMessage 消息回调 - * @param {Function} onComplete 完成回调 - * @param {Function} onError 错误回调 - * @returns {EventSource|null} EventSource实例 */ chatWithEventSource(message, conversationId = '', onMessage, onComplete, onError) { - // 关闭之前的连接 if (window.currentEventSource) { window.currentEventSource.close(); } - - // 构建请求参数 const params = new URLSearchParams({ uniacid: store.state.uniacid || '1', user_id: store.state.memberInfo?.id || '123456', @@ -91,34 +66,24 @@ export default { conversation_id: conversationId || '', stream: 'true' }); - const url = `/api/kefu/chat?${params.toString()}`; - try { const eventSource = new EventSource(url); window.currentEventSource = eventSource; - let aiMessage = ''; - - // 监听消息事件 eventSource.addEventListener('message', (event) => { try { const data = JSON.parse(event.data); - if (data.event === 'message') { - // 更新 AI 消息 aiMessage += data.answer || ''; if (onMessage) onMessage(data.answer || ''); } - if (data.event === 'message_end') { - // 对话完成 if (onComplete) onComplete({ conversation_id: data.conversation_id, message: aiMessage }); } - if (data.conversation_id) { conversationId = data.conversation_id; } @@ -126,8 +91,6 @@ export default { console.error('解析消息失败:', error); } }); - - // 监听完成事件 eventSource.addEventListener('done', (event) => { try { const data = JSON.parse(event.data); @@ -136,8 +99,6 @@ export default { console.error('解析完成事件失败:', error); } }); - - // 监听关闭事件 eventSource.addEventListener('close', (event) => { try { const data = JSON.parse(event.data); @@ -147,16 +108,12 @@ export default { } window.currentEventSource = null; }); - - // 监听错误事件 eventSource.addEventListener('error', (error) => { console.error('EventSource错误:', error); if (onError) onError({ error: 'EventSource连接错误' }); window.currentEventSource = null; }); - return eventSource; - } catch (error) { console.error('创建EventSource失败:', error); if (onError) onError({ error: error.message }); @@ -166,21 +123,13 @@ export default { /** * Fetch API 流式聊天 - * @param {string} message 用户消息 - * @param {string} conversationId 会话ID(可选) - * @param {Function} onMessage 消息回调 - * @param {Function} onComplete 完成回调 - * @param {Function} onError 错误回调 */ async chatWithFetchStream(message, conversationId = '', onMessage, onComplete, onError) { try { - // 构建请求体 - 支持JSON和FormData两种格式 let requestData; let headers = { 'Accept': 'text/event-stream' }; - - // 优先使用JSON格式 requestData = { uniacid: store.state.uniacid || '1', user_id: store.state.memberInfo?.id || '123456', @@ -191,45 +140,33 @@ export default { response_mode: 'streaming', user: store.state.memberInfo?.id || '123456' }; - headers['Content-Type'] = 'application/json'; - const response = await fetch('/api/kefu/chat', { method: 'POST', headers: headers, body: JSON.stringify(requestData) }); - if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } - if (!response.body) { throw new Error('响应体不可用'); } - const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; let aiMessage = ''; - while (true) { const { done, value } = await reader.read(); if (done) break; - - // 解码新接收的数据 buffer += decoder.decode(value, { stream: true }); - - // 处理缓冲的数据,按行分割 let lineEnd; while ((lineEnd = buffer.indexOf('\n')) !== -1) { const line = buffer.substring(0, lineEnd); buffer = buffer.substring(lineEnd + 1); - if (line.startsWith('data: ')) { try { const data = JSON.parse(line.substring(6)); - if (data.event === 'message' || data.event === 'text_message') { const textContent = data.answer || data.text || ''; aiMessage += textContent; @@ -243,7 +180,6 @@ export default { } else if (data.event === 'error' && onError) { onError(data); } - if (data.conversation_id) { conversationId = data.conversation_id; } @@ -253,8 +189,6 @@ export default { } } } - - // 处理剩余的缓冲数据 if (buffer.startsWith('data: ')) { try { const data = JSON.parse(buffer.substring(6)); @@ -272,7 +206,6 @@ export default { console.warn('解析剩余数据失败:', e); } } - } catch (error) { console.error('Fetch流式聊天请求失败:', error); if (onError) onError({ error: error.message }); @@ -280,17 +213,17 @@ export default { }, /** - * HTTP流式请求(现有方法保持不变) + * HTTP流式请求(修复conversationId未定义) */ async sendHttpStream(message, onChunk, onComplete) { const params = { url: '/api/kefu/chat', data: { query: message, - conversation_id: '', // 可以在外部传入或保持为空让服务器生成 + conversation_id: '', user_id: store.state.memberInfo?.id || 'anonymous', stream: true, - uniacid: store.state.uniacid, // 保留必填参数 + uniacid: store.state.uniacid, inputs: {}, response_mode: 'streaming', user: store.state.memberInfo?.id || 'anonymous' @@ -299,107 +232,86 @@ export default { 'Content-Type': 'application/json' } } - - // 使用fetch API进行流式请求(H5环境) // #ifdef H5 try { - const url = Config.baseUrl + `/api/kefu/chat`; + const url = Config.baseUrl + `/api/kefu/chat`; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Accept': 'text/event-stream', + 'Accept': 'text/event-stream', }, body: JSON.stringify({ - uniacid: store.state.uniacid || '1', + uniacid: store.state.uniacid || '1', inputs: {}, query: message, response_mode: 'streaming', user_id: store.state.memberInfo?.id || 'anonymous' }) }) - const reader = response.body.getReader() const decoder = new TextDecoder() let content = '' - let buffer = '' - - // 处理流式数据 - function processStreamData(buffer, callback) { - const lines = buffer.split('\n'); - buffer = lines.pop() || ''; // 最后一行可能不完整 - - lines.forEach(line => { - line = line.trim(); - if (!line) return; - - // 解析 SSE 格式 - if (line.startsWith('data:')) { - const dataPart = line.slice(5).trim(); - if (dataPart) { - try { - const data = JSON.parse(dataPart); - - if (data.event === 'message') { - callback(data.answer || ''); - } - - if (data.conversation_id) { - conversationId = data.conversation_id; - } - - if (data.event === 'message_end') { - // 对话完成 - console.log('对话完成'); - } - } catch (error) { - console.error('解析流式数据失败:', error); - } - } - } - }); - - return buffer; - } + let buffer = '' + // 修复:声明conversationId变量 + let conversationId = ''; + + function processStreamData(buffer, callback) { + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + lines.forEach(line => { + line = line.trim(); + if (!line) return; + if (line.startsWith('data:')) { + const dataPart = line.slice(5).trim(); + if (dataPart) { + try { + const data = JSON.parse(dataPart); + if (data.event === 'message') { + callback(data.answer || ''); + } + if (data.conversation_id) { + conversationId = data.conversation_id; + } + if (data.event === 'message_end') { + console.log('对话完成'); + } + } catch (error) { + console.error('解析流式数据失败:', error); + } + } + } + }); + return buffer; + } while (true) { const { done, value } = await reader.read() if (done) break - - buffer += decoder.decode(value, { stream: true }); - - // 处理接收到的数据 - buffer = processStreamData(buffer, (newData) => { - if (newData) { - // 更新 AI 消息 - content += newData; - if (onChunk) onChunk(newData); - } - }); + buffer += decoder.decode(value, { stream: true }); + buffer = processStreamData(buffer, (newData) => { + if (newData) { + content += newData; + if (onChunk) onChunk(newData); + } + }); } - if (onComplete) onComplete(content) return content - } catch (error) { console.error('HTTP流式请求失败:', error) throw error } // #endif - - // 非H5环境使用普通请求模拟流式效果 // #ifndef H5 const response = await http.sendRequest({ ...params, async: false }) - - // 模拟流式效果 if (response.success && response.data) { const content = response.data const chunkSize = 3 let index = 0 - const streamInterval = setInterval(() => { if (index < content.length) { const chunk = content.substring(index, index + chunkSize) @@ -434,26 +346,22 @@ export default { * 生成会话ID */ async generateConversationId() { - // 构建Dify API请求参数 - const params = { - url: '/api/kefu/createConversation', // 后端代理接口 - data: { - uniacid: store.state.uniacid, - user_id: store.state.memberInfo?.id || 'anonymous', - member_id: store.state.memberInfo?.id || '', - }, - header: { - 'Content-Type': 'application/json' - } + const params = { + url: '/api/kefu/createConversation', + data: { + uniacid: store.state.uniacid, + user_id: store.state.memberInfo?.id || 'anonymous', + member_id: store.state.memberInfo?.id || '', + }, + header: { + 'Content-Type': 'application/json' } - - // 发送请求 - const response = await http.sendRequest({ - ...params, - async: false // 使用Promise方式 - }) - - return this.handleResponse(response, options) + } + const response = await http.sendRequest({ + ...params, + async: false + }) + return this.handleResponse(response) }, /** @@ -461,20 +369,16 @@ export default { */ async getServiceStatus() { try { - // 简单的健康检查 const response = await http.sendRequest({ url: '/api/kefu/health', async: false, data: {} }) - const available = [response?.data?.status, response?.data?.components?.ai_service_config?.status].filter(item => item === 'healthy').length > 0; - return { available, reason: available ? '服务正常' : '服务异常' } - } catch (error) { return { available: false, @@ -493,12 +397,51 @@ export default { data: { conversation_id: conversationId }, async: false }) - return response.success - } catch (error) { console.error('清除会话失败:', error) return false } + }, + /** + * 获取会话历史 + */ + async getConversationHistory(params = {}) { + try { + if (!params.conversation_id) { + throw new Error('会话ID(conversation_id)是必填参数'); + } + const requestData = { + uniacid: params.uniacid || store.state.uniacid || '1', + conversation_id: params.conversation_id, + user_id: params.user_id || store.state.memberInfo?.id || 'anonymous', + limit: params.limit || 20, + offset: params.offset || 0, + member_id: params.member_id || store.state.memberInfo?.id || '', + token: params.token || '' + }; + const response = await http.sendRequest({ + url: '/api/kefu/getHistory', + data: requestData, + header: { + 'Content-Type': 'application/json' + }, + async: false + }); + if (response.code === 0 || response.success) { + return { + success: true, + data: response.data, + messages: response.data?.messages || [], + total: response.data?.total || 0, + page_info: response.data?.page_info || { limit: 20, offset: 0 } + }; + } else { + throw new Error(response.message || '获取会话历史失败'); + } + } catch (error) { + console.error('获取会话历史失败:', error); + throw new Error(error.message || '获取会话历史时发生错误,请稍后重试'); + } } } \ No newline at end of file