企业级自定义表单引擎解决方案(十一)–表单规则引擎1

.net core研发的自定义表单引擎,采用强大的规则引擎将所有的业务串联起来的,和其他低代码平台是有本质的区别的,目标是完全解放繁琐的CRUD工作。


规则引擎是自定义表单最为核心的内容,也是与其他低代码工具最为本质的区别,非常重要的内容,对于读者来说也是最难理解内容。
没有规则引擎的低代码工具,即使前端做得再花哨,也仅仅是解决了一些复制粘贴的问题
视图和表单都可以定义规则,规则将所有业务串联起来,至关重要。
内容比较多,分两篇文章介绍。

对象树

进行自定义表单页面,都会定义唯一一个最外层的表单,表单里面可以定义子表单、子视图(视图里面也可以定义子表单和子视图),视图或者表单里面会定义各种对象(可以是控件、弹框、行列信息等),整体构造一棵树,每一个节点都有唯一的编码,编码是根据树的每一个节点的Id组成,通过编码就可以找到唯一的对象。比如界面上的新增按钮的编码为“表单Id.列表视图Id.新增按钮控件Id”。
视图和表单Wrap、表单的表单列、ItemView的列,这几个地方可以定义子表单和子视图。

规则管理

表单、视图、控件都会触发各种事件,表单或视图对父表单或父视图一无所知,且只能控制操作直接子表单或子视图的对象,事件规则查找逻辑为:

  1. 如果是表单触发的事件,系统会查找本表单触发的规则(子表单Id、子视图Id、对象Id都为空)和父级(父表单或者父视图)定义的规则(子表单Id为触发事件的表单Id,子视图Id为空,对象Id为空)
  2. 如果是视图触发的事件,系统会查找本视图触发的规则(子表单Id、子视图Id、对象Id都为空)和父级(父表单或者父视图)定义的规则(子表单Id为空,子视图Id为触发事件的视图Id,对象Id为空)以及当父级为表单时,查找父级的父级定义的规则(子表单Id为父级表单Id,子视图为触发事件的视图Id,对象Id为空)
  3. 如果是对象触发的事件,系统直接查找父级规则(子表单Id为空、子视图Id为空、对象Id为对象Id)和父级的父级规则(子表单Id为空或者子视图Id为空(取决于父级的类型)、子表单Id为空或者子视图Id为父级Id(取决于父级的类型)、对象Id为对象Id)和父级的父级的父级(触发事件的对象的父级为视图,父级的父级为表单,且还会继续网上查找一级的规则,查找逻辑与其他情况类似)
    上述描述不好理解,可以结合到体验网站各种配置好的表单查看,多看一些规则就能理解清楚。
  • 业务分类
    只能是View或Form,系统自动选择
  • 子表单Id
    子表单Id
  • 子视图Id
    子视图Id
  • 对象Id
    表单或者视图内不同控件的Id,注意:Wrap对象的Id为:Wrap包裹的表单或视图Id_Wrap配置id
  • 事件名称
    基础控件的使用参见不同控件的ant的事件,自定义表单会将所有ant控件的事件抛出,可以定义规则进行拦截,其他自定义事件参考如下

viewloaded:视图加载时触发的事件
selectedrows:ListView的选中行
advancequery:ListView的高级查询按钮点击事件
pagechange:ListView分页触发事件
refresh:ListView页面刷新事件
selected:树视图节点选择事件
Workflow_FormValidate:工作流界面触发表单验证事件(流程管理界面点击提交按钮等地方)
Workflow_GetMethods:工作流管理获取表单信息事件(工作流管理界面装载自定义表单时,需要获取自定义表单信息)
Workflow_FormOpen:打开工作流管理界面事件
Workflow_TrySend:工作流预提交完成
Workflow_Save:工作流保存执行完成
Workflow_Send:工作流提交完成

另外,还可以自己定义一些事件,并配置执行规则,这些事件不是由系统自动触发,而是在需要的时候,可以在其他规则执行中选择执行其他规则,事件名称选择自定义的事件名称即可,这样就可以触发执行自定义的事件规则。比如在列表视图中,多个地方都会触发查询列表数据并绑定到表格中,此时就可以定义一个自定义的事件及规则,其他地方直接使用即可。

规则执行管理

一个事件会触发一系列的规则执行,每个规则执行都会有一个规则执行所在的宿主对象,宿主对象为事件所属的视图或者表单,规则执行都在相对宿主对象来定义执行的。
子表单、子视图、对象Id:这几个字段都是相对宿主对象来说有,某些规则执行需要指定规则执行的目标对象,比如绑定数据,需要知道将数据绑定到哪个目标对象上,比如可以绑定到本视图的某个按钮或者子表单的子视图的某个对象上。

  • 子表单Id
    子表单Id
  • 子视图Id
    子视图Id
  • 对象Id
  • 排序
    定义规则执行的先后顺序
  • 执行类型和执行配置
    不同的执行类型定义不同的配置支持,具体规则如下:
  1. 设置属性(actionType=1)
    设置目标对象的自定义属性,比如点击新增按钮弹出对话框,参考前端组件的属性,则设置对象为对话框Id(参见【Wrap对象定义】):66e68bf9-8e05-4691-bf99-ed95c820260d_modal1,执行设置为:{“visible”:true}
    特殊属性设置

弹出导入Excel对话框,{“type”:”importexcel”,”objectName”:”TemplateTests”,”applicationCode”:”Default”}
设置页码为1,{“skipCount”:0}
弹出模态对话框,{“visible”:true,”bodyStyle”:{“padding”:”10px”}},对象Id为af1756bb-da48-4b5f-821d-78aeeaddbd2e_modal1

  1. 执行方法(actionType=2)
    执行后端服务器方法,自定义表单默认了一系列默认方法执行,默认情况下满足绝大多数业务场景,默认方法不满足的情况下,可以定义Object对象方法,自定义执行逻辑,自定义方法的执行参考【方法管理
    方法执行配置结构:

applicationCode:应用编码,默认即可
isTransaction:是否封装到事务中执行,多数情况填写true
methods:执行的一系列后端方法集合,具体参数如下:

  • ruleId:方法执行Id,任意定义的值,用于存储方法执行的结果
  • objectName:方法所属的对象
  • methodId:如果是执行自定义的Object对象方法,则填写Object对象方法的Id,否则填写系统默认的方法名称,具体名称见下文
  • paramModel:server或者local,默认为local,定义方法参数是从前端绑定还是后端其他方法执行结果获取
  • serverParams:如果paramModel为server时使用,
  • userFields:定义查询结果哪些字段是需要构造用户名称的字段(数据库只存储用户id,界面显示的时候,需要显示用户名,系统自动为查询结果添加”原始字段_UserName”字段)
  • remoteFieldInfos:远程关联字段特殊处理(常常用在外键关联的地方,数据库只存储远程对象的Id,界面需要显示外键绑定的值),具体参考【外键关联处理
  • instanceIdField:如果表单挂接到流程引擎,系统自动带出流程相关数据字段,这里定义关联的流程定义字段名称,系统查询流程管理,获取流程审批状态等信息
  • execType:workflow_formId或者workflow_fact,只在流程引擎处使用,标识方法是获取表单Id值还是事实库Fact值
  • baseConfig: 基础配置,可空,配置之后,会先应用基础配置,再执行后续的传递替换参数值
  • transfors:执行方法所需的参数的绑定规则,真正请求的时候,参数封装到datas对象(如果是数组,请采用“字段名称:数组索引”,具体设置某个数组对象的值),datas具体属性内容参见不同方法执行配置举例。传递数据配置:
  1. paramName:需要赋值的参数名
  2. transType:参数传递类型,query、exportexcel、eventData等,具体值参考后续规则执行示例,eventData表示从事件触发处获取参数值,比如点击列表删除按钮,会将当前删除按钮所在行的数据加入到触发事件的事件绑定值上,可以在规则执行时获取
  3. transTypeParam:给具体参数哪个属性赋值,如Get方法需要给id赋值,参数层级比较深时,可以用冒号:隔开等。
  4. transfors:递归给参数赋值,当参数层级比较深时,需要递归到参数所在的层级再进行赋值,比如datas.paramValues.id字段赋值,则需要递归到paramValues对象,给paramValues对象的id属性赋值
    方法返回结果:[{ruleId:"XX1",result:object1},{ruleId:"XX2",result:object2}]
  • Get、Fact方法:
    Get方法可以传递paramValues和sqlFields参数,paramValues只需定义主键信息,sqlFields可以定义查询的哪些列,Fact方法同Get完全一致,流程引擎执行Fact方法获取表单实体对象
    例:

配置:{"aplicationCode":"Default","isTransaction":true,"methods":[{"ruleId":1,"objectName":"TemplateTests","methodId":"Get","transfors":[{"paramName":"paramValues","transfors":[{"parentParamName":"paramValues","paramName":"id","transType":"eventData","transTypeParam":"id"}]}]}]}
实际请求:{"routeName":"SingleTemplateTest","applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"Get","ruleId":1,"objectName":"TemplateTests","paramModel":"local","datas":{"paramValues":{"id":"0b3cb8d7-6cb4-44bf-a480-2003bf4733b4"}}}]}
说明:将触发事件绑定的对象的id字段赋值给datas.paramValues.id参数,执行默认的Get方法

  • CreateOrUpdate、Create、Update方法
    三个方法参数相同,一般情况调用CreateOrUpdate方法即可,新增或者修改数据库表数据,当id字段为空时,执行新增Create方法,当id字段不为空时,执行修改Update方法
    例:

配置:{"aplicationCode":"Default","isTransaction":true,"methods":[{"ruleId":1,"objectName":"TemplateTests","methodId":"CreateOrUpdate","transfors":[{"subFormId":"66e68bf9-8e05-4691-bf99-ed95c820260d","subViewId":"ecd0c03d-a67a-45a0-a64c-9c53e3522fb1","paramName":"paramValues","transType":""}]}]}}
实际请求:{"applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"CreateOrUpdate","ruleId":1,"objectName":"TemplateTests","paramModel":"local","datas":{"paramValues":{"id":"0b3cb8d7-6cb4-44bf-a480-2003bf4733b4","field1":"12","field2":"12",......}}}]}
说明:在ListView视图对话框保存按钮执行时,将子表单的子视图(ItemView)的数据传递到方法参数,执行CreateOrUpdate方法

  • Delete方法
    删除数据库一条数据
    例:

配置:{"ruleId":1,"objectName":"TemplateTests","methodId":"Delete","transfors":[{"paramName":"paramValues","transfors":[{"parentParamName":"paramValues","paramName":"id","transType":"eventData","transTypeParam":"id"}]}]}
实际请求:{"routeName":"SingleTemplateTest","applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"Delete","ruleId":1,"objectName":"TemplateTests","paramModel":"local","datas":{"paramValues":{"id":"499e1b14-5bd4-4d8d-bb72-46558996fbbd"}}}]}
说明:在ListView的表格某一行点击删除按钮,执行删除方法,将点击事件绑定的对象的id字段赋值给参数datas.paramValues.id,执行删除方法

  • DeleteWhere方法
    按照自定义条件批量删除数据,比如ListView的批量选择删除功能,自定义条件参见附录【自定义查询条件
    例:

配置:{"aplicationCode":"Default","isTransaction":true,"methods":[{"ruleId":1,"objectName":"TemplateTests","methodId":"DeleteWhere","transfors":[{"transType":"batchdelete"}]}]}
实际请求:{"applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"DeleteWhere","ruleId":1,"objectName":"TemplateTests","paramModel":"local","datas":{"sqlWheres":{"sqlExpressType":1,"children":[{"sqlExpressType":3,"field":"Id","conditionType":4,"value":["147632d6-7b12-411b-a7f0-c83744fb46c1","411c9856-07aa-4427-830c-35d6bfa29220"]}]}}}]}
说明:transType为batchdelete,系统自动将用户批量勾选的数据id构造为数组赋值给参数,调用批量删除功能

  • GetWhere方法
    同Get方法,只是查询条件不是以id查询,是自定义的sqlWhere查询条件
  • UpdateWhere方法
    同Update方法,只是查询条件是自定义的sqlWhere查询条件
  • PageList方法
    ListView视图列表数据分页查询,将查询数据构造为查询条件参数,执行后端分页查询获取数据库数据,参见附录【自定义查询条件
    例:

配置:{"ruleId":1,"objectName":"TemplateTests","methodId":"PageList","userFields":"creatorId;lastModifierId;userField;userFields","remoteFieldInfos":[{"objectName":"OTN1","field":"singleField","script":"${obj.string1}(${obj.sex})"},{"objectName":"OTN1","field":"singleFields","script":"${obj.string1}(${obj.sex})"},{"objectName":"Student","field":"studentId","script":"${obj.name}(${obj.userName})"}],"transfors":[{"transType":"query"}]}
实际请求:{"routeName":"SingleTemplateTest","applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"PageList","ruleId":1,"objectName":"TemplateTests","userFields":"creatorId;lastModifierId;userField;userFields","remoteFieldInfos":[{"objectName":"OTN1","field":"singleField","script":"${obj.string1}(${obj.sex})"},{"objectName":"OTN1","field":"singleFields","script":"${obj.string1}(${obj.sex})"},{"objectName":"Student","field":"studentId","script":"${obj.name}(${obj.userName})"}],"paramModel":"local","datas":{"sqlSkipCount":0,"sqlMaxResultCount":15,"sqlWheres":{"sqlExpressType":1,"children":[{"sqlExpressType":3,"field":"Education","conditionType":5,"value":"master"},{"sqlExpressType":3,"field":"studentId","conditionType":1,"value":"05781136-4fef-4961-b5d7-d5b6073ddd37"},{"sqlExpressType":3,"field":"userFields","conditionType":5,"value":"39f9719b-0a41-6364-133c-e9c39c92d01a"}]}}}]}
说明:将ListView查询区域用户输入的值构造为参数参数,执行分页查询功能,userFields参数定义哪些字段是用户字段,需要显示用户名,remoteFieldInfos标识哪些字段是外键关联字段,需要关联显示外键信息,参见【外键关联处理

  • ListWhere方法
    与PageList方法类似,只是去掉了分页功能,可以用在导出Excel等不需要分页获取数据集合的地方
    例:

配置:{"aplicationCode":"Default","isTransaction":true,"methods":[{"ruleId":1,"objectName":"TemplateTests","methodId":"ListWhere","userFields":"creatorId;lastModifierId;userField;userFields","remoteFieldInfos":[{"objectName":"OTN1","field":"singleField","script":"${obj.string1}(${obj.sex})"},{"objectName":"OTN1","field":"singleFields","script":"${obj.string1}(${obj.sex})"},{"objectName":"Student","field":"studentId","script":"${obj.name}(${obj.userName})","transfors":[{"transType":"exportexcel"}]}]}
实际请求:{"routeName":"SingleTemplateTest","applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"ListWhere","ruleId":1,"objectName":"TemplateTests","userFields":"creatorId;lastModifierId;userField;userFields","remoteFieldInfos":[{"objectName":"OTN1","field":"singleField","script":"${obj.string1}(${obj.sex})"},{"objectName":"OTN1","field":"singleFields","script":"${obj.string1}(${obj.sex})"},{"objectName":"Student","field":"studentId","script":"${obj.name}(${obj.userName})"}],"paramModel":"local","datas":{"sqlWheres":{"sqlExpressType":1,"children":[]},"excelDicts":[{"dict":"edu","field":"education"},{"dict":"sex","field":"sex"},{"dict":"post","field":"post"}],"excelName":"单表测试Excel数据","excelTemplate":[{"name":"string字段","field":"field1","fieldType":5,"isRequred":true,"validateType":0},{"name":"string2字段","field":"field2","fieldType":5,"validateType":0},{"name":"Int字段1","field":"fieldInt1","fieldType":1,"validateType":1},{"name":"int2字段","field":"fieldInt2","fieldType":1,"validateType":1},{"name":"datetime字段1","field":"fieldDatetime1","fieldType":7,"validateType":10},{"name":"datetime字段2","field":"fieldDatetime2","fieldType":7,"validateType":10},{"name":"bool字段","field":"fieldBool1","fieldType":4,"validateType":1},{"name":"学历","field":"education","fieldType":5,"validateType":11},{"name":"性别","field":"sex","fieldType":5,"validateType":11}]}}]}
说明:这个例子为导出Excel的配置,transType为exportexcel,excelName定义导出的Excel名称,excelDicts定义哪些字段是数据字典,excelTemplate定义导出Excel的模板信息,参见附录【导入导出Excel模版】其他参数同PageList方法说明

  • MultiPageList方法
    与PageList方法类似,PageList只能处理单表数据,对于多表关联处理无能为力(最多只显示关联表一个字段显示),此方法专门用于多表关联处理
    例:

配置:{"aplicationCode":"Default","isTransaction":true,"methods":[{"ruleId":1,"objectName":"OTO1","methodId":"MultiPageList","transfors":[{"transType":"query","aliasInfos":"OTO1:a;OTO2:b","joinInfos":"OTO1 a LEFT JOIN OTO2 b ON a.Id=b.Id"}]}]}
实际请求:{"applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"MultiPageList","ruleId":1,"objectName":"OTO1","paramModel":"local","datas":{"sqlSkipCount":0,"sqlMaxResultCount":15,"sqlWheres":{"sqlExpressType":1,"children":[]},"joinInfos":"OTO1 a LEFT JOIN OTO2 b ON a.Id=b.Id","aliasInfos":"OTO1:a;OTO2:b"}}]}
说明:特殊使用参见【外键关联处理】,其他同PageList方法

  • MultiListWhere
    与ListWhere方法类似,ListWhere只能处理单表数据,对于多表关联处理无能为力(只多只显示关联表一个字段显示),此方法专门用于多表关联处理
    说明:特殊使用参见【外键关联处理】,其他同ListWhere方法
  • TreeListWhere
    与ListWhere方法类似,此方法主要是针对树结构的数据库表进行查询,查询逻辑: 先根据查询条件查询出满足条件的数据,再把所有节点的祖先节点查询出来,构造为一树完整的树。
    例:

配置:{"aplicationCode":"Default","isTransaction":true,"methods":[{"ruleId":1,"objectName":"Depts","methodId":"TreeListWhere","transfors":[{"transType":"querytree"}]}]}
实际请求:{"applicationCode":"Default","isTransaction":true,"methods":[{"methodId":"TreeListWhere","ruleId":1,"objectName":"Depts","paramModel":"local","datas":{"sqlWheres":{"sqlExpressType":1,"children":[{"sqlExpressType":3,"field":"title","conditionType":5,"value":"后端"}]}}}]}
说明:获取树不会涉及到分页,整棵树完整数据显示,另外注意,树结构数据库对象自动添加维护PId、TreeCode、Path、Icon、Title字段

  • BatchCreate方法
    批量新增数据,比如导入Excel,获取一对多关系,批量添加子表数据等场景。
    例:

配置:{"aplicationCode":"Default","isTransaction":true,"methods":[{"ruleId":1,"objectName":"OTN1","methodId":"CreateOrUpdate","transfors":[{"subFormId":"e928438e-645f-45fb-9fe7-88c74dafaa00","subViewId":"8c78e079-c23d-4d27-907e-76eadfc30b05","paramName":"paramValues","transType":""}]},{"ruleId":2,"objectName":"OTN2","methodId":"BatchCreate","paramModel":"server","serverParams":[{"ruleId":"1","targetFields":"datas#oTN1_Id"}],"transfors":[{"subFormId":"e928438e-645f-45fb-9fe7-88c74dafaa00","subViewId":"852c8f20-c4f2-4e7f-bccb-1573d7a3af5c","transType":""}]}]}
说明:此配置执行一对多数据保存逻辑,执行两个方法,先执行主表的CreateOrUpdate方法,再执行BatchCreate批量新增子表数据方法,BatchCreate的执行逻辑为:子表的数据是从子表单XX子视图XX获取(ListView表格数据),主表执行完CreateOrUpdate方法后,将结果(主键)值赋值给子表的oTN1_Id字段,再执行子表的BatchCreate批量新增子表数据方


wike文档地址:https://gitee.com/kuangqifu/sprite/wikis/pages
开源地址:https://gitee.com/kuangqifu/sprite
体验地址:http://47.108.141.193:8031 (首次加载可能有点慢,用的阿里云最差的服务器)
自定义表单文章地址:https://www.cnblogs.com/spritekuang/
流程引擎文章地址:https://www.cnblogs.com/spritekuang/category/834975.html (采用WWF开发,已过时,已改用Elsa实现,https://www.cnblogs.com/spritekuang/p/14970992.html

张贴在2