Browse Source

代码生成模板 预览新和 导出新

zrd 3 months ago
parent
commit
24985eed04

+ 27 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java

@@ -130,6 +130,15 @@ public class CodegenController {
         Map<String, String> codes = codegenService.generationCodes(tableId);
         return success(CodegenConvert.INSTANCE.convert(codes));
     }
+    
+    @Operation(summary = "预览生成代码新")
+    @GetMapping("/previewNew")
+    @Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('infra:codegen:preview')")
+    public CommonResult<List<CodegenPreviewRespVO>> previewCodegenNew(@RequestParam("tableId") Long tableId) {
+        Map<String, String> codes = codegenService.generationCodesNew(tableId);
+        return success(CodegenConvert.INSTANCE.convert(codes));
+    }
 
     @Operation(summary = "下载生成代码")
     @GetMapping("/download")
@@ -147,5 +156,23 @@ public class CodegenController {
         // 输出
         writeAttachment(response, "codegen.zip", outputStream.toByteArray());
     }
+    
+    @Operation(summary = "下载生成代码新")
+    @GetMapping("/downloadNew")
+    @Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('infra:codegen:download')")
+    public void downloadCodegenNew(@RequestParam("tableId") Long tableId,
+                                   HttpServletResponse response) throws IOException {
+        // 生成代码
+        Map<String, String> codes = codegenService.generationCodesNew(tableId);
+        // 构建 zip 包
+        String[] paths = codes.keySet().toArray(new String[0]);
+        ByteArrayInputStream[] ins =
+                codes.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ZipUtil.zip(outputStream, paths, ins);
+        // 输出
+        writeAttachment(response, "codegen.zip", outputStream.toByteArray());
+    }
 
 }

+ 2 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java

@@ -87,6 +87,8 @@ public interface CodegenService {
      * @return 生成结果。key 为文件路径,value 为对应的代码内容
      */
     Map<String, String> generationCodes(Long tableId);
+    
+    Map<String, String> generationCodesNew(Long tableId);
 
     /**
      * 获得数据库自带的表定义列表

+ 47 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java

@@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateLi
 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
+import cn.iocoder.yudao.module.infra.controller.admin.codegentemple.vo.CodegenTemplePageReqVO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.codegentemple.CodegenTempleDO;
 import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
 import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
@@ -17,6 +19,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
 import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
 import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
 import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
+import cn.iocoder.yudao.module.infra.service.codegentemple.CodegenTempleService;
 import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import com.baomidou.mybatisplus.generator.config.po.TableField;
@@ -46,7 +49,9 @@ public class CodegenServiceImpl implements CodegenService {
 
     @Resource
     private DatabaseTableService databaseTableService;
-
+    @Resource
+    private CodegenTempleService codegenTempleService;
+    
     @Resource
     private CodegenTableMapper codegenTableMapper;
     @Resource
@@ -282,6 +287,47 @@ public class CodegenServiceImpl implements CodegenService {
         // 执行生成
         return codegenEngine.execute(table, columns, subTables, subColumnsList);
     }
+    
+    @Override
+    public Map<String, String> generationCodesNew(Long tableId) {
+        // 校验是否已经存在
+        CodegenTableDO table = codegenTableMapper.selectById(tableId);
+        if (table == null) {
+            throw exception(CODEGEN_TABLE_NOT_EXISTS);
+        }
+        List<CodegenColumnDO> columns = codegenColumnMapper.selectListByTableId(tableId);
+        if (CollUtil.isEmpty(columns)) {
+            throw exception(CODEGEN_COLUMN_NOT_EXISTS);
+        }
+        
+        // 如果是主子表,则加载对应的子表信息
+        List<CodegenTableDO> subTables = null;
+        List<List<CodegenColumnDO>> subColumnsList = null;
+        if (CodegenTemplateTypeEnum.isMaster(table.getTemplateType())) {
+            // 校验子表存在
+            subTables = codegenTableMapper.selectListByTemplateTypeAndMasterTableId(
+                    CodegenTemplateTypeEnum.SUB.getType(), tableId);
+            if (CollUtil.isEmpty(subTables)) {
+                throw exception(CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE);
+            }
+            // 校验子表的关联字段存在
+            subColumnsList = new ArrayList<>();
+            for (CodegenTableDO subTable : subTables) {
+                List<CodegenColumnDO> subColumns = codegenColumnMapper.selectListByTableId(subTable.getId());
+                if (CollUtil.findOne(subColumns, column -> column.getId().equals(subTable.getSubJoinColumnId())) == null) {
+                    throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId());
+                }
+                subColumnsList.add(subColumns);
+            }
+        }
+        CodegenTemplePageReqVO pageReqVO = new CodegenTemplePageReqVO();
+        pageReqVO.setPageNo(-1);
+        pageReqVO.setScene(table.getScene());
+        PageResult<CodegenTempleDO> rs = codegenTempleService.getCodegenTemplePage(pageReqVO);
+        List<CodegenTempleDO> codegenTempleDOs = rs.getList();
+        // 执行生成
+        return codegenEngine.executeNew(codegenTempleDOs, table, columns, subTables, subColumnsList);
+    }
 
     @Override
     public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {

+ 135 - 85
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java

@@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.template.Template;
 import cn.hutool.extra.template.TemplateConfig;
 import cn.hutool.extra.template.TemplateEngine;
+import cn.hutool.extra.template.TemplateUtil;
 import cn.hutool.extra.template.engine.velocity.VelocityEngine;
 import cn.hutool.system.SystemUtil;
 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
@@ -28,6 +30,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.codegentemple.CodegenTempleDO;
 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
@@ -145,10 +148,16 @@ public class CodegenEngine {
             .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("api/api.ts"),
                     vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
             .build();
-
+    /**
+     * 模板引擎,由 hutool 实现
+     */
+    private final TemplateEngine templateEngine;
+    /**
+     * 全局通用变量映射
+     */
+    private final Map<String, Object> globalBindingMap = new HashMap<>();
     @Resource
     private CodegenProperties codegenProperties;
-
     /**
      * 是否使用 jakarta 包,用于解决 Spring Boot 2.X 和 3.X 的兼容性问题
      *
@@ -158,15 +167,6 @@ public class CodegenEngine {
     @Setter // 允许设置的原因,是因为单测需要手动改变
     private Boolean jakartaEnable;
 
-    /**
-     * 模板引擎,由 hutool 实现
-     */
-    private final TemplateEngine templateEngine;
-    /**
-     * 全局通用变量映射
-     */
-    private final Map<String, Object> globalBindingMap = new HashMap<>();
-
     public CodegenEngine() {
         // 初始化 TemplateEngine 属性
         TemplateConfig config = new TemplateConfig();
@@ -175,7 +175,79 @@ public class CodegenEngine {
         // 设置 javaxEnable,按照是否使用 JDK17 来判断
         this.jakartaEnable = SystemUtil.getJavaInfo().isJavaVersionAtLeast(1700); // 17.00 * 100
     }
-
+    
+    private static String javaTemplatePath(String path) {
+        return "codegen/java/" + path + ".vm";
+    }
+    
+    private static String javaModuleImplVOFilePath(String path) {
+        return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
+                "vo/${sceneEnum.prefixClass}${table.className}" + path, "biz", "main");
+    }
+    
+    private static String javaModuleImplControllerFilePath() {
+        return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
+                "${sceneEnum.prefixClass}${table.className}Controller", "biz", "main");
+    }
+    
+    private static String javaModuleImplMainFilePath(String path) {
+        return javaModuleFilePath(path, "biz", "main");
+    }
+    
+    private static String javaModuleApiMainFilePath(String path) {
+        return javaModuleFilePath(path, "api", "main");
+    }
+    
+    private static String javaModuleImplTestFilePath(String path) {
+        return javaModuleFilePath(path, "biz", "test");
+    }
+    
+    private static String javaModuleFilePath(String path, String module, String src) {
+        return "yudao-module-${table.moduleName}/" + // 顶级模块
+                "yudao-module-${table.moduleName}-" + module + "/" + // 子模块
+                "src/" + src + "/java/${basePackage}/module/${table.moduleName}/" + path + ".java";
+    }
+    
+    private static String mapperXmlFilePath() {
+        return "yudao-module-${table.moduleName}/" + // 顶级模块
+                "yudao-module-${table.moduleName}-biz/" + // 子模块
+                "src/main/resources/mapper/${table.businessName}/${table.className}Mapper.xml";
+    }
+    
+    private static String vueTemplatePath(String path) {
+        return "codegen/vue/" + path + ".vm";
+    }
+    
+    private static String vueFilePath(String path) {
+        return "yudao-ui-${sceneEnum.basePackage}-vue2/" + // 顶级目录
+                "src/" + path;
+    }
+    
+    private static String vue3TemplatePath(String path) {
+        return "codegen/vue3/" + path + ".vm";
+    }
+    
+    private static String vue3FilePath(String path) {
+        return "yudao-ui-${sceneEnum.basePackage}-vue3/" + // 顶级目录
+                "src/" + path;
+    }
+    
+    private static String vue3VbenTemplatePath(String path) {
+        return "codegen/vue3_vben/" + path + ".vm";
+    }
+    
+    private static boolean isSubTemplate(String path) {
+        return path.contains("_sub");
+    }
+    
+    private static boolean isPageReqVOTemplate(String path) {
+        return path.contains("pageReqVO");
+    }
+    
+    private static boolean isListReqVOTemplate(String path) {
+        return path.contains("listReqVO");
+    }
+    
     @PostConstruct
     @VisibleForTesting
     void initGlobalBindingMap() {
@@ -247,6 +319,32 @@ public class CodegenEngine {
         });
         return result;
     }
+    
+    /**
+     * 生成代码
+     *
+     * @param table          表定义
+     * @param columns        table 的字段定义数组
+     * @param subTables      子表数组,当且仅当主子表时使用
+     * @param subColumnsList subTables 的字段定义数组
+     * @return 生成的代码,key 是路径,value 是对应代码
+     */
+    public Map<String, String> executeNew(List<CodegenTempleDO> templates, CodegenTableDO table,
+                                          List<CodegenColumnDO> columns,
+                                          List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {
+        // 1.1 初始化 bindMap 上下文
+        Map<String, Object> bindingMap = initBindingMap(table, columns, subTables, subColumnsList);
+        // 1.2 获得模版
+        //   Map<String, String> templates = getTemplates(table.getFrontType());
+        
+        // 2. 执行生成
+        Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(templates.size()); // 有序
+        
+        for (CodegenTempleDO template : templates) {
+            generateCodeNew(result, template, bindingMap);
+        }
+        return result;
+    }
 
     private void generateCode(Map<String, String> result, String vmPath,
                               String filePath, Map<String, Object> bindingMap) {
@@ -256,7 +354,30 @@ public class CodegenEngine {
         content = prettyCode(content);
         result.put(filePath, content);
     }
-
+    
+    /**
+     * 生成代码 新增
+     *
+     * @param result     结果
+     * @param templateDO template do
+     * @param bindingMap 绑定映射
+     */
+    private void generateCodeNew(Map<String, String> result, CodegenTempleDO templateDO,
+                                 Map<String, Object> bindingMap) {
+        String filePath = formatFilePath(templateDO.getTargetPackage() + "/" + templateDO.getFileName(), bindingMap);
+        
+        // 创建模板引擎,Hutool默认使用的是Velocity模板引擎,也可以指定其他引擎如Freemarker等
+        TemplateEngine engine = TemplateUtil.createEngine();
+        
+        // 将模板字符串包装成StringResource对象,以便模板引擎能够识别并解析
+        Template template = engine.getTemplate(templateDO.getTempleComment());
+        String content = template.render(bindingMap);
+        // 格式化代码
+        content = prettyCode(content);
+        result.put(filePath, content);
+    }
+    
+    
     private void generateSubCode(CodegenTableDO table, List<CodegenTableDO> subTables,
                                  Map<String, String> result, String vmPath,
                                  String filePath, Map<String, Object> bindingMap) {
@@ -423,6 +544,7 @@ public class CodegenEngine {
         filePath = StrUtil.replace(filePath, "${table.moduleName}", table.getModuleName());
         filePath = StrUtil.replace(filePath, "${table.businessName}", table.getBusinessName());
         filePath = StrUtil.replace(filePath, "${table.className}", table.getClassName());
+        filePath = StrUtil.replace(filePath, "${}", table.getClassName());
         // 特殊:主子表专属逻辑
         Integer subIndex = (Integer) bindingMap.get("subIndex");
         if (subIndex != null) {
@@ -436,76 +558,4 @@ public class CodegenEngine {
         return filePath;
     }
 
-    private static String javaTemplatePath(String path) {
-        return "codegen/java/" + path + ".vm";
-    }
-
-    private static String javaModuleImplVOFilePath(String path) {
-        return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
-                "vo/${sceneEnum.prefixClass}${table.className}" + path, "biz", "main");
-    }
-
-    private static String javaModuleImplControllerFilePath() {
-        return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
-                "${sceneEnum.prefixClass}${table.className}Controller", "biz", "main");
-    }
-
-    private static String javaModuleImplMainFilePath(String path) {
-        return javaModuleFilePath(path, "biz", "main");
-    }
-
-    private static String javaModuleApiMainFilePath(String path) {
-        return javaModuleFilePath(path, "api", "main");
-    }
-
-    private static String javaModuleImplTestFilePath(String path) {
-        return javaModuleFilePath(path, "biz", "test");
-    }
-
-    private static String javaModuleFilePath(String path, String module, String src) {
-        return "yudao-module-${table.moduleName}/" + // 顶级模块
-                "yudao-module-${table.moduleName}-" + module + "/" + // 子模块
-                "src/" + src + "/java/${basePackage}/module/${table.moduleName}/" + path + ".java";
-    }
-
-    private static String mapperXmlFilePath() {
-        return "yudao-module-${table.moduleName}/" + // 顶级模块
-                "yudao-module-${table.moduleName}-biz/" + // 子模块
-                "src/main/resources/mapper/${table.businessName}/${table.className}Mapper.xml";
-    }
-
-    private static String vueTemplatePath(String path) {
-        return "codegen/vue/" + path + ".vm";
-    }
-
-    private static String vueFilePath(String path) {
-        return "yudao-ui-${sceneEnum.basePackage}-vue2/" + // 顶级目录
-                "src/" + path;
-    }
-
-    private static String vue3TemplatePath(String path) {
-        return "codegen/vue3/" + path + ".vm";
-    }
-
-    private static String vue3FilePath(String path) {
-        return "yudao-ui-${sceneEnum.basePackage}-vue3/" + // 顶级目录
-                "src/" + path;
-    }
-
-    private static String vue3VbenTemplatePath(String path) {
-        return "codegen/vue3_vben/" + path + ".vm";
-    }
-
-    private static boolean isSubTemplate(String path) {
-        return path.contains("_sub");
-    }
-
-    private static boolean isPageReqVOTemplate(String path) {
-        return path.contains("pageReqVO");
-    }
-
-    private static boolean isListReqVOTemplate(String path) {
-        return path.contains("listReqVO");
-    }
-
 }