Activiti5入门--个人任务分配

在前面的章节中,我们指定用户任务的办理人(assignee)时,通常都是直接在bpmn文件中直接指定特定的人。本章,我们将学习动态指定用户任务办理人。

为了更好地学习动态指定用户任务办理人的相关知识,我们新建包cn.demo.personaltask,类TaskTest,流程图文件task.bpmn,并定义一个审批流程,该流程只含有一个用户任务-审批任务,我们将使用不同的方法演示如何动态改变用户任务的办理人。

定义审批流程

这个审批流程相当简单,只有一个用户任务节点以及开始和结束节点

  1. 创建一个开始和结束节点
  2. 创建一个用户任务节点,设置其Name值为审批,并且暂时指定其Assignee为张三丰
  3. 使用连线连接三个节点
  4. 点击空白处,设置Id的值为task ,Name的值为taskProcess

这样,经过上述两个步骤,一个审批流程就定义好了。

个人任务分配

三种方式指定用户任务

在activi中,有三种指定用户任务办理人的方式

  1. 直接指定办理人,即直接设置用户任务Assignee字段的值
  2. 使用流程变量,通过流程变量动态指定办理人的值
  3. 使用实现org.activiti.engine.delegate.TaskListener接口的类方法来动态指定办理人的值

直接指定办理人

部署流程定义

和前面一样,我们先要部署一个新的流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 部署流程定义
*/
@Test
public void deploymentProcessDefinition() {
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("task.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("task.png");
Deployment deploy = processEngine.getRepositoryService() // 与流程定义和部署对象相关的Service
.createDeployment() // 创建一个部署对象
.name("任务") // 添加部署的名称
.addInputStream("task.bpmn", inputStreamBpmn)
.addInputStream("task.png",inputStreamPng)
.deploy(); // 完成部署
System.out.println("部署ID: " + deploy.getId());
System.out.println("部署名称: " + deploy.getName());
}

运行代码,输出如下结果

部署ID: 5101
部署名称: 任务

启动流程实例

紧接着部署流程定义之后的,就是启动流程实例了,我们来启动一下

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 启动流程实例
*/
@Test
public void startProcessInstance() {
// 流程定义的key
String processDefinitionKey = "task";
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefinitionKey);
System.out.println("流程实例ID: " + processInstance.getId());
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId()); // 对应act_re_procdef表中的ID // helloworld:1:4
}

运行代码,得到如下结果

流程实例ID: 5201
流程定义ID: task:1:5104

这样,我们就启动好了流程实例了。

查询个人任务

很明显,我们指定的办理人是张三丰,于是,我们查询张三丰的个人任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 查询当前人的个人任务
*/
@Test
public void findMyPersonalTask() {
String assignee = "张三丰";
List<Task> list = processEngine.getTaskService()
.createTaskQuery() // 创建任务对象
/*查询条件(部分)*/
.taskAssignee(assignee) // 指定个人任务,指定办理人
/*排序*/
.orderByTaskCreateTime().asc()
/*返回结果集*/
// .singleResult() // 返回唯一结果集
// .count() // 返回结果集数量
// .listPage(arg0, arg1) // 分页查询结果集
.list();
// 存储在act_ru_task表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("执行对象ID: " + task.getExecutionId());
System.out.println("流程定义ID: " + task.getProcessDefinitionId());
System.out.println("");
}
}
}

运行代码,我们可以得到张三丰个人任务的详细信息

任务ID: 5204
任务名称: 审批
任务的创建时间: Sat Oct 27 23:08:47 CST 2018
任务的办理人: 张三丰
流程实例ID: 5201
执行对象ID: 5201
流程定义ID: task:1:5104

完成个人任务

通过上面获取的张三丰个人任务的任务Id,我们可以很容易地使用taskId完成任务

1
2
3
4
5
6
7
8
9
/**
* 完成我的任务
*/
@Test
public void completeMyPersonalTask() {
String taskId = "5204";
processEngine.getTaskService().complete(taskId);
System.out.println("完成任务:任务Id:" + taskId);
}

可以看到,这种直接指定办理人的方式不够灵活。所以,下面介绍两种动态指定办理人的方式。

使用流程变量指定办理人

动态指定办理人的方式其中之一就是使用流程变量。在这个流程中,我们选中审批用户任务,将Assignee的值修改为一个变量的取值。

使用流程变量指定办理人

部署流程定义和启动流程实例

我们重新部署流程定义以及启动流程实例。首先,重新部署流程定义

部署ID: 5401
部署名称: 任务

然后启动流程实例,并在启动时设置流程变量userID的值,指定办理人为周芷若

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 启动流程实例
*/
@Test
public void startProcessInstance() {
// 流程定义的key
String processDefinitionKey = "task";
// 启动流程实例的同时,设置流程变量,使用流程变量来指定办理人
Map<String,Object> variables = new HashMap<String,Object>();
variables.put("userID", "周芷若");
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefinitionKey,variables);
System.out.println("流程实例ID: " + processInstance.getId());
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId());
}

运行代码,输出如下结果

流程实例ID: 5501
流程定义ID: task:2:5404

查询个人任务

为了验证这种方式是否有效,我们查询一下办理人周芷若的个人任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 查询当前人的个人任务
*/
@Test
public void findMyPersonalTask() {
String assignee = "周芷若";
List<Task> list = processEngine.getTaskService() // 与正在执行的任务管理相关的Service
.createTaskQuery() // 创建任务对象
/*查询条件(部分)*/
.taskAssignee(assignee) // 指定个人任务,指定办理人
// .taskCandidateUser(arg0) 组任务的办理人
// .processDefinitionId(arg0) 流程定义id
// .processInstanceId(arg0) // 流程实例id
// .executionId(arg0) // 执行对象id
/*排序*/
.orderByTaskCreateTime().asc()
/*返回结果集*/
// .singleResult() // 返回唯一结果集
// .count() // 返回结果集数量
// .listPage(arg0, arg1) // 分页查询结果集
.list();
// 存储在act_ru_task表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("执行对象ID: " + task.getExecutionId());
System.out.println("流程定义ID: " + task.getProcessDefinitionId());
System.out.println("");
}
}
}

运行代码,我们得到了周芷若的个人任务详细信息,说明使用流程变量指定办理人这样方式是有效的。

任务ID: 5505
任务名称: 审批
任务的创建时间: Sun Oct 28 15:20:59 CST 2018
任务的办理人: 周芷若
流程实例ID: 5501
执行对象ID: 5501
流程定义ID: task:2:5404

完成个人任务

这样,我们再使用任务ID完成周芷若的个人任务

1
2
3
4
5
6
7
8
9
/**
* 完成我的任务
*/
@Test
public void completeMyPersonalTask() {
String taskId = "5505";
processEngine.getTaskService().complete(taskId);
System.out.println("完成任务:任务Id:" + taskId);
}

到此,该流程就结束了,下面,我们介绍另外一种指定任务办理人的方式。

使用类指定办理人

在本节中,我们将使用类来动态指定任务办理人。为了更好地演示,我们新建一个包cn.demo.personaltaskbyclass,并复制一份代码,将TaskTest.javatask.bpmn的复制到新包中来,并将

bpmn文件中审批用户任务的办理人字段的内容清空。

为了使用类动态分配个人任务,我们将创建一个新类TaskListenerImpl,该类要实现TaskListener接口,并实现notify方法达到指定用户任务办理人的目的。在这里,我们指定办理人为灭绝师太

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.demo.personaltaskbyclass;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
public class TaskListenerImpl implements TaskListener {
@Override
public void notify(DelegateTask arg0) {
// 指定个人任务的办理人,也可以指定组任务的办理人
// 通过类去查询数据库,将下一个任务的办理人查询获取,
// 然后通过setAssignee()的方法指定任务的办理
arg0.setAssignee("灭绝师太");
}
}

当然,这样做是还不够的,我们还需要选中审批用户任务节点,设置Listener的实现类。

设置Listener的实现类

我们查看一下XML文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="task" name="taskProcess" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="审批">
<extensionElements>
<activiti:taskListener event="create" class="cn.demo.personaltaskbyclass.TaskListenerImpl"></activiti:taskListener>
</extensionElements>
</userTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_task">
<bpmndi:BPMNPlane bpmnElement="task" id="BPMNPlane_task">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="360.0" y="20.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="325.0" y="120.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="360.0" y="240.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="377.0" y="55.0"></omgdi:waypoint>
<omgdi:waypoint x="377.0" y="120.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="377.0" y="175.0"></omgdi:waypoint>
<omgdi:waypoint x="377.0" y="240.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

注意到下面这一段,可以看到我们设置的Listener实现类

1
2
3
4
5
<userTask id="usertask1" name="审批">
<extensionElements>
<activiti:taskListener event="create" class="cn.demo.personaltaskbyclass.TaskListenerImpl"></activiti:taskListener>
</extensionElements>
</userTask>

部署流程定义和启动流程实例

和前面一样,我们重新部署一下流程定义

部署ID: 5701
部署名称: 任务

部署好流程定义以后,启动流程实例。此时,我们不再需要设置流程变量了,指定办理人的操作交给了监听类TaskListenerImpl

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 启动流程实例
*/
@Test
public void startProcessInstance() {
// 流程定义的key
String processDefinitionKey = "task";
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefinitionKey);
System.out.println("流程实例ID: " + processInstance.getId());
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId());
}

运行代码,得到如下结果

流程实例ID: 5801
流程定义ID: task:3:5704

查询个人任务

现在,我们查询一下灭绝师太的个人任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 查询当前人的个人任务
*/
@Test
public void findMyPersonalTask() {
String assignee = "灭绝师太";
List<Task> list = processEngine.getTaskService() // 与正在执行的任务管理相关的Service
.createTaskQuery() // 创建任务对象
/*查询条件(部分)*/
.taskAssignee(assignee) // 指定个人任务,指定办理人
// .taskCandidateUser(arg0) 组任务的办理人
// .processDefinitionId(arg0) 流程定义id
// .processInstanceId(arg0) // 流程实例id
// .executionId(arg0) // 执行对象id
/*排序*/
.orderByTaskCreateTime().asc()
/*返回结果集*/
// .singleResult() // 返回唯一结果集
// .count() // 返回结果集数量
// .listPage(arg0, arg1) // 分页查询结果集
.list();
// 存储在act_ru_task表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("执行对象ID: " + task.getExecutionId());
System.out.println("流程定义ID: " + task.getProcessDefinitionId());
System.out.println("");
}
}
}

运行代码,输出灭绝师太个人任务详情

任务ID: 5804
任务名称: 审批
任务的创建时间: Sun Oct 28 16:08:46 CST 2018
任务的办理人: 灭绝师太
流程实例ID: 5801
执行对象ID: 5801
流程定义ID: task:3:5704

可以看到,使用类动态分配个人任务也是可行的。

认领和完成任务

有些时候,我们需要将一个人的任务分配给另一个人,那么,我们就可以使用taskServicesetAssignee方法将灭绝师太的任务转给张翠山。

1
2
3
4
5
6
7
@Test
public void setAssigneeTask() {
String taskId = "5804";
String assignee = "张翠山";
processEngine.getTaskService()
.setAssignee(taskId, assignee);
}

查询一下张翠山的个人任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 查询当前人的个人任务
*/
@Test
public void findMyPersonalTask() {
String assignee = "张翠山";
List<Task> list = processEngine.getTaskService() // 与正在执行的任务管理相关的Service
.createTaskQuery() // 创建任务对象
/*查询条件(部分)*/
.taskAssignee(assignee) // 指定个人任务,指定办理人
// .taskCandidateUser(arg0) 组任务的办理人
// .processDefinitionId(arg0) 流程定义id
// .processInstanceId(arg0) // 流程实例id
// .executionId(arg0) // 执行对象id
/*排序*/
.orderByTaskCreateTime().asc()
/*返回结果集*/
// .singleResult() // 返回唯一结果集
// .count() // 返回结果集数量
// .listPage(arg0, arg1) // 分页查询结果集
.list();
// 存储在act_ru_task表
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("执行对象ID: " + task.getExecutionId());
System.out.println("流程定义ID: " + task.getProcessDefinitionId());
System.out.println("");
}
}
}

若没有异常,可以看到我们成功将灭绝师太的任务转给了张翠山

任务ID: 5804
任务名称: 审批
任务的创建时间: Sun Oct 28 16:08:46 CST 2018
任务的办理人: 张翠山
流程实例ID: 5801
执行对象ID: 5801
流程定义ID: task:3:5704

最后,我们完成张翠山的个人任务,结束该流程。

1
2
3
4
5
6
7
8
9
/**
* 完成我的任务
*/
@Test
public void completeMyPersonalTask() {
String taskId = "5804";
processEngine.getTaskService().complete(taskId);
System.out.println("完成任务:任务Id:" + taskId);
}

总结

通过上面的介绍,我们已经大致了解了分配个人任务的方式大致有三种

  1. 在bpmn文件中直接指定办理人
  2. 在bpmn文件中使用流程变量,通过给流程变量设值的方式指定办理人
  3. 定义一个实现TaskListener接口的类,并自定义delegateTask.setAssignee(assignee)方法,指定个人任务的办理人

除此之外,我们还可以使用taskService() setAssignee(taskId, userId)方法重新指定办理人,将某个人的任务转给另外一个人。