写在最前
之前三篇文章已经基本上将工作流引擎的原理阐述清楚了,接下来我们深入实现细节,来看一下各个节点如何实现。
节点总览
以节点类型来分,节点可分为开始节点、结束节点、任务节点、Decision节点、Fork节点、Join节点。
-
开始节点和结束节点作为流程的开始和结束关卡出现,主要是控制流程的开始和结束
-
任务节点可理解为需要人为介入的节点,通过用户的审批意见进行下一节点的选择。
-
Decision节点可理解为决策节点,目标出线可以有多个,只有一条符合要求的连线在节点决策时被选择。
-
Fork和join节点通常一起使用,fork节点分出可同时进行的流程,在fork节点中汇聚。
Start、End节点实现
作为流程的开始和结束,这两个节点的职责相对简单。Start节点只需要开始流程并且执行连线节点的动作即可,而End节点只需要结束整个流程即可。
开始节点
@Override
protected void exec(Execution execution) {
// 执行连线动作
runOutTransition(execution);
}
runOutTransition实现可以看上一篇文章的介绍,在此不再赘述。runOutTransition是节点如何到连线节点的过程,需要理解这一过程才能更好设计节点流转。 流程引擎介绍runOutTransition
结束节点
@Override
protected void exec(Execution execution) {
doExecution(new EndProcessHandler(), execution);
}
handler中做的动作就是将流程的状态改为结束状态,可根据业务需要进行其他结束操作的动作。
Task节点
任务节点,即需要人工介入的节点,需要指定人进行审批通过或驳回的决议来决定节点流转,所以会有停留和完成状态。创建任务等待审核的过程其实就可以理解为流程因任务挂起了,点击审核通过或者驳回就是触发任务节点。所以从代码的实现上,可根据是否有taskId来判断是任务创建还是完成任务,进而进行对应的动作。
@Override
protected void exec(Execution execution) {
Task task = execution.getTask();
if (canCreateNewTask(task)) {
doExecution(new TaskCreateHandler(), execution);
}
// 检测任务是否完成
doExecution(new TaskCheckCompleteHandler(), execution);
if (execution.getTask().isComplete()) {
// 后继执行下一个节点
runOutTransition(execution);
}
}
TaskCreateHandler就是在数据库中创建任务
TaskCheckCompleteHandler 该Handler就是检测任务是否完成(或签、会签完成的不同逻辑),如果完成进行连线节点动作runOutTransition
Decision节点
决策节点,只需要定义好决策策略,根据运行参数和表达式来判断进行哪一个连线的动作。如图,decision的语义就是要么Task1能被执行,要么Task2能被执行,只有一条连线能被执行,节点执行取决于Decision节点的表达式和运行参数共同作用的结果。
代码实现
@Override
protected void exec(Execution execution) {
if (ObjectUtil.isEmpty(expr)) {
throw new SmartFlowException("表达式解析器为空,请检查配置.");
}
boolean isFound = false;
String next = decisionHandler.decide(expr, execution);
if (ObjectUtil.isNotEmpty(next)) {
for (LineModel line : getOutputs()) {
// 找到后继节点,并继续往后执行
if (line.getId().equalsIgnoreCase(next)) {
line.setEnabled(true);
line.execute(execution);
isFound = true;
}
}
}
}
expr为定义的表达式,如
#day>2?'line3':'line4'。这里的表达式可自行选择,决定于你的decisionHandler用的是什么判断策略,此处使用的是EL表达式。
Fork、Join节点
- Fork节点逻辑相对简单,直接按顺序执行线节点逻辑即可。
- Join节点动作原理上就是 记录触发节点的次数与来源连线的size做equal判断,来判断join节点是否可以进入下一个节点。
如图,fork-join的语义就是Task1和Task2都会被执行,在join节点只有两个任务都完成了汇聚进join节点才能进入下一个节点。
代码实现
@Override
protected void exec(Execution execution) {
// 分流节点,直接找到连线,继续往下执行
runOutTransition(execution);
}
fork节点就是执行runOutTransition
join节点就是完成节点动作(创建一条记录),然后判断数据库中count数和入线的size是否相等,相等就说明join完成进入下一节点,否则结束等待下一次被触发。
写在最后
至此所有节点实现都已经完成,接下来需要做的就是一些扩展点的实现。在具体实现之前感觉工作流引擎十分复杂,再具体实际开发后,通过整体设计将复杂的流程分解为各个简单小步骤,最终化繁为简,发现原来每个节点的实现也并没有这么困难。