固定分配就是我们前面介绍的,在绘制流程图或者直接在流程文件中通过 Assignee 来指定的方式。
Flowable 使用 UEL 进行表达式解析。UEL代表Unified Expression Language,是EE6规范的一部分.Flowable支持两种UEL表达式: UEL-value 和 UEL-method。
值表达式 Value expression:解析为一个值。默认情况下,所有流程变量都可以使用。(若使用Spring)所有的Spring bean也可以用在表达式里。例如
可以看到通过表达式处理的效果。
先部署流程,然后在启动流程实例的时候绑定表达式对应的值。
/**
* 启动流程实例
*/
@Test
public void testRunProcess(){
// 设置 assignee 的取值
Map<String,Object> variables = new HashMap<>();
variables.put("assignee0","张三") ;
variables.put("assignee1","李四");
// 启动流程实例,第一个参数是流程定义的id
ProcessInstance processInstance = runtimeService
.startProcessInstanceById("MyHolidayUI:1:4", variables);// 启动流程实例
// 输出相关的流程实例信息
System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例的ID:" + processInstance.getId());
System.out.println("当前活动的ID:" + processInstance.getActivityId());
}
在流程变量表中我们可以看到对应的流程变量信息。
同时在 Task 表中,可以看到流程当前的分配人是 张三
,说明 UEL 表达式被解析了。
方法表达式 Method expression: 调用一个方法,可以带或不带参数。当调用不带参数的方法时,要确保在方法名后添加空括号(以避免与值表达式混淆)。传递的参数可以是字面值(literal value),也可以是表达式,它们会被自动解析。例如:
${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.doSomething(myVar, execution)}
myBean 是 Spring 容器中的个 Bean 对象,表示调用的是 bean 的 addNewOrder 方法。
可以使用监听器来完成很多 Flowable 的流程业务。我们在此处使用监听器来完成负责人的指定,那么我们在流程设计的时候就不需要指定 assignee。
①创建自定义监听器:
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
System.out.println("MyTaskListener 监听器被触发了!");
if("提交请假流程".equals(delegateTask.getName()) && "create".equals(delegateTask.getEventName())) {
delegateTask.setAssignee("小明");
}else {
delegateTask.setAssignee("王经理");
}
}
}
②然后在 FlowableUI 中关联对应的监听器。
在 Task 表中我们可以看到对应的分配人为 小明
说明通过监听也完成了任务分配的工作了。
回到目录…
流程实例按步骤执行时,需要使用一些数据。在Flowable中,这些数据称作变量(variable),并会存储在数据库中。变量可以用在表达式中(例如在排他网关中用于选择正确的出口路径),也可以在Java服务任务(service task)中用于调用外部服务(例如为服务调用提供输入或结果存储)等等。
流程实例可以持有变量(称作流程变量 process variables);用户任务以及执行(executions)——流程当前活动节点的指针——也可以持有变量。流程实例可以持有任意数量的变量,每个变量存储为ACT_RU_VARIABLE数据库表的一行。
所有的 startProcessInstanceXXX 方法都有一个可选参数,用于在流程实例创建及启动时设置变量。例如,在 RuntimeService 中:
ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);
也可以在流程执行中加入变量。例如,(RuntimeService):
void setVariable(String executionId, String variableName, Object value);
void setVariableLocal(String executionId, String variableName, Object value);
void setVariables(String executionId, Map<String, ? extends Object> variables);
void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为 global 变量。
注意:如: Global变量:userId(变量名)、zhangsan(变量值)
global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。
Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。
当部门经理进行审批任务时,他需要设置流程变量为下一步流程指明方向。
①部署流程。
@Test
public void deploy(){
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("evectionUI.bpmn20.xml")
.name("20230904出差申请")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println("deploy.getName() = " + deploy.getName());
System.out.println("deploy.getCategory() = " + deploy.getCategory());
}
②启动流程实例:并且指定全局流程变量。
/**
* 在启动流程实例的时候设置流程变量
*/
@Test
public void runProcess(){
// 设置流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("worker", "张三");
variables.put("manager1", "张经理");
variables.put("manager2", "王总经理");
variables.put("manager3", "孙钱务");
// 启动流程实例,第一个参数是流程定义的id
ProcessInstance processInstance = runtimeService
.startProcessInstanceById("evection:1:4",variables);// 启动流程实例
// 输出相关的流程实例信息
System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例的ID:" + processInstance.getId());
System.out.println("当前活动的ID:" + processInstance.getActivityId());
}
③完成 Task 任务,同时也可以指定流程变量。
/**
* 完成任务时指定流程变量
*/
@Test
public void completeTask(){
Task task = taskService.createTaskQuery()
.processDefinitionId("evection:1:4")
.taskAssignee("李四")
.singleResult();
// 添加流程变量
Map<String, Object> map = task.getProcessVariables();
map.put("num",4);
// 完成任务
taskService.complete(task.getId(),map);
}
当然,我们也可以在处理流程之外通过 Task 编号来修改流程变量。
/**
* 通过当前任务设置
*/
@Test
public void currentTask(){
Task task = taskService.createTaskQuery()
.processDefinitionId("evection:1:4")
.taskAssignee("王五")
.singleResult();
// 添加流程变量
Map<String, Object> map = task.getProcessVariables();
map.put("num",1);
// 一次设置多个值 设置局部变量
taskService.setVariables(task.getId(), map);
}
回到目录…
在流程定义中在任务结点的 assignee 固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。针对这种情况可以给任务设置多个候选人或者候选人组,可以从候选人中选择参与者来完成任务。
// 部署流程
@Test
public void deploy(){
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("Test3.bpmn20.xml")
.name("请求流程-候选人")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println(deploy.getName());
}
启动流程实例,并为候选人赋值。
@Test
public void runProcess(){
// 给流程定义中的UEL表达式赋值
Map<String,Object> variables = new HashMap<>();
variables.put("manager1","张经理");
variables.put("manager2","李经理");
variables.put("manager3","王经理");
runtimeService.startProcessInstanceById("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b", variables);
}
act_ru_variable 表中有了三个候选人的变量信息:
根据候选人查询可拾取的任务。
@Test
void queryTaskCandidate() {
List<Task> tasks = taskService.createTaskQuery()
.processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
.taskCandidateUser("李经理")
.list();
for(Task task : tasks) {
System.out.println("task.getId() = " + task.getId());
System.out.println("task.getName() = " + task.getName());
}
}
act_ru_task 表中的任务执行人还是 null,但候选人是可以查出来该任务的。
候选人知道有可拾取的任务后,拾取任务。一个候选人拾取任务后,其他候选人就不能再拾取该任务了。
@Test
void claimTaskCandidate() {
Task task = taskService.createTaskQuery()
.processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
.taskCandidateUser("李经理")
.singleResult();
if(task != null) {
taskService.claim(task.getId(), "李经理");
}
}
拾取后,act_ru_task 表中的任务执行人也显示出来了。
@Test
void unclaimTaskCandidate() {
Task task = taskService.createTaskQuery()
.processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
.taskAssignee("李经理")
.singleResult();
if(task != null) {
taskService.unclaim(task.getId());
}
}
退还后,act_ru_task 表中的任务执行人又变为 null。
如果我获取了任务,但是不想执行,那么我可以把这个任务交接给其他的用户。
@Test
void taskCandidate(){
// 李经理 --> 王经理
Task task = taskService.createTaskQuery()
.processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
.taskAssignee("李经理")
.singleResult();
if(task != null) {
taskService.setAssignee(task.getId(), "王经理");
}
}
交接前,act_ru_task 表中的任务执行人为 “李经理”。
交接后,act_ru_task 表中的任务执行人为 “王经理”。
由被指派的任务执行人 assignee 完成任务。
@Test
void complete() {
Task task = taskService.createTaskQuery()
.processDefinitionId("test3:1:40e8b336-4c84-11ee-8f39-200db0c7aa5b")
.taskAssignee("王经理")
.singleResult();
if(task != null) {
taskService.complete(task.getId());
}
}
回到目录…
①创建用户
@Test
void createUser() {
User user = identityService.newUser("诸葛亮");
user.setFirstName("亮");
user.setLastName("孔明");
user.setEmail("[email protected]");
user.setPassword("123456");
identityService.saveUser(user);
}
②创建组
@Test
void createGroup() {
Group group = identityService.newGroup("group2");
group.setName("生产部");
group.setType("type2");
identityService.saveGroup(group);
}
③将用户分配给组
@Test
void userGroup() {
Group group = identityService.createGroupQuery()
.groupId("group2")
.singleResult();
User user = identityService.createUserQuery()
.userId("诸葛亮")
.singleResult();
identityService.createMembership(user.getId(), group.getId());
}
在 act_id_membership 表中可以看到用户和组的关系了。
部署流程定义:
@Test
public void deploy(){
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("Test4-候选人组.bpmn20.xml")
.name("请求流程-候选人组")
.deploy();
System.out.println("deploy.getId() = " + deploy.getId());
System.out.println(deploy.getName());
}
启动流程实例:需要将候选组ID添加到运行时变量表中。
@Test
void runProcess(){
List<Group> groups = identityService.createGroupQuery().list();
Group group1 = groups.get(0);
Group group2 = groups.get(1);
Map<String,Object> variables = new HashMap<>();
variables.put("g1", group1.getId());
variables.put("g2", group2.getId());
runtimeService.startProcessInstanceById("test4:1:44ae7634-4bb2-11ee-9fc5-200db0c7aa5b", variables);
}
任务的查询和拾取:根据登录的用户查询对应的组,再根据组查询可以拾取的任务。
@Test
void queryTaskCandidateGroup() {
String userId = "赵云";
Group group = identityService.createGroupQuery().groupMember(userId).singleResult();
Task task = taskService.createTaskQuery()
.processDefinitionId("test4:1:44ae7634-4bb2-11ee-9fc5-200db0c7aa5b")
.taskCandidateGroup(group.getId())
.singleResult();
if(task != null) {
taskService.claim(task.getId(), userId);
}
}
完成任务。
@Test
void complete() {
Task task = taskService.createTaskQuery()
.processDefinitionId("test4:1:44ae7634-4bb2-11ee-9fc5-200db0c7aa5b")
.taskAssignee("赵云")
.singleResult();
if(task != null) {
taskService.complete(task.getId());
}
}
回到目录…
总结:
提示:这里对文章进行总结:
本文是对flowable的进阶学习,学习了流程中任务分配的方式,候选人和候选组拾取任务,以及四种网关的使用。之后的学习内容将持续更新!!!