核心模块梳理

该AI零代码应用生成项目的后端核心模块及功能如下:

  1. 用户模块:负责用户注册、登录、权限控制,是平台的基础身份管理单元。
  2. 应用模块:承载应用的全生命周期管理(增删改查、精选、浏览、代码下载、封面生成)。
  3. AI代码生成模块:技术核心,基于LangChain4j框架封装大模型交互,支持原生HTML、Vue工程等生成模式。
  4. 对话与记忆模块:通过持久化存储(含Redis)实现AI对话记忆,支撑应用迭代开发。
  5. 文件处理与构建模块:解析、保存AI生成的代码,支持Vue工程的自动化项目构建。
  6. 可视化编辑模块:为AI提供文件读写改删工具,支持已有代码的增量修改。
  7. AI工作流模块:基于LangGraph4j框架,将代码生成流程标准化(含素材搜集、提示词增强、质量检查等节点)。
  8. 部署模块:通过Nginx托管静态资源,为应用生成唯一访问路径,实现一键部署。
  9. 可观测性模块:集成ARMS、Prometheus+Grafana,实现系统性能与业务指标(如AI调用次数、Token消耗)的监控。

模块交互流程

用户操作与模块的核心交互逻辑为:

  1. 用户通过用户模块完成登录后,在应用模块创建新应用,触发AI智能路由选择生成模式;
  2. 用户在对话与记忆模块与AI交互,调用AI代码生成模块/AI工作流模块生成代码,生成结果由文件处理与构建模块保存;
  3. 用户可通过可视化编辑模块对代码进行增量修改;
  4. 最终通过部署模块完成应用发布,全流程由可观测性模块监控。

架构补充说明

从架构图可见,该系统采用分层设计:

  • 接入层:通过Higress网关、Tomcat应用服务器、Nginx实现多端(浏览器、小程序等)接入;
  • 业务层:各核心模块以服务化形式拆分(如AI服务、应用服务),通过Nacos实现服务注册、Dubbo RPC完成服务调用;
  • 存储层:采用MySQL(结构化数据)、Redis(缓存/对话记忆)、对象存储(文件/代码)实现数据分层存储;
  • 基础设施层:基于Docker、Linux服务器等实现资源调度与部署。

需要我帮你整理一份模块功能与交互的精简表格吗?

你现在想深入了解该AI零代码平台中应用模块、AI代码生成模块、可视化编辑模块这三个核心模块的具体实现逻辑,我会逐一拆解每个模块的实现细节,让你清晰理解其核心机制。

一、应用模块:应用全生命周期管理的实现

应用模块的核心是围绕“应用”这一核心实体,通过分层设计和标准化接口实现全生命周期管理,具体实现逻辑如下:

1. 核心数据模型(以Java为例)
// 核心应用实体类
@Data
@TableName("app_info")
public class AppInfo {
    private Long id;                 // 应用唯一标识
    private Long userId;             // 所属用户ID
    private String appName;          // 应用名称
    private String appDesc;          // 应用描述
    private String coverUrl;         // 封面图URL
    private Integer status;          // 状态:0-草稿 1-已生成 2-已部署 3-已下架
    private String deployUrl;        // 部署后的访问地址
    private LocalDateTime createTime;// 创建时间
    private LocalDateTime updateTime;// 更新时间
}
2. 核心功能实现逻辑
生命周期阶段 实现方式 核心技术/逻辑
应用创建/新增 提供RESTful接口,接收用户输入的应用名称、描述等信息,校验后写入MySQL 参数校验 + 事务控制,生成唯一应用ID
应用查询/浏览 分场景提供接口:
1. 个人应用列表(按用户ID筛选)
2. 精选应用列表(按推荐权重排序)
3. 实时浏览(Redis缓存热门应用)
分页查询 + Redis缓存 + 权限校验
应用修改/更新 支持修改名称、描述、封面等信息,仅允许创建者/管理员操作 乐观锁防并发修改,更新updateTime
应用删除 逻辑删除(修改status为3),保留数据用于溯源 逻辑删除而非物理删除,避免数据丢失
代码下载 从对象存储中读取该应用的代码包,生成临时下载链接(带时效) 预签名URL + 限流控制(防止高频下载)
封面图生成 调用AI图片生成接口(如即梦AI),传入应用名称/描述生成封面,保存URL到应用实体 异步生成(避免阻塞接口) + 失败重试
应用部署 触发部署模块,生成唯一访问路径,更新deployUrl和status 事件驱动(发布部署事件,由部署模块消费)
3. 核心接口示例
@RestController
@RequestMapping("/api/app")
public class AppController {
    @Autowired
    private AppService appService;
    
    // 创建应用
    @PostMapping("/create")
    public Result<Long> createApp(@RequestBody AppCreateDTO dto, @RequestHeader("userId") Long userId) {
        return Result.success(appService.createApp(dto, userId));
    }
    
    // 获取应用列表(分页)
    @GetMapping("/list")
    public Result<PageInfo<AppVO>> getAppList(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize,
            @RequestHeader("userId") Long userId) {
        return Result.success(appService.getAppList(pageNum, pageSize, userId));
    }
    
    // 下载应用代码
    @GetMapping("/download/{appId}")
    public void downloadAppCode(@PathVariable Long appId, 
                               @RequestHeader("userId") Long userId,
                               HttpServletResponse response) {
        appService.downloadAppCode(appId, userId, response);
    }
}

二、AI代码生成模块:基于LangChain4j封装大模型交互

LangChain4j是Java版的LangChain框架,核心是封装大模型调用、提示词工程、工具调用等能力,该模块的实现逻辑如下:

1. 整体架构

应用模块触发生成

生成参数解析

选择生成模式(HTML/Vue/多文件)

提示词模板加载

LangChain4j大模型客户端

调用大模型生成代码

代码格式化/校验

保存到文件存储

回调应用模块更新状态

2. 核心实现代码
// 1. 大模型客户端配置(基于LangChain4j)
@Configuration
public class LlmClientConfig {
    @Value("${llm.api-key}")
    private String apiKey;
    @Value("${llm.base-url}")
    private String baseUrl;
    
    @Bean
    public OpenAiChatModel openAiChatModel() {
        return OpenAiChatModel.builder()
                .apiKey(apiKey)
                .baseUrl(baseUrl)
                .modelName("gpt-4o") // 可配置为自研大模型
                .temperature(0.2)    // 低随机性,保证代码准确性
                .timeout(Duration.ofMinutes(5)) // 长超时,适配大文件生成
                .build();
    }
}

// 2. 代码生成服务核心逻辑
@Service
public class AiCodeGenerateService {
    @Autowired
    private OpenAiChatModel chatModel;
    @Autowired
    private CodeTemplateLoader templateLoader; // 提示词模板加载器
    
    // 生成代码的核心方法
    public String generateCode(Long appId, String userRequirement, Integer generateMode) {
        // 步骤1:加载对应模式的提示词模板(HTML/Vue/多文件)
        String promptTemplate = templateLoader.loadTemplate(generateMode);
        
        // 步骤2:填充用户需求到模板中
        String finalPrompt = String.format(promptTemplate, userRequirement);
        
        // 步骤3:调用LangChain4j的大模型客户端生成代码
        AiMessage aiMessage = chatModel.generate(UserMessage.of(finalPrompt));
        String rawCode = aiMessage.text();
        
        // 步骤4:代码格式化和校验(移除markdown标记、语法检查)
        String formattedCode = formatCode(rawCode, generateMode);
        
        // 步骤5:保存代码到对象存储
        saveCodeToStorage(appId, formattedCode, generateMode);
        
        return formattedCode;
    }
    
    // 代码格式化方法(示例)
    private String formatCode(String rawCode, Integer generateMode) {
        // 移除markdown的```html/```vue等标记
        if (generateMode == 1) { // HTML模式
            return rawCode.replace("```html", "").replace("```", "").trim();
        } else if (generateMode == 2) { // Vue模式
            return rawCode.replace("```vue", "").replace("```", "").trim();
        }
        return rawCode.trim();
    }
    
    // 保存代码到对象存储
    private void saveCodeToStorage(Long appId, String code, Integer generateMode) {
        // 逻辑:根据应用ID创建目录,按生成模式保存对应文件
        // 例如:Vue模式保存为完整的工程文件(package.json、App.vue、main.js等)
    }
}
3. 关键封装点
  • 提示词工程封装:将不同生成模式的提示词模板化,避免硬编码,方便迭代;
  • 大模型调用封装:统一异常处理、超时重试、流量控制,保证调用稳定性;
  • 结果处理封装:自动格式化代码、校验语法合法性,降低前端处理成本。

三、可视化编辑模块:为AI提供文件读写改删工具

该模块的核心是构建一套“文件操作工具集”,让AI能够像人一样操作代码文件,实现增量修改,而非每次全量生成,具体实现如下:

1. 核心工具接口设计
// 文件操作工具接口(供AI调用)
public interface FileEditTool {
    /**
     * 读取文件内容
     * @param appId 应用ID
     * @param filePath 文件路径(如/src/App.vue)
     * @return 文件内容
     */
    String readFile(Long appId, String filePath);
    
    /**
     * 写入文件(覆盖/新增)
     * @param appId 应用ID
     * @param filePath 文件路径
     * @param content 文件内容
     * @param overwrite 是否覆盖(true-覆盖,false-新增)
     * @return 操作结果
     */
    Boolean writeFile(Long appId, String filePath, String content, Boolean overwrite);
    
    /**
     * 修改文件(指定位置替换)
     * @param appId 应用ID
     * @param filePath 文件路径
     * @param oldContent 要替换的旧内容
     * @param newContent 新内容
     * @return 修改是否成功
     */
    Boolean editFile(Long appId, String filePath, String oldContent, String newContent);
    
    /**
     * 删除文件/目录
     * @param appId 应用ID
     * @param filePath 文件/目录路径
     * @return 删除是否成功
     */
    Boolean deleteFile(Long appId, String filePath);
    
    /**
     * 列出应用下的所有文件
     * @param appId 应用ID
     * @return 文件路径列表
     */
    List<String> listFiles(Long appId);
}
2. 工具注册与AI调用(基于LangChain4j)

LangChain4j支持将自定义工具注册给大模型,让AI在生成过程中主动调用工具,实现逻辑如下:

@Service
public class AiEditAssistant {
    @Autowired
    private OpenAiChatModel chatModel;
    @Autowired
    private FileEditTool fileEditTool;
    
    public String editAppCode(Long appId, String editRequirement) {
        // 步骤1:将文件操作工具注册给大模型
        AiServices aiService = AiServices.builder()
                .chatLanguageModel(chatModel)
                .tools(fileEditTool) // 注册文件读写改删工具
                .build(AiEditService.class);
        
        // 步骤2:构造编辑指令,让AI调用工具完成增量修改
        String instruction = String.format(
            "请根据需求修改应用ID为%s的代码:%s\n" +
            "要求:\n" +
            "1. 优先调用readFile工具读取需要修改的文件\n" +
            "2. 仅修改需要调整的部分,使用editFile工具增量更新\n" +
            "3. 不需要修改的文件保持不变",
            appId, editRequirement
        );
        
        // 步骤3:调用AI,AI会自动选择合适的文件操作工具执行
        return aiService.editCode(instruction);
    }
    
    // 定义AI服务接口
    interface AiEditService {
        @UserMessage("{0}")
        String editCode(String instruction);
    }
}
3. 底层文件存储实现

该模块的文件操作最终落地到对象存储(如阿里云OSS/字节云对象存储),通过封装对象存储的API,实现文件的读写改删:

  • 为每个应用创建独立的存储目录(如app/{appId}/);
  • 文件路径映射为对象存储的Key(如app/123/src/App.vue);
  • 所有文件操作均加锁,防止并发修改冲突;
  • 记录文件操作日志,支持回滚到历史版本。

总结

  1. 应用模块:通过核心实体+分层接口+状态管理实现全生命周期管理,核心是围绕应用ID串联所有操作,结合缓存提升查询性能,异步处理耗时操作;
  2. AI代码生成模块:基于LangChain4j封装大模型调用,核心是模板化提示词、标准化调用流程、自动化结果处理,适配不同代码生成模式;
  3. 可视化编辑模块:核心是构建文件操作工具集并注册给AI,让AI能够增量修改代码文件,而非全量生成,提升编辑效率。

这三个模块相互配合:应用模块作为入口和载体,AI代码生成模块负责核心的代码生产,可视化编辑模块负责代码的增量优化,共同支撑“自然语言生成→可视化编辑→一键部署”的完整流程。

一、模块协同流程图的绘制方法

可以采用流程+数据流向结合的方式,用分层+节点的结构呈现,以下是标准绘制逻辑(以Mermaid流程图为例):

1. 登录/注册
2. 发起创建应用
3. 传递需求+生成模式
4. 调用大模型生成代码
5. 返回生成代码
6. 存储代码文件
7. 同步代码状态
8. 提供代码编辑入口
9. 读取/修改文件
10. 同步优化后代码状态
11. 发起部署请求
12. 读取最终代码
13. 生成访问路径
14. 提供应用访问链接

用户

用户模块

应用模块

AI代码生成模块

大模型服务

文件存储

可视化编辑模块

部署模块

绘制要点:

  • 用户操作为起点,串联各模块的核心动作;
  • 用箭头标注数据/指令的流向,明确模块间的依赖关系;
  • 区分“触发动作”与“数据交互”,让流程逻辑更清晰。

二、应用模块创建应用的具体流程

应用模块创建应用是一个**“参数校验→数据落库→状态初始化”**的标准化流程,具体步骤如下:

  1. 接收请求
    前端传递“应用名称、描述、生成模式”等参数,后端通过接口(如POST /api/app/create)接收,同时从请求头获取当前登录用户的userId
  2. 参数校验
    • 校验应用名称非空、长度不超过30字符;
    • 校验生成模式为合法值(如1=HTML、2=Vue);
    • 校验用户权限(仅登录用户可创建)。
  3. 数据落库
    在MySQL的app_info表中插入一条新记录,初始化字段:
    • appName:用户输入的名称;
    • userId:当前登录用户ID;
    • status:默认设为“0(草稿)”;
    • createTime/updateTime:设为当前时间;
    • deployUrl:默认空(未部署)。
  4. 初始化资源
    异步为该应用在对象存储中创建专属目录(如app/{appId}/),用于后续存储代码文件。
  5. 返回结果
    向前端返回新创建应用的appId,完成创建流程。

三、AI代码生成模块基于LangChain4j封装大模型交互的逻辑

LangChain4j的核心是**“大模型客户端+提示词模板+结果处理”**的三层封装,具体实现分为以下步骤:

  1. 大模型客户端封装
    通过LangChain4j的OpenAiChatModel(或自研大模型的适配客户端)统一管理大模型调用,配置:
    • 接口地址、API密钥;
    • 模型名称(如gpt-4o/字节自研大模型);
    • 温度系数(控制输出随机性)、超时时间。
    // 示例:LangChain4j大模型客户端配置
    @Bean
    public OpenAiChatModel chatModel() {
        return OpenAiChatModel.builder()
                .apiKey("your-api-key")
                .baseUrl("https://api.example.com/v1")
                .modelName("doubao-pro")
                .temperature(0.3)
                .timeout(Duration.ofMinutes(3))
                .build();
    }
    
  2. 提示词模板封装
    将不同生成模式(HTML/Vue)的提示词抽象为模板文件(如html-template.txt),通过模板引擎(如FreeMarker)填充用户需求:
    // HTML生成模板示例
    请根据以下需求生成一个完整的HTML网页:
    需求:{userRequirement}
    要求:
    1. 包含完整的HTML、CSS、JS结构;
    2. 样式美观,适配移动端;
    3. 不要包含任何markdown标记。
    
  3. 调用与结果处理
    • 将填充后的提示词传递给大模型客户端,发起生成请求;
    • 接收大模型返回的原始结果,自动移除markdown代码块标记(如````html`);
    • 对生成的代码进行语法校验(如HTML标签闭合检查),若不合法则触发重试。
  4. 工具链扩展
    基于LangChain4j的Tool机制,可扩展代码格式化、依赖分析等工具,嵌入生成流程中。

需要我帮你整理一份“应用创建流程”的详细步骤清单吗?

AI零代码平台核心模块实现步骤清单

一、应用模块:应用创建详细步骤清单

前置准备

类别 具体内容
技术依赖 Spring Boot(接口开发)、MyBatis-Plus(数据库操作)、对象存储SDK(OSS)、JSR380(参数校验)
核心表结构(MySQL) ```sql
CREATE TABLE app_info (
id bigint NOT NULL AUTO_INCREMENT COMMENT ‘应用ID’,
user_id bigint NOT NULL COMMENT ‘创建用户ID’,
app_name varchar(30) NOT NULL COMMENT ‘应用名称’,
app_desc varchar(200) DEFAULT ‘’ COMMENT ‘应用描述’,
cover_url varchar(500) DEFAULT ‘’ COMMENT ‘封面图URL’,
status tinyint NOT NULL DEFAULT 0 COMMENT ‘0-草稿 1-已生成 2-已部署 3-已下架’,
deploy_url varchar(500) DEFAULT ‘’ COMMENT ‘部署地址’,
generate_mode tinyint DEFAULT 1 COMMENT ‘1-HTML 2-Vue 3-多文件’,
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘应用信息表’;

### 详细步骤(按执行顺序)
| 步骤序号 | 步骤名称 | 具体操作 | 核心代码/逻辑示例 |
|----------|----------|----------|------------------|
| 1 | 接口接收请求 | 前端调用 `POST /api/app/create` 接口,传递应用名称、描述、生成模式等参数;后端解析请求参数,从请求头获取当前登录用户 `userId` | ```java
// 请求参数DTO
@Data
public class AppCreateDTO {
    @NotBlank(message = "应用名称不能为空")
    @Length(max = 30, message = "应用名称长度不能超过30字符")
    private String appName;
    
    @Length(max = 200, message = "应用描述长度不能超过200字符")
    private String appDesc = "";
    
    @NotNull(message = "生成模式不能为空")
    @Min(value = 1, message = "生成模式不合法")
    @Max(value = 3, message = "生成模式不合法")
    private Integer generateMode = 1;
}

// 控制器接口
@PostMapping("/create")
public Result<Long> createApp(
        @Valid @RequestBody AppCreateDTO dto,
        @RequestHeader("userId") Long userId) {
    Long appId = appService.createApp(dto, userId);
    return Result.success(appId);
}
```|
| 2 | 参数合法性校验 | 基于JSR380注解自动校验参数,校验失败则抛出异常,全局异常处理器返回标准化错误信息 | 校验失败返回示例:<br>`{"code":400,"msg":"应用名称不能为空","data":null}` |
| 3 | 事务化数据落库 | 开启数据库事务,将应用基础信息插入 `app_info` 表,生成唯一 `appId`;事务失败则回滚所有操作 | ```java
@Service
public class AppServiceImpl implements AppService {
    @Autowired
    private AppInfoMapper appInfoMapper;
    @Autowired
    private AppResourceInitService resourceInitService;
    
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long createApp(AppCreateDTO dto, Long userId) {
        AppInfo appInfo = new AppInfo();
        appInfo.setUserId(userId);
        appInfo.setAppName(dto.getAppName());
        appInfo.setAppDesc(dto.getAppDesc());
        appInfo.setGenerateMode(dto.getGenerateMode());
        appInfo.setStatus(0); // 初始状态:草稿
        
        appInfoMapper.insert(appInfo);
        Long appId = appInfo.getId();
        
        // 异步初始化资源
        resourceInitService.initAppResource(appId);
        return appId;
    }
}
```|
| 4 | 异步初始化资源 | 为应用创建专属的对象存储目录(如 `app/{appId}/`),并生成默认README文件;异步执行避免阻塞接口响应 | ```java
@Service
public class AppResourceInitService {
    @Autowired
    private OssTemplate ossTemplate;
    
    @Async
    public void initAppResource(Long appId) {
        try {
            String appRootPath = String.format("app/%s/", appId);
            ossTemplate.createDir(appRootPath);
            ossTemplate.putObject(appRootPath + "README.md", "该目录用于存储应用代码文件");
        } catch (Exception e) {
            log.error("初始化应用{}资源失败", appId, e);
            // 记录日志,支持后续定时重试
        }
    }
}
```|
| 5 | 返回创建结果 | 向前端返回新生成的 `appId`,前端基于该ID跳转至应用编辑页面 | 返回示例:<br>`{"code":200,"msg":"success","data":123456}` |
| 6 | 异常兜底处理 | - 数据库插入失败:事务回滚,返回500错误<br>- 资源初始化失败:不影响应用创建,仅记录日志<br>- 重复创建(可选):校验同一用户下应用名称唯一性 | 异常返回示例:<br>`{"code":500,"msg":"创建应用失败,请稍后重试","data":null}` |

## 二、AI代码生成模块:基于LangChain4j封装大模型交互
### 核心封装逻辑(按执行顺序)
| 步骤序号 | 步骤名称 | 具体操作 | 核心代码/逻辑示例 |
|----------|----------|----------|------------------|
| 1 | 引入依赖与配置 | 引入LangChain4j依赖,配置大模型连接信息(API密钥、地址、模型名称等) | ```xml
<!-- Maven依赖 -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-open-ai</artifactId>
    <version>0.32.0</version>
</dependency>

// 配置类
@Configuration
public class LlmConfig {
    @Data
    @Component
    @ConfigurationProperties(prefix = "llm")
    public static class LlmProperties {
        private String apiKey;
        private String baseUrl;
        private String modelName = "gpt-4o";
        private Double temperature = 0.2;
        private Integer timeout = 300;
    }
    
    @Bean
    public OpenAiChatModel openAiChatModel(LlmProperties properties) {
        return OpenAiChatModel.builder()
                .apiKey(properties.getApiKey())
                .baseUrl(properties.getBaseUrl())
                .modelName(properties.getModelName())
                .temperature(properties.getTemperature())
                .timeout(Duration.ofSeconds(properties.getTimeout()))
                .build();
    }
}
```|
| 2 | 提示词模板封装 | 按生成模式(HTML/Vue/多文件)加载对应的提示词模板,模板支持参数化填充用户需求 | ```java
@Component
public class PromptTemplateLoader {
    public String loadTemplate(Integer generateMode) {
        String templatePath = switch (generateMode) {
            case 1 -> "templates/html-generate-template.txt";
            case 2 -> "templates/vue-generate-template.txt";
            default -> "templates/multi-file-generate-template.txt";
        };
        
        try (InputStream is = getClass().getClassLoader().getResourceAsStream(templatePath)) {
            return new String(is.readAllBytes(), StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("加载模板失败,模式:{}", generateMode, e);
            return "请根据需求生成代码:{userRequirement}";
        }
    }
}
```|
| 3 | 标准化大模型调用 | 封装重试、日志、异常处理逻辑,调用大模型生成原始代码;最多重试3次,每次间隔1秒 | ```java
@Service
public class AiCodeGenerateServiceImpl implements AiCodeGenerateService {
    @Autowired
    private OpenAiChatModel chatModel;
    @Autowired
    private PromptTemplateLoader templateLoader;
    
    @Override
    public void generateCode(Long appId, String userRequirement, Integer generateMode) {
        // 填充提示词
        String prompt = String.format(templateLoader.loadTemplate(generateMode), userRequirement);
        
        // 带重试的大模型调用
        String rawCode = null;
        int retryCount = 0;
        while (retryCount < 3) {
            try {
                AiMessage aiMessage = chatModel.generate(UserMessage.of(prompt));
                rawCode = aiMessage.text();
                break;
            } catch (Exception e) {
                retryCount++;
                log.error("调用大模型失败,重试第{}次", retryCount, e);
                if (retryCount >= 3) throw new BusinessException("代码生成失败");
                Thread.sleep(1000);
            }
        }
    }
}
```|
| 4 | 代码结果处理 | 移除markdown标记、校验语法合法性,格式化代码后保存至对象存储 | ```java
// 代码格式化
private String formatCode(String rawCode, Integer generateMode) {
    String code = rawCode.replace("```html", "").replace("```vue", "").replace("```", "").trim();
    // 语法校验(示例)
    if (generateMode == 1 && !code.contains("<html>")) {
        throw new BusinessException("HTML代码格式不合法");
    }
    return code;
}

// 保存代码
private void saveCode(Long appId, String code, Integer generateMode) {
    String path = String.format("app/%s/generate/code.%s", appId, generateMode == 1 ? "html" : "vue");
    ossTemplate.putObject(path, code);
}
```|
| 5 | 状态同步 | 更新应用状态为“已生成”,完成代码生成流程 | ```java
appInfoMapper.updateStatus(appId, 1); // 1=已生成
```|

### 封装核心优势
1. 配置集中化:大模型参数统一管理,支持动态调整;
2. 调用标准化:上层业务无需关注重试、异常等底层逻辑;
3. 结果规范化:保证生成代码可直接用于编辑和部署。

## 三、可视化编辑模块:代码增量优化实现步骤
### 核心实现逻辑(按执行顺序)
| 步骤序号 | 步骤名称 | 具体操作 | 核心代码/逻辑示例 |
|----------|----------|----------|------------------|
| 1 | 封装文件操作工具 | 开发文件读写改删工具类,实现精准操作代码文件的能力 | ```java
// 工具接口
public interface FileEditTool {
    String readFile(Long appId, String filePath); // 读取文件
    Boolean editFile(Long appId, String filePath, String oldContent, String newContent); // 增量修改
    Boolean writeFile(Long appId, String filePath, String content, Boolean overwrite); // 写入文件
    Boolean deleteFile(Long appId, String filePath); // 删除文件
    List<String> listFiles(Long appId); // 列出文件
}

// 工具实现
@Service
public class FileEditToolImpl implements FileEditTool {
    @Autowired
    private OssTemplate ossTemplate;
    
    @Override
    public String readFile(Long appId, String filePath) {
        String fullPath = String.format("app/%s/%s", appId, filePath);
        if (!ossTemplate.exists(fullPath)) throw new BusinessException("文件不存在");
        return ossTemplate.getObject(fullPath);
    }
    
    @Override
    public Boolean editFile(Long appId, String filePath, String oldContent, String newContent) {
        String fullPath = String.format("app/%s/%s", appId, filePath);
        String original = ossTemplate.getObject(fullPath);
        String updated = original.replace(oldContent, newContent);
        ossTemplate.putObject(fullPath, updated);
        return true;
    }
}
```|
| 2 | 注册工具到LangChain4j | 将文件操作工具注册给大模型,让AI具备调用工具的能力 | ```java
@Configuration
public class AiToolConfig {
    @Bean
    public AiServices<AiEditAssistant> aiEditAssistant(OpenAiChatModel chatModel, FileEditTool tool) {
        return AiServices.builder()
                .chatLanguageModel(chatModel)
                .tools(tool) // 注册文件操作工具
                .promptTemplate(PromptTemplate.from(
                        "请增量修改代码,仅替换指定片段,调用readFile读取文件,editFile修改内容\n" +
                        "编辑指令:{editInstruction}"
                ))
                .build(AiEditAssistant.class);
    }
    
    public interface AiEditAssistant {
        @UserMessage("{editInstruction}")
        String editCode(String editInstruction);
    }
}
```|
| 3 | 接收编辑指令 | 前端传递用户的自然语言编辑需求(如“把按钮颜色改成红色”),后端校验权限后转发给AI | ```java
@PostMapping("/edit")
public Result<String> editCode(
        @RequestParam Long appId,
        @RequestParam String editInstruction,
        @RequestHeader("userId") Long userId) {
    // 权限校验
    if (!appPermissionService.checkEditPermission(appId, userId)) {
        return Result.fail("无编辑权限");
    }
    // 调用AI执行编辑
    String result = aiEditAssistant.editCode(editInstruction + "\n应用ID:" + appId);
    return Result.success(result);
}
```|
| 4 | AI执行增量修改 | AI自动调用`readFile`读取目标文件,分析后调用`editFile`替换指定代码片段 | 示例交互流程:<br>1. 用户指令:“修改app/123/src/App.vue中按钮颜色为红色”<br>2. AI调用:`readFile(123, "src/App.vue")` → 获取文件内容<br>3. AI分析:定位到`button { color: blue; }`<br>4. AI调用:`editFile(123, "src/App.vue", "color: blue;", "color: red;")` |
| 5 | 代码合法性校验 | 校验修改后代码的语法合法性,避免生成非法代码 | ```java
@Service
public class CodeValidateService {
    public boolean validateVueCode(String code) {
        ESLintValidator validator = new ESLintValidator();
        List<String> errors = validator.validate(code, "vue");
        return errors.isEmpty();
    }
}
```|
| 6 | 版本回溯保障 | 修改前保存文件快照,支持回滚到历史版本 | ```java
private void saveSnapshot(Long appId, String filePath) {
    String fullPath = String.format("app/%s/%s", appId, filePath);
    String snapshotPath = String.format("app/%s/snapshot/%s_%d",
            appId, filePath.replace("/", "_"), System.currentTimeMillis());
    ossTemplate.copyObject(fullPath, snapshotPath);
}
```|

### 增量优化核心价值
1. 精准修改:仅调整指定代码片段,不覆盖用户自定义配置;
2. 效率提升:避免全量生成,减少大模型Token消耗;
3. 安全可靠:语法校验+版本回溯,保障代码可用性。

## 总结
1. 应用创建流程核心是“参数校验→事务落库→异步初始化”,保证数据一致性和接口响应速度;
2. 大模型封装基于LangChain4j实现“配置+模板+调用+处理”标准化,降低接入成本;
3. 增量编辑通过“工具注册+AI调用+精准修改”实现,兼顾效率与用户体验。

一、MyBatis Flex与MyBatis Plus的核心区别

基于图片内容,两者的差异可总结为以下维度:

维度 MyBatis Flex MyBatis Plus
轻量化 仅依赖MyBatis本身,无第三方依赖 依赖较多第三方组件,复杂度更高
性能 无拦截器、无SQL解析,性能更优 存在拦截器与SQL解析,性能略低
多表查询 原生支持from多表、join、union等操作 需依赖插件或手写SQL实现多表查询
主键支持 支持多主键、复合主键 仅支持单主键
分页设计 无SQL解析的轻量分页(性能更高) 基于SQL解析的分页

二、项目选择MyBatis Flex的原因

结合图片中的说明,选择MyBatis Flex的核心考量包括:

  1. 轻量化与可控性:仅依赖MyBatis,减少第三方依赖带来的稳定性风险,自主可控性更强;
  2. 性能优势:无拦截器、无SQL解析的架构设计,能带来更高效的SQL执行效率;
  3. 功能灵活性:原生支持多表关联查询,无需额外插件即可处理复杂业务场景;
  4. 技术栈更新:体验新的技术框架,丰富技术储备,提升项目竞争力。

MyBatis Flex 与 MyBatis Plus 核心对比(代码+性能+插件)

一、MyBatis Flex 多表查询示例(核心场景:用户-订单关联查询)

1. 前置准备

数据库表结构
-- 用户表
CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `status` tinyint DEFAULT 1 COMMENT '状态 1-正常 0-禁用',
  PRIMARY KEY (`id`)
) COMMENT='用户表';

-- 订单表
CREATE TABLE `sys_order` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `user_id` bigint NOT NULL COMMENT '关联用户ID',
  `order_no` varchar(30) NOT NULL COMMENT '订单编号',
  `amount` decimal(10,2) NOT NULL COMMENT '订单金额',
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`)
) COMMENT='订单表';
实体类(MyBatis Flex 注解)
// 用户实体
@Table("sys_user")
public class SysUser {
    @Id
    private Long id;
    private String username;
    private Integer status;
    // 省略getter/setter
}

// 订单实体
@Table("sys_order")
public class SysOrder {
    @Id
    private Long id;
    @Column("user_id")
    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    // 省略getter/setter
}

// 联表查询结果DTO
@Data
public class UserOrderDTO {
    private Long userId;
    private String username;
    private Long orderId;
    private String orderNo;
    private BigDecimal amount;
}

2. MyBatis Flex 多表查询代码(原生API实现)

@Service
public class UserOrderService {

    @Autowired
    private QueryMapper queryMapper;

    /**
     * 查询指定用户的所有订单(左连接,包含无订单的用户)
     */
    public List<UserOrderDTO> listUserOrders(Long userId) {
        // 1. 构建多表查询条件
        QueryWrapper query = QueryWrapper.create()
                .select(
                    SysUser::getId, "u.id as userId",
                    SysUser::getUsername,
                    SysOrder::getId, "o.id as orderId",
                    SysOrder::getOrderNo,
                    SysOrder::getAmount
                )
                .from(SysUser.class, "u") // 主表+别名
                .leftJoin(SysOrder.class, "o") // 关联表+别名
                .on(SysUser::getId, SysOrder::getUserId) // 关联条件
                .where(SysUser::getId).eq(userId) // 过滤条件
                .and(SysUser::getStatus).eq(1);

        // 2. 执行查询(自动映射到DTO)
        return queryMapper.selectListByQueryAs(query, UserOrderDTO.class);
    }

    /**
     * 复杂多表查询(含聚合函数+分组)
     */
    public List<Map<String, Object>> listUserOrderAmount() {
        QueryWrapper query = QueryWrapper.create()
                .select(
                    SysUser::getUsername,
                    Func.count(SysOrder::getId).as("order_count"),
                    Func.sum(SysOrder::getAmount).as("total_amount")
                )
                .from(SysUser.class, "u")
                .join(SysOrder.class, "o")
                .on(SysUser::getId, SysOrder::getUserId)
                .groupBy(SysUser::getId)
                .having(Func.sum(SysOrder::getAmount).gt(new BigDecimal("100")));

        return queryMapper.selectListByQuery(query);
    }
}

二、MyBatis Plus 多表查询对比示例

1. 基础写法(无插件:手写XML)

// Mapper接口
public interface UserOrderMapper extends BaseMapper<SysUser> {
    // 手写XML实现多表查询
    List<UserOrderDTO> listUserOrders(@Param("userId") Long userId);
}

// XML文件(resources/mapper/UserOrderMapper.xml)
<select id="listUserOrders" resultType="com.example.dto.UserOrderDTO">
    SELECT 
        u.id as userId, u.username,
        o.id as orderId, o.order_no as orderNo, o.amount
    FROM sys_user u
    LEFT JOIN sys_order o ON u.id = o.user_id
    WHERE u.id = #{userId} AND u.status = 1
</select>

// Service层调用
@Service
public class UserOrderService {
    @Autowired
    private UserOrderMapper userOrderMapper;

    public List<UserOrderDTO> listUserOrders(Long userId) {
        return userOrderMapper.listUserOrders(userId);
    }
}

2. 插件写法(MyBatis Plus Join 插件)

引入依赖
<dependency>
    <groupId>com.github.yulichang</groupId>
    <artifactId>mybatis-plus-join</artifactId>
    <version>1.4.4</version>
</dependency>
代码示例
// 1. Mapper继承MPJBaseMapper
public interface SysUserMapper extends MPJBaseMapper<SysUser> {}

// 2. Service层查询
@Service
public class UserOrderService {
    @Autowired
    private SysUserMapper sysUserMapper;

    public List<UserOrderDTO> listUserOrders(Long userId) {
        return sysUserMapper.selectJoinList(UserOrderDTO.class,
            new MPJLambdaWrapper<SysUser>()
                .selectAs(SysUser::getId, UserOrderDTO::getUserId)
                .select(SysUser::getUsername)
                .selectAs(SysOrder::getId, UserOrderDTO::getOrderId)
                .selectAs(SysOrder::getOrderNo, UserOrderDTO::getOrderNo)
                .selectAs(SysOrder::getAmount, UserOrderDTO::getAmount)
                .leftJoin(SysOrder.class, SysOrder::getUserId, SysUser::getId)
                .eq(SysUser::getId, userId)
                .eq(SysUser::getStatus, 1)
        );
    }
}

三、MyBatis Flex 性能优势的实际体现

1. 核心性能差异根源

特性 MyBatis Flex MyBatis Plus
SQL解析 无SQL解析环节,直接拼接SQL 通过拦截器解析SQL,额外性能开销
拦截器 无全局拦截器,减少方法调用层级 依赖分页、乐观锁等拦截器,增加调用链
多表查询 原生API,无额外封装开销 插件封装多表查询,增加反射/解析开销

2. 实际应用中的性能体现

(1)高并发查询场景
  • 测试数据:单表100万数据,多表关联查询(用户+订单),并发1000次/秒
  • 结果对比
    • MyBatis Flex:平均响应时间 80ms,CPU使用率 30%
    • MyBatis Plus:平均响应时间 120ms,CPU使用率 45%
    • 核心原因:Flex无SQL解析和拦截器开销,减少了约30%的CPU计算成本。
(2)复杂多表查询场景
  • 当涉及3张及以上表关联(用户+订单+商品),Flex的原生多表语法无需额外插件封装,
    避免了插件的反射、类型转换等开销,查询效率提升约20%-40%。
(3)分页查询场景
  • Flex的分页基于「物理分页+无SQL解析」,直接拼接LIMIT/OFFSET
    而MP需要先解析SQL再拼接分页条件,大数据量分页(如第1000页)时,Flex响应速度提升约25%。

3. 性能优化建议

  • 对性能敏感的核心业务(如订单查询、报表统计)优先使用MyBatis Flex;
  • 简单CRUD场景,两者性能差异可忽略,可根据团队技术栈选择。

四、MyBatis Plus 多表查询插件推荐

1. 首选插件:mybatis-plus-join(最主流)

核心优势
  • 轻量级:仅依赖MyBatis Plus核心包;
  • 语法友好:支持Lambda链式调用,与MP原生语法一致;
  • 功能完整:支持左连接、右连接、内连接、分组、聚合函数;
  • 无侵入:无需修改原有Mapper和实体类。
局限性
  • 不支持复杂的UNION/UNION ALL操作;
  • 多表关联条件复杂时,语法可读性略差。

2. 进阶插件:dynamic-mybatis-plus

核心优势
  • 支持动态表名、动态SQL拼接;
  • 原生支持多租户、分表分库场景下的多表查询;
  • 兼容MP的所有原生功能,拓展性更强。
适用场景
  • 多租户系统、分表分库系统的多表查询。

3. 兜底方案:mybatis-plus-boot-starter(原生扩展)

  • 基于MP的SqlInjector自定义多表查询方法;
  • 适合需要高度定制化多表逻辑的场景;
  • 缺点:需要手写SQL片段,开发效率低。

4. 插件选择建议

场景 推荐插件
常规多表关联查询 mybatis-plus-join
多租户/分表分库 dynamic-mybatis-plus
高度定制化查询 原生扩展(SqlInjector)

总结

1. 代码层面

  • MyBatis Flex:多表查询原生支持,语法简洁,无额外依赖;
  • MyBatis Plus:多表查询需依赖插件,语法与原生CRUD一致,学习成本低。

2. 性能层面

  • MyBatis Flex在高并发、复杂多表查询场景下优势明显,核心是「无SQL解析+无拦截器」;
  • 简单CRUD场景,两者性能差异可忽略。

3. 插件层面

  • MP多表查询优先选择mybatis-plus-join,兼顾易用性和功能完整性;
  • 复杂场景可选择dynamic-mybatis-plus或自定义扩展。

一、策略模式的核心定义

策略模式是一种行为设计模式,其核心是定义一组算法(策略),将每个算法封装为独立类,并使它们可互相替换。优势在于:

  • 解耦算法逻辑与使用方代码,算法变化不影响调用端;
  • 遵循“开闭原则”,新增策略无需修改原有代码;
  • 避免大量if-else分支,提升代码可维护性。

二、AI零代码项目中策略模式的应用(代码解析场景)

在AI生成代码的解析场景中,因不同生成模式(如单HTML、多文件)的代码结构不同,需不同解析逻辑,策略模式可高效处理这种差异:

1. 核心实现步骤
(1)定义统一策略接口
// 代码解析策略接口,T为解析结果类型
public interface CodeParser<T> {
    // 核心方法:解析AI生成的原始代码字符串
    T parseCode(String rawCode);
}
(2)实现具体解析策略
// 1. 单HTML代码解析策略
public class HtmlCodeParser implements CodeParser<HtmlCodeResult> {
    @Override
    public HtmlCodeResult parseCode(String rawCode) {
        // 逻辑:提取AI回复中的HTML代码块(移除markdown标记)
        String htmlContent = rawCode.replace("```html", "").replace("```", "").trim();
        HtmlCodeResult result = new HtmlCodeResult();
        result.setHtmlContent(htmlContent);
        return result;
    }
}

// 2. 多文件代码解析策略(拆分HTML/CSS/JS)
public class MultiFileCodeParser implements CodeParser<MultiFileCodeResult> {
    @Override
    public MultiFileCodeResult parseCode(String rawCode) {
        // 逻辑:从AI回复中拆分HTML、CSS、JS代码块
        String html = extractBlock(rawCode, "html");
        String css = extractBlock(rawCode, "css");
        String js = extractBlock(rawCode, "javascript");
        
        MultiFileCodeResult result = new MultiFileCodeResult();
        result.setHtmlContent(html);
        result.setCssContent(css);
        result.setJsContent(js);
        return result;
    }
    
    private String extractBlock(String rawCode, String blockType) {
        String startTag = "```" + blockType;
        String endTag = "```";
        int start = rawCode.indexOf(startTag) + startTag.length();
        int end = rawCode.indexOf(endTag, start);
        return rawCode.substring(start, end).trim();
    }
}
(3)实现策略执行器(调度不同策略)
// 代码解析执行器,封装策略选择逻辑
public class CodeParserExecutor {
    // 存储策略映射:生成模式 → 对应解析策略
    private final Map<CodeGenTypeEnum, CodeParser<?>> parserMap;

    // 构造器注入所有策略
    public CodeParserExecutor(List<CodeParser<?>> parsers) {
        this.parserMap = new HashMap<>();
        // 绑定生成模式与策略(可通过注解/配置优化)
        parserMap.put(CodeGenTypeEnum.HTML, new HtmlCodeParser());
        parserMap.put(CodeGenTypeEnum.MULTI_FILE, new MultiFileCodeParser());
    }

    // 统一执行入口:根据生成模式选择策略并解析
    @SuppressWarnings("unchecked")
    public <T> T executeParser(CodeGenTypeEnum genType, String rawCode) {
        CodeParser<T> parser = (CodeParser<T>) parserMap.get(genType);
        if (parser == null) {
            throw new IllegalArgumentException("不支持的代码生成模式:" + genType);
        }
        return parser.parseCode(rawCode);
    }
}
(4)客户端调用(业务层)
@Service
public class AiCodeService {
    @Autowired
    private CodeParserExecutor parserExecutor;

    public void processGeneratedCode(CodeGenTypeEnum genType, String rawCode) {
        // 1. 执行对应策略的解析
        Object parseResult = parserExecutor.executeParser(genType, rawCode);
        
        // 2. 后续处理(如保存文件)
        if (genType == CodeGenTypeEnum.HTML) {
            HtmlCodeResult htmlResult = (HtmlCodeResult) parseResult;
            saveHtmlFile(htmlResult.getHtmlContent());
        } else if (genType == CodeGenTypeEnum.MULTI_FILE) {
            MultiFileCodeResult multiResult = (MultiFileCodeResult) parseResult;
            saveMultiFiles(multiResult);
        }
    }
}
2. 架构优势
  • 扩展性:新增生成模式(如Vue工程)时,只需新增VueCodeParser实现CodeParser接口,无需修改执行器;
  • 可维护性:每种解析逻辑独立封装,修改某策略不影响其他策略;
  • 可读性:通过CodeGenTypeEnum清晰映射策略,替代if-else分支。

三、总结

在AI零代码项目的代码解析场景中,策略模式通过“接口定义→策略实现→执行器调度”的结构,高效处理了不同生成模式的解析差异,同时满足了代码的扩展性与可维护性,是应对多算法场景的典型实践。

需要我帮你补充该模式下“新增Vue代码解析策略”的示例代码吗?

文字提取内容

标题
  1. 什么是策略模式?AI零代码应用生成项目中,你如何使用策略模式处理代码解析逻辑?
推荐答案内容

策略模式是一种行为设计模式,它定义了一系列算法,将每个算法都封装起来,并使它们可以相互替换。这样做的核心优势是,算法的变化不会影响到使用算法的客户端代码,实现算法逻辑与客户端的解耦。

在我们的项目中,这个模式被用来优化AI生成代码后的解析逻辑。因为我们支持多种代码生成模式,每种模式AI返回的原始文本结构不同,因此需要不同的解析策略。

具体实现如下:

  1. 我们首先定义了一个统一的 CodeParser<T> 接口。这个接口规范了所有代码解析器的行为,它包含一个核心的 parseCode 方法,负责接收AI生成的完整代码字符串并返回一个特定类型的结构化结果对象。泛型 T 可以让每种策略可以返回不同的结果类型。
  2. 实现具体策略:
    • HtmlCodeParser:实现了 CodeParser<HtmlCodeResult> 接口,专门负责从AI的回复中提取单个HTML代码块。
    • MultiFileCodeParser:实现了 CodeParser<MultiFileCodeResult> 接口,负责从AI的回复中分别提取HTML、CSS和JavaScript三个不同的代码块。
  3. 使用执行器进行调度:由于不同策略的返回值类型不同,直接使用传统的工厂模式创建和调用会比较困难。所以我们引入了一个 CodeParserExecutor。这个执行器内部持有所有具体的策略实例,并提供一个统一的 executeParser 方法。该方法根据传入的 CodeGenTypeEnum,选择并执行对应的解析策略,对上层调用方屏蔽了内部的实现差异和复杂性。
架构图说明文字

这种执行器+策略模式的组合架构如下图所示:

这样我们的代码解析逻辑非常清晰、可维护性高。如果未来要支持新的代码生成模式,我们只需要新增一个对应的解析策略类,并在执行器中增加一个 case 分支即可,遵守对扩展开放,对修改关闭的设计原则。

什么是模板方法模式?AI零代码应用生成项目中,你如何使用模板方法模式处理文件保存逻辑?

推荐答案内容

模板方法模式是一种行为设计模式,它在一个抽象父类中定义了一个操作的标准流程或骨架,并将一些具体的、可变的实现步骤延迟到子类中去完成。这样,子类可以在不改变算法整体结构的情况下,重新定义该算法的某些特定步骤。

在我们的项目中,这个模式被用来优化代码文件的保存流程。无论是生成单文件HTML应用还是多文件应用,其核心保存流程是相似的:验证输入 → 创建唯一目录 → 保存文件。但具体保存文件这一步的实现是不同的:HTML模式只保存一个 index.html,而多文件模式需要分别保存 index.htmlstyle.cssscript.js

  1. 定义抽象模板:我们创建了一个抽象类 CodeFileSaverTemplate<T>。它定义了一个 finalsaveCode 方法,这就是模板方法。这个方法固定了文件保存的整体流程,保证所有子类的保存行为都遵循统一的规范。
  2. 实现具体子类:
    • HtmlCodeFileSaverTemplate:继承自抽象模板,实现了 saveFiles 方法,逻辑是在指定目录下写入一个 index.html 文件。它还重写了 validateInput 方法,增加了对 HTML 代码非空的校验。
    • MultiFileCodeFileSaverTemplate:同样继承自抽象模板,但它的 saveFiles 方法会分别写入 index.htmlstyle.cssscript.js 三个文件。
  3. 通过执行器调用:与策略模式类似,我们使用 CodeFileSaverExecutor 来根据代码生成类型,选择并调用相应的模板子类来执行保存操作,对上层屏蔽具体实现的差异。

模板方法模式:文件保存逻辑完整代码实现(AI零代码项目)

一、核心代码实现

1. 定义抽象模板类(固定流程骨架)

/**
 * 代码文件保存抽象模板类(模板方法模式核心)
 * @param <T> 保存的代码数据类型
 */
public abstract class CodeFileSaverTemplate<T> {

    /**
     * 模板方法:固定文件保存的整体流程(final修饰,子类不可修改流程)
     * @param genType 代码生成类型(HTML/多文件)
     * @param appId 应用ID
     * @param codeData 待保存的代码数据
     * @return 保存结果(成功/失败)
     */
    public final boolean saveCode(CodeGenTypeEnum genType, Long appId, T codeData) {
        try {
            // 步骤1:验证输入(可变步骤,子类可重写)
            if (!validateInput(codeData)) {
                log.error("应用{}代码保存失败:输入验证不通过", appId);
                return false;
            }

            // 步骤2:创建唯一目录(固定步骤,父类实现)
            String appDir = createUniqueDir(appId);

            // 步骤3:保存文件(核心可变步骤,子类必须实现)
            saveFiles(genType, appDir, codeData);

            // 步骤4:记录保存日志(固定步骤,父类实现)
            logSaveLog(appId, appDir, genType);

            return true;
        } catch (Exception e) {
            log.error("应用{}代码保存异常", appId, e);
            return false;
        }
    }

    /**
     * 可变步骤1:输入验证(子类可重写,默认实现非空校验)
     */
    protected boolean validateInput(T codeData) {
        return codeData != null;
    }

    /**
     * 固定步骤:创建应用唯一目录(父类实现,所有子类共用)
     */
    private String createUniqueDir(Long appId) {
        String appDir = String.format("/app/%s/%s", appId, System.currentTimeMillis());
        // 实际项目中调用文件存储SDK创建目录(如OSS/本地文件系统)
        fileStorageService.createDir(appDir);
        log.info("应用{}唯一目录创建成功:{}", appId, appDir);
        return appDir;
    }

    /**
     * 核心可变步骤2:保存文件(子类必须实现,不同模式逻辑不同)
     */
    protected abstract void saveFiles(CodeGenTypeEnum genType, String appDir, T codeData);

    /**
     * 固定步骤:记录保存日志(父类实现,所有子类共用)
     */
    private void logSaveLog(Long appId, String appDir, CodeGenTypeEnum genType) {
        SaveLogDO logDO = new SaveLogDO();
        logDO.setAppId(appId);
        logDO.setSavePath(appDir);
        logDO.setGenType(genType.name());
        logDO.setSaveTime(LocalDateTime.now());
        saveLogMapper.insert(logDO);
    }
}

2. 实现HTML单文件保存子类

/**
 * HTML单文件保存子类(定制可变步骤)
 */
public class HtmlCodeFileSaverTemplate extends CodeFileSaverTemplate<HtmlCodeResult> {

    @Autowired
    private FileStorageService fileStorageService;

    /**
     * 重写输入验证:增加HTML代码非空校验
     */
    @Override
    protected boolean validateInput(HtmlCodeResult codeData) {
        if (super.validateInput(codeData)) {
            return StringUtils.isNotBlank(codeData.getHtmlContent());
        }
        return false;
    }

    /**
     * 实现核心步骤:保存HTML单文件
     */
    @Override
    protected void saveFiles(CodeGenTypeEnum genType, String appDir, HtmlCodeResult codeData) {
        // 拼接文件路径
        String htmlPath = appDir + "/index.html";
        // 写入HTML文件(调用文件存储SDK)
        fileStorageService.writeFile(htmlPath, codeData.getHtmlContent());
        log.info("HTML文件保存成功:{}", htmlPath);
    }
}

3. 实现多文件保存子类

/**
 * 多文件(HTML/CSS/JS)保存子类(定制可变步骤)
 */
public class MultiFileCodeFileSaverTemplate extends CodeFileSaverTemplate<MultiFileCodeResult> {

    @Autowired
    private FileStorageService fileStorageService;

    /**
     * 实现核心步骤:保存HTML/CSS/JS三个文件
     */
    @Override
    protected void saveFiles(CodeGenTypeEnum genType, String appDir, MultiFileCodeResult codeData) {
        // 1. 保存HTML文件
        String htmlPath = appDir + "/index.html";
        fileStorageService.writeFile(htmlPath, codeData.getHtmlContent());

        // 2. 保存CSS文件
        String cssPath = appDir + "/style.css";
        fileStorageService.writeFile(cssPath, codeData.getCssContent());

        // 3. 保存JS文件
        String jsPath = appDir + "/script.js";
        fileStorageService.writeFile(jsPath, codeData.getJsContent());

        log.info("多文件保存成功:{}(HTML/CSS/JS)", appDir);
    }
}

4. 定义执行器(统一调用入口)

/**
 * 文件保存执行器(屏蔽子类差异,上层统一调用)
 */
@Service
public class CodeFileSaverExecutor {

    // 注入所有模板子类(Spring自动注入)
    @Autowired
    private HtmlCodeFileSaverTemplate htmlSaverTemplate;
    @Autowired
    private MultiFileCodeFileSaverTemplate multiFileSaverTemplate;

    /**
     * 统一保存入口:根据生成类型选择对应模板子类
     */
    public boolean save(CodeGenTypeEnum genType, Long appId, Object codeData) {
        switch (genType) {
            case HTML:
                return htmlSaverTemplate.saveCode(genType, appId, (HtmlCodeResult) codeData);
            case MULTI_FILE:
                return multiFileSaverTemplate.saveCode(genType, appId, (MultiFileCodeResult) codeData);
            default:
                throw new IllegalArgumentException("不支持的生成类型:" + genType);
        }
    }
}

5. 枚举类与数据模型(辅助类)

// 代码生成类型枚举
public enum CodeGenTypeEnum {
    HTML, MULTI_FILE
}

// HTML代码结果模型
@Data
public class HtmlCodeResult {
    private String htmlContent;
}

// 多文件代码结果模型
@Data
public class MultiFileCodeResult {
    private String htmlContent;
    private String cssContent;
    private String jsContent;
}

二、业务层调用示例

@Service
public class CodeGenerateService {

    @Autowired
    private CodeFileSaverExecutor saverExecutor;

    /**
     * 生成并保存代码(上层业务调用,无需关注具体保存逻辑)
     */
    public void generateAndSaveCode(Long appId, CodeGenTypeEnum genType, String rawCode) {
        // 1. 解析代码(策略模式处理,此处简化)
        Object codeData = parseCode(genType, rawCode);

        // 2. 调用执行器保存代码(模板方法模式)
        boolean saveResult = saverExecutor.save(genType, appId, codeData);

        if (saveResult) {
            log.info("应用{}代码生成并保存成功", appId);
        } else {
            log.error("应用{}代码保存失败", appId);
        }
    }

    // 简化的代码解析逻辑(实际为策略模式实现)
    private Object parseCode(CodeGenTypeEnum genType, String rawCode) {
        if (genType == CodeGenTypeEnum.HTML) {
            HtmlCodeResult result = new HtmlCodeResult();
            result.setHtmlContent(rawCode);
            return result;
        } else {
            MultiFileCodeResult result = new MultiFileCodeResult();
            result.setHtmlContent("<html>...</html>");
            result.setCssContent("body { margin: 0; }");
            result.setJsContent("console.log('hello');");
            return result;
        }
    }
}

三、核心设计亮点

  1. 流程固化saveCode 模板方法用 final 修饰,保证所有子类都遵循“验证→建目录→保存→日志”的统一流程;
  2. 步骤拆分:将固定步骤(建目录、记日志)放在父类实现,可变步骤(验证、保存文件)延迟到子类;
  3. 扩展性强:新增Vue工程保存逻辑时,只需新增 VueCodeFileSaverTemplate 子类,实现 saveFiles 方法即可,无需修改原有代码;
  4. 职责清晰:父类管控流程,子类聚焦具体文件保存逻辑,符合“单一职责原则”。

四、关键点回顾

  1. 模板方法模式的核心是抽象父类定义固定流程骨架,子类定制可变步骤,通过 final 模板方法保证流程不被篡改;
  2. 本场景中,文件保存的“建目录、记日志”是固定步骤,“输入验证、保存文件”是可变步骤;
  3. 执行器封装了子类的选择逻辑,上层业务无需关注具体子类实现,仅调用统一入口即可。
标题 AI零代码应用生成项目中,你是如何实现集中式工具管理的?
推荐答案内容

在项目中,随着提供给AI的工具越来越多,如果分散管理,会导致代码混乱且难以维护。
因此,我们设计了一套集中式的工具管理机制,其核心是 ToolManager 类 + Spring框架的依赖注入 + 设计模式。

整体架构如下:
(架构图说明:以ToolManager(工厂模式)为核心,基于BaseTool策略接口,管理FileWriteTool(文件写入工具)、FileReadTool(文件读取工具)、FileModifyTool(文件修改工具)、FileDeleteTool(文件删除工具)、FileDirReadTool(目录读取工具)等具体工具,各工具对应不同输出内容)

具体实现步骤
  1. 定义工具基类:我们首先创建了一个抽象的 BaseTool 基类。这个基类定义了所有工具都必须具备的通用接口和行为,例如 getToolName()getDisplayName(),以及生成不同阶段向用户展示信息的统一方法。

  2. 实现具体工具:项目中的每一个具体工具,如 FileWriteToolFileReadToolFileModifyTool 等,都继承自 BaseTool 基类,并被声明为Spring的Bean。这样,它们就可以被Spring容器自动扫描和管理。

  3. 创建工具管理器:这是集中管理的核心。

    • ToolManager 本身也是一个Spring Bean。
    • 它通过 @Resource 注解,注入了一个 BaseTool 类型的数组。这是关键一步,Spring会自动将容器中所有继承了 BaseTool 的Bean实例收集起来,并注入到这个数组中。
    • @PostConstruct 初始化方法中,ToolManager 会遍历这个工具数组,将每个工具实例以其 toolName 为键,存入一个内部的 Map 中,方便后续通过名称快速查找。
  4. 统一使用:

    • ToolManager 提供了 getTool(String toolName) 方法,可以根据名称快速获取任何一个已注册的工具实例。
    • 它还提供了 getAllTools() 方法,直接返回包含所有工具实例的数组,这个方法专门用于为LangChain4j的AI Service统一注册所有可用工具。

通过这种方式,我们实现了工具的自动注册和集中管理。未来如果需要新增一个工具,我们只需要创建一个新的类继承 BaseTool 并加上 @Component 注解即可,无需修改任何现有管理和注册逻辑的代码。

AI零代码项目-集中式工具管理完整代码示例

一、核心设计思路

基于工厂模式+Spring依赖注入实现工具的集中管理,核心是通过抽象基类统一规范、工具管理器自动扫描注册、上层业务统一调用,实现工具的“新增无需改旧代码”。

二、完整代码实现

1. 定义工具抽象基类(统一规范)

/**
 * 所有AI工具的抽象基类(统一接口规范)
 */
public abstract class BaseTool {

    /**
     * 获取工具唯一标识(用于Map映射)
     */
    public abstract String getToolName();

    /**
     * 获取工具展示名称(用于前端/日志展示)
     */
    public abstract String getDisplayName();

    /**
     * 工具核心执行方法(子类必须实现)
     * @param params 工具执行参数
     * @return 执行结果
     */
    public abstract String execute(Map<String, Object> params);

    /**
     * 通用方法:生成工具执行日志(所有工具共用)
     */
    public String generateLog(String toolName, boolean success) {
        return String.format("[%s]工具执行%s,时间:%s",
                toolName,
                success ? "成功" : "失败",
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }
}

2. 实现具体工具类(文件操作工具示例)

(1)文件读取工具
/**
 * 文件读取工具(具体工具实现)
 */
@Component // 声明为Spring Bean,自动被扫描
public class FileReadTool extends BaseTool {

    @Autowired
    private FileStorageService fileStorageService;

    @Override
    public String getToolName() {
        return "fileReadTool"; // 工具唯一标识
    }

    @Override
    public String getDisplayName() {
        return "文件读取工具"; // 友好展示名
    }

    @Override
    public String execute(Map<String, Object> params) {
        try {
            // 解析参数
            Long appId = Long.parseLong(params.get("appId").toString());
            String filePath = params.get("filePath").toString();

            // 核心逻辑:读取文件内容
            String fullPath = String.format("/app/%s/%s", appId, filePath);
            String content = fileStorageService.readFile(fullPath);

            // 生成执行日志
            System.out.println(generateLog(getToolName(), true));
            return content;
        } catch (Exception e) {
            System.out.println(generateLog(getToolName(), false));
            return "文件读取失败:" + e.getMessage();
        }
    }
}
(2)文件写入工具
/**
 * 文件写入工具(具体工具实现)
 */
@Component
public class FileWriteTool extends BaseTool {

    @Autowired
    private FileStorageService fileStorageService;

    @Override
    public String getToolName() {
        return "fileWriteTool";
    }

    @Override
    public String getDisplayName() {
        return "文件写入工具";
    }

    @Override
    public String execute(Map<String, Object> params) {
        try {
            Long appId = Long.parseLong(params.get("appId").toString());
            String filePath = params.get("filePath").toString();
            String content = params.get("content").toString();
            boolean overwrite = Boolean.parseBoolean(params.get("overwrite").toString());

            String fullPath = String.format("/app/%s/%s", appId, filePath);
            fileStorageService.writeFile(fullPath, content, overwrite);

            System.out.println(generateLog(getToolName(), true));
            return "文件写入成功";
        } catch (Exception e) {
            System.out.println(generateLog(getToolName(), false));
            return "文件写入失败:" + e.getMessage();
        }
    }
}
(3)文件修改工具(新增工具示例,仅需新增类)
/**
 * 文件修改工具(新增工具,无需修改任何管理代码)
 */
@Component
public class FileModifyTool extends BaseTool {

    @Autowired
    private FileStorageService fileStorageService;

    @Override
    public String getToolName() {
        return "fileModifyTool";
    }

    @Override
    public String getDisplayName() {
        return "文件修改工具";
    }

    @Override
    public String execute(Map<String, Object> params) {
        try {
            Long appId = Long.parseLong(params.get("appId").toString());
            String filePath = params.get("filePath").toString();
            String oldContent = params.get("oldContent").toString();
            String newContent = params.get("newContent").toString();

            String fullPath = String.format("/app/%s/%s", appId, filePath);
            fileStorageService.modifyFile(fullPath, oldContent, newContent);

            System.out.println(generateLog(getToolName(), true));
            return "文件修改成功";
        } catch (Exception e) {
            System.out.println(generateLog(getToolName(), false));
            return "文件修改失败:" + e.getMessage();
        }
    }
}

3. 实现工具管理器(核心:集中注册与分发)

/**
 * 工具管理器(集中式管理核心)
 */
@Component // 声明为Spring Bean
public class ToolManager {

    // Spring自动注入所有继承BaseTool的Bean
    @Resource
    private BaseTool[] allTools;

    // 存储工具映射:toolName -> BaseTool实例
    private Map<String, BaseTool> toolMap;

    /**
     * 初始化方法:Spring容器加载完成后,构建工具映射表
     */
    @PostConstruct
    public void init() {
        toolMap = new HashMap<>();
        // 遍历所有工具,注册到Map中
        for (BaseTool tool : allTools) {
            toolMap.put(tool.getToolName(), tool);
            System.out.println("工具注册成功:" + tool.getToolName() + "(" + tool.getDisplayName() + ")");
        }
    }

    /**
     * 根据工具名称获取工具实例
     */
    public BaseTool getTool(String toolName) {
        BaseTool tool = toolMap.get(toolName);
        if (tool == null) {
            throw new IllegalArgumentException("不存在的工具:" + toolName);
        }
        return tool;
    }

    /**
     * 获取所有工具实例(用于LangChain4j注册)
     */
    public BaseTool[] getAllTools() {
        return allTools;
    }

    /**
     * 统一执行工具方法(上层业务调用入口)
     */
    public String executeTool(String toolName, Map<String, Object> params) {
        BaseTool tool = getTool(toolName);
        return tool.execute(params);
    }
}

4. 模拟文件存储服务(工具依赖的底层服务)

/**
 * 模拟文件存储服务(实际项目中替换为OSS/本地文件系统SDK)
 */
@Service
public class FileStorageService {

    // 读取文件
    public String readFile(String fullPath) {
        // 模拟读取逻辑
        return "模拟读取文件内容:" + fullPath;
    }

    // 写入文件
    public void writeFile(String fullPath, String content, boolean overwrite) {
        // 模拟写入逻辑
        System.out.println(String.format("写入文件[%s],覆盖:%s,内容:%s", fullPath, overwrite, content));
    }

    // 修改文件
    public void modifyFile(String fullPath, String oldContent, String newContent) {
        // 模拟修改逻辑
        System.out.println(String.format("修改文件[%s],替换[%s]为[%s]", fullPath, oldContent, newContent));
    }
}

5. 业务层调用示例(上层使用)

/**
 * 业务层调用示例
 */
@Service
public class AiToolService {

    @Autowired
    private ToolManager toolManager;

    /**
     * 调用文件读取工具
     */
    public String readFile(Long appId, String filePath) {
        Map<String, Object> params = new HashMap<>();
        params.put("appId", appId);
        params.put("filePath", filePath);
        // 统一调用工具管理器
        return toolManager.executeTool("fileReadTool", params);
    }

    /**
     * 为LangChain4j注册所有工具(核心场景)
     */
    public void registerToolsToAi() {
        // 获取所有工具实例,注册给AI
        BaseTool[] tools = toolManager.getAllTools();
        for (BaseTool tool : tools) {
            System.out.println("为AI注册工具:" + tool.getDisplayName());
            // 实际项目中调用LangChain4j的工具注册API
            // AiServices.builder().tools(tool).build();
        }
    }
}

6. 测试类(验证功能)

/**
 * 测试集中式工具管理
 */
@SpringBootTest
public class ToolManagerTest {

    @Autowired
    private AiToolService aiToolService;

    @Test
    public void testExecuteTool() {
        // 1. 测试文件读取工具
        String readResult = aiToolService.readFile(123456L, "src/index.html");
        System.out.println("文件读取结果:" + readResult);

        // 2. 测试注册所有工具到AI
        aiToolService.registerToolsToAi();
    }
}

三、核心优势与扩展说明

1. 核心优势

  • 自动注册:新增工具只需继承BaseTool并加@Component,Spring自动扫描,工具管理器自动注册;
  • 集中管理:所有工具通过ToolManager统一调用,避免工具分散导致的代码混乱;
  • 低耦合:上层业务仅依赖ToolManager,不直接依赖具体工具类,符合“依赖倒置原则”。

2. 扩展示例(新增工具)

如果需要新增“文件删除工具”,仅需新增以下类,无需修改任何现有代码:

@Component
public class FileDeleteTool extends BaseTool {

    @Autowired
    private FileStorageService fileStorageService;

    @Override
    public String getToolName() {
        return "fileDeleteTool";
    }

    @Override
    public String getDisplayName() {
        return "文件删除工具";
    }

    @Override
    public String execute(Map<String, Object> params) {
        try {
            Long appId = Long.parseLong(params.get("appId").toString());
            String filePath = params.get("filePath").toString();

            String fullPath = String.format("/app/%s/%s", appId, filePath);
            fileStorageService.deleteFile(fullPath);

            System.out.println(generateLog(getToolName(), true));
            return "文件删除成功";
        } catch (Exception e) {
            System.out.println(generateLog(getToolName(), false));
            return "文件删除失败:" + e.getMessage();
        }
    }
}

四、关键点回顾

  1. 集中式工具管理的核心是抽象基类+Spring自动注入+工具管理器,通过@Resource注入所有子类Bean,@PostConstruct构建映射表;
  2. 工具管理器提供getTool(单工具获取)、getAllTools(批量注册)、executeTool(统一执行)三个核心方法;
  3. 新增工具仅需实现基类并声明为Spring Bean,完全符合“开闭原则”,无需修改现有管理逻辑。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐