07-map-status-filter-spec.md 13 KB

07: 地图摘要状态筛选与未关联设备语义规格(来自决策票 02)

Type: spec Status: ready-for-agent Label: ready-for-agent Blocked by: 02 Category: spec

本规格固化决策票 02的维护者确认结果,作为同一张 map的子规格单。它是「真实数据接线」阶段地图摘要筛选、未关联设备语义、全局状态筛选入口与自动渐进加载的实施输入;不覆盖 .scratch/cockpit-shell-panel-layout/spec.md。

进度:100%

规格文档已发布为 ready-for-agent 并挂到同一决策 map;100% 仅指规格交付,不表示筛选契约、自动加载或饼图已实现。后端契约改动(多值 pointStatus、命中设备子集、八桶全局计数、坐标汇总筛选后口径)需按地图约定在决策票收口后进入后端执行票;前端接线边界在本规格内。

Problem Statement

值班人员需要在地图上按点位状态聚焦观察,但当前实现存在五处缺口与缺陷:

  1. 状态筛选不作用到一张图:map-snapshot 不收 pointStatus(票 25 I1),状态条件只作用到监测分布面板与台账;值班人员选了状态,地图不跟随,界面却写着「全局筛选」。
  2. 未关联设备没有状态语义:未关联设备没有点位状态来源,此前只能「不参与状态筛选」或「被排除」,与「行业/范围/类型筛选对两类对象都生效」的语义不一致。
  3. 筛选死胡同:监测分布面板选中计数为 0 的状态后显示「该面板当前没有数据」,筛选行被吞掉,无法回到未筛选或改选其它状态。
  4. 入口分散且重复:状态筛选入口只在监测分布面板内,非全局可操作;地图顶部状态统计栏只读不可选。
  5. 手动渐进加载:地图摘要需要手动点「加载下一页」「刷新快照」,存在冗余按钮,也不符合系统自动分批加载的性能目标。

Solution

从值班人员视角:地图顶部的状态统计栏成为全局组合筛选入口(八桶多选 OR),筛选对监测点与未关联设备都生效,但各自使用各自的状态来源;监测分布面板改为饼图展示且不再自带筛选;地图摘要自动渐进加载,右上角保留一个小刷新图标(🔄)供手动换新快照。

关键语义(详见决策票 02 的 Answer):

  • 行业/范围/类型筛选=设备级匹配、点位级 OR 归并:监测点是设备聚合点,任一设备命中 industry/region/deviceType → 其所属监测点进入结果;未关联设备按自身这三个属性直接匹配。map-snapshot 与 /monitoring/distribution 两端同一语义。
  • 状态筛选对两类对象都生效、各自使用各自的状态来源:监测点按后端聚合 pointStatus(六态)匹配,不做设备级 OR 归并;未关联设备按设备状态词表(五态 + 状态未发布 UNKNOWN)匹配;EMPTY(无设备)只适用于监测点。
  • 「无关联设备」桶=只看未关联设备这类对象(类别筛选),与状态筛选正交:选状态但没选「无关联设备」桶 → 显示该状态的点位与未关联设备;只选「无关联设备」桶 → 只看未关联设备。
  • 状态栏计数=全局未筛全集(后端全量、不分页);筛选只改选中高亮、不改计数。状态栏与监测分布饼图共用同一份全局未筛分布。
  • pointStatus 契约=单参数多值(逗号分隔或重复参数,OR):pointStatus=ALARM,WARNING,UNKNOWN,UNASSOCIATED;UNKNOWN=点位上状态缺失或不在六态词典(未关联设备=设备状态未发布);认不出的值 400 INVALID_REQUEST;mapFilter 回显整套生效值;不传=全部。
  • 坐标汇总=筛选后口径(默认不筛时是全局,筛了就是筛选集内全局)。
  • 组合筛选=AND(行业 ∧ 范围 ∧ 类型 ∧ 状态桶);mapFilter 回显全部生效维度;结果为空走既有 EMPTY,前端不猜原因。
  • 展开点位:只显示命中设备 + 一行可展开的「另有 N 台」(未命中设备);未命中设备展开后可完全交互(hover/选中/定位/进详情),仅视觉弱化;设备总数 X=该点位全部关联设备、已上图 Y=命中且上图子集;map-snapshot 点位响应带命中设备子集(未筛=全部),前端无需 N+1。
  • 自动渐进加载与刷新:移除「加载下一页」按钮,进入系统/筛选变化后自动连续分批加载到完成(批次间留帧间隔);移除「刷新快照」按钮,右上角保留 🔄 手动刷新入口;自动刷新时机=筛选变化(换新快照第 1 页)、新鲜度周期到点、相关 WS 增量到达。
  • 死胡同根治:筛选控件常驻可操作;结果为空只是结果区一句说明,不吞掉入口。

User Stories

  1. 作为值班人员,我想在一张图上方始终看到点位状态统计栏,以便快速了解当前全局状态分布。
  2. 作为值班人员,我想点击状态统计栏中的状态项就能按状态筛选地图,以便聚焦报警/预警等关注对象。
  3. 作为值班人员,我想同时选择多个状态(如报警+预警)查看地图,以便一次看到多类关注点位。
  4. 作为值班人员,我想选了某个状态后未关联设备仍能按自身设备状态出现在地图上,以便不会漏掉正在报警的未挂接设备。
  5. 作为值班人员,我想只选「无关联设备」桶时地图只看未关联设备,以便单独核查未挂接设备。
  6. 作为值班人员,我想按行业筛选时点位的归属由它包含的设备决定(任一设备命中即显示),以便符合「监测点是设备聚合点」的业务认知。
  7. 作为值班人员,我想同时组合行业、范围、类型、状态筛选,以便精确缩小区间。
  8. 作为值班人员,我想点开一个命中筛选的监测点时只看到命中的设备,以便展开结果与筛选语义一致。
  9. 作为值班人员,我想展开「另有 N 台」能看到并操作未命中设备(hover/选中/定位/详情),以便不被筛选挡住必要操作。
  10. 作为值班人员,我想点位详情的设备总数显示该点位全部设备、已上图数显示命中子集,以便数字不自相矛盾。
  11. 作为值班人员,我想状态栏计数始终是全局未筛全集,切筛选不改计数,以便统计口径稳定可对照。
  12. 作为值班人员,我想监测分布面板用饼图展示状态分布,以便直观看出占比。
  13. 作为值班人员,我想筛选结果为空时仍能操作筛选入口(取消/改选),以便不会陷入「无法再筛」的死胡同。
  14. 作为值班人员,我想地图摘要自动分批加载直到完成,以便无需手动点「加载下一页」。
  15. 作为值班人员,我想在右上角保留一个小刷新图标手动换新快照,以便需要时强制刷新。
  16. 作为值班人员,我想筛选变化时地图自动换新快照从第 1 页重载,以便不会出现旧范围对象混在新范围内。
  17. 作为值班人员,我想坐标汇总说明与当前筛选后的对象集合一致,以便地图上点少了、摘要也如实跟随。
  18. 作为值班人员,我想后端对认不出的状态值返回 400 而不是静默忽略,以便错误参数能被发现。
  19. 作为值班人员,我想监测分布面板不再自带筛选入口,以便全局条件只有一个可操作面。
  20. 作为部署/维护者,我想通过 mapFilter 回显看到实际生效的筛选组合,以便排障时不靠猜。

Implementation Decisions

后端契约(供后端执行票)

  • map-snapshot:
    • 入参增加多值 pointStatus(逗号分隔或重复参数),取值=六态 + UNKNOWN + UNASSOCIATED;认不出的值 400 INVALID_REQUEST;mapFilter 回显整套生效值。
    • UNKNOWN=点位上 pointStatus 缺失或不在六态词典;UNASSOCIATED=结果包含未关联设备(类别筛选,不改变点位状态匹配)。
    • 行业/范围/类型筛选改按设备级匹配、点位 OR 归并:任一设备命中 → 点位进入;未关联设备按自身属性匹配。与 /monitoring/distribution 同一语义。
    • 点位响应带命中设备子集(deviceCodes/设备明细按当前筛选裁剪;未筛=全部)。
    • 未筛时提供八桶全局计数(六态 + UNKNOWN + UNASSOCIATED),供状态栏与饼图共用;筛选时计数不变。
    • 坐标校验汇总按筛选后口径计算。
  • /monitoring/distribution:
    • 入参多值化(同 map-snapshot 语义);pointStatusDistribution 未筛时含八桶;筛选语义与 map-snapshot 一致。
  • 两端点对「认不出的值 400」与 mapFilter 回显的约定统一。

前端

  • 统一「地图集合计划」纯函数层(新模块,与 domain/modeLayerPlan.js 同构):输入筛选(行业/范围/类型/状态集合)与地图批次,输出地图对象集合 + 状态栏八桶计数 + 命中设备子集,一次算清;组件/store 只消费它,不做第二次过滤判断。
  • stores/filterStore.js:pointStatus 由单值改为多值集合(空集=全部),增删/清空语义化;与行业/范围/类型同层级、切模式不重置。
  • stores/mapStore.js + domain/mapSummary.js 装载器:接入多值 pointStatus 入参;新增自动分批加载(进入/筛选变化后连续 loadNext 至 settled,批次间留帧间隔,被新筛选打断按 token 纪律);刷新时机=筛选变化、freshness tick、WS 增量,统一走 refresh()。
  • 组件层:
    • 地图顶部状态栏(MapCanvas 图例区)从只读变为可多选的全局筛选入口(八桶 OR、全局计数、选中高亮);行常驻可点(含计数 0 的行)。
    • 右上角新增 🔄 手动刷新入口,调用 mapStore.refresh();移除「加载下一页」「刷新快照」按钮(调试形态可保留)。
    • 监测分布面板:移除 statusFilter 块与自带筛选;状态统计改为饼图(消费全局未筛分布,高亮当前选中)。
    • 点位展开(关联设备图层):按命中设备子集渲染 + 「另有 N 台」可展开;未命中设备可完全交互但弱化;总数/已上图口径如实。
  • 视觉与文案:状态词表继续只来自 domain/pointStatus.js(六态 + UNKNOWN + UNASSOCIATED)与 domain/deviceStatus.js(设备五态);饼图与状态栏共用同一份全局未筛分布数据。

Testing Decisions

  • 好测试的定义:只测外部行为与契约语义——筛选入参拼装、点位归并结果、八桶计数、命中设备子集、自动分批加载状态机、刷新时机、死胡同可操作性;不测组件内部实现。
  • 主接缝:统一的「地图集合计划」纯函数(注入批次与筛选 → 断言对象集合/计数/子集)+ mapSummary 装载器(注入取数 → 断言分页/快照/自动分批/刷新)。沿用仓库既有模式:.scratch/cockpit-shell-panel-layout/verify/tNN-*.mjs 零依赖逻辑闸门(先例 t08-map-summary.mjs、t09-point-status.mjs、t18-monitoring-panels.mjs、t46-mode-layer-plan.mjs)。
  • 浏览器走查(沿用 -walkthrough.mjs + data-* 断言面):状态栏多选高亮与计数不变、🔄 手动刷新、饼图渲染、展开「另有 N 台」交互、死胡同修复(选中 0 计数状态后仍可改选)、自动分批加载完成态。
  • 边界断言:pointStatus 白名单(六态 + UNKNOWN + UNASSOCIATED,认不出不传参的既有兜底在「非法值 400」契约下调整语义);EMPTY 桶不出现在未关联设备匹配中;点位不跨状态重复(按聚合状态匹配);组合筛选 AND 语义与 mapFilter 回显一致。
  • 变异测试:对归并/计数/白名单守卫做变异确认有牙(先例 t18 的做法)。

Out of Scope

  • 「异常统计」的状态词汇与统计口径(归决策票 06)。
  • 工单状态、报警—工单闭环(归决策票 01/06)。
  • 设备级状态词表本身(domain/deviceStatus.js)的定义与视觉(关联设备图层既有契约);本规格只引用它作为未关联设备匹配取值。
  • real 模式下新鲜度 tick / WS 增量与装载器的接线细节(归决策票 04);本规格只定「刷新时机」的语义。
  • 后端实现与真实联调(后端执行票按地图约定在相关决策收口后创建)。
  • 移动端、UE5、Docker、国产化认证及非驾驶舱领域。

Further Notes

  • 本规格依赖决策票 02(已 resolved);后端契约改动需在决策票 03(数据时间)、04(real 生命周期)、05(联调基线)、06(业务语义)收口后进入后端执行票。
  • 2026-10-08 修订(按决策票 06):范围维度由「自由串 region + regionMap 配置」改为片区(district 0/1/2 → 城南/城北/太常,空值/乱码=未分区);本文中涉及 region/regionMap 的表述以该修订为准。
  • 领域词汇已同步 CONTEXT.md:新增「未关联设备」「状态筛选」词条。
  • 未运行 build/debug 验证(遵循仓库 AGENTS.md 规则);本规格只交付实施输入。