2026山东大学软件学院项目实训(二)|程序处理写入与门面模式深度落地

模块概述

在我们的LangChain4j+工作流+微服务AI零代码应用生成平台中,程序处理写入模块承接AI结构化输出的代码结果,负责将HTML/CSS/JS代码写入本地文件系统;而门面模式是本模块的核心设计思路,用于统一AI代码生成与文件保存的复杂流程,降低系统耦合、简化外部调用。

本文将带你从零到一实现该模块,详解代码逻辑与设计模式落地。


一、模块背景:为什么需要门面模式?

经过前面的开发,我们已经实现:

  1. AI通过LangChain4j生成结构化代码结果(单HTML文件/多文件模式)
  2. 代码封装为HtmlCodeResult/MultiFileCodeResult对象

但此时面临两个核心问题:

  1. 流程分散:代码生成、文件解析、目录创建、文件写入是多个独立步骤,客户端需手动编排,极易出错
  2. 耦合过高:外部调用需直接依赖AI服务、文件工具类,子系统变更会影响调用方

门面模式(Facade Pattern) 恰好解决这类问题:

为子系统提供统一高层接口,隐藏内部复杂逻辑,让客户端只与门面类交互,实现解耦与简化调用。

在本模块中:

  • 子系统:AI代码生成服务、文件保存工具类
  • 门面类:AiCodeGeneratorFacade
  • 客户端:测试类/后续Controller层

二、第一步:定义代码生成类型枚举

首先定义代码生成类型枚举,统一管理两种生成模式,避免硬编码,提升可维护性。

2.1 枚举代码实现

import lombok.Getter;

/**
 * 代码生成类型枚举
 * 用于区分原生HTML单文件、原生多文件两种生成模式
 */
@Getter
public enum CodeGenTypeEnum {
    /**
     * 原生HTML模式:所有代码整合在一个HTML文件中
     */
    HTML("原生HTML模式", "html"),

    /**
     * 原生多文件模式:分离HTML、CSS、JS三个文件
     */
    MULTI_FILE("原生多文件模式", "multi_file");

    // 文字描述
    private final String text;
    // 枚举值
    private final String value;

    CodeGenTypeEnum(String text, String value) {
        this.text = text;
        this.value = value;
    }

    /**
     * 根据value获取枚举
     * @param value 枚举值
     * @return 对应的枚举对象
     */
    public static CodeGenTypeEnum getEnumByValue(String value) {
        // 空值判断
        if (ObjectUtil.isEmpty(value)) {
            return null;
        }
        // 遍历匹配
        for (CodeGenTypeEnum anEnum : CodeGenTypeEnum.values()) {
            if (anEnum.value.equals(value)) {
                return anEnum;
            }
        }
        return null;
    }
}

2.2 枚举作用

  1. 统一两种生成模式的标识,避免魔法值
  2. 提供根据value匹配枚举的工具方法,方便后续流程判断

三、第二步:编写文件保存工具类CodeFileSaver

该工具类负责目录创建、文件写入、两种模式的代码保存,是核心工具层。

3.1 核心设计要点

  1. 保存根目录:项目根目录下的/tmp/code_output,避免污染业务目录
  2. 唯一目录:采用业务类型+雪花ID命名,确保目录不重复
  3. 两种保存逻辑:适配单HTML、多文件模式
  4. 编码统一:使用UTF-8写入,避免乱码
  5. 忽略提交:将tmp目录加入.gitignore,防止生成文件提交到仓库

3.2 工具类代码实现

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.IdUtil;
import cn.hutool.core.util.StrUtil;
import java.io.File;
import java.nio.charset.StandardCharsets;

/**
 * 代码文件保存工具类
 * 负责将AI生成的结构化代码写入本地文件系统
 */
public class CodeFileSaver {
    /**
     * 文件保存根目录:项目根目录/tmp/code_output
     */
    private static final String FILE_SAVE_ROOT_DIR = System.getProperty("user.dir") + "/tmp/code_output";

    /**
     * 保存单HTML文件结果
     * @param result HTML代码结果对象
     * @return 保存后的目录文件
     */
    public static File saveHtmlCodeResult(HtmlCodeResult result) {
        // 构建唯一目录
        String baseDirPath = buildUniqueDir(CodeGenTypeEnum.HTML.getValue());
        // 写入HTML文件
        writeToFile(baseDirPath, "index.html", result.getHtmlCode());
        return new File(baseDirPath);
    }

    /**
     * 保存多文件(HTML+CSS+JS)结果
     * @param result 多文件代码结果对象
     * @return 保存后的目录文件
     */
    public static File saveMultiFileCodeResult(MultiFileCodeResult result) {
        // 构建唯一目录
        String baseDirPath = buildUniqueDir(CodeGenTypeEnum.MULTI_FILE.getValue());
        // 分别写入三个文件
        writeToFile(baseDirPath, "index.html", result.getHtmlCode());
        writeToFile(baseDirPath, "style.css", result.getCssCode());
        writeToFile(baseDirPath, "script.js", result.getJsCode());
        return new File(baseDirPath);
    }

    /**
     * 构建唯一目录路径:规则 业务类型_雪花ID
     * @param bizType 业务类型(html/multi_file)
     * @return 唯一目录路径
     */
    private static String buildUniqueDir(String bizType) {
        // 生成唯一目录名
        String uniqueDirName = StrUtil.format("{}_{}", bizType, IdUtil.getSnowflakeNextIdStr());
        // 拼接完整路径
        String dirPath = FILE_SAVE_ROOT_DIR + File.separator + uniqueDirName;
        // 创建目录(不存在则创建)
        FileUtil.mkdir(dirPath);
        return dirPath;
    }

    /**
     * 写入单个文件
     * @param dirPath 目录路径
     * @param filename 文件名
     * @param content 文件内容
     */
    private static void writeToFile(String dirPath, String filename, String content) {
        String filePath = dirPath + File.separator + filename;
        // 按UTF-8编码写入字符串
        FileUtil.writeString(content, filePath, StandardCharsets.UTF_8);
    }
}

3.3 关键细节说明

  1. 雪花ID:使用Hutool工具类生成全局唯一ID,避免目录冲突
  2. 目录自动创建FileUtil.mkdir会自动创建不存在的目录
  3. 编码规范:强制使用StandardCharsets.UTF-8,解决跨系统乱码问题
  4. 职责单一:仅负责文件写入,不耦合业务逻辑

四、第三步:门面模式落地——AiCodeGeneratorFacade

这是本模块的核心,通过门面类统一生成+保存流程,对外提供极简调用入口。

4.1 门面类设计思路

  1. 注入AI代码生成服务AiCodeGeneratorService
  2. 提供统一入口方法:根据生成类型自动选择对应逻辑
  3. 封装异常处理,抛出统一业务异常
  4. 隐藏内部调用细节,客户端只需传入用户描述+生成类型

4.2 门面类代码实现

import com.yupi.yuaicodemother.ai.service.AiCodeGeneratorService;
import com.yupi.yuaicodemother.core.exception.BusinessException;
import com.yupi.yuaicodemother.core.model.enums.CodeGenTypeEnum;
import com.yupi.yuaicodemother.ai.model.result.HtmlCodeResult;
import com.yupi.yuaicodemother.ai.model.result.MultiFileCodeResult;
import lombok.Resource;
import org.springframework.stereotype.Service;
import java.io.File;

/**
 * AI代码生成门面类
 * 统一封装代码生成、文件保存逻辑,简化外部调用
 */
@Service
public class AiCodeGeneratorFacade {
    /**
     * 注入AI代码生成服务
     */
    @Resource
    private AiCodeGeneratorService aiCodeGeneratorService;

    /**
     * 统一入口:根据类型生成并保存代码
     * @param userMessage 用户输入的网站描述
     * @param codeGenTypeEnum 代码生成类型枚举
     * @return 保存后的文件目录
     */
    public File generateAndSaveCode(String userMessage, CodeGenTypeEnum codeGenTypeEnum) {
        // 空值校验
        if (codeGenTypeEnum == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成类型为空");
        }
        // 根据枚举类型选择对应逻辑
        return switch (codeGenTypeEnum) {
            // 单HTML模式
            case HTML -> generateAndSaveHtmlCode(userMessage);
            // 多文件模式
            case MULTI_FILE -> generateAndSaveMultiFileCode(userMessage);
            // 不支持的类型
            default -> {
                String errorMessage = "不支持的生成类型:" + codeGenTypeEnum.getValue();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
            }
        };
    }

    /**
     * 生成单HTML模式代码并保存
     * @param userMessage 用户描述
     * @return 保存目录
     */
    private File generateAndSaveHtmlCode(String userMessage) {
        // 1. 调用AI生成结构化代码
        HtmlCodeResult result = aiCodeGeneratorService.generateHtmlCode(userMessage);
        // 2. 调用工具类保存文件
        return CodeFileSaver.saveHtmlCodeResult(result);
    }

    /**
     * 生成多文件模式代码并保存
     * @param userMessage 用户描述
     * @return 保存目录
     */
    private File generateAndSaveMultiFileCode(String userMessage) {
        // 1. 调用AI生成结构化代码
        MultiFileCodeResult result = aiCodeGeneratorService.generateMultiFileCode(userMessage);
        // 2. 调用工具类保存文件
        return CodeFileSaver.saveMultiFileCodeResult(result);
    }
}

4.3 门面类核心价值

  1. 统一入口:外部只需调用generateAndSaveCode一个方法,无需关心内部流程
  2. 解耦依赖:客户端不直接依赖AiCodeGeneratorServiceCodeFileSaver
  3. 易于维护:新增生成类型只需修改枚举和门面类,不影响调用方
  4. 异常统一:集中处理参数校验、异常抛出,规范错误提示

五、第四步:单元测试验证全流程

编写测试类,验证AI生成+文件保存全流程是否正常。

import com.yupi.yuaicodemother.core.model.enums.CodeGenTypeEnum;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.io.File;
import static org.junit.jupiter.api.Assertions.assertNotNull;

/**
 * AI代码生成门面类测试
 * 验证生成+保存全流程
 */
@SpringBootTest
class AiCodeGeneratorFacadeTest {
    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;

    /**
     * 测试多文件模式生成并保存
     */
    @Test
    void generateAndSaveCode() {
        // 调用门面类统一方法
        File file = aiCodeGeneratorFacade.generateAndSaveCode("任务记录网站", CodeGenTypeEnum.MULTI_FILE);
        // 断言文件不为空
        assertNotNull(file);
        // 控制台打印保存路径
        System.out.println("文件保存路径:" + file.getAbsolutePath());
    }
}

测试效果

  1. 控制台输出唯一保存路径
  2. 打开路径可看到生成的index.html/style.css/script.js
  3. 双击index.html可直接预览AI生成的网站

六、门面模式在本模块的核心价值总结

问题 未用门面模式 使用门面模式后
调用复杂度 需手动调用AI服务+文件工具,步骤繁琐 仅需调用一个方法,极简入口
系统耦合 客户端依赖多个子系统,变更影响大 客户端只依赖门面类,解耦彻底
可维护性 代码分散,新增模式需修改多处 集中管理流程,符合开闭原则
代码可读性 逻辑散乱,难以快速理解流程 门面类清晰编排流程,一目了然

本质上,本模块的门面模式遵循迪米特法则(最少知识原则)
客户端只需要知道做什么(生成并保存代码),不需要知道怎么做(AI调用、目录创建、文件写入)。


七、模块总结

本模块通过枚举+工具类+门面模式,完美实现了AI代码生成后的本地文件落地

  1. 用枚举统一生成类型,规范代码
  2. 用工具类封装文件写入,职责单一
  3. 用门面模式统一流程,简化调用、降低耦合
Logo

一站式 AI 云服务平台

更多推荐