如何给个数据库就可自动生成完整后台系统,可视化添加定制功能?达到摸鱼神之境?

逻辑摆渡人
• 阅读 342

前言

谈到软件开发,总是避不开管理系统的,只要是一个系统总会附带 >=1 数据的后台管理系统。而管理系统的面貌就是功能重复、量大、管饱。可看似功能类型,却总不是重复的
所以总是要将这些类似的功能一个一个一遍一遍的手动开发,就会消耗大量的时间与人力。可是总是写这些重复没营养的页面,为人者总会心有不甘的。
所以就踊跃出一大批帝境强者,制造出各种一键生成增删改查功能的工具插件想来逆天改命。java 者的尤甚,而前端生成工具的略少,也许是框架层出不穷缘故,我所知有若依之列。
然后试了一下确实可以,不过要一个一个生成然后解压复制到本地对应目录,当数据表数量达到半步百张之境还是让人头疼不已。不过却实节约了大量时间与人力。
随之而来的是,事实上每个页面并不是纯增删改查之能,如:没有删除功能、没有查询行、某字段不可以编辑、A 数据依赖 B、操作列按状态展示、有审批功能等等。

思考

即使做为一条炼气境的小杂鱼,也还是想进一步思考如何去更好的解决此列问题。所以我在想,如果给我一个数据库(或创建库的 sql 结构)如何能最快完成我的需求的方案。

想法

没错,还是使用前后分离生成方案,因为主流。
服务端思路
老生常谈,没什么特别新颖的方法。读取表结构生成对应 PO、VO、service、controller 等等。方法就是先创建几个模板(静态文本原样输出、可以传入变量输入不定文本),
然后按需将表结构解析传入模板,生成文件到对应目录,到也不难。这里我们为了方便,不用去生成 sql 就用 mybatis-plus, 如我们的 controller 模板:

package ${package};

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.util.List;

import ${impPo};
import ${impVo};
import ${impService};

@EnableAutoConfiguration
@RestController
@RequestMapping(value="/${strutil.toLowerCase(entityName)}")
public class ${entityName}Controller {
    @Autowired
    private ${entityName}Service ${varName}Service;

    /**
     *${tableComment}添加
     **/
    @RequestMapping(value="/save", method=RequestMethod.POST)
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public Object save${entityName}(
            @RequestBody ${entityName}${poSuffix} ${varName}) {
        return ${varName}Service.save(${varName});
    }

    /**
     *${tableComment}修改
     **/
    @RequestMapping(value="/update", method=RequestMethod.POST)
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public Object update${entityName}(
            @RequestBody ${entityName}${poSuffix} ${varName}) {
        return ${varName}Service.updateById(${varName});
    }

    /**
     *${tableComment}添加或修改 
     **/
    @RequestMapping(value="/saveOrUpdate", method=RequestMethod.POST)
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public Object saveOrUpdate${entityName}(
            @RequestBody ${entityName}${poSuffix} ${varName}) {
        return ${varName}Service.saveOrUpdate(${varName});
    }

    /**
     *${tableComment}查找
     **/
    @RequestMapping(value="/search", method = RequestMethod.POST)
    public Object search${entityName}(@RequestBody ${entityName}${voSuffix} params) {
        ${entityName}${poSuffix} ${varName}${poSuffix} = new ${entityName}${poSuffix}();
        BeanUtils.copyProperties(params, ${varName}${poSuffix});
        Page<${entityName}${poSuffix}> page = new Page<>(params.getPageNo(), params.getPageSize());

        QueryWrapper<${entityName}${poSuffix}> queryWrapper = new QueryWrapper<>(${varName}${poSuffix});

        return ${varName}Service.page(page, queryWrapper);
    }
    
    /**
     *${tableComment}通过id删除
     **/
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    @RequestMapping(value="/removeById/{id}", method=RequestMethod.POST)
    public Object remove${entityName}ById(@PathVariable Integer id) {
        return ${varName}Service.removeById(id);
    }

    /**
     *${tableComment}删除
     **/
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    @RequestMapping(value="/remove", method=RequestMethod.POST)
    public Object remove${entityName}(@RequestBody ${entityName}${poSuffix} ${varName}) {
        return ${varName}Service.remove(new QueryWrapper<>(${varName}));
    }

    /**
     *${tableComment}删除多条
     **/
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    @RequestMapping(value="/removeByIds", method=RequestMethod.POST)
    public Object remove${entityName}ByIds(@RequestBody List<Integer> ids) {
        return ${varName}Service.removeByIds(ids);
    }
}

可以看到方法名,路径等都可以在模板里修改。启动项目暴露方法调用就能够生成基本了功能了,那么后端简单的增删改查功能接口就好了

前端思路

此处我们使用 react 加 antd 风格的吧,当然使用vue + 饿了摸滴呀也一样喽。读取表结构中的字段名做为名,数据类型来对应组件,是否为空为校验条件等。
按上面思路,诶定义模版,动态处理变化部分一哈子好像也就能生成出来了,但问题来了,我想要的是还能在生成的功能上以可视化去处理不要的字段,不对的组件、等其它的功能。
那么这样的话后续我就不好搞不定了。所以只能另寻它路,后来我将数据库的结构先解析成一个描述对象(JSON)在页面中对这个对象进行解析为设计结构并可以按实际运行预览,那么
我再修改设计结构不就可以修改描述对象了么,然后再将描述对象生成实际的代码是不是就可以实现我的需求了呢(代码生成 + 可视化编辑,或者也叫 LOWCODE 吧)

//如 JSON
[
    { 
        type: 'search',
        children: [
            { type: 'input', name: 'userName', label: '用户名' }
            { type: 'button', value: '查询' }
        ]
    },
    {
        type: 'region',
        children: [
            { type: 'button', value: '添加' }
            { type: 'button', value: '批量删除' }
        ]
    },
    {
        type: 'table',
        children: {
            { type: 'text', name: 'userName', label: '用户名' },
            [
                { type: 'button', value: '编辑' },
                { type: 'button', value: '删除' }
                { type: 'space' }
            ]
        }
    }
    ...
]

如上,先将数据库结构生成如上结构页面再以拖拽方式操作结构达到目的后点击再生成 React 代码:

    return (<div>
        <Form inline>
            <Form.Item name="userName" label="用户名">
                <Input />
            <Form.Item>
            <Form.Item>
                <Button>查询</Button>
            <Form.Item>
        </Form>
        ...
    </div>)

问题

但是服务端还是没想到好的方案可以自动生成定制化的功能,如果只是对数据的修改是很简单,可如果需要计算则没有好的实现方法(比如传到服务端数据要 + B表的某个字段值再入库等)。

路漫漫其修远兮,还需要继续探索

使用

抛砖引玉,看看设计的实验成果

准备好一个数据库,如先从一个简单的两页面开始,如下:


```sql
/*
Navicat Premium Data Transfer

Source Server         : localhost_3306
Source Server Type    : MySQL
Source Server Version : 50743
Source Host           : localhost:3306
Source Schema         : student_grade

Target Server Type    : MySQL
Target Server Version : 50743
File Encoding         : 65001

Date: 25/08/2023 16:12:20
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for grade
-- ----------------------------
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade`  (
`grade_id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NULL DEFAULT NULL,
`grade_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '考试名称',
`grade_chinese` float NULL DEFAULT NULL COMMENT '语文成绩',
`grade_math` float NULL DEFAULT NULL COMMENT '数学成绩',
`grade_english` float NULL DEFAULT NULL COMMENT '英文成绩',
`created_at` datetime NULL DEFAULT CURRENT_TIMESTAMP,
`deleted` tinyint(2) NULL DEFAULT 0,
PRIMARY KEY (`grade_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '成绩管理' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
`student_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`student_name` varchar(11) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '学生姓名',
`student_no` varchar(11) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '学号',
`student_in_time` datetime NOT NULL COMMENT '入学日期',
`created_at` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建日期',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除',
PRIMARY KEY (`student_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '学生管理' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
```

配置连接到数据库并启动服务端项目后,打开代码生成页面,点击生成:

如何给个数据库就可自动生成完整后台系统,可视化添加定制功能?达到摸鱼神之境?

看一下项目中已经生成了基本功能的代码

如何给个数据库就可自动生成完整后台系统,可视化添加定制功能?达到摸鱼神之境?

后端生成完成,我们再来操作下前端,连接或导入数据库结构 sql 再配置下接口路径等信息

如何给个数据库就可自动生成完整后台系统,可视化添加定制功能?达到摸鱼神之境?

如何给个数据库就可自动生成完整后台系统,可视化添加定制功能?达到摸鱼神之境?

生成后页面预览,基本功能都没有问题了,发现所以页面的基本功能只要测试一次

如何给个数据库就可自动生成完整后台系统,可视化添加定制功能?达到摸鱼神之境?

添加自定义的功能

如何给个数据库就可自动生成完整后台系统,可视化添加定制功能?达到摸鱼神之境?

使用视频

地址

服务端代码生成 github 地址
前端代码生成 gitee 地址
前端代码生成线上直接使用地址

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
3年前
sql注入
反引号是个比较特别的字符,下面记录下怎么利用0x00SQL注入反引号可利用在分隔符及注释作用,不过使用范围只于表名、数据库名、字段名、起别名这些场景,下面具体说下1)表名payload:select\from\users\whereuser\_id1limit0,1;!(https://o
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
美凌格栋栋酱 美凌格栋栋酱
4个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(