#2026山东大学软件学院项目实训(四)——AI应用生成模块完整实现
文章标签#Java #SpringBoot #SSE #AI编程 #LangChain4j一、模块整体设计思路AI应用生成模块是AI零代码应用生成平台的核心功能负责将单机版AI代码生成能力与应用管理系统深度集成实现用户-应用-代码的绑定关联。本模块遵循核心设计原则应用绑定代码生成与应用ID强关联文件按应用ID规范存储流式响应采用SSE服务器推送技术实现代码实时流式输出权限校验仅应用创建者可触发代码生成保障数据安全文件规范统一文件目录命名规则避免存储混乱异常统一全局捕获异常返回友好错误提示二、AI应用生成服务开发1. 业务流程设计平台化代码生成核心流程用户在主页输入提示词创建应用生成应用记录入库获取应用ID后跳转至AI对话页面前端调用SSE流式生成接口后端校验权限并触发AI生成代码按codeGenType_appId规则保存至文件系统流式返回生成内容至前端实时展示生成效果2. 核心代码改造1代码保存模板改造CodeFileSaverTemplate重构代码保存逻辑新增应用ID参数实现文件与应用绑定/** * 模板方法保存代码的标准流程(使用appId) * param result 代码结果对象 * param appId 应用ID * return 保存的目录 */publicfinalFilesaveCode(Tresult,LongappId){// 1.验证输入validateInput(result);// 2.构建基于appId的目录StringbaseDirPathbuildUniqueDir(appId);// 3.保存文件(具体实现由子类提供)saveFiles(result,baseDirPath);// 4.返回目录文件对象returnnewFile(baseDirPath);}/** * 构建基于appId的目录路径 * param appId 应用ID * return 目录路径 */protectedfinalStringbuildUniqueDir(LongappId){if(appIdnull){thrownewBusinessException(ErrorCode.PARAMS_ERROR,应用ID不能为空);}StringcodeTypegetCodeType().getValue();StringuniqueDirNameStrUtil.format({}_{},codeType,appId);StringdirPathFILE_SAVE_ROOT_DIRFile.separatoruniqueDirName;FileUtil.mkdir(dirPath);returndirPath;}2代码保存执行器改造CodeFileSaverExecutor修改执行方法补充appId参数适配新的保存逻辑/** * 执行代码保存(使用appId) * param codeResult 代码结果对象 * param codeGenType 代码生成类型 * param appId 应用ID * return 保存的目录 */publicstaticFileexecuteSaver(ObjectcodeResult,CodeGenTypeEnumcodeGenType,LongappId){returnswitch(codeGenType){caseHTML-htmlCodeFileSaver.saveCode((HtmlCodeResult)codeResult,appId);caseMULTI_FILE-multiFileCodeFileSaver.saveCode((MultiFileCodeResult)codeResult,appId);default-thrownewBusinessException(ErrorCode.SYSTEM_ERROR,不支持的代码生成类型:codeGenType);};}3AI代码生成门面改造AiCodeGeneratorFacade所有生成方法新增appId参数同步适配流式生成/** * 统一入口根据类型生成并保存代码(流式, 使用appId) * param userMessage 用户提示词 * param codeGenTypeEnum 生成类型 * param appId 应用ID * return 流式响应 */publicFluxStringgenerateAndSaveCodeStream(StringuserMessage,CodeGenTypeEnumcodeGenTypeEnum,LongappId){if(codeGenTypeEnumnull){thrownewBusinessException(ErrorCode.SYSTEM_ERROR,生成类型为空);}returnswitch(codeGenTypeEnum){caseHTML-{FluxStringcodeStreamaiCodeGeneratorService.generateHtmlCodeStream(userMessage);yieldprocessCodeStream(codeStream,CodeGenTypeEnum.HTML,appId);}caseMULTI_FILE-{FluxStringcodeStreamaiCodeGeneratorService.generateMultiFileCodeStream(userMessage);yieldprocessCodeStream(codeStream,CodeGenTypeEnum.MULTI_FILE,appId);}default-{StringerrorMessage不支持的生成类型:codeGenTypeEnum.getValue();thrownewBusinessException(ErrorCode.SYSTEM_ERROR,errorMessage);}};}4应用服务生成方法AppService#chatToGenCode实现权限校验、应用查询、AI生成调用的核心逻辑OverridepublicFluxStringchatToGenCode(LongappId,Stringmessage,UserloginUser){// 1.参数校验ThrowUtils.throwIf(appIdnull||appId0,ErrorCode.PARAMS_ERROR,应用ID不能为空);ThrowUtils.throwIf(StrUtil.isBlank(message),ErrorCode.PARAMS_ERROR,用户消息不能为空);// 2.查询应用信息Appappthis.getById(appId);ThrowUtils.throwIf(appnull,ErrorCode.NOT_FOUND_ERROR,应用不存在);// 3.验证权限仅本人可生成代码if(!app.getUserId().equals(loginUser.getId())){thrownewBusinessException(ErrorCode.NO_AUTH_ERROR,无权限访问该应用);}// 4.获取代码生成类型StringcodeGenTypeStrapp.getCodeGenType();CodeGenTypeEnumcodeGenTypeEnumCodeGenTypeEnum.getEnumByValue(codeGenTypeStr);if(codeGenTypeEnumnull){thrownewBusinessException(ErrorCode.SYSTEM_ERROR,不支持的代码生成类型);}// 5.调用AI生成代码returnaiCodeGeneratorFacade.generateAndSaveCodeStream(message,codeGenTypeEnum,appId);}三、SSE流式接口开发1. 接口设计采用SSEServer-Sent Events实现流式响应使用GET请求便于前端EventSource对接声明响应类型为text/event-stream。2. 核心接口代码AppController/** * 应用聊天生成代码(流式SSE) * param appId 应用ID * param message 用户消息 * param request 请求对象 * return 生成结果流 */GetMapping(value/chat/gen/code,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicFluxStringchatToGenCode(RequestParamLongappId,RequestParamStringmessage,HttpServletRequestrequest){// 参数校验ThrowUtils.throwIf(appIdnull||appId0,ErrorCode.PARAMS_ERROR,应用ID无效);ThrowUtils.throwIf(StrUtil.isBlank(message),ErrorCode.PARAMS_ERROR,用户消息不能为空);// 获取当前登录用户UserloginUseruserService.getLoginUser(request);// 调用服务生成代码(流式)returnappService.chatToGenCode(appId,message,loginUser);}四、SSE流式接口优化原生SSE接口存在空格丢失、无法区分正常结束/异常中断两大问题需针对性优化。1. 优化1解决前端空格丢失问题问题原因前端EventSource解析纯文本流式数据时空格/换行符会被异常过滤导致代码格式错乱。解决方案将流式数据封装为ServerSentEvent数据转为JSON格式传输保留原始格式。2. 优化2添加生成完成标识问题原因SSE默认通过关闭连接标识传输结束无法区分正常完成与网络异常中断。解决方案追加done事件前端通过监听事件类型判断生成状态。3. 优化后完整接口代码/** * 应用聊天生成代码(优化版SSE) * param appId 应用ID * param message 用户消息 * param request 请求对象 * return 优化后流式响应 */GetMapping(value/chat/gen/code,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicFluxServerSentEventStringchatToGenCode(RequestParamLongappId,RequestParamStringmessage,HttpServletRequestrequest){// 参数校验ThrowUtils.throwIf(appIdnull||appId0,ErrorCode.PARAMS_ERROR,应用ID无效);ThrowUtils.throwIf(StrUtil.isBlank(message),ErrorCode.PARAMS_ERROR,用户消息不能为空);// 获取当前登录用户UserloginUseruserService.getLoginUser(request);// 调用服务生成代码FluxStringcontentFluxappService.chatToGenCode(appId,message,loginUser);// 封装数据添加结束事件returncontentFlux.map(chunk-{// 封装为JSON保留空格格式MapString,StringwrapperMap.of(d,chunk);StringjsonDataJSONUtil.toJsonStr(wrapper);returnServerSentEvent.Stringbuilder().data(jsonData).build();}).concatWith(Mono.just(// 发送done事件标识生成完成ServerSentEvent.Stringbuilder().event(done).data().build()));}五、接口测试1. 测试步骤用户登录获取会话调用SSE流式生成接口查看实时输出结果2. 测试命令CURL# 1.用户登录保存cookiecurl-XPOSThttp://localhost:8123/api/user/login\-HContent-Type: application/json\-d{userAccount:yupi,userPassword:12345678}\-ccookies.txt# 2.调用SSE流式生成接口curl-Ghttp://localhost:8123/api/app/chat/gen/code\--data-urlencodeappId303320512563961856\--data-urlencodemessage我需要一个简单的任务记录工具网站\-HAccept: text/event-stream\-HCache-Control: no-cache\-bcookies.txt\--no-buffer3. 测试效果优化后接口可正常保留空格/换行生成完成后触发done事件前端可精准判断生成状态。六、模块核心要点总结应用绑定通过appId关联代码文件解决平台化存储混乱问题流式响应SSE技术实现代码实时输出提升用户交互体验双层优化JSON封装解决空格丢失done事件明确生成结束状态权限管控仅应用创建者可触发生成保障平台数据安全规范统一文件目录、接口返回、异常处理全链路标准化