import Config from './config.js' import http from './http.js' import store from '@/store/index.js' export default { /** * 发送消息到Dify API * @param {string} message 用户消息内容 * @param {Object} options 配置选项 * @returns {Promise} */ async sendMessage(message, options = {}) { try { const aiConfig = store.getters.globalAIKefuConfig const params = { url: '/api/kefu/chat', data: { user_id: store.state.memberInfo?.id || 'anonymous', stream: false, inputs: {}, query: message, response_mode: options.stream ? 'streaming' : 'blocking', user: store.state.memberInfo?.id || 'anonymous' }, header: { 'Content-Type': 'application/json' } } console.log('Sending request to:', params.url); console.log('Request data:', params.data); const response = await http.sendRequest({ ...params, async: false }) return this.handleResponse(response, options) } catch (error) { console.error('Dify API请求失败:', error) throw new Error('AI服务暂时不可用,请稍后重试') } }, /** * 流式消息处理 */ async sendStreamMessage(message, onChunk, onComplete) { try { return this.sendHttpStream(message, onChunk, onComplete) } catch (error) { console.error('流式消息发送失败:', error) throw error } }, /** * 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', query: message, 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') { 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; } } catch (error) { console.error('解析消息失败:', error); } }); eventSource.addEventListener('done', (event) => { try { const data = JSON.parse(event.data); if (onComplete) onComplete(data); } catch (error) { console.error('解析完成事件失败:', error); } }); eventSource.addEventListener('close', (event) => { try { const data = JSON.parse(event.data); console.log('连接正常结束:', data); } catch (error) { console.error('解析关闭事件失败:', error); } 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 }); return null; } }, /** * Fetch API 流式聊天 */ async chatWithFetchStream(message, conversationId = '', onMessage, onComplete, onError) { try { let requestData; let headers = { 'Accept': 'text/event-stream' }; requestData = { uniacid: store.state.uniacid || '1', user_id: store.state.memberInfo?.id || '123456', query: message, conversation_id: conversationId || '', stream: true, inputs: {}, 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; if (onMessage) onMessage(textContent); } else if (data.event === 'message_end' || data.event === 'done') { if (onComplete) onComplete({ conversation_id: data.conversation_id, message: aiMessage, ...data }); } else if (data.event === 'error' && onError) { onError(data); } if (data.conversation_id) { conversationId = data.conversation_id; } } catch (e) { console.warn('解析流式数据失败:', e); } } } } if (buffer.startsWith('data: ')) { try { const data = JSON.parse(buffer.substring(6)); if ((data.event === 'message' || data.event === 'text_message') && onMessage) { const textContent = data.answer || data.text || ''; onMessage(textContent); } else if ((data.event === 'message_end' || data.event === 'done') && onComplete) { onComplete({ conversation_id: data.conversation_id, message: aiMessage, ...data }); } } catch (e) { console.warn('解析剩余数据失败:', e); } } } catch (error) { console.error('Fetch流式聊天请求失败:', error); if (onError) onError({ error: error.message }); } }, /** * HTTP流式请求(修复conversationId未定义) */ async sendHttpStream(message, onChunk, onComplete) { const params = { url: '/api/kefu/chat', data: { query: message, conversation_id: '', user_id: store.state.memberInfo?.id || 'anonymous', stream: true, uniacid: store.state.uniacid, inputs: {}, response_mode: 'streaming', user: store.state.memberInfo?.id || 'anonymous' }, header: { 'Content-Type': 'application/json' } } // #ifdef H5 try { const url = Config.baseUrl + `/api/kefu/chat`; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream', }, body: JSON.stringify({ 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 = '' // 修复:声明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) { content += newData; if (onChunk) onChunk(newData); } }); } if (onComplete) onComplete(content) return content } catch (error) { console.error('HTTP流式请求失败:', error) throw error } // #endif // #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) index += chunkSize if (onChunk) onChunk(chunk) } else { clearInterval(streamInterval) if (onComplete) onComplete(content) } }, 100) } // #endif }, /** * 处理API响应 */ handleResponse(response, options) { if (response.code === 0 || response.success) { return { success: true, content: response.data?.answer || response.data?.content || response.data?.reply|| response.data, conversationId: response.data?.conversation_id, messageId: response.data?.message_id } } else { throw new Error(response.message || 'AI服务返回错误') } }, /** * 生成会话ID */ async generateConversationId() { 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 }) return this.handleResponse(response) }, /** * 获取AI服务状态 */ 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, reason: '服务检查失败' } } }, /** * 清除会话历史 */ async clearConversation(conversationId) { try { const response = await http.sendRequest({ url: '/api/kefu/clear-conversation', 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 || '获取会话历史时发生错误,请稍后重试'); } } }