OWS 维修业务流程图生成
把维修业务文档里的流程文字转成可编辑的 BPMN 2.0 泳道图,并渲染成可直接插入 Word 的 PNG。
适用与不适用
适用:程序文件 / 作业指导书 / SOP / 管理制度里"第 X 章 程序"这类分步骤描述;
需要按角色分泳道的跨部门流程;批量把一个目录的程序文件转成图;
把多个程序整合成一张全局程序地图并支持点击下钻。
不适用:
- 纯组织架构、纯清单(没有先后顺序和判断分支)
- 需要精确到字段级的数据流图(该用 DFD 而不是 BPMN)
- 已经提供了 .bpmn 文件只需看图 —— 直接渲染,不要重新建模
可以但不必建模:手上已有排过版的 .bpmn,只想换一种排布 —— 直接把它喂给
lane_layout.py,会先剥掉旧 DI 再重排。但它只重排,不改内容;改内容要改 XML。
五步流水线
1. 提取
python3 scripts/extract_doc.py <docx文件或目录> <输出目录>
按 w:body 子元素顺序遍历 w:p / w:tbl,抽取「职责」到「相关程序」之间的章节。
批量处理时输出目录下每份一个 .txt 摘要,先看摘要再建模,不要读全文。
2. 分析:先判断,再动手(不可跳过)
禁止把文档步骤逐条直译成节点。 先做三件事,产出「建模方案」交给用户确认:
a) 判定意图 —— 用户到底要什么:
| 用户说法 | 真实意图 | 该做什么 |
|---|---|---|
| "把这份程序画成流程图" | 要一张能放进文件的图 | 建模 + 判断 A4 可读性 + 必要时拆图 |
| "这个流程有问题吗" | 要诊断 | 只跑分析,不出图 |
| "把 X 环节改清楚一点" | 要优化局部 | 读现状 → 指出职责/分支问题 → 改 |
| "帮我把两份程序的关系画出来" | 要跨程序视图 | 用 endEvent 引用收口,不展开下游 |
| "先出个草稿看看" | 要快速反馈 | 只出主干,标注未覆盖分支 |
b) 找文档没写清的地方 —— 这些必须在建模前提出来,不要自己脑补:
- 职责章节列了角色,但程序正文没说明某一步谁做
- 有"如适用""必要时"却没写判断依据
- 只有正常路径,没有异常/退回路径
- 同一动作在文档里出现在两个角色名下(职责重叠)
c) 提出优化建议 —— 不是照抄,而是给出更好的结构:
| 症状 | 建议 |
|---|---|
| 只有 1 个结束事件 | 指出缺异常出口,问程序里是否真的没有分支 |
| 网关出边没写条件 | 补条件,或指出原文没写清 |
| 文档写"如适用/必要时"却没给判断依据 | 补网关条件,或标注"原文未明确" |
| 同一动作出现在两个角色名下 | 明确归属,或拆成两个任务 |
| 某步是"检查/确认"类 | 建议做成网关而不是任务(检查本身不产生动作) |
| 步骤超过 15 个 | 提示 A4 不可读,建议按阶段拆图 |
不要报的:职责负载不均、某角色承担 >50% 任务、某角色只做 1 个动作。
实际程序里一个人干主要活是常态,不是缺陷。职责分布只作为观察信息展示,不作判定。
产出格式:先给方案(泳道清单 + 节点数 + 预计列数 + A4 字号 + 3~5 条优化建议),用户确认后再建模。
3. 建模
按确认后的方案手写 BPMN 2.0 XML,放到 <项目>/src/*.xml。
XML 只写模型(process / laneSet / flowNode / sequenceFlow),不写 DI —— 坐标由脚本算。
建模规则见 references/modeling.md,核心三条:
- 泳道取「第 4 章 职责」里列出的角色,不要自己发明
- 异常分支用 按 QP-xxx 处理 的 endEvent 收口,不要把下游程序画进来
- 任务名控制在 12 个汉字以内,否则 100px 节点框放不下
4. 排版与渲染
python3 scripts/build.py <src目录> <输出目录> [scale]
对每份 XML 依次做:泳道感知排版 → 出 .bpmn → 出 .png(默认 scale=2.2,约 300dpi)→ 质检表。
5. 两道校验(都要跑)
结构合规 —— bpmnlint(开源,MIT)
npm install -g bpmnlint # 一次即可
cd <输出目录> && printf '{"extends":"bpmnlint:recommended"}' > .bpmnlintrc
bpmnlint xxx.bpmn
查 BPMN 规范层面:隐式分支、假汇合、重复连线、缺开始/结束事件、元素无名称。
业务表达 —— analyze.py(本技能自带)
python3 scripts/analyze.py <输出目录>/*.bpmn
查职责分布、异常出口、死路/孤立节点、假汇合、命名长度,并跨文件统计角色出现频次。
两者的分工是互补的:bpmnlint 不看职责,analyze.py 不看 BPMN 规范细节。
实测中 analyze.py 比 bpmnlint 更严 —— 曾抓到一处 bpmnlint 放过、但确实是 fake-join 的连线。
验收标准:bpmnlint 0 problems + analyze.py 无「高」危项 + build.py 重叠/溢出为 0。
build.py 质检项对照:
| 症状 | 原因 | 修法 |
|---|---|---|
| 文字溢出 | 任务名太长 | 缩短到 12 字内,细节留给表单 |
| 节点重叠 | 同泳道同列多节点(脚本已自动堆叠) | 若仍重叠,检查是否漏了泳道归属 |
| 游离节点告警 | 节点没写进任何 flowNodeRef |
补进对应泳道 |
多程序整合(把子流程拼成全局图)
单份程序出图后,常需要一张「全局程序地图」。三种载体,按目的选:
| 目的 | 载体 | 说明 |
|---|---|---|
| 要能看/汇报(推荐) | 全局 top_level.bpmn + 下钻 HTML |
本技能自带,离线可用 |
| 要能真的执行流程 | SpiffWorkflow(Python)/ cibseven(Java) | 需搭运行环境,程序文件通常不需要 |
| 只要一张静态总览 | 全局 BPMN 渲染成 PNG | 最简单,但不能下钻 |
做法
1. 写全局 XML,用 callActivity 指向各子流程的 process id:
<bpmn:callActivity id="CA_111" name="QP-111 航材检验" calledElement="Process_QP111">
<bpmn:incoming>T03</bpmn:incoming>
<bpmn:outgoing>T04</bpmn:outgoing>
</bpmn:callActivity>
lane_layout.py / di2png.py / di2svg.py 均支持 callActivity(粗边框 + ⊞ 标记)。
2. 生成下钻页:
python3 scripts/make_drilldown.py 全局.bpmn <子流程目录> 总览.html
产物是单个离线 HTML:左边全局图,点 Call Activity 右边显示对应子流程
(优先 .svg,没有则用 .png)。不依赖服务器、不依赖 React Flow。
调用关系从哪来
程序正文里的交叉引用("按 QP-107 处理")就是调用关系的线索,可用
grep -oE 'QP-[0-9]{3}' <摘要> 批量挖出引用图。
但必须人工区分两类引用:
- 调用:异常/分支真的跳进另一个程序(如航材检验发现可疑件 → QP-109)→ 画成 callActivity
- 规范性引用:文件控制、记录控制、印章管理(QP-101/114 等)→ 只是"参照",不是流程调用,不要连成流程边
边界:谁先谁后、哪里并行、条件分支怎么接,仍需人工判断。没有工具能可靠地
"读一堆子流程文件自动推断完整拓扑"——这部分是业务判断,不是解析问题。
硬约束
这些是踩过的坑,不要重新试错:
- 不要用
bpmn-auto-layout排泳道图。它布局时完全不看 lane 归属,节点会按最长路径横铺、y 坐标随机散落。本技能的lane_layout.py自己算坐标。 BPMNPlane的bpmnElement必须指向collaboration(有池时),不是participant。指错 bpmn-js 直接打不开。- 泳道内同列多节点要纵向堆叠,泳道高度按实际行数动态算,否则两个分支节点会重叠。
- 池标题画在池的上方,不能画在池内左上角 —— 泳道矩形会把它盖住。
- docx 提取必须按 body 顺序遍历。直接
iter(w:p)会把表格单元格混进段落流。 - 章节切片取跨度最大的一对。目录里也有「职责 | Duties」和「相关程序」,紧挨着,不取最大跨度只切到 3 行。
- 路径传给 Windows 的 python 要用
C:/...,Git Bash 的/c/...会失败。 - 新增 BPMN 元素类型时,要改四个脚本的类型表:
lane_layout.py的FLOW_NODE_TAGS、
di2png.py的NODE_TAGS、di2svg.py里的元组、analyze.py的TASK_TAGS。
只改一处会出现「布局算对了但渲染器不画」「分析器少算节点」这类隐蔽错误 —— 加callActivity时踩过两次。
A4 可读性(交付前必算)
A4 横向可用宽约 270mm。换算:
A4 字号(pt) = 12 × (270 / 画布宽px) × 72 / 25.4
经验值:8pt 字号下最多容纳 7 列(列距 160px)。
build.py 已经算出每份的 A4 字号,直接看表。
| A4 字号 | 判定 | 处理 |
|---|---|---|
| ≥ 6.5pt | 可用 | 直接给 PNG |
| 5.5–6.5pt | 勉强 | 提示用户,或把次要例外改成图下脚注省一列 |
| < 5.5pt | 不可用 | 必须拆分 |
拆分原则:按业务阶段切,图与图之间用 endEvent 做交叉引用
(转授权批准(见图 2) / 转入正式授权批准(回图 2))。
拆分方案与实测数据见 references/a4-sizing.md。
交付格式
| 用途 | 给什么 |
|---|---|
| 屏幕看、评审、培训 | .png |
| 插 Word | .png(300dpi),不要给 SVG —— Word 部分版本会掉字体/走样 |
| 需要二次编辑 | .bpmn,拖进 https://demo.bpmn.io |
| 用户问"bpmn 怎么打开" | 直接给 PNG;.bpmn 是 XML 源文件,双击会用记事本打开 |
参考资料
references/modeling.md— 建模规则、泳道/网关选择、命名约定、常见错误references/a4-sizing.md— A4 字号换算、拆图方案、实测案例
脚本
| 脚本 | 作用 |
|---|---|
scripts/extract_doc.py |
docx → 章节摘要 |
scripts/analyze.py |
模型质量分析(职责分布 / 异常出口 / 结构缺陷) |
scripts/lane_layout.py |
XML → 带 DI 的 .bpmn(泳道感知排版) |
scripts/di2png.py |
.bpmn → PNG(Pillow 直画,不依赖浏览器) |
scripts/di2svg.py |
.bpmn → SVG(供网页/矢量场景) |
scripts/build.py |
批量:排版 + 渲染 + 质检表 |
scripts/make_drilldown.py |
全局 BPMN + 子流程图 → 可下钻的离线 HTML |
外部依赖:bpmnlint(Node,可选但建议装,用于结构合规校验)。
所有脚本只用 Python 标准库 + Pillow,不需要 Node、Docker 或浏览器。
di2png.py 依赖 C:/Windows/Fonts/msyh.ttc(微软雅黑);非 Windows 环境需改 FONT_PATH。