Activiti 中“获取任务”“获取节点”“获取活动”听起来很相近,实际却跨越了流程定义、运行时和历史三个层次。先明确要找哪一类对象,再选择 ID 和查询入口,代码会简单得多。

先分清这些标识

标识或对象含义
taskId某一次具体用户任务的标识
taskDefinitionKey该任务在 BPMN 定义中对应的节点 ID
processDefinitionId某个已部署版本的流程定义
processInstanceId一次具体的流程运行
executionId运行中的执行对象标识
HistoricActivityInstance一次活动执行的历史记录

同一节点可以执行很多次,也可以在会签中产生多条任务。所以节点 ID 不等于任务 ID;任务名可重复或修改,也不适合作为可靠关联键。

从 taskId 取得任务

下面使用 Activiti 7.1.0.M6 引擎层接口,服务对象由项目注入。运行中任务可以这样查:

org.activiti.engine.task.Task task =
        taskService.createTaskQuery().taskId(taskId).singleResult();

任务完成后,运行时查询可能没有结果,此时改查历史:

org.activiti.engine.history.HistoricTaskInstance task =
        historyService.createHistoricTaskInstanceQuery()
                .taskId(taskId)
                .singleResult();

历史查询能否找到记录,取决于历史配置、保留策略和任务类型,不能承诺任意 taskId 都永久有效。空结果要按业务显示“记录不可用”或继续定位,不能直接认定用户从未处理过任务。

已有 Task,就不必按名称绕路

运行中任务和历史任务都能提供对应节点的 taskDefinitionKey。对于关联 BPMN 的用户任务,可以取得:

String nodeId = task.getTaskDefinitionKey();
String processDefinitionId = task.getProcessDefinitionId();

独立创建、没有关联流程的任务可能没有这些值,应先判断,不能直接转去查询流程模型。

从节点读取入线和出线

BpmnModel 是流程定义模型,FlowNode 表示节点,SequenceFlow 表示顺序连线。用定义 ID 读取正确版本的模型,再按节点 ID 找元素:

BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
FlowElement element = model.getFlowElement(nodeId);
if (!(element instanceof FlowNode)) {
    throw new IllegalArgumentException("目标不是流程节点");
}
FlowNode node = (FlowNode) element;

List<SequenceFlow> incoming = node.getIncomingFlows();
List<SequenceFlow> outgoing = node.getOutgoingFlows();

相关类型位于 org.activiti.bpmn.model。旧教程里的 PVM 类和 getOutgoingTransitions 不应与这里的模型 API 混用。BpmnModel 对应版本源码

取得出线只说明模型上怎样连接,不证明某条线已经执行,也不保证它的条件当前成立。

从任务找到对应的历史活动

当需要开始、结束、活动类型等执行信息时,可以在该流程实例的活动历史中匹配 taskId:

List<org.activiti.engine.history.HistoricActivityInstance> activities =
        historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .list();

List<org.activiti.engine.history.HistoricActivityInstance> matched =
        new ArrayList<>();
for (org.activiti.engine.history.HistoricActivityInstance activity : activities) {
    if (taskId.equals(activity.getTaskId())) {
        matched.add(activity);
    }
}

这里保留匹配列表,不用 findAny() 随意取一条。业务若要求唯一结果,应检查数量并处理异常;如果只需要用户任务的节点 ID,前面的 taskDefinitionKey 更直接。历史活动查询接口

定义图不是执行树

BpmnModel、FlowNode 和 SequenceFlow 描述定义;Execution 则属于某次运行的执行结构。并行、嵌套子流程、多实例都会影响执行树,不能按图中几条线就计算会有几个 execution。

查状态时回到运行时或历史数据,查连接关系时读取模型。把这条边界记住,就不容易把“拿到某个节点对象”误当成“找到了某一次审批任务”。