文章

【若依】26、流程任务

1 ReceiveTask(接收任务)

任务到达这个节点之后,一般来说,不需要额外做什么事情,但需要用户手动的触发。 image.png 带信封图标的任务,就是一个 ReceiveTask,不同于 UserTask,ReceiveTask 是不需要分配人员的。 绘制流程图,部署,启动 启动流程

1
2
3
4
5
6
7
8
/**
 * 启动流程
 */
@Test
public void startProcessInstanceByKey() {
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("ReceiveTaskDemo");
    log.info(")_(" + "id:{}", processInstance.getId());
}

流程启动成功后,会停在 统计今日销售额 节点上,触发,进入 发送报表 节点,再触发,结束流程;

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 接收任务触发
 */
@Test
public void trigger() {
    String[] activityIds = new String[]{"sid-8E756DDA-8849-49A4-8755-195EA0F9182D", "sid-BA450F64-6F60-47CB-9CA0-9A59025DEDE5"};
    String activityId = activityIds[1];

    List<Execution> list = runtimeService.createExecutionQuery().activityId(activityId).list();
    for (Execution execution : list) {
        runtimeService.trigger(execution.getId());
    }
}

注意:ReceiveTask 是不会被记录在ACT_RU_TASK表中的,它默认被记录在ACT_RU_EXECUTION表和ACT_RU_ACTINST表中。

2 UserTask(用户任务)

Flowable 中使用最多的一种任务类型,流程走到这个节点时,需要用户手动处理。

2.1 设置单个用户

指定 UserTask 的处理人,有四种方式

1. 指定具体用户

image.png image.png 启动流程

1
2
3
4
5
6
7
8
9
10
11
/**
 * 启动流程
 * act_ru_task
 * act_ru_actinst
 * act_ru_execution
 */
@Test
public void startProcessInstanceByKey1() {
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UserTaskDemo");
    log.info(")_(" + "id:{},name:{}", processInstance.getId(), processInstance.getName());
}

流程启动成功后,会自动进入用户审批这个节点,需要用户处理的 UserTask 都保存在ACT_RU_TASK表中 image.png 查询某一个用户需要处理的 Task

1
2
3
4
5
6
7
8
9
10
11
/**
 * 查询某一个用户需要处理的任务
 * act_ru_task
 */
@Test
public void queryTaskByAssignee() {
    List<Task> list = taskService.createTaskQuery().taskAssignee("yueyazhui").list();
    for (Task task : list) {
        log.info(")_(" + "name:{},assignee:{}", task.getName(), task.getAssignee());
    }
}

查询到用户需要处理的任务后,有两种处理思路

  1. 委派给其他人处理
  2. 自己处理
    1.1 委派给其他人处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    /**
     * 将 yueyazhui 需要处理的任务委派给 zhangyanpeng 去处理
     * 表:ACT_RU_TASK
     * 字段:ASSIGNEE_
     */
    @Test
    public void setAssignee() {
     List<Task> list = taskService.createTaskQuery().taskAssignee("yueyazhui").list();
     for (Task task : list) {
         // 为某一个 Task 设置处理人
         taskService.setAssignee(task.getId(), "zhangyanpeng");
     }
    }
    
    1.2 自己处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    /**
     * 自己处理
     */
    @Test
    public void complete() {
     List<Task> list = taskService.createTaskQuery().taskAssignee("yueyazhui").list();
     for (Task task : list) {
         taskService.complete(task.getId());
     }
    }
    

    2. 通过变量来设置

    image.png 在设置任务的处理人时,使用变量,${xxx} XML 内容

1
2
3
4
5
6
7
8
9
10
11
12
<process id="UserTaskDemo" name="UserTaskDemo" isExecutable="true">
  <documentation>UserTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <userTask id="sid-A8A30666-E378-4A28-96AD-E770166891FF" name="用户审批" flowable:assignee="${handler}" flowable:formFieldValidation="true">
    <extensionElements>
      <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
    </extensionElements>
  </userTask>
  <sequenceFlow id="sid-0EB3CCF3-ECB6-492D-A7A7-B0D890A9C00B" sourceRef="startEvent1" targetRef="sid-A8A30666-E378-4A28-96AD-E770166891FF"></sequenceFlow>
  <endEvent id="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></endEvent>
  <sequenceFlow id="sid-1CC2FA64-563D-4867-8C7B-6FA3DA6A5A79" sourceRef="sid-A8A30666-E378-4A28-96AD-E770166891FF" targetRef="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></sequenceFlow>
</process>

启动流程并指定任务的处理人

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 启动流程并指定任务的处理人
 * act_ru_task
 * act_ru_actinst
 * act_ru_execution
 */
@Test
public void startProcessInstanceByKey2() {
    Map<String, Object> map = new HashMap<>();
    map.put("manager", "yueyazhui");
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UserTaskDemo", map);
    log.info(")_(" + "id:{},name:{}", processInstance.getId(), processInstance.getName());
}

启动成功后,在ACT_RU_TASK表中,就可以看到任务的处理人已经是 yueyazhui 了 image.png

3. 通过监听器来设置

利用监听器,在一个任务创建的时候,为任务设置处理人 image.png image.png XML 内容

1
2
3
4
5
6
7
8
9
10
11
12
<process id="UserTaskDemo" name="UserTaskDemo" isExecutable="true">
  <documentation>UserTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <userTask id="sid-A8A30666-E378-4A28-96AD-E770166891FF" name="用户审批" flowable:formFieldValidation="true">
    <extensionElements>
      <flowable:taskListener event="create" class="top.yueyazhui.flowable_process.listener.MyTaskListener"></flowable:taskListener>
    </extensionElements>
  </userTask>
  <sequenceFlow id="sid-0EB3CCF3-ECB6-492D-A7A7-B0D890A9C00B" sourceRef="startEvent1" targetRef="sid-A8A30666-E378-4A28-96AD-E770166891FF"></sequenceFlow>
  <endEvent id="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></endEvent>
  <sequenceFlow id="sid-1CC2FA64-563D-4867-8C7B-6FA3DA6A5A79" sourceRef="sid-A8A30666-E378-4A28-96AD-E770166891FF" targetRef="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></sequenceFlow>
</process>

对应的监听器代码

1
2
3
4
5
6
7
8
public class MyTaskListener implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        // 设置任务的处理人
        delegateTask.setAssignee("yueyazhui");
    }
}

4. 设置为流程的发起人

在开始节点上,设置流程的发起人 image.png 给 UserTask 设置处理人时,设置变量 image.png image.png 流程启动并指定流程的发起人

1
2
3
4
5
6
7
8
9
10
/**
 * 启动流程并设置流程的发起人
 */
@Test
public void startProcessInstanceByKey3() {
    // 设置流程的发起人
    Authentication.setAuthenticatedUserId("yueyazhui");
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UserTaskDemo");
    log.info(")_(" + "id:{},name:{}", processInstance.getId(), processInstance.getName());
}

1
2
3
4
5
6
7
8
9
10
11
12
@Autowired
IdentityService identityService;

/**
 * 启动流程并设置流程的发起人
 */
@Test
public void startProcessInstanceByKey4() {
    identityService.setAuthenticatedUserId("yueyazhui");
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UserTaskDemo");
    log.info(")_(" + "id:{},name:{}", processInstance.getId(), processInstance.getName());
}

2.2 设置多个用户

1. 直接指定

直接指定多个候选用户 image.png image.png XML 内容

1
2
3
4
5
6
7
8
<process id="UserTaskDemo" name="UserTaskDemo" isExecutable="true">
  <documentation>UserTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <userTask id="sid-A8A30666-E378-4A28-96AD-E770166891FF" name="用户审批" flowable:candidateUsers="yueyazhui,zhangyanpeng" flowable:formFieldValidation="true"></userTask>
  <sequenceFlow id="sid-0EB3CCF3-ECB6-492D-A7A7-B0D890A9C00B" sourceRef="startEvent1" targetRef="sid-A8A30666-E378-4A28-96AD-E770166891FF"></sequenceFlow>
  <endEvent id="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></endEvent>
  <sequenceFlow id="sid-1CC2FA64-563D-4867-8C7B-6FA3DA6A5A79" sourceRef="sid-A8A30666-E378-4A28-96AD-E770166891FF" targetRef="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></sequenceFlow>
</process>

flowable:candidateUsers="yueyazhui,zhangyanpeng"就是设置流程可以由多个用户来处理,多个用户之间使用,隔开 部署并启动流程 这时查询某一个用户需要处理的任务,就不能使用之前的方案,因为当一个 UserTask 有多个用户可以处理时,那么在ACT_RU_TASK这个表中,是无法完全记录这个 Task 的处理人的,所以此时该表中的ASSIGNEE_字段就为 NULL 根据用户名去查询该用户能够处理的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 根据候选用户查询任务
 * ACT_RU_TASK
 * ACT_RU_IDENTITYLINK
 * SQL:SELECT RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? ) ) order by RES.ID_ asc
 */
@Test
public void queryTaskByCandidateUser() {
    List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("yueyazhui").list();
    for (Task task : tasks) {
        log.info(")_(" + "name:{}", task.getName());
    }
}

已知流程信息,查询流程的参与人

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 根据流程 ID 查询流程的参与者
 * ACT_RU_IDENTITYLINK
 */
@Test
public void queryProcessInstanceIdentityLinks() {
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
    // 根据流程 ID 查询流程的参与者
    List<IdentityLink> links = runtimeService.getIdentityLinksForProcessInstance(processInstance.getId());
    for (IdentityLink link : links) {
        log.info(")_(" + "流程参与者:{}", link.getUserId());
    }
}

从以上两个查询中可以看出,ACT_RU_IDENTITYLINK表的功能

  • 记录任务的候选人
  • 记录流程的参与人

任务认领:

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 认领任务
 * 表:ACT_RU_TASK
 * 字段:ASSIGNEE_
 */
@Test
public void claimTask() {
    List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("yueyazhui").list();
    for (Task task : tasks) {
        taskService.claim(task.getId(), "yueyazhui");
    }
}

2. 使用变量或监听器

变量

image.png 启动流程并指定候选人

1
2
3
4
5
6
7
8
9
/**
 * 启动流程并指定候选人
 */
@Test
public void startProcessInstanceByKey5() {
    Map<String, Object> map = new HashMap<>();
    map.put("candidateUsers", "yueyazhui,zhangyanpeng");
    runtimeService.startProcessInstanceByKey("UserTaskDemo", map);
}
监听器

image.png image.png

1
2
3
4
5
6
7
8
9
public class MyTaskCandidateUsersListener implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        // 设置任务的候选人
        delegateTask.addCandidateUser("yueyazhui");
        delegateTask.addCandidateUser("zhangyanpeng");
    }
}

3. 任务回退

已经认领的任务,退回去

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 任务回退
 * 表:ACT_RU_TASK
 * 字段:ASSIGNEE_
 */
@Test
public void setAssignee2() {
    List<Task> tasks = taskService.createTaskQuery().taskAssignee("yueyazhui").list();
    for (Task task : tasks) {
        // 设置任务的处理人为 null,就表示任务回退
        taskService.setAssignee(task.getId(), null);
    }
}

4. 任务候选人的添加与删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 任务增加候选人
 * ACT_RU_IDENTITYLINK
 */
@Test
public void addCandidateUser() {
    Task task = taskService.createTaskQuery().singleResult();
    taskService.addCandidateUser(task.getId(), "yueya");
    taskService.addCandidateUser(task.getId(), "yue");
}

/**
 * 任务删除候选人
 * ACT_RU_IDENTITYLINK
 */
@Test
public void deleteCandidateUser() {
    Task task = taskService.createTaskQuery().singleResult();
    taskService.deleteCandidateUser(task.getId(),"yueya");
    taskService.deleteCandidateUser(task.getId(),"yue");
}

2.3 按照角色分配任务处理人

准备数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 创建 yue 和 yueyazhui 两个用户
 * 创建 manager 的用户组
 * 设置 yue 和 yueyazhui 都属于 manager
 */
@Test
public void createUserAndGroup() {
    UserEntityImpl user1 = new UserEntityImpl();
    user1.setRevision(0);
    user1.setId("yue");
    user1.setPassword("123");
    user1.setDisplayName("月");
    user1.setEmail("yue@yue.com");
    identityService.saveUser(user1);
    UserEntityImpl user2 = new UserEntityImpl();
    user2.setRevision(0);
    user2.setId("yueyazhui");
    user2.setPassword("123");
    user2.setDisplayName("月牙坠");
    user2.setEmail("yueyazhui@yueyazhui.com");
    identityService.saveUser(user2);
    GroupEntityImpl group = new GroupEntityImpl();
    group.setRevision(0);
    group.setId("manager");
    group.setName("经理");
    identityService.saveGroup(group);
    identityService.createMembership("yue", "manager");
    identityService.createMembership("yueyazhui", "manager");
}
直接指定名称

image.png XML 内容

1
2
3
4
5
6
7
8
<process id="UserTaskDemo" name="UserTaskDemo" isExecutable="true">
  <documentation>UserTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <userTask id="sid-A8A30666-E378-4A28-96AD-E770166891FF" name="用户审批" flowable:candidateGroups="manager" flowable:formFieldValidation="true"></userTask>
  <sequenceFlow id="sid-0EB3CCF3-ECB6-492D-A7A7-B0D890A9C00B" sourceRef="startEvent1" targetRef="sid-A8A30666-E378-4A28-96AD-E770166891FF"></sequenceFlow>
  <endEvent id="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></endEvent>
  <sequenceFlow id="sid-1CC2FA64-563D-4867-8C7B-6FA3DA6A5A79" sourceRef="sid-A8A30666-E378-4A28-96AD-E770166891FF" targetRef="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></sequenceFlow>
</process>

flowable:candidateGroups="manager"就是用来指定候选组的 根据候选用户查询任务

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 根据候选人查询任务(可能 yueyazhui 就是候选人,也可能 yueyazhui 属于某一个或某多个用户组,那么此时就需要先查询到 yueyazhui 所属的用户组,然后再根据用户组去查询对应的任务)
 * 1. 查询 yueyazhui 所属的用户组
 * SELECT RES.* from ACT_ID_GROUP RES WHERE exists(select 1 from ACT_ID_MEMBERSHIP M where M.GROUP_ID_ = RES.ID_ and M.USER_ID_ = ?) order by RES.ID_ asc
 * 2. 根据用户组去查询对应的任务
 * SELECT RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? or ( LINK.GROUP_ID_ IN ( ? ) ) ) ) order by RES.ID_ asc
 */
@Test
public void queryTaskByCandidateUser2() {
    Task task = taskService.createTaskQuery().taskCandidateUser("yueyazhui").singleResult();
    log.info(")_(" + "name:{}", task.getName());
}

根据候选组查询任务

1
2
3
4
5
6
7
8
9
/**
 * 根据候选用户组查询任务
 * SELECT RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( ( LINK.GROUP_ID_ IN ( ? ) ) ) ) order by RES.ID_ asc
 */
@Test
public void queryTaskByCandidateGroup() {
    Task task = taskService.createTaskQuery().taskCandidateGroup("manager").singleResult();
    log.info(")_(" + "name:{}", task.getName());
}
通过变量来指定

image.png XML 内容

1
2
3
4
5
6
7
8
<process id="UserTaskDemo" name="UserTaskDemo" isExecutable="true">
  <documentation>UserTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <userTask id="sid-A8A30666-E378-4A28-96AD-E770166891FF" name="用户审批" flowable:candidateGroups="${candidateGroups}" flowable:formFieldValidation="true"></userTask>
  <sequenceFlow id="sid-0EB3CCF3-ECB6-492D-A7A7-B0D890A9C00B" sourceRef="startEvent1" targetRef="sid-A8A30666-E378-4A28-96AD-E770166891FF"></sequenceFlow>
  <endEvent id="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></endEvent>
  <sequenceFlow id="sid-1CC2FA64-563D-4867-8C7B-6FA3DA6A5A79" sourceRef="sid-A8A30666-E378-4A28-96AD-E770166891FF" targetRef="sid-188D3FA6-23F8-436C-9F97-7D81433C84E4"></sequenceFlow>
</process>

flowable:candidateGroups="${candidateGroups}"就是用来设置候选组变量的 启动流程并设置用户组变量值

1
2
3
4
5
6
7
8
9
10
/**
 * 启动流程并设置用户组变量值
 */
@Test
public void startProcessInstanceByKey6() {
    Map<String, Object> map = new HashMap<>();
    map.put("candidateGroups", "manager");
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("UserTaskDemo", map);
    log.info(")_(" + "id:{},name:{}", processInstance.getId(), processInstance.getName());
}

3 ServiceTask(服务任务)

服务任务:系统自动执行完成

3.1 监听类

定义监听类

1
2
3
4
5
6
7
8
9
10
/**
 * 自定义监听类,ServiceTask 执行到这里时,会自动执行该类中的 execute 方法
 */
public class MyServiceTask01 implements JavaDelegate {

    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("--------- MyServiceTask ---------");
    }
}

绘制流程图时,为 ServiceTask 配置监听类 image.png XML 内容

1
2
3
4
5
6
7
8
<process id="ServiceTaskDemo" name="ServiceTaskDemo" isExecutable="true">
  <documentation>ServiceTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <sequenceFlow id="sid-3A355604-DF55-4C5D-8249-4EFAE553DCA2" sourceRef="startEvent1" targetRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6"></sequenceFlow>
  <endEvent id="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></endEvent>
  <sequenceFlow id="sid-3A7C3AE6-F12A-4184-87C1-1D2F9DD95744" sourceRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" targetRef="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></sequenceFlow>
  <serviceTask id="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" flowable:class="top.yueyazhui.flowable_process.listener.MyServiceTask01"></serviceTask>
</process>

关键指令:flowable:class="top.yueyazhui.flowable_process.listener.MyServiceTask01" 流程测试

1
2
3
4
5
6
7
8
9
10
11
12
@Slf4j
@SpringBootTest
public class FlowableProcessServiceTaskTest {

    @Autowired
    RuntimeService runtimeService;

    @Test
    public void startProcessInstanceByKey() {
        runtimeService.startProcessInstanceByKey("ServiceTaskDemo");
    }
}

ServiceTask 在执行的过程中,任务的记录是不会保存到ACT_RU_TASK表中的。

设置字段

image.png image.png XML 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<process id="ServiceTaskDemo" name="ServiceTaskDemo" isExecutable="true">
  <documentation>ServiceTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <sequenceFlow id="sid-3A355604-DF55-4C5D-8249-4EFAE553DCA2" sourceRef="startEvent1" targetRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6"></sequenceFlow>
  <endEvent id="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></endEvent>
  <sequenceFlow id="sid-3A7C3AE6-F12A-4184-87C1-1D2F9DD95744" sourceRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" targetRef="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></sequenceFlow>
  <serviceTask id="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" flowable:class="top.yueyazhui.flowable_process.listener.MyServiceTask01">
    <extensionElements>
      <flowable:field name="username">
        <flowable:string><![CDATA[yueyazhui]]></flowable:string>
      </flowable:field>
    </extensionElements>
  </serviceTask>
</process>

接下来,在监听类中,就可以获取到 username 的值了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * 自定义监听类,ServiceTask 执行到这里时,会自动执行该类中的 execute 方法
 */
public class MyServiceTask01 implements JavaDelegate {

    Expression username;

    @Override
    public void execute(DelegateExecution delegateExecution) {
        // 获取 username 的值
        System.out.println("username.getExpressionText() = " + username.getExpressionText());
        System.out.println("username.getValue(delegateExecution) = " + username.getValue(delegateExecution));
        System.out.println("--------- MyServiceTask ---------");
    }
}

3.2 委托表达式

委托表达式类似于监听类,但是,可以注册到 Spring 容器中;绘制流程图时,配置 Bean 的名称即可 监听类

1
2
3
4
5
6
7
8
@Component
public class MyServiceTask02 implements JavaDelegate {

    @Override
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("--------- MyServiceTask ---------");
    }
}

image.png image.png XML 内容

1
2
3
4
5
6
7
8
<process id="ServiceTaskDemo" name="ServiceTaskDemo" isExecutable="true">
  <documentation>ServiceTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <sequenceFlow id="sid-3A355604-DF55-4C5D-8249-4EFAE553DCA2" sourceRef="startEvent1" targetRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6"></sequenceFlow>
  <endEvent id="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></endEvent>
  <sequenceFlow id="sid-3A7C3AE6-F12A-4184-87C1-1D2F9DD95744" sourceRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" targetRef="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></sequenceFlow>
  <serviceTask id="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" flowable:delegateExpression="${myServiceTask02}"></serviceTask>
</process>

flowable:delegateExpression="${myServiceTask02}"描述了 ServiceTask 对应的 Bean

3.3 表达式

前面两种,无论是直接配置类的全路径,还是配置 Bean 的名称,都离不开JavaDelegate接口。 其实一个普通的 Bean,也可以配置为 ServiceTask 的执行类。

1
2
3
4
5
6
7
@Component
public class MyServiceTask03 {

    public void hello() {
        System.out.println("--------- MyServiceTask ---------");
    }
}

image.png 在表达式中,指定要执行的 Bean 以及对应的方法。 image.png XML 内容

1
2
3
4
5
6
7
8
<process id="ServiceTaskDemo" name="ServiceTaskDemo" isExecutable="true">
  <documentation>ServiceTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <sequenceFlow id="sid-3A355604-DF55-4C5D-8249-4EFAE553DCA2" sourceRef="startEvent1" targetRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6"></sequenceFlow>
  <endEvent id="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></endEvent>
  <sequenceFlow id="sid-3A7C3AE6-F12A-4184-87C1-1D2F9DD95744" sourceRef="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" targetRef="sid-EE02C1CB-A1C2-40F2-87F5-6B03279FB102"></sequenceFlow>
  <serviceTask id="sid-15963CD2-01AC-40C6-A254-506BAF753CA6" flowable:expression="${myServiceTask03.hello()}"></serviceTask>
</process>

注意,表达式不可以像监听类似的去设置字段。

4 ScriptTask(脚本任务)

脚本任务和服务任务类似,也是系统自动执行完成的。不同的是,脚本任务的逻辑,是通过一些非 Java 的脚本语言来实现的。

4.1 JavaScript

绘制流程图 image.png image.png

  • 第一行执行一个加法运算。
  • 第二行保存一个流程变量。

XML 内容

1
2
3
4
5
6
7
8
9
10
11
<process id="ScriptTaskDemo" name="ScriptTaskDemo" isExecutable="true">
  <documentation>ScriptTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <sequenceFlow id="sid-781E4B49-2BF6-4E42-B556-8405F970603D" sourceRef="startEvent1" targetRef="sid-F3C21531-8080-421E-8A5D-A92102B8B302"></sequenceFlow>
  <endEvent id="sid-1917820C-9059-4AF1-A5B9-A57AEB08DF99"></endEvent>
  <sequenceFlow id="sid-169C8661-BCA4-4D67-8F14-D45239E6B3CC" sourceRef="sid-F3C21531-8080-421E-8A5D-A92102B8B302" targetRef="sid-1917820C-9059-4AF1-A5B9-A57AEB08DF99"></sequenceFlow>
  <scriptTask id="sid-F3C21531-8080-421E-8A5D-A92102B8B302" scriptFormat="JavaScript" flowable:autoStoreVariables="false">
    <script><![CDATA[var sum = a + b;
execution.setVariable("sum", sum);]]></script>
  </scriptTask>
</process>

注意:不能使用 let 关键字,可以使用 var 关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Slf4j
@SpringBootTest
public class FlowableProcessScriptTaskTest {

    @Autowired
    RuntimeService runtimeService;

    /**
    * act_hi_varinst
    */
    @Test
    public void startProcessInstanceByKey1() {
        Map<String, Object> map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        runtimeService.startProcessInstanceByKey("ScriptTaskDemo", map);
    }
}

4.2 Groovy

Groovy 是基于 JVM 的编程语言 添加依赖

1
2
3
4
5
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>3.0.13</version>
</dependency>

image.png image.png XML 内容

1
2
3
4
5
6
7
8
9
10
<process id="ScriptTaskDemo" name="ScriptTaskDemo" isExecutable="true">
  <documentation>ScriptTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <sequenceFlow id="sid-781E4B49-2BF6-4E42-B556-8405F970603D" sourceRef="startEvent1" targetRef="sid-F3C21531-8080-421E-8A5D-A92102B8B302"></sequenceFlow>
  <endEvent id="sid-1917820C-9059-4AF1-A5B9-A57AEB08DF99"></endEvent>
  <sequenceFlow id="sid-169C8661-BCA4-4D67-8F14-D45239E6B3CC" sourceRef="sid-F3C21531-8080-421E-8A5D-A92102B8B302" targetRef="sid-1917820C-9059-4AF1-A5B9-A57AEB08DF99"></sequenceFlow>
  <scriptTask id="sid-F3C21531-8080-421E-8A5D-A92102B8B302" scriptFormat="Groovy" flowable:autoStoreVariables="false">
    <script><![CDATA[println("Hello Groovy");]]></script>
  </scriptTask>
</process>
1
2
3
4
@Test
public void startProcessInstanceByKey2() {
    runtimeService.startProcessInstanceByKey("ScriptTaskDemo");
}

4.3 Juel

Juel(Java Unified Expression Language) 表达式 ${xxx} 就是一个 Juel image.png image.png 这个脚本表示执行 Spring 容器中一个名为 myServiceTask03 的 Bean 的 hello 方法 XML 内容

1
2
3
4
5
6
7
8
9
10
<process id="ScriptTaskDemo" name="ScriptTaskDemo" isExecutable="true">
  <documentation>ScriptTaskDemo</documentation>
  <startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
  <sequenceFlow id="sid-781E4B49-2BF6-4E42-B556-8405F970603D" sourceRef="startEvent1" targetRef="sid-F3C21531-8080-421E-8A5D-A92102B8B302"></sequenceFlow>
  <endEvent id="sid-1917820C-9059-4AF1-A5B9-A57AEB08DF99"></endEvent>
  <sequenceFlow id="sid-169C8661-BCA4-4D67-8F14-D45239E6B3CC" sourceRef="sid-F3C21531-8080-421E-8A5D-A92102B8B302" targetRef="sid-1917820C-9059-4AF1-A5B9-A57AEB08DF99"></sequenceFlow>
  <scriptTask id="sid-F3C21531-8080-421E-8A5D-A92102B8B302" scriptFormat="juel" flowable:autoStoreVariables="false">
    <script><![CDATA[${myServiceTask03.hello()}]]></script>
  </scriptTask>
</process>
1
2
3
4
@Test
public void startProcessInstanceByKey2() {
    runtimeService.startProcessInstanceByKey("ScriptTaskDemo");
}
本文由作者按照 CC BY 4.0 进行授权