Activiti——子流程应用详解

子流程

子流程是一种特殊的流程活动,它可以包含其他的流程元素,例如流程任务、流程网关、流程事件和顺序流等,它是一个较大的流程的组成部分,或者可以将其看作流程中的一个容器,用于存放其他流程活动。在BPMN规范中定义了5种子流程:嵌入式子流程、调用式子流程、事件子流程、事务子流程和特别子流程。Activiti5支持前4个子流程,Activiti6新增了对特别子流程(Ad-Hoc Sub-Process)的支持,BPMN规范定义它是一种特别的子流程,它的里面可以存放多个活动,这些活动之间可以不存在任何的流程顺序关系(可以不使用顺序流),它们只是零散地存在于特别子流程的容器中,流程的顺序和执行由这些活动在运行时决定。

1、嵌入式子流程

嵌入式子流程是最常见的子流程,整个子流程都会被完整地定义在父流程中。换言之,如果不使用子流程,同样也会将这些流程活动定义到主流程中,与子流程的效果一样,但是如果想为某部分流程活动添加特定的事件范围,那么此时使用嵌入式子流程就显得很有必要。假设当前有一个ATM机操作的业务流程,其中的转账可以分为若干个步骤(流程活动),,当其中一个步骤出现异常时,可以触发流程补偿或者取消等边界事件,因此可以将转账相关的流程活动划归为一个子流程,然后为子流程加入边界事件,这样相当于为转账的全部步骤划定了事件范围。除此之外,使用嵌入式子流程还可以让子流程在流程设计器中展开和收缩,对于一些庞大的流程,这有助于让流程图变得更加简洁,不过目前Activiti的流程设计器不支持该功能。

在Activiti中,子流程中只能有一个无指定开始事件,不能有其他类型的开始事件。BPMN2.0规范允许子流程没有开始事件或者结束事件,但是当前的Activiti对此不支持。图所示为一个含有子流程的流程图,对应的流程文件内容如下所示:
在这里插入图片描述

<process id="process1" name="process1" isExecutable="true">
	<startEvent id="startevent1" name="Start"></startEvent>
	<subProcess id="subprocess1" name="Sub Process">
		<startEvent id="startevent2" name="Start"></startEvent>
		<serviceTask id="usertask2" name="Sub Task"
			activiti:class="pers.zhang.delegate.EmbededJavaDelegate"></serviceTask>
		<endEvent id="endevent1" name="End"></endEvent>
		<sequenceFlow id="flow3" sourceRef="startevent2"
			targetRef="usertask2"></sequenceFlow>
		<sequenceFlow id="flow4" sourceRef="usertask2"
			targetRef="endevent1"></sequenceFlow>
	</subProcess>
	<boundaryEvent id="boundaryerror1" attachedToRef="subprocess1">
		<errorEventDefinition></errorEventDefinition>
	</boundaryEvent>
	<endEvent id="endevent2" name="End"></endEvent>
	<sequenceFlow id="flow1" sourceRef="startevent1"
		targetRef="subprocess1"></sequenceFlow>
	<sequenceFlow id="flow2" sourceRef="subprocess1"
		targetRef="endevent2"></sequenceFlow>
	<userTask id="usertask1" name="Error Task"></userTask>
	<sequenceFlow id="flow5" sourceRef="boundaryerror1"
		targetRef="usertask1"></sequenceFlow>
	<endEvent id="endevent3" name="End"></endEvent>
	<sequenceFlow id="flow6" sourceRef="usertask1" targetRef="endevent3"></sequenceFlow>
</process>
public class EmbededJavaDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("执行子流程Service Task的Java Delegate,抛出错误");
        throw new BpmnError("myError");
    }
}
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    RepositoryService repositoryService = processEngine.getRepositoryService();
    Deployment deploy = repositoryService.createDeployment().addClasspathResource("subprocess/EmbededSubProcess.bpmn").deploy();
    //启动流程
    RuntimeService runtimeService = processEngine.getRuntimeService();
    ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1");
    //查询任务
    TaskService taskService = processEngine.getTaskService();
    Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
    System.out.println("当前任务:" + task.getName());
执行子流程Service TaskJava Delegate,抛出错误
当前任务:Error Task

在Activiti的实现中,如果执行流到达子流程,会创建新的执行流数据(ACT_RU_EXECUTION表的数据),该执行流数据的PARENT ID字段值为主执行流的ID,即主执行流遇到子流程时,会创建新的执行流来执行该子流程。

2、调用式子流程

在实际的企业应用中,有可能存在由多个流程共用的子流程,例如请假流程和加班流程,都需要先经过人事审批,再经过总监审批,这个时候,就可以将人事审批和总监审批单独作为一个流程来定义,而请假流程和加班流程则可以使用“callActivity”元素来进行子流程的调用。
这些可以重用的流程定义,与普通的流程定义一样,需要有一个开始事件、一个结束事件和若干个流程活动(事件),但是由于它会被其他流程调用,因此,其只能拥有一个无指定开始事件。根据以上的请假流程例子,建立独立的人事审批和总监审批流程。其中,人事审批和总监审批的流程如图13-2所示,请假流程如图13-3所示。

在这里插入图片描述

图13-2所示为一个普通的流程,流程中只有两个用户任务,而图13-3中的请假流程中,第一个流程活动为“填写申请”用户任务,第二个活动为“人事、总监子流程”,第二个活动是一个调用式子流程,会调用图13-2中定义的流程。

<process id="process1" name="process1">
	<startEvent id="startevent1" name="Start"></startEvent>
	<endEvent id="endevent1" name="End"></endEvent>
	<callActivity id="subprocess1" name="人事、总监子流程"
		calledElement="SubProcess"></callActivity>
	<userTask id="usertask2" name="填写申请"></userTask>
	<sequenceFlow id="flow4" name="" sourceRef="startevent1"
		targetRef="usertask2"></sequenceFlow>
	<sequenceFlow id="flow5" name="" sourceRef="usertask2"
		targetRef="subprocess1"></sequenceFlow>
	<sequenceFlow id="flow6" name="" sourceRef="subprocess1"
		targetRef="endevent1"></sequenceFlow>
</process>

代码使用callActivity元素来配置一个调用式子流程,该子流程会调用一个key为“SubProcess”的独立部署流程(流程定义的key使用process元素的id属性)。

<process id="SubProcess" name="SubProcess">
	<startEvent id="startevent1" name="Start"></startEvent>
	<userTask id="servicetask1" name="人事审批"></userTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<userTask id="usertask1" name="总监审批"></userTask>
	<sequenceFlow id="flow2" name="" sourceRef="servicetask1"
		targetRef="usertask1"></sequenceFlow>
	<sequenceFlow id="flow3" name="" sourceRef="usertask1"
		targetRef="endevent1"></sequenceFlow>
</process>

注意代码清单中定义的流程,流程定义的key(process的id属性)为“SubProcess”。如果该流程不被其他流程调用,它就是一个简单的审批流程。

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    RepositoryService repositoryService = engine.getRepositoryService();
    RuntimeService runtimeService = engine.getRuntimeService();
    TaskService taskService = engine.getTaskService();
    repositoryService
            .createDeployment()
            .addClasspathResource("subprocess/SimpleCallActivity.bpmn")
            .addClasspathResource("subprocess/SubProcess.bpmn")
            .deploy();
    // 启动流程
    ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1");
    // 查询任务
    Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
    System.out.println("完成任务:" + task.getName());
    taskService.complete(task.getId());
    // 查询当前的流程实例
    List<ProcessInstance> pis = runtimeService.createProcessInstanceQuery().list();
    System.out.println("当前的流程实例数量:" + pis.size());
    // 查询当前全部的执行流数量
    List<Execution> executions = runtimeService.createExecutionQuery().list();
    System.out.println("当前执行流数量:" + executions.size());
    // 查询当前任务
    task = taskService.createTaskQuery().singleResult();
    System.out.println("当前任务名称:" + task.getName());
完成任务:填写申请
当前的流程实例数量:2
当前执行流数量:4
当前任务名称:人事审批

根据结果可知,完成填写申请的用户任务后,当前的流程实例数量为2,当前的执行流数量为4,当前的任务为“人事审批”任务,流程到达调用式子流程中。

3、调用式子流程的参数传递

根据上一节讲述可知,由于调用式的子流程是独立定义在一个流程文件中的,因此当执行流到达调用式子流程时,会创建新的流程实例,那么有两个流程实例,就会出现流程参数的传递问题。根据前面章节的讲述可知,可以使用RuntimeService的setVariable等方法设置流程参数,这些流程参数的作用域只限于所设置的执行流,对于调用式子流程这种会产生新的流程实例的“特殊情况”,就需要使用另外的方式解决。Activiti提供了扩展配置,允许流程参数在主流程和子流程中传递。

在这里插入图片描述

<process id="process1" name="process1" isExecutable="true">
	<startEvent id="startevent1" name="Start"></startEvent>
	<userTask id="usertask1" name="填写申请"></userTask>
	<callActivity id="subprocess1" name="人事、总监审批"
		calledElement="SubProcess">
		<extensionElements>
			<activiti:in source="days" target="newDays"></activiti:in>
			<activiti:out source="myDays" target="resultDays"></activiti:out>
		</extensionElements>
	</callActivity>
	<endEvent id="endevent1" name="End"></endEvent>
	<sequenceFlow id="flow1" sourceRef="startevent1"
		targetRef="usertask1"></sequenceFlow>
	<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="subprocess1"></sequenceFlow>
	<userTask id="usertask2" name="完成任务"></userTask>
	<sequenceFlow id="flow3" sourceRef="subprocess1"
		targetRef="usertask2"></sequenceFlow>
	<sequenceFlow id="flow4" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
</process>

代码调用了“SubProcess”子流程,并且为其提供了扩展参数。使用activiti:in元素定义子流程的输入参数,此处使用了source和target属性,表示会使用主流程名称为“days”的流程参数,将其转换成名为“newDays’”的子流程参数。使用activiti:out元素定义子流程的输出参数,同样使用了source和target属性,表示会将子流程中名称为
“myDays”的子流程参数,转换成名为“resultDays”的主流程参数:

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
TaskService taskService = engine.getTaskService();
repositoryService
        .createDeployment()
        .addClasspathResource("subprocess/CallActivityVariable.bpmn")
        .addClasspathResource("subprocess/SubProcess.bpmn")
        .deploy();
// 启动流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1");
// 完成填写申请任务并设置参数
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("days", 10);
Task task = taskService.createTaskQuery().singleResult();
taskService.complete(task.getId(), vars);
// 查询新建的流程实例
ProcessInstance subPi = runtimeService.createProcessInstanceQuery().processDefinitionKey("SubProcess").singleResult();
// 在主流程中查询参数
Integer days = (Integer) runtimeService.getVariable(pi.getId(), "days");
System.out.println("使用days名称查询参数:" + days);
// 在调用式子流程中查询参数
days = (Integer) runtimeService.getVariable(subPi.getId(), "newDays");
System.out.println("使用newDays名称查询参数:" + days);
// 设置流程参数
runtimeService.setVariable(subPi.getId(), "myDays", (days - 5));
// 完成子流程全部任务
task = taskService.createTaskQuery().singleResult();
System.out.println("当前任务:" + task.getName());
taskService.complete(task.getId());
task = taskService.createTaskQuery().singleResult();
System.out.println("当前任务:" + task.getName());
taskService.complete(task.getId());
// 在主流程中查询参数
days = (Integer) runtimeService.getVariable(pi.getId(), "resultDays");
System.out.println("使用resultDays名称查询参数:" + days);
使用days名称查询参数:10
使用newDays名称查询参数:10
当前任务:人事审批
当前任务:总监审批
使用resultDays名称查询参数:5

本例中的activiti:in和activiti::out元素,除了可以使用source和target属性外,还可以使用sourceExprerssion属性,该属性允许使用JUEL表达式来提供源参数,例如可以使用${bean.field}来获取源参数对象中的属性值。

4、事件子流程

事件子流程是指由事件触发的子流程,这种子流程可以在主流程中使用,也可以在嵌入式子流程中使用。由于这种子流程是由某些事件的发生作为触发条件的,因此在事件子流程中使用无指定开始事件就变得没有意义,所以事件子流程中不允许使用无指定开始事件,当前的
Activiti只支持错误开始事件。在BPMN2.0规范中,可以将事件子流程分为可中止和不可中止两种,但是目前Activiti只支持中止的事件子流程。在使用事件子流程的过程中需要注意,不允许有任何的顺序流连接到这个事件子流程上。事件子流程的配置与嵌入式子流程一样,使
用subProcess元素,但是需要为该元素添加triggeredByEvent属性来指定它是一个由事件触发的子流程。
在这里插入图片描述

<process id="process1" name="process1">
	<startEvent id="startevent1" name="Start"></startEvent>
	<serviceTask id="servicetask1" name="Service Task"
		activiti:class="pers.zhang.delegate.ErrorJavaDelegate"></serviceTask>
	<endEvent id="endevent1" name="End"></endEvent>
	<subProcess id="eventsubprocess1" name="Event sub Process"
		triggeredByEvent="true">
		<startEvent id="errorstartevent1" name="Error start">
			<errorEventDefinition></errorEventDefinition>
		</startEvent>
		<userTask id="usertask1" name="Error Task"></userTask>
		<endEvent id="endevent2" name="End"></endEvent>
		<sequenceFlow id="flow1" name="" sourceRef="errorstartevent1"
			targetRef="usertask1"></sequenceFlow>
		<sequenceFlow id="flow2" name="" sourceRef="usertask1"
			targetRef="endevent2"></sequenceFlow>
	</subProcess>
	<sequenceFlow id="flow3" name="" sourceRef="startevent1"
		targetRef="servicetask1"></sequenceFlow>
	<sequenceFlow id="flow4" name="" sourceRef="servicetask1"
		targetRef="endevent1"></sequenceFlow>
</process>
public class ErrorJavaDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("执行JavaDelegate类,抛出错误");
        throw new BpmnError("error");
    }
}
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
TaskService taskService = engine.getTaskService();
repositoryService.createDeployment().addClasspathResource("subprocess/ErrorEventProcess.bpmn").deploy();
ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1");
// 查询当前任务
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
System.out.println("当前任务名称:" + task.getName());
执行JavaDelegate类,抛出错误
当前任务名称:Error Task

事件子流程除了可以放在主流程中外,还可以放在嵌入式子流程中,这样做的效果类似于触发子流程的边界事件,如图13-7和图13-8所示。
在这里插入图片描述

在这里插入图片描述

两个流程的子流程中,如果其中的任务抛出错误,那么均会触发错误事件,在图13-7中触发的是事件子流程,在图13-8中触发的是错误边界事件,这两种流程所表达的业务含义实际上并无差别,但是在使用过程中,图13-7所示的流程中的事件子流程可以使用子流程中定
义的本地(Local)参数,因为它们存在于同一个事件范围中,而图l3-8中的“Error Task”已经脱离了子流程的事件范围,因此不可以访问子流程中定义的本地参数。

5、事务子流程

事务子流程需要嵌入到主流程中,也属于嵌入式子流程的一种,主要用于将多个流程活动存放到一个事务中。需要将流程的事务与数据库的事务区分开来,数据库的事务需要有ACID四个基本要素:原子性、一致性、隔离性和持久性。流程事务和数据库事务之间的区别和联系
如下所述:

  • 一个数据库事务会在一个很短的时间内完成,而流程中事务的时间跨度会非常大,有可能是以小时、天、月为单位,正是由于这种不确定性,造成流程中的事务不可能被划分时间间隔。
  • 一个流程事务可能包含多个数据库事务。
  • 在数据库事务中,即使事务中的一部分工作已经完成,外界也不会知道这部分工作已经完成。而在流程事务中,已经完成的流程活动,外界是可以知道的。
  • 一个流程事务不可能回滚,因为一个流程事务有可能被划分为多个数据库事务,当接收到流程事务取消的通知时,可能已经完成了多个数据库事务,因此流程事务不可能回滚。

对于流程事务,有可能会产生三种结果:事务成功完成、事务取消和事务错误完成。对于成功完成的事务,执行流完成以后沿着顺序流离开这个流程活动。对于取消的事务,一旦取消事件被触发,那么该事务子流程中的全部执行流将会被中断并且触发流程的补偿机制,补偿完成后沿着顺序流离开流程活动。对于错误完成的事务,并不会进行流程补偿。

在使用事务子流程时需要注意与数据库的事务区分,流程的事务没有回滚的概念,但是如果要取消事务,可以使用流程的补偿(Compensation)机制,只有指定补偿者的流程活动,才会被补偿,而数据库的事务,要么全部成功,要么全部失败,这是数据库事务的原子性决定的。

在这里插入图片描述

<process id="process1" name="process1" isExecutable="true">
	<startEvent id="startevent1" name="Start"></startEvent>
	<transaction id="subprocess1" name="电影票购票事务">
		<startEvent id="startevent2" name="Start"></startEvent>
		<userTask id="usertask1" name="选场次"></userTask>
		<serviceTask id="usertask2" name="锁定座位"
			activiti:class="pers.zhang.delegate.LockSeatDelegate"></serviceTask>
		<boundaryEvent id="boundarysignal1" attachedToRef="usertask2"
			cancelActivity="true">
			<compensateEventDefinition></compensateEventDefinition>
		</boundaryEvent>
		<serviceTask id="usertask3" name="支付"
			activiti:class="pers.zhang.delegate.PayDelegate"></serviceTask>
		<boundaryEvent id="boundaryerror1" attachedToRef="usertask3">
			<errorEventDefinition></errorEventDefinition>
		</boundaryEvent>
		<endEvent id="endevent1" name="End"></endEvent>
		<serviceTask id="servicetask1" name="取消座位锁定"
			isForCompensation="true" activiti:class="pers.zhang.delegate.UnlockSeatDelegate"></serviceTask>
		<endEvent id="endevent2" name="End">
			<cancelEventDefinition></cancelEventDefinition>
		</endEvent>
		<sequenceFlow id="flow3" sourceRef="startevent2"
			targetRef="usertask1"></sequenceFlow>
		<sequenceFlow id="flow4" sourceRef="usertask1"
			targetRef="usertask2"></sequenceFlow>
		<sequenceFlow id="flow5" sourceRef="usertask2"
			targetRef="usertask3"></sequenceFlow>
		<sequenceFlow id="flow6" sourceRef="usertask3"
			targetRef="endevent1"></sequenceFlow>
		<sequenceFlow id="flow13" sourceRef="boundaryerror1"
			targetRef="endevent2"></sequenceFlow>
	</transaction>
	<boundaryEvent id="boundarysignal2" attachedToRef="subprocess1"
		cancelActivity="true">
		<cancelEventDefinition></cancelEventDefinition>
	</boundaryEvent>
	<endEvent id="endevent4" name="End"></endEvent>
	<endEvent id="endevent5" name="End"></endEvent>
	<sequenceFlow id="flow1" sourceRef="startevent1"
		targetRef="subprocess1"></sequenceFlow>
	<sequenceFlow id="flow2" sourceRef="subprocess1"
		targetRef="endevent5"></sequenceFlow>
	<sequenceFlow id="flow12" sourceRef="boundarysignal2"
		targetRef="endevent4"></sequenceFlow>
	<association id="a2" sourceRef="boundarysignal1"
		targetRef="servicetask1"></association>
</process>

图中定义了一个电影购票流程,当流程开始时,会执行一个购票的事件子流程,用户需要先进行选择电影场次的操作,然后进行座位选定,最后进行支付。在座位选定的任务中,会将所选的场次座位锁定,一旦支付失败,就会触发错误边界事件(支付任务的边界事件),然后执行流会到达取消结束事件,此时事务子流程的取消边界事件便会被触发,并且触发事务子流程里面的补偿边界事件。本例中的补偿边界事件是锁定座位的边界事件,当补偿触发时,会执行“取消锁定座位”的任务。

流程中的三个Service Task配置了JavaDelegate类,这三个JavaDelegate类,仅仅在控制台输出相应的文字,而进行支付的JavaDelegate类,除了出文字外,还会抛出BpmnError,.抛出的错误会被“支付”任务的错误边界事件所捕获,然后改变流程走向,最终以取消结束事件来结束事务子流程。

public class LockSeatDelegate implements JavaDelegate {

	public void execute(DelegateExecution execution) {
		System.out.println("执行锁定座位");
	}
}

public class PayDelegate implements JavaDelegate {

	public void execute(DelegateExecution execution) {
		System.out.println("支付失败,抛出错误");
		throw new BpmnError("payError");
	}
}

public class UnlockSeatDelegate implements JavaDelegate {

	public void execute(DelegateExecution execution) {
		System.out.println("取消锁定座位");
	}
}
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
TaskService taskService = engine.getTaskService();
repositoryService.createDeployment().addClasspathResource("subprocess/BuyMovieTicket.bpmn").deploy();
ProcessInstance pi = runtimeService.startProcessInstanceByKey("process1");
// 查询当前任务
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
System.out.println("当前任务:" + task.getName());
// 完成选场次的任务
taskService.complete(task.getId());
当前任务:选场次
执行锁定座位
支付失败,抛出错误
取消锁定座位

6、特别子流程

Activiti6.0中增加了对特别子流程的支持,在特别子流程的容器中可以存放多个流程节点,这些节点在运行前相互之间不存在先后顺序,流程的顺序在执行时决定。本书成书时,Activiti尚未提供关于特别子流程的API,并且Eclipse的流程设计器也不支持显示特别子流程,这里暂时使用普通的子流程代替。图13-10所示为一个特别子流程的示例。

如图13-10所示,特别子流程中有两个用户任务,在定义流程时,并没有设定流程走向,子流程完成后,就会到达“After task”。

在这里插入图片描述

<process id="simpleSubProcess">
	<startEvent id="theStart" />
	<sequenceFlow id="flow1" sourceRef="theStart" targetRef="adhocSubProcess" />
	<adHocSubProcess id="adhocSubProcess" ordering="Sequential">
		<userTask id="subProcessTask" name="Task in subprocess" />
		<userTask id="subProcessTask2" name="Task2 in subprocess" />
	</adHocSubProcess>
	<sequenceFlow id="flow2" sourceRef="adhocSubProcess"
		targetRef="afterTask" />
	<userTask id="afterTask" name="After task" />
	<sequenceFlow id="flow3" sourceRef="afterTask" targetRef="theEnd" />
	<endEvent id="theEnd" />
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
TaskService taskService = engine.getTaskService();
repositoryService.createDeployment().addClasspathResource("subprocess/AdHocProcess.bpmn").deploy();
// 启动流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("simpleSubProcess");
System.out.println("开始流程后,执行流数量:" + runtimeService.createExecutionQuery().processInstanceId(pi.getId()).count());
// 查询子流程的执行流
Execution exe = runtimeService.createExecutionQuery()
        .processInstanceId(pi.getId()).activityId("adhocSubProcess")
        .singleResult();
// 让执行流到达第二个任务
runtimeService.executeActivityInAdhocSubProcess(exe.getId(),
        "subProcessTask2");
// 查询执行流数量
System.out.println("让执行流到达第二个任务后,执行流数量:"
        + runtimeService.createExecutionQuery()
        .processInstanceId(pi.getId()).count());
// 完成第二个任务
Task subProcessTask2 = taskService.createTaskQuery()
        .processInstanceId(pi.getId())
        .taskDefinitionKey("subProcessTask2").singleResult();
taskService.complete(subProcessTask2.getId());
// 查询执行流数量
System.out.println("完成子流程的第二任务后,执行流数量:"
        + runtimeService.createExecutionQuery()
        .processInstanceId(pi.getId()).count());
// 完成特别子流程
runtimeService.completeAdhocSubProcess(exe.getId());
// 查询数量
System.out.println("完成整个特别子流程后,当前任务名称:"
        + taskService.createTaskQuery().processInstanceId(pi.getId())
        .singleResult().getName());
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值