工作流的概念在百度百科和wiki上描述的都比较抽象,我们可以大致理解为做一些事情的时候,我们把做事情抽象为几个步骤,然后再合理的组织这些步骤,最终通过组织好的步骤来做这些事情。例如我们早上起来做的事情看做一项工作流,可以拆分为上厕所、洗手、刷牙、吃饭这些步骤。有的人“上厕所->洗手->吃饭->刷牙”;有的人“刷牙->上厕所->吃饭->洗手”。这里可以看到“组织步骤”的不同,最后表现的效果也不同。我们把事情的拆分、组织、执行、最后到管理都搬到计算机上面来,就可以叫做工作流。
工作流管理系统
这个就是我们平时开发的系统,这些系统大多是以流程为中心,用图形化的方式来表示工作流。既然是图形化,那就需要画图,现在大多数系统都把画图搬到网页上来,所以才需要我们前端参与进来。
工作流引擎
BPMN规范
bpmn-js
这个很好理解,就是一个开源的实现BPMN2.0规范的web建模器。需要注意一下,bpmn-js虽然名字中有bpmn,但是并不是提出BPMN规范的官方组织开发的。所以不代表工作流引擎支持BPMN规范,前端就一定要用bpmn-js。我们也可以基于BPMN规范重新开发一套符合自己业务的流程设计器。
从上面的介绍中,我们可以发现bpmn-js天然满足BPMN规范,而绝大多数工作流引擎也是支持BPMN规范,我们前端只需要把bpmn-js引入到项目中来,直接开箱即用。为什么还要想换掉bpmn-js呢?主要是因为bpmn-js给我们带来了下面几个痛点。
假设后端需要在某个节点产生的数据上加一个字段:有的人选择在数据导出的时候直接正则暴力替换,有的人选择到bpmn-js源码中去写死,有的人强制获取所有的节点,然后给对应的节点updateProperties。随着这样的随意写代码的地方越来越多,项目的前端开发人员的更换,最终会让项目难以维护。
目前网上有各种各样对bpmn-js进行自定义的教程,也有着各种”黑科技“。归根结底是因为bpmn-js对自定义并不是完全开放,虽然有很多预设场景的示例,但是并不能满足实际项目中所有的场景。
对前端来说,花费大量的精力去学习bpmn-js源码意义不大,我们作为一个铁打的bpmn-js前端对自己的择业和成长不利。单就bpmn-js来说,它的形态、规范都是固定的,并不能发挥我们前端在UI层面的专业性。如果任何需求,产品问能不能实现,我们都说”不能,bpmn-js没这个功能”,会让我们自身的专业性被挑战,不利于我们在这个领域深入发展。
虽然在BPMN2.0中,将流程执行语义分为Events(事件)、Gateways(网关)、Activities(活动)这三类要素,但是在实际项目中,我们并不需要按照BPMN规范来定义流程图的中内容,而是需要按照我们项目业务来定义流程图的内容。这里还是以我之前做的教育类审批为例:在流程图上有一个节点是审批,在bpmn规范中它实际是一个属于UserTask的Activities。我们之间定义一个ApprovalNode节点就行,然后再导出数据的时候,把数据转换为后端引擎需要的UserTask即可。这样的好处是不论产品说的“审批”还是后端说的“UserTask”在我们的代码中都有体现,我们将不再游离在业务之外了。
LogicFlow完全自定义节点主要代码如下:
import{h,RectNode,RectNodeModel}from'@logicflow/core';classApprovalModelextendsRectNodeModel{initNodeData(data){super.initNodeData(data);this.text={value:data.text||"",x:data.x,y:data.y+40,};this.width=100;this.height=80;}}classApprovalViewextendsRectNode{/***完全自定义节点外观方法*/getShape(){const{model,graphModel}=this.props;const{x,y,width,height,radius}=model;conststyle=model.getNodeStyle();returnh("g",{},[h("rect",{...style,x:x-width/2,y:y-height/2,rx:radius,ry:radius,width,height})]);}}exportdefault{type:'approval',view:ApprovalView,model:ApprovalModel,LogicFlow通过定义节点的model和view来实现节点的自定义机制。我们可以继承内置的或者其它已经定义好的节点,在它们的基础上进行扩展。这里不对LogicFlow的用法做过多的介绍,LogicFlow支持各种节点、连线、插件的自定义,基本上完全能满足我们产品的各种奇葩需求。
对于LogicFlow的自定义机制大家可以看这些资料:
bpmn-js完全自定义节点主要代码如下:
对于bpmn-js元素的定义,大家可以看这些资料:
在前面我们提到过,如果后续需要我们给某类节点加个属性,我们来看看LogicFlow是怎么做的。首先是LogicFlow在数据上预留了一个properties属性,支持我们在里面设置任何自定义属性。LogicFlow在数据层面将业务数据和图数据是分开的,这比较直观。
这里假设我们有一个属性叫做rollback,用来标识节点是否支持回滚。支持回滚节点颜色就是绿色,不支持就是蓝色。对于后端服务来说,他不需要关系颜色,只需要给他的节点信息中有rollback字段即可。
{id:"userTask",type:"rect",x:100,y:100,text:{x:100,y:100,value:'节点文本'},properties:{//这里可以放任何业务属性rollback:true}}在自定义节点的时候可以这样写:
classApprovalModelextendsRectNodeModel{getNodeStyle(){conststyle=super.getNodeStyle()const{properties}=thisif(properties.rollback){style.fill='green'}else{style.fill='blue'}returnstyle}}转换为流程引擎可识别的xml从上面的代码可以看到,我们代码中更多的是对业务和UI的定义,并没有过多的去涉及BPMN规范。那么我们如何把这类业务的代码产生的数据转换为流程引擎可识别的内容呢?
最方便的方法当前是让后端自己去转换,比如后端用Java就让他写一个filter。当然,很多时候后端不愿意做,我们也可以前端来转。写转换的过程也是前端对BPMN规范了解的过程。
在工作流项目中,一开始为了快速出成果,直接用bpmn-js也没有问题。而且很多项目可能只是给研发使用B端管理类项目,对UI也没有啥要求,甚至出现后端同学自己一套搞完,都不需要前端介入。
但是随着项目的发展,特别是项目中开始出现产品角色,打算拿出去做商业化应用的时候。这个时候如果还是保留bpmn-js作为流程设计器,会给前端研发带来巨大的压力。在必要的时候,建议前端同学激进一点,选择更易维护的流程设计器来开发。
LogicFlow确实是一个比较好的选择,文档全、源码也易理解,使用后会将你从bpmn-js的痛苦中解脱出来。当你的代码变得更易维护,也更贴近业务后,也方便其他同学快速接手项目,避免陷入谁做谁跑路的窘境。