From f0d3d1986f8cc28ecdec0381d79742902ea08d97 Mon Sep 17 00:00:00 2001 From: jinhhanhan <1683105490@qq.com> Date: Thu, 11 Dec 2025 09:42:28 +0800 Subject: [PATCH] =?UTF-8?q?chore=EF=BC=9A=E6=8A=8A=20message=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E6=88=90=20query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/js/ai-service.js | 597 +++++++++++++++++----------------------- 1 file changed, 249 insertions(+), 348 deletions(-) diff --git a/common/js/ai-service.js b/common/js/ai-service.js index 593b992..32fc199 100644 --- a/common/js/ai-service.js +++ b/common/js/ai-service.js @@ -1,349 +1,250 @@ -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.globalAIKefuConfig - - // const new_conversationId = await this.generateConversationId() - - // 构建Dify API请求参数 - const params = { - url: '/api/kefu/chat', // 后端代理接口 - data: { - message: message, - // conversation_id: options.conversationId ?? new_conversationId, - 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' - } - } - - // 发送请求 - 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 { - // 使用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.globalAIKefuConfig - 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.globalAIKefuConfig - - const params = { - url: '/api/kefu/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(`/api/kefu/chat-messages`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - 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?.reply|| response.data, - conversationId: response.data?.conversation_id, - messageId: response.data?.message_id - } - } else { - throw new Error(response.message || 'AI服务返回错误') - } - }, - - /** - * 生成会话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 response = await http.sendRequest({ - ...params, - async: false // 使用Promise方式 - }) - - return this.handleResponse(response, options) - }, - - /** - * 获取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 - } - } +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.globalAIKefuConfig + + // const new_conversationId = await this.generateConversationId() + + // 构建Dify API请求参数 + const params = { + 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', + user: store.state.memberInfo?.id || 'anonymous' + }, + header: { + 'Content-Type': 'application/json' + } + } + + // 发送请求 + 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 { + // 使用HTTP流式请求 + return this.sendHttpStream(message, onChunk, onComplete) + + } catch (error) { + console.error('流式消息发送失败:', error) + throw error + } + }, + + /** + * HTTP流式请求 + */ + async sendHttpStream(message, onChunk, onComplete) { + const params = { + url: '/api/kefu/chat', + data: { + query: message, + conversation_id: this.generateConversationId(), + user_id: store.state.memberInfo?.id || 'anonymous', + stream: true, + uniacid: store.state.uniacid, // 保留必填参数 + }, + header: { + 'Content-Type': 'application/json' + } + } + + // 使用fetch API进行流式请求(H5环境) + // #ifdef H5 + try { + const response = await fetch(`/api/kefu/chat`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + 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?.reply|| response.data, + conversationId: response.data?.conversation_id, + messageId: response.data?.message_id + } + } else { + throw new Error(response.message || 'AI服务返回错误') + } + }, + + /** + * 生成会话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 response = await http.sendRequest({ + ...params, + async: false // 使用Promise方式 + }) + + return this.handleResponse(response, options) + }, + + /** + * 获取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 + } + } } \ No newline at end of file