_bid_gen_figs2.py 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. # -*- coding: utf-8 -*-
  2. """附件五第 3~5 章、附件六第 1~4 章配图,风格与 _bid_gen_figs.py 一致。"""
  3. import os
  4. import sys
  5. sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
  6. sys.stdout.reconfigure(encoding="utf-8", errors="replace")
  7. from _bid_fig_kit import Fig, row # noqa: E402
  8. # ============================================================ 第3章 系统建设方案
  9. def fig_module_map():
  10. f = Fig("子系统功能模块分解与依赖关系", w=13.2, h=7.4,
  11. sub="S0 提供基准 · S1–S4 驱动计划与执行 · S5–S7 贴源回收实绩 · S8–S9 汇聚监控与决策")
  12. f.panel(3, 91, 94, 13.0, "决策与监控层", tone="purple")
  13. row(6, 87.0, 21.5, 7.4, 4, 2.4,
  14. ["S9 运营指标\n九宫格看板 · L1–L4 下钻",
  15. "S9 智慧诊断\n根因分析 · 改善闭环",
  16. "S9 ChatBI\n自然语言取数",
  17. "S8 异常监控\n看板 · 大屏 · 配置中心"], f, tone="purple", fs=8.0)
  18. f.panel(3, 76.0, 94, 15.5, "业务执行层(计划 → 执行 → 实绩)", tone="green")
  19. plan = row(6, 72.0, 13.6, 7.6, 3, 2.2,
  20. ["S1 产销协同\n评审 · 交付 · 发货",
  21. "S2 制造协同\n排程 · 日计划 · 追踪",
  22. "S3 供应协同\n需求计划 · 采购 · 齐套"], f, tone="green", fs=8.0)
  23. exe = row(53.4, 72.0, 13.6, 7.6, 3, 2.2,
  24. ["S4 采购执行\n交货 · 退货 · 门户",
  25. "S5 物料仓储\nS6 生产执行",
  26. "S7 成品仓储\n入库 · 发货 · FQC"], f, tone="green", fs=8.0)
  27. for a, b in zip(plan, plan[1:] + exe[:1]):
  28. f.arrow(a["r"], b["l"], lw=1.0)
  29. for a, b in zip(exe, exe[1:]):
  30. f.arrow(a["r"], b["l"], lw=1.0)
  31. f.note(6, 62.2, "计划下达 →                     "
  32. "           ← 实绩回收", fs=8.2)
  33. f.panel(3, 59.5, 94, 15.0, "基准数据层", tone="blue")
  34. row(6, 55.5, 17.4, 10.0, 5, 2.4,
  35. ["S0 销售建模\n客户 · 物料 · 评审周期",
  36. "S0 制造建模\nBOM · 工艺路线 · 产线",
  37. "S0 供应建模\n供应商 · 货源 · 前置期",
  38. "S0 质量建模\n检验项目 · 规范 · 抽样",
  39. "S0 仓储建模\n库位 · 单号 · 标签"], f, tone="blue", fs=8.0)
  40. f.panel(3, 42.5, 94, 13.5, "数据与集成层", tone="teal")
  41. row(6, 38.5, 17.4, 8.5, 5, 2.4,
  42. ["集成通道\n6 种接入方式",
  43. "数据中台\nSTG–STD–DWD–DWS",
  44. "指标计算\n日批 + 实时",
  45. "出站事务\nmdp_outbox 回写",
  46. "运行监控\n任务日志 · 血缘"], f, tone="teal", fs=8.0)
  47. f.panel(3, 27.0, 94, 12.5, "平台支撑层", tone="gray")
  48. row(6, 23.2, 13.6, 7.0, 6, 2.2,
  49. ["多租户与组织", "RBAC 与数据权限", "审批工作流",
  50. "任务调度", "消息与推送", "审计与日志"], f, tone="gray", fs=8.0)
  51. f.note(4, 11.0,
  52. "依赖原则:上层只依赖下层,同层之间通过领域服务接口交互,不直接读写对方数据表;"
  53. "S5–S7 对源系统只读,业务扩展状态写入平台扩展表,\n"
  54. "既保证源系统数据一致性,也使各子系统可独立升级而不相互破坏。",
  55. fs=8.4, color="#404040", va="top")
  56. f.crop(3)
  57. return f.save("fig_module_map")
  58. def fig_deploy():
  59. f = Fig("部署架构与环境划分", w=13.0, h=7.0,
  60. sub="容器化部署 · 四套环境隔离 · 关键组件冗余;支持信创软硬件环境适配")
  61. f.panel(3, 91, 30, 30, "外部接入区", tone="gray")
  62. row(5.5, 86.5, 25, 5.6, 4, 1.6,
  63. ["内网办公终端(浏览器)", "移动端(企业微信 / 钉钉)",
  64. "看板大屏一体机", "供应商门户(受限外网)"], f, tone="gray", fs=8.0)
  65. f.panel(35, 91, 30, 30, "DMZ 隔离区", tone="orange")
  66. nginx = f.box(37.5, 86.5, 25, 6.4, "Nginx 反向代理\nHTTPS 终结 · 负载均衡", tone="orange", fs=8.2)
  67. gw = f.box(37.5, 78.2, 25, 6.4, "API 网关\n统一鉴权 · 限流 · 审计", tone="orange", fs=8.2)
  68. waf = f.box(37.5, 69.9, 25, 6.0, "WAF / 防火墙策略", tone="orange", fs=8.2)
  69. f.arrow(nginx["b"], gw["t"])
  70. f.arrow(gw["b"], waf["t"])
  71. f.arrow((33.6, 82), (36.7, 82), lw=1.4)
  72. f.panel(67, 91, 30, 30, "应用服务区", tone="green")
  73. app = f.box(69.5, 86.5, 25, 6.4, "应用服务容器 ×N\n前端静态 + 后端 API", tone="green", fs=8.2)
  74. job = f.box(69.5, 78.2, 25, 6.4, "调度与集成执行容器\n日批 · 实时同步 · 指标计算", tone="green", fs=8.2)
  75. ai = f.box(69.5, 69.9, 25, 6.0, "AI 能力容器(诊断 / ChatBI)", tone="green", fs=8.2)
  76. f.arrow(app["b"], job["t"])
  77. f.arrow(job["b"], ai["t"])
  78. f.arrow((65.6, 82), (68.7, 82), lw=1.4)
  79. f.panel(20, 58, 60, 17.5, "数据服务区", tone="teal")
  80. row(23, 53.5, 17.5, 6.4, 3, 2.0,
  81. ["MySQL 8 主从\n业务库 + 数据中台库",
  82. "Redis\n缓存 · 会话 · 分布式锁",
  83. "对象存储 / 文件服务\n附件 · 报表 · 标签"], f, tone="teal", fs=8.0)
  84. f.note(23, 45.0, "备份:全量日备 + 增量归档,异地留存;主从切换与恢复每季度演练一次",
  85. fs=8.2, color="#404040")
  86. f.arrow((82, 74.5), (82, 58), lw=1.2)
  87. f.note(83, 66, "读写", fs=8.0)
  88. f.panel(3, 39.5, 45, 15.0, "源系统区(只读)", tone="blue")
  89. row(5.5, 35.5, 19.5, 5.4, 2, 1.5, ["ERP", "MES"], f, tone="blue", fs=8.6)
  90. row(5.5, 28.8, 19.5, 5.4, 2, 1.5, ["QMS / WMS", "SRM · CRM · OA · IoT"], f,
  91. tone="blue", fs=8.6)
  92. f.note(5.5, 21.5, "仅开放只读账号与指定视图;平台不直连生产库写入",
  93. fs=8.2, color="#404040")
  94. f.panel(52, 39.5, 45, 15.0, "环境划分", tone="purple")
  95. row(54.5, 35.5, 20, 5.4, 2, 1.5, ["开发环境 DEV", "测试环境 SIT"], f,
  96. tone="purple", fs=8.6)
  97. row(54.5, 28.8, 20, 5.4, 2, 1.5, ["验收环境 UAT", "生产环境 PROD"], f,
  98. tone="purple", fs=8.6)
  99. f.note(54.5, 21.5, "UAT 与 PROD 配置一致,数据脱敏同步;上线走 CI/CD 灰度与回滚",
  100. fs=8.2, color="#404040")
  101. f.note(4, 15.0,
  102. "可用性设计:应用与调度容器多实例部署,单实例故障自动摘除;数据库主从热备;"
  103. "全年可用率目标 ≥ 99%,重大故障力争 48 小时内恢复。\n"
  104. "信创适配:操作系统支持麒麟 / 统信,数据库支持达梦 / 人大金仓,"
  105. "中间件与浏览器均采用兼容方案,可按采购人实际环境选型。",
  106. fs=8.4, color="#404040", va="top")
  107. f.crop(3)
  108. return f.save("fig_deploy")
  109. def fig_data_layer():
  110. f = Fig("数据架构:分层模型、调度与血缘", w=13.2, h=6.8,
  111. sub="四层收敛 · 一处修改全局同步 · 每层可追溯到上游来源与下游用途")
  112. layers = [
  113. ("STG 贴源层", "与源系统表结构一一对应\n只增不改,保留原始快照",
  114. "blue", "按主键增量 + 每日全量校验"),
  115. ("STD 标准层", "字段标准化 · 编码统一\n空值过滤 · 异常值标记",
  116. "teal", "统一物料 / 客户 / 供应商编码"),
  117. ("DWD 明细宽表层", "按业务过程建模\n订单 · 工单 · 采购 · 库存流水",
  118. "green", "补全维度,形成可直接分析的明细"),
  119. ("DWS 汇总与指标层", "按主题与时间粒度汇总\nKPI 指标结果表",
  120. "purple", "指标口径唯一,看板与诊断共用"),
  121. ]
  122. y = 88
  123. prev = None
  124. for name, desc, tone, note in layers:
  125. b = f.box(6, y, 26, 11.0, f"{name}\n{desc}", tone=tone, fs=8.4)
  126. f.note(34, y - 5.5, note, fs=8.6, color="#404040")
  127. if prev:
  128. f.arrow(prev["b"], b["t"], lw=1.4)
  129. prev = b
  130. y -= 14.5
  131. f.panel(64, 91, 33, 44, "数据消费", tone="orange")
  132. for i, t in enumerate(["九宫格与部门看板", "指标下钻与明细证据",
  133. "智慧诊断与根因分析", "ChatBI 自然语言取数",
  134. "S8 异常规则计算", "对外数据接口"]):
  135. f.box(66.5, 87 - i * 6.6, 28, 5.2, t, tone="orange", fs=8.4)
  136. f.arrow(prev["r"], (65.5, prev["c"][1]), lw=1.4)
  137. f.panel(3, 42.5, 94, 14.5, "调度与治理", tone="gray")
  138. row(6, 38.5, 17.4, 8.5, 5, 2.4,
  139. ["调度策略\n日批凌晨 + 实时增量",
  140. "数据质量校验\n主键 · 非空 · 值域 · 对账",
  141. "血缘登记\n表级 + 字段级",
  142. "任务日志\n耗时 · 行数 · 失败原因",
  143. "重跑与补数\n断点续传 · 幂等写入"], f, tone="gray", fs=8.0)
  144. f.panel(3, 26.0, 94, 12.0, "主数据一致性", tone="red")
  145. row(6, 22.2, 21.5, 6.5, 4, 2.4,
  146. ["主数据在 S0 唯一维护", "变更触发全链路同步",
  147. "同步失败进入待处理队列", "口径变更留痕并通知"], f, tone="red", fs=8.2)
  148. f.note(4, 11.5,
  149. "指标一致性保证:同一指标只在 DWS 层计算一次,看板、诊断、报表、"
  150. "ChatBI 全部取自同一结果表,不允许各自重算;\n"
  151. "口径调整只改一处配置并留下变更记录,避免出现「同一个指标几个部门几个数」。",
  152. fs=8.4, color="#404040", va="top")
  153. f.crop(3)
  154. return f.save("fig_data_layer")
  155. def fig_api():
  156. f = Fig("接口设计与调用规范", w=13.0, h=6.6,
  157. sub="接口分四类 · 统一契约与错误码 · 全链路可追踪;新增一套第三方系统配置 ≤ 4 小时")
  158. cats = [
  159. ("数据采集类", "blue", ["数据库直连拉取", "HTTP / WebService 拉取",
  160. "FTP 文件解析", "MQ 消息订阅"]),
  161. ("数据回写类", "orange", ["异常处理结果回写", "检验结论回写",
  162. "计划变更通知", "回执核对与重试"]),
  163. ("对外服务类", "green", ["指标查询接口", "订单进度查询接口",
  164. "供应商门户接口", "移动端接口"]),
  165. ("平台内部类", "purple", ["领域服务接口", "任务调度接口",
  166. "诊断引擎接口", "AI 能力接口"]),
  167. ]
  168. x = 4
  169. for name, tone, items in cats:
  170. f.box(x, 89, 22, 6.0, name, tone=tone, fs=9.6)
  171. for i, t in enumerate(items):
  172. f.obox(x, 81.5 - i * 6.4, 22, 5.2, t, tone=tone, fs=8.4)
  173. x += 24
  174. f.panel(3, 52.0, 94, 16.0, "统一接口契约", tone="gray")
  175. row(6, 47.8, 21.5, 10.0, 4, 2.4,
  176. ["请求规范\nRESTful + JSON\n统一分页与筛选参数",
  177. "响应规范\ncode / message / data\n统一错误码字典",
  178. "安全规范\nOAuth2.0 / JWT 鉴权\n凭据加密托管 · 传输 TLS",
  179. "可观测规范\n全链路 TraceId\n耗时与失败率埋点"], f, tone="gray", fs=8.2)
  180. f.panel(3, 34.0, 94, 14.0, "调用可靠性设计", tone="red")
  181. row(6, 30.0, 17.4, 8.0, 5, 2.4,
  182. ["超时与重试\n指数退避", "幂等键\n重复调用不重复入库",
  183. "限流与熔断\n保护源系统", "失败补偿\n进入待处理队列",
  184. "版本管理\n接口变更向后兼容"], f, tone="red", fs=8.0)
  185. f.note(4, 17.0,
  186. "边界约定:外部系统超时或不可用导致的延迟不计入本平台性能指标,但平台须记录、告警并在恢复后自动补齐;"
  187. "接口文档随版本同步更新并纳入交付物,\n"
  188. "采购人可凭接口清单核对每一条集成需求的落地情况。",
  189. fs=8.4, color="#404040", va="top")
  190. f.crop(6)
  191. return f.save("fig_api")
  192. def fig_diag_arch():
  193. f = Fig("智能诊断集成架构", w=13.0, h=6.6,
  194. sub="诊断引擎独立于业务模块,通过数据与事件双通道接入;诊断结论回流业务形成闭环")
  195. f.panel(3, 91, 25, 46, "输入", tone="blue")
  196. ins = []
  197. for i, t in enumerate(["DWS 指标结果表", "DWD 明细宽表",
  198. "S8 异常事件流", "业务动作日志",
  199. "阈值与规则配置"]):
  200. ins.append(f.box(5.5, 86.5 - i * 8.4, 20, 6.6, t, tone="blue", fs=8.4))
  201. f.panel(30, 91, 38, 46, "诊断引擎", tone="red")
  202. e1 = f.box(32.5, 86.5, 33, 7.0, "问题识别\n阈值触发 · 趋势突变 · 同环比异常", tone="red", fs=8.4)
  203. e2 = f.box(32.5, 77.0, 33, 7.0, "根因定位\n维度下钻 · 链路回溯 · 规则匹配", tone="red", fs=8.4)
  204. e3 = f.box(32.5, 67.5, 33, 7.0, "影响评估\n涉及订单 · 金额 · 交期风险", tone="red", fs=8.4)
  205. e4 = f.box(32.5, 58.0, 33, 7.0, "改善建议\n措施库匹配 · 责任岗位推荐", tone="red", fs=8.4)
  206. for a, b in zip([e1, e2, e3], [e2, e3, e4]):
  207. f.arrow(a["b"], b["t"])
  208. for s in ins:
  209. f.arrow(s["r"], (31.6, s["c"][1]), lw=0.9)
  210. f.panel(70, 91, 27, 46, "输出与回流", tone="green")
  211. outs = []
  212. for i, t in enumerate(["诊断报告与结论卡片", "改善任务派单",
  213. "看板异常标记", "预警推送(企微 / 钉钉)",
  214. "回写业务系统"]):
  215. outs.append(f.box(72.5, 86.5 - i * 8.4, 22, 6.6, t, tone="green", fs=8.4))
  216. f.arrow(e4["r"], (71.6, e4["c"][1]), lw=1.4)
  217. f.arrow(outs[-1]["b"], (83.5, 43), lw=1.2, dashed=True)
  218. f.note(5, 41.0, "闭环回流:改善任务执行结果与指标复测数据重新进入诊断引擎,"
  219. "验证措施是否有效,未通过验证不允许关闭",
  220. fs=8.4, color="#404040")
  221. f.panel(3, 37.0, 94, 15.0, "运行时序与时效", tone="gray")
  222. row(6, 33.0, 21.5, 9.0, 4, 2.4,
  223. ["实时类\n事件驱动\n识别 ≤ 3 分钟",
  224. "周期类\n每日凌晨批量\n随指标计算完成",
  225. "人工触发\n按订单 / 产线即时诊断",
  226. "定期复盘\n周报 / 月报自动生成"], f, tone="gray", fs=8.2)
  227. f.note(4, 19.0,
  228. "解耦设计:诊断引擎不直接访问业务模块数据表,只消费数据中台的标准结果与事件;"
  229. "诊断规则、阈值、维度与措施库全部为配置项,\n"
  230. "新增一类诊断场景由实施人员配置完成,不需要修改代码,"
  231. "这是平台能够在不同企业间复用的关键。",
  232. fs=8.4, color="#404040", va="top")
  233. f.crop(5)
  234. return f.save("fig_diag_arch")
  235. # ======================================================== 第4章 智能诊断与分析
  236. def fig_diag_model():
  237. f = Fig("智能诊断模型三层设计", w=13.0, h=6.8,
  238. sub="规则层保证可解释 · 统计层发现隐性异常 · 知识层沉淀经验;三层结论合并输出")
  239. f.panel(3, 91, 94, 22.0, "第一层:规则模型(确定性问题)", tone="blue")
  240. row(6, 86.5, 17.4, 8.0, 5, 2.4,
  241. ["阈值规则\n指标低于目标值",
  242. "时效规则\n节点滞后 10% / 30%",
  243. "齐套规则\n缺料且无在途",
  244. "一致性规则\n账实不符 · 单据不平",
  245. "组合规则\n多条件与或非"], f, tone="blue", fs=8.0)
  246. f.note(6, 76.0, "输出:明确的问题事件 + 触发依据(哪条规则、哪个数值、超出多少),"
  247. "业务人员可直接复核,不存在「说不清为什么报警」",
  248. fs=8.2, color="#404040")
  249. f.panel(3, 68.0, 94, 22.0, "第二层:统计模型(隐性问题)", tone="teal")
  250. row(6, 63.5, 17.4, 8.0, 5, 2.4,
  251. ["趋势突变检测\n移动均值偏离",
  252. "同环比异常\n季节性修正",
  253. "离群点识别\n箱线图 / Z 分数",
  254. "相关性分析\n指标间联动",
  255. "分布漂移\n结构性变化"], f, tone="teal", fs=8.0)
  256. f.note(6, 53.0, "输出:尚未越过阈值但走势值得关注的预警,附统计依据与置信度,"
  257. "定位为「提示」而非「结论」,交由业务判断",
  258. fs=8.2, color="#404040")
  259. f.panel(3, 45.0, 94, 22.0, "第三层:知识模型(根因与措施)", tone="purple")
  260. row(6, 40.5, 17.4, 8.0, 5, 2.4,
  261. ["根因树\n问题→原因分层展开",
  262. "链路回溯\n沿订单链定位断点",
  263. "历史案例匹配\n相似问题与处置",
  264. "措施库\n原因→措施→责任岗位",
  265. "效果反馈\n措施有效性积累"], f, tone="purple", fs=8.0)
  266. f.note(6, 30.0, "输出:根因判断 + 建议措施 + 建议责任岗位;措施执行效果回流后调整推荐权重,"
  267. "使系统随使用逐步贴合企业实际",
  268. fs=8.2, color="#404040")
  269. f.note(4, 24.0,
  270. "模型可解释性原则:所有诊断结论必须能回答「依据什么数据、经过什么判断、结论是什么」三个问题,"
  271. "并可下钻到明细单据;\n"
  272. "不采用无法解释的黑盒判定作为管理决策依据,"
  273. "这是制造业运营诊断能够被业务部门接受并落地执行的前提。",
  274. fs=8.4, color="#404040", va="top")
  275. f.crop(9)
  276. return f.save("fig_diag_model")
  277. def fig_auto_closedloop():
  278. f = Fig("业务闭环自动化策略", w=13.2, h=6.4,
  279. sub="从异常触发到验证关闭全程系统驱动;超时自动升级,未验证不得关闭")
  280. steps = [
  281. ("① 触发", "规则命中 / 统计预警\n人工提报", "blue"),
  282. ("② 定级", "一般 · 重要 · 紧急\n按场景配置", "teal"),
  283. ("③ 派单", "按责任矩阵自动指派\n通知责任人", "green"),
  284. ("④ 处置", "填报原因与措施\n上传证据", "gold"),
  285. ("⑤ 验证", "指标复测 / 现场确认\n验证人签署", "orange"),
  286. ("⑥ 关闭", "归档并沉淀案例\n更新措施库", "purple"),
  287. ]
  288. boxes = []
  289. for i, (t, d, tone) in enumerate(steps):
  290. boxes.append(f.box(4 + i * 15.6, 88, 14.0, 12.0, f"{t}\n{d}",
  291. tone=tone, fs=8.2))
  292. for a, b in zip(boxes, boxes[1:]):
  293. f.arrow(a["r"], b["l"], lw=1.3)
  294. f.arrow(boxes[4]["b"], (boxes[4]["c"][0], 68), lw=1.2, dashed=True)
  295. f.elbow((boxes[4]["c"][0], 68), boxes[3]["b"], mid=68, dashed=True)
  296. f.note(50, 65.5, "验证不通过 → 退回重新处置", fs=8.4, color="#C00000", ha="center")
  297. f.panel(3, 60.0, 45, 22.0, "自动升级机制", tone="red")
  298. for i, t in enumerate(["紧急异常 1 小时未响应 → 通知上级",
  299. "重要异常 4 小时未响应 → 通知上级",
  300. "一般异常 24 小时未响应 → 通知上级",
  301. "超时两级仍未处置 → 进入管理层看板"]):
  302. f.obox(5.5, 55.5 - i * 5.2, 40, 4.4, t, tone="red", fs=8.2)
  303. f.panel(51, 60.0, 46, 22.0, "自动化动作", tone="green")
  304. for i, t in enumerate(["自动生成改善任务并关联异常事件",
  305. "自动按责任矩阵指派责任人与协办人",
  306. "自动推送企业微信 / 钉钉并附处理链接",
  307. "自动复测指标并判定验证结果"]):
  308. f.obox(53.5, 55.5 - i * 5.2, 41, 4.4, t, tone="green", fs=8.2)
  309. f.panel(3, 35.0, 94, 15.0, "闭环质量看板", tone="gray")
  310. row(6, 31.0, 17.4, 9.0, 5, 2.4,
  311. ["异常发现数\n按类型与来源",
  312. "响应及时率\n首次响应时长",
  313. "闭环率\n已验证关闭占比",
  314. "平均闭环时长\n分级统计",
  315. "复发率\n同类问题再发生"], f, tone="gray", fs=8.0)
  316. f.note(4, 18.0,
  317. "设计意图:把「谁在什么时候必须做什么」写进系统而不是写进制度文件。"
  318. "异常一旦产生就有明确责任人、明确时限与明确升级路径,\n"
  319. "闭环质量本身也成为可考核的指标,"
  320. "使运营改善由依赖个人推动转为依赖机制运转。",
  321. fs=8.4, color="#404040", va="top")
  322. f.crop(6)
  323. return f.save("fig_auto_closedloop")
  324. def fig_metric_model():
  325. f = Fig("数据分析建模:指标体系与维度模型", w=13.0, h=6.6,
  326. sub="指标分四级逐层支撑 · 维度统一定义 · 一个指标一处计算")
  327. f.panel(3, 91, 52, 46, "指标体系(四级)", tone="purple")
  328. lv = [
  329. ("L1 战略层", "订单交付及时率 · 生产计划完成率\n库存周转率 · 质量合格率"),
  330. ("L2 战术层", "分客户 / 产品 / 产线的达成情况\n趋势与同环比"),
  331. ("L3 诊断层", "瓶颈工序 · 缺料原因分布\n供应商交付表现 · 异常类型构成"),
  332. ("L4 明细层", "具体订单 · 工单 · 采购单\n检验记录与库存流水"),
  333. ]
  334. prev = None
  335. for i, (n, d) in enumerate(lv):
  336. b = f.box(5.5, 86.5 - i * 11.0, 47, 9.0, f"{n} {d}", tone="purple", fs=8.2)
  337. if prev:
  338. f.arrow(prev["b"], b["t"])
  339. prev = b
  340. f.panel(58, 91, 39, 46, "维度模型(星型)", tone="teal")
  341. fact = f.box(70, 72, 16, 9.0, "事实表\n业务过程度量", tone="teal", fs=8.6)
  342. dims = [("时间", 62, 88), ("组织", 84, 88), ("客户", 60, 62),
  343. ("物料", 86, 62), ("供应商", 71, 52)]
  344. for t, x, y in dims:
  345. d = f.box(x, y, 12, 5.4, t, tone="blue", fs=8.6)
  346. f.arrow(d["c"], fact["c"], lw=0.9, style="-")
  347. f.note(59.5, 45.0, "维度一处定义、全部指标共用,避免各看板口径不一",
  348. fs=8.2, color="#404040")
  349. f.panel(3, 42.0, 94, 15.0, "建模与计算规范", tone="gray")
  350. row(6, 38.0, 17.4, 9.0, 5, 2.4,
  351. ["指标卡片\n定义 · 公式 · 单位\n目标值 · 责任岗位",
  352. "计算频次\n日批 / 实时\n补数与重跑规则",
  353. "口径版本\n变更留痕\n历史可追溯",
  354. "数据质量\n完整性 · 及时性\n准确性对账",
  355. "权限控制\n按组织与角色\n控制可见范围"], f, tone="gray", fs=8.0)
  356. f.note(4, 25.0,
  357. "每个指标在系统中都有一张指标卡片,写明业务定义、计算公式、数据来源、"
  358. "统计口径、目标值与责任岗位,并在看板上可直接查看。\n"
  359. "指标口径由采购人在项目实施的口径确认环节逐项签署确认,"
  360. "作为后续验收与考核的共同基准,避免上线后反复争论「这个数怎么算的」。",
  361. fs=8.4, color="#404040", va="top")
  362. f.crop(6)
  363. return f.save("fig_metric_model")
  364. # ==================================================== 第5章 UAT 测试与质量保障
  365. def fig_test_v():
  366. f = Fig("测试体系与阶段交付物", w=13.0, h=6.6,
  367. sub="需求—设计—开发左侧每一层,都有右侧对应层级的验证活动")
  368. left = [("需求确认", "业务需求 REQ-Sn-nnn"),
  369. ("功能设计", "功能设计 FUNC-Sn-nnn"),
  370. ("技术设计", "接口 · 数据模型 · 指标口径"),
  371. ("配置与开发", "主数据 · 通道 · 看板配置")]
  372. right = [("UAT 验收测试", "业务场景端到端,采购人主导"),
  373. ("系统集成测试", "跨模块链路与集成通道"),
  374. ("接口与数据测试", "接口契约 · 指标口径核对"),
  375. ("单元与配置自测", "开发自测 · 配置项检查")]
  376. for i, (n, d) in enumerate(left):
  377. f.box(5, 88 - i * 11.5, 26, 9.0, f"{n}\n{d}", tone="blue", fs=8.4)
  378. for i, (n, d) in enumerate(right):
  379. f.box(69, 88 - i * 11.5, 26, 9.0, f"{n}\n{d}", tone="green", fs=8.4)
  380. for i in range(4):
  381. y = 83.5 - i * 11.5
  382. f.arrow((31.5, y), (68.5, y), style="<|-|>", lw=1.0, dashed=True)
  383. f.note(50, y + 2.0, ["验收基准", "链路基准", "契约基准", "配置基准"][i],
  384. fs=8.0, ha="center")
  385. f.panel(3, 41.0, 94, 16.0, "测试类型覆盖", tone="teal")
  386. row(6, 37.0, 17.4, 10.0, 5, 2.4,
  387. ["功能测试\n167 项功能逐项验证",
  388. "业务场景测试\nOTD 端到端主线",
  389. "诊断模型验证\n历史数据回溯比对",
  390. "性能测试\n1000 在线 / 400 并发",
  391. "安全测试\n权限 · 越权 · 漏洞扫描"], f, tone="teal", fs=8.0)
  392. f.panel(3, 23.0, 94, 13.0, "阶段交付物", tone="gray")
  393. row(6, 19.2, 17.4, 7.0, 5, 2.4,
  394. ["测试计划与方案", "测试用例集 TC-Sn-nnn", "测试执行记录",
  395. "缺陷清单与处理记录", "测试报告与验收结论"], f, tone="gray", fs=8.0)
  396. f.note(4, 9.5,
  397. "三联对照贯穿始终:业务需求 REQ、功能设计 FUNC、测试用例 TC 一一对应,"
  398. "验收时按编号逐项核对,需求是否实现、由哪个功能实现、\n"
  399. "由哪条用例验证、验证结果如何,全部有据可查。",
  400. fs=8.4, color="#404040", va="top")
  401. f.crop(2)
  402. return f.save("fig_test_v")
  403. def fig_defect_flow():
  404. f = Fig("缺陷管理流程与分级标准", w=13.0, h=5.8,
  405. sub="统一缺陷库 · 分级响应 · 修复必回归;遗留缺陷须双方确认方可上线")
  406. steps = ["提出\n描述 · 复现步骤\n截图 · 环境",
  407. "确认\n分级 · 指派\n判定重复与非缺陷",
  408. "修复\n定位 · 修改\n自测通过",
  409. "回归\n用例复测\n关联场景验证",
  410. "关闭\n提出人确认\n归档"]
  411. tones = ["blue", "teal", "gold", "orange", "green"]
  412. bs = []
  413. for i, (t, tone) in enumerate(zip(steps, tones)):
  414. bs.append(f.box(4 + i * 18.8, 88, 17.5, 12.0, t, tone=tone, fs=8.2))
  415. for a, b in zip(bs, bs[1:]):
  416. f.arrow(a["r"], b["l"], lw=1.3)
  417. f.arrow(bs[3]["b"], (bs[3]["c"][0], 68), lw=1.2, dashed=True)
  418. f.elbow((bs[3]["c"][0], 68), bs[2]["b"], mid=68, dashed=True)
  419. f.note(56, 65.5, "回归不通过 → 退回修复", fs=8.4, color="#C00000", ha="center")
  420. f.panel(3, 60.0, 94, 26.0, "缺陷分级与响应时限", tone="red")
  421. hdr = ["级别", "判定标准", "响应时限", "修复时限"]
  422. rows = [
  423. ["致命", "主流程不可用 · 数据错误或丢失 · 系统不可访问", "≤ 1 小时", "≤ 24 小时"],
  424. ["严重", "主要功能不可用且无替代操作 · 指标口径错误", "≤ 4 小时", "≤ 3 个工作日"],
  425. ["一般", "次要功能异常或有替代操作 · 交互不合理", "≤ 1 个工作日", "≤ 5 个工作日"],
  426. ["轻微", "文字 · 样式 · 提示语等不影响业务的问题", "≤ 2 个工作日", "版本内解决"],
  427. ]
  428. ws = [10, 46, 18, 18]
  429. x = 5.5
  430. for w, t in zip(ws, hdr):
  431. f.box(x, 55.5, w, 5.0, t, tone="red", fs=8.8)
  432. x += w + 0.6
  433. for r, data in enumerate(rows):
  434. x = 5.5
  435. for w, t in zip(ws, data):
  436. f.obox(x, 49.5 - r * 5.6, w, 5.0, t, tone="red", fs=8.2)
  437. x += w + 0.6
  438. f.note(4, 22.0,
  439. "上线门槛:致命与严重缺陷必须全部关闭;一般缺陷关闭率不低于 95%;"
  440. "剩余轻微缺陷列入遗留清单,明确责任人与解决版本,\n"
  441. "经采购人书面确认后方可上线。缺陷统计(发现数、修复率、"
  442. "重开率、平均修复时长)纳入项目周报,过程可见。",
  443. fs=8.4, color="#404040", va="top")
  444. f.crop(6)
  445. return f.save("fig_defect_flow")
  446. # ============================================== 附件六 第1章 质量与工期保障
  447. def fig_quality_gate():
  448. f = Fig("实施里程碑与质量控制点", w=13.2, h=6.2,
  449. sub="每个阶段设置准入准出条件 · 关键节点由采购人签署确认 · 未通过不进入下一阶段")
  450. ms = [("M1\n项目启动", "blue"), ("M2\n方案确认", "teal"),
  451. ("M3\n配置实现", "green"), ("M4\n集成联调", "gold"),
  452. ("M5\nUAT 验收", "orange"), ("M6\n上线运行", "purple"),
  453. ("M7\n结项交付", "red")]
  454. bs = []
  455. for i, (t, tone) in enumerate(ms):
  456. bs.append(f.box(3 + i * 13.9, 90, 12.6, 9.0, t, tone=tone, fs=8.8))
  457. for a, b in zip(bs, bs[1:]):
  458. f.arrow(a["r"], b["l"], lw=1.3)
  459. gates = [
  460. "范围与\n计划确认书", "蓝图与口径\n确认书", "配置自检\n通过报告",
  461. "集成通道\n联调记录", "UAT 报告\n与验收结论", "上线检查单\n与回滚预案",
  462. "交付物清单\n签收单",
  463. ]
  464. for i, g in enumerate(gates):
  465. b = f.obox(3 + i * 13.9, 76, 12.6, 9.5, g, tone="gray", fs=8.0)
  466. f.arrow(bs[i]["b"], b["t"], lw=1.0)
  467. f.note(50, 64.0, "▲ 以上七项均为采购人签署确认的质量控制点",
  468. fs=8.6, color="#C00000", ha="center")
  469. f.panel(3, 60.0, 46, 25.0, "质量保障措施", tone="green")
  470. for i, t in enumerate(["需求与口径逐项书面确认,变更走评审",
  471. "配置与代码双人复核,关键改动留痕",
  472. "每周质量例会,缺陷与风险逐项过",
  473. "阶段准出自检清单,不合格不移交",
  474. "交付物按模板统一,版本受控"]):
  475. f.obox(5.5, 55.5 - i * 4.8, 41, 4.2, t, tone="green", fs=8.2)
  476. f.panel(52, 60.0, 45, 25.0, "工期保障措施", tone="orange")
  477. for i, t in enumerate(["平台功能已建成,实施以配置为主",
  478. "关键路径识别,资源优先保障",
  479. "并行推进:主数据与集成通道同步开展",
  480. "周计划滚动跟踪,偏差当周纠偏",
  481. "关键岗位设置备份人员"]):
  482. f.obox(54.5, 55.5 - i * 4.8, 40, 4.2, t, tone="orange", fs=8.2)
  483. f.note(4, 32.0,
  484. "承诺:项目自合同签订之日起按采购需求约定工期完成开发、部署、测试与验收;"
  485. "因我方原因导致工期延误,按合同约定承担违约责任。\n"
  486. "上线后继续提供质量保障与技术服务,服务内容与响应时效见附件六第 3 章。",
  487. fs=8.4, color="#404040", va="top")
  488. f.crop(16)
  489. return f.save("fig_quality_gate")
  490. # ==================================================== 附件六 第2章 应急支撑
  491. def fig_emergency():
  492. f = Fig("应急响应机制与安全保障", w=13.0, h=6.4,
  493. sub="三级事件分级 · 明确响应与恢复时限 · 漏洞按等级限时修复")
  494. f.panel(3, 91, 94, 30.0, "事件分级与响应", tone="red")
  495. hdr = ["级别", "情形", "响应时限", "恢复目标", "升级路径"]
  496. rows = [
  497. ["一级\n重大", "系统不可访问 · 核心数据异常\n影响全部用户", "≤ 30 分钟",
  498. "力争 ≤ 8 小时\n最长 48 小时", "项目经理 +\n技术负责人 + 我方管理层"],
  499. ["二级\n较大", "主要功能不可用 · 集成通道中断\n影响部门级业务", "≤ 1 小时",
  500. "≤ 24 小时", "项目经理 +\n技术负责人"],
  501. ["三级\n一般", "次要功能异常 · 单点数据问题\n有替代操作", "≤ 4 小时",
  502. "≤ 3 个工作日", "项目经理"],
  503. ]
  504. ws = [10, 27, 14, 17, 22]
  505. x = 5.5
  506. for w, t in zip(ws, hdr):
  507. f.box(x, 86.5, w, 5.0, t, tone="red", fs=8.8)
  508. x += w + 0.6
  509. for r, data in enumerate(rows):
  510. x = 5.5
  511. for w, t in zip(ws, data):
  512. f.obox(x, 80.5 - r * 8.0, w, 7.4, t, tone="red", fs=8.0)
  513. x += w + 0.6
  514. f.panel(3, 58.0, 45, 24.0, "应急处置流程", tone="orange")
  515. for i, t in enumerate(["① 接报与确认(值班电话 / 工单 / 监控告警)",
  516. "② 定级与启动响应,通知责任人与采购人",
  517. "③ 应急处置:先恢复可用,再定位根因",
  518. "④ 恢复验证:功能与数据双向核对",
  519. "⑤ 复盘报告:原因 · 措施 · 防止再发"]):
  520. f.obox(5.5, 53.5 - i * 4.6, 40, 4.0, t, tone="orange", fs=8.2)
  521. f.panel(51, 58.0, 46, 24.0, "漏洞修复机制", tone="purple")
  522. for i, t in enumerate(["定期漏洞扫描与依赖组件版本巡检",
  523. "高危漏洞:24 小时内提交修复方案",
  524. "中危漏洞:5 个工作日内修复",
  525. "低危漏洞:随版本迭代修复",
  526. "修复后回归验证并出具修复说明"]):
  527. f.obox(53.5, 53.5 - i * 4.6, 41, 4.0, t, tone="purple", fs=8.2)
  528. f.panel(3, 32.0, 94, 15.0, "安全保障措施", tone="teal")
  529. row(6, 28.0, 17.4, 9.0, 5, 2.4,
  530. ["访问控制\nRBAC + 数据权限\n最小权限原则",
  531. "传输与存储\nTLS 传输\n敏感字段加密",
  532. "数据备份\n全量日备 + 增量\n异地留存",
  533. "审计留痕\n登录 · 操作 · 数据变更\n日志不可篡改",
  534. "演练机制\n每季度备份恢复\n与故障切换演练"], f, tone="teal", fs=8.0)
  535. f.note(4, 15.0,
  536. "值班安排:提供 7×24 小时应急联系渠道,重大事件不受工作时间限制;"
  537. "应急联系人与备份联系人在项目启动会上书面提交采购人,\n"
  538. "变更时即时更新。所有应急事件均形成记录并纳入月度服务报告。",
  539. fs=8.4, color="#404040", va="top")
  540. f.crop(3)
  541. return f.save("fig_emergency")
  542. # ==================================================== 附件六 第3章 售后服务
  543. def fig_service():
  544. f = Fig("售后服务体系与响应时效", w=13.0, h=6.2,
  545. sub="三级支持 · 多渠道受理 · 含诊断模型持续优化承诺")
  546. f.panel(3, 91, 94, 22.0, "三级技术支持体系", tone="blue")
  547. lv = [("一线支持\n服务热线 · 在线工单\n常见问题与操作指导", "响应 ≤ 30 分钟"),
  548. ("二线支持\n项目实施与配置工程师\n配置调整 · 数据核查", "响应 ≤ 2 小时"),
  549. ("三线支持\n产品研发团队\n程序缺陷 · 版本修复", "响应 ≤ 4 小时")]
  550. bs = []
  551. for i, (t, r) in enumerate(lv):
  552. b = f.box(6 + i * 30.5, 86.5, 28, 11.0, t, tone="blue", fs=8.4)
  553. f.note(6 + i * 30.5 + 14, 73.5, r, fs=8.6, color="#C00000", ha="center")
  554. bs.append(b)
  555. for a, b in zip(bs, bs[1:]):
  556. f.arrow(a["r"], b["l"], lw=1.1)
  557. f.note(50, 70.5, "一线无法解决即升级,升级过程对用户透明,不需用户重复描述问题",
  558. fs=8.2, color="#404040", ha="center")
  559. f.panel(3, 67.0, 45, 22.0, "服务内容", tone="green")
  560. for i, t in enumerate(["系统运行巡检与状态报告",
  561. "配置调整与主数据维护支持",
  562. "缺陷修复与版本升级",
  563. "指标口径与看板调整",
  564. "数据备份与恢复支持",
  565. "月度服务报告与季度回顾"]):
  566. f.obox(5.5, 62.5 - i * 4.4, 40, 3.8, t, tone="green", fs=8.2)
  567. f.panel(51, 67.0, 46, 22.0, "诊断模型持续优化", tone="purple")
  568. for i, t in enumerate(["季度回顾诊断准确率与误报率",
  569. "按实际处置结果校准规则与阈值",
  570. "补充新增异常场景与措施库",
  571. "更新根因树与责任矩阵",
  572. "优化结果书面报采购人确认",
  573. "服务期内模型优化不额外收费"]):
  574. f.obox(53.5, 62.5 - i * 4.4, 41, 3.8, t, tone="purple", fs=8.2)
  575. f.panel(3, 41.0, 94, 15.0, "受理渠道与服务承诺", tone="orange")
  576. row(6, 37.0, 17.4, 9.0, 5, 2.4,
  577. ["服务热线\n工作日 8:30–17:30\n重大事件 7×24",
  578. "在线工单\n全天受理\n状态可查",
  579. "企业微信 / 钉钉\n即时沟通群\n项目经理在群",
  580. "远程支持\n远程协助排查",
  581. "现场支持\n重大问题按需到场"], f, tone="orange", fs=8.0)
  582. f.note(4, 24.0,
  583. "服务期:系统正式上线验收合格之日起提供免费质保与技术服务,"
  584. "期限按合同约定执行;质保期内不因缺陷修复、版本升级、\n"
  585. "指标口径调整与诊断模型优化另行收费。质保期满后可续签服务协议,"
  586. "服务内容与响应时效不低于质保期标准。",
  587. fs=8.4, color="#404040", va="top")
  588. f.crop(11)
  589. return f.save("fig_service")
  590. # ==================================================== 附件六 第4章 项目培训
  591. def fig_training():
  592. f = Fig("培训体系与实施安排", w=13.0, h=6.4,
  593. sub="分角色分层培训 · 训后考核 · 配套手册与录像,人员流动后可自主复训")
  594. f.panel(3, 91, 94, 30.0, "五类培训对象与内容", tone="blue")
  595. cols = [
  596. ("系统管理员", "teal", ["组织与用户权限管理", "主数据维护与批量导入",
  597. "集成通道配置与监控", "备份恢复与日常巡检"], "2 天 · 8 课时"),
  598. ("业务操作员", "green", ["本岗位功能操作实操", "单据流转与异常处理",
  599. "查询与报表导出", "移动端使用"], "3 天 · 12 课时"),
  600. ("智能诊断与\n数据分析专项", "purple", ["指标口径与看板下钻", "诊断结论判读与复核",
  601. "改善任务闭环操作", "ChatBI 取数与自助分析"],
  602. "2 天 · 8 课时"),
  603. ("开发与\n运维人员", "blue", ["架构与数据模型讲解", "接口与二次开发",
  604. "部署升级与回滚", "监控告警与故障排查"], "2 天 · 8 课时"),
  605. ("管理层专项", "orange", ["九宫格看板判读", "异常与预警关注要点",
  606. "运营例会用法", "移动端决策看板"], "0.5 天 · 2 课时"),
  607. ]
  608. for i, (name, tone, items, dur) in enumerate(cols):
  609. x = 5.5 + i * 18.4
  610. f.box(x, 86.5, 17.0, 7.5, name, tone=tone, fs=8.4)
  611. for j, t in enumerate(items):
  612. f.obox(x, 77.5 - j * 5.2, 17.0, 4.6, t, tone=tone, fs=7.6)
  613. f.note(x + 8.5, 54.5, dur, fs=8.2, color="#C00000", ha="center")
  614. f.panel(3, 50.0, 45, 20.0, "培训实施方式", tone="orange")
  615. for i, t in enumerate(["集中授课 + 系统实操,一人一机",
  616. "使用采购人真实业务数据演练",
  617. "关键岗位一对一辅导",
  618. "上线后一周现场跟班支持"]):
  619. f.obox(5.5, 45.5 - i * 4.6, 40, 4.0, t, tone="gold", fs=8.2)
  620. f.panel(51, 50.0, 46, 20.0, "培训成果与考核", tone="green")
  621. for i, t in enumerate(["训后考核,不合格安排补训",
  622. "培训签到表与考核记录归档",
  623. "培训满意度评价并纳入验收",
  624. "关键岗位培养内部种子讲师"]):
  625. f.obox(53.5, 45.5 - i * 4.6, 41, 4.0, t, tone="green", fs=8.2)
  626. f.panel(3, 27.0, 94, 13.0, "培训资料(随项目交付)", tone="gray")
  627. row(6, 23.2, 17.4, 7.0, 5, 2.4,
  628. ["系统管理员手册", "业务操作手册(分岗位)",
  629. "智能诊断与数据分析手册", "常见问题手册(FAQ)",
  630. "培训录像与课件"], f, tone="gray", fs=8.0)
  631. f.note(4, 13.0,
  632. "培训手册按岗位编写,以实际操作截图配步骤说明,不用抽象术语描述界面;"
  633. "培训录像随手册一并交付,人员流动后新人可自主学习,\n"
  634. "不必依赖我方重复到场培训——这是系统能够在采购人内部长期自主运行的基础。",
  635. fs=8.4, color="#404040", va="top")
  636. f.crop(3)
  637. return f.save("fig_training")
  638. FIGS = [fig_module_map, fig_deploy, fig_data_layer, fig_api, fig_diag_arch,
  639. fig_diag_model, fig_auto_closedloop, fig_metric_model,
  640. fig_test_v, fig_defect_flow,
  641. fig_quality_gate, fig_emergency, fig_service, fig_training]
  642. if __name__ == "__main__":
  643. for fn in FIGS:
  644. fn()
  645. print(f"\n共生成 {len(FIGS)} 张配图")