会签通常用多实例用户任务表示。一个 BPMN 节点可以产生多条任务,所以“这个节点完成了吗”不能只查询某一个 taskId,也不能把所有历史记录混在一起计数。

首先明确查询范围:哪一个流程实例、哪一个节点、哪一轮会签。流程循环或退回后再次进入同一节点时,节点 ID 不变,但执行轮次已经不同。

按节点查出相关任务

在 Activiti 7.1.0.M6 引擎层,可以按流程实例和节点 ID 查询历史活动。用户任务活动中包含关联的任务 ID:

List<org.activiti.engine.history.HistoricActivityInstance> activities =
        historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .activityId(nodeId)
                .activityType("userTask")
                .list();

for (org.activiti.engine.history.HistoricActivityInstance activity : activities) {
    String taskId = activity.getTaskId();
    Date endTime = activity.getEndTime();
    // 保存每次活动执行的信息;不要仅按 nodeId 去重后判断本轮状态。
}

历史活动查询接口适合需要活动执行信息的情况。如果只需要用户任务,也可以用历史任务查询的 taskDefinitionKey(nodeId):

List<org.activiti.engine.history.HistoricTaskInstance> tasks =
        historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .taskDefinitionKey(nodeId)
                .list();

这个过滤条件由 TaskInfoQuery 提供。没有必要为了“历史任务不能按节点查”而绕一大圈;应先看所用版本实际支持的查询条件。

单实例:结束时间只说明一次执行结束

对于某次具体的活动执行,endTime 非空可以说明它已结束,但还要区分正常完成、取消或其他结束原因。

如果节点可能重复进入,只要某一条旧记录结束就把整个节点标绿,会掩盖当前正在执行的新任务。查询不到记录,也可能因为历史级别不足或历史已经清理,不能一律标成“未开始”。

多实例:计数器要从正确作用域读取

多实例执行常见的计数变量是:

  • nrOfInstances:本轮实例总数。
  • nrOfActiveInstances:当前活跃实例数。
  • nrOfCompletedInstances:已完成实例数。

它们属于多实例相关执行作用域。不能任取一个任务的 executionId,再把不存在的变量当成零。下面是已经确定多实例根执行 ID 后的读取片段:

Object total = runtimeService.getVariableLocal(miRootExecutionId, "nrOfInstances");
Object active = runtimeService.getVariableLocal(miRootExecutionId, "nrOfActiveInstances");
Object completed = runtimeService.getVariableLocal(miRootExecutionId, "nrOfCompletedInstances");

if (!(total instanceof Number) || !(active instanceof Number)
        || !(completed instanceof Number)) {
    throw new IllegalStateException("多实例计数不可用,先检查执行作用域和生命周期");
}

运行时执行结束后,不应再依赖它的局部变量查询结果。需要长期展示的业务统计,应在适当事件中保存,并与历史记录对照,而不是等执行已删除才读取计数器。

为什么“完成数等于总数”不够

流程可以配置提前完成条件。例如满足一定审批比例后,剩余任务被取消,多实例活动照样结束。这和每个审批人都完成不是同一件事。中断边界事件也可能结束整轮会签。官方多实例概念说明(6.0 用户指南)

因此,页面汇总最好能表达“进行中”“全部正常完成”“因完成条件提前结束”“被取消”和“未知”。“未知”是必要状态:缺历史数据或无法确定轮次时,比显示一个没有依据的“已完成”更准确。

对有循环、退回或并行的流程,先用实例 ID 和执行作用域确定本轮范围,再做状态汇总。相关标识的区别见 任务、节点和历史活动。