这里讲解BPMN2.0的流程任务,它是工作流的核心元素,每个任务表示在流程中需要执行的工作。
在BPMN2.0中定义了许多任务,它有着各自的特定属性:
流程任务 | 属性 |
Service Task(服务任务) | 可以用于调外部服务或自动执行程序。 |
Send Task(发送任务) | 用于处理外部流程参与人发送消息的工作。eg:EmailTask。 |
Receive Taks(接收任务) | 等待外部流程参与者发送消息的任务。 |
User Task(用户任务) | 表示需要人参与的任务,可以配置候选人/组/代理人等。 |
Script Task(脚本任务) | 用于执行定义好的脚本程序,流程执行到这个结点自动执行脚本。 |
这里我们着重讲解User Task用户任务。
用户任务流程图如下:
流程定义文件如下:
这里的UserTask定义的很简单,就定义了id与name。我们可以对UserTask进行更多的属性设置,比如候选人、候选组、指定的委托代理人和最后设定的指定时间。
bpmn2.0规范配置候选人/候选组:
user(jjf),group(student)
可以看到做了一个相对比较复杂的嵌套结构,在 formalExpression 标签中设置了对应的user为“jjf”与group为“student”。这样的设置比较复杂,所提activiti给我们提供了一个更为简单的扩展方式。
候选用户:
候选组:
比如有个用户任务需要候选人,但是候选人有多个并且只有其中一个候选人才能真正操作某个用户任务,从候选人中选出真正操作的人称为代理人。bpmn2.0规范配置代理人:
jjf
我们在formalExpression标签中设置了代理人为 “jjf”,这里我们的代理人只能有一个,不会是代理人列表。
activiti提供了一个简化代理人配置:
当这个任务指定了代理人后,应该要限制代理人在规定时间内完成这个任务,标记代理人完成任务时间配置如下:
我们可以在dateVariable表达式中设置时间,比如当前任务启动以后三小时以内或三天以内。
监听器配置方式如下:
在userTask标签中加上 extensionElements 扩展对象,然后我们在扩展对象中自定义 activiti:taskListener ,指定class(监听器的实现类),还有就是要指定事件类型,它指task对应的事件类型公有四种事件类型:create(创建)、assignment(指定代理人)、complete(完成事件)、delete(删除)。
其中 MyTaskListener 自定义监听器类内容如下:
public class MyTaskListener implements TaskListener {
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("jjf");
delegateTask.setVariable("key1","value1");
// ... setXXX
delegateTask.setDueDate(DateTime.now().plusDays(3).toDate()); //设定截止日期为当前时间后推迟3天
}
}
我们创建的 MyTaskListener 类,并且继承了 TaskListener 接口,对应实现了 notify 方法,在方法中获取了 delegateTask 参数,做了属性设置。这里我设置了delegateTask的委托代理人、上下文变量与截止日期(当前时间向后推迟3天)。
接下来进行userTask的实例讲解。
首先创建 my-process-usertask.bpmn20.xml 流程定义文件:
其中涉及测试的流程图内容:
然后创建 UserTaskTest 测试类:
测试类内容如下:
public class UserTaskTest {
private static final Logger LOGGER = LoggerFactory.getLogger(UserTaskTest.class); //配置日志
@Rule
public ActivitiRule activitiRule = new ActivitiRule();
@Test
@Deployment(resources = {"my-process-usertask.bpmn20.xml"})
public void testUserTask(){
activitiRule.getRuntimeService().startProcessInstanceByKey("my-process"); //启动my-process流程
TaskService taskService = activitiRule.getTaskService();
Task task = taskService.createTaskQuery().taskCandidateUser("user1").singleResult();
LOGGER.info("find by user1 task = {}" , task);
task = taskService.createTaskQuery().taskCandidateUser("user2").singleResult();
LOGGER.info("find by user2 task = {}" , task);
task = taskService.createTaskQuery().taskCandidateGroup("group1").singleResult();
LOGGER.info("find by group1 task = {}" , task);
}
}
测试输出结果如下:
可以看到我们通过候选用户user1、user2都可以获取到对应的task,通过候选组group1也可以获取到task。
下面来测试用户任务的委托代理人:
委托代理人有两种方法,claim与setAssignee
taskService.claim(task.getId(), "user2");
taskService.setAssignee(task.getId(),"user2");
这里我们推荐使用claim方法,它会做校验,如果task已经被指定候选人则会抛出异常。然而使用 setAssignee 设定指定处理人,只修改属性,并不校验。
我们在上述测试函数中添加以下内容,最后测试函数为:
@Test
@Deployment(resources = {"my-process-usertask.bpmn20.xml"})
public void testUserTask(){
//........上述测试函数内容
//下面为两种委托代理人的方法
taskService.claim(task.getId(), "user2"); //更适合用claim,会做校验,如果task已经被指定候选人则会抛出异常
//taskService.setAssignee(task.getId(),"user2"); //设定指定处理人,只修改属性,并不校验
LOGGER.info("claim task.id = {} by user2",task.getId());
Task task1 = taskService.createTaskQuery().taskCandidateOrAssigned("user1").singleResult(); //看看通过user1可以获取什么内容
LOGGER.info("find by user1 task = {}",task1);
Task task2 = taskService.createTaskQuery().taskCandidateOrAssigned("user2").singleResult(); //看看通过user1可以获取什么内容
LOGGER.info("find by user2 task = {}",task2);
}
输出结果:
可以看到当我们指定了代理人为user2后,再查找user1的task则为空,查询user2的才能查询出来。