BPMN task节点
节点介绍
task节点是和业务最贴切的节点,表示一次审批
除了id,name,inComingFlows,outGoingFlows这些属性以外,还有一些权限信息.用于区分一个任务哪些人可以审核的
flowable自带一个ui配置界面,很多的数据库表设计都是和ui界面挂钩的,所以数据库会有很多我们不是很理解的字段,官方文档说明
public class UserTask extends Task {
protected String assignee;
protected String owner;
protected String priority;
protected String formKey;
protected String dueDate;
protected String businessCalendarName;
protected String category;
protected String extensionId;
protected List<String> candidateUsers = new ArrayList<>();
protected List<String> candidateGroups = new ArrayList<>();
protected List<FormProperty> formProperties = new ArrayList<>();
protected List<FlowableListener> taskListeners = new ArrayList<>();
protected String skipExpression;
}
其中有很多属性,form表单,候选组候选人,待办人等等,非常多的概念
实际项目中,我们肯定是走自己的权限系统,不会走flowable里的权限系统,所以,我们在配置BPMN的时候,可以使用一些取巧的方式.
flowable的这么多属性最终会保存到数据库中的,我们只需要将我们的业务id存储进来,然后不同的业务员通过自己的员工id,能够查询到待审批的流程即可.可以不用关心这些属性值在flowable究竟是什么意义,对我们而言就是一个塞业务id的一个列而已.
- owner 表示流程的拥有者
- candidateGroups 候选组, 当一个userTask节点同时存在owner和candidateGroups的时候,owner会变为空
- assignee 表示任务受让人,当我们不指定的时候为空
- skipExpression 表示跳过表达式,如果多级审批人都是同一个人,可以考虑跳过同一个人的下面几个审批,解决烦人的多次审核问题
借助owner和candidateGroups这两点,我们基本上可以完成我们审批人权限数据的填入了.
example
前端先获取机构人员id的下拉框,然后塞进流程引擎的candidateGroups里面,传给后端
查询待办的时候,通过当前系统登录人员工id来查询即可
- 如果业务中,我们的一个节点只能是一个人审核,那么我们直接给owner赋值一个业务id即可
- 如果可以是多个人进行审核,那么我们就把多个id权限字段存储到一个List’<‘String’>'里,然后传入candidateGroups里
- 如果一个字段不够用,可以用 : 进行字符串拼接 , 比如 {公司id}:{部门id}:{员工id}
- 如果我们的流程需要让另一个小哥进行审核,比如原员工跑路了,那么我们可以把这个受让人属性设置为新的审批人权限id
伪代码如下
List<String> auditors = new ArrayList<>();
//有代理人,代理人就是审批人
if(userTask.getAssignee() != null){
auditors.add(userTask.getAssignee());
}
//没有代理人,但是候选组不为空,所有候选组的人都是待审核的人
if(CollectionUtils.isNotEmpty(userTask.getCandidateGroups()){
auditors.addAll(userTask.getCandidateGroups());
}
else{
//审批人只有一个人
auditors.add(userTask.getOwner());
}
还有一种方式
- 如果觉得这样改flowable数据库表的用法不太好,我们可以跳出flowable,自己先创建一个权限关系表,绑定好flowable的流程,先通过权限关系表拿到这个人可以审核的流程的一些信息,再结合这些进行去查询流程,查询到的待审批的任务就是我们的当前登录人可以审批的任务节点了
- 流程引擎的这些权限主要还是针对于查询,实际上我们调用推动流程的方法时,流程引擎并不会去校验权限,只要有taskId,processsId调用推动流程API即可,所以对于非Sass系统,非权限系统,仅限于一个简单的流程配置给一个系统使用,userTask的这些owner,assignee,candidateGroups这些属性都可以为空.那么我们查询流程都通过流程id来查询即可
介绍完userTask的一些属性,我们结合例子来说明一下,如何在实际项目开发中使用到这些属性
BPMN配置userTask节点
这里用官方的绘图工具,下载以及启动可以参考这一篇博客链接
先绘制一个最简单的流程
给流程设置审批组
身份存储需要先从flowable的权限系统注册一个审批人信息,太重了.
我们这边直接使用固定值的形式,添加两个候选人名字的字符串
添加好了保存,然后导出bpmn20.xml文件,该编辑器详细界面参考这篇博文链接
得到bpmn图,这里id和name属性都会自动生成
<?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: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="key" name="demo" isExecutable="true">
<documentation>demo</documentation>
<!-- 开始节点 -->
<startEvent id="startEvent1" flowable:initiator="sponsor" flowable:formFieldValidation="true"></startEvent>
<!-- 第一个审核节点 有jakt和jack两个候选人 -->
<userTask id="sid-AD4D3CCF-4705-4F03-A381-269C354AB992" flowable:candidateGroups="jakt,jack" flowable:formFieldValidation="true"></userTask>
<userTask id="sid-3FBBAF8C-F882-49B8-9723-3A3D163B6E8B" flowable:formFieldValidation="true"></userTask>
<endEvent id="sid-F95597D2-838C-46BD-95D2-4CD0C7B41A17"></endEvent>
<sequenceFlow id="sid-1D4DE32E-2FE6-4E4B-A95F-82265E6893B1" sourceRef="startEvent1" targetRef="sid-AD4D3CCF-4705-4F03-A381-269C354AB992"></sequenceFlow>
<sequenceFlow id="sid-5FF980E1-87E8-4F72-A5B6-6D549367C797" sourceRef="sid-3FBBAF8C-F882-49B8-9723-3A3D163B6E8B" targetRef="sid-F95597D2-838C-46BD-95D2-4CD0C7B41A17" skipExpression="{outcome}=='通过'"></sequenceFlow>
<sequenceFlow id="sid-295254FA-B051-423E-B3EE-091421844E7B" sourceRef="sid-AD4D3CCF-4705-4F03-A381-269C354AB992" targetRef="sid-3FBBAF8C-F882-49B8-9723-3A3D163B6E8B" skipExpression="{outcome}=='通过'"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_key">
<bpmndi:BPMNPlane bpmnElement="key" id="BPMNPlane_key">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="90.0" y="175.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-AD4D3CCF-4705-4F03-A381-269C354AB992" id="BPMNShape_sid-AD4D3CCF-4705-4F03-A381-269C354AB992">
<omgdc:Bounds height="80.0" width="100.0" x="300.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-3FBBAF8C-F882-49B8-9723-3A3D163B6E8B" id="BPMNShape_sid-3FBBAF8C-F882-49B8-9723-3A3D163B6E8B">
<omgdc:Bounds height="80.0" width="100.0" x="603.5" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-F95597D2-838C-46BD-95D2-4CD0C7B41A17" id="BPMNShape_sid-F95597D2-838C-46BD-95D2-4CD0C7B41A17">
<omgdc:Bounds height="28.0" width="28.0" x="915.0" y="176.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-295254FA-B051-423E-B3EE-091421844E7B" id="BPMNEdge_sid-295254FA-B051-423E-B3EE-091421844E7B">
<omgdi:waypoint x="399.95000000000005" y="190.0"></omgdi:waypoint>
<omgdi:waypoint x="603.4999999998836" y="190.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-1D4DE32E-2FE6-4E4B-A95F-82265E6893B1" id="BPMNEdge_sid-1D4DE32E-2FE6-4E4B-A95F-82265E6893B1">
<omgdi:waypoint x="119.94999969544602" y="190.0"></omgdi:waypoint>
<omgdi:waypoint x="300.0" y="190.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-5FF980E1-87E8-4F72-A5B6-6D549367C797" id="BPMNEdge_sid-5FF980E1-87E8-4F72-A5B6-6D549367C797">
<omgdi:waypoint x="703.4499999999911" y="190.0"></omgdi:waypoint>
<omgdi:waypoint x="915.0" y="190.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
我们将BPMN文件导入项目,然后再导出看看
@CrossOrigin(origins = "*", maxAge = 3600)
@GetMapping("/loading")
public ResultData<String> loading() throws FileNotFoundException {
//先部署流程模板
Deployment deployment = repositoryService
.createDeployment()
.tenantId(companyName)
.addInputStream("demo.bpmn20.xml", new FileInputStream(ResourceUtils.getFile("classpath:demo.bpmn20.xml")))
.deploy();
//再查看流程对象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("key")
.processDefinitionTenantId(companyName)
.singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
System.out.println(bpmnModel);
return ResultData.ofSuccess();
}
这里我偷懒了,直接打断点了,可以看到我们配置的信息都生效了
bpmnModel ==> processes ==> flowElementList ==> userTask ==> candidateGroups=> {‘jack’,‘jakt’}
我们现在开启这个流程,然后再查询流程进度
@CrossOrigin(origins = "*", maxAge = 3600)
@GetMapping("/createTaskByKey")
public ResultData<String> createTaskByKey(@RequestParam String id) {
//设置发起人
Authentication.setAuthenticatedUserId("jakt");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(key, companyName);
ResultData<String> stringResultData = ResultData.ofSuccess(processInstance.getId());
Authentication.setAuthenticatedUserId(null);
return stringResultData;
}
先不设置查询条件,直接全表查询
@CrossOrigin(origins = "*", maxAge = 3600)
@GetMapping("/queryAllTask")
public ResultData<String> queryAllTask(String id) {
//全查再说
System.out.println(taskService.createTaskQuery().list());
return ResultData.ofSuccess();
}
我们目前有一个任务task,然后owner是空的,因为我们确实没设置
接下里我们加个查询条件
jakt是属于候选人(jakt,jack)中的,我们加一个条件试试,确实拿到了
我换一个查询条件,嗯没有数据了.
通过这candidateGroups字段,我们就可以实现最简单的数据权限了
总结
- 设置一个task节点的数据权限,然后我们结合我们业务系统的权限id来查询流程就可以实现审批人权限控制了
- flowable的userTask对象的很多属性都是适配于其自带的模型的一些功能而定制的如果我们只是用flowable的后台API接口的话,可以稍加修改就可以适配我们现有的代码