前言

上文中已经对模型进行了高度的笼统,本文将对模型行为进行进一步阐明。这里以发问的方式开场:

怎么让流程从开端节点按箭头指向走到完毕节点?

StartModel(start)->TaskModel(apply)->TaskModel(deptApprove)->EndModel(end)

工作流引擎设计与实现流程的简单执行

履行进程分析

目标图:

工作流引擎设计与实现流程的简单执行

时序图:

工作流引擎设计与实现流程的简单执行

履行进程阐明:

  1. 开端节点调用输出边t1的履行办法
  2. t1输出边调用请假请求节点的履行办法
  3. 请假节点调用输出边t2的履行办法
  4. t2输出边调用部分领导批阅的履行办法
  5. 部分领导批阅调用输出边t3的履行办法
  6. 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

参加安排

请在微信中打开: 《立东和他的朋友们》

工作流引擎设计与实现流程的简单执行

相关源码

mldong-flow-demo-02

流程设计器

在线体验