SpringCloud微服務實戰——搭建企業級開發框架(三十一):自定義MybatisPlus程式碼生成器實現前後端程式碼自動生成

全棧程式猿發表於2021-12-08

  理想的情況下,程式碼生成可以節省很多重複且沒有技術含量的工作量,並且程式碼生成可以按照統一的程式碼規範和格式來生成程式碼,給日常的程式碼開發提供很大的幫助。但是,程式碼生成也有其侷限性,當牽涉到複雜的業務邏輯時,簡單的程式碼生成功能無法解決。
  目前市面上的程式碼生成器層出不窮,大多數的原理是基於已有的程式碼邏輯模板,按照一定的規則來生成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、程式碼生成功能執行介面

資料來源配置:
資料來源配置
程式碼生成配置:
程式碼生成配置
關聯表配置:
關聯表配置
表欄位配置:
表欄位配置
表單配置:
表單配置
表單校驗配置:
表單校驗配置
列表查詢配置:
列表查詢配置
資料字典配置:
資料字典配置
校驗規則配置:
校驗規則配置

原始碼地址:

Gitee: https://gitee.com/wmz1930/GitEgg

GitHub: https://github.com/wmz1930/GitEgg

相關文章