0.文档说明与导读
0.1 文档定位与版本演进
本白皮书是对前期全部技术文档的系统性整合与重构。它把两条原本分离的线索——面向甲方与管理层的"业务与需求论证"(V1.0 白皮书)与面向研发运维的"工程实施细节"(V2.0 实施文档)——合并为一份自洽、完整、可追溯的单一文档,避免读者在多个版本间反复对照。
| 版本 | 文档性质 | 主要内容与局限 |
| V1.0 技术白皮书 | 方案论证 | 介绍建设背景、总体思路与功能蓝图;偏概念,缺乏可落地的工程细节 |
| V2.0 实施文档 | 研发实施 | 给出目录结构、DDL、核心算法、API、RBAC、容器化部署;偏研发视角,缺少需求论证与验收视角 |
| V4.0 系统整合版 | 综合集成 | 整合两者并补全:需求论证 + 技术选型 + 工程规范 + 数据库 + 算法 + 接口 + 前端 + 安全 + 部署 + 运维 + 测试 + 实施 + 风险,全链贯通 |
📖 本次整合的三点关键调整
① 不包含智慧工地相关部分:涉及施工现场物联网遥测(深基坑、高支模、塔吊等监测数据接入与告警)的内容在本版本中整体移除,相关表结构、接口与算法均不出现在本文档中。
② 不涉及服务器连通性内容:不对具体服务器的连通状态、端口可达性、SSH 访问等运行态问题进行描述,但完整保留服务器部署方案(域名解析、容器编排、Nginx、HTTPS、备份等),确保方案本身可直接执行。
③ 尽可能详尽:在保持结构清晰的前提下,对每个模块给出定义、设计理由、实现细节与验证方法。
核心功能域
5 大模块
健康度/风险/合同支付/进度/系统管理
技术栈
Vue3 + Node
MySQL 8.0 / Redis / Docker
部署形态
容器化
Docker Compose + Nginx
文档结构导航
| 章节 | 内容 | 主要读者 |
| 第 1 章 | 项目背景、管理现状与痛点诊断、建设目标与价值论证 | 甲方 / 管理层 |
| 第 2 章 | 需求来源、需求分析、功能与非功能需求、追踪矩阵、变更控制 | 甲方 / 产品 / 项目经理 |
| 第 3 章 | 总体架构、技术选型论证、领域模型、工程规范、部署拓扑 | 架构师 / 研发 |
| 第 4 章 | 数据库物理模型、核心表 DDL、索引优化、数据字典 | DBA / 研发 |
| 第 5 章 | EVM 引擎、四单匹配、风险状态机核心算法 | 研发 |
| 第 6 章 | RESTful API 规范、接口清单、示例、错误码 | 研发 / 前端 |
| 第 7 章 | 前端技术栈、页面结构、关键交互设计 | 前端 / 产品 |
| 第 8 章 | RBAC 权限矩阵、鉴权实现、审计防篡改、安全措施 | 研发 / 安全 |
| 第 9 章 | 系统部署方案(DNS / 容器编排 / Nginx / HTTPS / 上线 / 回滚) | 运维 / 研发 |
| 第 10 章 | 备份、监控告警、数据主权与技术退出 | 运维 / 甲方 |
| 第 11 章 | 测试策略、核心算法测试用例、验收标准 | 测试 / 项目经理 |
| 第 12-14 章 | 实施计划、风险应对、附录清单 | 全体 |
阅读建议
- 关注投资回报与建设必要性 → 第 1、2、12、13 章
- 关注技术可行性与实现细节 → 第 3~8、11 章
- 关注上线落地方案 → 第 9、10、14 章
本文档可独立成篇,亦可按章节拆分为《需求规格说明书》(第 2 章)、《概要设计说明书》(第 3~8 章)、《部署实施手册》(第 9 章)、《测试方案》(第 11 章)四份子文档。
0.2 范围界定
纳入范围
✓业务范围
健康度看板、风险管理、合同与支付管理、进度管理、系统与权限管理五大功能域;覆盖项目经理部、成本合约、财务、监理与施工方等岗位的在线协同。
✓技术范围
前后端分离的应用系统、关系型数据库、缓存、容器化部署与运维体系;核心算法(EVM、四单匹配、风险状态机)的完整实现。
明确排除(Out of Scope)
| 排除项 | 说明 |
| 智慧工地 / 物联网监测 | 施工现场物联网设备接入、深基坑与高支模变形监测、塔吊安全监控等不在本版本范围内。相关数据表、接口、告警机制暂不设计。 |
| 服务器运行态与连通性 | 不对具体服务器的网络连通、端口可达性、远程访问方式等运行态问题做描述与排障。部署方案本身(配置与步骤)完整给出。 |
| BIM / 三维模型集成 | 本期不做模型轻量化与三维可视化对接,可作为后续演进方向。 |
| 移动端原生 App | 本期前端为响应式 Web,覆盖 PC 与平板;不开发 iOS/Android 原生应用。 |
| 财务总账与税务接口 | 系统只做支付申请与审批流转,不替代甲方财务系统做账务处理与税务申报。 |
0.3 术语表
| 术语 | 说明 |
| EVM | Earned Value Management,挣值管理。通过 PV/EV/AC 三大基础值及其衍生指标量化项目进度与成本健康状况的方法论。 |
| PV / EV / AC | 计划价值(Planned Value)、挣值(Earned Value)、实际成本(Actual Cost)。 |
| SV / CV | 进度偏差(Schedule Variance,EV−PV)与成本偏差(Cost Variance,EV−AC)。 |
| SPI / CPI | 进度绩效指数(EV/PV)与成本绩效指数(EV/AC)。SPI<1 表示进度滞后,CPI<1 表示成本超支。 |
| EAC / VAC | 完工预测(Estimate at Completion)与完工偏差(Variance at Completion)。 |
| 四单匹配 | 合同、送货单、验收单、发票四类单据的金额与数量一致性自动校验,是防止超付错付的核心管控手段。 |
| 红黄绿状态机 | 按"风险发生概率 × 影响程度"矩阵将风险划分为三级(红/黄/绿),并驱动预警、督办、升级流转的状态模型。 |
| RBAC | Role-Based Access Control,基于角色的访问控制。 |
| WBS | Work Breakdown Structure,工作分解结构,是进度与成本核算的共同基础。 |
| 审计留痕 | 对资金、合同等关键操作记录操作人、时间、IP 与变更前后数据快照,用于追溯与举证。 |
| COS | 对象存储服务(Cloud Object Storage),用于异地存放加密数据库备份。 |
| DNSPod | 域名解析服务,负责把域名指向服务器 IP。 |
1.项目背景与建设必要性
1.1 项目概况
中欧(苏州)数字科技产业园建设工程(以下简称"本项目")是一项涉及土建施工、机电安装、智能化系统集成、精装修多专业协同的综合性工程。项目具有参建单位多、合同关系复杂、资金拨付节点密集、安全质量监管要求高四大特征。
在这些特征下,项目管理的核心矛盾可概括为:
⚡ 核心矛盾
项目总监与项目管理团队需要实时、准确、可追溯地掌握四大维度的健康状况(进度、成本、质量、安全),而当前信息获取依赖层层上报与人工汇总,存在滞后性(T+3 至 T+7)、失真性(口径不一)、不可追溯性(线下签批无留痕)三大结构性缺陷。
为解决上述矛盾,需建设一套数字化项目管理系统,作为本项目的"管理驾驶舱",实现管理动作的在线化、指标化、自动化,并以其沉淀的过程数据支撑决策、审计与争议解决。
1.2 管理现状与痛点诊断
通过对现有管理流程的梳理与关键岗位访谈,识别出以下六类高价值痛点。每类痛点均对应本系统的一个建设模块,构成"痛点—功能"的强映射关系。
①进度掌控滞后
进度数据靠周报人工汇总,管理层看到的永远是"过去时"。偏差发现时往往已滞后 3~7 天,纠偏窗口被严重压缩。
②成本超支发现晚
成本核算与工程进度脱节,无法回答"花掉的钱换回了多少进度"。概算红线缺乏过程性抓手,只能在结算时暴露。
③支付审核风险高
合同、送货、验收、发票四类单据分散在不同岗位手中,人工核对易出现超付、错付、重复支付,且核对过程无留痕。
④风险预警被动
重大风险缺乏分级与提前预警机制,"提前 15 日预警"的管理要求无系统承载,只能依赖责任人主动上报,漏报风险高。
⑤质量整改不闭环
质量问题与安全隐患靠线下台账记录,整改、复查、销项缺少强制流程约束,闭环率难以统计,责任难以追溯。
⑥数据主权不明
数据散落于各参建单位的私有系统与个人电脑中,甲方难以完整掌握原始数据,退出机制下数据交接困难。
痛点—模块映射表
| 痛点 | 对应模块 | 关键指标 / 机制 | 优先级 |
| 进度掌控滞后 | 健康度看板 / 进度管理 | SPI 每日计算,SPI<0.95 自动触发纠偏预警 | P0 |
| 成本超支发现晚 | 健康度看板 / 成本管理 | CPI 每日计算,EAC/VAC 完工预测 | P0 |
| 支付审核风险高 | 合同与支付管理 | 四单匹配自动校验 + 1% 尾差容错 | P0 |
| 风险预警被动 | 风险登记册 | 红黄绿状态机 + 提前 15 日定时扫描 | P0 |
| 质量整改不闭环 | 质量问题跟踪 | 登记 → 整改 → 复查 → 销项强制流程 | P1 |
| 数据主权不明 | 系统管理 / 数据主权 | 加密备份上云 + 甲方持密钥 + 全量导出 | P1 |
1.3 建设目标与价值论证
1.3.1 建设目标(SMART 化)
| 目标维度 | 具体目标 | 可量化验收标准 |
| 及时性 | 管理指标从"周级"提升至"日级" | EVM 指标每日 06:00 前完成刷新,看板打开即见最新值 |
| 准确性 | 支付审核由人工核对转为系统自动校验 | 四单匹配校验覆盖率 100%,超付事件 0 起 |
| 前瞻性 | 重大风险预警提前量不低于 15 日 | 红色风险提前 15 日触达责任人与项目总监 |
| 闭环性 | 质量与安全问题强制走完整闭环流程 | 整改闭环率可统计,销项需复查确认 |
| 可追溯 | 所有资金与合同变更操作 100% 留痕 | 审计日志含操作人、IP、变更前后 JSON 快照 |
| 数据主权 | 甲方完整掌握数据所有权与解密权 | 备份加密密钥由甲方持有,服务方无权解密 |
| 交付时效 | 系统在正式开工前 15 日上线可用 | 以开工令日期倒排,预留 15 日缓冲 |
1.3.2 价值论证
💰成本价值
通过 CPI 过程监控与四单匹配拦截,预计可避免合同金额 1%~3% 的超付与错付损失,以本项目合同规模估算,具备明确的经济回报。
⏱️进度价值
SPI 提前预警使纠偏窗口从"事后"前移至"事中",减少因工期延误产生的索赔与赶工成本。
🛡️合规价值
全量审计留痕满足内控与审计要求,数据主权条款保障甲方在争议场景(含诉讼举证)中的证据完整性。
✅ 结论
建设本系统不是"锦上添花"的数字化点缀,而是本项目成本可控、进度可见、风险可防、数据可溯的必要管理基础设施。其投入产出比在项目全周期内可清晰验证。
2.需求提出与需求基线
2.1 需求来源与干系人
本系统需求并非凭空设计,而是从三个层面逐级推导、交叉印证形成。
干系人登记册
| 干系人 / 角色 | 类型 | 核心关注点 | 需求贡献 |
| 项目总监 | 内部 / 决策 | 全局健康度、重大风险、资金安全 | 一票否决重大支付、风险督办 |
| 项目经理 | 内部 / 执行 | 进度可控、资源协调、问题闭环 | 日常推进、风险登记、支付复审 |
| 成本 / 合同工程师 | 内部 / 执行 | 合同履约、成本核算、支付准确性 | 四单数据录入与初审 |
| 质量 / 安全工程师 | 内部 / 执行 | 质量问题整改闭环、隐患排查 | 问题登记与整改跟踪 |
| 监理单位 | 外部 | 验收数据真实性、旁站记录 | 验收单录入、质量上报 |
| 施工单位 / 分包商 | 外部 | 进度报量、支付进度查询 | 送货单、进度报量提交 |
| 甲方(产业园) | 外部 / 业主 | 投资回报、数据主权、内控合规 | 验收标准、数据归属条款 |
| 财务部门 | 内部 / 支持 | 付款依据合规、资金计划 | 支付规则、票据核验要求 |
| 审计 / 法务 | 内部 / 监督 | 操作留痕完整性、证据可采信 | 审计日志要求、导出格式 |
2.2 需求分析与建模
需求收集后,采用三种方法交叉分析,避免遗漏与误读。
🔍流程穿越法
沿"业务发起 → 审核 → 审批 → 归档"逐环节穿越,识别每一步的输入、输出、责任人与痛点,转化为功能点。
👥角色用功法
为每个岗位梳理"每日/每周必做动作",把高频动作优先产品化,确保系统上线即被使用。
📐指标倒推法
从管理要求的指标出发(SPI、CPI、闭环率、留痕率),倒推需要采集的数据与计算逻辑,形成数据需求。
用例摘要(核心用例)
| 编号 | 用例名 | 主要参与者 | 主流程 |
| UC-01 | 查看项目健康度 | 项目总监 / 项目经理 | 登录 → 进入看板 → 查看四维健康度与 EVM 指标 → 下钻标段 |
| UC-02 | 登记风险 | 项目经理 / 质量安全工程师 | 新增风险 → 填概率与影响 → 系统算等级 → 指派责任人 |
| UC-03 | 处置风险 | 责任人 | 查看预警 → 填应对措施 → 更新状态 → 提交验证 |
| UC-04 | 发起支付申请 | 成本/合同工程师 | 选合同 → 录入四单数据 → 系统校验 → 通过则提交审批 |
| UC-05 | 审批支付 | 项目经理 / 项目总监 | 查看校验结果 → 复审 → 终审 → 状态流转 |
| UC-06 | 录入进度报量 | 施工方 / 项目经理 | 提交报量 → 监理确认 → 计入 EV 计算 |
| UC-07 | 质量问题闭环 | 质量工程师 | 登记问题 → 指派整改 → 复查 → 销项 |
| UC-08 | 查询审计日志 | 审计角色 | 按人/时间/对象筛选 → 查看变更快照 → 导出 |
| UC-09 | 账号与权限管理 | 项目总监 / 系统管理员 | 新建/停用账号 → 分配角色 → 权限即时生效 |
2.3 功能性需求(FR)
功能性需求按五大模块组织,共 22 项,均给出唯一编号、描述、优先级与验收标准,构成需求基线。
模块一:健康度看板(DB)
| 编号 | 需求描述 | 优先级 | 验收标准 |
| FR-DB-01 | 四维健康度可视化(进度/成本/质量/安全),支持总体与分项视图 | P0 | 看板加载 <2s,显示最新计算值 |
| FR-DB-02 | EVM 核心指标展示:PV/EV/AC/SV/CV/SPI/CPI/EAC/VAC | P0 | 指标与后端计算一致,保留 3 位小数 |
| FR-DB-03 | SPI < 0.95 时看板醒目提示"纠偏预警" | P0 | 预警状态与阈值逻辑严格一致 |
| FR-DB-04 | 趋势曲线:SPI/CPI 近 30 日走势 | P1 | 支持按周/月聚合切换 |
| FR-DB-05 | 按标段/专业下钻查看健康度 | P2 | 下钻层级支持二级 |
模块二:风险管理(RK)
| 编号 | 需求描述 | 优先级 | 验收标准 |
| FR-RK-01 | 风险登记:录入分类、描述、概率(1-5)、影响(1-5)、责任人、预计发生日期 | P0 | 必填项校验完整 |
| FR-RK-02 | 系统按"概率×影响"矩阵自动计算红黄绿等级 | P0 | 等级计算规则可配置且结果可复现 |
| FR-RK-03 | 风险状态流转:active → mitigated → closed | P0 | 流转留痕,不可跨级跳过 |
| FR-RK-04 | 红色风险提前 15 日自动推送责任人与项目总监 | P0 | 定时任务每日执行,触达率 100% |
| FR-RK-05 | 风险清单支持按等级/状态/责任人筛选导出 | P1 | 导出 Excel 格式规范 |
| FR-RK-06 | 风险处置记录:措施、责任人、期限、验证结果 | P1 | 形成闭环记录 |
模块三:合同与支付管理(PM)
| 编号 | 需求描述 | 优先级 | 验收标准 |
| FR-PM-01 | 合同台账:合同编号、金额、乙方、工期、付款条件 | P0 | 支持合同变更留痕 |
| FR-PM-02 | 四单匹配自动校验:合同量 ≥ 送货量 ≥ 验收量,发票金额与验收核算匹配 | P0 | 校验规则全部命中,误差容限 1% |
| FR-PM-03 | 支付审批流:发起 → 初审 → 复审 → 终审(多级) | P0 | 节点、角色、权限严格受控 |
| FR-PM-04 | 校验不通过时阻断提交并明确提示失败原因 | P0 | 错误提示精确到具体单据项 |
| FR-PM-05 | 支付进度跟踪:累计已付 / 合同额 / 剩余可付 | P1 | 实时准确 |
| FR-PM-06 | 付款申请单与审批意见导出归档 | P2 | PDF 格式,含签批记录 |
模块四:进度管理(SC)
| 编号 | 需求描述 | 优先级 | 验收标准 |
| FR-SC-01 | WBS 分解与里程碑管理 | P0 | 支持三级 WBS |
| FR-SC-02 | 进度报量录入与审核 | P0 | 报量数据计入 EV 计算 |
| FR-SC-03 | 关键路径识别与延误预警 | P1 | 延误超阈值触发提醒 |
| FR-SC-04 | 进度对比:计划 vs 实际 | P1 | 可视化对比清晰 |
模块五:质量管理与系统管理(QS / SY)
| 编号 | 需求描述 | 优先级 | 验收标准 |
| FR-QS-01 | 质量问题登记、整改、复查、销项闭环 | P1 | 整改闭环率可统计,销项需复查确认 |
| FR-QS-02 | 安全隐患排查与整改跟踪 | P1 | 隐患等级分类管理 |
| FR-SY-01 | 用户、角色、权限管理(RBAC) | P0 | 角色权限矩阵可配置 |
| FR-SY-02 | 操作审计日志,记录资金/合同变更前后快照 | P0 | 留痕率 100%,不可篡改 |
| FR-SY-03 | 人员离职 24 小时内账号注销机制 | P0 | 停用后 Token 立即失效 |
| FR-SY-04 | 数据导出与备份(甲方完整所有权) | P1 | 支持全量导出为开放格式 |
2.4 非功能性需求(NFR)
| 类别 | 编号 | 指标要求 |
| 性能 | NFR-P-01 | 看板类查询接口 P95 响应时间 < 800ms(并发 50) |
| NFR-P-02 | 单表数据量按 50 万行设计,关键查询走索引,避免全表扫描 |
| NFR-P-03 | 列表分页默认 20 条,单页最大 100 条,防止大结果集拖垮服务 |
| 可用性 | NFR-A-01 | 系统可用性 ≥ 99.5%(月度),计划外停机 ≤ 2 小时/月 |
| NFR-A-02 | 容器 restart: always,进程崩溃后自动拉起 |
| 安全 | NFR-S-01 | 全站 HTTPS,TLS 1.2+,HSTS 开启 |
| NFR-S-02 | 密码 bcrypt 加盐哈希存储;JWT 有效期 ≤ 8h 并支持吊销 |
| NFR-S-03 | 单 IP 接口限流 100 req/min,登录失败 5 次锁定 15 分钟 |
| 可维护性 | NFR-M-01 | 分层架构,核心算法(EVM/四单匹配/风险状态机)具备单元测试,覆盖率 ≥ 70% |
| 可移植性 | NFR-X-01 | 全容器化交付,镜像 + 编排文件在新服务器可一键复现 |
| 兼容性 | NFR-C-01 | 适配 Chrome / Edge 最新两版、Safari,支持 1366px 起响应式 |
| 合规性 | NFR-R-01 | 对齐等级保护三级技术要求,审计日志保留 ≥ 180 天 |
2.5 需求追踪矩阵(RTM)
追踪矩阵用于保证"管理要求 → 需求 → 设计 → 代码 → 测试"全链路可追溯,是验收与审计的关键工具。
| 需求编号 | 来源依据 | 设计章节 | 实现位置 | 优先级 |
| FR-DB-01/02/03 | 健康度管理要求 | §3.3 / §5.1 | evmEngine.js | P0 |
| FR-RK-02/03/04 | 风险预警要求 | §5.3 | riskEngine.js / riskScanner.js | P0 |
| FR-PM-02/04 | 四单匹配要求 | §5.2 | paymentValidator.js | P0 |
| FR-SC-02 | 进度报量要求 | §3.3 / §5.1 | scheduleService.js | P0 |
| FR-QS-01 | 质量闭环要求 | §7.2 | qualityController.js | P1 |
| FR-SY-01/03 | 岗位职责要求 | §8.1 / §8.2 | auth.js | P0 |
| FR-SY-02 | 防篡改要求 | §8.3 | auditMiddleware.js | P0 |
| NFR-S-01 | 内控合规要求 | §9.5 | nginx.conf + Certbot | P0 |
| NFR-R-01 | 数据主权要求 | §10.3 | backup.sh | P1 |
2.6 需求变更控制
需求基线冻结后,任何变更均通过变更控制流程(CCB)管理,防止范围蔓延导致工期与质量失控。
| 变更类型 | 处理策略 |
| P0 需求调整 | 必须经 CCB 评审,重排工期,优先保障;若影响关键路径需项目总监批准 |
| P1 需求调整 | 列入当期迭代,视工期弹性排期,不挤占 P0 资源 |
| P2 需求新增 | 默认进入"后续增强清单",本期不实现(除非工期有余量) |
| 缺陷修复 | P0 缺陷即时修复;P1 缺陷当期修复;P2 缺陷按迭代计划 |
📌 需求基线冻结
本章所列 22 项功能性需求与 11 项非功能性需求,经甲乙双方需求评审会确认后冻结为需求基线 V1.0。基线变更须走上述变更控制流程,评估对工期、成本、测试范围的影响后方可实施。
3.技术路径与架构选型
3.1 总体技术架构
系统采用经典前后端分离 + 分层架构,自上而下划分为五层。分层的目的在于让业务逻辑与实现细节解耦,核心算法可独立测试,易于长期维护。
① 表现层 Presentation
Vue3 + TypeScript + Pinia + Element Plus + ECharts | 响应式,覆盖 PC 与平板
▼ HTTPS / RESTful JSON ▼
② 接入层 Gateway
Nginx | TLS 终止、静态资源、反向代理、限流、gzip
▼ 内网 3000 ▼
③ 应用层 Application
Node.js + Express | Controller / Service / Middleware(鉴权·审计·四单拦截)
▼ ORM (Sequelize) ▼
④ 数据层 Data
MySQL 8.0(业务数据) + Redis(缓存·Token 吊销·限流计数)
▼ 定时任务 ▼
⑤ 支撑层 Support
node-cron(EVM 日算·风险扫描) + 通知服务 + COS 备份
分层职责说明
| 层级 | 核心构件 | 职责与设计约束 |
| 表现层 | Vue3 组件、Pinia、路由 | 只负责渲染与交互,不承载业务规则;所有计算与校验由后端返回,前端仅做即时提示 |
| 接入层 | Nginx | 统一入口;TLS 终止;静态资源直出;/api 反向代理到应用;限流与压缩 |
| 应用层 | Controller / Service / Middleware | Controller 只做参数校验与响应封装;Service 承载业务逻辑;Middleware 处理横切关注点(鉴权、审计、拦截) |
| 数据层 | MySQL / Redis | MySQL 为唯一事实来源;Redis 只做加速与状态缓存,丢失不致命 |
| 支撑层 | 定时任务、通知、备份 | 与主请求链路解耦,独立运行,故障不影响在线服务 |
3.2 技术栈选型论证
技术选型不是"用最新最炫",而是在成熟度、团队能力、交付周期、运维成本、生态完备性之间取最优解。以下对关键选型逐项论证。
3.2.1 后端框架选型
| 候选方案 | 优势 | 劣势 | 结论 |
| Node.js + Express | 异步 I/O 适合高并发查询;生态成熟;学习曲线平缓;与前端同语言,人员复用度高 | CPU 密集型计算偏弱(本项目无此场景) | 选定 |
| NestJS | 架构规范、依赖注入、适合大型团队 | 学习成本高,对本项目规模而言偏重 | 备选 |
| Java Spring Boot | 企业级生态强、事务管理成熟 | 部署体积大、内存占用高、交付周期长;轻量服务器资源受限 | 不选 |
| Python Django | 开发快、ORM 强 | 异步性能弱于 Node,并发场景需额外调优 | 不选 |
💡 选型理由
Express 轻量、可控、启动快,在资源受限的服务器上能以最小内存占用稳定承载本项目 50 并发量级,且工程结构简单、便于交付后由甲方团队接手维护。NestJS 作为备选保留——若后续团队规模扩大、模块数量激增,可按分层结构平滑迁移。
3.2.2 数据库选型
| 候选 | 适配度分析 | 结论 |
| MySQL 8.0 | 事务与 ACID 完备,资金类数据必须强一致;团队熟悉;云上运维成熟;窗口函数支持 EVM 聚合计算 | 选定 |
| PostgreSQL | 功能更强(GIS/JSONB),但对本项目而言优势未充分体现 | 备选 |
| MongoDB | 文档模型灵活,但资金与合同数据需强事务,且报表聚合不便 | 不选 |
3.2.3 前端与部署形态
✓Vue3 + TS
组合式 API 逻辑复用强,TypeScript 保障接口数据结构一致,降低联调成本。
✓Element Plus
组件完备、表格表单能力强,天然适配管理后台类系统,开发效率高。
✓ECharts
看板趋势图与仪表盘图表表现力强,国内生态完善,文档中文。
✓Docker
环境一致性,避免"开发机能跑、服务器跑不起来"的经典问题。
✓Redis
Token 吊销黑名单、看板结果缓存、接口限流计数,一物三用。
✓PM2
作为容器内进程守护兜底,配合 Docker restart 策略实现双重保活。
⚠️ 关于部署形态的说明
本项目不采用公有云 SaaS 或低代码平台方案。原因是资金与合同数据涉及甲方核心商业信息,且明确要求数据主权归甲方。系统采用私有化部署,数据落盘位置、访问凭证、加密密钥均由甲方掌控。
3.3 领域模型设计
领域模型是数据库设计与 API 设计的共同蓝本。下图以实体关系描述核心业务对象及其关联。
📦核心实体
Project 项目 → WBS 工作分解 → Contract 合同 → Payment 支付单 → Risk 风险 → User 用户 → AuditLog 审计日志。其中 Contract 与 Payment 是一对多,Payment 内部承载四单匹配的四个数据面。
🔗关键关联约束
· 一个项目包含多个 WBS 节点,每个节点有唯一责任人与进度计划
· 一份合同可发起多次付款申请
· 一次付款申请必须关联唯一的合同,且四单数据必须齐全
· 每条风险必须挂载唯一的责任人
核心实体关系(ER 摘要)
-- 逻辑关系示意(非可执行 SQL)
projects 1 ──── N wbs_nodes // 一个项目多个工作分解节点
projects 1 ──── N contracts // 一个项目多份合同
contracts 1 ──── N payment_flow // 一份合同多次付款申请
projects 1 ──── N risk_registry // 一个项目多条风险
projects 1 ──── N quality_issues // 质量问题清单
users 1 ──── N risk_registry // 责任人
users 1 ──── N audit_logs // 操作人
projects 1 ──── N evm_snapshots // 每日健康度快照(趋势数据源)
3.4 工程化规范与目录结构
为保证多人协作下代码质量与可维护性,项目遵循统一的工程规范。后端采用 Node.js (Express),遵循分层架构,确保业务逻辑、数据访问与路由控制解耦;前端采用 Vue3 + TypeScript + Pinia。
3.4.1 后端工程目录规范
zh-euro-park-api/ # 后端工程根目录
├── src/
│ ├── config/ # 环境变量、数据库、JWT、Redis 配置
│ │ ├── index.js # 配置聚合与校验(启动时校验必填项)
│ │ ├── database.js # Sequelize 连接池配置
│ │ └── redis.js # Redis 客户端
│ ├── controllers/ # 路由控制器(HTTP 请求处理)
│ │ ├── authController.js
│ │ ├── healthController.js # 健康度看板
│ │ ├── riskController.js
│ │ ├── paymentController.js
│ │ └── auditController.js
│ ├── services/ # 核心业务逻辑(可独立单元测试)
│ │ ├── evmEngine.js # 挣值分析引擎
│ │ ├── paymentValidator.js # 四单匹配校验
│ │ ├── riskEngine.js # 风险红黄绿状态机
│ │ └── notificationService.js # 通知服务(邮件/站内)
│ ├── models/ # Sequelize 数据模型定义
│ ├── middlewares/ # 鉴权、日志、异常捕获、审计、四单校验拦截
│ │ ├── auth.js
│ │ ├── auditMiddleware.js
│ │ └── errorHandler.js
│ ├── routes/ # 路由声明(版本化 /api/v1)
│ ├── utils/ # 工具类(日期处理、加密、文件导出)
│ ├── cron/ # 定时任务(EVM 日算、风险扫描)
│ └── app.js # 应用入口
├── tests/ # 单元测试(Jest)与接口测试
│ ├── unit/
│ └── integration/
├── docker/ # Dockerfile 及 Nginx / MySQL 配置
├── ecosystem.config.js # PM2 进程管理配置
├── .env.example # 环境变量模板
└── package.json
3.4.2 前端工程目录规范
zh-euro-park-web/ # 前端工程根目录
├── src/
│ ├── api/ # 接口封装(按模块划分,统一 axios 实例)
│ ├── assets/ # 静态资源
│ ├── components/ # 通用组件(表格、图表、弹窗)
│ ├── layouts/ # 布局(侧边栏 + 顶栏)
│ ├── router/ # 路由与权限守卫
│ ├── stores/ # Pinia 状态管理
│ ├── views/ # 页面
│ │ ├── dashboard/ # 健康度看板
│ │ ├── risk/ # 风险登记册
│ │ ├── payment/ # 合同与支付
│ │ ├── schedule/ # 进度管理
│ │ └── system/ # 系统管理
│ ├── utils/ # 工具函数(格式化、权限判断)
│ └── main.ts
├── vite.config.ts # 构建配置(本地代理 /api → 后端)
└── package.json
3.4.3 编码规范要点
| 规范项 | 要求 |
| 命名约定 | 文件与变量 camelCase;类名 PascalCase;数据库表与字段 snake_case;常量 UPPER_SNAKE |
| 接口分层 | Controller 不写业务逻辑;Service 不直接操作 req/res;模型不写业务规则 |
| 错误处理 | 统一异常中间件捕获,返回标准错误结构,禁止裸抛出导致 500 无信息 |
| 参数校验 | 所有入参在 Controller 入口校验,非法参数立即返回 400 |
| 日志 | 结构化日志,含 traceId;敏感字段(密码、Token)脱敏 |
| 代码审查 | 所有合并请求需至少一人 Review,核心算法变更需补充测试用例 |
| 提交规范 | 遵循约定式提交(feat/fix/docs/refactor/test/chore) |
3.5 部署拓扑与网络路径
系统以容器化方式部署,通过统一域名对外提供服务。整体拓扑与访问链路如下(具体配置详见第 9 章)。
用户浏览器
Chrome / Edge / Safari
▼ ① HTTPS 443 请求
DNS 域名解析
域名 → 服务器公网 IP
▼ ② 解析到服务器 IP
云服务器防火墙 放行 443/80
服务器公网 IP 拒绝 3306/6379
▼ ③ 容器网络 park-net(bridge)
Nginx 容器(zh-park-nginx)
TLS 终止 · 静态资源 · /api 反向代理
▼ ④ 内网转发 app:3000
App 容器
Node.js
MySQL 容器
仅内网
Redis 容器
仅内网
🚫 部署安全红线
1. 云服务器防火墙严禁对外开放 MySQL 3306 与 Redis 6379 端口,仅允许 Nginx 的 80/443 及运维管理端口(建议改非标准端口 + 来源 IP 白名单)。
2. 所有环境变量(数据库密码、JWT Secret、加密密钥)必须通过 .env 文件注入,严禁硬编码在代码仓库中。
3. 数据库与缓存容器不配置 ports 映射到宿主机,仅通过 Compose 内部网络 park-net 被应用访问。
4.数据库设计
数据库采用 MySQL 8.0,字符集统一为 utf8mb4(排序规则 utf8mb4_0900_ai_ci),存储引擎 InnoDB。以下为核心业务表的建表语句,含必要的索引与约束,以支撑高并发查询与数据一致性。
4.1 物理模型总览
| 表名 | 用途 | 预估量级 | 对应模块 |
projects | 项目主表 | 十级 | 系统 |
users | 用户与角色 | 百级 | 系统 |
contracts | 合同台账 | 百级 | 合同支付 |
payment_flow | 支付审批与四单匹配 | 千级 | 合同支付 |
risk_registry | 风险登记册 | 千级 | 风险管理 |
quality_issues | 质量问题与整改跟踪 | 千级 | 质量管理 |
evm_snapshots | 健康度每日快照 | 万级 | 健康度 |
audit_logs | 操作审计日志 | 十万级 | 安全 |
4.2 核心表建表语句
4.2.1 项目主表(projects)
CREATE TABLE `projects` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL COMMENT '项目名称',
`code` varchar(60) NOT NULL COMMENT '项目编号',
`bac` decimal(14,2) NOT NULL DEFAULT 0 COMMENT '完工预算 BAC',
`status` enum('planning','active','closed') DEFAULT 'active',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目主表';
4.2.2 用户表(users)
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(60) NOT NULL,
`password` varchar(100) NOT NULL COMMENT 'bcrypt 哈希',
`real_name` varchar(60) NOT NULL COMMENT '真实姓名',
`role` varchar(40) NOT NULL COMMENT 'project_director/project_manager/cost_engineer/qs_engineer/auditor/external',
`is_disabled` tinyint(1) DEFAULT 0 COMMENT '离职注销标记',
`last_login` timestamp NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
KEY `idx_role` (`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
4.2.3 合同台账表(contracts)
CREATE TABLE `contracts` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`project_id` int unsigned NOT NULL,
`contract_no` varchar(60) NOT NULL COMMENT '合同编号',
`party_b` varchar(200) NOT NULL COMMENT '乙方名称',
`total_amount` decimal(14,2) NOT NULL COMMENT '合同总额',
`paid_amount` decimal(14,2) NOT NULL DEFAULT 0 COMMENT '累计已付',
`unit_price` decimal(12,4) DEFAULT NULL COMMENT '合同单价',
`total_qty` int DEFAULT NULL COMMENT '合同约定总量',
`signed_date` date DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_contract_no` (`contract_no`),
KEY `idx_project` (`project_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='合同台账';
4.2.4 风险登记册表(risk_registry)
支撑"红黄绿"三级预警及提前 15 日预警机制。risk_score 为生成列,由概率与影响自动相乘,避免应用层重复计算。
CREATE TABLE `risk_registry` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`project_id` int unsigned NOT NULL COMMENT '项目ID',
`category` varchar(50) NOT NULL COMMENT '分类: 进度/成本/质量/安全',
`description` text NOT NULL COMMENT '风险描述',
`probability` tinyint NOT NULL COMMENT '发生概率(1-5)',
`impact` tinyint NOT NULL COMMENT '影响程度(1-5)',
`risk_score` tinyint GENERATED ALWAYS AS (`probability` * `impact`) STORED COMMENT '风险值',
`risk_level` enum('red','yellow','green') NOT NULL COMMENT '风险等级(系统计算)',
`expected_occur_date` date DEFAULT NULL COMMENT '预计发生日期(提前15日预警依据)',
`owner_id` bigint unsigned NOT NULL COMMENT '责任人ID',
`status` enum('active','mitigated','closed') DEFAULT 'active',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_level_status` (`risk_level`,`status`),
KEY `idx_occur_date` (`expected_occur_date`),
KEY `idx_owner` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='风险登记册';
4.2.5 支付审批与四单匹配表(payment_flow)
实现合同、送货单、验收单、发票的"四单匹配"自动校验。四个数据面各占一列,校验结果与失败明细分别写入 match_status 与 match_errors。
CREATE TABLE `payment_flow` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`contract_id` bigint unsigned NOT NULL COMMENT '关联合同ID',
`apply_amount` decimal(12,2) NOT NULL COMMENT '申请支付金额',
`contract_qty` int NOT NULL COMMENT '合同约定总量(快照)',
`delivery_qty` int DEFAULT 0 COMMENT '送货单数量',
`acceptance_qty` int DEFAULT 0 COMMENT '验收合格数量',
`invoice_amount` decimal(12,2) DEFAULT 0.00 COMMENT '发票金额',
`unit_price` decimal(12,4) NOT NULL COMMENT '合同单价',
`match_status` enum('matched','mismatch','pending') DEFAULT 'pending',
`match_errors` json DEFAULT NULL COMMENT '校验失败明细',
`flow_state` varchar(20) DEFAULT 'draft' COMMENT 'draft/initial/review/final/paid',
`created_by` bigint unsigned NOT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_contract` (`contract_id`),
KEY `idx_match_state` (`match_status`,`flow_state`),
CONSTRAINT `chk_qty` CHECK (`delivery_qty` >= 0 AND `acceptance_qty` >= 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付审批与四单匹配';
4.2.6 质量问题表(quality_issues)
CREATE TABLE `quality_issues` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`project_id` int unsigned NOT NULL,
`issue_no` varchar(40) NOT NULL COMMENT '问题编号',
`description` text NOT NULL COMMENT '问题描述',
`location` varchar(200) DEFAULT NULL COMMENT '部位',
`severity` enum('serious','normal','minor') NOT NULL COMMENT '严重程度',
`owner_id` bigint unsigned NOT NULL COMMENT '整改责任人',
`due_date` date DEFAULT NULL COMMENT '整改期限',
`status` enum('open','fixing','reviewing','closed') DEFAULT 'open',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_issue_no` (`issue_no`),
KEY `idx_status_due` (`status`,`due_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质量问题与整改跟踪';
4.2.7 健康度快照表(evm_snapshots)
每日计算完成后落一条快照,作为 SPI/CPI 趋势曲线的数据源,避免每次看板都全量重算。
CREATE TABLE `evm_snapshots` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`project_id` int unsigned NOT NULL,
`snapshot_date` date NOT NULL COMMENT '快照日期',
`bac` decimal(14,2) NOT NULL COMMENT '完工预算',
`pv` decimal(14,2) NOT NULL COMMENT '计划价值',
`ev` decimal(14,2) NOT NULL COMMENT '挣值',
`ac` decimal(14,2) NOT NULL COMMENT '实际成本',
`spi` decimal(6,3) NOT NULL,
`cpi` decimal(6,3) NOT NULL,
`eac` decimal(14,2) NOT NULL COMMENT '完工预测',
`vac` decimal(14,2) NOT NULL COMMENT '完工偏差',
`health` enum('red','yellow','green') NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_project_date` (`project_id`,`snapshot_date`),
KEY `idx_date` (`snapshot_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康度每日快照';
4.2.8 审计日志表(audit_logs)
落实"防篡改、100% 留痕"要求。before_snapshot 与 after_snapshot 存 JSON 格式的变更前后数据,任何资金与合同变更均可完整复盘。
CREATE TABLE `audit_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`user_role` varchar(40) NOT NULL,
`action` varchar(50) NOT NULL COMMENT 'create/update/delete/approve/intercept',
`resource` varchar(60) NOT NULL COMMENT '目标资源表',
`resource_id` varchar(64) NOT NULL,
`before_snapshot` json DEFAULT NULL,
`after_snapshot` json DEFAULT NULL,
`client_ip` varchar(45) NOT NULL,
`user_agent` varchar(255) DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_time` (`user_id`,`created_at`),
KEY `idx_resource` (`resource`,`resource_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='操作审计日志';
4.3 索引与性能设计
⚡索引策略
· 风险表按 (risk_level, status) 建组合索引,覆盖"查红色活跃风险"高频场景
· 支付表按 (match_status, flow_state) 建组合索引,支撑待办列表
· 快照表按 (project_id, snapshot_date) 建唯一索引,天然去重且利于趋势查询
· 审计表按 (user_id, created_at) 建组合索引,支撑按人按时间的追溯查询
· 质量问题表按 (status, due_date) 建索引,支撑"逾期未整改"预警查询
📈性能与容量
· 看板指标走每日快照读取,不实时全表聚合
· Redis 缓存看板结果 TTL 300s,降低数据库压力
· MySQL 配置 innodb_buffer_pool_size 为容器内存的 60%
· 大表(审计日志)按季度归档冷数据
· 关键查询全部命中索引,禁止全表扫描(开启慢查询日志监控)
4.4 数据字典与约束
关键字段类型选择理由
| 字段 | 类型 | 选择理由 |
| 金额类(amount) | DECIMAL(14,2) | 避免浮点误差导致资金对账不平;精确到分 |
| 单价类(unit_price) | DECIMAL(12,4) | 支持复杂计价,保留 4 位小数 |
| 绩效指数(spi/cpi) | DECIMAL(6,3) | 保留 3 位小数,足以表达 0.001 级差异 |
| 概率/影响 | TINYINT | 取值 1-5,占用最小 |
| 状态类字段 | ENUM | 取值范围固定,节省空间且自带约束 |
| 变更快照 | JSON | 结构灵活,便于存储不同资源的 diff;MySQL 8.0 原生支持 |
| IP 地址 | VARCHAR(45) | 兼容 IPv6 最长表示 |
💡 金额字段设计原则
所有涉及金额的字段一律使用 DECIMAL 而非 FLOAT/DOUBLE,避免浮点精度误差导致的资金对账不平。金额统一精确到分(2 位小数),单价精确到 4 位小数以支持复杂计价。
⚠️ 字符集与排序规则
库、表、连接三处字符集必须统一为 utf8mb4,否则中文备注字段可能出现乱码或截断。连接层需在 my.cnf 的 [client] 与 [mysql] 段落同时指定,应用层 ORM 连接参数亦需显式声明。
5.核心业务算法实现
本章是系统的技术内核。三大算法分别对应成本进度量化、资金风险拦截、风险前瞻预警三个关键管控点。
5.1 挣值分析(EVM)计算引擎
根据管理要求,系统需每日计算 SPI、CPI,并在 SPI < 0.95 时触发纠偏预警。EVM 的价值在于用一组统一指标同时回答两个问题:进度跑得比计划快还是慢?花的钱比挣的进度多还是少?
5.1.1 指标定义
| 指标 | 符号 | 公式 | 解读 |
| 计划价值 | PV | 计划完成工作量 × 预算单价 | 截止某时点"本应完成"的预算价值 |
| 挣值 | EV | 实际完成工作量 × 预算单价 | "实际完成"的预算价值 |
| 实际成本 | AC | 实际发生的全部费用 | "实际花掉"的钱 |
| 进度偏差 | SV | EV − PV | <0 表示进度滞后 |
| 成本偏差 | CV | EV − AC | <0 表示成本超支 |
| 进度绩效 | SPI | EV / PV | <1 表示进度落后,<0.95 触发纠偏 |
| 成本绩效 | CPI | EV / AC | <1 表示花钱效率低,<0.90 触发成本预警 |
| 完工预测 | EAC | BAC / CPI | 按当前效率推算,最终要花多少钱 |
| 完工偏差 | VAC | BAC − EAC | <0 表示最终将超支 |
5.1.2 核心实现(Node.js)
// services/evmEngine.js — 挣值分析核心引擎(纯函数设计,便于单元测试)
const SPI_THRESHOLD = Number(process.env.SPI_ALERT_THRESHOLD || 0.95);
const CPI_THRESHOLD = Number(process.env.CPI_ALERT_THRESHOLD || 0.90);
const round = (n, d) => Number(Number(n).toFixed(d));
/**
* 计算项目健康度指标
* @param {number} bac - 完工预算 (Budget at Completion)
* @param {number} pv - 计划价值 (Planned Value)
* @param {number} ev - 挣值 (Earned Value)
* @param {number} ac - 实际成本 (Actual Cost)
*/
function calculateHealth(bac, pv, ev, ac) {
const sv = ev - pv; // 进度偏差
const cv = ev - ac; // 成本偏差
// 防御除零:分母为 0 时视为中性值 1,避免 NaN 污染看板
const spi = pv === 0 ? 1 : ev / pv;
const cpi = ac === 0 ? 1 : ev / ac;
// 完工预测 (EAC = BAC / CPI)
const eac = cpi === 0 ? bac : bac / cpi;
const vac = bac - eac;
return {
bac: round(bac, 2),
sv: round(sv, 2), cv: round(cv, 2),
spi: round(spi, 3), cpi: round(cpi, 3),
eac: round(eac, 2), vac: round(vac, 2),
health: deriveHealth(spi, cpi),
alerts: {
schedule: spi < SPI_THRESHOLD, // SPI < 0.95 触发纠偏
cost: cpi < CPI_THRESHOLD // CPI < 0.90 触发成本预警
}
};
}
// 综合健康度:取进度与成本中最差的一维,短板决定整体
function deriveHealth(spi, cpi) {
const worst = Math.min(spi, cpi);
if (worst < 0.90) return 'red';
if (worst < 1.00) return 'yellow';
return 'green';
}
module.exports = { calculateHealth, deriveHealth, round };
5.1.3 每日计算调度
// cron/evmSnapshot.js — 每日 05:30 计算并落快照
const cron = require('node-cron');
const { calculateHealth } = require('../services/evmEngine');
cron.schedule('30 5 * * *', async () => {
const projects = await Project.findAll({ where: { status: 'active' } });
for (const p of projects) {
// 三个数据源口径统一:计划来自基准计划,挣值来自监理确认报量,成本来自财务入账
const pv = await sumPlannedValue(p.id);
const ev = await sumEarnedValue(p.id);
const ac = await sumActualCost(p.id);
const r = calculateHealth(p.bac, pv, ev, ac);
await EvmSnapshot.upsert({
project_id: p.id,
snapshot_date: new Date(),
bac: p.bac, pv, ev, ac,
spi: r.spi, cpi: r.cpi, eac: r.eac, vac: r.vac, health: r.health
});
}
console.log('[evmSnapshot] 健康度快照计算完成');
});
⚠️ 计算口径一致性
PV / EV / AC 三项数据的取数口径必须全项目统一:PV 以批准的实施计划为基准,EV 以经监理确认的报量进度为基准,AC 以财务实际入账金额为基准。口径不一致会导致 SPI/CPI 失真,反而误导决策。系统通过 evm_snapshots 固化每日口径,保证历史可比。
5.2 四单匹配自动校验逻辑
在提交支付审批时,系统自动拦截并校验"合同-送货-验收-发票"四类单据的数据一致性,防止超付、错付。这是资金安全的第一道闸门。
5.2.1 校验规则
| 序号 | 校验规则 | 业务含义 | 失败后果 |
| 规则1 | 送货量 ≤ 合同量 | 送货不能超出合同约定总量,防止供应商超量供货后强行结算 | 阻断 |
| 规则2 | 验收量 ≤ 送货量 | 验收合格量不能大于实际送达量,防止虚报验收 | 阻断 |
| 规则3 | |发票金额 − 验收核算金额| ≤ 1% | 发票金额须与"验收量×单价"匹配,允许 1% 尾差容错 | 阻断 |
| 规则4 | 申请金额 ≤ 可付余额 | 累计申请不得超出合同总额,防止重复支付 | 阻断 |
5.2.2 核心实现
// services/paymentValidator.js — 四单匹配校验器
const INVOICE_TOLERANCE_RATIO = Number(process.env.INVOICE_TOLERANCE_RATIO || 0.01);
function validateFourWayMatch(data) {
const {
contractQty, deliveryQty, acceptanceQty,
invoiceAmount, unitPrice, applyAmount, contractTotal
} = data;
const errors = [];
const push = (code, msg) => errors.push({ code, msg });
// 规则1:送货量不能大于合同量
if (deliveryQty > contractQty) {
push('R1', `送货数量(${deliveryQty})超出合同约定总量(${contractQty})`);
}
// 规则2:验收量不能大于送货量
if (acceptanceQty > deliveryQty) {
push('R2', `验收合格数量(${acceptanceQty})大于实际送货数量(${deliveryQty})`);
}
// 规则3:发票金额与验收核算金额匹配(允许 1% 尾差容错)
const expectedAmount = acceptanceQty * unitPrice;
const tolerance = expectedAmount * INVOICE_TOLERANCE_RATIO;
if (Math.abs(invoiceAmount - expectedAmount) > tolerance) {
push('R3',
`发票金额(${invoiceAmount})与验收核算金额(${expectedAmount.toFixed(2)})不匹配,` +
`偏差超出 ${(INVOICE_TOLERANCE_RATIO * 100).toFixed(0)}% 容限`);
}
// 规则4:申请金额不得超出合同可付余额
if (typeof contractTotal === 'number' && applyAmount > contractTotal) {
push('R4', `申请金额(${applyAmount})超出合同可付余额(${contractTotal})`);
}
const isValid = errors.length === 0;
return {
isValid,
matchStatus: isValid ? 'matched' : 'mismatch',
errors
};
}
module.exports = { validateFourWayMatch };
✅ 拦截效果
校验不通过时,接口直接返回 mismatch 并附精确到具体规则的错误明细,前端在提交按钮处阻断,同时将失败尝试写入审计日志。所有校验均以纯函数实现,而非数据库触发器,便于单元测试与规则迭代。
5.3 风险红黄绿状态机与定时预警
风险等级由"发生概率(1-5)× 影响程度(1-5)"的乘积决定,形成 5×5 风险矩阵。结合 node-cron 实现每日凌晨扫描,落实"重大风险提前 15 日预警"的要求。
5.3.1 风险等级判定矩阵
| 概率 \ 影响 | 1 轻微 | 2 较小 | 3 中等 | 4 严重 | 5 灾难 |
| 5 极高 | 黄(5) | 红(10) | 红(15) | 红(20) | 红(25) |
| 4 较高 | 绿(4) | 黄(8) | 红(12) | 红(16) | 红(20) |
| 3 中等 | 绿(3) | 黄(6) | 黄(9) | 红(12) | 红(15) |
| 2 较低 | 绿(2) | 绿(4) | 黄(6) | 黄(8) | 红(10) |
| 1 极低 | 绿(1) | 绿(2) | 绿(3) | 绿(4) | 黄(5) |
判定规则:乘积 ≥ 10 为红;5 ≤ 乘积 < 10 为黄;乘积 < 5 为绿。阈值可在系统配置中调整。
5.3.2 状态机与核心实现
// services/riskEngine.js — 风险等级计算与状态流转校验
const RED_THRESHOLD = Number(process.env.RISK_RED_THRESHOLD || 10);
const YELLOW_THRESHOLD = Number(process.env.RISK_YELLOW_THRESHOLD || 5);
// 合法状态流转路径(防止跨级跳跃)
const VALID_TRANSITIONS = {
active: ['mitigated'],
mitigated: ['closed', 'active'],
closed: []
};
function calcRiskLevel(probability, impact) {
const p = Number(probability), i = Number(impact);
if (!Number.isInteger(p) || p < 1 || p > 5) throw new Error('发生概率必须为 1-5 的整数');
if (!Number.isInteger(i) || i < 1 || i > 5) throw new Error('影响程度必须为 1-5 的整数');
const score = p * i;
let level = 'green';
if (score >= RED_THRESHOLD) level = 'red';
else if (score >= YELLOW_THRESHOLD) level = 'yellow';
return { score, level };
}
function validateTransition(from, to) {
if (!VALID_TRANSITIONS[from]) return { valid: false, reason: `未知状态:${from}` };
if (!VALID_TRANSITIONS[from].includes(to)) return { valid: false, reason: `不允许从 ${from} 直接流转到 ${to}` };
return { valid: true };
}
5.3.3 定时预警任务
// cron/riskScanner.js — 每日 06:00 扫描红色风险并提前预警
const cron = require('node-cron');
const { Op } = require('sequelize');
const ALERT_DAYS = Number(process.env.RISK_ALERT_DAYS || 15);
cron.schedule('0 6 * * *', async () => {
const today = new Date();
const thresholdDate = new Date();
thresholdDate.setDate(today.getDate() + ALERT_DAYS);
// 查找未来 15 天内预计发生的红色风险
const criticalRisks = await Risk.findAll({
where: {
risk_level: 'red',
status: 'active',
expected_occur_date: { [Op.lte]: thresholdDate, [Op.gte]: today }
}
});
for (const risk of criticalRisks) {
const daysLeft = Math.ceil((risk.expected_occur_date - today) / 86400000);
await NotificationService.sendRiskAlert({
to: risk.owner_id,
cc: ['project_director'], // 抄送项目总监
title: `【红色预警】${risk.description}`,
msg: `距离预计发生仅剩 ${daysLeft} 天,请立即启动应对措施`
});
}
console.log(`[riskScanner] 风险扫描完成,预警 ${criticalRisks.length} 条`);
});
🔔 通知渠道
通知服务采用可插拔设计:初期以站内消息 + 邮件为主,后续可按需接入企业微信 / 钉钉机器人。通知记录同步写入 audit_logs,形成"预警已触达"的证据链,避免责任推诿。
6.API 接口规范
6.1 统一响应与鉴权
所有接口采用 JWT Bearer Token 鉴权。统一返回格式 { code, message, data, traceId },其中 code=0 表示成功,非 0 为业务错误码。
📦成功响应
{
"code": 0,
"message": "success",
"data": { /* 业务数据 */ },
"traceId": "a1b2c3d4"
}
⚠️错误响应
{
"code": 40001,
"message": "四单匹配校验失败",
"data": null,
"traceId": "a1b2c3d4"
}
通用请求约定
| 约定项 | 说明 |
| 版本前缀 | 所有接口以 /api/v1 开头,便于后续版本并行 |
| 鉴权头 | Authorization: Bearer <jwt> |
| 内容类型 | Content-Type: application/json |
| 分页参数 | ?page=1&pageSize=20,返回 { list, total, page, pageSize } |
| 时间格式 | ISO 8601,含时区,如 2026-10-11T13:45:00+08:00 |
| 幂等性 | 资金类 POST 接口要求携带 Idempotency-Key 请求头,防止重复提交 |
6.2 接口清单
| 模块 | 方法 | 端点 (Endpoint) | 说明 |
| 认证 | POST | /api/v1/auth/login | 登录获取 JWT Token |
| 认证 | POST | /api/v1/auth/logout | 登出,Token 加入吊销黑名单 |
| 健康度 | GET | /api/v1/health/dashboard | 获取最新四维健康度指标及 EVM 数据 |
| 健康度 | GET | /api/v1/health/trend | 获取 SPI/CPI 趋势曲线数据 |
| 风险 | GET | /api/v1/risks | 风险清单(支持筛选分页) |
| 风险 | POST | /api/v1/risks | 新增风险登记,自动计算红黄绿等级 |
| 风险 | PUT | /api/v1/risks/:id | 更新风险状态与处置记录 |
| 合同 | GET | /api/v1/contracts | 合同台账列表 |
| 支付 | POST | /api/v1/payments/validate | 提交前执行四单匹配自动校验 |
| 支付 | POST | /api/v1/payments/submit | 校验通过后,发起审批流转 |
| 支付 | POST | /api/v1/payments/:id/approve | 审批通过(角色受控) |
| 进度 | POST | /api/v1/schedules/report | 提交进度报量 |
| 质量 | POST | /api/v1/quality/issues | 登记质量问题 |
| 质量 | PUT | /api/v1/quality/issues/:id | 更新整改进度与销项 |
| 审计 | GET | /api/v1/audit/logs | 审计日志查询(仅总监与审计角色) |
| 健康检查 | GET | /api/v1/ping | 服务存活探针 |
6.3 关键接口示例
6.3.1 四单匹配校验
POST /api/v1/payments/validate
Authorization: Bearer <jwt>
Content-Type: application/json
{
"contractId": 1024,
"contractQty": 1000,
"deliveryQty": 800,
"acceptanceQty": 780,
"unitPrice": 256.50,
"invoiceAmount": 199800.00,
"applyAmount": 199800.00
}
// ---------- 校验通过 ----------
{ "code": 0, "message": "success", "data": {
"isValid": true, "matchStatus": "matched", "errors": [] } }
// ---------- 校验失败 ----------
{
"code": 40001,
"message": "四单匹配校验失败",
"data": {
"isValid": false,
"matchStatus": "mismatch",
"errors": [
{ "code": "R3", "msg": "发票金额(199800)与验收核算金额(200070.00)不匹配,偏差超出 1% 容限" }
]
}
}
6.3.2 新增风险登记
POST /api/v1/risks
{
"category": "进度风险",
"description": "主结构钢材供应商产能不足,可能导致封顶节点延误",
"probability": 4,
"impact": 4,
"expectedOccurDate": "2026-10-22",
"ownerId": 1007
}
// ---------- 响应:系统已自动判定等级 ----------
{ "code": 0, "data": {
"id": 29,
"riskScore": 16,
"riskLevel": "red",
"status": "active"
} }
6.3.3 获取健康度看板
GET /api/v1/health/dashboard?projectId=1
{ "code": 0, "data": {
"health": "yellow",
"metrics": {
"bac": 85000000.00, "pv": 49300000.00,
"ev": 46240000.00, "ac": 44200000.00,
"spi": 0.938, "cpi": 1.046,
"eac": 81250000.00, "vac": 3750000.00
},
"alerts": { "schedule": true, "cost": false },
"riskSummary": { "red": 3, "yellow": 5, "green": 8 }
} }
💡 接口设计原则
所有涉及资金、合同变更的 POST/PUT 接口,必须在中间件层强制写入 audit_logs 审计表,记录操作人、IP、变更前后的 JSON 快照。审计写入失败时,业务操作应一并回滚,保证"无留痕即无操作"。
6.4 错误码规范
| 错误码 | 含义 | 处理建议 |
0 | 成功 | — |
400 | 请求参数错误 | 前端提示具体字段错误,不重试 |
401 | 未授权 / Token 失效 | 跳转登录页 |
403 | 权限不足 | 提示无权限,不重试 |
404 | 资源不存在 | 提示并返回列表 |
40001 | 四单匹配校验失败 | 展示 errors 明细,人工修正后重提 |
40002 | 支付流程状态非法 | 刷新页面获取最新状态 |
40003 | 风险状态流转非法 | 提示合法的下一步状态 |
40004 | 账号已停用 | 提示联系管理员 |
429 | 请求过于频繁 | 退避重试 |
500 | 服务器内部错误 | 记录 traceId 便于排查 |
7.前端设计与交互规范
7.1 前端技术栈
| 技术 | 版本 | 用途与理由 |
| Vue 3 | 3.4+ | 组合式 API,逻辑复用强,性能优于 Vue2 |
| TypeScript | 5.x | 静态类型,保障前后端接口数据结构一致 |
| Vite | 5.x | 极快的冷启动与热更新,构建产物小 |
| Pinia | 2.x | 状态管理,替代 Vuex,TypeScript 友好 |
| Vue Router | 4.x | 路由与权限守卫(按角色动态生成菜单) |
| Element Plus | 2.x | 管理后台组件库,表格/表单/弹窗完备 |
| ECharts | 5.x | 看板趋势图、仪表盘、柱状图 |
| Axios | 1.x | HTTP 客户端,统一拦截器处理鉴权与错误 |
7.2 页面结构与导航
系统采用"左侧导航 + 顶部栏 + 主内容区"的经典管理后台布局,响应式适配 PC 与平板。
🧭导航结构
左侧固定导航按功能域分组;顶部栏显示面包屑、当前用户与角色;主内容区承载页面。移动端导航收起为抽屉,避免拥挤。
🔐权限驱动菜单
菜单项由当前用户角色动态过滤,无权限的入口直接不渲染,避免"点进去才发现没权限"的糟糕体验。
页面清单
| 页面 | 路由 | 主要内容 | 可见角色 |
| 登录 | /login | 账号密码登录、失败锁定提示 | 全部(未登录) |
| 健康度看板 | /dashboard | 四维健康度、EVM 指标、趋势曲线、标段完成率、风险提示 | 全角色(视图不同) |
| 风险登记册 | /risk | 风险列表、登记表单、处置流转、等级统计 | 项目经理及以上 |
| 合同与支付 | /payment | 合同台账、支付申请、四单校验结果、审批流转 | 成本岗、项目经理、总监 |
| 进度管理 | /schedule | WBS、里程碑、报量录入、计划/实际对比、关键路径 | 项目经理、施工方 |
| 质量管理 | /quality | 质量问题列表、整改闭环、逾期提醒 | 质量工程师、项目经理 |
| 审计日志 | /audit | 操作记录查询、变更快照对比、导出 | 总监、审计角色 |
| 系统管理 | /system | 用户管理、角色权限矩阵、业务阈值配置 | 总监、系统管理员 |
7.3 关键交互设计
7.3.1 四单匹配的交互流程
这是资金安全的关键交互。设计目标是让用户在提交前就清楚看到校验结果,而不是提交后被驳回。
💡 交互要点
① 校验失败时,提交按钮置灰并标注原因,而非点击后才报错;② 错误提示精确到规则编号与数值(例如"发票金额 199,800 与核算金额 200,070 偏差超限"),便于定位;③ 失败尝试同样写入审计日志,形成完整证据链。
7.3.2 风险登记的即时反馈
用户在填写概率与影响时,界面实时显示计算出的风险等级与颜色,让用户建立"风险值"的直觉,而不是提交后才知道等级。
| 交互动作 | 系统响应 |
| 选择概率 / 影响 | 即时计算乘积,显示风险值与等级徽章(红/黄/绿) |
| 等级为红色 | 提示"将在预计发生日前 15 天自动推送责任人与项目总监" |
| 提交成功 | 列表即时刷新,等级统计卡实时更新 |
7.3.3 通用交互规范
| 规范项 | 要求 |
| 加载态 | 所有异步请求显示骨架屏或 loading,禁止白屏 |
| 错误态 | 请求失败展示可重试的提示,附 traceId |
| 空状态 | 列表无数据时展示引导文案与操作入口 |
| 危险操作 | 删除、回滚等二次确认,明确后果 |
| 表单校验 | 前端即时校验 + 后端强校验,双重保障 |
| 数字展示 | 金额千分位分隔,指标保留约定小数位 |
| 无障碍 | 表单标签关联输入框,颜色对比度达标 |
8.安全与权限控制(RBAC)
权限设计遵循最小权限原则与职责分离原则:发起支付的人不能同时是终审人,登记风险的人不能自己确认风险关闭。
8.1 权限矩阵设计
| 角色 (Role) | 健康度看板 | 风险登记/处置 | 支付审批 | 审计日志 | 系统配置 |
| 项目总监 | ✅ 全局视图 | ✅ 审批/督办 | ✅ 终审 | ✅ 只读 | ✅ |
| 项目经理 | ✅ 全局视图 | ✅ 登记/更新 | ✅ 复审 | 👁️ 本项目 | ❌ |
| 成本/合同工程师 | 👁️ 成本视图 | ❌ | ✅ 发起/初审 | ❌ | ❌ |
| 质量/安全工程师 | 👁️ 质量视图 | ✅ 登记/整改 | ❌ | ❌ | ❌ |
| 监理/施工方(外部) | 👁️ 受限视图 | ✅ 仅上报 | ❌ | ❌ | ❌ |
| 审计角色 | 👁️ 全局只读 | 👁️ 只读 | 👁️ 只读 | ✅ 全量 | ❌ |
图例:✅ 完整权限 👁️ 只读/受限视图 ❌ 无权限
角色定义
| 角色标识 | 中文名 | 职责边界 |
project_director | 项目总监 | 全局决策、重大支付终审、风险督办、系统配置 |
project_manager | 项目经理 | 日常推进、风险登记与更新、支付复审、进度确认 |
cost_engineer | 成本/合同工程师 | 合同台账维护、四单数据录入、支付发起与初审 |
qs_engineer | 质量/安全工程师 | 质量问题登记与整改跟踪、隐患排查 |
auditor | 审计角色 | 只读查询全部业务与审计数据,不可修改 |
external | 监理 / 施工方 | 仅可上报进度、验收与送货数据,视图受限 |
8.2 鉴权中间件实现(Node.js)
// middlewares/auth.js — JWT 鉴权 + 角色校验 + 账号状态校验
const jwt = require('jsonwebtoken');
const redis = require('../config/redis');
const authorize = (...allowedRoles) => {
return async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ code: 401, message: '未授权' });
try {
// 1) 校验 Token 是否已被吊销(登出或强制下线场景)
const revoked = await redis.get(`revoked:${token}`);
if (revoked) return res.status(401).json({ code: 401, message: 'Token 已失效' });
// 2) 验证签名与有效期
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
// 3) 校验角色权限
if (!allowedRoles.includes(decoded.role)) {
return res.status(403).json({ code: 403, message: '权限不足,拒绝访问' });
}
// 4) 要求:乙方离职人员 24 小时内注销(校验账号状态)
if (decoded.is_disabled) {
return res.status(403).json({ code: 40004, message: '账号已停用' });
}
next();
} catch (err) {
res.status(401).json({ code: 401, message: 'Token 无效或已过期' });
}
};
};
// 路由使用示例:仅项目总监和项目经理可审批支付
router.post('/approve', authorize('project_director', 'project_manager'), controller.approve);
module.exports = { authorize };
8.3 审计与防篡改
防篡改是资金类系统的底线要求。本系统从三个层面构建证据链。
①写前留痕
变更类接口在中间件层读取变更前快照,写入 before_snapshot,业务事务提交后写入 after_snapshot,形成完整 diff。
②失败回滚
审计写入与业务操作置于同一事务中。审计失败则业务回滚,确保不存在"改了数据却没留痕"的情况。
③append-only
审计表仅允许 INSERT,数据库账号层面收回 UPDATE/DELETE 权限,从权限模型上杜绝日志被篡改。
// middlewares/auditMiddleware.js — 变更留痕(简化示意)
const auditChange = (resource) => async (req, res, next) => {
const before = await loadSnapshot(resource, req.params.id);
// 挂载到 req,业务处理完后由响应拦截器统一落库(与业务同事务)
req.__audit = {
resource,
resourceId: req.params.id,
userId: req.user.id,
userRole: req.user.role,
before,
clientIp: req.ip,
userAgent: req.headers['user-agent']
};
next();
};
// 数据库账号权限收紧(DDL 层面)
-- GRANT SELECT,INSERT,UPDATE ON zh_euro_park_db.* TO 'pms_app'@'%';
-- GRANT INSERT ON zh_euro_park_db.audit_logs TO 'pms_app'@'%'; -- 仅追加,不可改删
8.4 其他安全措施
| 措施 | 实现方式 |
| 密码存储 | bcrypt(cost=12)加盐哈希,不可逆存储 |
| 登录防爆破 | Redis 记录失败次数,5 次失败锁定 15 分钟 |
| 接口限流 | Nginx + Redis 双层限流,单 IP 100 req/min |
| 幂等保护 | 资金类接口要求 Idempotency-Key,防止重复提交造成重复支付 |
| SQL 注入防护 | ORM 参数化查询,禁止拼接 SQL |
| XSS 防护 | 前端输出转义 + CSP 响应头 |
| CSRF 防护 | 使用 Bearer Token(非 Cookie),天然规避 CSRF |
| 传输加密 | 强制 HTTPS,TLS 1.2+,HSTS max-age=31536000 |
| 敏感信息 | 日志脱敏,不打印密码/Token/密钥 |
| 依赖安全 | 定期执行 npm audit,及时修复高危依赖 |
| 最小权限运行 | 容器以非 root 用户运行,降低逃逸风险 |
9.系统部署方案
本章给出系统的完整部署方案。系统以容器化方式部署在云服务器上,通过统一域名对外提供 HTTPS 服务。方案本身可直接执行,不依赖任何特定的服务器运行状态。
9.1 部署架构与前置条件
9.1.1 服务器目录规划
/opt/zh-park-pms/ # 部署根目录
├── docker-compose.yml # 容器编排定义
├── .env # 环境变量(权限 600,不入库)
├── docker/
│ ├── Dockerfile # 后端应用镜像
│ ├── nginx.conf # Nginx 站点配置
│ ├── my.cnf # MySQL 性能参数
│ └── ssl/ # 证书目录
├── src/ # 后端源码
├── frontend/dist/ # 前端构建产物(静态)
├── sql/
│ └── schema.sql # 数据库结构
└── scripts/
├── deploy.sh # 一键部署脚本
└── backup.sh # 备份脚本
9.1.2 前置条件清单
| ✓ | 项目 | 要求 |
| ☐ | 云服务器 | ≥ 2 核 4G,≥ 40G 系统盘,Ubuntu 22.04 LTS(或同级 Linux) |
| ☐ | 公网 IP | 服务器具备固定公网 IP,供域名解析指向 |
| ☐ | 域名 | 已持有目标域名,并具备 DNS 解析管理权限 |
| ☐ | 防火墙 | 放行 80 / 443;运维管理端口建议改非标准并限制来源 IP |
| ☐ | 软件环境 | Docker 24+、Docker Compose v2 |
| ☐ | 备案 | 境内服务器须完成域名 ICP 备案,否则 80/443 会被阻断 |
| ☐ | 对象存储 | 用于异地备份的存储桶(可选,见 §10.1) |
9.2 域名与 DNS 解析
系统对外访问统一使用一个业务子域名(示例以 pms.example.com 表示,实际替换为甲方域名)。解析操作在域名托管服务商的 DNS 控制台完成。
9.2.1 解析记录配置
| 记录类型 | 主机记录 | 记录值 | TTL | 说明 |
| A | pms | <服务器公网 IP> | 600 | 核心记录:业务子域名指向服务器 |
| CNAME | www | 按现有配置保持不变 | — | 如无需要,无需改动 |
⚠️ 解析注意事项
1. 主机记录填子域名前缀(如 pms),不要填完整域名——DNS 服务商会自动拼接主域。
2. 若服务器公网 IP 会变化,应使用弹性公网 IP 或 DDNS,避免解析失效。
3. 若域名未完成备案,境内服务器的 80/443 会被运营商阻断,即使解析正确也无法访问,务必先行确认。
9.2.2 验证解析生效
# 方式一:nslookup
nslookup pms.example.com
# 期望输出:Address: <服务器公网 IP>
# 方式二:dig(Linux/macOS)
dig pms.example.com +short
# 方式三:HTTP 探测(解析生效后)
curl -I http://pms.example.com
9.3 容器化编排(Docker Compose)
将 Node.js 应用、MySQL 8.0、Redis 及 Nginx 统一编排,一键启动。为确保环境一致性,摒弃传统的环境直装方式。
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: docker/Dockerfile
container_name: zh-park-api
restart: always
env_file: .env
environment:
- NODE_ENV=production
- DB_HOST=mysql
- REDIS_HOST=redis
- TZ=Asia/Shanghai
depends_on:
mysql: { condition: service_healthy }
redis: { condition: service_started }
networks: [park-net]
# 不映射端口到宿主机,仅内网可达
mysql:
image: mysql:8.0
container_name: zh-park-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PWD}
MYSQL_DATABASE: ${DB_NAME}
TZ: Asia/Shanghai
volumes:
- mysql_data:/var/lib/mysql
- ./docker/my.cnf:/etc/mysql/conf.d/my.cnf:ro
- ./sql:/docker-entrypoint-initdb.d:ro
networks: [park-net]
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 10
# 注意:不配置 ports,3306 仅内网可达
redis:
image: redis:7-alpine
container_name: zh-park-redis
restart: always
command: redis-server --requirepass ${REDIS_PWD} --appendonly yes
volumes:
- redis_data:/data
networks: [park-net]
# 注意:不配置 ports,6379 仅内网可达
nginx:
image: nginx:alpine
container_name: zh-park-nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./frontend/dist:/usr/share/nginx/html:ro
- ./docker/ssl:/etc/nginx/ssl:ro
- certbot_www:/var/www/certbot
depends_on: [app]
networks: [park-net]
volumes:
mysql_data:
redis_data:
certbot_www:
networks:
park-net:
driver: bridge
9.3.1 后端 Dockerfile
# docker/Dockerfile
FROM node:20-alpine
RUN apk add --no-cache tzdata wget \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
WORKDIR /app
# 优先拷贝依赖清单,利用镜像层缓存加速重复构建
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
# 以非 root 用户运行,降低容器逃逸风险
RUN addgroup -S app && adduser -S app -G app && chown -R app:app /app
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/api/v1/ping || exit 1
CMD ["node", "src/app.js"]
9.4 Nginx 站点配置
Nginx 承担 TLS 终止、静态资源服务与 API 反向代理三项职责。为业务子域名建立独立 server 块,避免与其他站点互相干扰。
# docker/nginx.conf — 业务子域名站点配置
# ---------- HTTP:仅用于 ACME 验证与跳转 HTTPS ----------
server {
listen 80;
server_name pms.example.com;
# 域名验证目录(webroot 模式)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 健康检查端点(供监控探测)
location /healthz {
return 200 "ok\n";
add_header Content-Type text/plain;
}
# 其余请求全部跳转到 HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# ---------- HTTPS:正式服务 ----------
server {
listen 443 ssl http2;
server_name pms.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# HSTS:强制浏览器后续均使用 HTTPS
add_header Strict-Transport-Security "max-age=31536000" always;
# 安全响应头
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
# gzip 压缩
gzip on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml image/svg+xml;
root /usr/share/nginx/html;
index index.html;
# ---- 前端静态资源(SPA 路由回退)----
location / {
try_files $uri $uri/ /index.html;
}
# 带 hash 的构建产物可长期缓存
location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2?|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# ---- API 反向代理到 app 容器 ----
location /api/ {
proxy_pass http://app:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
}
client_max_body_size 20m;
}
9.4.1 MySQL 性能参数(my.cnf)
# docker/my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci
# InnoDB 缓冲池:建议设为容器可用内存的 60%(2G 容器示例)
innodb_buffer_pool_size = 768M
max_connections = 200
innodb_flush_log_at_trx_commit = 2
max_allowed_packet = 64M
default-time-zone = '+08:00'
# 慢查询日志(超过 1 秒记录)
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/slow.log
long_query_time = 1
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
9.5 HTTPS 证书配置
采用免费 Let's Encrypt 证书(webroot 模式)申请,并配置定时续期,避免证书过期导致站点不可访问。
# 首次申请证书(DNS 已生效后执行)
# 1) 先用 HTTP 配置启动容器(nginx.conf 中已将 acme-challenge 指向 /var/www/certbot)
docker compose up -d nginx
# 2) 使用 certbot 容器申请证书
docker run --rm \
-v "$PWD/docker/ssl:/etc/letsencrypt" \
-v "$PWD/certbot_www:/var/www/certbot" \
certbot/certbot certonly --webroot \
-w /var/www/certbot \
-d pms.example.com \
--email admin@example.com --agree-tos --no-eff-email --non-interactive
# 3) 将证书拷贝到 nginx 读取路径
cp docker/ssl/live/pms.example.com/fullchain.pem docker/ssl/fullchain.pem
cp docker/ssl/live/pms.example.com/privkey.pem docker/ssl/privkey.pem
# 4) 重载 Nginx 使 HTTPS 生效
docker compose exec nginx nginx -s reload
9.5.1 证书自动续期(Cron)
# crontab -e 添加(每月 1 日凌晨 2:30 检查续期)
30 2 1 * * cd /opt/zh-park-pms && docker run --rm \
-v "$PWD/docker/ssl:/etc/letsencrypt" \
-v "$PWD/certbot_www:/var/www/certbot" \
certbot/certbot renew --webroot -w /var/www/certbot --quiet \
&& docker compose exec nginx nginx -s reload
🔐 证书方案备选
若需与已存在的其他站点共用同一张证书,可申请多域名证书(增加 -d 参数)。若甲方偏好商业证书或通配符证书,则替换证书文件路径即可,Nginx 配置无需改动。证书文件(.key)权限须为 600,仅 root 可读。
9.6 上线部署操作步骤
以下步骤按顺序执行,形成完整的首次上线流程。
STEP 1 · 服务器初始化
安装 Docker 环境
apt update && apt install -y ca-certificates curl git
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker --version && docker compose version
STEP 2 · 代码部署
上传代码并配置环境变量
mkdir -p /opt/zh-park-pms && cd /opt/zh-park-pms
# 上传后端源码、前端 dist、部署配置
# 生成 .env(严禁提交到代码仓库)
cp .env.example .env && chmod 600 .env
# 编辑 .env 填入 DB_ROOT_PWD / REDIS_PWD / JWT_SECRET / ENCRYPT_KEY 等
# 使用强随机密钥: openssl rand -hex 32
STEP 3 · 配置 DNS
在 DNS 控制台添加解析记录
按 §9.2 添加 A 记录,主机记录为子域名前缀,指向服务器公网 IP。用 nslookup 验证生效后再进行下一步——若未先生效,Certbot 申请证书会失败。
STEP 4 · 启动容器
构建并启动全部服务
cd /opt/zh-park-pms
docker compose up -d --build
docker compose ps
STEP 5 · 初始化数据库
导入 DDL 并创建最小权限账号
docker compose exec -T mysql \
mysql -uroot -p$DB_ROOT_PWD $DB_NAME < sql/schema.sql
# 创建最小权限应用账号(不要用 root 跑应用)
docker compose exec -T mysql mysql -uroot -p$DB_ROOT_PWD -e "
CREATE USER 'pms_app'@'%' IDENTIFIED BY '<强密码>';
GRANT SELECT,INSERT,UPDATE ON zh_euro_park_db.* TO 'pms_app'@'%';
GRANT INSERT ON zh_euro_park_db.audit_logs TO 'pms_app'@'%';
FLUSH PRIVILEGES;"
STEP 6 · 申请证书
启用 HTTPS
按 §9.5 执行 Certbot 申请命令,成功后重载 Nginx。
STEP 7 · 验收测试
功能与安全验证
按 §9.7 执行验证清单,全部通过后交付。
STEP 8 · 配置备份与监控
上线收尾
配置 §10 的自动备份 Cron 与监控告警,关闭不必要端口。
9.7 验证与回滚预案
9.7.1 上线验证清单
| ✓ | 验证项 | 验证方法 | 预期结果 |
| ☐ | DNS 解析 | nslookup pms.example.com | 返回服务器公网 IP |
| ☐ | HTTP 跳转 | curl -I http://pms.example.com | 301 → https |
| ☐ | HTTPS 可用 | curl -I https://pms.example.com | 200,证书有效 |
| ☐ | 证书链完整 | openssl s_client -connect pms.example.com:443 | Verify return code: 0 |
| ☐ | HSTS 生效 | curl -I https://pms.example.com | 含 Strict-Transport-Security |
| ☐ | API 连通 | curl https://pms.example.com/api/v1/ping | {"code":0} |
| ☐ | 数据库端口隔离 | 外部探测 3306 | 拒绝连接 |
| ☐ | 缓存端口隔离 | 外部探测 6379 | 拒绝连接 |
| ☐ | 登录功能 | 浏览器登录测试账号 | 成功进入看板 |
| ☐ | 权限隔离 | 用低权限账号访问审批接口 | 返回 403 |
| ☐ | 四单校验 | 提交不匹配数据 | 被阻断并提示原因 |
| ☐ | 审计留痕 | 查询 audit_logs | 存在完整变更快照 |
| ☐ | 容器自愈 | docker kill zh-park-api | 自动重启恢复 |
9.7.2 回滚预案
🚨 回滚触发条件
上线后出现核心功能不可用(登录失败、看板无法加载、支付接口报错)且 30 分钟内无法定位修复时,立即执行回滚,优先恢复服务可用性,事后复盘。
# 回滚方案 A:镜像回滚(推荐,最快)
docker compose down
docker tag zh-park-api:previous zh-park-api:latest
docker compose up -d
docker compose logs -f app
# 回滚方案 B:代码回滚
cd /opt/zh-park-pms
git log --oneline -5 # 找到上一个稳定 commit
git checkout <stable-commit>
docker compose up -d --build
# 回滚方案 C:数据库回滚(仅在数据损坏时使用)
openssl enc -d -aes-256-cbc -pbkdf2 \
-in /data/backups/zh_park_db_YYYYMMDD.sql.gz.enc \
-out /tmp/restore.sql.gz -pass pass:$ENCRYPT_KEY
gunzip -c /tmp/restore.sql.gz | docker compose exec -T mysql \
mysql -uroot -p$DB_ROOT_PWD $DB_NAME
⚠️ 数据回滚需谨慎
数据库回滚属于破坏性操作,会导致备份时间点之后的所有业务数据丢失。执行前必须:① 先备份当前状态;② 经项目总监与甲方双重确认;③ 记录回滚原因与时间点,纳入审计。
10.运维保障与数据主权
10.1 自动化备份策略
落实"数据所有权归甲方"要求,配置 Cron 定时将 MySQL 数据 dump 并加密上传至对象存储。密钥由甲方持有,服务方无权解密。
#!/bin/bash
# scripts/backup.sh — 每日凌晨 3:00 执行
set -euo pipefail
# 载入环境变量
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[ -f "${SCRIPT_DIR}/../.env" ] && source "${SCRIPT_DIR}/../.env"
DATE="$(date +%Y%m%d_%H%M%S)"
BACKUP_DIR="${BACKUP_DIR:-/data/backups}"
RETENTION_DAYS="${RETENTION_DAYS:-30}"
FILE_NAME="zh_park_db_${DATE}.sql.gz"
mkdir -p "${BACKUP_DIR}"
# 1. 导出并压缩(--single-transaction 保证一致性且不锁表)
docker exec zh-park-db mysqldump \
-u root -p"${DB_ROOT_PWD}" \
--single-transaction --routines --triggers --events \
"${DB_NAME}" | gzip > "${BACKUP_DIR}/${FILE_NAME}"
# 2. OpenSSL 加密(甲方持有解密密钥)
if [ -z "${ENCRYPT_KEY:-}" ]; then
echo "错误:未配置 ENCRYPT_KEY,拒绝生成未加密备份" >&2
rm -f "${BACKUP_DIR}/${FILE_NAME}"; exit 1
fi
openssl enc -aes-256-cbc -salt -pbkdf2 \
-in "${BACKUP_DIR}/${FILE_NAME}" \
-out "${BACKUP_DIR}/${FILE_NAME}.enc" \
-pass pass:"${ENCRYPT_KEY}"
# 3. 删除本地明文,避免残留
rm -f "${BACKUP_DIR}/${FILE_NAME}"
# 4. 上传至对象存储(异地容灾)
if command -v coscmd >/dev/null 2>&1 && [ -n "${COS_BUCKET:-}" ]; then
coscmd upload "${BACKUP_DIR}/${FILE_NAME}.enc" /db-backups/
fi
# 5. 清理过期本地备份
find "${BACKUP_DIR}" -type f -name "*.enc" -mtime +"${RETENTION_DAYS}" -delete
echo "[$(date)] Backup completed: ${FILE_NAME}.enc" >> /var/log/backup.log
备份策略参数
| 维度 | 策略 |
| 执行频率 | 每日 03:00(业务低峰期) |
| 本地保留 | 30 天 |
| 异地保留 | 对象存储,保留 180 天(可转低频存储降本) |
| 加密算法 | AES-256-CBC + PBKDF2 密钥派生 |
| 密钥管理 | 由甲方单独保管,不进代码库、不告知运维人员 |
| 恢复演练 | 每季度一次,验证备份可解密、可恢复 |
✅ 数据主权三原则
① 数据归属:全部业务数据归属甲方,服务方仅有运维所需的受限访问权限;② 密钥独占:备份加密密钥由甲方持有,确保服务方技术上无法读取全量明文数据;③ 退出交付:合同终止时,提供全量数据导出(开放格式)与解密工具,确保甲方可独立迁移。
10.2 监控与告警
📊基础监控
· 容器状态:docker compose ps + restart: always
· 资源水位:CPU / 内存 / 磁盘,磁盘使用率超 80% 告警
· 服务存活:容器 HEALTHCHECK 每 30s 探测 /api/v1/ping
🔔应用监控
· 接口错误率:5xx 占比超 1% 告警
· 定时任务:EVM 日算与风险扫描失败即告警
· 登录异常:短时间内大量失败触发安全告警
📜日志管理
· 应用日志:docker compose logs + 按天切割
· Nginx 访问日志:保留 30 天
· 审计日志:数据库留存 ≥ 180 天
🚨告警通道
· 初期:邮件 + 站内消息
· 后续可接入企业微信 / 钉钉机器人
· 分级告警:P0 电话+群消息,P1 群消息,P2 邮件
10.3 数据主权与技术退出保障
| 保障项 | 具体措施 |
| 部署自主 | 系统部署于甲方掌控的服务器,不留后门、不设隐蔽远程维护通道 |
| 源码交付 | 项目验收时交付完整源码、构建脚本、部署文档,甲方可独立维护 |
| 数据导出 | 内置全量导出功能,支持 SQL / CSV 开放格式 |
| 无供应商锁定 | 技术栈均为开源组件(Node/MySQL/Redis/Nginx),无商业授权依赖 |
| 密钥独占 | 备份加密密钥由甲方持有,构成技术上的数据保护屏障 |
| 人员交接 | 乙方人员变更时,账号 24 小时内注销,权限交接留痕 |
11.测试与质量保证
11.1 测试策略与层级
采用金字塔式测试策略:底层单元测试覆盖核心算法,中层集成测试覆盖接口与数据库,顶层端到端测试覆盖关键业务流程。
🧪单元测试
目标:核心算法 100% 分支覆盖
工具:Jest
对象:EVM、四单匹配、风险状态机
🔗集成测试
目标:接口 + 数据库联动正确
工具:Supertest + 测试库
对象:API、事务、审计落库
🎯端到端测试
目标:关键业务链路可走通
方式:手工 + 脚本
对象:登录→登记→审批→留痕
| 测试类型 | 覆盖率要求 | 重点验证 |
| 单元测试 | ≥ 70%(核心算法 100%) | 边界值、除零、非法输入、容差临界 |
| 集成测试 | 核心接口全覆盖 | 鉴权、权限、审计写入、事务回滚 |
| 性能测试 | P95 < 800ms | 看板查询、列表分页(并发 50) |
| 安全测试 | 无高危漏洞 | 越权、注入、限流、Token 吊销 |
| 兼容性测试 | 主流浏览器通过 | Chrome / Edge / Safari |
11.2 核心算法测试用例
11.2.1 EVM 引擎
| 编号 | 用例 | 输入 | 预期 |
| TC-E-01 | 正常进度滞后场景 | BAC=8500, PV=4930, EV=4624, AC=4420 | SPI=0.938, CPI=1.046, 进度预警 true |
| TC-E-02 | 进度与成本均正常 | PV=4000, EV=4200, AC=4000 | SPI=1.05, CPI=1.05, 无预警 |
| TC-E-03 | 成本超支预警 | PV=4000, EV=3800, AC=4500 | CPI=0.844, 成本预警 true |
| TC-E-04 | PV 为 0 防除零 | PV=0, EV=100, AC=100 | SPI=1(不产生 NaN) |
| TC-E-05 | AC 为 0 防除零 | AC=0, PV=100, EV=100 | CPI=1,EAC=BAC |
| TC-E-06 | 综合健康度取短板 | SPI=0.95, CPI=0.85 | health='red' |
11.2.2 四单匹配校验
| 编号 | 用例 | 关键输入 | 预期 |
| TC-P-01 | 全部匹配 | 量合规,发票=核算金额 | isValid=true, matched |
| TC-P-02 | 送货超合同(R1) | delivery=1200 > contract=1000 | 命中 R1,拦截 |
| TC-P-03 | 验收超送货(R2) | acceptance=900 > delivery=800 | 命中 R2,拦截 |
| TC-P-04 | 发票超限(R3) | 偏差 > 1% | 命中 R3,拦截 |
| TC-P-05 | 1% 容差内通过 | 偏差 0.135% | 通过(不误拦) |
| TC-P-06 | 超可付余额(R4) | apply > contractTotal | 命中 R4,拦截 |
| TC-P-07 | 多项同时违规 | 超量 + 超限 + 超额 | 同时返回 R1/R3/R4 |
11.2.3 风险状态机
| 编号 | 用例 | 输入 | 预期 |
| TC-R-01 | 红色边界(=10) | 概率5 × 影响2 | score=10, level='red' |
| TC-R-02 | 黄色下界(=5) | 概率5 × 影响1 | score=5, level='yellow' |
| TC-R-03 | 绿色上界(=4) | 概率2 × 影响2 | score=4, level='green' |
| TC-R-04 | 非法概率(6) | 概率=6 | 抛出异常 |
| TC-R-05 | 合法流转 | active → mitigated | valid=true |
| TC-R-06 | 非法跨级流转 | active → closed | valid=false |
11.3 验收标准
| 验收维度 | 验收标准 | 验收方式 |
| 功能完整性 | 22 项功能需求全部实现并通过测试 | 需求追踪矩阵核对 |
| 算法准确性 | 核心算法单元测试全部通过,边界用例无误 | 自动化测试报告 |
| 性能达标 | 看板接口 P95 < 800ms(并发 50) | 压力测试报告 |
| 安全合规 | 无高危漏洞,权限隔离有效,留痕完整 | 安全测试 + 抽样核查 |
| 部署可用 | §9.7 验证清单全部通过 | 现场演示 |
| 文档齐全 | 交付物清单全部齐备 | 文档审查 |
| 试运行 | 试运行 10 天无 P0 缺陷 | 试运行记录 |
12.实施计划与里程碑
以正式开工日为基准倒排,总工期 60 天,确保系统在开工前 15 日上线可用。
| 阶段 | 里程碑 | 周期 | 主要交付物 | 验收标准 |
| M1 | 需求确认与设计 | D1–D10 | 需求规格说明书、需求基线 V1.0、概要设计、数据库设计 | 需求评审通过 |
| M2 | 核心后端开发 | D11–D28 | 数据库 DDL、EVM 引擎、四单匹配、风险状态机、API 接口 | 单元测试覆盖率 ≥70% |
| M3 | 前端开发与联调 | D23–D38 | 健康度看板、风险/支付/进度/质量页面、接口联调 | 联调通过率 100% |
| M4 | 部署与集成测试 | D35–D45 | 容器编排、Nginx 配置、DNS 解析、HTTPS 证书 系统可对外访问 | §9.7 验证清单全通过 |
| M5 | 试运行与培训 | D46–D56 | 用户手册、培训材料、试运行问题清单、缺陷修复 | 试运行 10 天无 P0 缺陷 |
| M6 | 正式交付 | D57–D60 | 源码、部署文档、验收报告、运维交接 | 甲方签字验收 |
关键路径与缓冲
📅 关键路径
M2(后端核心算法)与 M4(部署上线)为关键路径节点。其中 M4 的 DNS 解析生效与证书申请存在外部依赖(备案状态、生效延迟),已预留 10 天缓冲。若开工令提前,M4 可独立提前完成——因为系统部署与主体工程无关,随时可先上线。
13.风险识别与应对
本章识别项目实施过程中的主要风险,并给出应对措施。风险分级参照 §5.3 的红黄绿判定逻辑。
| 编号 | 风险描述 | 等级 | 应对措施 | 责任人 |
| R-01 | 域名未完成备案导致 80/443 被阻断 | 红 | 项目启动首周即核查备案状态;若未备案,立即启动备案流程,或临时使用内网/直连 IP 内测 | 项目总监 |
| R-02 | DNS 解析生效延迟影响证书申请 | 黄 | 提前配置解析并验证;证书申请安排在解析确认生效之后;必要时临时使用 HTTP 上线 | 运维 |
| R-03 | EVM 取数口径不一致导致指标失真 | 红 | 需求阶段即与财务、监理、成本岗共同确认口径并写入文档;系统固化口径,历史数据可比 | 项目经理 |
| R-04 | 四单数据录入质量差导致校验频繁失败 | 黄 | 现场培训 + 录入校验规则前置(录入时即校验);对高频失败供应商专项辅导 | 成本工程师 |
| R-05 | 服务器资源不足导致性能下降 | 黄 | 按 2 核 4G 起配;监控资源水位;预留升配预案 | 运维 |
| R-06 | 进度报量数据滞后影响 EV 准确性 | 黄 | 明确报量时限要求;监理确认后即时录入;看板标注数据时效 | 项目经理 |
| R-07 | 用户抵触、系统上线后使用率低 | 红 | 让关键岗位用户参与需求评审与 UAT;上线初期"人机并行"过渡;纳入考核 | 项目总监 |
| R-08 | 数据备份未验证可恢复 | 黄 | 建立季度恢复演练机制;演练记录归档,形成报告 | 运维 |
| R-09 | 人员变动造成账号与密钥泄露 | 红 | 离职人员账号 24h 内注销;密钥定期轮换;最小权限授权 | 项目总监 |
| R-10 | 需求蔓延导致工期延误 | 黄 | 需求基线冻结,变更走 CCB 评估;P2 需求视工期弹性排期 | 项目经理 |
| R-11 | 核心算法实现偏差导致误判 | 黄 | 核心算法 100% 单元测试覆盖;边界用例专项验证;试运行期人工复核抽样 | 研发负责人 |
⚠️ 最高优先级风险
R-01 备案与 R-07 用户接受度是两项可能直接导致项目失败的红级风险。前者是技术合规硬门槛(不可绕过),后者是价值实现的前提(系统再好,没人用就没有价值)。建议在项目启动会上即明确这两项的负责人与时间点。
14.附录
14.1 环境变量清单(.env)
| 变量名 | 说明 | 生成建议 / 取值 |
NODE_ENV | 运行环境,生产固定为 production | 固定值 |
APP_PORT | 应用监听端口,默认 3000 | 固定值 |
DB_ROOT_PWD | MySQL root 密码 | openssl rand -base64 24 |
DB_NAME | 数据库名 | zh_euro_park_db |
DB_USER / DB_PASS | 应用专用数据库账号(非 root,最小权限) | 强随机密码 |
REDIS_PWD | Redis 密码 | openssl rand -base64 24 |
JWT_SECRET | JWT 签名密钥 | openssl rand -hex 32 |
JWT_EXPIRES_IN | Token 有效期 | 8h |
ENCRYPT_KEY | 备份加密密钥,由甲方持有 | openssl rand -hex 32 |
COS_SECRET_ID / COS_SECRET_KEY | 对象存储访问凭证 | 控制台获取 |
COS_BUCKET | 存储桶名称 | 控制台创建 |
SPI_ALERT_THRESHOLD | 进度预警阈值 | 0.95 |
CPI_ALERT_THRESHOLD | 成本预警阈值 | 0.90 |
RISK_ALERT_DAYS | 风险提前预警天数 | 15 |
INVOICE_TOLERANCE_RATIO | 发票尾差容限比例 | 0.01 |
14.2 上线检查清单(Go-Live Checklist)
🌐域名与网络
☐ 业务子域名解析生效
☐ 域名备案状态确认
☐ 防火墙放行 80/443
☐ 3306/6379 未对外暴露
☐ 运维管理端口已改非标 + IP 白名单
🔒安全
☐ HTTPS 证书有效且自动续期
☐ HSTS 已开启
☐ .env 权限 600 且未入库
☐ 应用使用最小权限数据库账号
☐ 审计表收回 UPDATE/DELETE 权限
⚙️应用
☐ 全部容器 running
☐ 数据库 DDL 已导入
☐ 初始管理员账号已创建并改密
☐ 定时任务(EVM/风险扫描)已注册
☐ 核心接口连通性测试通过
💾运维
☐ 备份 Cron 已配置
☐ 备份恢复演练已执行
☐ 监控告警通道已联通
☐ 回滚预案已评审
☐ 交付物清单已归档
14.3 交付物清单
| 序号 | 交付物 | 形式 | 说明 |
| 1 | 技术白皮书(本文档) | HTML / PDF | 需求、技术、开发、部署、运维全链路 |
| 2 | 需求规格说明书 | 文档 | 由第 2 章拆分深化 |
| 3 | 概要设计说明书 | 文档 | 由第 3~8 章拆分深化 |
| 4 | 数据库设计文档 + DDL 脚本 | 文档 + SQL | 含全部建表与索引语句 |
| 5 | 后端源码 | Git 仓库 | 含单元测试 |
| 6 | 前端源码与构建产物 | Git 仓库 + dist | 可直接部署的静态资源 |
| 7 | 部署配置包 | 目录 | docker-compose / Nginx / Dockerfile / 备份脚本 / .env 模板 |
| 8 | 部署实施手册 | 文档 | 由第 9 章拆分深化 |
| 9 | 测试方案与测试报告 | 文档 | 由第 11 章拆分深化 |
| 10 | 用户操作手册 | 文档 | 面向各岗位的分角色说明 |
| 11 | 验收报告 | 文档 | 含测试用例与验收结论 |
| 12 | 运维交接文档 | 文档 | 监控、备份、回滚、常见问题处置 |