# FT546 实时只读影子运行手册 ## 1. 适用范围 本手册只用于运行独立的 `ruoyi-wcs-shadow` 进程,采集新调度算法的只读影子证据。 - 现场 `LEGACY` WCS 始终是唯一生产调度者、PLC 写入者和 WMS 回调者。 - 本阶段不切换 `STANDARD`,不允许 shadow 连接 PLC/WMS,也不允许写生产业务表。 - 停止或移除 shadow 进程就是本阶段的回滚;不得停止或切换稳定 WCS。 - 本地自动化测试不能替代 SQL Server、网络隔离或现场观察门禁。 ## 2. 当前验证边界 | 门禁 | 当前状态 | 允许的结论 | | --- | --- | --- | | Java 单元、Spring 上下文、H2 E2E | 可在本地执行 | 代码级只读链路和协议证据可验证 | | PowerShell 脚本生命周期 | 可用无网络 sleeper jar 本地执行 | 确认、哈希、PID 精确启停和离线导出逻辑可验证 | | SQL Server 集成 | `NOT_RUN` | 不得宣称数据库集成或现场就绪 | | PLC/WMS 网络 deny | `NOT_RUN` | 必须由目标 shadow 主机的新鲜审计报告证明 | | HIL 与现场影子窗口 | `NOT_RUN` | 不得部署或开始采集 | `application-shadow-local.yml` 是外部配置模板,不是可直接运行的 H2 profile。H2 仍是 test-scope 依赖,且测试资源不会进入打包 jar;没有审批过的独立 SQL Server 环境时,不运行 jar 本地 smoke。 当前 `shadow_hold_history` 能跨重启恢复 wait edge 和连续 HOLD 计数,测试覆盖第三次相同 HOLD 后计数为 3。生产调度内核尚无消费该计数并触发 reroute 的策略,因此不得把“计数恢复”报告成“第三次 HOLD 已重规划”。该语义应在取得影子证据并明确路线/阻塞资源状态后单独设计。 ## 3. 前置条件 开始任何现场操作前,逐项确认: 1. 稳定 WCS 健康,实际制品、配置、PID、命令行和监听端口可读取。 2. source 登录只能读取 `shadow_ro.*` 批准视图,无生产表 `INSERT`、`UPDATE`、`DELETE`、`EXECUTE`、`ALTER`、`CONTROL` 或所有权权限。 3. evidence 数据库与 source 数据库物理分离,evidence 登录只在 evidence 数据库具备所需写权限。 4. shadow 部署目录、日志目录、端口和 PID 文件均与稳定 WCS 分离。 5. shadow 主机到 PLC 写端点和生产 WMS 回调端点已在网络层拒绝,并生成不超过 60 分钟的 JSON 审计报告。 6. 已约定生产 DB 延迟、shadow CPU/内存、source lag 和 evidence 磁盘阈值。 7. 已准备立即停止 shadow 的值守人员;不安排任何稳定 WCS 切换动作。 权限报告最小结构: ```json { "generatedAtUtc": "2026-08-14T10:00:00Z", "status": "PASS", "source": { "server": "source-host", "database": "FT546-wcs", "login": "ft546_shadow_reader", "approvedViewsReadable": true, "productionWritePermissionCount": 0 }, "evidence": { "server": "evidence-host", "database": "FT546-shadow", "login": "ft546_shadow_writer", "requiredWritesGranted": true } } ``` 网络 deny 报告最小结构: ```json { "generatedAtUtc": "2026-08-14T10:00:00Z", "status": "PASS", "plcEgressDenied": true, "wmsEgressDenied": true } ``` 报告必须来自已批准环境的实际检查。不得手工伪造 `PASS`。 ## 4. 构建与本地验证 在隔离工作树执行: ```powershell mvn clean test mvn -pl ruoyi-wcs-shadow -am -DskipTests package powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass ` -File .\tools\shadow\tests\Test-Ft546ShadowScripts.ps1 ``` 打包后确认 shadow jar 不含 H2 或测试 profile: ```powershell jar tf .\ruoyi-wcs-shadow\target\ruoyi-wcs-shadow-3.8.9.jar | Select-String 'h2|application-shadow-test|application-shadow-local' ``` 预期没有匹配项。该检查证明打包边界,不证明现场可运行。 ## 5. 捕获稳定 WCS 基线 使用现场实际路径和 PID,不用 Git 分支或远端引用代替已部署制品: ```powershell .\tools\shadow\Capture-StableWcsManifest.ps1 ` -StableArtifact 'D:\wcs-stable\ruoyi-admin.jar' ` -StablePid 1234 ` -Configuration 'D:\wcs-stable\application.yml' ` -OutputJson 'D:\shadow-evidence\stable-manifest.json' ``` 归档输出 JSON。确认其中的 artifact/config SHA-256、PID、启动时间、命令行和监听端口与现场一致。 ## 6. 准备 shadow 目录和配置 将打包的 shadow jar 和一份外部 site 配置放在独立部署目录。配置只允许以下网络相关内容: - loopback 状态端点; - source SQL Server 只读连接; - 独立 evidence SQL Server 写连接。 配置不得包含 PLC IP/port 或 WMS URL。密码使用受控环境变量或主机密钥设施,不写入 Git、预检 JSON、PID 文件或命令行。 `Start-Ft546Shadow.ps1` 会从成功预检向进程注入 artifact/config SHA-256、source server/database、loopback address/port 和磁盘阈值,供应用内 startup gate 复核。 ## 7. 运行预检 ```powershell .\tools\shadow\Test-Ft546ShadowPreflight.ps1 ` -StableManifest 'D:\shadow-evidence\stable-manifest.json' ` -ShadowArtifact 'D:\ft546-shadow\ruoyi-wcs-shadow.jar' ` -Config 'D:\ft546-shadow\application-shadow-site.yml' ` -DeploymentDirectory 'D:\ft546-shadow' ` -PermissionReport 'D:\shadow-evidence\permission-report.json' ` -NetworkDenyReport 'D:\shadow-evidence\network-deny-report.json' ` -StatusUri 'http://127.0.0.1:18091/shadow/status' ` -MinimumFreeBytes 10737418240 ` -OutputJson 'D:\shadow-evidence\shadow-preflight.json' ``` 预检会失败关闭并留下 JSON,检查内容包括: - 稳定 PID 健康且 artifact/config 哈希未变; - shadow jar/config 位于独立部署目录; - YAML 中 source/evidence server、database、username 与权限报告一致; - source/evidence 数据库不同; - 权限和网络 deny 报告为新鲜 `PASS`; - 状态端点为 loopback,端口不与稳定 WCS 冲突; - 配置无 PLC/WMS 端点; - evidence 磁盘空间达到阈值; - shadow 部署目录中不存在当前 `shadow.pid.json`。即使其中记录的进程已退出,陈旧 PID 文件也会使预检失败;必须先核验并按停机/归档流程处理,禁止覆盖后启动。 任何一项失败都不得启动。 ### 7.1 处理陈旧 PID 记录 不得删除或覆盖 `shadow.pid.json`。先读取其中的 `pid`:如果该 PID 仍存在,只能执行第 10 节的 `Stop-Ft546Shadow.ps1`,由脚本校验启动时间、Java 可执行文件、shadow jar 哈希和 `-jar` 命令行;校验不一致时停止操作并升级处理。 只有在 `Get-Process` 连续两次(间隔至少 5 秒)均确认该 PID 不存在后,才可由两名复核人员将原记录改名为 `shadow.stale..json`。变更单必须保留原记录 SHA-256、两次查询输出、归档路径、操作者和复核人。归档后重新执行完整预检,旧的预检 JSON 不得复用。 ## 8. 启动与初始观察 只有经过双人复核的成功预检才能启动: ```powershell $started = .\tools\shadow\Start-Ft546Shadow.ps1 ` -ConfirmReadOnlyShadow ` -PreflightJson 'D:\shadow-evidence\shadow-preflight.json' $started Invoke-RestMethod 'http://127.0.0.1:18091/shadow/status' ``` 启动后立即重新核对: 1. 稳定 PID、制品哈希、健康状态和 PLC writer 所有权均未变化。 2. shadow PID 与稳定 PID 不同;PID 文件位于 shadow 部署目录。 3. shadow 只监听预检中的 loopback 端口。 4. source 审计只有批准视图的 `SELECT`;生产业务表写审计为零。 5. PLC/WMS 网络监控没有 shadow 连接尝试。 先以低频轮询观察 15 分钟。只有稳定 WCS 指标不变、source 查询延迟在阈值内、source lag 正常、evidence 持续增长且磁盘安全时,才可延长观察窗口。 `STARTING` 表示尚无完整 cycle;`READY` 表示最近 cycle 完成;`DEGRADED` 必须按 reason code 调查。证据存储失败或碰撞会永久停止该进程继续读取,需停止并保留现场。 ## 9. 立即停止条件 出现任一条件,立即停止 shadow,不等待当前观察窗口结束: - 稳定 WCS 健康、CPU、内存、线程、PID 或 PLC writer 所有权变化; - 生产 DB 延迟超过约定阈值,或出现锁等待/连接池压力; - source 权限、数据库身份或批准视图发生变化; - source lag 超出事实新鲜度,或连续 query timeout; - 任意 PLC/WMS 连接尝试或任意生产表写审计; - evidence 磁盘低于阈值、写失败、哈希碰撞或 evidence backlog 异常; - shadow CPU/内存超过约定上限; - 状态为 `DEGRADED` 且原因不能立即解释为短暂 source 故障。 ## 10. 精确停止与回滚 ```powershell .\tools\shadow\Stop-Ft546Shadow.ps1 ` -PidFile 'D:\ft546-shadow\shadow.pid.json' ``` 停止脚本只接受该 PID 文件记录的进程,并逐项验证 PID、启动时间、Java 可执行文件、shadow jar 哈希和 `-jar` 命令行。任何不一致都拒绝停止,不会按 `java` 进程名批量查杀。成功后 PID 记录改名归档为 `shadow.stopped..json`。 回滚完成条件:shadow PID 不存在,稳定 PID/健康/PLC 所有权保持原值,网络与数据库审计无 shadow 副作用。稳定 WCS 从未停止或切换。 ## 11. 导出证据 现场 SQL Server 模式使用集成认证,或把 SQL 密码短暂放入当前进程环境变量 `FT546_SHADOW_EVIDENCE_PASSWORD`: ```powershell .\tools\shadow\Export-Ft546ShadowEvidence.ps1 ` -EvidenceServer 'evidence-host' ` -EvidenceDatabase 'FT546-shadow' ` -EvidenceUsername 'ft546_shadow_writer' ` -RunId '00000000-0000-0000-0000-000000000000' ` -OutputDirectory 'D:\shadow-evidence\export-20260814' ``` 离线复核模式接受已审批工具生成的快照目录: ```powershell .\tools\shadow\Export-Ft546ShadowEvidence.ps1 ` -SnapshotDirectory 'D:\shadow-evidence\approved-snapshot' ` -RunId '00000000-0000-0000-0000-000000000000' ` -OutputDirectory 'D:\shadow-evidence\export-20260814' ``` 输出固定包含: - `cycles.csv`、`cycles.json`; - `protocol-differences.csv`; - `algorithm-differences.csv`; - `insufficient-facts.csv`; - `startup-checks.json`; - `manifest.json`、`manifest.sha256`。 校验: ```powershell $expected = (Get-Content 'D:\shadow-evidence\export-20260814\manifest.sha256').Split()[0] $actual = (Get-FileHash 'D:\shadow-evidence\export-20260814\manifest.json' -Algorithm SHA256).Hash.ToLowerInvariant() if ($expected -ne $actual) { throw 'EVIDENCE_MANIFEST_HASH_MISMATCH' } ``` 将稳定 manifest、preflight JSON、权限报告、网络 deny 报告、启动/停止 PID 记录、状态响应、现场监控截图和导出目录作为同一批次归档。 ## 12. 禁止事项 - 不在生产数据库内创建 evidence schema 代替独立 evidence 数据库。 - 不给 source 登录任何生产写权限。 - 不把 `ruoyi-admin` 以 `SHADOW` 模式启动。 - 不允许 shadow 配置 PLC/WMS endpoint。 - 不用宽泛 Java 进程查杀命令代替 `Stop-Ft546Shadow.ps1`。 - 不因为本地/H2 测试通过就跳过 SQL Server、网络或现场门禁。 - 不把本阶段结果解释为 `STANDARD` 上线许可。