TECHNICAL WHITE PAPER · V4.0

数字化项目管理系统
技术白皮书

面向中欧(苏州)数字科技产业园建设工程,系统整合需求提出 → 技术路径 → 代码开发 → 系统部署 → 运维保障全生命周期的技术方案、工程规范与实施蓝图。
📄 文档编号:CH-ZH-WP-2026-04 🔄 版本:V4.0(系统整合版) 🎯 受众:甲方评审 / 管理层 / 研发 / DBA / 运维 📅 2026-3-11

0.文档说明与导读

0.1 文档定位与版本演进

本白皮书是对前期全部技术文档的系统性整合与重构。它把两条原本分离的线索——面向甲方与管理层的"业务与需求论证"(V1.0 白皮书)与面向研发运维的"工程实施细节"(V2.0 实施文档)——合并为一份自洽、完整、可追溯的单一文档,避免读者在多个版本间反复对照。

版本文档性质主要内容与局限
V1.0 技术白皮书方案论证介绍建设背景、总体思路与功能蓝图;偏概念,缺乏可落地的工程细节
V2.0 实施文档研发实施给出目录结构、DDL、核心算法、API、RBAC、容器化部署;偏研发视角,缺少需求论证与验收视角
V4.0 系统整合版综合集成整合两者并补全:需求论证 + 技术选型 + 工程规范 + 数据库 + 算法 + 接口 + 前端 + 安全 + 部署 + 运维 + 测试 + 实施 + 风险,全链贯通
📖 本次整合的三点关键调整 ① 不包含智慧工地相关部分:涉及施工现场物联网遥测(深基坑、高支模、塔吊等监测数据接入与告警)的内容在本版本中整体移除,相关表结构、接口与算法均不出现在本文档中。
② 不涉及服务器连通性内容:不对具体服务器的连通状态、端口可达性、SSH 访问等运行态问题进行描述,但完整保留服务器部署方案(域名解析、容器编排、Nginx、HTTPS、备份等),确保方案本身可直接执行。
③ 尽可能详尽:在保持结构清晰的前提下,对每个模块给出定义、设计理由、实现细节与验证方法。
建设周期
60 天
含试运行,开工前 15 日交付
核心功能域
5 大模块
健康度/风险/合同支付/进度/系统管理
技术栈
Vue3 + Node
MySQL 8.0 / Redis / Docker
部署形态
容器化
Docker Compose + Nginx
安全等级
三级等保对齐
RBAC + 全量审计留痕

文档结构导航

章节内容主要读者
第 1 章项目背景、管理现状与痛点诊断、建设目标与价值论证甲方 / 管理层
第 2 章需求来源、需求分析、功能与非功能需求、追踪矩阵、变更控制甲方 / 产品 / 项目经理
第 3 章总体架构、技术选型论证、领域模型、工程规范、部署拓扑架构师 / 研发
第 4 章数据库物理模型、核心表 DDL、索引优化、数据字典DBA / 研发
第 5 章EVM 引擎、四单匹配、风险状态机核心算法研发
第 6 章RESTful API 规范、接口清单、示例、错误码研发 / 前端
第 7 章前端技术栈、页面结构、关键交互设计前端 / 产品
第 8 章RBAC 权限矩阵、鉴权实现、审计防篡改、安全措施研发 / 安全
第 9 章系统部署方案(DNS / 容器编排 / Nginx / HTTPS / 上线 / 回滚)运维 / 研发
第 10 章备份、监控告警、数据主权与技术退出运维 / 甲方
第 11 章测试策略、核心算法测试用例、验收标准测试 / 项目经理
第 12-14 章实施计划、风险应对、附录清单全体

阅读建议

本文档可独立成篇,亦可按章节拆分为《需求规格说明书》(第 2 章)、《概要设计说明书》(第 3~8 章)、《部署实施手册》(第 9 章)、《测试方案》(第 11 章)四份子文档。

0.2 范围界定

纳入范围

✓业务范围

健康度看板、风险管理、合同与支付管理、进度管理、系统与权限管理五大功能域;覆盖项目经理部、成本合约、财务、监理与施工方等岗位的在线协同。

✓技术范围

前后端分离的应用系统、关系型数据库、缓存、容器化部署与运维体系;核心算法(EVM、四单匹配、风险状态机)的完整实现。

明确排除(Out of Scope)

排除项说明
智慧工地 / 物联网监测施工现场物联网设备接入、深基坑与高支模变形监测、塔吊安全监控等不在本版本范围内。相关数据表、接口、告警机制暂不设计。
服务器运行态与连通性不对具体服务器的网络连通、端口可达性、远程访问方式等运行态问题做描述与排障。部署方案本身(配置与步骤)完整给出。
BIM / 三维模型集成本期不做模型轻量化与三维可视化对接,可作为后续演进方向。
移动端原生 App本期前端为响应式 Web,覆盖 PC 与平板;不开发 iOS/Android 原生应用。
财务总账与税务接口系统只做支付申请与审批流转,不替代甲方财务系统做账务处理与税务申报。

0.3 术语表

术语说明
EVMEarned 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)。
四单匹配合同、送货单、验收单、发票四类单据的金额与数量一致性自动校验,是防止超付错付的核心管控手段。
红黄绿状态机按"风险发生概率 × 影响程度"矩阵将风险划分为三级(红/黄/绿),并驱动预警、督办、升级流转的状态模型。
RBACRole-Based Access Control,基于角色的访问控制。
WBSWork 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 需求来源与干系人

本系统需求并非凭空设计,而是从三个层面逐级推导、交叉印证形成。

1
管理要求
管理制度与项目章程
→
2
岗位诉求
关键岗位访谈
→
3
流程痛点
六类痛点诊断
→
4
需求基线
评审冻结 V1.0

干系人登记册

干系人 / 角色类型核心关注点需求贡献
项目总监内部 / 决策全局健康度、重大风险、资金安全一票否决重大支付、风险督办
项目经理内部 / 执行进度可控、资源协调、问题闭环日常推进、风险登记、支付复审
成本 / 合同工程师内部 / 执行合同履约、成本核算、支付准确性四单数据录入与初审
质量 / 安全工程师内部 / 执行质量问题整改闭环、隐患排查问题登记与整改跟踪
监理单位外部验收数据真实性、旁站记录验收单录入、质量上报
施工单位 / 分包商外部进度报量、支付进度查询送货单、进度报量提交
甲方(产业园)外部 / 业主投资回报、数据主权、内控合规验收标准、数据归属条款
财务部门内部 / 支持付款依据合规、资金计划支付规则、票据核验要求
审计 / 法务内部 / 监督操作留痕完整性、证据可采信审计日志要求、导出格式

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-02EVM 核心指标展示:PV/EV/AC/SV/CV/SPI/CPI/EAC/VACP0指标与后端计算一致,保留 3 位小数
FR-DB-03SPI < 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 → closedP0流转留痕,不可跨级跳过
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付款申请单与审批意见导出归档P2PDF 格式,含签批记录

模块四:进度管理(SC)

编号需求描述优先级验收标准
FR-SC-01WBS 分解与里程碑管理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.1evmEngine.jsP0
FR-RK-02/03/04风险预警要求§5.3riskEngine.js / riskScanner.jsP0
FR-PM-02/04四单匹配要求§5.2paymentValidator.jsP0
FR-SC-02进度报量要求§3.3 / §5.1scheduleService.jsP0
FR-QS-01质量闭环要求§7.2qualityController.jsP1
FR-SY-01/03岗位职责要求§8.1 / §8.2auth.jsP0
FR-SY-02防篡改要求§8.3auditMiddleware.jsP0
NFR-S-01内控合规要求§9.5nginx.conf + CertbotP0
NFR-R-01数据主权要求§10.3backup.shP1

2.6 需求变更控制

需求基线冻结后,任何变更均通过变更控制流程(CCB)管理,防止范围蔓延导致工期与质量失控。

1
提出变更
填写变更申请
→
2
影响评估
工期/成本/测试
→
3
CCB 评审
甲乙双方
→
4
批准实施
更新基线
→
5
回归验证
补充测试
变更类型处理策略
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 / MiddlewareController 只做参数校验与响应封装;Service 承载业务逻辑;Middleware 处理横切关注点(鉴权、审计、拦截)
数据层MySQL / RedisMySQL 为唯一事实来源;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实际发生的全部费用"实际花掉"的钱
进度偏差SVEV − PV<0 表示进度滞后
成本偏差CVEV − AC<0 表示成本超支
进度绩效SPIEV / PV<1 表示进度落后,<0.95 触发纠偏
成本绩效CPIEV / AC<1 表示花钱效率低,<0.90 触发成本预警
完工预测EACBAC / CPI按当前效率推算,最终要花多少钱
完工偏差VACBAC − 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 33.4+组合式 API,逻辑复用强,性能优于 Vue2
TypeScript5.x静态类型,保障前后端接口数据结构一致
Vite5.x极快的冷启动与热更新,构建产物小
Pinia2.x状态管理,替代 Vuex,TypeScript 友好
Vue Router4.x路由与权限守卫(按角色动态生成菜单)
Element Plus2.x管理后台组件库,表格/表单/弹窗完备
ECharts5.x看板趋势图、仪表盘、柱状图
Axios1.xHTTP 客户端,统一拦截器处理鉴权与错误

7.2 页面结构与导航

系统采用"左侧导航 + 顶部栏 + 主内容区"的经典管理后台布局,响应式适配 PC 与平板。

🧭导航结构

左侧固定导航按功能域分组;顶部栏显示面包屑、当前用户与角色;主内容区承载页面。移动端导航收起为抽屉,避免拥挤。

🔐权限驱动菜单

菜单项由当前用户角色动态过滤,无权限的入口直接不渲染,避免"点进去才发现没权限"的糟糕体验。

页面清单

页面路由主要内容可见角色
登录/login账号密码登录、失败锁定提示全部(未登录)
健康度看板/dashboard四维健康度、EVM 指标、趋势曲线、标段完成率、风险提示全角色(视图不同)
风险登记册/risk风险列表、登记表单、处置流转、等级统计项目经理及以上
合同与支付/payment合同台账、支付申请、四单校验结果、审批流转成本岗、项目经理、总监
进度管理/scheduleWBS、里程碑、报量录入、计划/实际对比、关键路径项目经理、施工方
质量管理/quality质量问题列表、整改闭环、逾期提醒质量工程师、项目经理
审计日志/audit操作记录查询、变更快照对比、导出总监、审计角色
系统管理/system用户管理、角色权限矩阵、业务阈值配置总监、系统管理员

7.3 关键交互设计

7.3.1 四单匹配的交互流程

这是资金安全的关键交互。设计目标是让用户在提交前就清楚看到校验结果,而不是提交后被驳回。

1
录入四单数据
合同/送货/验收/发票
→
2
实时校验
失焦即触发
→
3
结果可视化
逐条规则展示
→
4
提交或阻断
不通过则禁用按钮
💡 交互要点 ① 校验失败时,提交按钮置灰并标注原因,而非点击后才报错;② 错误提示精确到规则编号与数值(例如"发票金额 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说明
Apms<服务器公网 IP>600核心记录:业务子域名指向服务器
CNAMEwww按现有配置保持不变—如无需要,无需改动
⚠️ 解析注意事项 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.com301 → https
☐HTTPS 可用curl -I https://pms.example.com200,证书有效
☐证书链完整openssl s_client -connect pms.example.com:443Verify 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=4420SPI=0.938, CPI=1.046, 进度预警 true
TC-E-02进度与成本均正常PV=4000, EV=4200, AC=4000SPI=1.05, CPI=1.05, 无预警
TC-E-03成本超支预警PV=4000, EV=3800, AC=4500CPI=0.844, 成本预警 true
TC-E-04PV 为 0 防除零PV=0, EV=100, AC=100SPI=1(不产生 NaN)
TC-E-05AC 为 0 防除零AC=0, PV=100, EV=100CPI=1,EAC=BAC
TC-E-06综合健康度取短板SPI=0.95, CPI=0.85health='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-051% 容差内通过偏差 0.135%通过(不误拦)
TC-P-06超可付余额(R4)apply > contractTotal命中 R4,拦截
TC-P-07多项同时违规超量 + 超限 + 超额同时返回 R1/R3/R4

11.2.3 风险状态机

编号用例输入预期
TC-R-01红色边界(=10)概率5 × 影响2score=10, level='red'
TC-R-02黄色下界(=5)概率5 × 影响1score=5, level='yellow'
TC-R-03绿色上界(=4)概率2 × 影响2score=4, level='green'
TC-R-04非法概率(6)概率=6抛出异常
TC-R-05合法流转active → mitigatedvalid=true
TC-R-06非法跨级流转active → closedvalid=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源码、部署文档、验收报告、运维交接甲方签字验收
M1
需求设计
D1–D10
→
M2
后端开发
D11–D28
→
M3
前端联调
D23–D38
→
M4
部署上线
D35–D45
→
M5
试运行
D46–D56
→
M6
正式交付
D57–D60

关键路径与缓冲

📅 关键路径 M2(后端核心算法)与 M4(部署上线)为关键路径节点。其中 M4 的 DNS 解析生效与证书申请存在外部依赖(备案状态、生效延迟),已预留 10 天缓冲。若开工令提前,M4 可独立提前完成——因为系统部署与主体工程无关,随时可先上线。

13.风险识别与应对

本章识别项目实施过程中的主要风险,并给出应对措施。风险分级参照 §5.3 的红黄绿判定逻辑。

编号风险描述等级应对措施责任人
R-01域名未完成备案导致 80/443 被阻断红项目启动首周即核查备案状态;若未备案,立即启动备案流程,或临时使用内网/直连 IP 内测项目总监
R-02DNS 解析生效延迟影响证书申请黄提前配置解析并验证;证书申请安排在解析确认生效之后;必要时临时使用 HTTP 上线运维
R-03EVM 取数口径不一致导致指标失真红需求阶段即与财务、监理、成本岗共同确认口径并写入文档;系统固化口径,历史数据可比项目经理
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_PWDMySQL root 密码openssl rand -base64 24
DB_NAME数据库名zh_euro_park_db
DB_USER / DB_PASS应用专用数据库账号(非 root,最小权限)强随机密码
REDIS_PWDRedis 密码openssl rand -base64 24
JWT_SECRETJWT 签名密钥openssl rand -hex 32
JWT_EXPIRES_INToken 有效期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运维交接文档文档监控、备份、回滚、常见问题处置