企微流程引擎和flowable区别

zxbandzby
2
2025-09-11

问题

我发现企业微信的审批流程比如是:A->B->C审批嘛,A审批之后,B驳回上一步,显示的流程需要经过的节点是ABABC,但是flowable的话,经过的流程图还是画的xml的图,最多有多个审批历史而已,并且是如果流程没有完全审批完成看不到ABABC 只能看到一下个审批人。

答案

企业微信的审批流程展示与Flowable的流程引擎在设计理念和实现上有所不同。企业微信可能采用了一种“预计算”或“动态生成”的方式,在流程设计时就已经确定了所有可能的路径,并在审批过程中实时展示整个流程的路径(包括已经经过的节点和未来可能经过的节点)。

而Flowable等BPMN引擎通常只记录当前的流程实例状态和历史活动记录,并不会预先展示所有可能的路径(因为流程可能根据条件分支有不同的走向)。

但是,我们可以通过Flowable的API获取流程定义和当前流程实例的信息,来构建类似的全局视图。

以下是实现思路:

  1. 1.

    获取流程定义​:通过RepositoryService获取流程定义,解析出所有的节点和顺序流。

  2. 2.

    获取当前流程实例的活跃节点​:通过RuntimeService获取当前流程实例正在执行的节点。

  3. 3.

    获取历史节点​:通过HistoryService获取已经执行过的节点。

  4. 4.

    根据流程定义和当前实例状态,计算出已经经过的节点和未来可能经过的节点

但是,请注意,由于流程可能存在分支、合并、循环等复杂结构,准确预测所有路径是非常困难的。企业微信的审批流程可能相对简单(通常是线性或分支较少),所以能够展示出完整的路径。

如果你想要在Flowable中实现类似企业微信的全局流程展示,可以尝试以下步骤:

  • 解析流程定义(BPMN XML)获取所有节点和顺序流,构建一个流程图结构。

  • 结合历史记录和当前活跃节点,标记出已经经过的节点。

  • 对于未来节点,可能需要根据当前节点和流程定义中的顺序流,以及网关的条件来判断可能的路径。但是注意,如果流程中有多个分支,未来路径可能不止一条。

然而,企业微信的审批流程展示可能是这样的:在流程发起时,就已经根据预设的审批人规则生成了一个固定的审批路径(即使有分支,也可能在设计时就已经确定了所有分支的展示)。这种固定路径的展示方式与Flowable的动态性有所不同。

另一种可能是,企业微信并不使用BPMN这样的标准流程引擎,而是自定义的审批流程引擎,它在流程实例化时就已经将整个流程的节点生成好了(包括所有分支的展示),即使某些分支可能不会被执行。

如果你希望在Flowable中实现类似的全局视图,可以考虑在流程启动时,就根据流程定义生成一个预期的节点顺序列表(可能需要考虑所有分支),然后在流程执行过程中,根据实际执行的路径来标记哪些节点已经完成,哪些节点正在进行,哪些节点尚未执行。

但是,由于Flowable的流程定义可能包含复杂网关(如并行网关、包含网关等),预测所有路径是非常复杂的。因此,很多系统只展示已经走过的路径和当前节点,对于未来的节点,只展示立即下一步,而不是全部。

如果你确实需要展示全局的节点(包括未来可能经过的节点),那么可能需要限制流程的复杂度(例如,只使用线性流程和排他网关),然后通过解析BPMN XML来获取节点顺序。

以下是一个简单的示例,假设流程是线性的(没有分支):

  1. 1.

    获取流程定义中的所有用户任务节点(按顺序)。

  2. 2.

    获取历史用户任务列表(按完成时间排序)。

  3. 3.

    获取当前待办任务节点。

  4. 4.

    将历史任务节点标记为已完成,当前任务节点标记为进行中,后续任务节点标记为未开始。

对于有分支的流程,则需要根据历史任务和当前任务所处的分支来推断后续任务。

由于这是一个复杂的问题,具体实现需要根据你的流程复杂度来决定。

如果你希望看到类似企业微信的效果,可能需要在前端绘制整个流程的节点,并根据流程实例的状态来更新节点的状态(已完成、进行中、未开始)。节点的顺序和连接关系可以从流程定义中获取。

总结:企业微信可能是通过预生成固定路径的方式展示全局流程,而Flowable作为通用的BPMN引擎,需要你自己根据流程定义和实例状态来生成类似的视图。

企业微信审批流程展示与Flowable的实现差异

企业微信的审批流程展示确实与Flowable这类标准BPMN引擎有所不同,主要体现在流程可视化方式和节点预测机制上。以下是详细分析和实现方案:

核心差异分析

特性

企业微信

Flowable

流程可视化

预生成完整审批路径图

基于BPMN XML动态渲染

节点预测

提前显示所有可能节点

只显示当前活动节点和历史节点

驳回处理

显示完整驳回路径(ABABC)

只记录历史活动,不预测未来

实现方式

自定义流程引擎+预计算

标准BPMN引擎+实时计算

企业微信的实现原理

  1. 1.

    预计算审批路径​:

    • 在流程设计阶段就计算出所有可能的审批路径

    • 存储为结构化的路径数据,而非单纯的BPMN图

  2. 2.

    路径可视化引擎​:

    • 使用自定义的渲染引擎而非BPMN标准图

    • 能够显示"虚拟路径"(如驳回后的重新审批路径)

  3. 3.

    状态追踪机制​:

    • 记录每个节点的多次出现(如A→B→A)

    • 维护节点状态(待处理、已完成、已跳过等)

在Flowable中实现类似效果

虽然Flowable本身不直接提供这种预计算路径功能,但可以通过以下方式实现类似效果:

方案1:扩展历史数据查询

java

下载

复制

运行

// 获取流程实例的所有历史活动信息
List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
    .processInstanceId(processInstanceId)
    .orderByHistoricActivityInstanceStartTime().asc()
    .list();

// 构建可视化路径
List<ProcessNode> visualPath = new ArrayList<>();
for (HistoricActivityInstance activity : activities) {
    ProcessNode node = new ProcessNode();
    node.setId(activity.getActivityId());
    node.setName(activity.getActivityName());
    node.setType(activity.getActivityType());
    node.setStatus("COMPLETED"); // 已完成
    
    visualPath.add(node);
}

// 添加当前活动节点
List<ActivityInstance> activeActivities = runtimeService.getActivityInstances(processInstanceId);
for (ActivityInstance activity : activeActivities) {
    ProcessNode node = new ProcessNode();
    node.setId(activity.getActivityId());
    node.setName(activity.getActivityName());
    node.setType(activity.getActivityType());
    node.setStatus("ACTIVE"); // 进行中
    
    visualPath.add(node);
}

方案2:预计算可能的审批路径

java

下载

复制

运行

// 流程启动时预计算所有可能路径
public List<List<String>> calculateAllPaths(String processDefinitionId) {
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
    Process process = bpmnModel.getMainProcess();
    
    List<List<String>> allPaths = new ArrayList<>();
    calculatePathsFromNode(process, "startEvent", new ArrayList<>(), allPaths);
    
    return allPaths;
}

private void calculatePathsFromNode(Process process, String currentNode, 
                                   List<String> currentPath, List<List<String>> allPaths) {
    currentPath.add(currentNode);
    
    FlowElement element = process.getFlowElement(currentNode);
    if (element instanceof EndEvent) {
        allPaths.add(new ArrayList<>(currentPath));
        currentPath.remove(currentPath.size() - 1);
        return;
    }
    
    if (element instanceof SequenceFlow) {
        element = process.getFlowElement(((SequenceFlow) element).getTargetRef());
    }
    
    if (element instanceof UserTask || element instanceof Gateway) {
        Collection<SequenceFlow> outgoingFlows = null;
        
        if (element instanceof UserTask) {
            outgoingFlows = ((UserTask) element).getOutgoingFlows();
        } else if (element instanceof ExclusiveGateway) {
            outgoingFlows = ((ExclusiveGateway) element).getOutgoingFlows();
            // 这里可以处理条件分支
        }
        
        if (outgoingFlows != null) {
            for (SequenceFlow flow : outgoingFlows) {
                calculatePathsFromNode(process, flow.getTargetRef(), currentPath, allPaths);
            }
        }
    }
    
    currentPath.remove(currentPath.size() - 1);
}

方案3:自定义流程可视化服务

java

下载

复制

运行

@Service
public class ProcessVisualizationService {
    
    @Autowired
    private RuntimeService runtimeService;
    
    @Autowired
    private HistoryService historyService;
    
    @Autowired
    private RepositoryService repositoryService;
    
    public ProcessVisualization getProcessVisualization(String processInstanceId) {
        ProcessVisualization visualization = new ProcessVisualization();
        
        // 获取历史节点
        List<HistoricActivityInstance> historicActivities = historyService
            .createHistoricActivityInstanceQuery()
            .processInstanceId(processInstanceId)
            .orderByHistoricActivityInstanceStartTime().asc()
            .list();
        
        // 获取当前节点
        List<ActivityInstance> activeActivities = runtimeService
            .getActivityInstances(processInstanceId);
        
        // 获取流程定义
        HistoricProcessInstance processInstance = historyService
            .createHistoricProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .singleResult();
        
        BpmnModel bpmnModel = repositoryService
            .getBpmnModel(processInstance.getProcessDefinitionId());
        
        // 构建可视化数据
        visualization.setHistoricNodes(convertToVisualNodes(historicActivities));
        visualization.setActiveNodes(convertToVisualNodes(activeActivities));
        visualization.setPossiblePaths(calculatePossiblePaths(
            bpmnModel, 
            activeActivities.get(0).getActivityId()
        ));
        
        return visualization;
    }
    
    // 其他辅助方法...
}

方案4:前端渲染优化

在前端实现一个专门的可视化组件:

javascript

下载

复制

运行

// 前端组件:审批流程可视化
class ApprovalProcessVisualizer {
    constructor(container) {
        this.container = container;
        this.nodes = [];
        this.connections = [];
    }
    
    // 渲染方法
    render(visualizationData) {
        this.clear();
        
        // 渲染历史节点(灰色)
        visualizationData.historicNodes.forEach(node => {
            this.addNode(node, 'historic');
        });
        
        // 渲染当前节点(蓝色)
        visualizationData.activeNodes.forEach(node => {
            this.addNode(node, 'active');
        });
        
        // 渲染可能路径(虚线)
        visualizationData.possiblePaths.forEach(path => {
            this.addPath(path, 'possible');
        });
        
        // 渲染实际连接(实线)
        visualizationData.actualConnections.forEach(conn => {
            this.addConnection(conn, 'actual');
        });
    }
    
    // 其他方法...
}

数据库设计扩展

如果需要持久化存储审批路径信息,可以扩展表结构:

sql

复制

-- 审批路径预计算表
CREATE TABLE wf_predicted_path (
    id VARCHAR(64) PRIMARY KEY,
    process_def_id VARCHAR(64),
    path_hash VARCHAR(128),
    node_sequence TEXT,
    created_time TIMESTAMP
);

-- 流程实例路径表
CREATE TABLE wf_instance_path (
    id VARCHAR(64) PRIMARY KEY,
    process_instance_id VARCHAR(64),
    path_type VARCHAR(32), -- ACTUAL, PREDICTED, etc.
    node_sequence TEXT,
    updated_time TIMESTAMP
);

-- 节点状态表
CREATE TABLE wf_node_status (
    id VARCHAR(64) PRIMARY KEY,
    process_instance_id VARCHAR(64),
    activity_id VARCHAR(255),
    status VARCHAR(32), -- PENDING, COMPLETED, REJECTED, etc.
    execution_count INT DEFAULT 1,
    last_updated TIMESTAMP
);

总结

企业微信的审批流程展示是通过预计算和自定义可视化引擎实现的,与Flowable的标准BPMN方式有所不同。要在Flowable中实现类似效果,需要:

  1. 1.

    扩展历史数据查询​:获取完整的活动历史

  2. 2.

    预计算可能路径​:基于流程定义分析所有可能的审批路径

  3. 3.

    自定义可视化服务​:构建专门的可视化数据结构

  4. 4.

    前端渲染优化​:使用自定义组件而非标准BPMN图

这种实现方式会增加系统复杂性,但能提供更符合企业审批场景的用户体验。

动物装饰