Flowable实战:自定义流程图生成器与高亮渲染进阶

📅2026/7/15 2:30:23 👁️次浏览
Flowable实战:自定义流程图生成器与高亮渲染进阶
1. 为什么需要自定义流程图生成器在业务系统开发中流程图的可视化展示往往需要满足特定的业务需求。比如在审批流程监控场景中我们可能需要用不同颜色区分已审批、当前处理中和被驳回的节点在运维流程中可能需要用特殊图标标记存在风险的环节。Flowable自带的流程图生成器虽然功能完善但在这些定制化需求面前就显得力不从心了。我去年参与开发的一个电商风控系统就遇到了这个问题。系统需要实时展示订单审核流程但默认的流程图生成器只能简单高亮当前节点无法直观展示各个审核环节的状态如初审通过、复审拒绝等。这给运营人员带来了很大困扰经常需要反复点击查看每个节点的详情。2. 核心定制化方案2.1 继承DefaultProcessDiagramCanvasFlowable的图形渲染最终都是由DefaultProcessDiagramCanvas完成的。要自定义渲染效果首先需要继承这个类public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { // 自定义高亮当前活动节点的方法 public void drawActiveHighLight(int x, int y, int width, int height) { // 使用绿色填充当前节点 this.drawHighLight(x, y, width, height, Color.GREEN); } // 自定义挂起节点渲染 public void drawSuspendedActivity(int x, int y, int width, int height) { // 使用黄色填充并添加斜线标记 this.drawHighLight(x, y, width, height, Color.YELLOW); this.drawLine(x, y, xwidth, yheight, Color.RED); } }这里我添加了两个典型场景的渲染方法drawActiveHighLight用绿色填充当前正在处理的节点drawSuspendedActivity用黄色标记被挂起的节点并添加红色斜线2.2 重写DefaultProcessDiagramGenerator有了自定义的画布后还需要重写生成器来控制何时使用这些特殊渲染public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator { Override protected void drawActivity(ProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode, ListString highLightedActivities) { // 先调用父类方法绘制基础图形 super.drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities); // 自定义渲染逻辑 if (highLightedActivities.contains(flowNode.getId())) { GraphicInfo graphicInfo bpmnModel.getGraphicInfo(flowNode.getId()); // 判断节点状态并调用对应的渲染方法 if(isSuspended(flowNode.getId())) { ((CustomProcessDiagramCanvas)processDiagramCanvas) .drawSuspendedActivity((int)graphicInfo.getX(), (int)graphicInfo.getY(), (int)graphicInfo.getWidth(), (int)graphicInfo.getHeight()); } else if(isActive(flowNode.getId())) { ((CustomProcessDiagramCanvas)processDiagramCanvas) .drawActiveHighLight((int)graphicInfo.getX(), (int)graphicInfo.getY(), (int)graphicInfo.getWidth(), (int)graphicInfo.getHeight()); } } } }3. 完整实现案例3.1 后端控制器实现在Spring Boot中我们可以这样暴露流程图生成接口GetMapping(/process/diagram) public void generateDiagram(RequestParam String processInstanceId, HttpServletResponse response) throws IOException { // 获取流程定义和实例信息 ProcessInstance processInstance runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); // 收集高亮节点 ListString highLightedActivities new ArrayList(); ListString highLightedFlows new ArrayList(); // 查询历史活动记录 ListHistoricActivityInstance activities historyService .createHistoricActivityInstanceQuery() .processInstanceId(processInstanceId) .list(); // 区分节点类型 for (HistoricActivityInstance activity : activities) { if (sequenceFlow.equals(activity.getActivityType())) { highLightedFlows.add(activity.getActivityId()); } else { highLightedActivities.add(activity.getActivityId()); } } // 使用自定义生成器 BpmnModel bpmnModel repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); ProcessDiagramGenerator generator new CustomProcessDiagramGenerator(); try (InputStream is generator.generateDiagram(bpmnModel, png, highLightedActivities, highLightedFlows, 宋体, 宋体, 宋体, 1.0, true); OutputStream os response.getOutputStream()) { // 将图像写入响应流 byte[] buffer new byte[1024]; int bytesRead; while ((bytesRead is.read(buffer)) ! -1) { os.write(buffer, 0, bytesRead); } } }3.2 前端交互增强为了让流程图更具交互性可以在前端添加鼠标悬停显示节点详情的功能// 假设流程图以img标签展示 $(#processDiagram).on(mousemove, function(e) { const x e.offsetX; const y e.offsetY; // 遍历所有节点信息 nodes.forEach(node { if (x node.x x node.x node.width y node.y y node.y node.height) { // 显示节点详情弹窗 showNodeTooltip(node, x, y); } }); }); function showNodeTooltip(node, x, y) { // 动态创建tooltip元素 const tooltip $(div classnode-tooltip/div) .html( h4${node.name}/h4 p状态: ${node.status}/p p处理人: ${node.assignee}/p p耗时: ${node.duration}/p ) .css({ position: absolute, left: x px, top: y px }); $(#diagramContainer).append(tooltip); }4. 进阶优化技巧4.1 性能优化方案当流程实例很多时频繁生成流程图可能导致性能问题。我们可以采用以下优化策略缓存机制对已生成的流程图进行缓存Cacheable(value processDiagrams, key #processInstanceId _ #highlightType) public byte[] generateDiagram(String processInstanceId, String highlightType) { // 生成逻辑 }异步生成使用Spring的Async实现异步生成Async public Futurebyte[] asyncGenerateDiagram(String processInstanceId) { // 生成逻辑 return new AsyncResult(diagramBytes); }预生成策略在流程关键节点触发预生成4.2 动态样式配置为了更灵活地控制渲染样式可以引入样式配置# application.yml flowable: diagram: colors: active: #4CAF50 suspended: #FFC107 completed: #9E9E9E fonts: default: Microsoft YaHei label: Microsoft YaHei, 12px然后在生成器中使用这些配置Value(${flowable.diagram.colors.active}) private String activeColor; protected void drawActiveNode(ProcessDiagramCanvas canvas, GraphicInfo graphicInfo) { Color color Color.decode(activeColor); // 使用配置的颜色进行绘制 }5. 常见问题排查在实际项目中可能会遇到以下典型问题中文乱码问题确保系统安装了中文字体在生成器配置中明确指定中文字体generateDiagram(bpmnModel, png, activities, flows, Microsoft YaHei, Microsoft YaHei, Microsoft YaHei, 1.0, true);节点坐标偏移检查BPMN文件中是否正确定义了graphicInfo确保没有在自定义渲染时错误修改了坐标计算渲染性能低下避免在draw方法中执行复杂计算考虑使用缓冲图像技术BufferedImage image new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d image.createGraphics(); // 绘制操作 g2d.dispose();高亮效果不明显增加边框宽度使用更醒目的颜色组合添加动画效果前端实现通过继承Flowable的核心渲染类我们可以实现高度定制化的流程图展示效果。这种方案不仅能够满足各种业务场景的可视化需求还能保持与Flowable引擎的无缝集成。在实际项目中建议先明确业务对流程图的具体要求再针对性地设计渲染方案避免过度设计。