#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 保存的目录
*/
public final File saveCode(T result, Long appId) {
// 1.验证输入
validateInput(result);
// 2.构建基于appId的目录
String baseDirPath = buildUniqueDir(appId);
// 3.保存文件(具体实现由子类提供)
saveFiles(result, baseDirPath);
// 4.返回目录文件对象
return new File(baseDirPath);
}
/**
* 构建基于appId的目录路径
* @param appId 应用ID
* @return 目录路径
*/
protected final String buildUniqueDir(Long appId) {
if (appId == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用ID不能为空");
}
String codeType = getCodeType().getValue();
String uniqueDirName = StrUtil.format("{}_{}", codeType, appId);
String dirPath = FILE_SAVE_ROOT_DIR + File.separator + uniqueDirName;
FileUtil.mkdir(dirPath);
return dirPath;
}
(2)代码保存执行器改造(CodeFileSaverExecutor)
修改执行方法,补充appId参数,适配新的保存逻辑:
/**
* 执行代码保存(使用appId)
* @param codeResult 代码结果对象
* @param codeGenType 代码生成类型
* @param appId 应用ID
* @return 保存的目录
*/
public static File executeSaver(Object codeResult, CodeGenTypeEnum codeGenType, Long appId) {
return switch (codeGenType) {
case HTML -> htmlCodeFileSaver.saveCode((HtmlCodeResult) codeResult, appId);
case MULTI_FILE -> multiFileCodeFileSaver.saveCode((MultiFileCodeResult) codeResult, appId);
default -> throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不支持的代码生成类型:" + codeGenType);
};
}
(3)AI代码生成门面改造(AiCodeGeneratorFacade)
所有生成方法新增appId参数,同步适配流式生成:
/**
* 统一入口:根据类型生成并保存代码(流式, 使用appId)
* @param userMessage 用户提示词
* @param codeGenTypeEnum 生成类型
* @param appId 应用ID
* @return 流式响应
*/
public Flux<String> generateAndSaveCodeStream(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
if (codeGenTypeEnum == null) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成类型为空");
}
return switch (codeGenTypeEnum) {
case HTML -> {
Flux<String> codeStream = aiCodeGeneratorService.generateHtmlCodeStream(userMessage);
yield processCodeStream(codeStream, CodeGenTypeEnum.HTML, appId);
}
case MULTI_FILE -> {
Flux<String> codeStream = aiCodeGeneratorService.generateMultiFileCodeStream(userMessage);
yield processCodeStream(codeStream, CodeGenTypeEnum.MULTI_FILE, appId);
}
default -> {
String errorMessage = "不支持的生成类型:" + codeGenTypeEnum.getValue();
throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
}
};
}
(4)应用服务生成方法(AppService#chatToGenCode)
实现权限校验、应用查询、AI生成调用的核心逻辑:
@Override
public Flux<String> chatToGenCode(Long appId, String message, User loginUser) {
// 1.参数校验
ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID不能为空");
ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "用户消息不能为空");
// 2.查询应用信息
App app = this.getById(appId);
ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
// 3.验证权限:仅本人可生成代码
if (!app.getUserId().equals(loginUser.getId())) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限访问该应用");
}
// 4.获取代码生成类型
String codeGenTypeStr = app.getCodeGenType();
CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenTypeStr);
if (codeGenTypeEnum == null) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不支持的代码生成类型");
}
// 5.调用AI生成代码
return aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
}
三、SSE流式接口开发
1. 接口设计
采用SSE(Server-Sent Events) 实现流式响应,使用GET请求便于前端EventSource对接,声明响应类型为text/event-stream。
2. 核心接口代码(AppController)
/**
* 应用聊天生成代码(流式SSE)
* @param appId 应用ID
* @param message 用户消息
* @param request 请求对象
* @return 生成结果流
*/
@GetMapping(value = "/chat/gen/code", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatToGenCode(@RequestParam Long appId,
@RequestParam String message,
HttpServletRequest request) {
// 参数校验
ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID无效");
ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "用户消息不能为空");
// 获取当前登录用户
User loginUser = userService.getLoginUser(request);
// 调用服务生成代码(流式)
return appService.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", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> chatToGenCode(@RequestParam Long appId,
@RequestParam String message,
HttpServletRequest request) {
// 参数校验
ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID无效");
ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "用户消息不能为空");
// 获取当前登录用户
User loginUser = userService.getLoginUser(request);
// 调用服务生成代码
Flux<String> contentFlux = appService.chatToGenCode(appId, message, loginUser);
// 封装数据+添加结束事件
return contentFlux
.map(chunk -> {
// 封装为JSON,保留空格格式
Map<String, String> wrapper = Map.of("d", chunk);
String jsonData = JSONUtil.toJsonStr(wrapper);
return ServerSentEvent.<String>builder()
.data(jsonData)
.build();
})
.concatWith(Mono.just(
// 发送done事件标识生成完成
ServerSentEvent.<String>builder()
.event("done")
.data("")
.build()
));
}
五、接口测试
1. 测试步骤
- 用户登录获取会话
- 调用SSE流式生成接口
- 查看实时输出结果
2. 测试命令(CURL)
# 1.用户登录,保存cookie
curl -X POST "http://localhost:8123/api/user/login" \
-H "Content-Type: application/json" \
-d '{"userAccount":"yupi","userPassword":"12345678"}' \
-c cookies.txt
# 2.调用SSE流式生成接口
curl -G "http://localhost:8123/api/app/chat/gen/code" \
--data-urlencode "appId=303320512563961856" \
--data-urlencode "message=我需要一个简单的任务记录工具网站" \
-H "Accept: text/event-stream" \
-H "Cache-Control: no-cache" \
-b cookies.txt \
--no-buffer
3. 测试效果
优化后接口可正常保留空格/换行,生成完成后触发done事件,前端可精准判断生成状态。
六、模块核心要点总结
- 应用绑定:通过appId关联代码文件,解决平台化存储混乱问题
- 流式响应:SSE技术实现代码实时输出,提升用户交互体验
- 双层优化:JSON封装解决空格丢失,done事件明确生成结束状态
- 权限管控:仅应用创建者可触发生成,保障平台数据安全
- 规范统一:文件目录、接口返回、异常处理全链路标准化
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)