从手机充电器到5G基站:深入浅出聊聊TVS、压敏电阻这些‘电路保镖’是怎么工作的
2026/6/1 4:51:16
毕设所有选题:
https://blog.csdn.net/2303_76227485/article/details/131104075
项目编号:260
本项目前后端分离,分为用户、管理员2种角色。
后端技术栈:
前端技术栈:
基础环境 :IDEA/eclipse, JDK17或以上, Mysql5.7及以上, Maven3.6, node14, navicat, 通义千问apikey
所有项目以及源代码本人均调试运行无问题 可支持远程调试运行
前台地址:http://localhost:8800
用户账号密码:test/123456
管理员账户密码:admin/123456
使用Navicat或者其它工具,在mysql中创建对应名称的数据库,并执行项目的sql文件
使用IDEA/Eclipse导入springboot项目,若为maven项目请选择maven,等待依赖下载完成
修改application.yml里面的数据库配置和数据库配置,还有通义千问的apikey配置,
src/main/java/org/example/springboot/SpringbootApplication.java启动后端项目
vscode或idea打开vue3项目
在编译器中打开terminal,执行npm install 依赖下载完成后执行 npm run serve,执行成功后会显示访问地址
/** * 流式聊天接口(SSE) */@PostMapping(value="/stream",produces=MediaType.TEXT_EVENT_STREAM_VALUE)publicFlux<String>chatStream(@Valid@RequestBodyChatRequestrequest,HttpServletResponseresponse){response.setHeader("X-Accel-Buffering","no");// 禁用Nginx缓冲response.setHeader("Cache-Control","no-cache, no-store");// 禁用浏览器缓冲response.setHeader("Pragma","no-cache");response.setCharacterEncoding("UTF-8");LonguserId=JwtTokenUtils.getCurrentUserId();// 验证权限if(!sessionService.isSessionOwnedByUser(request.getSessionId(),userId)){returnFlux.just("data: 无权访问此会话\n\n");}log.info("开始流式对话,sessionId: {}, userId: {}",request.getSessionId(),userId);returntongyiQianwenService.chatStream(request.getSessionId(),request.getUserMessage()).map(chatResponse->"data: "+JSON.toJSONString(chatResponse)+"\n\n");}/** * 流式聊天(SSE实时返回) */publicFlux<ChatResponse>chatStream(StringsessionId,Stringprompt){log.info("开始AI对话,sessionId: {}, userMessage: {}",sessionId,prompt);// 保存用户消息sessionService.saveMessage(sessionId,"user",prompt);// 1. 获取会话上下文SessionContextcontext=sessionManager.getSessionContext(sessionId);if(context==null){context=newSessionContext(sessionId);// 初始化空上下文sessionManager.saveSessionContext(context);}StringfinalSessionId=context.getSessionId();// 2. 构建请求体Map<String,Object>requestBody=buildRequestBody(context,prompt,true);// 定义原子变量累加完整响应文本AtomicReference<String>fullAnswer=newAtomicReference<>("");// 3. 流式调用APIreturnWebClient.create().post().uri(apiUrl).headers(headers->headers.addAll(buildHeaders())).bodyValue(requestBody).retrieve()// 禁用WebClient的响应缓冲(关键!).bodyToFlux(String.class)// 逐个处理通义千问返回的流式片段.doOnNext(line->log.info("通义千问原生片段:{}",line))// 打印原生片段,验证是否逐行返回// 步骤1:先过滤空行(提前剔除无效数据).filter(line->line!=null&&!line.trim().isEmpty()).scan(newAccumulator(null,""),(acc,line)->{// 空行处理:保留原有累加文本,line设为nullif(line==null||line.isEmpty()){returnnewAccumulator(null,acc.getFullAnswer());}// 解析SSE数据前缀Stringdata=line.startsWith("data: ")?line.substring(6):line;// 解析流式片段TongyiQianwenResponsefragment=JSON.parseObject(data,TongyiQianwenResponse.class);// 1. 流结束标识:返回完整响应if("stop".equals(fragment.getOutput().getFinish_reason())){returnnewAccumulator(data,acc.getFullAnswer());}// 解析失败:保留原有累加文本,line设为nullif(fragment==null||fragment.getOutput()==null){returnnewAccumulator(null,acc.getFullAnswer());}// 正常片段:累加文本StringnewFullAnswer=acc.getFullAnswer()+fragment.getOutput().getText();// 返回新的累加器(当前行 + 最新累加文本)returnnewAccumulator(line,newFullAnswer);})// 步骤3:过滤累加器中line为null的无效数据(替代map里返回null).filter(acc->acc.getLine()!=null).map(acc->{Stringline=acc.getLine();StringcurrentFullAnswer=acc.getFullAnswer();ChatResponseresponse=newChatResponse();// 空行/解析失败的累加器直接返回null(后续filter过滤)if(line==null){returnnull;}// 解析SSE数据前缀Stringdata=line.startsWith("data: ")?line.substring(6):line;// 2. 解析流式片段TongyiQianwenResponsefragment=JSON.parseObject(data,TongyiQianwenResponse.class);// 1. 流结束标识:返回完整响应if("stop".equals(fragment.getOutput().getFinish_reason())){response.setSuccess(true);response.setSessionId(finalSessionId);response.setDone(true);response.setAnswer(currentFullAnswer);// 完整回答// 保存完整回答到会话sessionService.saveMessage(finalSessionId,"assistant",currentFullAnswer);log.info("AI对话完成,sessionId: {}, 响应长度: {}",sessionId,currentFullAnswer.length());returnresponse;}if(fragment==null||fragment.getOutput()==null){returnnull;}// 3. 异常处理(非stop结束)if(fragment.getOutput().getFinish_reason()!=null&&!"null".equals(fragment.getOutput().getFinish_reason())&&!"stop".equals(fragment.getOutput().getFinish_reason())){response.setSuccess(false);response.setMessage(fragment.getOutput().getFinish_reason());response.setRequestId(fragment.getRequest_id());returnresponse;}// 4. 正常片段:返回单条流式响应response.setSuccess(true);response.setSessionId(finalSessionId);response.setAnswer(fragment.getOutput().getText());// 单片段文本response.setDone(false);response.setRequestId(fragment.getRequest_id());// 设置token用量response.setUsage(Map.of("inputTokens",fragment.getUsage().getInput_tokens(),"outputTokens",fragment.getUsage().getOutput_tokens(),"totalTokens",fragment.getUsage().getTotal_tokens()));returnresponse;})// 过滤掉null响应(空行/解析失败的情况).filter(res->res!=null);}