无论在那个流程引擎中,UserTask都是其核心Activity之一,我们的流程或多或少都会有人参与其中,所以这篇博客主要讲解一下如何对usertask进行操作
,而且最重要的是,流程引擎作为一个中间件的角色,我们的业务系统在接入的时候,会涉及到角,权限问题,比如:我们规定这个UserTask谁来操作,那些
指定的人来做,直接接入我们自己单独的业务系统的权限管理进行控制。
<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:flowable="http://flowable.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.flowable.org/processdef">
<process id="taskuser" name="taskuserdemo" isExecutable="true">
<documentation>用来测试taskuserdocumentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true">startEvent>
<userTask id="sid-E6BEE15E-5E0C-4CB6-8429-EF04E154EB07" name="task1" flowable:assignee="jack" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler">modeler:initiator-can-complete>
extensionElements>
userTask>
<sequenceFlow id="sid-63BFAE0B-3C2B-40B0-BBD7-9F50E7E640B1" sourceRef="startEvent1" targetRef="sid-E6BEE15E-5E0C-4CB6-8429-EF04E154EB07">sequenceFlow>
<userTask id="sid-7F157B39-8EF6-44D5-AF6F-131F7E811CA5" name="task2" flowable:assignee="${taskuser}" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler">modeler:initiator-can-complete>
extensionElements>
userTask>
<sequenceFlow id="sid-DF243F47-88E5-42F6-BC75-39E0383E6FCB" sourceRef="sid-E6BEE15E-5E0C-4CB6-8429-EF04E154EB07" targetRef="sid-7F157B39-8EF6-44D5-AF6F-131F7E811CA5">sequenceFlow>
<userTask id="sid-F7354E79-68EA-4F5F-8059-BE841CC1E1A8" name="task3" flowable:formFieldValidation="true">
<extensionElements>
<flowable:taskListener event="create" class="com.example.flowable.listener.TaskAssigneeListener">flowable:taskListener>
extensionElements>
userTask>
<sequenceFlow id="sid-204CEF7D-C334-4D4B-8897-96D99AA2E595" sourceRef="sid-7F157B39-8EF6-44D5-AF6F-131F7E811CA5" targetRef="sid-F7354E79-68EA-4F5F-8059-BE841CC1E1A8">sequenceFlow>
<endEvent id="sid-7E49054C-097E-4A47-BDC2-2868EAAEC97D">endEvent>
<sequenceFlow id="sid-97225C0C-90B7-482E-9A3B-322736BB2E83" sourceRef="sid-F7354E79-68EA-4F5F-8059-BE841CC1E1A8" targetRef="sid-7E49054C-097E-4A47-BDC2-2868EAAEC97D">sequenceFlow>
process>
<bpmndi:BPMNDiagram id="BPMNDiagram_taskuser">
<bpmndi:BPMNPlane bpmnElement="taskuser" id="BPMNPlane_taskuser">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="90.0" y="150.0">omgdc:Bounds>
bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-E6BEE15E-5E0C-4CB6-8429-EF04E154EB07" id="BPMNShape_sid-E6BEE15E-5E0C-4CB6-8429-EF04E154EB07">
<omgdc:Bounds height="80.0" width="100.0" x="165.0" y="125.0">omgdc:Bounds>
bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-7F157B39-8EF6-44D5-AF6F-131F7E811CA5" id="BPMNShape_sid-7F157B39-8EF6-44D5-AF6F-131F7E811CA5">
<omgdc:Bounds height="80.0" width="100.0" x="495.0" y="125.0">omgdc:Bounds>
bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-F7354E79-68EA-4F5F-8059-BE841CC1E1A8" id="BPMNShape_sid-F7354E79-68EA-4F5F-8059-BE841CC1E1A8">
<omgdc:Bounds height="80.0" width="100.0" x="810.0" y="125.0">omgdc:Bounds>
bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-7E49054C-097E-4A47-BDC2-2868EAAEC97D" id="BPMNShape_sid-7E49054C-097E-4A47-BDC2-2868EAAEC97D">
<omgdc:Bounds height="28.0" width="28.0" x="1075.0" y="151.0">omgdc:Bounds>
bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-DF243F47-88E5-42F6-BC75-39E0383E6FCB" id="BPMNEdge_sid-DF243F47-88E5-42F6-BC75-39E0383E6FCB">
<omgdi:waypoint x="264.94999999999334" y="165.0">omgdi:waypoint>
<omgdi:waypoint x="494.9999999997877" y="165.0">omgdi:waypoint>
bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-63BFAE0B-3C2B-40B0-BBD7-9F50E7E640B1" id="BPMNEdge_sid-63BFAE0B-3C2B-40B0-BBD7-9F50E7E640B1">
<omgdi:waypoint x="119.94999848995758" y="165.0">omgdi:waypoint>
<omgdi:waypoint x="165.0" y="165.0">omgdi:waypoint>
bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-97225C0C-90B7-482E-9A3B-322736BB2E83" id="BPMNEdge_sid-97225C0C-90B7-482E-9A3B-322736BB2E83">
<omgdi:waypoint x="909.9499999999626" y="165.0">omgdi:waypoint>
<omgdi:waypoint x="1075.0" y="165.0">omgdi:waypoint>
bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-204CEF7D-C334-4D4B-8897-96D99AA2E595" id="BPMNEdge_sid-204CEF7D-C334-4D4B-8897-96D99AA2E595">
<omgdi:waypoint x="594.9499999999543" y="165.0">omgdi:waypoint>
<omgdi:waypoint x="810.0" y="165.0">omgdi:waypoint>
bpmndi:BPMNEdge>
bpmndi:BPMNPlane>
bpmndi:BPMNDiagram>
definitions>
从上面可以看出来 定义了三个usertask,task1,task2,task3。
然后我分别给task1指定了任务分配人:jack
task2的任务分配人:${taskuser} (这是个EL表达式,还支持bean.attribute)
task3没有指定任务分配人但是task3指定了任务监听器:触发事件是create, 监听器的类是:
com.example.flowable.listener.TaskAssigneeListener
可以看上面的xml,也可以看到每个task的定义里面的assign属性都是指定了对应的人和监听器。
这里要注意一下:
我们可以看到上图,这上面类型有两个,一个是身份存储,一个是固定值,那个身份存储 应该走的是flowable自己的身份管理模块:IDM,所以我选择了固定值,就是比较灵活一点,上面说的三个task的分配人的设定都是通过固定值这个类型设置的。
初始化了流程之后,我们看一下:
我们看下task1的初始化信息:
我们可以看到因为我们在流程定义里指定了 assignee 是jack,所以流程走到task1的时候默认已经被分配给了jack,然后我们尝试在complete之前修改一下assignee 看行不行,通过java taskService.setAssignee(taskId, "olsen");
将jack换成olsen
此时我们再看task的assignee已经被改成了 olsen,下面我们进行complete这个task1:
他报错了,内容是说找不到${taskuser}这个property,为什么会这样呢?我分析一下:
其实从刚开始我们初始化了流程之后,流程就自动进行到task1,并且根据我们定义时候填入的信息初始化了task1,
也就是说我们complete之后,流程会自动进入到task2,并且根据们定义的信息初始化task2,然后发现找不到 ${taskuser}这个el表达式对应的值,所以就报了上面的错误,也就是说我们要再完成complete的时候或者之前,就已经要有taskuser这个属性了,
虽然报错了,我们先来看一下task1究竟complete成功了没,流程进行到哪里了。
可以看到流程没有变化,下面看下数据库中task1的进度是否已完成
从截图我们可以看到根据task的id我们看到 assignee确实已经从定义默认的jack转换成了oslen,end_time是空,说明也没结束,
好了,确保了流程没问题,我就去看complete的api,然后我换了complete的api,在完成的时候设置taskuser,
Map<String, Object> Tmap = new HashMap<>();
Tmap.put("taskuser", "allen");
taskService.complete(taskId, null, Tmap);
结果很完美,成功了。
流程从task1进入到task2,我们看一下task2的初始化信息:
我们可以看到此时的task2的assignee被初始化为allen了,也就是说我们再 上面complete task1的时候的 Map生效了,那么我们直接complete task2 进入到task3。
下面是任务监听器的内容:触发事件是create,也就是说在我complete task2之后,会自动触发 任务监听器。
package com.example.flowable.listener;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
public class TaskAssigneeListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
System.out.println(delegateTask.getName());
delegateTask.setAssignee("孙露通");
}
}
根据 任务监听器的内容就是做了一个打印然后,把任务assignee 给了"孙露通"这个人,然后我们看下现在流程的进度:
我们再看一下此时task3的一些基本信息:
我们可以看到此时的task3的assignee 是:“孙露通”,
也就是说:我们在complete task2之后,流程会创建task3,此时触发了 create事件,又因为我们在task3上面定义了事件监听器TaskAssigneeListener
然后会触发任务监听器的 notify(DelegateTask delegateTask)
函数,然后将task3 assignee给了"孙露通"。
从上面的案例我们可以清楚的看到我们在给usertask设置assignee的时候我们用了三种方式:
综上来看:建议使用任务监听器这种方式,因为相对比较灵活,自控能力比较强,同时可以通过这个接入自己业务的用户管理权限组