理想的情況下,代碼生成可以節省很多重復且沒有技術含量的工作量,並且代碼生成可以按照統一的代碼規范和格式來生成代碼,給日常的代碼開發提供很大的幫助。但是,代碼生成也有其局限性,當牽涉到復雜的業務邏輯時,簡單的代碼生成功能無法解決。
目前市面上的代碼生成器層出不窮,大多數的原理是基於已有的代碼邏輯模板,按照一定的規則來生成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、代碼生成功能運行界面
數據源配置:
代碼生成配置:
關聯表配置:
表字段配置:
表單配置:
表單校驗配置:
列表查詢配置:
數據字典配置:
校驗規則配置: