Flowable 中节点任意跳转

工作流开发中经常有一种回退的需求,审批回退、驳回、撤回等都需要。

在 Activiti 中流程底层没开放对应的接口,而在 Flowable 中已经提供了支持节点跳转的功能。同时我们也可以像在 Activiti 中通过实现自己的 Command 来实现任意节点跳转的需求。

下面我们先介绍一下 Flowable 提供的内置跳转功能,最后我们再手动实现任务跳转的 Command。

1. Flowable 内置功能

Flowable 的 RuntimeService 内置了一个强大的功能,可以实现节点到任意节点的跳转。

1
2
3
4
runtimeService.createChangeActivityStateBuilder()
.processInstanceId("id")
.moveActivityIdTo("node1", "node2")
.changeState();

上面的代码表示流程实例 ID 为 id 的流程,将 node1 节点的任务跳转到 node2 上。ChangeActivityStateBuilder 类还提供了其它接口可以实现主流程跳子流程,子流程到父流程,非常强大。

运行实例直接跳转到节点

这个接口比较常用,通过取当前运行的实例,我们直接跳转到目标节点,无需关心当前节点是什么。

1
ChangeActivityStateBuilder moveExecutionToActivityId(String executionId, String activityId);

跳转到子流程的节点

内嵌子流程直接使用上面的节点跳转方式既可,外部子流程需要启动新的流程,所以接口不太一样。需要提供外部子流程的 CallActivity Id 和在外部子流程中的节点 ID。

1
ChangeActivityStateBuilder moveActivityIdToSubProcessInstanceActivityId(String currentActivityId, String newActivityId, String callActivityId);

在跳转的时候还可以修改流程或任务本地变量。

1
ChangeActivityStateBuilder processVariable(String processVariableName, Object processVariableValue);

此处我们只说明了几个常用的接口,其它功能可以查看 ChangeActivityStateBuilder 类的源码,注释非常详细。

2. 自制 Command 实现跳转

Flowable 内部也是通过 ChangeActivityStateCmd 这个 Command 实现的,在 Flowable 中其实已经不建议自己实现 Command 来实现了,这里只是提供一种可能性给大家参考。

要自定义 Command 只需要实现 Command 接口就行,T 是这个 Command 的返回值类型,我们这里打算返回 processInstanceId 所以是 String 类型。

1
2
3
4
5
6
public class JumpCommand implements Command<String> {
@Override
public String execute(CommandContext commandContext) {

}
}

首先我们新建三个变量在存放操作的参数,分别是操作的任务 ID、目标节点的 ID 和注释。

1
2
3
protected String taskId;
protected String targetActivityId;
protected String comment;

通过任务 ID 取出 Task 内容

1
2
3
4
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
TaskEntityManager taskEntityManager = CommandContextUtil.getTaskServiceConfiguration().getTaskEntityManager();

TaskEntity task = taskEntityManager.findById(this.taskId);

再通过 Task 对象取出流程,再从流程定义中取出目标节点的流程定义元素

1
2
3
4
5
ExecutionEntity executionEntity = executionEntityManager.findById(task.getExecutionId());

Process process = ProcessDefinitionUtil.getProcess(executionEntity.getProcessDefinitionId());

FlowElement targetFlowElement = process.getFlowElement(targetActivityId);

然后将当前执行实例的节点修改为目标节点,通过 agenda 方式通知 Flowable 通过对象变化补偿完成流程变化

1
2
3
executionEntity.setCurrentFlowElement(targetFlowElement);

CommandContextUtil.getAgenda().planContinueProcessInCompensation(executionEntity);

注意,我们通过这种方式将 Task 结束并跳转是不会产生 TASK_COMPLETED 事件的,包括 Flowable 内置的接口,产生的不是 TASK_COMPLETED 而是 ACTIVITY_CANCELLED 事件。

但是这里我们业务预期来说,应该是正常结束任务,产生 TASK_COMPLETED 才对。为了实现这个目标,我们需要手动触发这个事件。

1
2
3
4
5
6
7
8
executionEntity.setCurrentFlowElement(targetFlowElement);

// 放 Agenda 后会导致 Create 事件比 Complete 事件早执行,所以这里先分发 TASK_COMPLETED 事件
commandContext.getCurrentEngineConfiguration()
.getEventDispatcher()
.dispatchEvent(FlowableEventBuilder.createEntityEvent(FlowableEngineEventType.TASK_COMPLETED, task));

CommandContextUtil.getAgenda().planContinueProcessInCompensation(executionEntity);

Flowable 中同样需要手动触发 TASK_COMPLETED 事件,因为通过这种方式转的任务触发的是 ACTIVITY_CANCELLED 事件。

最后将原来的 Task 删除

1
2
3
4
5
6
7
task.setExecutionId(null);

taskEntityManager.update(task);

TaskHelper.deleteTask(taskId, comment, false);

return task.getProcessInstanceId();
作者

Jakes Lee

发布于

2020-02-29

更新于

2021-11-18

许可协议

评论