全栈低码设计开源框架 json-script-rule 简介

前言

json-script-rule是一款开源的全栈低码设计框架,之所以说是设计框架,是因为它更倾向于程序的设计,将传统的低码概念(将程序员由面向代码转向面向业务)升级到由面向业务转向面向程序设计,通过在后台组装横向的代码逻辑将其封装成一个个功能插件进而再通过页面直接输入指令达到一种可配置的动态代码的低代码功能架构。

为了更好的能够理解其中的含义,我们先来举一个简单的例子进行说明,假设你写了一个Service A,如果你的代码考虑的很全,设计的很好,那么你可以通过参数可以随时调整这个接口的功能,但如果你考虑的不是很全面,那有可能某一天因为一个新功能,你可能要去修改这个Service A。除此之外,假设这个Service A某一天你将它做的非常完美了,那么你又如何将它发布给其它人去用呢?这个时候你可能会去想假如当初有人给我提供了这个Service A我是否不用花精力和心思再去写一个新的了?而我写的插件似乎又没法分享给其它人,甚至自己的项目中也无法做到很好的迁移,造成了同样的功能不仅要写大量的代码,而且复用性还很差,不仅如此在使用过程中还需要去看对方的代码,真的是辣眼睛0 0

说了这么多其实我们可以再转换一个思路去思考一个问题,假如有一天你只需要在前端或postman中输入一行指令,就能让你得到你想要的结果,比如去查询一个表的数据,那么你只需要输入get tableA,比如将某张表的数据用excel导出来,那么你只需要输入export tableA,这样看起来是不是会很舒服?我想答案是肯定的,当然了,实际上的功能不可能只有这么简单,它可能会让你导出到某个路径下,那么我们也可以更改一下指令export tableA to path D:/xxxx。总而言之,我们希望像yum或Homebrew那样,只需要通过命令来解决代码的问题,那么我们就需要将插件设计好,这个时候我们的json-script-rule便有了用武之地了。

json-script-rule顾名思义它是由json脚本所组成的前后端通用的规则引擎架构,它意在解决一些简单的、通用的业务场景的逻辑封装,从而达到一种可通过配置的方式来实现后端功能的效果。目前框架包含增、删、改、查、上传、导入、导出、主子表插入,分流器等9个内置插件,我们可以在任意地方调用这些插件来实现一些简单的功能,无需后端代码,无需后端部署,只需要在前端写一行简单的json脚本即可实现一个功能接口的开发。此外它还支持自定义插件的开发,你可以将一些简单的、通用的业务逻辑封装成一个自定义的插件集成到框架插件库中,最后再通过调用这些封装好的插件来实现快速0码开发

那么这个东西它与我们平常的开发到底有什么不同?它有哪些优势?它又能解决开发者的哪些痛点呢?
接下来将会用几篇文章详细概述这个框架的使用,首先我们先简单直观的对比一下传统开发与低码开发的差异,说到差异这里就不得不先说一下传统方案的一些弊病,先以mybatis和jpa为例:

  1. 生成的文件过多,如果按照规范开发,每当增加一个功能或者一个表的时候,通常都会增加一个controller、一个service、一个impl、一个mapper、一个xml、一个po,dto,vo甚至do等,造成打包后的项目变得不再"轻量"
  2. 查询数据库的代码中嵌入了过多的eq,And,Or,isNull等耦合性和可读性均较差的代码,且不同的人用的框架不一样,所写的逻辑也不一样,没有一个统一的标准,让人不得不去一行一行的读代码来理解前任开发者的逻辑
  3. 每当项目出了bug,或是业务上的更改,都需要修改后端代码才能解决问题,并且需要重新打包部署才能生效,这样不仅增加了工作量,也拉长了开发周期,更容易出现反复测试反复重现bug的现象
  4. 开发人员的水平良莠不齐,尤其是在做项目时很少有人会考虑程序的设计、封装、复用等特性,一旦出现各写各的代码,随着人员的流动公司原有的产品将会变得十分难以维护,不得不进行重构
  5. 有些通用的功能缺乏统一的标准,如发送短信,上传图片等,虽然有第三方的api支持,但是每个公司对这些api的后台处理逻辑却是不相同的,缺少一个功能强大的统一插件,这样就不需要重复开发了。
  6. 如果你的项目里一旦确定使用了某一种框架,如mybatis或是jpa等,那么日后想要更换几乎是不可能的,因为这样的改动很有可能会带来巨大的工作量
  7. 一旦类和服务写的过多,当你修改一个功能时你会发现你需要一层一层的去逐源,此外当你展开一个文件夹时映入眼帘的将会是大量的文件,看了便让人觉得眼花缭乱,不仅来回切换十分的麻烦,而且极容易改错
  8. 如果后端代码的改动涉及到了前端,又或是前端想调整json结构,如返回字段的名称有改变,返回字段的个数有改变,返回数据的主子表结构或顺序有改变等等,这些全都需要后端配合才能完成,这不仅增加了沟通的成本还极大的降低了开发的效率,需要依赖后端开发人员不说而且还非常的麻烦,时时刻刻都要考虑改动所带来的成本,因此在敏捷开发中绝对是个硬伤
  9. 调试错误十分耗费时间,通常开发一个接口可能需要借助各种工具或框架,一旦出现错误,很难在短时间内排查出问题并加以解决

说完了几个传统开发的痛点,那么接下来看看json-script-rule它是如何开发一个功能的。
在进行开发之前首先需要引入依赖和po类的配置,这些将在稍后会进行具体的说明。此外,如果你希望降低学习成本,不使用原生的方式,那么你还需要在前端还需要引入json-script-rule.js这个文件(用于简化构建json),接下来从项目上截取一部分vue开发的例子做一个demo:

export function requestRule(body) {
  return request({
    url: "/json/script/start",
    method: 'post',
    data: body
  })
}

上面的代码是前端自己封装的方法,用于处理所有调用json-script-rule的请求(如设置token什么的),如果没有更改框架内置控制器的路径的话,那么这个url是系统默认的/json/script/start,是固定不变的(如果配置了context-path则需在前面加上),接下来在前端只需要一行代码便可以实现一个插入的功能,如下:

import { jsr_build_body } from "@/xx/xxxx/json-script-rule";
add() {
      let body = jsr_build_body([{ name: 'asddsa', plugin: 'add', class: 'ClassName', datas: this.form }]);
      requestRule(body ).then(response => {
        if (response.code == 200) {
          this.msgSuccess("新增成功");
          this.open = false;
          this.getList();
        }
      });
}

说明:上面的代码只是列举了一个简单的例子,相信稍微有些基础的朋友一眼就能看得懂,这里实现功能的一行代码就是构建body的jsr_build_body这个方法,它是json-script-rule.js中封装的方法,用于简单构建json脚本的,这一行的代码里有几个关键属性,如下:

  • name:是随便起的名字,用于标识返回结果的名字
  • plugin:表示调用的是框架内置的add插件
  • class:对应的是po实体类名(实体类会映射表名)
  • datas:要插入的数据对象数组

总结:至此,一个后台的插入功能便做好了,整个过程我们只在前端配置了4个参数,可以说开发效率是非常的快,而且对于前端开发者来说,如果你会使用这个插件,那么只要是框架内置的这些插件支持的功能,则都可以通过这种方式来实现全栈的开发,即便是内置插件不满足的功能,你也可以通过框架提供的自定义插件开发来实现这种低代码配置化开发。而对于后端开发者而言整个过程后端是不需要写任何代码的,即便哪一天业务变更了,也不需要更改后端的代码,只需要调整前端json的参数就可以了,比如说由原来的插入A表变成插入B表,比如说插入A表后再插入C表D表等,都可以通过调整json参数来实现,只需要修改class的属性以及增加action的个数即可,而且修改完成后也不需要后端打包部署,十分的便捷。

介绍

json-script-rule是一个依赖于spring容器的全栈低码开发框架,它的设计主要依赖于两个重要的概念,一是前后倒置,二是面向规则(配置)。

  • 前后倒置:是指后端的java代码由前端的json脚本来替代,前端通过配置post请求中的json参数来动态的调整后端插件的功能,结合shunt插件,可以将一部分逻辑转移到前端。
  • 面向规则:将一些代码高度内聚化,按照功能封装成一个个可配置功能的插件,进而实现其高可读、高复用和高扩展的特性。

目前crud插件支持 mysql,postgresql,kingbase,oracle(6,8,10) 数据库,绝大部分的查询场景均支持,这里包括字段,函数,分组,排序、关联,条件,左右内外连查询,子查询和视图查询等等。

优点
  1. 前后端通用的设计,无论是后端还是前端都可以使用
  2. 面向规则开发,强化代码设计,有效规避bug,提升代码复用性,节约开发时间成本
  3. 后端不需要打包及重新部署,即改即出结果
  4. 轻量级容器,整个jar包大概200kb左右
  5. 支持扩展,自定义开发,整个框架的设计几乎可以满足大部分定制化开发需要
  6. 采用分离式设计,不耦合于主项目的bean配置,如mybatis配置sqlSessionFactory,pagehelper配置分页等,插件中的配置属性与主项目的完全分离,互不干扰,无论是在pom中直接引用starter或是单独的包,或是代码中实现的配置属性,均不产生耦合
  7. 日志体系健全,对于所发生的错误能够通过日志信息快速定位问题
  8. 代码侵入性低,采用以空间换时间的方式优化性能
  9. 兼容多个数据库,采用单独处理的方式处理各种数据库之间的差异
  10. 提供自定义开发所需要用到的注解,工具,配置,接口,以及缓存等
  11. 支持加密处理,目前仅支持国密sm2非对称性加密(集成hutool包处理)
  12. 安全接口,有效杜绝sql的注入
  13. 一次请求可返回多个结果,因此不需要加载一个页面时请求后台的多个接口
  14. 框架本身独立于项目,是个基于spring容器的小型容器,因此项目即便更换框架后,那些原有的已经用该框架开发过的接口也不会受到任何的影响
  15. 安装上手十分简单接地气,后端只需要简单的两步就可以进行json开发了,执行效率非常高
  16. 插件可以极大程度的在不同的环境下进行复用,迁移都十分的方便
  17. 不仅有效降低了项目大小,而且还减少了书写开发文档的时间
  18. 开源的微框架,国人项目,且一直在维护,后续可能还会发布一些更为全面的功能以及插件,可以极大的减少开发的工作量
  19. 结合良好的横向设计,再进行纵向的封装,便能够纵横捭阖,一动皆动
缺点
  1. 仅支持http协议
  2. 仅支持post请求
  3. 仅支持单数据源(多数据源下可指定其一)
  4. 不支持webflux
插件主要依赖说明

jdk1.8
spring boot dependencies
lombok

常见问题说明

错误信息:Unsupported class file major version 61.
解决:spring boot以及mybatis框架等恢复支持jdk1.8的版本,如spring的2.7.10,mybatis2.3.1以下的版本

安装说明
  1. 引入依赖,仅此一步即可


    io.github.ying1dudu
    json-script-rule-spring-boot-starter
    3.2.7

提示:如果maven无法下载或想体验抢先版本的则可以直接到插件地址下载jar包并放入本地maven库里,这里面还包括了json-script-rule.js等前端、postman脚本等
插件地址:https://gitee.com/ying1dudu/json-script-rule-jar.git

  1. 如果你不使用框架内置的crud功能(包括引用crud插件的导入导出和主子表),那么此步可以跳过,application(yml和properties等文件类型都可以)中配置po实体类(映射数据库表或视图的java简单对象)的包路径,属性为locations(3.2.4版本以后支持多路径扫描,多个用逗号分割),如下

edi:
  rule:
    locations: xx.xx,yy.yy,zz.zz

注意1:locations可以指向一个或多个目录,它用于识别po实体类存放的位置(它可以指向你旧有的mybatis的目录),框架中所说的直接类名是指以这些目录为basePackges的包名+类名,自3.2.4版本以后,JSRuleTable注解不是必须的注解,如果不加该注解则跳过该po实体类(不做缓存则不能进行crud操作)**,JSRuleField注解也是非必须的,如果你的变量名刚好和表字段名一致,那么就不需要配置该注解

注意2:尽量避免多个根目录下含有相同的包名+类名,如果无法避免名称冲突,则需要写上类的全名(Class.getName(),只有冲突时才会用到全名),如配置外键注解fk属性时,且在使用json时前缀也要换成类的全名而不能用locations根目录下的直接类名,如果没有名称冲突,则json正常使用直接类名即可(默认禁用全名)

提示1:多模块应用下开发一样可以通过locations指向子模块po实体类路径(只要这些路径可以通过getResource获取到即可),locations通常在启动模块的application配置文件中配置即可,此外还可以通过继承JSRuleExtendDefault类或实现IJSRuleMappingsInfo这个接口来自定义数据库或xml等文件中存储的信息,将其对象化后可加载到系统缓存中,通过代码手动一样可以实现类似locations的配置(详情参考自定义开发)

提示2:locations所指向的目录全部都是根目录,这些根目录下还可以有子目录(这些目录下的包名+类名尽量避免名称冲突),在使用json进行请求时,classes属性中的类名应该以根目录为basePackges,如果根目录下还有子目录(包)则类名前面应加上这个包名,此为直接类名,如下:包名.ZsTestPO便是直接类名

{
    ......
    "relation":{
        "classes":["包名.ZsTestPO","xx.ZsTestSon2"]
    }
}

提示3:如果在当前配置文件中配置了spring.profiles.active属性并指向了其它配置文件,则子配置文件将会覆盖当前属性
至此,通过上面的两个步骤,整个框架便安装完成了,这其实和大多数集成在springboot的第三方框架一样

使用说明

由于插件的功能比较多,此处只简单说明查询插件的最基本的用法,后面将会推出更为详细的文档说明。
注:实例中为了配合说明,故意加了很多不规范的命名,如多加了两个空格,如有下划线等,此外还加了很多重复的字段等

  1. 配置po类,示例如下

@JSRuleTable(name= "zs_test")
@Data
public class ZsTestPO {
    @JSRuleField(pk=true)
    private String id;
    private String name;
    @JSRuleField(name= " create_date ")
    private Date create_date;
    @JSRuleField(name= "birth_day")
    private Date birthDay;
    private double salary;
    private String remark;
    private Double bonus;
    @JSRuleField(name= "test_field",alias="sum_test_field")
    private String test_field;
    @JSRuleField(name= "sum1_salary",alias="lbv_salary")
    private String sumSalary;
    @JSRuleField(name= "qian",imports= {"qian"})
    private String qian;
}

@JSRuleTable(name="zs_test_son2")
public class ZsTestSon2 {
    @JSRuleField(pk=true)
    public String id;
    @JSRuleField(name= "zs_test_id",fk="ZsTestPO",dependent= "ZsTestPO.id")
    public String zs_test_id;
    @JSRuleField(name= "zs_test_id",fk="view.neibu.ZsTestPOView",dependent= "view.neibu.ZsTestPOView.id")
    public String zs_test_id2;
    @JSRuleField(name= "zs_test_id",fk="ZsTestPOCopy")
    public String zs_test_id3;
    @JSRuleField(name= "zs_test_son1_id",fk="ZsTestUpdate")
    public String zs_test_son_id;
    @JSRuleField(name="zs_test_son1_id",fk= "view.ZsTestView")
    public String zs_test_son1_id2;
    @JSRuleField(name= "oh_yes")
    public String ohYes;
    public String test_field_a;
    @JSRuleField(dependent= "ZsTestPO.name")
    public String name;
}

说明:所有的json所面向的操作均是后端的class类(无论是Po实体映射类还是model参数类)对象而非数据库表,因此尽管表里有10个字段,但在po类里你配置了8个字段,那么你就只能用这8个字段,如果多配置了一些不是表里的字段,那么需要用JSRuleField注解中的ignore属性设置为true对其忽略处理,否则使用的时候会报错

参数说明:
  • pk:设置主键(目前仅支持单主键,不支持复合主键,也不提议使用复合主键)
  • fk:设置外键,这里的值对应的是直接类名,直接类名就是在 locations 属性所配置的多个basePackges根路径下的位置开始,在这个目录下如果有包名则加上包名,例如xx.ZsTestPO便是直接类名,此外,直接写成全类名也是可以的
  • name:表示对应数据库的表字段名,如果没有配置则默认认为java字段名即为数据库表字段名
  • alias:表示数据库表字段的别名,如果设置了则会以别名的形式返回给前端
  • imports:设置导入时,模板文件头部行对应的某表列的字段名,后续将会在导入规则中详细说明
  • exports:设置导出时,如果没有设置模板,将会使用此属性进行默认导出列的头
  • dependent:设置主子表的依赖关系,使用主子表时子表的某个字段值将从主表中获得,如主外键关联
  1. 启动开发者自己的本地项目,并用postman进行测试http://localhost:端口号/配置的context-path路径/json/script/start

11:05:26.901 [main] INFO  e.r.w.s.JSRuleStart - [printInfomation,38] - action=edi.rule.model.JSRuleAction
11:05:26.901 [main] INFO  e.r.w.s.JSRuleStart - [printInfomation,39] - mapper=edi.rule.frame.mybatis.dao.MapperForMysql
11:05:26.901 [main] INFO  e.r.w.s.JSRuleStart - [printInfomation,40] - dbFields=edi.rule.extend.adapter.JSRuleMysqlSysFields
11:05:26.901 [main] INFO  e.r.w.s.JSRuleStart - [printInfomation,41] - dbFunctions=edi.rule.extend.adapter.JSRuleMysqlFunctions
11:05:26.901 [main] INFO  e.r.w.s.JSRuleStart - [printInfomation,42] - handler=edi.rule.work.processor.JSRuleInitByJson
11:05:26.901 [main] INFO  e.r.w.s.JSRuleStart - [printInfomation,43] - locations=edi.business.po
11:05:26.901 [main] INFO  e.r.w.s.JSRuleStart - [printInfomation,44] - rootPath=D:\workspace\json-script-rule\rule
  1. 通过postman请求接口,json如下

{
    "rule": {
        "actions": [
            {
                "name": "test-get",
                "get": {
                    "relation":{
                        "classes":["ZsTestPO","ZsTestSon2"]
                    },
                    "isGroupShow":true,
                    "fields":["ZsTestSon2.id","salary"],
                    "condition":{
                        "where":{
                            "equal":{"ZsTestPO.id":["5260c8c5-47f2-4890-aff3-6b4fd5fb62f0"]}
                        }
                    }
                }
            }
        ]
    }
}
参数说明:
  • classes:表示关联的类的相关信息,也就是相关联的表的信息,这里的ZsTestPO是类名而不是表名
  • name:action作为结果返回的唯一标识
  • get:表示此次操作为查询动作
  • where:是matches属性的别名,表示具体的查询条件,这里表示查询ZsTestPO实体类对应的表的id字段等于"5260c8c5-47f2-4890-aff3-6b4fd5fb62f0"的数据(注意这里的属性是一个数组,用于某些条件为某一个值或(or)为null的情况)
  • fields:表示所要查询并返回的字段
  • isGroupShow:表示将两个表的数据分开显示(因此这里实际上datas中只有2条数据返回)
    返回结果如下

{
    "code": 200,
    "msg": "操作成功",
    "result": {
        "test-get:": {
            "pageNum": 1,
            "pageSize": 10,
            "totalPage": 1,
            "totalCount": 2,
            "datas": [
                {
                    "ZsTestSon2": {
                        "id": "2d20b69a-6980-4354-8723-8fe922414926"
                    },
                    "ZsTestPO": {
                        "salary": 2200
                    }
                },
                {
                    "ZsTestSon2": {
                        "id": "dba7c159-4442-49dc-a5da-bf5ba28a0d17"
                    },
                    "ZsTestPO": {
                        "salary": 2200
                    }
                }
            ]
        }
    },
    "log": null
}

所解析的sql为

select zs_test_son2.id as "ZsTestSon2-id" ,salary as "salary"  from  zs_test_son2 , zs_test  where   zs_test.id=zs_test_son2.zs_test_id and zs_test.id='5260c8c5-47f2-4890-aff3-6b4fd5fb62f0'

至此,后端的json-script-rule便安装测试完成了,接下来的几篇将介绍如何使用

你可能感兴趣的:(低码,数据库,json,spring,boot,mybatis,maven)