前言

谈到软件开发,总是避不开管理系统的,只要是一个系统总会附带 >=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 地址
前端代码生成线上直接使用地址


杨一一
15 声望0 粉丝