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 { // 获取AI配置 const aiConfig = store.getters.globalAIAgentConfig // 构建Dify API请求参数 const params = { url: '/api/ai/chat', // 后端代理接口 data: { message: message, conversation_id: options.conversationId || this.generateConversationId(), user_id: store.state.memberInfo?.id || 'anonymous', stream: options.stream || false, // 是否流式响应 // Dify API参数 inputs: {}, query: message, response_mode: options.stream ? 'streaming' : 'blocking', user: store.state.memberInfo?.id || 'anonymous' }, header: { 'Content-Type': 'application/json' } } // 如果有Dify配置,添加API密钥 if (aiConfig?.difyApiKey) { params.header['Authorization'] = `Bearer ${aiConfig.difyApiKey}` } if (aiConfig?.difyBaseUrl) { params.header['X-Dify-Url'] = aiConfig.difyBaseUrl } // 发送请求 const response = await http.sendRequest({ ...params, async: false // 使用Promise方式 }) return this.handleResponse(response, options) } catch (error) { console.error('Dify API请求失败:', error) throw new Error('AI服务暂时不可用,请稍后重试') } }, /** * 流式消息处理 * @param {string} message 用户消息 * @param {Function} onChunk 流式数据回调 * @param {Function} onComplete 完成回调 */ async sendStreamMessage(message, onChunk, onComplete) { try { const aiConfig = store.getters.globalAIAgentConfig // 检查配置 if (!aiConfig?.difyBaseUrl || !aiConfig?.difyApiKey) { throw new Error('未配置Dify服务') } // 创建WebSocket连接或使用Server-Sent Events if (aiConfig?.difyWsUrl) { return this.connectWebSocket(message, onChunk, onComplete) } else { // 使用HTTP流式请求 return this.sendHttpStream(message, onChunk, onComplete) } } catch (error) { console.error('流式消息发送失败:', error) throw error } }, /** * WebSocket连接 */ connectWebSocket(message, onChunk, onComplete) { return new Promise((resolve, reject) => { const aiConfig = store.getters.globalAIAgentConfig const wsUrl = aiConfig.difyWsUrl if (!wsUrl) { reject(new Error('未配置WebSocket地址')) return } // #ifdef H5 const ws = new WebSocket(wsUrl) ws.onopen = () => { // 发送消息 ws.send(JSON.stringify({ message: message, user_id: store.state.memberInfo?.id || 'anonymous', conversation_id: this.generateConversationId() })) } ws.onmessage = (event) => { try { const data = JSON.parse(event.data) if (data.type === 'chunk' && onChunk) { onChunk(data.content) } else if (data.type === 'complete' && onComplete) { onComplete(data.content) ws.close() resolve(data.content) } } catch (e) { console.error('WebSocket消息解析失败:', e) } } ws.onerror = (error) => { console.error('WebSocket连接错误:', error) reject(error) } ws.onclose = () => { console.log('WebSocket连接关闭') } // #endif // #ifdef MP-WEIXIN || APP-PLUS // 小程序和APP使用uni.connectSocket uni.connectSocket({ url: wsUrl, success: () => { uni.onSocketOpen(() => { uni.sendSocketMessage({ data: JSON.stringify({ message: message, user_id: store.state.memberInfo?.id || 'anonymous', conversation_id: this.generateConversationId() }) }) }) uni.onSocketMessage((res) => { try { const data = JSON.parse(res.data) if (data.type === 'chunk' && onChunk) { onChunk(data.content) } else if (data.type === 'complete' && onComplete) { onComplete(data.content) uni.closeSocket() resolve(data.content) } } catch (e) { console.error('WebSocket消息解析失败:', e) } }) uni.onSocketError((error) => { console.error('WebSocket连接错误:', error) reject(error) }) }, fail: (error) => { reject(error) } }) // #endif }) }, /** * HTTP流式请求 */ async sendHttpStream(message, onChunk, onComplete) { const aiConfig = store.getters.globalAIAgentConfig const params = { url: '/api/ai/chat-stream', data: { message: message, conversation_id: this.generateConversationId(), user_id: store.state.memberInfo?.id || 'anonymous', stream: true }, header: { 'Content-Type': 'application/json' } } if (aiConfig?.difyApiKey) { params.header['Authorization'] = `Bearer ${aiConfig.difyApiKey}` } // 使用fetch API进行流式请求(H5环境) // #ifdef H5 try { const response = await fetch(`${aiConfig.difyBaseUrl}/chat-messages`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${aiConfig.difyApiKey}` }, body: JSON.stringify({ inputs: {}, query: message, response_mode: 'streaming', user: store.state.memberInfo?.id || 'anonymous' }) }) const reader = response.body.getReader() const decoder = new TextDecoder() let content = '' while (true) { const { done, value } = await reader.read() if (done) break const chunk = decoder.decode(value) const lines = chunk.split('\n') for (const line of lines) { if (line.startsWith('data: ')) { try { const data = JSON.parse(line.slice(6)) if (data.event === 'text_message' && data.text) { content += data.text if (onChunk) onChunk(data.text) } } catch (e) { // 忽略解析错误 } } } } 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) 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, conversationId: response.data?.conversation_id, messageId: response.data?.message_id } } else { throw new Error(response.message || 'AI服务返回错误') } }, /** * 生成会话ID */ generateConversationId() { return 'conv_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) }, /** * 获取AI服务状态 */ async getServiceStatus() { try { const aiConfig = store.getters.globalAIAgentConfig if (!aiConfig?.difyBaseUrl || !aiConfig?.difyApiKey) { return { available: false, reason: '未配置Dify服务' } } // 简单的健康检查 const response = await http.sendRequest({ url: '/api/ai/health', async: false }) return { available: response.success, reason: response.success ? '服务正常' : '服务异常' } } catch (error) { return { available: false, reason: '服务检查失败' } } }, /** * 清除会话历史 */ async clearConversation(conversationId) { try { const response = await http.sendRequest({ url: '/api/ai/clear-conversation', data: { conversation_id: conversationId }, async: false }) return response.success } catch (error) { console.error('清除会话失败:', error) return false } } }