Flowable09Task---------------持续更新中

在Flowable中,Task是整个工作流的“执行单元”,是流程得以具体实施和向前推进的基石。
可以通俗地理解,流程图(BPMN)是计划,而Task就是这个计划中需要被完成的具体待办事项。

一、 Task 的主要类型

在BPMN规范中,Task有多种类型,但对于应用开发者来说,最核心、最常用的是以下两种:

1. 用户任务 (User Task)
图标:一个左上角有“小人”图标的圆角矩形。
定义:代表需要人工参与和处理的工作环节。这是工作流应用中交互最频繁的部分。
示例:
“经理审批报销单”
“申请人填写请假表单”
“仓库管理员确认库存”
核心:当流程执行到用户任务节点时,它会在数据库的 ACT_RU_TASK 表中创建一条任务记录,然后暂停,等待外部(通常是用户)的指令来完成它。

2. 服务任务 (Service Task)
图标:一个带有“齿轮”图标的圆角矩形。
定义:代表由系统自动执行的后台工作,无需人工干预。
示例:
“调用外部API发送短信通知”
“根据订单数据生成PDF发票”
“更新数据库中的订单状态”
核心:当流程执行到服务任务节点时,它会立即、自动地执行预定义的逻辑(如调用一个Java类或执行一个表达式),执行完毕后自动向前推进,整个过程对用户是透明的。
其他任务类型:还包括脚本任务(Script Task)、业务规则任务(Business Rule Task)、邮件任务(Mail Task)等,它们都是服务任务的特定变种,用于执行特定类型的自动化逻辑。
接下来的内容,我们将重点聚焦于与开发者关系最密切的用户任务 (User Task)。

二、 用户任务 (User Task) 的完整生命周期

理解一个用户任务从诞生到消亡的全过程,是掌握Flowable开发的关键。
1. 创建 (Creation)
时机:当流程实例的执行流到达BPMN模型中的一个用户任务节点时,Flowable引擎会自动在数据库中创建一条任务记录。
结果:在 ACT_RU_TASK 表中新增一行数据,包含了任务的ID、名称、办理人(如果已指定)、创建时间等信息。
2. 分配 (Assignment)
任务创建后,必须明确由谁来处理。这就是任务分配,主要有三种策略:

策略BPMN 属性说明
直接办理人flowable:assignee将任务直接分配给一个用户。只有该用户能看到并处理此任务。
候选用户lowable:candidateUsers指定多个可以处理此任务的用户。他们都能看到任务,但需要先“认领”。
候选组flowable:candidateGroups指定一个或多个用户组。该组下的所有成员都是任务的候选人。

动态分配:在实际应用中,这些属性的值通常不是写死的,而是使用EL表达式(如 ${initiator})或方法表达式(如 ${myBean.findManager(execution)})来动态计算得出。
3. 查询 (Querying)
用户需要一个“待办事项列表”来查看分配给他们的任务。这通过 TaskService 的查询API实现。
核心API:taskService.createTaskQuery()
常用查询条件:
.taskAssignee(userId): 查询直接分配给某用户的任务。
.taskCandidateUser(userId): 查询某用户作为候选人的任务。
.taskCandidateGroup(groupName): 查询某用户组作为候选组的任务。
.processInstanceId(processInstanceId): 查询某个流程实例下的所有活动任务。
示例代码:

// 查询用户'manager-jack'的所有待办任务(包括直接分配和作为候选人的)
List<Task> tasks = taskService.createTaskQuery()
        .taskCandidateOrAssigned("manager-jack") // 常用组合查询
        .orderByTaskCreateTime().desc()
        .list();

4. 认领 (Claiming) - 仅针对候选任务
对于候选任务(即只有candidateUsers或candidateGroups的任务),它没有明确的办理人(assignee字段为null)。在处理之前,必须有一个候选人将其认领,表示“这件事我来跟进”。
核心API:taskService.claim(String taskId, String userId)
作用:
将任务的assignee字段设置为认领人的ID。
任务从所有候选人的“公共待办池”中消失,只出现在认领人的“个人待办”列表中。
防止同一个任务被多人重复处理。
取消认领:taskService.unclaim(String taskId),将任务退回公共池。
5. 完成 (Completion)
这是推动流程前进的核心动作。当用户处理完任务后,调用complete方法。
核心API:taskService.complete(String taskId, Map<String, Object> variables)
作用:
从 ACT_RU_TASK 表中删除该任务记录。
在 ACT_HI_TASKINST (历史任务) 表中更新该任务的状态为“已完成”。
将传入的variables(流程变量)更新到流程实例中。
流程引擎根据BPMN模型,计算并执行下一步的流转。

三、 Task 的高级特性

1. 任务监听器 (Task Listener)
你可以在任务生命周期的特定事件点(create, assignment, complete, delete)挂载自定义的Java逻辑。
用途:实现与核心业务无关的“切面”逻辑,如:
任务创建时:发送邮件/钉钉通知给办理人。
任务分配时:记录分配日志。
任务完成时:更新外部系统的状态。

任务监听器(Task Listener)是实现业务与流程解耦、添加“非核心”业务逻辑的利器。下面我将为你设计一个非常贴合企业日常开发的Demo,并提供完整的代码和配置说明。
智能的“经理审批”任务通知系统

业务需求:
在一个请假流程中,当“经理审批”任务产生时,我们希望系统能实现以下智能化的通知功能,而不是简单地发个邮件。
任务创建时 (create event):
工作时间(周一至周五,9:00-18:00):立即通过钉钉/企业微信发送一条消息给经理,提醒他有新审批任务。
非工作时间:为了不打扰经理休息,只发送一封邮件作为备忘。
任务分配时 (assignment event):
这个场景通常用于会签或转派。当任务的assignee发生变化时(比如经理A把任务转给了经理B),在操作日志中记录一条:“任务 [任务ID] 已由 [原办理人] 转派给 [新办理人]”。
任务完成时 (complete event):
计算该审批任务的处理时长,如果超过24小时,标记为“审批超时”,并记录到审批效率分析表中,用于后续的流程优化分析。
实现步骤
步骤 1:创建任务监听器 Java 类
我们需要创建一个或多个实现了 org.flowable.task.service.delegate.TaskListener 接口的Java类。为了结构清晰,我们为每个事件创建一个单独的监听器。
1.1. 任务创建通知监听器 (TaskCreateNotificationListener)

import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.delegate.TaskListener;
import org.springframework.stereotype.Component;

import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;

// 注册为Spring Bean,以便在BPMN中通过表达式引用
@Component("taskCreateNotificationListener")
public class TaskCreateNotificationListener implements TaskListener {

    // 假设这是注入的钉钉和邮件服务
    // @Autowired private DingTalkService dingTalkService;
    // @Autowired private EmailService emailService;

    @Override
    public void notify(DelegateTask delegateTask) {
        String assignee = delegateTask.getAssignee();
        String taskName = delegateTask.getName();
        String processInstanceId = delegateTask.getProcessInstanceId();

        // 模拟获取流程发起人
        String initiator = (String) delegateTask.getVariable("initiator");

        // 核心逻辑:判断是否为工作时间
        if (isWorkingHours()) {
            // 工作时间,发钉钉
            String dingTalkMessage = String.format(
                "Hi %s, 您有一个新的审批任务需要处理!\n- 任务名称: %s\n- 申请人: %s\n- 点击链接处理: http://myapp.com/tasks/%s",
                assignee, taskName, initiator, delegateTask.getId()
            );
            System.out.println("【钉钉发送】: " + dingTalkMessage);
            // dingTalkService.send(assignee, dingTalkMessage);
        } else {
            // 非工作时间,发邮件
            String emailMessage = String.format(
                "【邮件提醒】您有一个新的审批任务 '%s' (来自: %s) 已进入您的待办列表,请在工作时间处理。",
                taskName, initiator
            );
            System.out.println("【邮件发送】: " + emailMessage);
            // emailService.send(assignee, "新审批任务提醒", emailMessage);
        }
    }

    private boolean isWorkingHours() {
        LocalDateTime now = LocalDateTime.now();
        DayOfWeek day = now.getDayOfWeek();
        LocalTime time = now.toLocalTime();

        // 周一到周五
        if (day.getValue() >= 1 && day.getValue() <= 5) {
            // 9:00 - 18:00
            return !time.isBefore(LocalTime.of(9, 0)) && !time.isAfter(LocalTime.of(18, 0));
        }
        return false;
    }
}

1.2. 任务分配日志监听器 (TaskAssignmentLogListener)

import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.delegate.TaskListener;
import org.springframework.stereotype.Component;

@Component("taskAssignmentLogListener")
public class TaskAssignmentLogListener implements TaskListener {
    // 假设这是注入的操作日志服务
    // @Autowired private OperationLogService logService;

    @Override
    public void notify(DelegateTask delegateTask) {
        String taskId = delegateTask.getId();
        String originalAssignee = delegateTask.getOriginalAssignee(); // 获取原始办理人
        String newAssignee = delegateTask.getAssignee();

        // 只有在办理人确实发生变化时才记录
        if (originalAssignee != null && !originalAssignee.equals(newAssignee)) {
            String logMessage = String.format(
                "任务转派记录:任务 '%s' (%s) 已由用户 '%s' 转派给 '%s'。",
                taskId, delegateTask.getName(), originalAssignee, newAssignee
            );
            System.out.println("【操作日志】: " + logMessage);
            // logService.addLog(logMessage);
        }
    }
}

1.3. 任务完成效率分析监听器 (TaskCompletionAnalysisListener)、

import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.delegate.TaskListener;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;
import java.util.Date;

@Component("taskCompletionAnalysisListener")
public class TaskCompletionAnalysisListener implements TaskListener {
    // 假设这是注入的效率分析服务
    // @Autowired private EfficiencyAnalyticsService analyticsService;

    @Override
    public void notify(DelegateTask delegateTask) {
        Date createTime = delegateTask.getCreateTime();
        Date completeTime = new Date(); // 任务完成时,当前时间就是完成时间

        long processingMillis = Duration.between(createTime.toInstant(), completeTime.toInstant()).toMillis();
        long processingHours = processingMillis / (1000 * 60 * 60);

        System.out.println(String.format("【效率分析】: 任务 '%s' 处理耗时 %d 小时。", delegateTask.getId(), processingHours));

        // 如果处理时长超过24小时
        if (processingHours > 24) {
            String analysisRecord = String.format(
                "审批超时:任务 '%s', 办理人 '%s', 耗时 %d 小时。",
                delegateTask.getName(), delegateTask.getAssignee(), processingHours
            );
            System.out.println("【效率分析】记录到数据库: " + analysisRecord);
            // analyticsService.recordTimeout(delegateTask.getId(), delegateTask.getAssignee(), processingHours);
        }
    }
}

步骤 2:在 BPMN 中配置监听器
现在,打开你的BPMN流程图(例如 leave-process.bpmn20.xml),找到“经理审批”这个用户任务节点,并为其添加监听器配置。
你可以在 Flowable Modeler 的属性面板中,找到 “监听器 (Listeners)” 这一项,然后添加。
对应的 XML 看起来是这样的:

<userTask id="managerApprovalTask" name="经理审批" flowable:assignee="${managerId}">
  <extensionElements>
    <!-- 1. 配置 'create' 事件的监听器 -->
    <flowable:taskListener 
        event="create" 
        class="com.yourcompany.flowable.listeners.TaskCreateNotificationListener">
    </flowable:taskListener>

    <!-- 2. 配置 'assignment' 事件的监听器,使用表达式方式 -->
    <flowable:taskListener 
        event="assignment" 
        type="expression" 
        expression="${taskAssignmentLogListener.notify(task)}">
    </flowable:taskListener>
    
    <!-- 3. 配置 'complete' 事件的监听器 -->
    <flowable:taskListener 
        event="complete" 
        type="delegateExpression" 
        expression="${taskCompletionAnalysisListener}">
    </flowable:taskListener>

  </extensionElements>
</userTask>

配置方式详解:
event: 指定监听器触发的事件,如 create, assignment, complete, delete。
class: 直接指定监听器类的完全限定名。Flowable会直接new一个实例,无法使用Spring注入。
expression: 执行一段EL表达式。这里我们调用了Spring Bean的方法,并手动传入了task对象。task是Flowable在执行此表达式时提供的内置变量,代表DelegateTask。
delegateExpression (推荐): 指定一个实现了TaskListener接口的Spring Bean的ID。Flowable会从Spring容器中获取这个Bean,并调用其notify方法。这是与Spring集成的最佳方式,因为它支持依赖注入。

2. 任务局部变量 (Task-Local Variables)
如之前讨论的,这是只属于单个任务的变量,任务完成后即销毁。非常适合存储仅用于当前任务UI展示或临时计算的、不想污染全局流程变量的数据。
3. 表单 (Forms)(本章节不做详细说明,后续章节会有详细介绍)
用户任务是与用户交互的载体,因此它通常需要关联一个表单来收集或展示数据。
BPMN 属性:flowable:formKey
作用:将一个在Flowable Form Modeler中创建的表单,或一个外部URL(如你的前端页面路由)与该任务关联起来。当用户处理任务时,系统可以根据formKey为他渲染正确的界面。

总结

Task 是Flowable工作流的核心执行单元,它定义了“谁,在何时,需要做什么事”。
用户任务 (User Task) 是流程中的人工处理节点,它的生命周期包括创建、分配、查询、认领、完成等一系列标准化操作,这些操作都通过强大的 TaskService API来完成。
服务任务 (Service Task) 则是流程中的自动化处理节点,由系统在后台自动完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值