Accumulate investigation paths and preserve failed attempts across rounds. Keep graph annotations separate from execution state, synchronize the API contract, and consolidate documentation.
104 lines
10 KiB
Markdown
104 lines
10 KiB
Markdown
# 外围系统接入服务
|
||
|
||
该 FastAPI 子应用接入真实业务工单,提供版本化 CRUD、处理反馈和事件调查入口,同时保留独立的历史监测回放与演练数据接口。生产环境挂载在主 Server 的 `/peripheral`,随 `next-backend` 单容器发布;源码仍可独立启动用于本地隔离调试。管理入口位于供水工作台“外围系统”。
|
||
|
||
外围工单的 `origin` 固定为 `system_data`,前端录入、同步器、任务快照、执行回执和工单查询均使用该来源,不再提供演练来源分支。业务录入代表系统中的上报,不代表投诉内容或故障原因已核实,Agent 仍需查询实际证据。
|
||
|
||
此次变更不修改表结构;部署时需在每个项目业务库执行 `resources/sql/029_peripheral_work_orders_system_data.sql`,统一修正源工单、修订记录、任务快照和外围反馈证据。API 路径、配置键中的 `sample` 为兼容名称,不代表工单是模拟数据。更新需发布 Server、同步器、Agent 与前端对应版本。
|
||
|
||
浏览器通过 `TJWATER_PERIPHERAL_SAMPLE_API_BASE_URL` 连接外围服务。服务复用主系统的 Bearer 身份和 `X-Project-Id`,读取需要 `simulation.view`,写入需要 `simulation.run`。权限键沿用现有契约,不用于判断数据来源。
|
||
|
||
生产环境不需要为外围服务新增域名。前端设置
|
||
`TJWATER_PERIPHERAL_SAMPLE_API_BASE_URL=/peripheral`,在现有前端 NPMplus Proxy Host 中增加以下
|
||
Custom Location:
|
||
|
||
| 配置项 | 值 |
|
||
| --- | --- |
|
||
| Location | `/peripheral`,不添加末尾斜杠 |
|
||
| Scheme | `http` |
|
||
| Forward Hostname / IP | `192.168.1.114`,不追加路径或末尾斜杠 |
|
||
| Forward Port | `8001` |
|
||
| Forward Path | 留空 |
|
||
|
||
容器内部仍监听 `8000`,生产发布在宿主机映射为 `8001`,避免与已有服务冲突。该配置保留 `/peripheral` 前缀,因此浏览器请求
|
||
`/peripheral/api/sample/v1/context` 时,主 Server 收到相同路径并交给挂载的子应用。不要在
|
||
Forward Hostname 后追加 `/api/sample/v1`。本机开发和预览模式也将 `/peripheral` 代理到
|
||
`TJWATER_SERVER_API_BASE_URL`,默认是 `http://127.0.0.1:8000`。
|
||
|
||
外围 API 支持已授权项目切换。每次请求先由主系统验证项目成员身份和权限,再从 Metadata DB 解析该项目的 `biz_data` 路由;历史观测另解析 `iot_data` 路由。逻辑项目代码、业务物理库名和时序物理库名可以不同,连接沿用各自完整 DSN。路由缺失返回 `503 PROJECT_STORAGE_NOT_CONFIGURED`,不会回退到其他项目数据库。未配置存储表的项目仍需单独完成结构部署;API 不自动创建表或复制演示数据。
|
||
|
||
`PERIPHERAL_SAMPLE_PROJECT_CODE` 和 `PERIPHERAL_SAMPLE_DB_NAME` 仅供显式运行的种子、导出等脚本选择默认库,不再限制 HTTP 请求。工单变更在当前项目已有 `workbench.domain_event_log` 时于同一事务发布事件;未配置事件表时不自动建表,实时同步仍需项目工作台事件结构。
|
||
|
||
后台同步器和调查 worker 支持 `OPERATIONAL_WORKER_PROJECT_IDS`(项目 UUID,逗号分隔),每个项目维护独立凭据、事件游标和会话状态;未配置时继续使用原 `OPERATIONAL_WORKER_PROJECT_ID`。多项目配置只选择运行范围,不新增项目成员权限,服务身份必须已获各项目授权。修改后需重启两个常驻进程,参见 [工作进程配置](operational-task-workers.md)。
|
||
|
||
主要接口包括:
|
||
|
||
- `/api/sample/v1/context` 与 `/scenario`
|
||
- `/api/sample/v1/work-orders`
|
||
- `/api/sample/v1/test-data`
|
||
- `/api/sample/v1/monitoring/points`、`observations`、`alerts`
|
||
- `/api/sample/v1/demo-data`
|
||
|
||
SCADA 按月数据集、5 分钟时间定位、主动周期监测、质量校验、爆管调查与定位边界见 [SCADA 外围系统时段回放、主动监测与爆管调查设计](scada-peripheral-replay-design.md)。部署回放存储需在项目 BizDB 执行 `031_scada_replays.sql`;项目通用配置需在 MetaDB 执行 `033_metadata_project_settings.sql`。监测频率保存于 `projects.settings.scada_monitoring_interval_seconds`,压力与出厂边界流量角色保存在类型化 `projects.settings.scada_monitoring` 中;本阶段不注册定时任务,也不部署 SCADA 回放 worker。代码和结构迁移完成不等于月度数据或 Agent 联调已经验收。
|
||
|
||
独立监测样本和测试数据通过 `scripts/seed_peripheral_sample.py` 写入 `tjwater_v2` 的 `peripheral_sample` schema。固定窗口为 2026-09-17,样本标记为 `synthetic` 并使用 `DEMO-` 前缀;业务工单保持 `system_data` 来源。
|
||
|
||
## 工单事件调查
|
||
|
||
`POST /api/sample/v1/work-orders` 可创建 `work_order_type=water_quality_complaint` 的水质投诉事件,或 `work_order_type=burst_report` 的爆管上报,均默认使用业务来源。二者由外围同步器转换为统一事件调查任务,再由后端 worker 可靠交给 Agent;浏览器不再负责启动调查。事件和执行请求 ID 均由稳定业务身份确定,回执恢复或重试不会重复创建任务。普通业务录入无需绑定演练场景码,也不会向 Agent 注入预设监测结果或邻区投诉情况。
|
||
|
||
外围服务只保存工单,不直接调用 Agent。`scripts/sync_peripheral_work_orders.py` 使用受认证的后台身份同步外部版本并创建调查任务,`scripts/run_operational_worker.py` 再将持久化的待执行任务交给 Agent,因此浏览器关闭后流程仍可继续。
|
||
|
||
## 工单事件主动同步
|
||
|
||
工单的创建、编辑、现场反馈和删除改为由来源事件触发同步。浏览器继续使用已有工作台 SSE,不需要新增连接或定时工单轮询。
|
||
|
||
```text
|
||
外围工单写入
|
||
→ 同一 PostgreSQL 事务保存源记录、历史版本和变更事件
|
||
→ COMMIT 后 NOTIFY
|
||
→ Server 工作台 SSE 唤醒外围同步器
|
||
→ 同步器通过授权 API 更新任务投影
|
||
→ task/action/notification 事件推送给浏览器
|
||
```
|
||
|
||
### 项目与存储范围
|
||
|
||
当前 HTTP 请求按已授权项目的 Metadata 路由访问其 BizDB,源变更事件复用 `workbench.domain_event_log` 的 `workbench.work_order.changed` topic。聚合类型为 `peripheral_work_order`,元数据只包含 `source=peripheral_sample`、工单 ID 和 `created|updated|deleted`,不包含正文。
|
||
|
||
源记录、历史版本及变更事件在同一事务写入,提交后通过 PostgreSQL NOTIFY 唤醒监听;事务失败整体回滚,幂等重放不再次产生事件。外围服务只保存源事实,任务投影、调查创建和通知仍经 Server 状态机与授权 API 处理。
|
||
|
||
代码仅在当前项目已有事件表时写事件,不自动创建表、不回退其他项目数据库;表缺失时跳过事件发布,已有表写入失败则使源变更事务失败。HTTP 项目路由、数据库结构是否部署、后台同步器是否配置该项目是三个条件,需要分别核验。早期测试和迁移仅在 `tjwater_v2` 执行的记录不再作为运行时代码的固定库限制。扩展遵循 [数据库变更门禁](workbench-database-rollout.md)。
|
||
|
||
### 同步器
|
||
|
||
从 Server 仓库运行已有命令:
|
||
|
||
```bash
|
||
conda run -n server python scripts/sync_peripheral_work_orders.py
|
||
conda run -n server python scripts/sync_peripheral_work_orders.py --once
|
||
```
|
||
|
||
继续复用 `OPERATIONAL_WORKER_*` OAuth 工作身份及 `OPERATIONAL_ACTION_PERIPHERAL_URL`,不需要新账号或密钥。启动和游标过期时先读取事件游标,再进行全量对账;扫描期间的事件从该游标继续补读。相同源版本仍由原幂等键去重。源事件按工单 ID 唤醒同步器;动作回执事件只补查该任务关联的工单,避免源创建早于回执保存时漏同步。任务投影事件不再触发全量扫描。
|
||
|
||
正常状态收到源事件即处理对应 ID:`GET /api/v1/operational-tasks?peripheral_work_order_id=<uuid>` 只返回同项目的来源任务和动作回执关联任务;外围接口只读取该工单,不再分页遍历全部工单和全部调查详情。重复 ID 合并;目标工单的明确删除只撤回该工单投影,不影响其他任务。
|
||
|
||
内存待处理 ID 最多 200 个,超过上限合并为一次全量对账,不持续扩张队列。这是突发积压保护,不增加公共配置项。
|
||
|
||
默认每 300 秒仍进行一次全量对账,处理漏通知、断线、旧批量脚本直接写库及重启。持续收到事件不会推迟全量对账期限。可用 `--reconcile-seconds` 调整这一兜底周期(最小 1 秒),不再复用工况 worker 的 30 秒租约检查周期。同步失败按 5、10、20、40、60 秒退避重试;事件连接断开每 5 秒重试,保留最后游标。扫描开始前取出待处理 ID,扫描期间新到达的 ID 留给下一轮;失败的 ID 会放回队列,重试不依赖新消息。
|
||
|
||
浏览器收到源事件时不会提前刷新业务快照,而是等待任务投影事件后读取对应任务。工况手动刷新仍只读 Server 权威快照。工单事件不等于 Header 通知;只有需要处理的业务待办或告警才进入通知列表。
|
||
|
||
游标顺序、共享 Hub 查询与兼容边界见 [工作台事件链路](workbench-event-refinement.md)。新增查询参数已从 Server OpenAPI 同步生成 Agent 类型,不另建手写契约。
|
||
|
||
### 验证与启用
|
||
|
||
```bash
|
||
conda run -n server python -m pytest tests/unit/test_peripheral_push_sync.py tests/unit/test_peripheral_work_order_events.py tests/unit/test_peripheral_task_sync.py -q
|
||
PERIPHERAL_SAMPLE_LIVE_TEST=1 conda run -n server python -m pytest tests/integration/test_peripheral_work_order_events.py -q
|
||
```
|
||
|
||
数据库测试强制限定 `tjwater_v2`,使用随机项目键和回滚事务,不保留测试工单或事件。覆盖创建、修改、反馈、删除、幂等重放及回滚无事件。HTTP 隔离测试覆盖源事件即时投影、游标重置、断线续传、失败重试和监听取消。
|
||
|
||
发布时外围服务随主 Server 镜像更新,同步器是独立常驻进程;需按维护者授权的发布流程分别更新 Server、同步器及配套前端。提交代码不证明现有进程已重载。回退应用代码不涉及数据结构回滚,已有事件仍可正常补读;回退到旧同步器后恢复周期对账。
|