• 如何3分钟,快速开发一个新功能


    背景

    关于为什么做这个代码生成器,其实主要有两点:

    1. 参与的项目中有很多分析报表需要展示给业务部门,公司使用的商用产品,或多或少有些问题,这部分可能是历史选型导致的,这里撇开不不谈;
    2. 项目里面也有很多CRUD的功能,而这些功能的实现代码基本上差不多,这些功能都去手写,也比较浪费时间而且效率很低,还可能会写错;

    针对这两个问题,就思考做一个简单的半自动的代码生成器,帮助团队的提升开发效率,只要在数据库中设计好表结构,就可以一键生成前后端代码、Swagger接口文档 、Validator参数校验。

    设计

    此篇主要是聊聊关于单表CRUD代码生成器的实现,后续大家可以扩展到树表、主子表,甚至还可以接入一些AI来扩展一下。整体技术方案,后端采用Spring Boot、Freemarker、MybatisPlus、Swagger等,前端使用Vue3、Element Plus。看到这里我想很多后端就知道我是如何做的了,整体核心实现就是通过Freemarker模版,增加一些配置项,动态生成CRUD的代码。这里做了一些细化设计,首先来看下整体的一个界面效果。
    image.png
    image.png
    image.png
    image.png

    表结构设计

    核心设计不变,只是增加三张表,可以进行自定义配置,让整个流程更加丝滑,整体表结构设计如下:
    image.png
    三张表分别是数据源配置表(tool_data_source_config)、表定义(tool_generator_table)以及列定义(tool_generator_column),数据源配置表主要是连接数据库相关的配置,表定义主要是类的抽象,包括整个类的包结构目录,列定义主要类字段的定义,并且包含一些CRUD条件定义。通过这三张表的定义,就可以实现半自动化代码生成。

    代码设计

    整体设计分为两部分,一部分通过MyBatis Plus Generator读取数据源配置,获取对应表的TableInfo信息,最终映射到表定义和列定义的表中,另外一部分就是通过Freemarker定义模板生成对应前端和后端代码。这里需要注意的是每个表名定义都是模块名加自身的业务抽象。
    image.png
    给大家看下核心代码,以下代码是通过MyBatis Plus Generator获取TableInfo,TableInfo包含数据库表的定义,后需要主要使用tableName、columns、tableComment等相关的属性,转换为tool_generator_table和tool_generator_column表相关的内容;

        private List<TableInfo> getAllTable(Long dataSourceConfigId, String tableName) {
            // 获得数据源配置
            ToolDataSourceConfig config =
                    dataSourceConfigService.getDataSourceConfig(dataSourceConfigId);
    
            // 使用 MyBatis Plus Generator 解析表结构
            DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(config.getUrl(), config.getUsername(),
                    config.getPassword()).build();
            StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder();
            if (StringUtils.isNotEmpty(tableName)) {
                strategyConfig.addInclude(tableName);
            }
            GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build();
            ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(),
                    null, globalConfig, null);
            List<TableInfo> tables = builder.getTableInfoList();
            tables.sort(Comparator.comparing(TableInfo::getName));
            return tables;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    以下代码是通过Freemarker生成对应的模版,具体的细节部分,可以在GitHub上查看;

    public Map<String, String> execute(ToolGeneratorTable table, List<ToolGeneratorColumn> columns) {
        //初始化
        Map<String, Object> bindingMap = initBindingMap(table, columns);
        //获取对应模版
        Map<String, String> templates = getTemplates();
        Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(templates.size());
        templates.forEach((key, value) -> {
            generateCode(result, key, value, bindingMap);
        });
        return result;
    }
    
    private void generateCode(Map<String, String> result, String vmPath, String filePath, Map<String, Object> bindingMap) {
        filePath = formatFilePath(filePath, bindingMap);
        try {
            StringWriter writer = new StringWriter();
            Template template = configuration.getTemplate(vmPath);
            template.process(bindingMap, writer);
            result.put(filePath, writer.toString());
        } catch (Exception exception) {
            log.error(vmPath + "模版加载异常" + exception);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    结束

    GIt地址 欢迎大家Star,下图截图是核心代码的重要实现的类。
    image.png

  • 相关阅读:
    【数据库原理与应用(第3版)】第一章:数据库系统概论(选择与填空)
    服务器补丁管理软件
    Spring修炼之路--基础知识
    【云原生】Helm 架构和基础语法详解
    PassUAC的简单实现(二)
    工程机械比例阀电流采集方案
    linux网卡驱动注册与接受数据处理
    百多安医疗冲刺科创板:半年营收1亿 为张海军与郭海宏夫妻店
    【Kaggle项目实战记录】一个图片分类项目的步骤和思路分享——以树叶分类为例(用Pytorch)
    状态压缩DP 图文详解(二)
  • 原文地址:https://blog.csdn.net/weixin_38592881/article/details/138200070