spec.md 13 KB

城市生命线驾驶舱系统:后端只读接口与实时状态契约

Status: ready-for-agent

Problem Statement

城市生命线驾驶舱系统面向指挥中心领导和值班人员,是一个只读展示大屏。UE 需要在进入程序后加载“一张图”,以监测点为首要地图对象,查看关联智能设备的基础档案、当前监测数据、预警、报警和工单处置状态。现有后端主要提供管理型接口,字段、分页和写入能力与驾驶舱展示边界不一致;预警接口仍处于向统一工单模型迁移的阶段,设备状态 WebSocket 也使用旧的驼峰设备字段。

如果 UE 直接拼接管理接口,客户端将不得不推断监测点综合状态、复制事件关系和承担迁移兼容,导致状态口径不一致、地图分页重复、局部详情失败被空数据掩盖以及只读大屏误用业务写接口。因此后端需要提供稳定、版本化、只读的驾驶舱专用契约,并在内部复用现有表和服务。

Solution

新增驾驶舱只读聚合服务和 DTO,统一挂载于 /api/cockpit/v1/。服务端负责从现有监测点、智能设备、设备类型、预警、报警、alarm_warning_rel、工单和指标数据中组合稳定响应;UE 不直接访问管理接口或数据库。

地图摘要使用一个共享 snapshotAt 的分页快照,分别返回监测点摘要和未关联智能设备摘要。后端返回已解析的 pointStatus、状态原因、统计和坐标校验结果。监测点优先渲染;已关联设备不重复生成独立点位;未关联且坐标有效的设备才作为直接设备点位;无效坐标对象保留在列表和详情中但禁止地图渲染。

设备详情采用基础档案、当前指标、预警、报警、工单、工单时间线和图片等独立只读分区。REST 首次提供权威快照,WebSocket 仅推送状态增量。现有 /ws/deviceStatus 增加 cockpitSubscribe 范围订阅,规范字段使用 deviceCode,迁移期可额外返回旧 device_code;UE 以 serverUpdatedAt 判断新旧消息,断线后自动重连和恢复订阅。

User Stories

  1. As a 指挥中心领导, I want the cockpit to load the map frame before data completes, so that the display never appears frozen during startup.
  2. As a 值班人员, I want paginated monitoring-point summaries, so that a large city dataset can load progressively.
  3. As a 地图使用者, I want one consistent snapshot across all map pages, so that points are not duplicated or omitted while data changes.
  4. As a 值班人员, I want unassociated intelligent equipment with valid coordinates to appear directly on the map, so that equipment is not lost when an association is absent.
  5. As a 数据负责人, I want invalid or unverified coordinates returned with a reason, so that the cockpit never guesses a spatial transform.
  6. As a 地图使用者, I want the server to return the resolved monitoring-point status, so that UE does not recompute status from a partial page.
  7. As a 值班人员, I want to click a monitoring point and see its summary and backend-ordered equipment cards, so that I can choose the relevant equipment.
  8. As a 值班人员, I want to open equipment details by stable equipment code, so that detail navigation does not depend on database primary IDs.
  9. As a 值班人员, I want equipment archive fields and the latest image, so that I can identify the physical equipment.
  10. As a 值班人员, I want type-specific current metrics, including sewage environmental metrics when supplied, so that the display reflects the equipment schema.
  11. As a 值班人员, I want warning and alarm events in separate sections, so that their distinct event families remain clear.
  12. As a 值班人员, I want current and historical event results with explicit empty and unavailable states, so that missing data is not mistaken for no events.
  13. As a 值班人员, I want every related work order and its complete nine-state timeline, so that handling progress is visible without merging records.
  14. As a 系统集成负责人, I want event and work-order relationships resolved by the backend, so that the cockpit does not infer relationships from names, coordinates or timestamps.
  15. As a 驾驶舱维护者, I want independent detail sections, so that one failed query does not erase successful sibling sections.
  16. As a 值班人员, I want a short-lived cached detail with an explicit stale marker, so that I retain context without being misled about freshness.
  17. As a 值班人员, I want scheduled summary refresh between three and ten minutes and a manual refresh, so that display freshness can match deployment needs.
  18. As a 系统运维人员, I want device-state calibration to start at sixty seconds and be extendable, so that device population does not overload the service.
  19. As a 值班人员, I want incremental WebSocket updates to identify the affected device, so that the map and open detail update promptly.
  20. As a 网络异常场景下的值班人员, I want automatic reconnect and subscription recovery, so that temporary disconnections do not permanently stop updates.
  21. As a 安全负责人, I want fixed display identity supplied outside UE assets, so that credentials are not exposed in packaged content.
  22. As a 安全负责人, I want all cockpit endpoints to be read-only, so that display interactions cannot mutate business records.
  23. As a 后端协作者, I want dedicated JSON fields to use camelCase, so that the contract is consistent with the existing Java stack while keeping database names internal.
  24. As a 后端协作者, I want deviceCode to be canonical and device_code to be a migration alias, so that old WebSocket clients remain compatible.
  25. As a 统计使用者, I want facility counts and intelligent-equipment counts separated, so that water plants and pump stations are not mixed with sensor equipment.
  26. As a 业务负责人, I want the formal scope limited to gas, supply water and drainage, so that unknown types are not silently counted in a formal industry total.
  27. As a 业务负责人, I want sensor-equipped manholes represented as intelligent equipment, so that manholes are not modeled as a separate industry or facility entity.
  28. As a 统计使用者, I want overview, infrastructure, monitoring and warning-disposal metrics to use one timezone and freshness vocabulary, so that dashboards are comparable.
  29. As a 后端协作者, I want contract handoff material before warning migration replacement, so that the adapter changes from evidence rather than guessed fields.
  30. As a 测试人员, I want the aggregate behavior observable through one public read model seam, so that contract tests verify user-visible behavior without coupling to database joins.

Implementation Decisions

  • Add a versioned read-only cockpit API surface under /api/cockpit/v1/; keep management endpoints as internal composition sources only.
  • Preserve the existing response envelope convention, while placing dedicated camelCase DTOs in the data payload.
  • Provide map-summary, monitoring-point-detail and equipment-detail contracts, plus read-only metric, warning, alarm, work-order, timeline and image sections.
  • Map summaries use page, pageSize and snapshotAt; default page size is 500 and maximum is 1000. The response includes total, batch update time and completion metadata.
  • Return monitoring points and unassociated equipment as separate collections. A valid published map coordinate is WGS84; missing, zero, out-of-range or unvalidated coordinates fail the render gate and include a reason.
  • Return pointStatus and a status reason from the backend. Fixed codes are ALARM, WARNING, WORK_ORDER, NORMAL, OFFLINE and EMPTY, each with a display label. The backend owns precedence.
  • Keep facility and intelligent-equipment statistics separate. Facilities include water plants, pump stations and pipelines; sensor-equipped manholes belong to intelligent equipment and are not an industry.
  • Use the database equipment_base.equipment_code as the preferred shared association key. Dedicated cockpit JSON exposes it as deviceCode; legacy data may retain device_code or other aliases inside the adapter boundary.
  • Keep warning and alarm event families distinct. Reuse backend authority for alarm_warning_rel, event cardinality and work-order uniqueness; the cockpit never creates or deduplicates work orders.
  • Preserve all nine work-order states and each work order's independent timeline. Do not merge timelines across events.
  • Use Asia/Shanghai for business date and statistic boundaries. Emit ISO-8601 timestamps with timezone information and return selected range, timezone and update time.
  • Extend /ws/deviceStatus with cockpitSubscribe and scope=all, while retaining existing device-code subscription compatibility. REST provides initial snapshots; WebSocket sends changed-device deltas only.
  • WebSocket payloads use canonical deviceCode, may include legacy device_code, and include current status, statusTime and serverUpdatedAt. No sequence field is added; newest serverUpdatedAt wins.
  • On reconnect, the client may resubscribe automatically. The service must not treat subscription, refresh or detail queries as business writes.
  • Enforce service identity at the deployment or gateway layer. Do not embed credentials in UE, DTOs or packaged assets. Use standard HTTP errors to distinguish empty data, invalid input, missing resources, authentication failure and server failure.
  • Implement the read model by composing existing tables and services first. Add indexes or a projection only after query and representative-scale measurements demonstrate the need.
  • Do not publish unverified CAD coordinates. Conversion from CGCS2000, Xi'an 80, Beijing 54 and elevation references happens before publication and requires later control-point validation.

Testing Decisions

  • Use one highest-level public seam: a fake read-only cockpit adapter feeds the aggregate presentation contract with deterministic map pages, details and WebSocket updates; assert only public DTO behavior, freshness, selection identity and update results.
  • Add controller/service contract tests for response envelopes, camelCase fields, status dictionaries, HTTP error categories, pagination snapshot consistency and facility/equipment count separation.
  • Add integration tests against representative database fixtures for monitoring-point association, unassociated equipment, invalid coordinates, event-family separation, alarm_warning_rel relationships and nine work-order states.
  • Add WebSocket tests for canonical and legacy device-code fields, delta-only pushes, serverUpdatedAt ordering, missing point status fallback, scope subscription, reconnect subscription and heartbeat behavior.
  • Add read-only safety tests using an adapter/request spy; all map loading, detail loading, refresh, retry and subscription operations must produce no business mutation request.
  • Add freshness tests for successful empty results, one missed cycle, two missed cycles, stale retention and independent section failure.
  • Add statistics tests for timezone boundaries, selected ranges, formal industry filtering, unknown-type exclusion and separate facility/equipment totals.
  • Reuse existing Spring controller/service test conventions and add dedicated contract fixtures rather than testing private SQL helpers or requiring a particular join implementation.

Out of Scope

  • No UE implementation, UMG changes, Actor implementation, Three.js migration or Pixel Streaming work.
  • No login page, role management, threshold configuration, publishing, upgrading, resolving, dispatching, accepting, processing, verifying, approving or other business write operation in the cockpit API.
  • No replacement of the existing management APIs or removal of their compatibility behavior.
  • No local UE aggregation of monitoring-point state, event relationships, coordinate transforms or work-order uniqueness.
  • No Flowable introduction or redesign of the existing work-order state machine.
  • No judgment of warning truthfulness or false positives.
  • No final GIS accuracy threshold, production-scale performance threshold or Galaxy Kylin certification; these remain later validation gates.

Further Notes

  • The backend working tree contains pre-existing uncommitted changes. This spec does not authorize reverting or mixing those changes into the cockpit implementation.
  • The warning lifecycle is expected to migrate to the same work-order model as alarms. The dedicated adapter must remain stable while the backend absorbs that migration.
  • Before replacing transitional warning mappings, backend handoff must include endpoint paths, request parameters, response examples, field and status dictionaries, relationship/cardinality rules, pagination and freshness semantics, and WebSocket samples.
  • The current repository has /MonitorPoint, /EquipmentBase, /api/alarm and /ws/deviceStatus management or transitional surfaces, but no completed /api/cockpit/v1 contract. This spec defines the target seam without claiming those endpoints already exist.
  • Apply the ready-for-agent triage label when publishing this spec to Gogs Issues.