工作流只要是涉及到组织审批和审核的情况下基本都需要,Flowable作为一个工作流现在比较流行的框架,算是activiti框架的修正版,国外这种情况还蛮多的就是,就是原班人马走了然后另起一家,据说硅谷没有竞业协议的说法,也不知道这个是不是真的。
前面的知识我也是摸着别人的入坑的
这里有两个简单的关于flowable的简单例子,一个是简单的整合的flowable的maven,还有一个是简单的springboot整合的项目,当然实际例子肯定不会那么简单,我看到的一些BPMN文件复杂的不像话,各种条件。
一 前置知识
如果你有使用过flow able的经历或者启动过我上面的demo,会有一个疑问就是我的工作流要怎么设计的,程序如何获取这些信息处理。
这涉及到一个BPMN,也就是业务流模型和标记标准,根据这个标准去定义工作流模型,工程中是我们的一个resorce/process文件夹下面的一个xml文件,当然这个文件看起来不直观易懂,尤其是添加复杂的情况下,实际上还有很多可视化工具可以将其转化图的形式,比如我IDE用的是Flowable BPMN visualizer。同样前端也有很多展示的JS插件工具。
这个文件就是流程定义文件,他和flowable数据库的基础表(里面的基础表是第一次启动连接数据自动生成的,只有你自定义的表要自己去建。)
以下是我demo的一个流程定义文件:
二 启动事件
现在通用的标准是BPMN2.0大家都用这一个标准的产品,我换别人家的产品时,就很平滑不至于说,我只能用这一家,用别人的要重新开始。
启动事件
这里介绍的启动事件和结束事件是流程的一个基础部分,启动事件指的是start event,流程的起点.
<startEvent id="theStart" />
启动事件在XML中,类型由子元素声明来定义。
启动事件保持等候状态,直到特定的触发器被触发。
启动事件是细线图形。
1 空启动事件
none Start Event 未指定触发器,就是由用户来触发启动的事件。
空心圆来代表空启动事件(中间没有图标就是没有触发器的意思)
<startEvent id="start" name="my start event" />
使用方法
ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX();
扩展
<startEvent id="request" flowable:formKey="simpleForm" />
2 定时器启动事件
定时器启动,timer start event,在指定时间内创建一次或多次流程实例。
定时器启动事件,在流程部署的同时就开始计时。不需要调用startProcessInstanceByXXX就会在时间启动。调用startProcessInstanceByXXX时会在定时启动之外额外启动一个流程。
当部署带有定时器启动事件的流程的更新版本时,上一版本的定时器作业会被移除。这是因为通常并不希望旧版本的流程仍然自动启动新的流程实例。
这个定时器只能包含以下一种元素
(实际上就是普通启动事件+定时器子元素,这也就是说为什么启动类型由子元素确定的意思)
timeDate 到时间就会触发
<timerEventDefinition>
<timeDate>2022-01-11T12:13:14</timeDate>
</timerEventDefinition>
timeDuration 等待多长时间就会触发,P是period P10D D是Day天的意思,P10D就是每10天,同样的还有T 就是时间的意思,这些都遵循着ISO_8601标准。
duration ISO_8601格式
- P is the duration designator (for period) placed at the start of the duration representation.
Y is the year designator that follows the value for the number of calendar years.
M is the month designator that follows the value for the number of calendar months.
W is the week designator that follows the value for the number of weeks.
D is the day designator that follows the value for the number of calendar days.
T is the time designator that precedes the time components of the representation.
H is the hour designator that follows the value for the number of hours.
M is the minute designator that follows the value for the number of minutes.
S is the second designator that follows the value for the number of seconds.
For example, “P3Y6M4DT12H30M5S” represents a duration of “three years, six months, four days, twelve hours, thirty minutes, and five seconds”.
<timerEventDefinition>
<timeDuration>P10D</timeDuration>
</timerEventDefinition>
timeCycle 重复周期 例如:R3/PT10H/2022-01-12T23:59:59+00:00 重复三次启动,每次间隔为10小时,到指定时间时结束重复
<timerEventDefinition>
<timeCycle>R3/PT10H/2022-01-12T23:59:59+00:00</timeCycle>
</timerEventDefinition>
可以搭配变量比如结束时间${EndDate}
<timerEventDefinition>
<timeCycle>R3/PT10H/${EndDate}</timeCycle>
</timerEventDefinition>
3 消息启动事件
消息启动事件,message start event 使用具名消息启动流程实例
一个流程定义不得包含多个同名的消息启动事件。
在流程定义中,一个或多个消息启动事件引用了已经部署的另一流程定义中消息启动事件的消息名,则会抛出异常。
流程版本:在部署流程定义的新版本时,会取消上一版本的消息订阅,即使新版本中并没有这个消息事件。
普通事件+messageEventDefinition子元素
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples"
xmlns:tns="Examples">
<message id="newInvoice" name="newInvoiceMessage" />
<process id="invoiceProcess">
<!--- 这里启动事件 + messageEventDefinition元素作为消息启动事件 -->
<startEvent id="messageStart" >
<messageEventDefinition messageRef="tns:newInvoice" />
</startEvent>
...
</process>
</definitions>
相关代码
ProcessInstance startProcessInstanceByMessage(String messageName);
ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables);
ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey,
Map<String, Object< processVariables);
4信号启动事件
信号启动事件(signal start event),使用具名信号启动流程实例。
普通启动事件+signalEventDefinition子元素
这个信号可以由流程实例中的信号抛出中间事件(intermediary signal throw event),或者API(runtimeService.signalEventReceivedXXX方法)触发。
消息与信号的区别:消息是一对一的,一个消息只能启动一个流程事件。信号是全局的,广播式的,一个信号可以启动多个流程事件。
<signal id="theSignal" name="The Signal" />
<process id="processWithSignalStart1">
<startEvent id="theStart">
<signalEventDefinition id="theSignalEventDefinition" signalRef="theSignal" />
</startEvent>
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
<userTask id="theTask" name="Task in process A" />
<sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" />
<endEvent id="theEnd" />
</process>
5异常启动事件
error start event 异常启动事件
普通启动事件声明加上errorEventDefinition子元素
<startEvent id="messageStart" >
<errorEventDefinition errorRef="someError" />
</startEvent>
结束事件
结束事件(end event)标志着流程或子流程中一个分支的结束。结束事件总是抛出(型)事件。这意味着当流程执行到达结束事件时,会抛出一个结果。结果的类型由事件内部的黑色图标表示。在XML表示中,类型由子元素声明给出。
1 空结束事件
空结束事件(none end event),意味着当到达这个事件时,没有特别指定抛出的结果。因此,引擎除了结束当前执行分支之外,不会多做任何事情。
<endEvent id="end" name="my end event" />
2 异常结束事件
当流程执行到达异常结束事件(error end event)时,结束执行的当前分支,并抛出错误。
异常结束事件事件用内部有一个异常图标的标准结束事件(粗圆圈)表示。异常图标是全黑的,代表抛出的含义。
<endEvent id="myErrorEndEvent">
<errorEventDefinition errorRef="myError" />
</endEvent>
errorRef属性可以引用在流程外定义的error元素:相当于自定义错误码了
<error id="myError" errorCode="123" />
...
<process id="myProcess">
...
error的errorCode用于查找匹配的异常捕获边界事件。如果errorRef不匹配任何已定义的error,则该errorRef会用做errorCode的快捷方式。这个快捷方式是Flowable特有的。下面的代码片段在功能上是相同的。
<error id="myError" errorCode="error123" />
...
<process id="myProcess">
...
<endEvent id="myErrorEndEvent">
<errorEventDefinition errorRef="myError" />
</endEvent>
...
3 终止结束事件
当到达终止结束事件(terminate end event)时,当前的流程实例或子流程会被终止。也就是说,当执行到达终止结束事件时,会判断第一个范围 scope(流程或子流程)并终止它。请注意在BPMN 2.0中,子流程可以是嵌入式子流程,调用活动,事件子流程,或事务子流程。有一条通用规则:当存在多实例的调用过程或嵌入式子流程时,只会终止一个实例,其他的实例与流程实例不会受影响。
可以添加一个可选属性terminateAll。当其为true时,无论该终止结束事件在流程定义中的位置,也无论它是否在子流程(甚至是嵌套子流程)中,都会终止(根)流程实例。
普通结束事件加上terminateEventDefinition子元素。
<endEvent id="myEndEvent >
<terminateEventDefinition flowable:terminateAll="true"></terminateEventDefinition>
</endEvent>
4 取消结束事件
取消结束事件(cancel end event)只能与BPMN事务子流程(BPMN transaction subprocess)一起使用。当到达取消结束事件时,会抛出取消事件,且必须由取消边界事件(cancel boundary event)捕获。取消边界事件将取消事务,并触发补偿(compensation)。
取消结束事件用内部有一个取消图标的标准结束事件(粗圆圈)表示。取消图标是全黑的,代表抛出的含义。
<endEvent id="myCancelEndEvent">
<cancelEventDefinition />
</endEvent>