任务分配是工作流应用的核心,直接决定了“工作”如何流转到“正确的人”手中。Flowable 提供了非常灵活且强大的任务分配机制,下面我将为您进行详细的解释说明。
在 Flowable 中,任务的分配策略是在设计BPMN流程图时,通过设置用户任务 (User Task) 的属性来定义的。
主要有三种方式来指定一个任务应该由谁来处理,它们分别是:直接办理人 (Assignee)、候选用户 (Candidate Users) 和 候选组 (Candidate Groups)。
1.直接办理人 (Assignee)
概念:最简单的分配方式。直接将任务指派给一个唯一确定的用户。一旦设置了 Assignee,这个任务就有了明确的“主人”,只有这个用户能看到并处理它。
BPMN 属性:flowable:assignee
适用场景:
任务的处理人非常明确,没有第二个人可以做(例如,“由申请人本人确认信息”)。
上一个节点已经确定了下一个节点的处理人(例如,流程中有一个步骤是“选择复审专家”,选定后,下一个任务就直接分配给这位专家)。
如何配置:
静态分配:直接在属性面板中填写用户的ID。
<userTask id="confirmInfoTask" name="确认信息" flowable:assignee="zhangsan" />
这个任务将永远分配给ID为 zhangsan 的用户。
动态分配(更常用):使用EL表达式,从流程变量中获取用户ID。
<userTask id="confirmInfoTask" name="确认信息" flowable:assignee="${initiator}" />
这里的 ${initiator} 是一个流程变量,它代表流程发起人的用户ID。这样,谁发起的流程,这个任务就自动分配给谁。
2. 候选组 (Candidate Groups)
概念:将任务分配给一个或多个角色/部门/团队(即用户组)。该组内的所有成员都能看到这个任务,但任务处于“待认领”状态。任何人都可以认领 (Claim) 它,一旦有人认领,任务就从“公共任务池”进入这个人的“个人待办列表”,其他人便无法再处理。
BPMN 属性:flowable:candidateGroups
适用场景:
基于角色的工作分配(例如,“财务部审批”、“IT部支持”)。
需要多人协作,谁有空谁来处理的场景,以提高效率。这是企业应用中最常用的分配方式。
如何配置:
静态分配:填写一个或多个固定的用户组ID,用逗号分隔。
<userTask id="financialApprovalTask" name="财务审批" flowable:candidateGroups="finance,senior_finance" />
finance 和 senior_finance 这两个组的所有成员都能看到此任务。
动态分配:同样可以使用EL表达式。
<userTask id="deptApprovalTask" name="部门审批" flowable:candidateGroups="${deptManagerGroup}" />
deptManagerGroup 可以是一个流程变量,其值可能是 ‘sales_managers’ 或 ‘tech_managers’,具体取决于申请人所在的部门。
3. 候选用户 (Candidate Users)
概念:与候选组类似,但它直接指定一组具体的用户作为候选人,而不是一个角色组。这组用户都能看到任务,同样需要认领后才能处理。
BPMN 属性:flowable:candidateUsers
适用场景:
处理人是几个特定的人,但又不方便为他们专门创建一个组时。
处理人选是在流程上一个节点动态决定的一个小组。
如何配置:
静态分配:填写一个或多个用户ID,用逗 Всего分隔。
<userTask id="expertReviewTask" name="专家评审" flowable:candidateUsers="expertA,expertB,expertC" />
动态分配:EL表达式的值可以是一个用户ID集合或逗号分隔的字符串。
<userTask id="peerReviewTask" name="同事互审" flowable:candidateUsers="${peerReviewerList}" />
peerReviewerList 是一个流程变量,其值为 List 或 “user1,user2”。
核心操作:认领 (Claim) 与任务查询
当任务分配给“候选人”或“候选组”时,认领 (Claim) 这个动作变得至关重要。
查询待办任务:
查询我的个人任务 (已直接分配给我或我已认领的):
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("zhangsan")
.list();
查询我所在组的公共任务 (我可以认领的):
// 假设'zhangsan'属于'managers'组
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateGroup("managers")
.list();
查询我的所有待办 (个人任务 + 可认领的公共任务):这才是用户“待办中心”的完整查询。
List<String> userGroups = myIdentityService.getUserGroups("zhangsan"); // 先获取用户所属的组
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateOrAssigned("zhangsan", userGroups) // Flowable 7.x 提供了便捷方法
.list();
执行认领:
当用户决定处理一个公共任务时,他必须先认领它。
// 'managerA' 决定处理这个任务
taskService.claim(taskId, "managerA");
认领后,该任务的 ASSIGNEE_ 字段在数据库中会被设置为 managerA,其他经理在查询公共任务时就看不到它了。
优先使用 Candidate Groups:在企业应用中,基于角色的权限管理和任务分配是最常见、最灵活且最易于维护的方式。人员变动时,只需调整用户与组的关系,无需修改流程定义。
结合动态分配:充分利用EL表达式 (${…}) 和流程变量,让任务分配逻辑能根据流程数据动态变化,以适应复杂的业务场景。
清晰的UI设计:在前端界面上,要明确区分“我的个人待办”和“我们组的待办(公共任务池)”,并提供清晰的“认领”按钮。
方法表达式
使用方法表达式 (Method Expression) 是实现动态任务分配的一种非常强大和灵活的方式,它允许你调用一个Java Bean(通常是Spring Bean)的方法来动态计算出办理人。
这比简单的变量表达式 ${variableName} 更强大,因为你可以在Java方法中执行复杂的业务逻辑,比如查询数据库、调用外部API等。
方法表达式的语法格式是:
${beanName.methodName(arguments)}
#有三种写法
${printer.print()} #无参
${myBean.addNewOrder('orderName')} #直接方法加入常量
${myBean.doSomething(myVar, execution)} #方法加入变量
${…}: 表示这是一个表达式。
beanName: 是你在Spring容器中定义的Bean的ID。
methodName: 是该Bean中的一个公开方法。
arguments: 是传递给该方法的参数。Flowable在执行时会自动提供一些有用的内置对象作为参数,最常用的就是 execution
现在有个问题就是,我现在使用候选组的时候,从那里获取小组信息,小组里面的用户有谁?怎么添加目前都不知道,这就牵扯到Identity 的问题了,该内容我们下章节细说。