工作流引擎设计与实现·流程的简单执行
前言
上文中已经对模型进行了高度的抽象,本文将对模型行为进行进一步说明。这里以提问的方式开场:
如何让流程从开始节点按箭头指向走到结束节点?
StartModel(start)->TaskModel(apply)->TaskModel(deptApprove)->EndModel(end)
执行过程分析
对象图:
时序图:
执行过程说明:
- 开始节点调用输出边t1的执行方法
- t1输出边调用请假申请节点的执行方法
- 请假节点调用输出边t2的执行方法
- t2输出边调用部门领导审批的执行方法
- 部门领导审批调用输出边t3的执行方法
- t3调用结束节点的执行方法
代码实现步骤
节点模型的execute方法增加打印当前对象的编码和名称。
public abstract class NodeModel extends BaseModel implements Action {
private String layout;// 布局属性(x,y,w,h)
// 输入边集合
private List<TransitionModel> inputs = new ArrayList<TransitionModel>();
// 输出边集合
private List<TransitionModel> outputs = new ArrayList<TransitionModel>();
private String preInterceptors; // 节点前置拦截器
private String postInterceptors; // 节点后置拦截器
/**
* 由子类自定义执行方法
* @param execution
*/
abstract void exec(Execution execution);
@Override
public void execute(Execution execution) {
// 1. 调用前置拦截器
// 2. 调用子类的exec方法
// 3. 调用后置拦截器
System.out.println(StrUtil.format("model:{},name:{},displayName:{}", this.getClass().getSimpleName(), getName(),getDisplayName()));
exec(execution);
}
}
流程模型增加获取开始节点方法
public class ProcessModel extends BaseModel {
private String type; // 流程定义分类
private String instanceUrl; // 启动实例要填写的表单key
private String expireTime; // 期待完成时间变量key
private String instanceNoClass; // 实例编号生成器实现类
// 流程定义的所有节点
private List<NodeModel> nodes = new ArrayList<NodeModel>();
// 流程定义的所有任务节点
private List<TaskModel> tasks = new ArrayList<TaskModel>();
/**
* 获取开始节点
* @return
*/
public StartModel getStart() {
StartModel startModel = null;
for (int i = 0; i < nodes.size(); i++) {
NodeModel nodeModel = nodes.get(i);
if(nodeModel instanceof StartModel) {
startModel = (StartModel) nodeModel;
break;
}
}
return startModel;
}
}
流程模型拿到开始节点对象并调用执行方法
processModel.getStart().execute(new Execution());
此时只打印开始节点信息
model:StartModel,name:start,displayName:开始
节点模型对象的execute方法增加遍历调用下一个节点执行方法的
public abstract class NodeModel extends BaseModel implements Action {
private String layout;// 布局属性(x,y,w,h)
// 输入边集合
private List<TransitionModel> inputs = new ArrayList<TransitionModel>();
// 输出边集合
private List<TransitionModel> outputs = new ArrayList<TransitionModel>();
private String preInterceptors; // 节点前置拦截器
private String postInterceptors; // 节点后置拦截器
/**
* 由子类自定义执行方法
* @param execution
*/
abstract void exec(Execution execution);
@Override
public void execute(Execution execution) {
// 1. 调用前置拦截器
// 2. 调用子类的exec方法
// 3. 调用后置拦截器
System.out.println(StrUtil.format("model:{},name:{},displayName:{}", this.getClass().getSimpleName(), getName(),getDisplayName()));
outputs.forEach(tr->{
tr.getTarget().execute(execution);
});
exec(execution);
}
}
结果:
model:StartModel,name:start,displayName:开始
model:TaskModel,name:apply,displayName:请假申请
model:TaskModel,name:deptApprove,displayName:部门领导审批
model:EndModel,name:end,displayName:结束
为了突显边的作用,我们可以实现边的执行方法:
public class TransitionModel extends BaseModel implements Action {
private NodeModel source; // 边源节点引用
private NodeModel target; // 边目标节点引用
private String to; // 目标节点名称
private String expr; // 边表达式
private String g; // 边点坐标集合(x1,y1;x2,y2,x3,y3……)开始、拐角、结束
private boolean enabled; // 是否可执行
@Override
public void execute(Execution execution) {
if(!enabled) return;
target.execute(execution);
}
}
然后改造节点模型增加runOutTransition方法
public abstract class NodeModel extends BaseModel implements Action {
private String layout;// 布局属性(x,y,w,h)
// 输入边集合
private List<TransitionModel> inputs = new ArrayList<TransitionModel>();
// 输出边集合
private List<TransitionModel> outputs = new ArrayList<TransitionModel>();
private String preInterceptors; // 节点前置拦截器
private String postInterceptors; // 节点后置拦截器
/**
* 由子类自定义执行方法
* @param execution
*/
abstract void exec(Execution execution);
@Override
public void execute(Execution execution) {
// 1. 调用前置拦截器
// 2. 调用子类的exec方法
// 3. 调用后置拦截器
System.out.println(StrUtil.format("model:{},name:{},displayName:{}", this.getClass().getSimpleName(), getName(),getDisplayName()));
// 执行输出边
runOutTransition(execution);
exec(execution);
}
/**
* 执行输出边
*/
protected void runOutTransition(Execution execution) {
outputs.forEach(tr->{
tr.setEnabled(true);
tr.execute(execution);
});
}
}
最终效果为:
model:StartModel,name:start,displayName:开始
model:TaskModel,name:apply,displayName:请假申请
model:TaskModel,name:deptApprove,displayName:部门领导审批
model:EndModel,name:end,displayName:结束
如何让流程产生阻塞?
上面的例子执行过程太顺利了,真实的工作流场景会存在一些阻塞任务,产生阻塞的意思是,即调用节点执行方法,如果条件不满足,依然不能驱动流程往下一个节点进行。那我们如何使用程序去模拟这一过程呢?
首先改造节点模型
并不是每个节点的执行方式都一样,我们需要对不同节点进行不同的输出处理,所以这里
- 暂时去掉原来节点模型的打印语句和调用执行边的方法
- 重写toString()方法
public abstract class NodeModel extends BaseModel implements Action {
private String layout;// 布局属性(x,y,w,h)
// 输入边集合
private List<TransitionModel> inputs = new ArrayList<TransitionModel>();
// 输出边集合
private List<TransitionModel> outputs = new ArrayList<TransitionModel>();
private String preInterceptors; // 节点前置拦截器
private String postInterceptors; // 节点后置拦截器
/**
* 由子类自定义执行方法
* @param execution
*/
abstract void exec(Execution execution);
@Override
public void execute(Execution execution) {
// 1. 调用前置拦截器
// 2. 调用子类的exec方法
// 3. 调用后置拦截器
exec(execution);
}
/**
* 执行输出边
*/
protected void runOutTransition(Execution execution) {
outputs.forEach(tr->{
tr.setEnabled(true);
tr.execute(execution);
});
}
@Override
public String toString() {
return StrUtil.format("model:{},name:{},displayName:{},time:{}", this.getClass().getSimpleName(), getName(),getDisplayName(), DateUtil.dateSecond());
}
}
实现开始节点的exec方法
开始节点的exec主要执行如下逻辑:
- 输出super.toString()
- 调用runOutTransition
public class StartModel extends NodeModel {
@Override
void exec(Execution execution) {
System.out.println(super.toString());
runOutTransition(execution);
}
}
实现结束节点的exec方法
结束节点是没有输出边的,所以只输出super.toString()
public class EndModel extends NodeModel {
@Override
public void exec(Execution execution) {
System.out.println(super.toString());
}
}
实现任务节点的exec方法
任务节点的比较特殊,我们可以做如下处理让其产生临时的阻塞:
public class TaskModel extends NodeModel {
private String form; // 表单标识
private String assignee; // 参与人
private String assignmentHandler; // 参与人处理类
private TaskTypeEnum taskType; // 任务类型(主办/协办)
private TaskPerformTypeEnum performType; // 参与类型(普通参与/会签参与)
private String reminderTime; // 提醒时间
private String reminderRepeat; // 重复提醒间隔
private String expireTime; // 期待任务完成时间变量key
private String autoExecute; // 到期是否自动执行Y/N
private String callback; // 自动执行回调类
private Dict ext = Dict.create(); // 自定义扩展属性
@Override
public void exec(Execution execution) {
// 执行任务节点自定义执行逻辑
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(super.toString());
runOutTransition(execution);
}
}
此时打印结果如下:
model:StartModel,name:start,displayName:开始,time:2023-04-25 22:27:45
model:TaskModel,name:apply,displayName:请假申请,time:2023-04-25 22:27:48
model:TaskModel,name:deptApprove,displayName:部门领导审批,time:2023-04-25 22:27:51
model:EndModel,name:end,displayName:结束,time:2023-04-25 22:27:51
加入组织
相关源码
流程设计器
转载自:https://juejin.cn/post/7228831835756724282