# -*- coding: utf-8 -*- """附件五第 3~5 章、附件六第 1~4 章配图,风格与 _bid_gen_figs.py 一致。""" import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.stdout.reconfigure(encoding="utf-8", errors="replace") from _bid_fig_kit import Fig, row # noqa: E402 # ============================================================ 第3章 系统建设方案 def fig_module_map(): f = Fig("子系统功能模块分解与依赖关系", w=13.2, h=7.4, sub="S0 提供基准 · S1–S4 驱动计划与执行 · S5–S7 贴源回收实绩 · S8–S9 汇聚监控与决策") f.panel(3, 91, 94, 13.0, "决策与监控层", tone="purple") row(6, 87.0, 21.5, 7.4, 4, 2.4, ["S9 运营指标\n九宫格看板 · L1–L4 下钻", "S9 智慧诊断\n根因分析 · 改善闭环", "S9 ChatBI\n自然语言取数", "S8 异常监控\n看板 · 大屏 · 配置中心"], f, tone="purple", fs=8.0) f.panel(3, 76.0, 94, 15.5, "业务执行层(计划 → 执行 → 实绩)", tone="green") plan = row(6, 72.0, 13.6, 7.6, 3, 2.2, ["S1 产销协同\n评审 · 交付 · 发货", "S2 制造协同\n排程 · 日计划 · 追踪", "S3 供应协同\n需求计划 · 采购 · 齐套"], f, tone="green", fs=8.0) exe = row(53.4, 72.0, 13.6, 7.6, 3, 2.2, ["S4 采购执行\n交货 · 退货 · 门户", "S5 物料仓储\nS6 生产执行", "S7 成品仓储\n入库 · 发货 · FQC"], f, tone="green", fs=8.0) for a, b in zip(plan, plan[1:] + exe[:1]): f.arrow(a["r"], b["l"], lw=1.0) for a, b in zip(exe, exe[1:]): f.arrow(a["r"], b["l"], lw=1.0) f.note(6, 62.2, "计划下达 →                     " "           ← 实绩回收", fs=8.2) f.panel(3, 59.5, 94, 15.0, "基准数据层", tone="blue") row(6, 55.5, 17.4, 10.0, 5, 2.4, ["S0 销售建模\n客户 · 物料 · 评审周期", "S0 制造建模\nBOM · 工艺路线 · 产线", "S0 供应建模\n供应商 · 货源 · 前置期", "S0 质量建模\n检验项目 · 规范 · 抽样", "S0 仓储建模\n库位 · 单号 · 标签"], f, tone="blue", fs=8.0) f.panel(3, 42.5, 94, 13.5, "数据与集成层", tone="teal") row(6, 38.5, 17.4, 8.5, 5, 2.4, ["集成通道\n6 种接入方式", "数据中台\nSTG–STD–DWD–DWS", "指标计算\n日批 + 实时", "出站事务\nmdp_outbox 回写", "运行监控\n任务日志 · 血缘"], f, tone="teal", fs=8.0) f.panel(3, 27.0, 94, 12.5, "平台支撑层", tone="gray") row(6, 23.2, 13.6, 7.0, 6, 2.2, ["多租户与组织", "RBAC 与数据权限", "审批工作流", "任务调度", "消息与推送", "审计与日志"], f, tone="gray", fs=8.0) f.note(4, 11.0, "依赖原则:上层只依赖下层,同层之间通过领域服务接口交互,不直接读写对方数据表;" "S5–S7 对源系统只读,业务扩展状态写入平台扩展表,\n" "既保证源系统数据一致性,也使各子系统可独立升级而不相互破坏。", fs=8.4, color="#404040", va="top") f.crop(3) return f.save("fig_module_map") def fig_deploy(): f = Fig("部署架构与环境划分", w=13.0, h=7.0, sub="容器化部署 · 四套环境隔离 · 关键组件冗余;支持信创软硬件环境适配") f.panel(3, 91, 30, 30, "外部接入区", tone="gray") row(5.5, 86.5, 25, 5.6, 4, 1.6, ["内网办公终端(浏览器)", "移动端(企业微信 / 钉钉)", "看板大屏一体机", "供应商门户(受限外网)"], f, tone="gray", fs=8.0) f.panel(35, 91, 30, 30, "DMZ 隔离区", tone="orange") nginx = f.box(37.5, 86.5, 25, 6.4, "Nginx 反向代理\nHTTPS 终结 · 负载均衡", tone="orange", fs=8.2) gw = f.box(37.5, 78.2, 25, 6.4, "API 网关\n统一鉴权 · 限流 · 审计", tone="orange", fs=8.2) waf = f.box(37.5, 69.9, 25, 6.0, "WAF / 防火墙策略", tone="orange", fs=8.2) f.arrow(nginx["b"], gw["t"]) f.arrow(gw["b"], waf["t"]) f.arrow((33.6, 82), (36.7, 82), lw=1.4) f.panel(67, 91, 30, 30, "应用服务区", tone="green") app = f.box(69.5, 86.5, 25, 6.4, "应用服务容器 ×N\n前端静态 + 后端 API", tone="green", fs=8.2) job = f.box(69.5, 78.2, 25, 6.4, "调度与集成执行容器\n日批 · 实时同步 · 指标计算", tone="green", fs=8.2) ai = f.box(69.5, 69.9, 25, 6.0, "AI 能力容器(诊断 / ChatBI)", tone="green", fs=8.2) f.arrow(app["b"], job["t"]) f.arrow(job["b"], ai["t"]) f.arrow((65.6, 82), (68.7, 82), lw=1.4) f.panel(20, 58, 60, 17.5, "数据服务区", tone="teal") row(23, 53.5, 17.5, 6.4, 3, 2.0, ["MySQL 8 主从\n业务库 + 数据中台库", "Redis\n缓存 · 会话 · 分布式锁", "对象存储 / 文件服务\n附件 · 报表 · 标签"], f, tone="teal", fs=8.0) f.note(23, 45.0, "备份:全量日备 + 增量归档,异地留存;主从切换与恢复每季度演练一次", fs=8.2, color="#404040") f.arrow((82, 74.5), (82, 58), lw=1.2) f.note(83, 66, "读写", fs=8.0) f.panel(3, 39.5, 45, 15.0, "源系统区(只读)", tone="blue") row(5.5, 35.5, 19.5, 5.4, 2, 1.5, ["ERP", "MES"], f, tone="blue", fs=8.6) row(5.5, 28.8, 19.5, 5.4, 2, 1.5, ["QMS / WMS", "SRM · CRM · OA · IoT"], f, tone="blue", fs=8.6) f.note(5.5, 21.5, "仅开放只读账号与指定视图;平台不直连生产库写入", fs=8.2, color="#404040") f.panel(52, 39.5, 45, 15.0, "环境划分", tone="purple") row(54.5, 35.5, 20, 5.4, 2, 1.5, ["开发环境 DEV", "测试环境 SIT"], f, tone="purple", fs=8.6) row(54.5, 28.8, 20, 5.4, 2, 1.5, ["验收环境 UAT", "生产环境 PROD"], f, tone="purple", fs=8.6) f.note(54.5, 21.5, "UAT 与 PROD 配置一致,数据脱敏同步;上线走 CI/CD 灰度与回滚", fs=8.2, color="#404040") f.note(4, 15.0, "可用性设计:应用与调度容器多实例部署,单实例故障自动摘除;数据库主从热备;" "全年可用率目标 ≥ 99%,重大故障力争 48 小时内恢复。\n" "信创适配:操作系统支持麒麟 / 统信,数据库支持达梦 / 人大金仓," "中间件与浏览器均采用兼容方案,可按采购人实际环境选型。", fs=8.4, color="#404040", va="top") f.crop(3) return f.save("fig_deploy") def fig_data_layer(): f = Fig("数据架构:分层模型、调度与血缘", w=13.2, h=6.8, sub="四层收敛 · 一处修改全局同步 · 每层可追溯到上游来源与下游用途") layers = [ ("STG 贴源层", "与源系统表结构一一对应\n只增不改,保留原始快照", "blue", "按主键增量 + 每日全量校验"), ("STD 标准层", "字段标准化 · 编码统一\n空值过滤 · 异常值标记", "teal", "统一物料 / 客户 / 供应商编码"), ("DWD 明细宽表层", "按业务过程建模\n订单 · 工单 · 采购 · 库存流水", "green", "补全维度,形成可直接分析的明细"), ("DWS 汇总与指标层", "按主题与时间粒度汇总\nKPI 指标结果表", "purple", "指标口径唯一,看板与诊断共用"), ] y = 88 prev = None for name, desc, tone, note in layers: b = f.box(6, y, 26, 11.0, f"{name}\n{desc}", tone=tone, fs=8.4) f.note(34, y - 5.5, note, fs=8.6, color="#404040") if prev: f.arrow(prev["b"], b["t"], lw=1.4) prev = b y -= 14.5 f.panel(64, 91, 33, 44, "数据消费", tone="orange") for i, t in enumerate(["九宫格与部门看板", "指标下钻与明细证据", "智慧诊断与根因分析", "ChatBI 自然语言取数", "S8 异常规则计算", "对外数据接口"]): f.box(66.5, 87 - i * 6.6, 28, 5.2, t, tone="orange", fs=8.4) f.arrow(prev["r"], (65.5, prev["c"][1]), lw=1.4) f.panel(3, 42.5, 94, 14.5, "调度与治理", tone="gray") row(6, 38.5, 17.4, 8.5, 5, 2.4, ["调度策略\n日批凌晨 + 实时增量", "数据质量校验\n主键 · 非空 · 值域 · 对账", "血缘登记\n表级 + 字段级", "任务日志\n耗时 · 行数 · 失败原因", "重跑与补数\n断点续传 · 幂等写入"], f, tone="gray", fs=8.0) f.panel(3, 26.0, 94, 12.0, "主数据一致性", tone="red") row(6, 22.2, 21.5, 6.5, 4, 2.4, ["主数据在 S0 唯一维护", "变更触发全链路同步", "同步失败进入待处理队列", "口径变更留痕并通知"], f, tone="red", fs=8.2) f.note(4, 11.5, "指标一致性保证:同一指标只在 DWS 层计算一次,看板、诊断、报表、" "ChatBI 全部取自同一结果表,不允许各自重算;\n" "口径调整只改一处配置并留下变更记录,避免出现「同一个指标几个部门几个数」。", fs=8.4, color="#404040", va="top") f.crop(3) return f.save("fig_data_layer") def fig_api(): f = Fig("接口设计与调用规范", w=13.0, h=6.6, sub="接口分四类 · 统一契约与错误码 · 全链路可追踪;新增一套第三方系统配置 ≤ 4 小时") cats = [ ("数据采集类", "blue", ["数据库直连拉取", "HTTP / WebService 拉取", "FTP 文件解析", "MQ 消息订阅"]), ("数据回写类", "orange", ["异常处理结果回写", "检验结论回写", "计划变更通知", "回执核对与重试"]), ("对外服务类", "green", ["指标查询接口", "订单进度查询接口", "供应商门户接口", "移动端接口"]), ("平台内部类", "purple", ["领域服务接口", "任务调度接口", "诊断引擎接口", "AI 能力接口"]), ] x = 4 for name, tone, items in cats: f.box(x, 89, 22, 6.0, name, tone=tone, fs=9.6) for i, t in enumerate(items): f.obox(x, 81.5 - i * 6.4, 22, 5.2, t, tone=tone, fs=8.4) x += 24 f.panel(3, 52.0, 94, 16.0, "统一接口契约", tone="gray") row(6, 47.8, 21.5, 10.0, 4, 2.4, ["请求规范\nRESTful + JSON\n统一分页与筛选参数", "响应规范\ncode / message / data\n统一错误码字典", "安全规范\nOAuth2.0 / JWT 鉴权\n凭据加密托管 · 传输 TLS", "可观测规范\n全链路 TraceId\n耗时与失败率埋点"], f, tone="gray", fs=8.2) f.panel(3, 34.0, 94, 14.0, "调用可靠性设计", tone="red") row(6, 30.0, 17.4, 8.0, 5, 2.4, ["超时与重试\n指数退避", "幂等键\n重复调用不重复入库", "限流与熔断\n保护源系统", "失败补偿\n进入待处理队列", "版本管理\n接口变更向后兼容"], f, tone="red", fs=8.0) f.note(4, 17.0, "边界约定:外部系统超时或不可用导致的延迟不计入本平台性能指标,但平台须记录、告警并在恢复后自动补齐;" "接口文档随版本同步更新并纳入交付物,\n" "采购人可凭接口清单核对每一条集成需求的落地情况。", fs=8.4, color="#404040", va="top") f.crop(6) return f.save("fig_api") def fig_diag_arch(): f = Fig("智能诊断集成架构", w=13.0, h=6.6, sub="诊断引擎独立于业务模块,通过数据与事件双通道接入;诊断结论回流业务形成闭环") f.panel(3, 91, 25, 46, "输入", tone="blue") ins = [] for i, t in enumerate(["DWS 指标结果表", "DWD 明细宽表", "S8 异常事件流", "业务动作日志", "阈值与规则配置"]): ins.append(f.box(5.5, 86.5 - i * 8.4, 20, 6.6, t, tone="blue", fs=8.4)) f.panel(30, 91, 38, 46, "诊断引擎", tone="red") e1 = f.box(32.5, 86.5, 33, 7.0, "问题识别\n阈值触发 · 趋势突变 · 同环比异常", tone="red", fs=8.4) e2 = f.box(32.5, 77.0, 33, 7.0, "根因定位\n维度下钻 · 链路回溯 · 规则匹配", tone="red", fs=8.4) e3 = f.box(32.5, 67.5, 33, 7.0, "影响评估\n涉及订单 · 金额 · 交期风险", tone="red", fs=8.4) e4 = f.box(32.5, 58.0, 33, 7.0, "改善建议\n措施库匹配 · 责任岗位推荐", tone="red", fs=8.4) for a, b in zip([e1, e2, e3], [e2, e3, e4]): f.arrow(a["b"], b["t"]) for s in ins: f.arrow(s["r"], (31.6, s["c"][1]), lw=0.9) f.panel(70, 91, 27, 46, "输出与回流", tone="green") outs = [] for i, t in enumerate(["诊断报告与结论卡片", "改善任务派单", "看板异常标记", "预警推送(企微 / 钉钉)", "回写业务系统"]): outs.append(f.box(72.5, 86.5 - i * 8.4, 22, 6.6, t, tone="green", fs=8.4)) f.arrow(e4["r"], (71.6, e4["c"][1]), lw=1.4) f.arrow(outs[-1]["b"], (83.5, 43), lw=1.2, dashed=True) f.note(5, 41.0, "闭环回流:改善任务执行结果与指标复测数据重新进入诊断引擎," "验证措施是否有效,未通过验证不允许关闭", fs=8.4, color="#404040") f.panel(3, 37.0, 94, 15.0, "运行时序与时效", tone="gray") row(6, 33.0, 21.5, 9.0, 4, 2.4, ["实时类\n事件驱动\n识别 ≤ 3 分钟", "周期类\n每日凌晨批量\n随指标计算完成", "人工触发\n按订单 / 产线即时诊断", "定期复盘\n周报 / 月报自动生成"], f, tone="gray", fs=8.2) f.note(4, 19.0, "解耦设计:诊断引擎不直接访问业务模块数据表,只消费数据中台的标准结果与事件;" "诊断规则、阈值、维度与措施库全部为配置项,\n" "新增一类诊断场景由实施人员配置完成,不需要修改代码," "这是平台能够在不同企业间复用的关键。", fs=8.4, color="#404040", va="top") f.crop(5) return f.save("fig_diag_arch") # ======================================================== 第4章 智能诊断与分析 def fig_diag_model(): f = Fig("智能诊断模型三层设计", w=13.0, h=6.8, sub="规则层保证可解释 · 统计层发现隐性异常 · 知识层沉淀经验;三层结论合并输出") f.panel(3, 91, 94, 22.0, "第一层:规则模型(确定性问题)", tone="blue") row(6, 86.5, 17.4, 8.0, 5, 2.4, ["阈值规则\n指标低于目标值", "时效规则\n节点滞后 10% / 30%", "齐套规则\n缺料且无在途", "一致性规则\n账实不符 · 单据不平", "组合规则\n多条件与或非"], f, tone="blue", fs=8.0) f.note(6, 76.0, "输出:明确的问题事件 + 触发依据(哪条规则、哪个数值、超出多少)," "业务人员可直接复核,不存在「说不清为什么报警」", fs=8.2, color="#404040") f.panel(3, 68.0, 94, 22.0, "第二层:统计模型(隐性问题)", tone="teal") row(6, 63.5, 17.4, 8.0, 5, 2.4, ["趋势突变检测\n移动均值偏离", "同环比异常\n季节性修正", "离群点识别\n箱线图 / Z 分数", "相关性分析\n指标间联动", "分布漂移\n结构性变化"], f, tone="teal", fs=8.0) f.note(6, 53.0, "输出:尚未越过阈值但走势值得关注的预警,附统计依据与置信度," "定位为「提示」而非「结论」,交由业务判断", fs=8.2, color="#404040") f.panel(3, 45.0, 94, 22.0, "第三层:知识模型(根因与措施)", tone="purple") row(6, 40.5, 17.4, 8.0, 5, 2.4, ["根因树\n问题→原因分层展开", "链路回溯\n沿订单链定位断点", "历史案例匹配\n相似问题与处置", "措施库\n原因→措施→责任岗位", "效果反馈\n措施有效性积累"], f, tone="purple", fs=8.0) f.note(6, 30.0, "输出:根因判断 + 建议措施 + 建议责任岗位;措施执行效果回流后调整推荐权重," "使系统随使用逐步贴合企业实际", fs=8.2, color="#404040") f.note(4, 24.0, "模型可解释性原则:所有诊断结论必须能回答「依据什么数据、经过什么判断、结论是什么」三个问题," "并可下钻到明细单据;\n" "不采用无法解释的黑盒判定作为管理决策依据," "这是制造业运营诊断能够被业务部门接受并落地执行的前提。", fs=8.4, color="#404040", va="top") f.crop(9) return f.save("fig_diag_model") def fig_auto_closedloop(): f = Fig("业务闭环自动化策略", w=13.2, h=6.4, sub="从异常触发到验证关闭全程系统驱动;超时自动升级,未验证不得关闭") steps = [ ("① 触发", "规则命中 / 统计预警\n人工提报", "blue"), ("② 定级", "一般 · 重要 · 紧急\n按场景配置", "teal"), ("③ 派单", "按责任矩阵自动指派\n通知责任人", "green"), ("④ 处置", "填报原因与措施\n上传证据", "gold"), ("⑤ 验证", "指标复测 / 现场确认\n验证人签署", "orange"), ("⑥ 关闭", "归档并沉淀案例\n更新措施库", "purple"), ] boxes = [] for i, (t, d, tone) in enumerate(steps): boxes.append(f.box(4 + i * 15.6, 88, 14.0, 12.0, f"{t}\n{d}", tone=tone, fs=8.2)) for a, b in zip(boxes, boxes[1:]): f.arrow(a["r"], b["l"], lw=1.3) f.arrow(boxes[4]["b"], (boxes[4]["c"][0], 68), lw=1.2, dashed=True) f.elbow((boxes[4]["c"][0], 68), boxes[3]["b"], mid=68, dashed=True) f.note(50, 65.5, "验证不通过 → 退回重新处置", fs=8.4, color="#C00000", ha="center") f.panel(3, 60.0, 45, 22.0, "自动升级机制", tone="red") for i, t in enumerate(["紧急异常 1 小时未响应 → 通知上级", "重要异常 4 小时未响应 → 通知上级", "一般异常 24 小时未响应 → 通知上级", "超时两级仍未处置 → 进入管理层看板"]): f.obox(5.5, 55.5 - i * 5.2, 40, 4.4, t, tone="red", fs=8.2) f.panel(51, 60.0, 46, 22.0, "自动化动作", tone="green") for i, t in enumerate(["自动生成改善任务并关联异常事件", "自动按责任矩阵指派责任人与协办人", "自动推送企业微信 / 钉钉并附处理链接", "自动复测指标并判定验证结果"]): f.obox(53.5, 55.5 - i * 5.2, 41, 4.4, t, tone="green", fs=8.2) f.panel(3, 35.0, 94, 15.0, "闭环质量看板", tone="gray") row(6, 31.0, 17.4, 9.0, 5, 2.4, ["异常发现数\n按类型与来源", "响应及时率\n首次响应时长", "闭环率\n已验证关闭占比", "平均闭环时长\n分级统计", "复发率\n同类问题再发生"], f, tone="gray", fs=8.0) f.note(4, 18.0, "设计意图:把「谁在什么时候必须做什么」写进系统而不是写进制度文件。" "异常一旦产生就有明确责任人、明确时限与明确升级路径,\n" "闭环质量本身也成为可考核的指标," "使运营改善由依赖个人推动转为依赖机制运转。", fs=8.4, color="#404040", va="top") f.crop(6) return f.save("fig_auto_closedloop") def fig_metric_model(): f = Fig("数据分析建模:指标体系与维度模型", w=13.0, h=6.6, sub="指标分四级逐层支撑 · 维度统一定义 · 一个指标一处计算") f.panel(3, 91, 52, 46, "指标体系(四级)", tone="purple") lv = [ ("L1 战略层", "订单交付及时率 · 生产计划完成率\n库存周转率 · 质量合格率"), ("L2 战术层", "分客户 / 产品 / 产线的达成情况\n趋势与同环比"), ("L3 诊断层", "瓶颈工序 · 缺料原因分布\n供应商交付表现 · 异常类型构成"), ("L4 明细层", "具体订单 · 工单 · 采购单\n检验记录与库存流水"), ] prev = None for i, (n, d) in enumerate(lv): b = f.box(5.5, 86.5 - i * 11.0, 47, 9.0, f"{n} {d}", tone="purple", fs=8.2) if prev: f.arrow(prev["b"], b["t"]) prev = b f.panel(58, 91, 39, 46, "维度模型(星型)", tone="teal") fact = f.box(70, 72, 16, 9.0, "事实表\n业务过程度量", tone="teal", fs=8.6) dims = [("时间", 62, 88), ("组织", 84, 88), ("客户", 60, 62), ("物料", 86, 62), ("供应商", 71, 52)] for t, x, y in dims: d = f.box(x, y, 12, 5.4, t, tone="blue", fs=8.6) f.arrow(d["c"], fact["c"], lw=0.9, style="-") f.note(59.5, 45.0, "维度一处定义、全部指标共用,避免各看板口径不一", fs=8.2, color="#404040") f.panel(3, 42.0, 94, 15.0, "建模与计算规范", tone="gray") row(6, 38.0, 17.4, 9.0, 5, 2.4, ["指标卡片\n定义 · 公式 · 单位\n目标值 · 责任岗位", "计算频次\n日批 / 实时\n补数与重跑规则", "口径版本\n变更留痕\n历史可追溯", "数据质量\n完整性 · 及时性\n准确性对账", "权限控制\n按组织与角色\n控制可见范围"], f, tone="gray", fs=8.0) f.note(4, 25.0, "每个指标在系统中都有一张指标卡片,写明业务定义、计算公式、数据来源、" "统计口径、目标值与责任岗位,并在看板上可直接查看。\n" "指标口径由采购人在项目实施的口径确认环节逐项签署确认," "作为后续验收与考核的共同基准,避免上线后反复争论「这个数怎么算的」。", fs=8.4, color="#404040", va="top") f.crop(6) return f.save("fig_metric_model") # ==================================================== 第5章 UAT 测试与质量保障 def fig_test_v(): f = Fig("测试体系与阶段交付物", w=13.0, h=6.6, sub="需求—设计—开发左侧每一层,都有右侧对应层级的验证活动") left = [("需求确认", "业务需求 REQ-Sn-nnn"), ("功能设计", "功能设计 FUNC-Sn-nnn"), ("技术设计", "接口 · 数据模型 · 指标口径"), ("配置与开发", "主数据 · 通道 · 看板配置")] right = [("UAT 验收测试", "业务场景端到端,采购人主导"), ("系统集成测试", "跨模块链路与集成通道"), ("接口与数据测试", "接口契约 · 指标口径核对"), ("单元与配置自测", "开发自测 · 配置项检查")] for i, (n, d) in enumerate(left): f.box(5, 88 - i * 11.5, 26, 9.0, f"{n}\n{d}", tone="blue", fs=8.4) for i, (n, d) in enumerate(right): f.box(69, 88 - i * 11.5, 26, 9.0, f"{n}\n{d}", tone="green", fs=8.4) for i in range(4): y = 83.5 - i * 11.5 f.arrow((31.5, y), (68.5, y), style="<|-|>", lw=1.0, dashed=True) f.note(50, y + 2.0, ["验收基准", "链路基准", "契约基准", "配置基准"][i], fs=8.0, ha="center") f.panel(3, 41.0, 94, 16.0, "测试类型覆盖", tone="teal") row(6, 37.0, 17.4, 10.0, 5, 2.4, ["功能测试\n167 项功能逐项验证", "业务场景测试\nOTD 端到端主线", "诊断模型验证\n历史数据回溯比对", "性能测试\n1000 在线 / 400 并发", "安全测试\n权限 · 越权 · 漏洞扫描"], f, tone="teal", fs=8.0) f.panel(3, 23.0, 94, 13.0, "阶段交付物", tone="gray") row(6, 19.2, 17.4, 7.0, 5, 2.4, ["测试计划与方案", "测试用例集 TC-Sn-nnn", "测试执行记录", "缺陷清单与处理记录", "测试报告与验收结论"], f, tone="gray", fs=8.0) f.note(4, 9.5, "三联对照贯穿始终:业务需求 REQ、功能设计 FUNC、测试用例 TC 一一对应," "验收时按编号逐项核对,需求是否实现、由哪个功能实现、\n" "由哪条用例验证、验证结果如何,全部有据可查。", fs=8.4, color="#404040", va="top") f.crop(2) return f.save("fig_test_v") def fig_defect_flow(): f = Fig("缺陷管理流程与分级标准", w=13.0, h=5.8, sub="统一缺陷库 · 分级响应 · 修复必回归;遗留缺陷须双方确认方可上线") steps = ["提出\n描述 · 复现步骤\n截图 · 环境", "确认\n分级 · 指派\n判定重复与非缺陷", "修复\n定位 · 修改\n自测通过", "回归\n用例复测\n关联场景验证", "关闭\n提出人确认\n归档"] tones = ["blue", "teal", "gold", "orange", "green"] bs = [] for i, (t, tone) in enumerate(zip(steps, tones)): bs.append(f.box(4 + i * 18.8, 88, 17.5, 12.0, t, tone=tone, fs=8.2)) for a, b in zip(bs, bs[1:]): f.arrow(a["r"], b["l"], lw=1.3) f.arrow(bs[3]["b"], (bs[3]["c"][0], 68), lw=1.2, dashed=True) f.elbow((bs[3]["c"][0], 68), bs[2]["b"], mid=68, dashed=True) f.note(56, 65.5, "回归不通过 → 退回修复", fs=8.4, color="#C00000", ha="center") f.panel(3, 60.0, 94, 26.0, "缺陷分级与响应时限", tone="red") hdr = ["级别", "判定标准", "响应时限", "修复时限"] rows = [ ["致命", "主流程不可用 · 数据错误或丢失 · 系统不可访问", "≤ 1 小时", "≤ 24 小时"], ["严重", "主要功能不可用且无替代操作 · 指标口径错误", "≤ 4 小时", "≤ 3 个工作日"], ["一般", "次要功能异常或有替代操作 · 交互不合理", "≤ 1 个工作日", "≤ 5 个工作日"], ["轻微", "文字 · 样式 · 提示语等不影响业务的问题", "≤ 2 个工作日", "版本内解决"], ] ws = [10, 46, 18, 18] x = 5.5 for w, t in zip(ws, hdr): f.box(x, 55.5, w, 5.0, t, tone="red", fs=8.8) x += w + 0.6 for r, data in enumerate(rows): x = 5.5 for w, t in zip(ws, data): f.obox(x, 49.5 - r * 5.6, w, 5.0, t, tone="red", fs=8.2) x += w + 0.6 f.note(4, 22.0, "上线门槛:致命与严重缺陷必须全部关闭;一般缺陷关闭率不低于 95%;" "剩余轻微缺陷列入遗留清单,明确责任人与解决版本,\n" "经采购人书面确认后方可上线。缺陷统计(发现数、修复率、" "重开率、平均修复时长)纳入项目周报,过程可见。", fs=8.4, color="#404040", va="top") f.crop(6) return f.save("fig_defect_flow") # ============================================== 附件六 第1章 质量与工期保障 def fig_quality_gate(): f = Fig("实施里程碑与质量控制点", w=13.2, h=6.2, sub="每个阶段设置准入准出条件 · 关键节点由采购人签署确认 · 未通过不进入下一阶段") ms = [("M1\n项目启动", "blue"), ("M2\n方案确认", "teal"), ("M3\n配置实现", "green"), ("M4\n集成联调", "gold"), ("M5\nUAT 验收", "orange"), ("M6\n上线运行", "purple"), ("M7\n结项交付", "red")] bs = [] for i, (t, tone) in enumerate(ms): bs.append(f.box(3 + i * 13.9, 90, 12.6, 9.0, t, tone=tone, fs=8.8)) for a, b in zip(bs, bs[1:]): f.arrow(a["r"], b["l"], lw=1.3) gates = [ "范围与\n计划确认书", "蓝图与口径\n确认书", "配置自检\n通过报告", "集成通道\n联调记录", "UAT 报告\n与验收结论", "上线检查单\n与回滚预案", "交付物清单\n签收单", ] for i, g in enumerate(gates): b = f.obox(3 + i * 13.9, 76, 12.6, 9.5, g, tone="gray", fs=8.0) f.arrow(bs[i]["b"], b["t"], lw=1.0) f.note(50, 64.0, "▲ 以上七项均为采购人签署确认的质量控制点", fs=8.6, color="#C00000", ha="center") f.panel(3, 60.0, 46, 25.0, "质量保障措施", tone="green") for i, t in enumerate(["需求与口径逐项书面确认,变更走评审", "配置与代码双人复核,关键改动留痕", "每周质量例会,缺陷与风险逐项过", "阶段准出自检清单,不合格不移交", "交付物按模板统一,版本受控"]): f.obox(5.5, 55.5 - i * 4.8, 41, 4.2, t, tone="green", fs=8.2) f.panel(52, 60.0, 45, 25.0, "工期保障措施", tone="orange") for i, t in enumerate(["平台功能已建成,实施以配置为主", "关键路径识别,资源优先保障", "并行推进:主数据与集成通道同步开展", "周计划滚动跟踪,偏差当周纠偏", "关键岗位设置备份人员"]): f.obox(54.5, 55.5 - i * 4.8, 40, 4.2, t, tone="orange", fs=8.2) f.note(4, 32.0, "承诺:项目自合同签订之日起按采购需求约定工期完成开发、部署、测试与验收;" "因我方原因导致工期延误,按合同约定承担违约责任。\n" "上线后继续提供质量保障与技术服务,服务内容与响应时效见附件六第 3 章。", fs=8.4, color="#404040", va="top") f.crop(16) return f.save("fig_quality_gate") # ==================================================== 附件六 第2章 应急支撑 def fig_emergency(): f = Fig("应急响应机制与安全保障", w=13.0, h=6.4, sub="三级事件分级 · 明确响应与恢复时限 · 漏洞按等级限时修复") f.panel(3, 91, 94, 30.0, "事件分级与响应", tone="red") hdr = ["级别", "情形", "响应时限", "恢复目标", "升级路径"] rows = [ ["一级\n重大", "系统不可访问 · 核心数据异常\n影响全部用户", "≤ 30 分钟", "力争 ≤ 8 小时\n最长 48 小时", "项目经理 +\n技术负责人 + 我方管理层"], ["二级\n较大", "主要功能不可用 · 集成通道中断\n影响部门级业务", "≤ 1 小时", "≤ 24 小时", "项目经理 +\n技术负责人"], ["三级\n一般", "次要功能异常 · 单点数据问题\n有替代操作", "≤ 4 小时", "≤ 3 个工作日", "项目经理"], ] ws = [10, 27, 14, 17, 22] x = 5.5 for w, t in zip(ws, hdr): f.box(x, 86.5, w, 5.0, t, tone="red", fs=8.8) x += w + 0.6 for r, data in enumerate(rows): x = 5.5 for w, t in zip(ws, data): f.obox(x, 80.5 - r * 8.0, w, 7.4, t, tone="red", fs=8.0) x += w + 0.6 f.panel(3, 58.0, 45, 24.0, "应急处置流程", tone="orange") for i, t in enumerate(["① 接报与确认(值班电话 / 工单 / 监控告警)", "② 定级与启动响应,通知责任人与采购人", "③ 应急处置:先恢复可用,再定位根因", "④ 恢复验证:功能与数据双向核对", "⑤ 复盘报告:原因 · 措施 · 防止再发"]): f.obox(5.5, 53.5 - i * 4.6, 40, 4.0, t, tone="orange", fs=8.2) f.panel(51, 58.0, 46, 24.0, "漏洞修复机制", tone="purple") for i, t in enumerate(["定期漏洞扫描与依赖组件版本巡检", "高危漏洞:24 小时内提交修复方案", "中危漏洞:5 个工作日内修复", "低危漏洞:随版本迭代修复", "修复后回归验证并出具修复说明"]): f.obox(53.5, 53.5 - i * 4.6, 41, 4.0, t, tone="purple", fs=8.2) f.panel(3, 32.0, 94, 15.0, "安全保障措施", tone="teal") row(6, 28.0, 17.4, 9.0, 5, 2.4, ["访问控制\nRBAC + 数据权限\n最小权限原则", "传输与存储\nTLS 传输\n敏感字段加密", "数据备份\n全量日备 + 增量\n异地留存", "审计留痕\n登录 · 操作 · 数据变更\n日志不可篡改", "演练机制\n每季度备份恢复\n与故障切换演练"], f, tone="teal", fs=8.0) f.note(4, 15.0, "值班安排:提供 7×24 小时应急联系渠道,重大事件不受工作时间限制;" "应急联系人与备份联系人在项目启动会上书面提交采购人,\n" "变更时即时更新。所有应急事件均形成记录并纳入月度服务报告。", fs=8.4, color="#404040", va="top") f.crop(3) return f.save("fig_emergency") # ==================================================== 附件六 第3章 售后服务 def fig_service(): f = Fig("售后服务体系与响应时效", w=13.0, h=6.2, sub="三级支持 · 多渠道受理 · 含诊断模型持续优化承诺") f.panel(3, 91, 94, 22.0, "三级技术支持体系", tone="blue") lv = [("一线支持\n服务热线 · 在线工单\n常见问题与操作指导", "响应 ≤ 30 分钟"), ("二线支持\n项目实施与配置工程师\n配置调整 · 数据核查", "响应 ≤ 2 小时"), ("三线支持\n产品研发团队\n程序缺陷 · 版本修复", "响应 ≤ 4 小时")] bs = [] for i, (t, r) in enumerate(lv): b = f.box(6 + i * 30.5, 86.5, 28, 11.0, t, tone="blue", fs=8.4) f.note(6 + i * 30.5 + 14, 73.5, r, fs=8.6, color="#C00000", ha="center") bs.append(b) for a, b in zip(bs, bs[1:]): f.arrow(a["r"], b["l"], lw=1.1) f.note(50, 70.5, "一线无法解决即升级,升级过程对用户透明,不需用户重复描述问题", fs=8.2, color="#404040", ha="center") f.panel(3, 67.0, 45, 22.0, "服务内容", tone="green") for i, t in enumerate(["系统运行巡检与状态报告", "配置调整与主数据维护支持", "缺陷修复与版本升级", "指标口径与看板调整", "数据备份与恢复支持", "月度服务报告与季度回顾"]): f.obox(5.5, 62.5 - i * 4.4, 40, 3.8, t, tone="green", fs=8.2) f.panel(51, 67.0, 46, 22.0, "诊断模型持续优化", tone="purple") for i, t in enumerate(["季度回顾诊断准确率与误报率", "按实际处置结果校准规则与阈值", "补充新增异常场景与措施库", "更新根因树与责任矩阵", "优化结果书面报采购人确认", "服务期内模型优化不额外收费"]): f.obox(53.5, 62.5 - i * 4.4, 41, 3.8, t, tone="purple", fs=8.2) f.panel(3, 41.0, 94, 15.0, "受理渠道与服务承诺", tone="orange") row(6, 37.0, 17.4, 9.0, 5, 2.4, ["服务热线\n工作日 8:30–17:30\n重大事件 7×24", "在线工单\n全天受理\n状态可查", "企业微信 / 钉钉\n即时沟通群\n项目经理在群", "远程支持\n远程协助排查", "现场支持\n重大问题按需到场"], f, tone="orange", fs=8.0) f.note(4, 24.0, "服务期:系统正式上线验收合格之日起提供免费质保与技术服务," "期限按合同约定执行;质保期内不因缺陷修复、版本升级、\n" "指标口径调整与诊断模型优化另行收费。质保期满后可续签服务协议," "服务内容与响应时效不低于质保期标准。", fs=8.4, color="#404040", va="top") f.crop(11) return f.save("fig_service") # ==================================================== 附件六 第4章 项目培训 def fig_training(): f = Fig("培训体系与实施安排", w=13.0, h=6.4, sub="分角色分层培训 · 训后考核 · 配套手册与录像,人员流动后可自主复训") f.panel(3, 91, 94, 30.0, "五类培训对象与内容", tone="blue") cols = [ ("系统管理员", "teal", ["组织与用户权限管理", "主数据维护与批量导入", "集成通道配置与监控", "备份恢复与日常巡检"], "2 天 · 8 课时"), ("业务操作员", "green", ["本岗位功能操作实操", "单据流转与异常处理", "查询与报表导出", "移动端使用"], "3 天 · 12 课时"), ("智能诊断与\n数据分析专项", "purple", ["指标口径与看板下钻", "诊断结论判读与复核", "改善任务闭环操作", "ChatBI 取数与自助分析"], "2 天 · 8 课时"), ("开发与\n运维人员", "blue", ["架构与数据模型讲解", "接口与二次开发", "部署升级与回滚", "监控告警与故障排查"], "2 天 · 8 课时"), ("管理层专项", "orange", ["九宫格看板判读", "异常与预警关注要点", "运营例会用法", "移动端决策看板"], "0.5 天 · 2 课时"), ] for i, (name, tone, items, dur) in enumerate(cols): x = 5.5 + i * 18.4 f.box(x, 86.5, 17.0, 7.5, name, tone=tone, fs=8.4) for j, t in enumerate(items): f.obox(x, 77.5 - j * 5.2, 17.0, 4.6, t, tone=tone, fs=7.6) f.note(x + 8.5, 54.5, dur, fs=8.2, color="#C00000", ha="center") f.panel(3, 50.0, 45, 20.0, "培训实施方式", tone="orange") for i, t in enumerate(["集中授课 + 系统实操,一人一机", "使用采购人真实业务数据演练", "关键岗位一对一辅导", "上线后一周现场跟班支持"]): f.obox(5.5, 45.5 - i * 4.6, 40, 4.0, t, tone="gold", fs=8.2) f.panel(51, 50.0, 46, 20.0, "培训成果与考核", tone="green") for i, t in enumerate(["训后考核,不合格安排补训", "培训签到表与考核记录归档", "培训满意度评价并纳入验收", "关键岗位培养内部种子讲师"]): f.obox(53.5, 45.5 - i * 4.6, 41, 4.0, t, tone="green", fs=8.2) f.panel(3, 27.0, 94, 13.0, "培训资料(随项目交付)", tone="gray") row(6, 23.2, 17.4, 7.0, 5, 2.4, ["系统管理员手册", "业务操作手册(分岗位)", "智能诊断与数据分析手册", "常见问题手册(FAQ)", "培训录像与课件"], f, tone="gray", fs=8.0) f.note(4, 13.0, "培训手册按岗位编写,以实际操作截图配步骤说明,不用抽象术语描述界面;" "培训录像随手册一并交付,人员流动后新人可自主学习,\n" "不必依赖我方重复到场培训——这是系统能够在采购人内部长期自主运行的基础。", fs=8.4, color="#404040", va="top") f.crop(3) return f.save("fig_training") FIGS = [fig_module_map, fig_deploy, fig_data_layer, fig_api, fig_diag_arch, fig_diag_model, fig_auto_closedloop, fig_metric_model, fig_test_v, fig_defect_flow, fig_quality_gate, fig_emergency, fig_service, fig_training] if __name__ == "__main__": for fn in FIGS: fn() print(f"\n共生成 {len(FIGS)} 张配图")