diff --git a/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java b/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java index a34999c7..74c85647 100644 --- a/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java +++ b/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java @@ -72,15 +72,12 @@ public class SseController { // 使用AiService处理消息,传递模型参数 String response = aiService.processMessage(message, model); log.info("==========AI回复:{}",response); - // 确保SSE消息格式正确,处理多行消息 - String[] lines = response.split("\n"); - StringBuilder formattedResponse = new StringBuilder(); - for (String line : lines) { - formattedResponse.append("data:").append(line).append("\n"); - } - formattedResponse.append("\n"); // 结束消息 - - emitter.send(formattedResponse.toString()); + // 将响应消息包装成JSON格式 + String jsonResponse = String.format("{\"text\": \"%s\"}", response.replace("\"", "\\\"").replace("\n", "\\n")); + // 发送JSON格式的消息 + emitter.send(SseEmitter.event() + .name("message") + .data(jsonResponse)); log.info("发送给前端的消息: {}", response); return "消息发送成功"; diff --git a/vue/src/views/index.vue b/vue/src/views/index.vue index 8c9a62e0..416e1981 100644 --- a/vue/src/views/index.vue +++ b/vue/src/views/index.vue @@ -140,6 +140,8 @@ export default { return { md: new MarkdownIt(), inputMessage: '', + messageBuffer: '', + messageTimeout: null, messages: [ { content: '您好!我是您的工作助手,有什么可以帮助您的吗?', @@ -205,27 +207,104 @@ export default { // 监听消息 this.sse.addEventListener('message', (event) => { console.log('收到SSE消息:', event.data); - // 移除正在思考的消息 - if (this.isLoading) { - this.messages = this.messages.filter(msg => !msg.isLoading); - this.isLoading = false; + + // 清除之前的超时定时器 + if (this.messageTimeout) { + clearTimeout(this.messageTimeout); } - // 添加实际回复消息 - // 确保消息内容是完整的,处理多行消息 - let messageContent = event.data; - // 移除可能的data:前缀 - messageContent = messageContent.replace(/^data:/g, ''); - // 使用markdown-it将markdown格式转换为HTML - const htmlContent = this.md.render(messageContent); - this.messages.push({ - content: htmlContent, - time: this.formatTime(new Date()), - isMe: false, - avatar: '' - }); - this.scrollToBottom(); + + // 将当前消息添加到缓冲区 + this.messageBuffer += event.data + '\n'; + + // 设置超时定时器,如果在1秒内没有收到新的消息,就认为消息已经结束 + this.messageTimeout = setTimeout(() => { + this.processMessageBuffer(); + }, 1000); }); + // 处理消息缓冲区 + this.processMessageBuffer = function() { + // 清除超时定时器 + if (this.messageTimeout) { + clearTimeout(this.messageTimeout); + this.messageTimeout = null; + } + + // 移除所有data:前缀 + let messageContent = this.messageBuffer.replace(/^data:/gm, ''); + // 移除末尾的空行 + messageContent = messageContent.trim(); + + // 只有当消息内容不为空时才处理 + if (messageContent) { + try { + // 解析JSON数据 + const jsonData = JSON.parse(messageContent); + let textContent = jsonData.text || messageContent; + + // 检查是否包含订单信息,转换为表格格式 + if (textContent.includes('订单号:') || textContent.includes('订单详情')) { + // 提取订单信息并转换为表格 + textContent = this.convertOrderToTable(textContent); + } + + // 移除正在思考的消息 + if (this.isLoading) { + this.messages = this.messages.filter(msg => !msg.isLoading); + this.isLoading = false; + } + + // 使用markdown-it将markdown格式转换为HTML + const htmlContent = this.md.render(textContent); + this.messages.push({ + content: htmlContent, + time: this.formatTime(new Date()), + isMe: false, + avatar: '' + }); + this.scrollToBottom(); + } catch (e) { + console.error('解析SSE消息失败:', e); + // 移除正在思考的消息 + if (this.isLoading) { + this.messages = this.messages.filter(msg => !msg.isLoading); + this.isLoading = false; + } + + // 使用markdown-it将markdown格式转换为HTML + const htmlContent = this.md.render(messageContent); + this.messages.push({ + content: htmlContent, + time: this.formatTime(new Date()), + isMe: false, + avatar: '' + }); + this.scrollToBottom(); + } + } + + // 清空缓冲区 + this.messageBuffer = ''; + }; + + // 将订单信息转换为markdown表格 + this.convertOrderToTable = function(text) { + // 提取订单信息 + const orderMatches = text.match(/订单号:\s*(\S+)/); + const dateMatches = text.match(/日期:\s*(\S+)/); + const customerMatches = text.match(/客户:\s*(\S+)/); + const amountMatches = text.match(/金额:\s*(\S+)/); + const statusMatches = text.match(/状态:\s*(\S+)/); + + if (orderMatches && dateMatches && customerMatches && amountMatches && statusMatches) { + // 构建markdown表格 + return `| 订单号 | 日期 | 客户 | 金额 | 状态 | +| --- | --- | --- | --- | --- | +| ${orderMatches[1]} | ${dateMatches[1]} | ${customerMatches[1]} | ${amountMatches[1]} | ${statusMatches[1]} |`; + } + return text; + }; + // 监听心跳 this.sse.addEventListener('heartbeat', (event) => { console.log('收到心跳:', event.data);