理想的情况下,代码生成可以节省很多重复且没有技术含量的工作量,并且代码生成可以按照统一的代码规范和格式来生成代码,给日常的代码开发提供很大的帮助。但是,代码生成也有其局限性,当牵涉到复杂的业务逻辑时,简单的代码生成功能无法解决。
目前市面上的代码生成器层出不穷,大多数的原理是基于已有的代码逻辑模板,按照一定的规则来生成CRUD代码。至于更为复杂的代码生成大家都在人工智能领域探索,目前基于代码训练的人工智能代码生成还在于提供代码补全功能方面,比如智能编程助手aiXcoder提供了常用IDE插件,在项目开发过程中,可以基于你项目的代码进行训练,编程时提供合适的代码提示。由微软、OpenAI、GitHub 三家联合打造的Copilot 也有异曲同工之妙,都是在项目开发中,提供优秀的代码自动补全功能从而可以提升工作效率。希望在不远的将来,我们可以实现复杂业务逻辑的代码也通过人工智能对大量代码的训练和分析来实现吧。
这里我们制作的代码生成器,是按照平时开发过程中的思考来设计,一般情况下我们的开发步骤是: 需求分析->数据建模->数据库设计->编写后台代码(增删改查)->编写前台代码(增删改查)->字段校验 ->业务逻辑完善->测试,所以我们希望代码生成器能够:
- 读取数据库表和字段
- 根据数据库字段生成实体类和CRUD方法
- 根据数据库字段生成前端操作页面
- 前端页面的展示方式可以根据需要配置(form表单、数据展示列表)
- 可以生成多表联合查询的代码
- 可以配置字段的校验规则
一、引入依赖的库
1、修改GitEgg-Platform项目中的gitegg-platform-bom工程的pom.xml文件,这里使用mybatis-plus-generator目前最新版本3.5.1来自定义我们需要的代码生成器。
pom.xml
<properties> ...... <!-- Mybatis Plus增强工具代码生成 --> <mybatis.plus.generator.version>3.5.1</mybatis.plus.generator.version> ...... </properties> <dependencyManagement> <dependencies> ...... <!-- Mybatis Plus代码生成工具 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>${mybatis.plus.generator.version}</version> </dependency> ...... </dependencies> </dependencyManagement>
2、在GitEgg-Platform项目中新建gitegg-platform-code-generator工程,提供基本的自定义代码生成能力,以及定义一些常量。
GitEggCodeGeneratorConstant.java常量类
package com.gitegg.platform.code.generator.constant; import java.io.File; /** * @ClassName: GitEggCodeGeneratorConstant * @Description: 常量类 * @author GitEgg * @since 2021-10-12 */ public class GitEggCodeGeneratorConstant { /** * CONFIG */ public static final String CONFIG = "config"; /** * FIELDS */ public static final String FIELDS = "fields"; /** * FORM_FIELDS */ public static final String FORM_FIELDS = "formFields"; /** * BASE_ENTITY_FIELD_LIST */ public static final String BASE_ENTITY_FIELD_LIST = "baseEntityFieldList"; /** * Author */ public static final String AUTHOR = "GitEgg"; /** * JAVA_PATH */ public static final String JAVA_PATH = File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator; /** * RESOURCES_PATH */ public static final String RESOURCES_PATH = File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator; /** * VUE_PATH */ public static final String VUE_PATH = File.separator + "src" + File.separator + "views" + File.separator; /** * JS_PATH */ public static final String JS_PATH = File.separator + "src" + File.separator + "api" + File.separator; /** * VUE_JS_PATH */ public static final String VUE_JS_PATH = "vueJsPath"; /** * CUSTOM_FILE_PATH_MAP */ public static final String CUSTOM_FILE_PATH_MAP = "customFilePathMap"; }
3、mybatis-plus-generator3.5.1版本支持生成默认支持生成service、serviceImpl、mapper、mapperXml、controller、entity以及自定的other。这些文件都可以自定义模板和输出路径,但是mybatis-plus-generator是将所有的自定义文件都生成到other定义的目录下面的,这显然不符合我们的需求,比如我们需要的DTO文件,vue文件、js文件都会生成到不同的目录里面去,我们需要自定义扩展FreemarkerTemplateEngine方法,实现自定义文件生成到不同的目录,因为我们使用的是Freemarker所以自定义FreemarkerTemplateEngine这个实现类。
package com.gitegg.platform.code.generator.engine; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.io.File; import java.util.Map; /** * Freemarker 自定义输出自定义模板文件 * * @author GitEgg * @since 2021-10-12 */ public class GitEggFreemarkerTemplateEngine extends FreemarkerTemplateEngine { /** * 自定义输出自定义模板文件 * * @param customFile 自定义配置模板文件信息 * @param tableInfo 表信息 * @param objectMap 渲染数据 * @since 3.5.1 */ @Override protected void outputCustomFile( Map<String, String> customFile, TableInfo tableInfo, Map<String, Object> objectMap) { Map<String, String> customFilePath = (Map<String, String>)objectMap.get("customFilePathMap"); customFile.forEach((key, value) -> { String otherPath = customFilePath.get(key); String fileName = String.format((otherPath + File.separator + "%s"), key); outputFile(new File(fileName), objectMap, value); }); } }
二、业务及实现方法
代码生成作为系统的一个功能模块,也需要考虑业务、数据库设计,这里主要有这几个模块:
- 数据源配置:因为是微服务,可能会有多个数据库,分库分表等,所以这里选择使用配置数据源的方式,在代码生成的时候,让开发人员可以自己选择在哪个数据源下的表进行代码生成。
- 代码生成基础配置(数据字典):代码生成时用到的组件类型、展示类型等基础配置,都配置的代码生成的数据字典中,这里不使用系统的数据字典。同时,在组件选择时,只可以选择业务的数据字典。
- 校验规则配置:可以配置字段校验的正则表单式,在字段配置时选择哪些字段进行校验。
- 代码生成规则配置:数据表配置、联合表配置、字段配置、表单配置、 校验配置、列表配置
1、根据以上业务需求,设计了t_sys_code_generator_datasource(数据源配置)、t_sys_code_generator_config(主数据表配置)、t_sys_code_generator_table_join(联表配置)、t_sys_code_generator_field(表字段配置)、t_sys_code_generator_validate(校验规则配置)、t_sys_code_generator_dict(数据字典配置)共六张表。
CREATE TABLE `t_sys_code_generator_datasource` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户id', `datasource_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据源名称', `url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '连接地址', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名', `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码', `driver` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据库驱动', `db_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据库类型', `comments` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', `creator` bigint(20) NULL DEFAULT NULL COMMENT '创建者', `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', `operator` bigint(20) NULL DEFAULT NULL COMMENT '更新者', `del_flag` tinyint(2) NULL DEFAULT 0 COMMENT '1:删除 0:不删除', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '数据源配置表' ROW_FORMAT = Dynamic;
CREATE TABLE `t_sys_code_generator_config` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户id', `datasource_id` bigint(20) NULL DEFAULT NULL COMMENT '数据源', `module_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '模块名称', `module_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '模块代码', `service_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '服务名称', `table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表名', `table_alias` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表别名', `table_prefix` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表前缀', `parent_package` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '父级包名', `controller_path` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'controller路径', `form_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表单类型 modal弹出框 drawer抽屉 tab新窗口', `table_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表类型 single单表 multi多表', `table_show_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '展示类型 table数据表格 tree_table 树表格 3 left_tree_table左树右表 tree数据树 table_table左表右表 left_table_tree左表右树', `form_item_col` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表单字段排列 1一列一行 2 两列一行', `left_tree_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '左树类型 organization机构树 resource资源权限树 ', `front_code_path` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '前端代码路径', `service_code_path` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '后端代码路径', `import_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否支持导入 1支持 0不支持', `export_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否支持导出 1支持 0不支持', `query_reuse` tinyint(1) NOT NULL DEFAULT 1 COMMENT '查询复用:分页查询和单条记录查询公用同一个sql语句', `status_handling` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态处理', `code_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '代码生成类型 全部 仅后端代码 仅前端代码', `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', `creator` bigint(20) NULL DEFAULT NULL COMMENT '创建者', `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', `operator` bigint(20) NULL DEFAULT NULL COMMENT '更新者', `del_flag` tinyint(2) NULL DEFAULT 0 COMMENT '1:删除 0:不删除', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '代码生成配置表' ROW_FORMAT = Dynamic;
CREATE TABLE `t_sys_code_generator_table_join` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户id', `generation_id` bigint(20) NOT NULL COMMENT '代码生成主键', `datasource_id` bigint(20) NULL DEFAULT NULL COMMENT '数据源和主表一致', `join_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表名', `join_table_alias` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表别名', `join_table_prefix` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表前缀', `join_table_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'left左连接 right右连接 inner等值连接 union联合查询', `join_table_select` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '自定义查询字段', `join_table_on` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '自定义on条件', `table_sort` int(11) NULL DEFAULT NULL COMMENT '显示排序', `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', `creator` bigint(20) NULL DEFAULT NULL COMMENT '创建者', `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', `operator` bigint(20) NULL DEFAULT NULL COMMENT '更新者', `del_flag` tinyint(2) NULL DEFAULT 0 COMMENT '1:删除 0:不删除', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '多表查询时的联合表配置' ROW_FORMAT = Dynamic;
CREATE TABLE `t_sys_code_generator_field` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户id', `generation_id` bigint(20) NOT NULL COMMENT '代码生成主键', `join_id` bigint(20) NOT NULL COMMENT '关联表主键', `join_table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表名', `field_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字段名称', `field_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字段类型', `comment` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字段描述', `entity_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '实体类型', `entity_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '实体名称', `form_add` tinyint(1) NOT NULL DEFAULT 0 COMMENT '表单新增', `form_edit` tinyint(1) NOT NULL DEFAULT 0 COMMENT '表单编辑', `query_term` tinyint(1) NOT NULL DEFAULT 0 COMMENT '查询条件', `list_show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '列表展示', `import_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否支持导入 1支持 0不支持', `export_flag` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否支持导出 1支持 0不支持', `required` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否必填', `field_unique` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否唯一', `query_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '查询类型', `control_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '组件类型', `dict_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '字典编码', `min` bigint(20) NULL DEFAULT NULL COMMENT '最小值', `max` bigint(20) NULL DEFAULT NULL COMMENT '最大值', `min_length` int(11) NOT NULL DEFAULT 0 COMMENT '最小长度', `max_length` int(11) NULL DEFAULT NULL COMMENT '字段最大长度', `default_value` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '默认值', `validate_id` bigint(20) NULL DEFAULT NULL COMMENT '校验规则主键', `validate_regular` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '自定义正则表达式校验规则', `field_sort` int(11) NOT NULL DEFAULT 1 COMMENT '显示排序', `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', `creator` bigint(20) NULL DEFAULT NULL COMMENT '创建者', `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', `operator` bigint(20) NULL DEFAULT NULL COMMENT '更新者', `del_flag` tinyint(2) NULL DEFAULT 0 COMMENT '1:删除 0:不删除', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `unique_field`(`generation_id`, `join_id`, `join_table_name`, `field_name`) USING BTREE COMMENT '联合约束' ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字段属性配置表' ROW_FORMAT = Dynamic;
CREATE TABLE `t_sys_code_generator_validate` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户id', `validate_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '校验名称', `validate_regular` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '正则表达式校验规则', `status` tinyint(2) NOT NULL DEFAULT 1 COMMENT '\'0\'禁用,\'1\' 启用', `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', `creator` bigint(20) NULL DEFAULT NULL COMMENT '创建者', `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', `operator` bigint(20) NULL DEFAULT NULL COMMENT '更新者', `del_flag` tinyint(2) NULL DEFAULT 0 COMMENT '1:删除 0:不删除', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '字段校验规则配置表' ROW_FORMAT = Dynamic;
CREATE TABLE `t_sys_code_generator_dict` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `tenant_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '租户id', `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '字典上级', `ancestors` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '所有上级字典id的集合,便于查找', `dict_name` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典名称', `dict_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '字典值', `dict_order` int(11) NULL DEFAULT NULL COMMENT '排序', `dict_status` tinyint(2) NULL DEFAULT 1 COMMENT '1有效,0禁用', `comments` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注', `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间', `creator` bigint(20) NULL DEFAULT NULL COMMENT '创建人', `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', `operator` bigint(20) NULL DEFAULT NULL COMMENT '操作人', `del_flag` tinyint(2) NOT NULL DEFAULT 0 COMMENT '1:删除 0:不删除', PRIMARY KEY (`id`) USING BTREE, INDEX `INDEX_DICT_NAME`(`dict_name`) USING BTREE, INDEX `INDEX_DICT_CODE`(`dict_code`) USING BTREE, INDEX `INDEX_PARENT_ID`(`parent_id`) USING BTREE, INDEX `INDEX_TENANT_ID`(`tenant_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '数据字典表' ROW_FORMAT = Dynamic;
表结构建立好之后,先用mybatis-plus-generator默认功能生成基本的CRUD代码,这些CRUD代码就不列出来了,主要说明如何利用mybatis-plus-generator读取数据库表和字段,并结合业务在界面上展示,从而进行代码生成规则的配置。
2、在GitEgg-Cloud项目下,gitegg-plugin子项目下新建gitegg-code-generator工程,新建IEngineService接口和接口实现类EngineServiceImpl用于实现:查询某个数据源的所有表、查询某个表的字段信息、查询某个代码生成配置里面所有的字段配置、执行代码生成功能。
package com.gitegg.code.generator.engine.service; import com.baomidou.mybatisplus.generator.config.po.TableField; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.gitegg.code.generator.config.dto.QueryConfigDTO; import com.gitegg.code.generator.engine.dto.TableDTO; import java.util.List; /** * 代码生成器接口 * * @author GitEgg */ public interface IEngineService { /** * 查询某个数据源的所有表 * * @param queryConfigDTO * @return */ List<TableDTO> queryTableList(QueryConfigDTO queryConfigDTO); /** * 查询某个数据源表的字段信息 * * @param datasourceId * @param tableNames * @return */ List<TableInfo> queryTableFields(String datasourceId, List<String> tableNames); /** * 查询某个代码生成配置里面所有的字段 * @param queryConfigDTO * @return */ List<TableInfo> queryConfigFields(QueryConfigDTO queryConfigDTO); /** * 执行代码生成 * @param queryConfigDTO * @return */ boolean processGenerateCode(QueryConfigDTO queryConfigDTO); }
package com.gitegg.code.generator.engine.service.impl; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.OutputFile; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.fill.Column; import com.gitegg.code.generator.config.dto.QueryConfigDTO; import com.gitegg.code.generator.config.entity.Config; import com.gitegg.code.generator.config.service.IConfigService; import com.gitegg.code.generator.datasource.entity.Datasource; import com.gitegg.code.generator.datasource.service.IDatasourceService; import com.gitegg.code.generator.engine.GitEggDatabaseQuery; import com.gitegg.code.generator.engine.constant.CodeGeneratorConstant; import com.gitegg.code.generator.engine.dto.TableDTO; import com.gitegg.code.generator.engine.enums.CustomFileEnum; import com.gitegg.code.generator.engine.service.IEngineService; import com.gitegg.code.generator.field.dto.FieldDTO; import com.gitegg.code.generator.field.dto.QueryFieldDTO; import com.gitegg.code.generator.field.service.IFieldService; import com.gitegg.code.generator.join.entity.TableJoin; import com.gitegg.code.generator.join.service.ITableJoinService; import com.gitegg.platform.base.enums.BaseEntityEnum; import com.gitegg.platform.code.generator.constant.GitEggCodeGeneratorConstant; import com.gitegg.platform.code.generator.engine.GitEggFreemarkerTemplateEngine; import com.gitegg.platform.mybatis.entity.BaseEntity; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * 代码生成器接口类 * * @author GitEgg */ @Slf4j @Service @RequiredArgsConstructor(onConstructor_ = @Autowired) public class EngineServiceImpl implements IEngineService { private final IConfigService configService; private final IDatasourceService datasourceService; private final ITableJoinService tableJoinService; /** * 解决循环依赖问题 */ private IFieldService fieldService; @Autowired public void setFieldService(@Lazy IFieldService fieldService) { this.fieldService = fieldService; } @Override public List<TableDTO> queryTableList(QueryConfigDTO queryConfigDTO) { Datasource datasource = datasourceService.getById(queryConfigDTO.getDatasourceId()); DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(datasource.getUrl(), datasource.getUsername(), datasource.getPassword()).build(); ConfigBuilder configBuilder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null); List<TableDTO> tableInfos = (new GitEggDatabaseQuery(configBuilder)).queryDatasourceTables(); return tableInfos; } @Override public List<TableInfo> queryTableFields(String datasourceId, List<String> tableNames) { Datasource datasource = datasourceService.getById(datasourceId); DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(datasource.getUrl(), datasource.getUsername(), datasource.getPassword()).build(); //设置有哪些表 StrategyConfig strategyConfig = new StrategyConfig.Builder() .addInclude(tableNames.toArray(new String[]{})) .entityBuilder() .enableChainModel() .enableLombok() .enableRemoveIsPrefix() .enableTableFieldAnnotation() .enableActiveRecord() .logicDeleteColumnName(BaseEntityEnum.DEL_FLAG.field) .logicDeletePropertyName(BaseEntityEnum.DEL_FLAG.entity) .naming(NamingStrategy.underline_to_camel) .columnNaming(NamingStrategy.underline_to_camel) .addTableFills(new Column(BaseEntityEnum.CREATE_TIME.field, FieldFill.INSERT)) .addTableFills(new Column(BaseEntityEnum.UPDATE_TIME.field, FieldFill.INSERT_UPDATE)) .idType(IdType.AUTO) .build(); ConfigBuilder configBuilder = new ConfigBuilder(null, dataSourceConfig, strategyConfig, null, null, null); List<TableInfo> tableInfoList = configBuilder.getTableInfoList(); return tableInfoList; } @Override public List<TableInfo> queryConfigFields(QueryConfigDTO queryConfigDTO) { List<String> tableNames = new ArrayList<>(); String tableName = queryConfigDTO.getTableName(); tableNames.add(tableName); Long id = queryConfigDTO.getId(); // 查询是否有联表 if (CodeGeneratorConstant.TABLE_DATA_TYPE_MULTI.equals(queryConfigDTO.getTableType())) { QueryWrapper<TableJoin> queryWrapper = new QueryWrapper<>(); queryWrapper.eq(CodeGeneratorConstant.GENERATION_ID, id); List<TableJoin> tableJoinList = tableJoinService.list(queryWrapper); if(!CollectionUtils.isEmpty(tableJoinList)) { tableJoinList.stream().forEach(tableJoin->{ tableNames.add(tableJoin.getJoinTableName()); }); } } Datasource datasource = datasourceService.getById(queryConfigDTO.getDatasourceId()); DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(datasource.getUrl(), datasource.getUsername(), datasource.getPassword()).build(); //设置有哪些表 StrategyConfig strategyConfig = new StrategyConfig.Builder().addInclude(tableNames.toArray(new String[]{})).build(); ConfigBuilder configBuilder = new ConfigBuilder(null, dataSourceConfig, strategyConfig, null, null, null); List<TableInfo> tableInfoList = configBuilder.getTableInfoList(); return tableInfoList; } @Override public boolean processGenerateCode(QueryConfigDTO queryConfigDTO){ Config config = configService.getById(queryConfigDTO.getId()); QueryFieldDTO queryFieldDTO = new QueryFieldDTO(); queryFieldDTO.setGenerationId(queryConfigDTO.getId()); List<FieldDTO> fieldDTOS = fieldService.queryFieldList(queryFieldDTO); //提取表单的字段 List<FieldDTO> formFieldDTOS = fieldDTOS.stream().filter(f->f.getFormAdd() || f.getFormEdit()).collect(Collectors.toList()); Map<String, Object> customMap = new HashMap<>(); customMap.put(GitEggCodeGeneratorConstant.CONFIG, config); customMap.put(GitEggCodeGeneratorConstant.FIELDS, fieldDTOS); customMap.put(GitEggCodeGeneratorConstant.FORM_FIELDS, formFieldDTOS); //baseEntity里面有的,DTO中需要排除的字段 List<String> baseEntityFieldList = BaseEntityEnum.getBaseEntityFieldList(); customMap.put(GitEggCodeGeneratorConstant.BASE_ENTITY_FIELD_LIST, baseEntityFieldList); //查询数据源配置 Datasource datasource = datasourceService.getById(config.getDatasourceId()); String serviceName = config.getServiceName(); //前端代码路径 String frontCodePath = config.getFrontCodePath(); //后端代码路径 String serviceCodePath = config.getServiceCodePath(); //自定义路径 String parent = config.getParentPackage(); String moduleName = config.getModuleCode(); String codeDirPath = (parent + StrUtil.DOT + moduleName).replace(StrUtil.DOT, File.separator) + File.separator; FastAutoGenerator.create(datasource.getUrl(), datasource.getUsername(), datasource.getPassword()) .globalConfig(builder -> { //全局配置 String author = GitEggCodeGeneratorConstant.AUTHOR; builder.author(author) // 设置作者 .enableSwagger() // 开启 swagger 模式 .fileOverride() // 覆盖已生成文件 .disableOpenDir() .outputDir(serviceCodePath + GitEggCodeGeneratorConstant.JAVA_PATH); // 指定输出目录 }) .packageConfig(builder -> { //包配置 Map<OutputFile, String> pathInfoMap = new HashMap<>(); pathInfoMap.put(OutputFile.mapperXml, serviceCodePath + GitEggCodeGeneratorConstant.RESOURCES_PATH + codeDirPath + CodeGeneratorConstant.MAPPER); builder.parent(parent) // 设置父包名 .moduleName(moduleName) // 设置父包模块名 .pathInfo(pathInfoMap); // 自定义生成路径 }) .injectionConfig(builder -> { String dtoName = StrUtil.upperFirst(config.getModuleCode()); //dto String dtoFile = dtoName + CodeGeneratorConstant.DTO_JAVA; String createDtoFile = CodeGeneratorConstant.CREATE + dtoFile; String updateDtoFile = CodeGeneratorConstant.UPDATE + dtoFile; String queryDtoFile = CodeGeneratorConstant.QUERY + dtoFile; //Export and Import String exportFile = dtoName + CodeGeneratorConstant.EXPORT_JAVA; String importFile = dtoName + CodeGeneratorConstant.IMPORT_JAVA; // SQL String sqlFile = dtoName + CodeGeneratorConstant.RESOURCE_SQL; // 设置自定义输出文件 Map<String, String> customFileMap = new HashMap<>(); customFileMap.put(dtoFile, CustomFileEnum.DTO_FILE.path); customFileMap.put(createDtoFile, CustomFileEnum.CREATE_DTO.path); customFileMap.put(updateDtoFile, CustomFileEnum.UPDATE_DTO.path); customFileMap.put(queryDtoFile, CustomFileEnum.QUERY_DTO.path); // Export and Import customFileMap.put(exportFile, CustomFileEnum.EXPORT.path); customFileMap.put(importFile, CustomFileEnum.IMPORT.path); // SQL customFileMap.put(sqlFile, CustomFileEnum.SQL.path); //因为目前版本框架只支持自定义输出到other目录,所以这里利用重写AbstractTemplateEngine的outputCustomFile方法支持所有自定义文件输出目录 Map<String, String> customFilePath = new HashMap<>(); int start = serviceName.indexOf(StrUtil.DASHED); int end = serviceName.length(); String servicePath = serviceName.substring(start, end).replace(StrUtil.DASHED, File.separator); //判断是否生成后端代码 if (config.getCodeType().equals(CodeGeneratorConstant.CODE_ALL) || config.getCodeType().equals(CodeGeneratorConstant.CODE_SERVICE)) { //dto String dtoPath = serviceCodePath + GitEggCodeGeneratorConstant.JAVA_PATH + codeDirPath + CodeGeneratorConstant.DTO; customFilePath.put(dtoFile, dtoPath); customFilePath.put(createDtoFile, dtoPath); customFilePath.put(updateDtoFile, dtoPath); customFilePath.put(queryDtoFile, dtoPath); // Export and Import String entityPath = serviceCodePath + GitEggCodeGeneratorConstant.JAVA_PATH + codeDirPath + CodeGeneratorConstant.ENTITY; customFilePath.put(exportFile, entityPath); customFilePath.put(importFile, entityPath); // SQL String sqlPath = serviceCodePath + GitEggCodeGeneratorConstant.RESOURCES_PATH + codeDirPath + CodeGeneratorConstant.MAPPER; customFilePath.put(sqlFile, sqlPath); } //判断是否生成后端代码 if (config.getCodeType().equals(CodeGeneratorConstant.CODE_ALL) || config.getCodeType().equals(CodeGeneratorConstant.CODE_FRONT)) { // vue and js String vueFile = config.getModuleCode() + CodeGeneratorConstant.TABLE_VUE; String jsFile = config.getModuleCode() + CodeGeneratorConstant.JS; String vuePath = frontCodePath + GitEggCodeGeneratorConstant.VUE_PATH + servicePath + File.separator + config.getModuleCode(); String jsPath = frontCodePath + GitEggCodeGeneratorConstant.JS_PATH + servicePath + File.separator + config.getModuleCode(); customFilePath.put(vueFile, vuePath); customFilePath.put(jsFile, jsPath); // VUE AND JS // TODO 要支持树形表、左树右表、左表右表、左表右树、左树右树形表、左树右树 customFileMap.put(vueFile, CustomFileEnum.VUE.path); customFileMap.put(jsFile, CustomFileEnum.JS.path); customMap.put(GitEggCodeGeneratorConstant.VUE_JS_PATH, servicePath.replace(File.separator, StrUtil.SLASH) + StrUtil.SLASH + config.getModuleCode() + StrUtil.SLASH + config.getModuleCode()); } customMap.put(GitEggCodeGeneratorConstant.CUSTOM_FILE_PATH_MAP, customFilePath); builder.customMap(customMap) .customFile(customFileMap); }) .strategyConfig(builder -> { builder .addInclude(config.getTableName()) .addTablePrefix(config.getTablePrefix()) .entityBuilder() .enableLombok() .enableTableFieldAnnotation() // 实体字段注解 .superClass(BaseEntity.class) .addSuperEntityColumns(BaseEntityEnum.TENANT_ID.field, BaseEntityEnum.CREATE_TIME.field, BaseEntityEnum.CREATOR.field, BaseEntityEnum.UPDATE_TIME.field, BaseEntityEnum.OPERATOR.field, BaseEntityEnum.DEL_FLAG.field) .naming(NamingStrategy.underline_to_camel) .addTableFills(new Column(BaseEntityEnum.CREATE_TIME.field, FieldFill.INSERT)) //基于数据库字段填充 .addTableFills(new Column(BaseEntityEnum.UPDATE_TIME.field, FieldFill.INSERT_UPDATE)) //基于模型属性填充 .controllerBuilder() .enableRestStyle() .enableHyphenStyle() .mapperBuilder() // .enableMapperAnnotation() .enableBaseResultMap() .enableBaseColumnList() ; }) .templateConfig(builder -> { if (config.getCodeType().equals(CodeGeneratorConstant.CODE_FRONT)) { builder.disable(); } }) // 使用Freemarker引擎模板,默认的是Velocity引擎模板 .templateEngine(new GitEggFreemarkerTemplateEngine()) .execute(); return true; } }
3、修改代码生成的模板文件,因为默认的代码模板生成文件不能满足我们的需求,我们需要新增DTO、vue、js、数据导入导出实体定义类等模板,在模板接口新增导入导出等方法,在DTO添加字段校验等。因为模板代码太多,这里不详细列举,可以在在GitHub 或者 Gitee下载查看。
4、代码生成功能运行界面
数据源配置:
数据源配置
代码生成配置:
代码生成配置
关联表配置:
关联表配置
表字段配置:
表字段配置
表单配置:
表单配置
表单校验配置:
表单校验配置
列表查询配置:
列表查询配置
数据字典配置:
数据字典配置
校验规则配置:
校验规则配置