ruoyi(若依) 整合 Spring AI Alibaba 实际应用(2) vue3 流式显示大模型结果
第一:ruoyi vue3 流式显示大模型结果:直接看效果
屏幕录制2025-04-12 上午10.52.24
注意事项:ruoyi框架 前端vue版本从2 转到vue3了
第二:实现流式显示:需要的前端组件:
组件1:用来实现SSE和后端spring AI 通讯
npm install @microsoft/fetch-event-source --registry=https://registry.npmmirror.com
组件2:以markdown 的形式来显示 文字内容
npm install vue3-markdown-it --registry=https://registry.npmmirror.com
第三:具体实现步骤
1:最快的搞定方式
直接git 下来:(vue3Dev 分支)
前端:git clone https://gitcode.com/zqy216_2008/RuoYi-Cloud-Vue3.git
后端: git clone https://gitee.com/auto_release/RuoYi-Cloud.git
注意分支:springboot3dev
2:前端 需要安装 上面说到的两个组件:microsoft/fetch-event-source --registry,vue3-markdown-it
通过fetch-event-source --registry 和后端通讯
通过 vue3-markdown-it 流式展示结果:通过组件 MarkdownIt
3:后端:完善alibaba ai的返回结果,便于前端知道什么时候结束
4:关键点:SSE为什么选择 microsoft/fetch-event-source 因为可以 进行post 请求,以及增加header 和token 便于验证
//还是csdn 的代码展示比 微信公众号体贴
//这是点击 搜索按钮调用的方法
前端代码
function llmHandleQuery() {
llmParams.value.platform=queryParams.value.platform;
const urlFinal=import.meta.env.VITE_APP_BASE_API+'/aiOperationalTools/aiAskAnswer/askQuestion';
console.log("url:",urlFinal);
//每点一次查询 就把之前的查询结果清楚掉
llmAnswers.value="";
let aboutContainer = new AbortController(); //用于中断请求
fetchEventSource(urlFinal, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Authorization': 'Bearer '+ getToken(),
},
body: JSON.stringify(llmParams.value),
openWhenHidden: false, //页面退至后台是否保持连接
signal: aboutContainer.signal,
async onopen(response) {
if (response.ok && response.headers.get('content-type').includes(EventStreamContentType)) {
console.log('SSE 连接成功');
} else {
throw new Error(response.statusText);
}
},
onmessage(ev) {
console.log("ev:",ev);
if (ev.data === '[DONE]') {
console.log('SSE 数据接收完成');
aboutContainer.abort("回答完成");
} else {
const jsonData = ev.data;
console.log("内容:",jsonData);
llmAnswers.value+=jsonData;
}
},
onclose() {
console.log('SSE 连接关闭');
},
onerror(err) {
console.error('SSE 错误:', err);
if (err.name === 'AbortError') {
aboutContainer.abort("服务器错误");
}
}
});
}
后端代码
```java
@PostMapping("/askQuestion")
// @RequiresPermissions("aiOperationalTools:aiAskAnswer:askQuestion")
public Flux<ServerSentEvent<String>> stream(@RequestBody
QuestionVO questionVO, HttpServletRequest request
) {
String prompt=questionVO.getPrompt();
String platform=questionVO.getPlatform();
//深度思考
String Rflag= request.getParameter("Rflag");
//联网搜索
String forInternet= request.getParameter("forInternet");
System.out.println("type:"+Rflag+" "+forInternet);
if (!StringUtils.hasText(platform)) {
return Flux.just(ServerSentEvent.builder("请选择大模型平台").event("message").build())
.concatWithValues(ServerSentEvent.builder("[DONE]").build())
.onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
}
if (Objects.equals("dashscope", platform)) {
System.out.println("命中 dashscope ......");
return chatClient.prompt(prompt).stream().content()
.map(content -> ServerSentEvent.builder(content).event("message").build())
//问题回答结速标识,以便前端消息展示处理
.concatWithValues(ServerSentEvent.builder("[DONE]").build())
.onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
}
if (Objects.equals("ollama", platform)) {
System.out.println("命中 ollama ......");
return ChatClient.builder(ollamaChatModel).build().prompt(prompt).stream().content()
.map(content -> ServerSentEvent.builder(content).event("message").build())
//问题回答结速标识,以便前端消息展示处理
.concatWithValues(ServerSentEvent.builder("[DONE]").build())
.onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
}
if (Objects.equals("openai", platform)) {
System.out.println("命中 openai ......");
return ChatClient.builder(openAIChatModel).build().prompt(prompt).stream().content()
.map(content -> ServerSentEvent.builder(content).event("message").build())
//问题回答结速标识,以便前端消息展示处理
.concatWithValues(ServerSentEvent.builder("[DONE]").build())
.onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
}
return Flux.just(ServerSentEvent.builder("不存在该模型平台,请管理员新增大模型").event("message").build())
.concatWithValues(ServerSentEvent.builder("[DONE]").build())
.onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));
}
5:用到的表
ai_ask_answer 保存用户提问信息
sys_dict_type 新增大模型平台