在流程的简略履行章节中,咱们让一条普通的顺序流程从开端节点走向完毕节点。那假如是条件流程呢?咱们又应该怎么处理呢?
流程界说
如上图烘托的流程图,可由以下两种流程界说文件生成。
src/test/resources/leave_02.json
由决议计划节点的输出边特点来界说表达式,该表达式返回值为true/false
注:以下json并非悉数,缺少方位信息。
{
"name": "leave",
"displayName": "请假",
"instanceUrl": "leaveForm",
"nodes": [
{
"id": "start",
"type": "snaker:start",
"properties": {},
"text": {
"value": "开端"
}
},
{
"id": "apply",
"type": "snaker:task",
"properties": {},
"text": {
"value": "请假请求"
}
},
{
"id": "approveDept",
"type": "snaker:task",
"x": 740,
"y": 160,
"properties": {},
"text": {
"value": "部分领导批阅"
}
},
{
"id": "approveBoss",
"type": "snaker:task",
"properties": {},
"text": {
"value": "公司领导批阅"
}
},
{
"id": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"type": "snaker:decision",
"properties": {}
},
{
"id": "end",
"type": "snaker:end",
"properties": {},
"text": {
"value": "完毕"
}
}
],
"edges": [
{
"id": "3037be41-5682-4344-b94a-9faf5c3e62ba",
"type": "snaker:transition",
"sourceNodeId": "start",
"targetNodeId": "apply",
"properties": {}
},
{
"id": "c79642ae-9f28-4213-8cdf-0e0d6467b1b9",
"type": "snaker:transition",
"sourceNodeId": "apply",
"targetNodeId": "approveDept",
"properties": {}
},
{
"id": "09d9b143-9473-4a0f-8287-9abf6f65baf5",
"type": "snaker:transition",
"sourceNodeId": "approveDept",
"targetNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"properties": {}
},
{
"id": "a64348ec-4168-4f36-8a61-15cf12c710b9",
"type": "snaker:transition",
"sourceNodeId": "approveBoss",
"targetNodeId": "end"
"properties": {}
},
{
"id": "517ef2c7-3486-4992-b554-0f538ab91751",
"type": "snaker:transition",
"sourceNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"targetNodeId": "end",
"properties": {
"expr": "#f_day<3"
},
"text": {
"value": "请假天数小于3"
}
},
{
"id": "d7ec4166-f3fc-4fd6-a2ac-a6c4d509c4dd",
"type": "snaker:transition",
"sourceNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"targetNodeId": "approveBoss",
"properties": {
"expr": "#f_day>=3"
},
"text": {
"value": "请假天数大于等于3"
}
}
]
}
src/test/resources/leave_03.json
由决议计划节点的expr特点来界说表达式,该表达式返回值为方针节点称号。
注:以下json并非悉数,缺少方位信息。
{
"name": "leave",
"displayName": "请假",
"instanceUrl": "leaveForm",
"nodes": [
{
"id": "start",
"type": "snaker:start",
"properties": {},
"text": {
"value": "开端"
}
},
{
"id": "apply",
"type": "snaker:task",
"properties": {},
"text": {
"value": "请假请求"
}
},
{
"id": "approveDept",
"type": "snaker:task",
"properties": {},
"text": {
"value": "部分领导批阅"
}
},
{
"id": "approveBoss",
"type": "snaker:task",
"properties": {},
"text": {
"value": "公司领导批阅"
}
},
{
"id": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"type": "snaker:decision",
"properties": {
"expr": "#f_day>=3?'approveBoss':'end'"
}
},
{
"id": "end",
"type": "snaker:end",
"properties": {},
"text": {
"value": "完毕"
}
}
],
"edges": [
{
"id": "3037be41-5682-4344-b94a-9faf5c3e62ba",
"type": "snaker:transition",
"sourceNodeId": "start",
"targetNodeId": "apply",
"properties": {}
},
{
"id": "c79642ae-9f28-4213-8cdf-0e0d6467b1b9",
"type": "snaker:transition",
"sourceNodeId": "apply",
"targetNodeId": "approveDept",
"properties": {},
},
{
"id": "09d9b143-9473-4a0f-8287-9abf6f65baf5",
"type": "snaker:transition",
"sourceNodeId": "approveDept",
"targetNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"properties": {}
},
{
"id": "a64348ec-4168-4f36-8a61-15cf12c710b9",
"type": "snaker:transition",
"sourceNodeId": "approveBoss",
"targetNodeId": "end",
"properties": {}
},
{
"id": "517ef2c7-3486-4992-b554-0f538ab91751",
"type": "snaker:transition",
"sourceNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"targetNodeId": "end",
"properties": {},
"text": {
"value": "请假天数小于3"
}
},
{
"id": "d7ec4166-f3fc-4fd6-a2ac-a6c4d509c4dd",
"type": "snaker:transition",
"sourceNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"targetNodeId": "approveBoss",
"text": {
"value": "请假天数大于等于3"
}
}
]
}
src/test/resources/leave_04.json
由决议计划节点界说的handleClasss特点,实例化决议计划类,决议下一个节点称号。
注:以下json并非悉数,缺少方位信息。
{
"name": "leave",
"displayName": "请假",
"instanceUrl": "leaveForm",
"nodes": [
{
"id": "start",
"type": "snaker:start",
"text": {
"value": "开端"
}
},
{
"id": "apply",
"type": "snaker:task",
"properties": {},
"text": {
"value": "请假请求"
}
},
{
"id": "approveDept",
"type": "snaker:task",
"x": 740,
"y": 160,
"properties": {},
"text": {
"value": "部分领导批阅"
}
},
{
"id": "approveBoss",
"type": "snaker:task",
"properties": {},
"text": {
"value": "公司领导批阅"
}
},
{
"id": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"type": "snaker:decision",
"properties": {
"handleClass": "com.mldong.flow.LeaveDecisionHandler"
}
},
{
"id": "end",
"type": "snaker:end",
"text": {
"value": "完毕"
}
}
],
"edges": [
{
"id": "3037be41-5682-4344-b94a-9faf5c3e62ba",
"type": "snaker:transition",
"sourceNodeId": "start",
"targetNodeId": "apply"
},
{
"id": "c79642ae-9f28-4213-8cdf-0e0d6467b1b9",
"type": "snaker:transition",
"sourceNodeId": "apply",
"targetNodeId": "approveDept"
},
{
"id": "09d9b143-9473-4a0f-8287-9abf6f65baf5",
"type": "snaker:transition",
"sourceNodeId": "approveDept",
"targetNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634"
},
{
"id": "a64348ec-4168-4f36-8a61-15cf12c710b9",
"type": "snaker:transition",
"sourceNodeId": "approveBoss",
"targetNodeId": "end"
},
{
"id": "517ef2c7-3486-4992-b554-0f538ab91751",
"type": "snaker:transition",
"sourceNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"targetNodeId": "end",
"text": {
"value": "请假天数小于3"
},
},
{
"id": "d7ec4166-f3fc-4fd6-a2ac-a6c4d509c4dd",
"type": "snaker:transition",
"sourceNodeId": "2c75eebf-5baf-4cd0-a7b3-05466be13634",
"targetNodeId": "approveBoss",
"text": {
"value": "请假天数大于等于3"
}
}
]
}
旧的代码逻辑
新增src/test/java/com/mldong/flow/ExecuteTest.java
共两个办法executeLeave_01和executeLeave_02,两者的履行逻辑都一样,便是解析的流程界说文件不一样。
- 加载配置
- 解析流程界说文件
- 履行流程
package com.mldong.flow;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import com.mldong.flow.engine.cfg.Configuration;
import com.mldong.flow.engine.core.Execution;
import com.mldong.flow.engine.model.ProcessModel;
import com.mldong.flow.engine.parser.ModelParser;
import org.junit.Test;
/**
*
* 履行测验
* @author mldong
* @date 2023/5/1
*/
public class ExecuteTest {
@Test
public void executeLeave_01() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
processModel.getStart().execute(execution);
}
@Test
public void executeLeave_02() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_02.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
processModel.getStart().execute(execution);
}
}
当履行executeLeave_01办法时,成果如下:
model:StartModel,name:start,displayName:开端
model:TaskModel,name:apply,displayName:请假请求
model:TaskModel,name:approveDept,displayName:部分领导批阅
model:EndModel,name:end,displayName:完毕
当履行executeLeave_02办法时,成果如下:
model:StartModel,name:start,displayName:开端
model:TaskModel,name:apply,displayName:请假请求
model:TaskModel,name:approveDept,displayName:部分领导批阅
咱们会看到,executeLeave_02的履行是不完好的,由于咱们并没有对决议计划节点进行处理。下面咱们要对决议计划节点进行处理,使其完好的从开端节点走向完毕节点。
决议计划节点剖析
从图中看,咱们可以得到如下两条途径:
- 开端->请假请求->部分领导批阅->完毕
- 开端->请假请求->部分领导批阅->公司领导批阅->完毕
检查流程界说文件leave_02.json
,在节点输出边中,咱们会看到如下特点:
{
"expr": "#f_day<3"
}
{
"expr": "#f_day>=3"
}
检查流程界说文件leave_03.json
,在节点特点中,咱们会看到如下特点:
{
"expr": "#f_day>=3?'approveBoss':'end'"
}
检查流程界说文件leave_04.json
,在节点特点中,咱们会看到如下特点:
{
"handleClass": "com.mldong.flow.LeaveDecisionHandler"
}
那咱们在代码上应该怎么实现呢?其实思路很简略,分三种状况判断:
假如决议计划节点界说有表达式特点:
- 从节点特点中获取表达式
- 调用表达式引擎,得到下一个节点的节点称号
- 遍历一切输出边,假如输出边方针节点称号和上面找到的下一个节点称号共同,则设置enabled=true
- 调用输出边的execute办法
假如决议计划节点界说有决议计划类字段串特点:
- 从节点特点中获取决议计划类
- 实例类决议计划类
- 调用决议计划类办法,得到下一个节点的节点称号
- 遍历一切输出边,假如输出边方针节点称号和上面找到的下一个节点称号共同,则设置enabled=true
- 调用输出边的execute办法
假如决议计划节点未界说有表达式特点:
- 从节点的输出边中获取表达式
- 调用表达式引擎,设置输出边的enabled特点
- 调用输出边的execute办法
代码实现
model/DecisionModel.java
package com.mldong.flow.engine.model;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.expression.ExpressionUtil;
import com.mldong.flow.engine.core.Execution;
import com.mldong.flow.engine.enums.ErrEnum;
import com.mldong.flow.engine.ex.JeeFlowException;
import engine.DecisionHandler;
import lombok.Data;
/**
*
* 决议计划模型
* @author mldong
* @date 2023/4/25
*/
@Data
public class DecisionModel extends NodeModel {
private String expr; // 决议计划表达式
private String handleClass; // 决议计划处理类
@Override
public void exec(Execution execution) {
// 履行决议计划节点自界说履行逻辑
boolean isFound = false;
String nextNodeName = null;
if(StrUtil.isNotEmpty(expr)) {
Object obj = ExpressionUtil.eval(expr, execution.getArgs());
nextNodeName = Convert.toStr(obj,"");
} else if(StrUtil.isNotEmpty(handleClass)) {
DecisionHandler decisionHandler = ReflectUtil.newInstance(handleClass);
nextNodeName = decisionHandler.decide(execution);
}
for(TransitionModel transitionModel: getOutputs()){
if (StrUtil.isNotEmpty(transitionModel.getExpr()) && Convert.toBool(ExpressionUtil.eval(transitionModel.getExpr(), execution.getArgs()), false)) {
// 决议计划节点输出边存在表达式,则运用输出边的表达式,true则履行
isFound = true;
transitionModel.setEnabled(true);
transitionModel.execute(execution);
} else if(transitionModel.getTo().equalsIgnoreCase(nextNodeName)) {
// 找到对应的下一个节点
isFound = true;
transitionModel.setEnabled(true);
transitionModel.execute(execution);
}
}
if(!isFound) {
// 找不到下一个可履行路线
throw new JeeFlowException(ErrEnum.NOT_FOUND_NEXT_NODE);
}
}
}
单元测验类改造
src/test/java/com/mldong/flow/ExecuteTest.java
package com.mldong.flow;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import com.mldong.flow.engine.cfg.Configuration;
import com.mldong.flow.engine.core.Execution;
import com.mldong.flow.engine.model.ProcessModel;
import com.mldong.flow.engine.parser.ModelParser;
import org.junit.Test;
/**
*
* 履行测验
* @author mldong
* @date 2023/5/1
*/
public class ExecuteTest {
@Test
public void executeLeave_01() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
processModel.getStart().execute(execution);
}
@Test
public void executeLeave_02() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_02.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
processModel.getStart().execute(execution);
}
@Test
public void executeLeave_02_1() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_02.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
execution.getArgs().put("f_day",1);
processModel.getStart().execute(execution);
}
@Test
public void executeLeave_02_2() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_02.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
execution.getArgs().put("f_day",3);
processModel.getStart().execute(execution);
}
@Test
public void executeLeave_03_1() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_03.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
execution.getArgs().put("f_day",1);
processModel.getStart().execute(execution);
}
@Test
public void executeLeave_03_2() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_03.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
execution.getArgs().put("f_day",3);
processModel.getStart().execute(execution);
}
@Test
public void executeLeave_04() {
new Configuration();
ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave_04.json")));
Execution execution = new Execution();
execution.setArgs(Dict.create());
processModel.getStart().execute(execution);
}
}
测验验证
当履行executeLeave_02_1办法时,成果如下:
- 流程界说文件:leave_02.json
- f_day=1
model:StartModel,name:start,displayName:开端
model:TaskModel,name:apply,displayName:请假请求
model:TaskModel,name:approveDept,displayName:部分领导批阅
model:EndModel,name:end,displayName:完毕
当履行executeLeave_02_2办法时,成果如下:
- 流程界说文件:leave_02.json
- f_day=3
model:StartModel,name:start,displayName:开端
model:TaskModel,name:apply,displayName:请假请求
model:TaskModel,name:approveDept,displayName:部分领导批阅
model:TaskModel,name:approveBoss,displayName:公司领导批阅
model:EndModel,name:end,displayName:完毕
当履行executeLeave_03_1办法时,成果如下:
- 流程界说文件:leave_03.json
- f_day=1
model:StartModel,name:start,displayName:开端
model:TaskModel,name:apply,displayName:请假请求
model:TaskModel,name:approveDept,displayName:部分领导批阅
model:EndModel,name:end,displayName:完毕
当履行executeLeave_03_2办法时,成果如下:
- 流程界说文件:leave_03.json
- f_day=3
model:StartModel,name:start,displayName:开端
model:TaskModel,name:apply,displayName:请假请求
model:TaskModel,name:approveDept,displayName:部分领导批阅
model:TaskModel,name:approveBoss,displayName:公司领导批阅
model:EndModel,name:end,displayName:完毕
参加组织
请在微信中打开: 《立东和他的朋友们》
相关源码
mldong-flow-demo-04
流程设计器
在线体验