问题
我发现企业微信的审批流程比如是:A->B->C审批嘛,A审批之后,B驳回上一步,显示的流程需要经过的节点是ABABC,但是flowable的话,经过的流程图还是画的xml的图,最多有多个审批历史而已,并且是如果流程没有完全审批完成看不到ABABC 只能看到一下个审批人。
答案
企业微信的审批流程展示与Flowable的流程引擎在设计理念和实现上有所不同。企业微信可能采用了一种“预计算”或“动态生成”的方式,在流程设计时就已经确定了所有可能的路径,并在审批过程中实时展示整个流程的路径(包括已经经过的节点和未来可能经过的节点)。
而Flowable等BPMN引擎通常只记录当前的流程实例状态和历史活动记录,并不会预先展示所有可能的路径(因为流程可能根据条件分支有不同的走向)。
但是,我们可以通过Flowable的API获取流程定义和当前流程实例的信息,来构建类似的全局视图。
以下是实现思路:
1.
获取流程定义:通过RepositoryService获取流程定义,解析出所有的节点和顺序流。
2.
获取当前流程实例的活跃节点:通过RuntimeService获取当前流程实例正在执行的节点。
3.
获取历史节点:通过HistoryService获取已经执行过的节点。
4.
根据流程定义和当前实例状态,计算出已经经过的节点和未来可能经过的节点。
但是,请注意,由于流程可能存在分支、合并、循环等复杂结构,准确预测所有路径是非常困难的。企业微信的审批流程可能相对简单(通常是线性或分支较少),所以能够展示出完整的路径。
如果你想要在Flowable中实现类似企业微信的全局流程展示,可以尝试以下步骤:
•
解析流程定义(BPMN XML)获取所有节点和顺序流,构建一个流程图结构。
•
结合历史记录和当前活跃节点,标记出已经经过的节点。
•
对于未来节点,可能需要根据当前节点和流程定义中的顺序流,以及网关的条件来判断可能的路径。但是注意,如果流程中有多个分支,未来路径可能不止一条。
然而,企业微信的审批流程展示可能是这样的:在流程发起时,就已经根据预设的审批人规则生成了一个固定的审批路径(即使有分支,也可能在设计时就已经确定了所有分支的展示)。这种固定路径的展示方式与Flowable的动态性有所不同。
另一种可能是,企业微信并不使用BPMN这样的标准流程引擎,而是自定义的审批流程引擎,它在流程实例化时就已经将整个流程的节点生成好了(包括所有分支的展示),即使某些分支可能不会被执行。
如果你希望在Flowable中实现类似的全局视图,可以考虑在流程启动时,就根据流程定义生成一个预期的节点顺序列表(可能需要考虑所有分支),然后在流程执行过程中,根据实际执行的路径来标记哪些节点已经完成,哪些节点正在进行,哪些节点尚未执行。
但是,由于Flowable的流程定义可能包含复杂网关(如并行网关、包含网关等),预测所有路径是非常复杂的。因此,很多系统只展示已经走过的路径和当前节点,对于未来的节点,只展示立即下一步,而不是全部。
如果你确实需要展示全局的节点(包括未来可能经过的节点),那么可能需要限制流程的复杂度(例如,只使用线性流程和排他网关),然后通过解析BPMN XML来获取节点顺序。
以下是一个简单的示例,假设流程是线性的(没有分支):
1.
获取流程定义中的所有用户任务节点(按顺序)。
2.
获取历史用户任务列表(按完成时间排序)。
3.
获取当前待办任务节点。
4.
将历史任务节点标记为已完成,当前任务节点标记为进行中,后续任务节点标记为未开始。
对于有分支的流程,则需要根据历史任务和当前任务所处的分支来推断后续任务。
由于这是一个复杂的问题,具体实现需要根据你的流程复杂度来决定。
如果你希望看到类似企业微信的效果,可能需要在前端绘制整个流程的节点,并根据流程实例的状态来更新节点的状态(已完成、进行中、未开始)。节点的顺序和连接关系可以从流程定义中获取。
总结:企业微信可能是通过预生成固定路径的方式展示全局流程,而Flowable作为通用的BPMN引擎,需要你自己根据流程定义和实例状态来生成类似的视图。
企业微信审批流程展示与Flowable的实现差异
企业微信的审批流程展示确实与Flowable这类标准BPMN引擎有所不同,主要体现在流程可视化方式和节点预测机制上。以下是详细分析和实现方案:
核心差异分析
企业微信的实现原理
1.
预计算审批路径:
•
在流程设计阶段就计算出所有可能的审批路径
•
存储为结构化的路径数据,而非单纯的BPMN图
2.
路径可视化引擎:
•
使用自定义的渲染引擎而非BPMN标准图
•
能够显示"虚拟路径"(如驳回后的重新审批路径)
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.
扩展历史数据查询:获取完整的活动历史
2.
预计算可能路径:基于流程定义分析所有可能的审批路径
3.
自定义可视化服务:构建专门的可视化数据结构
4.
前端渲染优化:使用自定义组件而非标准BPMN图
这种实现方式会增加系统复杂性,但能提供更符合企业审批场景的用户体验。