Commit 9519ca969cb3f93807c9aac6daa139827fd520b7

Authored by YunaiV
1 parent 36708447

feat:【infra】代码生成:menu 生成时,支持多种

yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java
... ... @@ -4,12 +4,14 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
7 8 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
8 9 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
9 10 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
10 11 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
11 12 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
12 13 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  14 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
13 15 import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
14 16 import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
15 17 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
... ... @@ -17,7 +19,9 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
17 19 import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
18 20 import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
19 21 import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
  22 +import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
20 23 import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
  24 +import com.baomidou.mybatisplus.annotation.DbType;
21 25 import com.baomidou.mybatisplus.generator.config.po.TableField;
22 26 import com.baomidou.mybatisplus.generator.config.po.TableInfo;
23 27 import com.google.common.annotations.VisibleForTesting;
... ... @@ -45,6 +49,8 @@ public class CodegenServiceImpl implements CodegenService {
45 49  
46 50 @Resource
47 51 private DatabaseTableService databaseTableService;
  52 + @Resource
  53 + private DataSourceConfigService dataSourceConfigService;
48 54  
49 55 @Resource
50 56 private CodegenTableMapper codegenTableMapper;
... ... @@ -284,8 +290,11 @@ public class CodegenServiceImpl implements CodegenService {
284 290 }
285 291 }
286 292  
  293 + // 获取数据源对应的数据库类型
  294 + DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(table.getDataSourceConfigId());
  295 + DbType dbType = JdbcUtils.getDbType(dataSourceConfig.getUrl());
287 296 // 执行生成
288   - return codegenEngine.execute(table, columns, subTables, subColumnsList);
  297 + return codegenEngine.execute(dbType, table, columns, subTables, subColumnsList);
289 298 }
290 299  
291 300 @Override
... ...
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
... ... @@ -33,6 +33,7 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
33 33 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
34 34 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenVOTypeEnum;
35 35 import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
  36 +import com.baomidou.mybatisplus.annotation.DbType;
36 37 import com.google.common.annotations.VisibleForTesting;
37 38 import com.google.common.collect.ImmutableTable;
38 39 import com.google.common.collect.Maps;
... ... @@ -310,16 +311,17 @@ public class CodegenEngine {
310 311 /**
311 312 * 生成代码
312 313 *
  314 + * @param dbType 数据库类型
313 315 * @param table 表定义
314 316 * @param columns table 的字段定义数组
315 317 * @param subTables 子表数组,当且仅当主子表时使用
316 318 * @param subColumnsList subTables 的字段定义数组
317 319 * @return 生成的代码,key 是路径,value 是对应代码
318 320 */
319   - public Map<String, String> execute(CodegenTableDO table, List<CodegenColumnDO> columns,
  321 + public Map<String, String> execute(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,
320 322 List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {
321 323 // 1.1 初始化 bindMap 上下文
322   - Map<String, Object> bindingMap = initBindingMap(table, columns, subTables, subColumnsList);
  324 + Map<String, Object> bindingMap = initBindingMap(dbType, table, columns, subTables, subColumnsList);
323 325 // 1.2 获得模版
324 326 Map<String, String> templates = getTemplates(table.getFrontType());
325 327  
... ... @@ -425,10 +427,11 @@ public class CodegenEngine {
425 427 return content;
426 428 }
427 429  
428   - private Map<String, Object> initBindingMap(CodegenTableDO table, List<CodegenColumnDO> columns,
  430 + private Map<String, Object> initBindingMap(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,
429 431 List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {
430 432 // 创建 bindingMap
431 433 Map<String, Object> bindingMap = new HashMap<>(globalBindingMap);
  434 + bindingMap.put("dbType", dbType);
432 435 bindingMap.put("table", table);
433 436 bindingMap.put("columns", columns);
434 437 bindingMap.put("primaryColumn", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段
... ...
yudao-module-infra/src/main/resources/codegen/sql/sql.vm
  1 +## 通用变量定义
  2 +#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
  3 +#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
  4 +##
  5 +## 宏定义:生成按钮 SQL(通用部分)
  6 +#macro(insertButtonSql $parentIdVar)
  7 +#foreach ($functionName in $functionNames)
  8 +#set ($index = $foreach.count - 1)
  9 + INSERT INTO system_menu(
  10 + name, permission, type, sort, parent_id,
  11 + path, icon, component, status
  12 + )
  13 + VALUES (
  14 + '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, ${parentIdVar},
  15 + '', '', '', 0
  16 + );
  17 +#end
  18 +#end
  19 +##
  20 +## ======================= MySQL / OceanBase =======================
  21 +#if ($dbType.name() == 'MYSQL' || $dbType.name() == 'OCEAN_BASE')
1 22 -- 菜单 SQL
2 23 INSERT INTO system_menu(
3 24 name, permission, type, sort, parent_id,
... ... @@ -9,12 +30,9 @@ VALUES (
9 30 );
10 31  
11 32 -- 按钮父菜单ID
12   --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
13 33 SELECT @parentId := LAST_INSERT_ID();
14 34  
15 35 -- 按钮 SQL
16   -#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
17   -#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
18 36 #foreach ($functionName in $functionNames)
19 37 #set ($index = $foreach.count - 1)
20 38 INSERT INTO system_menu(
... ... @@ -25,4 +43,139 @@ VALUES (
25 43 '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
26 44 '', '', '', 0
27 45 );
28   -#end
29 46 \ No newline at end of file
  47 +#end
  48 +##
  49 +## ======================= Oracle / 达梦 DM =======================
  50 +#elseif ($dbType.name() == 'ORACLE' || $dbType.name() == 'DM')
  51 +-- 菜单 SQL
  52 +INSERT INTO system_menu(
  53 + name, permission, type, sort, parent_id,
  54 + path, icon, component, status, component_name
  55 +)
  56 +VALUES (
  57 + '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
  58 + '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
  59 +);
  60 +
  61 +-- 按钮父菜单ID
  62 +-- 说明:Oracle/达梦 使用序列获取上一个插入的 ID
  63 +DECLARE
  64 + v_parent_id NUMBER;
  65 +BEGIN
  66 + SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;
  67 + -- 按钮 SQL
  68 +#foreach ($functionName in $functionNames)
  69 +#set ($index = $foreach.count - 1)
  70 + INSERT INTO system_menu(
  71 + name, permission, type, sort, parent_id,
  72 + path, icon, component, status
  73 + )
  74 + VALUES (
  75 + '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,
  76 + '', '', '', 0
  77 + );
  78 +#end
  79 +END;
  80 +/
  81 +##
  82 +## ======================= PostgreSQL / 人大金仓 KingbaseES =======================
  83 +#elseif ($dbType.name() == 'POSTGRE_SQL' || $dbType.name() == 'KINGBASE_ES')
  84 +-- 菜单 SQL
  85 +INSERT INTO system_menu(
  86 + name, permission, type, sort, parent_id,
  87 + path, icon, component, status, component_name
  88 +)
  89 +VALUES (
  90 + '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
  91 + '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
  92 +);
  93 +
  94 +-- 按钮父菜单ID
  95 +-- 说明:PostgreSQL/KingbaseES 使用 lastval() 获取上一个插入的序列值
  96 +DO $$
  97 +DECLARE
  98 + v_parent_id BIGINT;
  99 +BEGIN
  100 + v_parent_id := lastval();
  101 + -- 按钮 SQL
  102 +#foreach ($functionName in $functionNames)
  103 +#set ($index = $foreach.count - 1)
  104 + INSERT INTO system_menu(
  105 + name, permission, type, sort, parent_id,
  106 + path, icon, component, status
  107 + )
  108 + VALUES (
  109 + '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,
  110 + '', '', '', 0
  111 + );
  112 +#end
  113 +END $$;
  114 +##
  115 +## ======================= SQL Server =======================
  116 +#elseif ($dbType.name() == 'SQL_SERVER' || $dbType.name() == 'SQL_SERVER2005')
  117 +-- 菜单 SQL
  118 +INSERT INTO system_menu(
  119 + name, permission, type, sort, parent_id,
  120 + path, icon, component, status, component_name
  121 +)
  122 +VALUES (
  123 + '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
  124 + '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
  125 +);
  126 +
  127 +-- 按钮父菜单ID
  128 +-- 说明:SQL Server 使用 SCOPE_IDENTITY() 获取上一个插入的自增 ID
  129 +DECLARE @parentId BIGINT;
  130 +SET @parentId = SCOPE_IDENTITY();
  131 +
  132 +-- 按钮 SQL
  133 +#foreach ($functionName in $functionNames)
  134 +#set ($index = $foreach.count - 1)
  135 +INSERT INTO system_menu(
  136 + name, permission, type, sort, parent_id,
  137 + path, icon, component, status
  138 +)
  139 +VALUES (
  140 + '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
  141 + '', '', '', 0
  142 +);
  143 +#end
  144 +##
  145 +## ======================= 不支持的数据库类型 =======================
  146 +#else
  147 +-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  148 +-- 注意:当前数据库类型 ${dbType.name()} 暂不支持自动生成菜单 SQL
  149 +-- 请参考以下 MySQL 语法,手动修改为您的数据库对应的语法
  150 +-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  151 +
  152 +-- 菜单 SQL
  153 +INSERT INTO system_menu(
  154 + name, permission, type, sort, parent_id,
  155 + path, icon, component, status, component_name
  156 +)
  157 +VALUES (
  158 + '${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
  159 + '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
  160 +);
  161 +
  162 +-- 按钮父菜单ID
  163 +-- TODO: 请根据您的数据库类型,修改获取上一个插入 ID 的方式
  164 +-- MySQL: SELECT @parentId := LAST_INSERT_ID();
  165 +-- Oracle: SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;
  166 +-- PostgreSQL: SELECT lastval() INTO v_parent_id;
  167 +-- SQL Server: SET @parentId = SCOPE_IDENTITY();
  168 +SELECT @parentId := LAST_INSERT_ID();
  169 +
  170 +-- 按钮 SQL
  171 +#foreach ($functionName in $functionNames)
  172 +#set ($index = $foreach.count - 1)
  173 +INSERT INTO system_menu(
  174 + name, permission, type, sort, parent_id,
  175 + path, icon, component, status
  176 +)
  177 +VALUES (
  178 + '${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
  179 + '', '', '', 0
  180 +);
  181 +#end
  182 +#end
... ...
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImplTest.java
... ... @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTa
11 11 import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
12 12 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
13 13 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
  14 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
14 15 import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
15 16 import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
16 17 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
... ... @@ -19,7 +20,9 @@ import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
19 20 import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
20 21 import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
21 22 import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
  23 +import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
22 24 import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
  25 +import com.baomidou.mybatisplus.annotation.DbType;
23 26 import com.baomidou.mybatisplus.generator.config.po.TableField;
24 27 import com.baomidou.mybatisplus.generator.config.po.TableInfo;
25 28 import jakarta.annotation.Resource;
... ... @@ -62,6 +65,8 @@ public class CodegenServiceImplTest extends BaseDbUnitTest {
62 65  
63 66 @MockitoBean
64 67 private DatabaseTableService databaseTableService;
  68 + @MockitoBean
  69 + private DataSourceConfigService dataSourceConfigService;
65 70  
66 71 @MockitoBean
67 72 private CodegenBuilder codegenBuilder;
... ... @@ -453,9 +458,12 @@ public class CodegenServiceImplTest extends BaseDbUnitTest {
453 458 CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())
454 459 .setOrdinalPosition(2));
455 460 codegenColumnMapper.insert(column02);
  461 + // mock 数据(DataSourceConfigDO)
  462 + when(dataSourceConfigService.getDataSourceConfig(eq(table.getDataSourceConfigId())))
  463 + .thenReturn(randomPojo(DataSourceConfigDO.class, o -> o.setUrl("jdbc:mysql://")));
456 464 // mock 执行生成
457 465 Map<String, String> codes = MapUtil.of(randomString(), randomString());
458   - when(codegenEngine.execute(eq(table), argThat(columns -> {
  466 + when(codegenEngine.execute(eq(DbType.MYSQL), eq(table), argThat(columns -> {
459 467 assertEquals(2, columns.size());
460 468 assertEquals(column01, columns.get(0));
461 469 assertEquals(column02, columns.get(1));
... ... @@ -494,9 +502,12 @@ public class CodegenServiceImplTest extends BaseDbUnitTest {
494 502 // mock 数据(sub CodegenColumnDO)
495 503 CodegenColumnDO subColumn01 = randomPojo(CodegenColumnDO.class, o -> o.setId(1024L).setTableId(subTable.getId()));
496 504 codegenColumnMapper.insert(subColumn01);
  505 + // mock 数据(DataSourceConfigDO)
  506 + when(dataSourceConfigService.getDataSourceConfig(eq(table.getDataSourceConfigId())))
  507 + .thenReturn(randomPojo(DataSourceConfigDO.class, o -> o.setUrl("jdbc:mysql://")));
497 508 // mock 执行生成
498 509 Map<String, String> codes = MapUtil.of(randomString(), randomString());
499   - when(codegenEngine.execute(eq(table), argThat(columns -> {
  510 + when(codegenEngine.execute(eq(DbType.MYSQL), eq(table), argThat(columns -> {
500 511 assertEquals(2, columns.size());
501 512 assertEquals(column01, columns.get(0));
502 513 assertEquals(column02, columns.get(1));
... ...
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java
... ... @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
4 4 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
5 5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
6 6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.DbType;
7 8 import org.junit.jupiter.api.Disabled;
8 9 import org.junit.jupiter.api.Test;
9 10  
... ... @@ -28,7 +29,7 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
28 29 List<CodegenColumnDO> columns = getColumnList("student");
29 30  
30 31 // 调用
31   - Map<String, String> result = codegenEngine.execute(table, columns, null, null);
  32 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
32 33 // 生成测试文件
33 34 //writeResult(result, resourcesPath + "/vue2_one");
34 35 // 断言
... ... @@ -44,7 +45,7 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
44 45 List<CodegenColumnDO> columns = getColumnList("category");
45 46  
46 47 // 调用
47   - Map<String, String> result = codegenEngine.execute(table, columns, null, null);
  48 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
48 49 // 生成测试文件
49 50 //writeResult(result, resourcesPath + "/vue2_tree");
50 51 // 断言
... ... @@ -88,7 +89,7 @@ public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
88 89 List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
89 90  
90 91 // 调用
91   - Map<String, String> result = codegenEngine.execute(table, columns,
  92 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
92 93 Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
93 94 // 生成测试文件
94 95 //writeResult(result, resourcesPath + path);
... ...
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java
... ... @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
4 4 import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
5 5 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
6 6 import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.DbType;
7 8 import org.junit.jupiter.api.Disabled;
8 9 import org.junit.jupiter.api.Test;
9 10  
... ... @@ -28,7 +29,7 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
28 29 List<CodegenColumnDO> columns = getColumnList("student");
29 30  
30 31 // 调用
31   - Map<String, String> result = codegenEngine.execute(table, columns, null, null);
  32 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
32 33 // 生成测试文件
33 34 //writeResult(result, resourcesPath + "/vue3_one");
34 35 // 断言
... ... @@ -44,7 +45,7 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
44 45 List<CodegenColumnDO> columns = getColumnList("category");
45 46  
46 47 // 调用
47   - Map<String, String> result = codegenEngine.execute(table, columns, null, null);
  48 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null);
48 49 // 生成测试文件
49 50 //writeResult(result, resourcesPath + "/vue3_tree");
50 51 // 断言
... ... @@ -88,7 +89,7 @@ public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
88 89 List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
89 90  
90 91 // 调用
91   - Map<String, String> result = codegenEngine.execute(table, columns,
  92 + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns,
92 93 Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
93 94 // 生成测试文件
94 95 //writeResult(result, resourcesPath + path);
... ...