docs(design): close Sense second-review gaps
Harness governance / validate (push) Has been cancelled
Harness governance / validate (pull_request) Has been cancelled

This commit is contained in:
QiuSW
2026-08-04 11:08:41 +08:00
parent e18a1ea83b
commit e0b0bfafeb
13 changed files with 576 additions and 687 deletions
+4 -1
View File
@@ -33,7 +33,8 @@
- 批量开通不依赖逐路手工操作,支持待激活中间态。
- 探活、离线告警、开通校时、断线自动恢复。
- 容量写入时校验站点配额;配额服务不可用时拒绝新增/启用,但不影响已有流。
- 隐私准入策略属于 Area,使用 `capture_policy = video_allowed | non_imaging_only`;Sense 在设备新增/启用时依据设备成像能力校验。策略读取失败时拒绝新的成像设备变更并告警,已有链路不静默停用或伪装为已收敛。
- Site、Area 与隐私准入策略由 Bell 统一持有;`capture_policy = video_allowed | non_imaging_only` 通过版本化内部 API 或只读投影提供给 Sense。Sense 在设备新增/启用时依据设备成像能力校验,并显示策略版本/同步状态;策略读取失败时拒绝新的成像设备变更并告警,已有链路不静默停用或伪装为已收敛。
- ONVIF 能力探测必须持久化厂商、型号、固件、认证方式、Profiles、校时与事件订阅结果以及探测时间;UI 区分探测值和最终生效值。M6 前未实现的非视频协议适配器使用 `adapter_not_ready`,不得伪造在线状态或遥测。
### 3.2 分析与规则
@@ -63,12 +64,14 @@
- 多租户数据、账号、配置和存储隔离;最小 RBAC 为平台管理员、租户管理员、站点管理员、值班员、只读。
- 全链路审计,预警生命周期可追溯。
- Prometheus/Grafana 至少覆盖设备在线、流状态、推理延迟、事件量、未收敛项和投递 SLA。
- Sense 提供面向接入运维的运维中心,聚合对账差异、重试退避、孤儿安全闸、媒体/推理分片、边缘隧道/补传和运维告警;它不承担 Bell 的业务预警和全局管理审计。
- 断网时边缘缓存事件,恢复后补传。
- 不在代码、日志、证据文件名和工单中泄露摄像头凭据、客户名或敏感地址。
### 3.6 管理端与外部集成
- Bell 自研并持有事件、Alert、ack、升级链、租户和审计真相;客户既有平台不得成为这些状态的唯一真相源。
- Bell 管理端持有 Tenant、Site、Area、RBAC、配额和全局审计真相;Sense 只消费当前租户/站点/角色上下文和带版本的配额/Area 策略投影。Area 策略变更与已有成像设备冲突时必须显式迁移或取消,禁止静默停用。
- 对外使用版本化 OpenAPI/Webhook;M3 客户端为值班室 Web + 响应式移动 H5,可嵌入客户系统。
- 投递层必须使用供应商无关的 provider 接口;试点至少有本地声光/Web 与一条短信或语音,生产前补齐两条独立路径及故障切换。
+13 -8
View File
@@ -16,16 +16,16 @@ YoVision 使用“通用底座 + 场景包”,按变化频率分为:
| 系统 | 语言/状态 | 职责 | 不负责 |
| --- | --- | --- | --- |
| Sense | Go,有状态 | 设备台账(`modality + capabilities`)、ONVIF、MediaMTX 控制、对账、探活、隧道、设备型触发、流分片、隐私区域设备准入 | AI 判定、事件业务、预警 |
| Sense | Go,有状态 | 设备台账(`modality + capabilities`)、ONVIF 能力探测、MediaMTX 控制、对账、探活、隧道、设备型触发、流分片、配额/Area 策略投影与准入执行、接入运维中心 | AI 判定、事件业务、预警、Tenant/Site/Area/RBAC/全局审计真相 |
| Brain | Python/CUDA,业务无状态 | 解码/推理、检测/姿态/跟踪/ReID、时间窗判定、事件 mapper、像素级触发 | 设备真相源、告警升级、租户权限 |
| Bell | Go + Web,有状态 | 事件校验/存储、规则、预警状态机、投递、反馈、租户/RBAC、审计、配额真相源和管理端 | 媒体转发、模型执行 |
| Bell | Go + Web,有状态 | 事件校验/存储、规则、预警状态机、投递、反馈、Tenant/Site/Area/RBAC、全局审计、配额与 `capture_policy` 真相源和统一管理端 | 媒体转发、模型执行、设备实际态 |
MediaMTX、PostgreSQL、MinIO、Prometheus 等作为独立基础设施部署。
## 3. 部署与系统边界
- 首期每个客户部署一套私有实例,数据和事件证据留在客户环境;数据库实体、RBAC、配置与 API 从第一版携带 `tenant_id` 并保持 SaaS-ready 边界。
- Bell 自研且是事件、Alert、ack、升级链、租户和审计的唯一业务真相源;客户平台通过版本化 OpenAPI/Webhook 集成,不反向接管核心状态机。
- Bell 自研且是事件、Alert、ack、升级链、Tenant/Site/Area/RBAC、配额和全局审计的唯一业务真相源;客户平台通过版本化 OpenAPI/Webhook 集成,不反向接管核心状态机。Sense 只保存执行所需的版本化只读投影,不形成第二份组织/策略真相。
- M1–M3 的视频入口只有 ONVIF/RTSP。现有 NVR 在售前盘点;仅支持 GB/T 28181 的项目必须建立独立适配器任务,不把国标信令混入 Sense 最小骨架。
- M3 客户端为值班室 Web + 响应式移动 H5,可嵌入客户系统;是否开发原生 App 在 M4 后另行决定。
@@ -45,7 +45,7 @@ Sense ── 视频流/触发信号 ──> Brain
主流程:
1. Bell 持有站点配额;Sense 在新增/启用设备时通过版本化内部 API 或只读投影校验。
1. Bell 持有站点、Area、配额与 `capture_policy`;Sense 在新增/启用设备时通过版本化内部 API 或只读投影校验,并记录所用投影版本。
2. Sense 维护设备期望态,通过 MediaMTX API 和对账器收敛实际态。
3. Brain 消费视频与触发信号,产生符合 v0.1 的事件。
4. Bell 做 schema 与代码级断言,生成平台 ULID,保存不可变事件。
@@ -55,7 +55,7 @@ Sense ── 视频流/触发信号 ──> Brain
首个 M3 数据流部署在 S2 民办寄宿学校的 16 路高风险点位,只运行越线、危险区域和聚集等匿名规则,不加载人脸底库。
## 5. 十条不可越界的决定
## 5. 十二条不可越界的决定
1. MediaMTX 独立运行,Sense 管配置与生命周期。
2. 设备型触发源归 Sense;需要解码的像素级触发归 Brain。
@@ -66,7 +66,9 @@ Sense ── 视频流/触发信号 ──> Brain
7. Bell 拥有 `site.max_video_channels`,Sense 在设备写路径执行;不跨 schema 直接写。
8. 事件片段写入客户侧 MinIO/S3,常态录像留在客户 NVR;元数据/审计、人脸和训练样本使用独立生命周期。
9. 投递状态机只依赖 Bell provider 接口,不直接依赖某家短信或语音 SDK;生产前至少两条独立路径并能故障切换。
10. 设备领域模型使用 `modality + capabilities`,页面不以摄像头作为唯一根实体;隐私策略属于 Area,Sense 在新增/启用写路径按成像能力执行,策略不可用时只阻断新变更,不静默切断已有链路。
10. 设备领域模型使用 `modality + capabilities`,页面不以摄像头作为唯一根实体;未实现协议适配器明确为 `adapter_not_ready`,不得用模拟遥测伪装交付。
11. Tenant/Site/Area/RBAC、配额、`capture_policy` 与全局审计属于 Bell;Sense 只读消费版本化投影并在设备写路径执行,投影不可用时只阻断相关新变更,不静默切断已有链路。
12. Sense 的设备操作审计先写本地持久化 outbox,再由幂等 relay 异步送入 Bell 全局审计;不得使用“先执行高风险操作、再尽力入队”的顺序。具体字段、签名、重放与留存契约必须由独立 API/契约任务冻结后实现。
## 6. 容量架构
@@ -83,14 +85,17 @@ Sense ── 视频流/触发信号 ──> Brain
- 对账器水平触发、幂等、指数退避、限制并发;部分失败不做跨系统回滚,只持续收敛。
- 孤儿删除必须有 10% 安全闸和人工可观察指标。
- 配额读取失败只阻止新增/启用,不中断已有流。
- Area 策略投影读取失败只阻止相关设备新增/启用;已有设备保持原状态并产生运维告警。策略变更与已有成像设备冲突时由 Bell 管理端显式处置。
- 高风险设备操作在本地事务内同时写期望态与审计 outbox;异步 relay 可重试、幂等投递到 Bell。T-004 只验证交互归属,不定义或实现接口契约。
- Brain 投递失败落本地队列重试,不阻塞实时推理主链路。
- Alert 先落库再投递,进程重启恢复未完成升级链。
- 事件证据技术默认保留 30 天并按生命周期删除;客户/法务在 M3 生产上线前确认法规适用性和最终期限,技术默认值不能覆盖其结论。
## 8. 数据与契约
- 核心实体:Tenant → Site → Area(含 `capture_policy`)/Device(含 `modality + capabilities`)→ StreamBinding/Zone;Rule → Event → Alert → DeliveryAttempt/Ack。
- Event 与 Alert 不合并:一个事件可触发多次预警与投递,一次预警也可聚合多个事件。
- Bell 核心实体:Tenant → Site → Area(含 `capture_policy`)以及 Role/Binding/Quota/Audit;Sense 核心实体:Device(含 `modality + capabilities`)→ StreamBinding/Zone,以及带 `source_version`/`synced_at` 的 SiteQuota/AreaPolicyProjection。两个 schema 以稳定逻辑 ID 关联,不跨 schema 写入。
- 业务实体:Rule → Event → Alert → DeliveryAttempt/Ack;Event 与 Alert 不合并。
- 一个事件可触发多次预警与投递,一次预警也可聚合多个事件。
- 事件 v0.1 以 `raw/contracts/event-v0.1.schema.json` 与 `raw/contracts/README.md` 为准;未知顶层字段拒绝,只允许通过 `ext` 扩展。
- v0.1 还需代码校验时间自洽、`confidence` 当前为 null、证据文件名隐私、唯一 primary sensor 等跨字段约束。
- S2 MVP 不处理人脸。首个人脸试点最早 M5,只允许经法务/客户门禁确认的 S4 成人访客/承包商白名单(≤10,000 人),并提供非人脸替代方式。
+23 -7
View File
@@ -7,16 +7,16 @@
- 角色:实施工程师。
- 目标:一次导入并验证一个默认 16 路视频站点,不逐路手工配置;操作入口位于通用设备台账的视频模态筛选下。
- 价值:降低交付时间并为 128 路扩展保留操作效率。
- 验收:支持待激活、逐设备结果、失败可重试、超配额明确拒绝;128 路规模下仍使用分页/批量流程。
- 关联:RQ-C-01~RQ-C-08,IX-001~IX-003。
- 验收:导入后先进入待激活,任务历史可查看逐设备成功/失败原因并仅重试失败项;认证失败时提供不回显旧密码的凭据更新路径;超配额明确拒绝;128 路规模下仍使用分页/批量流程。
- 关联:RQ-C-01~RQ-C-08,IX-001~IX-004、IX-015。
## US-002 查看设备与流健康
- 角色:平台运维/站点管理员。
- 目标:快速知道哪些设备离线、时间漂移、流未收敛或分片异常。
- 目标:快速知道哪些设备离线、认证失败、时间漂移、流未收敛、隧道异常或分片异常,并能区分期望态与各下游实际态。
- 价值:在业务预警受影响前定位故障。
- 验收:设备期望态与实际态分开显示;断线重连可观察;业务预警和运维告警不混用。
- 关联:RQ-C-06~RQ-C-08、RQ-C-29、RQ-C-34,IX-004。
- 验收:设备期望态与媒体/推理/存储实际态分开显示;对账差异、退避、下次重试和孤儿安全闸可观察;边缘隧道与视频数据面分别显示;业务预警和运维告警不混用。
- 关联:RQ-C-06~RQ-C-08、RQ-C-29、RQ-C-34,IX-004、IX-019。
## US-003 处置业务预警
@@ -63,8 +63,24 @@
- 角色:实施工程师/站点管理员。
- 目标:在统一设备台账中按模态和能力添加、筛选与查看视频、雷达、门磁、按钮、可穿戴等设备;隐私区域只允许非成像设备。
- 价值:M2 固化不会因 M6 异构设备接入而推倒重来的信息架构,同时把隐私要求落实为系统准入约束。
- 验收:一级入口使用“设备”;添加时先选模态,详情按能力显示页面;`non_imaging_only` Area 不允许新增或启用成像设备,策略读取失败时拒绝新变更并给出恢复路径;M2 完成信息架构与视频准入,M6 完成非视频适配器。
- 关联:RQ-S1-05、NFR-CMP-05,IX-014、IX-016。
- 验收:一级入口使用“设备”;添加时先选模态,详情按探测能力显示页面并标明来源、时间与生效差异;未实现适配器显示 `adapter_not_ready`,不得伪装在线;`non_imaging_only` Area 不允许新增或启用成像设备,策略读取失败时拒绝新变更并给出恢复路径;M2 完成信息架构与视频准入,M6 完成非视频适配器。
- 关联:RQ-S1-05、NFR-CMP-05,IX-014、IX-016、IX-019。
## US-009 以最小权限运维接入系统
- 角色:平台运维/租户管理员/站点管理员/只读用户。
- 目标:进入 Sense 时明确当前租户、站点、账号和角色,并只看到权限范围内的数据与操作。
- 价值:让私有化单租户交付也保留 SaaS-ready 隔离边界,避免原型把管理员权限当成默认事实。
- 验收:切换站点会刷新上下文;只读角色看不到写操作;深链越权安全拒绝且不泄露设备存在性;设备操作日志可带筛选上下文跳到 Bell 全局审计。
- 关联:RQ-C-31~RQ-C-33,IX-013、IX-020。
## US-010 管理站点与区域准入策略
- 角色:租户管理员/站点管理员。
- 目标:在统一管理端维护 Site、Area 层级与 `capture_policy`,并让 Sense 使用同一版本的策略执行设备准入。
- 价值:避免 Area 在 Sense、Bell 各有一份真相,保证权限、报表和隐私准入口径一致。
- 验收:Bell 是 Site/Area/配额/策略真相源;策略变更影响已有成像设备时必须显式选择迁移或取消,不自动停用;Sense 展示策略版本与同步状态,投影不可用时仅拒绝相关新写入。
- 关联:RQ-S1-05、NFR-CMP-05,IX-016、IX-020。
## 追溯规则
+8 -6
View File
@@ -4,10 +4,10 @@
| ID | 场景 | 必须覆盖的状态与行为 | 关联 US | 阶段 |
| --- | --- | --- | --- | --- |
| IX-001 | 批量导入设备 | 下载模板、上传校验、逐行错误、重复序列号、待激活、确认写入 | US-001 | M2 |
| IX-002 | 配额与批量结果 | 显示已用/上限;超 16 不等于非法,按站点配置校验;部分成功可重试 | US-001 | M2 |
| IX-001 | 批量导入设备 | 下载模板、上传校验、逐行错误、重复序列号、待激活、确认写入;任务历史保留逐项结果、操作者与时间,支持仅重试失败项 | US-001 | M2 |
| IX-002 | 配额与批量结果 | 显示已用/上限;超 16 不等于非法,按站点配置校验;部分成功可重试;配额不可读取时只禁用新增/启用并保留已有列表与流 | US-001 | M2 |
| IX-003 | 设备列表 | 分页/筛选/批量选择;128 路不一次加载所有视频和详情 | US-001 | M2/M4 |
| IX-004 | 设备健康 | 期望态、实际态、最后在线、时间漂移、分片、重试进度;运维告警独立 | US-002 | M2/M4 |
| IX-004 | 设备健康 | 期望态、媒体/推理/存储实际态、认证失败、最后在线完整时间与站点时区、时间漂移、分片、重试进度;运维告警独立,凭据更新只允许写入新值且旧值永不回显 | US-002 | M2/M4 |
| IX-005 | 预警到达 | 明确严重度、站点、时间、证据可用性;重复投递不产生重复处置 | US-003 | M3 |
| IX-006 | ack | 一次点击可确认,显示确认人/时间;并发 ack 有清晰结果 | US-003 | M3 |
| IX-007 | 升级 | 显示当前层级、下一次升级时间、每次投递状态;失败不能伪装成功 | US-003 | M3 |
@@ -17,11 +17,13 @@
| IX-011 | 规则试运行 | 明确“未正式生效”,展示命中样本与影响范围,支持取消/回滚 | US-005 | M4 |
| IX-012 | 升级链/静默 | 联系人顺序、超时、双通道;静默最长 4h、显示自动恢复时间、无永久选项 | US-005 | M3/M4 |
| IX-013 | 权限与隐私 | 越权统一处理;无授权租户不展示人脸入口;不泄露流 URL/凭据 | US-006 | M3/M4 |
| IX-014 | 设备模态与能力 | 一级入口为“设备”;列表可按 `modality` 筛选,添加时先选模态,详情按 `capabilities` 渐进展示;视频设备显示画面/媒体/检测区域,非视频设备保留连接与健康并显示对应遥测,不用禁用的视频页签占位 | US-008 | M2/M6 |
| IX-015 | 停用与收敛 | 明确区分暂停推理、停用设备接入和仅踢当前会话;操作前展示对观看、推理、录制与证据回捞的影响并二次确认;期望态立即改变、实际态经对账收敛,部分失败可逐项重试;重复请求不产生重复副作用,但每次请求均留审计结果 | US-001、US-002 | M2 |
| IX-016 | 隐私区域设备准入 | `capture_policy = non_imaging_only` 的 Area 不允许新增/启用具有成像能力的设备;前端就地禁用并解释,后端返回稳定错误码;策略读取失败时拒绝新变更并告警,已有设备遇到区域策略变化时进入显式处置流程,不静默留存或停用 | US-008 | M2/M6 |
| IX-014 | 设备模态与能力 | 一级入口为“设备”;列表可按 `modality` 筛选,添加时先选模态,详情按 `capabilities` 渐进展示;能力同时显示厂商/型号/固件/认证、探测来源与时间、探测值和生效值差异;视频设备显示画面/媒体/检测区域,未交付的非视频适配器显示 `adapter_not_ready`,不用虚构遥测或禁用的视频页签占位 | US-008 | M2/M6 |
| IX-015 | 停用与收敛 | 明确区分暂停推理订阅、停用设备接入和仅踢当前会话;暂停推理必须说明“解除推理侧订阅”,并在结果中显示推理订阅、其他 reader 数与上游按需拉流状态;操作前展示对观看、录制与证据回捞的影响并二次确认;期望态立即改变、实际态经对账收敛,部分失败可逐项重试;重复请求不产生重复副作用,但每次请求均留审计结果 | US-001、US-002 | M2 |
| IX-016 | 隐私区域设备准入 | Area 与 `capture_policy` 由 Bell 统一管理,Sense 消费带版本的只读投影;`non_imaging_only` 不允许新增/启用具有成像能力的设备;前端就地禁用并解释,后端返回稳定错误码;策略读取失败时拒绝新变更并告警,已有设备遇到策略变化时由 Bell 进入显式迁移/取消流程,不静默留存或停用 | US-008、US-010 | M2/M6 |
| IX-017 | 检测区域几何编辑 | 多边形和方向警戒线支持绘制、显式完成、撤销、清空、顶点编辑;警戒线在草稿/已保存态均显示方向箭头并可反转;鼠标与键盘坐标操作等效;画面参数变化后标记待校准且不自动改坐标;区域版本与规则版本独立 | US-005 | M3 |
| IX-018 | 草稿与跨系统上下文 | 几何草稿自动保存并可恢复;只有持久化失败或切换空间类型等会破坏草稿的动作才拦截确认;Sense↔Bell 双向携带并消费 `camera + zone + return_to` 上下文;返回后恢复草稿,版本已被他人更新时提示冲突且不覆盖 | US-005 | M3 |
| IX-019 | 运维中心与写入降级 | Sense 用一个一级“运维中心”聚合对账差异/退避/下次重试/孤儿安全闸、媒体与推理分片、边缘节点/隧道/补传、运维告警和系统状态;配额或 Area 策略不可读取时页面继续可读且已有链路不受影响,只禁用相关写操作;不得混入 Bell 业务预警 | US-002、US-008 | M2/M4/M6 |
| IX-020 | 管理上下文、RBAC 与审计归属 | 全局头部显示租户、站点、账号和角色;只读角色隐藏写操作,深链越权统一拒绝且不泄露资源是否存在;Tenant/Site/Area/RBAC/配额/全局审计由 Bell 管理,Sense 只呈现消费上下文和设备操作入口,携带 tenant/site/device/return_to 深链到 Bell 审计 | US-006、US-009、US-010 | M2/M4 |
## 全局状态
+45 -6
View File
@@ -41,6 +41,7 @@
--shadow: 0 14px 34px rgba(32, 52, 78, .12);
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { min-width: 320px; background: var(--bg); }
body { margin: 0; min-height: 100dvh; background: var(--bg); color: var(--text); font: 14px/1.5 Inter, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; }
button, input, select, textarea { font: inherit; }
@@ -177,6 +178,14 @@
.rule-context { margin-bottom: 12px; padding: 11px 13px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border: 1px solid rgba(56,189,248,.34); border-radius: 10px; background: rgba(56,189,248,.08); }
.rule-context[hidden] { display: none; }
.rule-context p { margin: 0; color: var(--muted); }
.site-grid { grid-template-columns: minmax(260px,.72fr) minmax(520px,1.45fr); }
.site-list { padding: 8px; display: grid; gap: 7px; }
.site-item { width: 100%; min-height: 62px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); color: var(--text); display: flex; justify-content: space-between; align-items: center; gap: 10px; text-align: left; }
.site-item.active { border-color: var(--primary); background: var(--primary-soft); }
.site-item small { display: block; color: var(--muted); }
.policy-note { margin: 12px 14px 0; padding: 10px 12px; border-left: 3px solid var(--warning); background: rgba(251,191,36,.08); color: var(--muted); }
.context-strip { margin-bottom: 12px; padding: 11px 13px; border: 1px solid rgba(56,189,248,.34); border-radius: 10px; background: rgba(56,189,248,.08); display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.context-strip p { margin: 0; color: var(--muted); }
.linked-zone { grid-column: 1/-1; padding: 11px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2); }
.linked-zone strong, .linked-zone small { display: block; }
.linked-zone small { color: var(--muted); }
@@ -217,6 +226,7 @@
.form-field { display: grid; gap: 6px; }
.form-field.full { grid-column: 1/-1; }
.form-field label { color: var(--muted); font-size: 12px; font-weight: 700; }
.radio-card { display: block; margin-bottom: 9px; padding: 11px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); }
.toast { position: fixed; right: 20px; bottom: 20px; z-index: 1100; max-width: min(420px, calc(100% - 40px)); padding: 12px 14px; border: 1px solid rgba(52,211,153,.35); border-radius: 10px; background: #0b2c25; color: #d9fff3; box-shadow: var(--shadow); transform: translateY(20px); opacity: 0; pointer-events: none; transition: .2s ease; }
.toast.show { transform: none; opacity: 1; }
.muted { color: var(--muted); }
@@ -240,11 +250,14 @@
.topbar { padding: 8px 12px; }
.site span, .top-actions .select { display: none; }
.content { padding: 12px; }
.btn, .select, .field, .textarea { min-height: 44px; }
.icon-btn { min-width: 44px; }
.duty-grid, .chain-grid, .chart-grid { grid-template-columns: 1fr; }
.alert-list, .action-scroll { max-height: none; }
.action-scroll { display: block; }
.disposition { border-left: 0; border-top: 1px solid var(--line); }
.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.site-grid { grid-template-columns: 1fr; }
.rule-grid { grid-template-columns: 1fr 1fr; }
.fact-grid { grid-template-columns: repeat(2, 1fr); }
.mobile-nav { position: fixed; z-index: 50; left: 8px; right: 8px; bottom: 8px; display: grid; grid-template-columns: repeat(5, 1fr); padding: 5px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); box-shadow: var(--shadow); }
@@ -296,6 +309,7 @@
<button data-view="rules"><svg class="icon"><use href="#i-rule"/></svg><span>规则策略</span></button>
<button data-view="chains"><svg class="icon"><use href="#i-phone"/></svg><span>升级链</span></button>
<button data-view="reports"><svg class="icon"><use href="#i-chart"/></svg><span>运营报表</span></button>
<button data-view="sites"><svg class="icon"><use href="#i-rule"/></svg><span>站点与 Area</span></button>
<button data-view="audit"><svg class="icon"><use href="#i-shield"/></svg><span>审计日志</span></button>
</nav>
<div class="shift-card"><strong>安保值班组 · 白班</strong><span>08:00–20:00 · 4 人在线</span><div class="shift-bar"><i></i></div></div>
@@ -303,7 +317,7 @@
<main class="main" id="main-content" tabindex="-1">
<header class="topbar">
<div class="site"><strong>青藤寄宿学校 · 值班中心</strong><span>客户侧私有实例 · 当前仅匿名安全规则</span></div>
<div class="site"><strong>青藤教育集团 · 青藤寄宿学校</strong><span>当前账号:林工 · 租户管理员 · Asia/Shanghai</span></div>
<div class="top-actions">
<select class="select" id="stateSelect" aria-label="原型状态演示"><option value="normal">正常状态</option><option value="loading">加载中</option><option value="weak">弱网 / 证据失败</option><option value="empty">队列为空</option><option value="denied">无权限</option></select>
<button class="btn icon-btn" id="themeToggle" aria-label="切换明暗主题" title="切换明暗主题"><svg class="icon"><use href="#i-sun"/></svg></button>
@@ -319,13 +333,13 @@
<div class="duty-grid">
<section class="card queue" aria-label="待处置预警队列">
<div class="card-head"><h2>待处置预警</h2><span class="badge danger">6 个未 ack</span></div>
<div class="queue-head"><div class="queue-tabs" role="tablist"><button class="active" role="tab" aria-selected="true">全部 9</button><button role="tab">高危 3</button><button role="tab">我的 2</button></div></div>
<div class="queue-head"><div class="queue-tabs" aria-label="预警队列筛选"><button class="active" aria-pressed="true">全部 9</button><button aria-pressed="false">高危 3</button><button aria-pressed="false">我的 2</button></div></div>
<div class="alert-list" id="alertList"></div>
</section>
<section class="card evidence" aria-label="事件事实与证据">
<div class="evidence-summary"><div><span class="badge danger" id="selectedSeverity">高危 · S1</span><h2 id="selectedTitle">危险区域闯入</h2><p id="selectedMeta">北围墙 01 · 23:14:08 · EVT-01J8…7A2</p></div><button class="btn icon-btn" aria-label="更多事件操作"><svg class="icon"><use href="#i-more"/></svg></button></div>
<div class="evidence-tabs" role="tablist"><button class="active" role="tab" aria-selected="true">视频片段</button><button role="tab">抓拍 2</button><button role="tab">结构化事实</button></div>
<div class="evidence-tabs" aria-label="证据类型筛选"><button class="active" aria-pressed="true">视频片段</button><button aria-pressed="false">抓拍 2</button><button aria-pressed="false">结构化事实</button></div>
<div class="video-stage" id="videoStage">
<svg viewBox="0 0 960 540" role="img" aria-label="北围墙 01 事件视频模拟画面,匿名目标进入危险区域">
<defs><linearGradient id="bellSky" x1="0" y1="0" x2="0" y2="1"><stop stop-color="#102337"/><stop offset="1" stop-color="#060a11"/></linearGradient><pattern id="bellScan" width="8" height="8" patternUnits="userSpaceOnUse"><path d="M0 8h8" stroke="#fff" stroke-opacity=".035"/></pattern></defs>
@@ -392,9 +406,17 @@
<div class="grid chart-grid"><article class="card chart"><h2>近 7 日预警量</h2><p>按站点日期汇总;可切换到每路每天误报数。</p><div class="bars" role="img" aria-label="近七日预警量依次为 18、22、15、31、27、24、19"><div class="bar" style="--h:58%"><span>周一</span></div><div class="bar" style="--h:70%"><span>周二</span></div><div class="bar" style="--h:48%"><span>周三</span></div><div class="bar" style="--h:100%"><span>周四</span></div><div class="bar" style="--h:87%"><span>周五</span></div><div class="bar" style="--h:77%"><span>周六</span></div><div class="bar" style="--h:61%"><span>周日</span></div></div></article><article class="card chart"><h2>处置结果</h2><p>本月已闭环事件。</p><div class="donut" role="img" aria-label="正常处置 61%,误报 22%,升级未闭环 17%"></div><div class="legend"><span><i style="background:var(--success)"></i>正常处置 61%</span><span><i style="background:var(--warning)"></i>误报 22%</span><span><i style="background:var(--danger)"></i>未闭环 17%</span></div></article></div>
</section>
<section class="view" data-page="sites">
<div class="page-head"><div><h1>站点与 Area</h1><p>Bell 是 Tenant、Site、Area、配额与隐私准入策略的唯一真相源;Sense 只消费版本化投影。</p></div><div class="page-actions"><button class="btn">管理角色权限</button><button class="btn btn-primary" id="newSite">新建站点</button></div></div>
<div class="grid stats"><article class="card stat"><span>租户</span><strong>1</strong><span class="muted">青藤教育集团</span></article><article class="card stat"><span>站点</span><strong>2</strong><span class="muted">1 个试点中</span></article><article class="card stat"><span>视频配额</span><strong>16 / 128</strong><span class="muted">默认 16,不是硬上限</span></article><article class="card stat"><span>策略投影</span><strong>v42</strong><span class="muted">Sense 2 分钟前已同步</span></article></div>
<div class="grid site-grid"><article class="card"><div class="card-head"><h2>站点</h2><button class="btn btn-quiet">筛选</button></div><div class="site-list"><button class="site-item active"><span><strong>青藤寄宿学校</strong><small>site-ivy · S2 校园包</small></span><span class="badge success">启用</span></button><button class="site-item"><span><strong>城东试验点</strong><small>site-east · 未绑定场景包</small></span><span class="badge warning">准备中</span></button></div></article>
<article class="card"><div class="card-head"><div><h2>青藤寄宿学校 · Area</h2><span class="muted">层级、报表口径和设备准入共用同一逻辑 ID</span></div><button class="btn btn-primary" id="newArea">新建 Area</button></div><div class="policy-note"><strong>策略变更不会静默处置已有设备。</strong> 若改为“仅非成像”且 Area 内已有摄像头,必须显式迁移设备或取消变更。</div><div class="table-wrap"><table><thead><tr><th>Area</th><th>父级</th><th>capture_policy</th><th>设备</th><th>策略版本</th><th>投影状态</th><th>操作</th></tr></thead><tbody><tr><td><strong>北围墙</strong><br><span class="muted">area-north-wall</span></td><td>校园</td><td><span class="badge success">允许成像</span></td><td>4 视频</td><td>v42</td><td><span class="badge success">已同步</span></td><td><button class="btn" data-edit-area="north">编辑策略</button></td></tr><tr><td><strong>宿舍卧室</strong><br><span class="muted">area-dorm-room</span></td><td>宿舍楼</td><td><span class="badge warning">仅非成像</span></td><td>0 视频 · 1 雷达规划</td><td>v38</td><td><span class="badge success">已同步</span></td><td><button class="btn" data-edit-area="dorm">编辑策略</button></td></tr><tr><td><strong>实验楼入口</strong><br><span class="muted">area-lab-gate</span></td><td>实验楼</td><td><span class="badge success">允许成像</span></td><td>2 视频</td><td>v41</td><td><span class="badge warning">待 Sense 拉取</span></td><td><button class="btn" data-edit-area="lab">编辑策略</button></td></tr></tbody></table></div></article></div>
</section>
<section class="view" data-page="audit">
<div class="context-strip" id="auditContext" hidden><p><strong>来自 Sense 设备日志:</strong>租户 <span id="auditTenant">—</span> · 站点 <span id="auditSite">—</span> · 设备 <span id="auditDevice">—</span>。已锁定筛选上下文。</p><a class="btn" id="auditReturn" href="../sense/index.html?view=device-detail">返回设备详情 ↗</a></div>
<div class="page-head"><div><h1>审计日志</h1><p>查询、ack、规则发布和证据下载均可追溯;不记录密码与可复用连接串。</p></div><div class="page-actions"><button class="btn">导出审计记录</button></div></div>
<article class="card"><div class="card-head"><div class="search"><svg class="icon"><use href="#i-search"/></svg><input class="field" aria-label="搜索审计日志" placeholder="操作、角色或对象编号"></div><span class="badge info">只读</span></div><div class="table-wrap"><table><thead><tr><th>时间</th><th>角色</th><th>动作</th><th>对象</th><th>结果</th><th>来源</th></tr></thead><tbody>
<article class="card"><div class="card-head"><div class="search"><svg class="icon"><use href="#i-search"/></svg><input class="field" id="auditSearch" aria-label="搜索审计日志" placeholder="操作、角色或对象编号"></div><span class="badge info">只读</span></div><div class="table-wrap"><table><thead><tr><th>时间</th><th>角色</th><th>动作</th><th>对象</th><th>结果</th><th>来源</th></tr></thead><tbody id="auditRows">
<tr><td>23:14:18</td><td>站点值班员</td><td>ACK_ALERT</td><td class="mono">ALT-1842</td><td><span class="badge success">成功</span></td><td>值班室 Web</td></tr><tr><td>22:52:06</td><td>站点管理员</td><td>PUBLISH_RULE</td><td class="mono">RULE-007</td><td><span class="badge success">成功</span></td><td>管理端</td></tr><tr><td>22:20:31</td><td>只读用户</td><td>VIEW_EVIDENCE</td><td class="mono">EVT-01J8…52C</td><td><span class="badge danger">拒绝</span></td><td>响应式 H5</td></tr>
</tbody></table></div></article>
</section>
@@ -419,6 +441,10 @@
<dialog id="falseDialog" aria-labelledby="falseTitle"><form method="dialog"><div class="dialog-head"><h2 id="falseTitle">标记为误报</h2><button class="btn btn-quiet icon-btn" value="cancel" aria-label="关闭对话框">×</button></div><div class="dialog-body"><p>这只会追加事件 outcome,不会修改原始事件事实和证据。</p><div class="form-grid"><div class="form-field full"><label for="falseReason">误报原因</label><select id="falseReason" class="select" required><option value="">请选择</option><option>区域配置不准确</option><option>目标分类错误</option><option>时段策略不准确</option><option>其他</option></select></div><div class="form-field full"><label for="falseNote">补充说明(可选)</label><textarea id="falseNote" class="textarea" placeholder="便于算法团队归因,不填写真实个人信息"></textarea></div><div class="form-field full"><label><input type="checkbox" checked> 进入标注复核队列</label></div></div></div><div class="dialog-foot"><button class="btn" value="cancel">取消</button><button class="btn btn-danger" id="confirmFalse" value="default">确认标记</button></div></form></dialog>
<dialog id="muteDialog" aria-labelledby="muteTitle"><form method="dialog"><div class="dialog-head"><h2 id="muteTitle">创建限时静默</h2><button class="btn btn-quiet icon-btn" value="cancel" aria-label="关闭对话框">×</button></div><div class="dialog-body"><div class="form-grid"><div class="form-field"><label for="muteScope">影响范围</label><select id="muteScope" class="select"><option>北围墙 01 · 当前规则</option><option>北围墙区域</option></select></div><div class="form-field"><label for="muteTime">持续时间</label><select id="muteTime" class="select"><option>30 分钟</option><option>1 小时</option><option>2 小时</option><option>4 小时(上限)</option></select></div><div class="form-field full"><label for="muteCause">原因</label><textarea id="muteCause" class="textarea" required placeholder="例如:现场施工维护,已安排人工巡查"></textarea></div></div><p class="muted">到期自动恢复;S1 高危事件与设备离线运维告警不受此静默影响。</p></div><div class="dialog-foot"><button class="btn" value="cancel">取消</button><button class="btn btn-primary" id="confirmMute" value="default">确认并记录审计</button></div></form></dialog>
<dialog id="areaDialog" aria-labelledby="areaTitle"><form method="dialog"><div class="dialog-head"><h2 id="areaTitle">编辑 Area 准入策略</h2><button class="btn btn-quiet icon-btn" value="cancel" aria-label="关闭对话框">×</button></div><div class="dialog-body"><div class="form-grid"><div class="form-field"><label for="areaName">Area</label><input id="areaName" class="field" value="北围墙"></div><div class="form-field"><label for="areaParent">父级</label><select id="areaParent" class="select"><option>校园</option><option>教学区</option><option>宿舍楼</option></select></div><div class="form-field full"><label for="areaPolicy">capture_policy</label><select id="areaPolicy" class="select"><option value="video_allowed">允许成像设备</option><option value="non_imaging_only">仅允许非成像设备</option></select></div><div class="form-field full"><label for="areaReason">变更原因</label><textarea id="areaReason" class="textarea" placeholder="必填,进入全局审计"></textarea></div></div><p class="muted">保存生成新策略版本;Sense 投影同步前会显示旧版本,不允许绕过后端准入检查。</p></div><div class="dialog-foot"><button class="btn" value="cancel">取消</button><button type="button" class="btn btn-primary" id="saveAreaPolicy">保存策略版本</button></div></form></dialog>
<dialog id="policyConflictDialog" aria-labelledby="policyConflictTitle"><form method="dialog"><div class="dialog-head"><h2 id="policyConflictTitle">策略与已有设备冲突</h2><button class="btn btn-quiet icon-btn" value="cancel" aria-label="关闭对话框">×</button></div><div class="dialog-body"><p>北围墙当前有 4 台成像设备。改为“仅非成像”前必须选择显式处置;系统不会自动停用、移动或保留违规设备。</p><label class="radio-card"><input type="radio" name="conflictAction" value="move"> <strong>先迁移 4 台设备到允许成像的 Area</strong><br><span class="muted">创建迁移任务,全部成功后再发布策略。</span></label><label class="radio-card"><input type="radio" name="conflictAction" value="cancel" checked> <strong>取消本次策略变更</strong><br><span class="muted">保持 v42 和现有设备不变。</span></label></div><div class="dialog-foot"><button class="btn" value="cancel">返回编辑</button><button class="btn btn-primary" value="default" id="confirmPolicyConflict">确认选择</button></div></form></dialog>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script>
@@ -440,8 +466,8 @@
function switchView(name) { $$('.view').forEach(v=>v.classList.toggle('active',v.dataset.page===name)); $$('[data-view]').forEach(b=>{const on=b.dataset.view===name;b.toggleAttribute('aria-current',on);b.classList.toggle('active',on);}); $('#stateSelect').value='normal'; renderState('normal'); $('#main-content').focus({preventScroll:true}); }
$$('[data-view]').forEach(b=>b.addEventListener('click',()=>switchView(b.dataset.view)));
$$('.queue-tabs button').forEach(b=>b.addEventListener('click',()=>{$$('.queue-tabs button').forEach(x=>{x.classList.toggle('active',x===b);x.setAttribute('aria-selected',x===b)});notify(`队列已切换到“${b.textContent}”`);}));
$$('.evidence-tabs button').forEach(b=>b.addEventListener('click',()=>{$$('.evidence-tabs button').forEach(x=>{x.classList.toggle('active',x===b);x.setAttribute('aria-selected',x===b)});notify(`正在演示“${b.textContent}”标签结构`);}));
$$('.queue-tabs button').forEach(b=>b.addEventListener('click',()=>{$$('.queue-tabs button').forEach(x=>{x.classList.toggle('active',x===b);x.setAttribute('aria-pressed',x===b)});notify(`队列已切换到“${b.textContent}”`);}));
$$('.evidence-tabs button').forEach(b=>b.addEventListener('click',()=>{$$('.evidence-tabs button').forEach(x=>{x.classList.toggle('active',x===b);x.setAttribute('aria-pressed',x===b)});notify(`正在演示“${b.textContent}”标签结构`);}));
function renderState(state) { const layer=$('#stateLayer'), views=$$('.view'); layer.classList.toggle('show',state!=='normal'); views.forEach(v=>v.style.display=state==='normal'?'':'none'); if(state==='normal')return; const states={
loading:`<div><div class="skeleton" aria-label="正在加载"><i></i><i></i><i></i><i></i></div><h2>正在读取最新预警状态</h2><p>超过 300ms 显示进度;ack 和升级事实始终从服务端重新读取。</p></div>`,
@@ -458,6 +484,12 @@
$('#muteButton').addEventListener('click',()=>$('#muteDialog').showModal());
$('#confirmMute').addEventListener('click',e=>{if(!$('#muteCause').value.trim()){e.preventDefault();notify('请填写静默原因。');$('#muteCause').focus();return;}notify('已创建限时静默,到期将自动恢复并通知值班组。');});
$$('[data-edit-area]').forEach(button=>button.addEventListener('click',()=>{const isDorm=button.dataset.editArea==='dorm';$('#areaName').value=isDorm?'宿舍卧室':button.dataset.editArea==='lab'?'实验楼入口':'北围墙';$('#areaPolicy').value=isDorm?'non_imaging_only':'video_allowed';$('#areaDialog').showModal();}));
$('#newArea').addEventListener('click',()=>{$('#areaName').value='';$('#areaPolicy').value='video_allowed';$('#areaDialog').showModal();});
$('#newSite').addEventListener('click',()=>notify('新建站点需先填写时区、场景包、配额和管理员,本原型只枚举入口。'));
$('#saveAreaPolicy').addEventListener('click',()=>{if(!$('#areaReason').value.trim()){notify('请填写变更原因。');$('#areaReason').focus();return;}if($('#areaName').value==='北围墙'&&$('#areaPolicy').value==='non_imaging_only'){$('#areaDialog').close();$('#policyConflictDialog').showModal();return;}$('#areaDialog').close();notify('Area 策略新版本已保存,等待 Sense 拉取投影。');});
$('#confirmPolicyConflict').addEventListener('click',()=>{const action=$('input[name="conflictAction"]:checked').value;notify(action==='move'?'已创建 4 台设备迁移任务;完成前不会发布策略。':'已取消策略变更,v42 和已有设备保持不变。');});
const ruleDialog = $('#ruleDialog');
function openRuleEditor() { ruleDialog.showModal(); }
$('#openRuleDialog').addEventListener('click', openRuleEditor);
@@ -473,6 +505,13 @@
setInterval(()=>{if($('#ackState').textContent==='已 ack'||remain<=0)return;remain--;const m=String(Math.floor(remain/60)).padStart(2,'0'),s=String(remain%60).padStart(2,'0');$('#countdown').textContent=`${m}:${s}`;},1000);
const routeParams = new URLSearchParams(location.search);
if (routeParams.get('view')) switchView(routeParams.get('view'));
if (routeParams.get('view')==='audit' && (routeParams.has('device')||routeParams.has('site'))) {
const tenant=routeParams.get('tenant')||'当前租户',site=routeParams.get('site')||'当前站点',device=routeParams.get('device')||'全部设备';
$('#auditContext').hidden=false;$('#auditTenant').textContent=tenant;$('#auditSite').textContent=site;$('#auditDevice').textContent=device;$('#auditSearch').value=device;
$('#auditRows').insertAdjacentHTML('afterbegin',`<tr><td>10:47:36</td><td>站点管理员</td><td>PAUSE_INFERENCE</td><td class="mono">${device}</td><td><span class="badge success">成功</span></td><td>Sense outbox · relay 已送达</td></tr>`);
$('#auditReturn').href=`../sense/index.html?view=device-detail&device=${encodeURIComponent(device)}&return_to=bell-audit`;
notify('已应用来自 Sense 的租户、站点和设备审计筛选。');
}
if (routeParams.has('camera')) {
switchView('rules'); $('#ruleContext').hidden=false;
const camera=routeParams.get('camera'); if([...$('#ruleCamera').options].some(option=>option.value===camera)) $('#ruleCamera').value=camera;
File diff suppressed because one or more lines are too long
+9 -3
View File
@@ -125,7 +125,7 @@ erDiagram
| --- | --- | --- |
| **Tenant 租户** | 计费与数据隔离的最小单位 | 所有查询必须带 tenant_id,无例外 |
| **Site 站点** | 一个物理场所(一户 / 一所学校 / 一个小区) | 升级链、时段策略挂在这一层 |
| **Area 区域** | 站点内的逻辑分区(教学楼 / 单元 / 客厅) | 用于权限范围、报表口径与设备准入;`capture_policy` 表达是否仅允许非成像设备 |
| **Area 区域** | 站点内的逻辑分区(教学楼 / 单元 / 客厅),由 Bell 统一管理 | 用于权限范围、报表口径与设备准入;`capture_policy` 表达是否仅允许非成像设备,Sense 只消费带版本的策略投影 |
| **Device 设备** | **不叫 Camera**;`modality` 表达 video/radar/contact/button/wearable/other,`capabilities` 表达成像、媒体、遥测等具体能力 | 模态负责稳定分类,能力负责决定字段与操作,避免接入新设备时重做台账和页面 |
| **Zone 画面区域** | 画在某路画面上的多边形或警戒线 | 与 Area 是两回事:Area 是物理概念,Zone 是像素坐标 |
| **Rule 规则** | 「主体 × 条件 × 时空 × 动作」的可配置组合 | 三级覆盖:租户默认 → 站点 → 设备 |
@@ -608,14 +608,18 @@ GPU 型号和数量不在需求阶段写死。M1 用 5 路打通,M3 以 16 路
在参考项目基础上扩展多租户与规则/事件。**字段级设计见《03》,此处只给结构与关键约束。**
```sql
-- ── 组织(Bell 为真相源;Sense 只读配额投影)────────
-- ── 组织(Bell 为真相源;Sense 不直接写这些表)────────
tenants(id, code, name, status, plan, created_at)
sites(id, tenant_id, code, name, scene_pack, subnet CIDR, timezone, status,
max_video_channels INT NOT NULL DEFAULT 16 CHECK (max_video_channels BETWEEN 1 AND 128))
areas(id, site_id, name, parent_id,
capture_policy) -- video_allowed | non_imaging_only
-- ── 设备(不叫 cameras,为异构传感器预留)────
-- ── Sense 只读投影(来源版本与同步时间必须可追溯)────
site_quota_projections(site_id, max_video_channels, source_version, synced_at)
area_policy_projections(area_id, site_id, capture_policy, source_version, synced_at)
-- ── Sense 设备(不叫 cameras,为异构传感器预留)────
devices(
id, tenant_id, site_id, area_id,
modality, -- video | radar | contact | button | wearable | other
@@ -703,6 +707,8 @@ CREATE INDEX ON alerts(state) WHERE state NOT IN ('closed','suppressed');
7. 人脸相关表独立 schema、独立加密、独立审计;`persons.valid_until` 必填,无"永久有效"选项
8. `sites.max_video_channels` 默认 16、最大 128;配额在写入设备时校验,分片容量由运行时配置与压测决定
9. `sites.max_video_channels` 由 Bell 持有,Sense 通过版本化内部 API 或只读投影校验设备新增/启用;不得跨 schema 直接写入
10. Tenant/Site/Area/RBAC、配额与全局审计属于 Bell;Sense 仅使用稳定逻辑 ID 关联并保存带 `source_version` / `synced_at` 的执行投影
11. 高风险设备写操作与本地审计 outbox 在 Sense 同一事务提交,再由幂等 relay 异步汇入 Bell;审计接口字段、签名和重放规则另立契约任务冻结
---
+7 -4
View File
@@ -1067,7 +1067,7 @@ capacity:
`stream_bindings` 同时记录 `mtx_instance` 与 `inference_shard`。默认按 `site_id + device_id` 稳定分配;扩容时允许迁移,但同一设备任一时刻只能归属一个有效媒体分片和一个推理分片。
`sites.max_video_channels` 的唯一真相源在 Bell;Sense 在设备新增/启用写路径通过版本化内部 API 或只读投影执行配额校验,不跨 schema 直接写入。配额依赖暂时不可用时只拒绝新的容量变更,已有视频链路继续运行。
`sites.max_video_channels`、Site/Area 层级和 `areas.capture_policy` 的唯一真相源在 Bell;Sense 在设备新增/启用写路径通过版本化内部 API 或带 `source_version` / `synced_at` 的只读投影执行校验,不跨 schema 直接写入。配额或 Area 策略依赖暂时不可用时只拒绝相关新变更,已有视频链路继续运行并产生运维告警。
> **不要单实例硬扛 128 路**:扩容必须通过增加分片与 Worker 完成。单分片故障只允许影响该分片,不能拖垮整个站点。GPU 型号与数量不在架构文档写死,M1 用 5 路打通,M3 建立 16 路基线,M4/M5 再验证 64/128 路。
@@ -1115,7 +1115,8 @@ OpenTelemetry → Jaeger,排查单帧处理延迟与端到端事件链路。
| 端 | 核心功能 |
| --- | --- |
| **管理系统(Web)** | 租户/站点/设备管理、批量开通、规则配置(含试运行)、Zone 画图、升级链配置、事件看板与筛选、事件详情(视频+观测回放)、处置与工单、误报标记、统计报表、审计查询、RBAC |
| **Sense 接入工作台(Web)** | 当前租户/站点上下文、设备台账与批量开通、能力探测、Zone 画图、对账/分片/边缘隧道/补传/运维告警;不管理 Tenant/Site/Area/RBAC 或全局审计 |
| **Bell 统一管理端(Web)** | Tenant/Site/Area/配额/`capture_policy`、RBAC、规则配置(含试运行)、升级链、事件看板与筛选、事件详情(视频+观测回放)、处置与工单、误报标记、统计报表、全局审计;设备操作日志从 Sense 汇入并可深链返回 |
| **App(家属/值班员)** | 预警接收与 ack、事件详情与视频回看、一键呼叫、误报反馈、限时静默、设备状态、(家属)多老人切换 |
| **大屏/值班台** | 实时事件流、地图/平面图点位、声光联动、当班交接 |
| **Webhook/开放 API** | 对接客户既有平台、门禁、消防、工单系统 |
@@ -1144,7 +1145,9 @@ OpenTelemetry → Jaeger,排查单帧处理延迟与端到端事件链路。
| --- | --- | --- | --- | --- | --- |
| **Sense** | 感知系统 | L1 接入 | Go | 有(设备台账) | M1 |
| **Brain** | 推理系统 | L2 流水线 + L3 算法 | Python / CUDA | **无状态** | M3 |
| **Bell** | 管理系统 | L4 业务 + L0 基座 | Go + 前端 | 有(事件、告警) | M3 → M4 |
| **Bell** | 管理系统 | L4 业务 + L0 基座 | Go + 前端 | 有(事件、告警、组织与策略真相) | M3 → M4 |
Sense 的高风险设备操作不能采用“先执行、再尽力记录审计”。实现时应在本地事务内同时写期望态与持久化 outbox,再由幂等 relay 异步汇入 Bell 全局审计。该跨系统接口的字段、签名、重放和留存必须另立 API/契约任务,不在 UI 原型任务中顺带冻结。
三者之间只有一条契约——**事件实例 JSON**(§2.6,规范本体见 `contracts/event-v0.1.schema.json`)。Sense 向 Brain 供流与信号,Brain 产出事件,Bell 消费事件。这条边界让推理侧换模型、换框架(Savant → Pipeless、YOLO → YOLOX)时,另两个系统一行不改。
@@ -1228,7 +1231,7 @@ redis 消息总线
- [ ] 终止服务时的底库彻底删除义务与证明方式已约定
## 架构
- [ ] `devices` 表而非 `cameras`,含 `modality + capabilities`;Area 持有 `capture_policy`
- [ ] `devices` 表而非 `cameras`,含 `modality + capabilities`;Bell 持有 Area 与 `capture_policy`,Sense 只存带版本的准入投影
- [ ] 设备身份用序列号
- [ ] 触发入口独立成 handler
- [ ] `sync_state` 每系统一行
+6 -4
View File
@@ -31,6 +31,7 @@
- WireGuard 控制面隧道
- mediamtx 鉴权回调(401 + 踢流是四种停用粒度之一)
- 容量配额执行:设备新增/启用时读取 Bell 的站点配额,只拒绝超额变更;配额服务暂时不可用时不影响已有流
- Area 准入执行:消费 Bell 的版本化 `capture_policy` 投影;策略不可用时只拒绝相关新写入,不静默中断已有设备
- 流绑定与媒体分片调度:维护 `mtx_instance`,默认 16 路交付;扩到 128 路时按 `media_shard.max_streams` 横向分片
- 边缘节点 agent:推流、本地环形缓冲、断网续传
- **设备型触发源**:雷达、门磁、按钮、ONVIF 事件订阅
@@ -54,8 +55,8 @@
- 告警状态机:升级链、ack、抑制、静默(**≤4h,无永久选项**)
- 投递:push / 短信 / 语音,**双供应商**
- 误报反馈闭环:`outcome` 回写 Brain
- 多租户 RBAC、`tenant_features`(人脸授权开关:未授权时能力在 API 与 UI 中**不可见**,不是禁用)
- 审计日志
- Tenant/Site/Area、`capture_policy`、多租户 RBAC、`tenant_features`(人脸授权开关:未授权时能力在 API 与 UI 中**不可见**,不是禁用)
- 全局审计日志;接收 Sense 设备操作审计并保留来源上下文
- 站点容量配额的唯一真相源:`site.max_video_channels` 默认 16、上限 128;提供版本化内部读取接口/投影给 Sense 执行
- 管理后台前端 + 大屏:按 128 路设计分页/虚拟列表、筛选与批量操作,不一次性加载全部视频
@@ -149,9 +150,10 @@ Bell/
| 5 | 一个 PostgreSQL 还是三个 | **一个实例,schema 分离**(`sense` / `bell`),Brain 无 schema。"DB 是唯一真相源"是对账器成立的前提,拆库即失效 |
| 6 | 16 路与 128 路分别指什么 | **16 路是默认交付规格,128 路是本阶段单逻辑站点上限**,都不是“单进程/单服务器保证值”。媒体与推理必须横向分片;单机承载量由分辨率、码率、帧率、模型和硬件基准测试决定 |
| 7 | 站点配额谁拥有、谁执行 | **Bell 拥有 `sites.max_video_channels`,Sense 在设备新增/启用写路径执行**。通过版本化内部 API 或只读投影同步,不允许 Sense 直接写 Bell schema;依赖暂时不可用时拒绝新变更,但已有视频链路继续运行 |
| 8 | 设备模态与隐私准入怎么建模 | 台账使用 `modality + capabilities`,不以摄像头作为唯一根实体;**Area 拥有 `capture_policy`,Sense 按设备 `captures_image` 能力在新增/启用写路径执行**。策略不可用时拒绝新变更并告警,已有链路不静默停用;M1 固化模型,M6 再增加非视频适配器 |
| 8 | 设备模态与隐私准入怎么建模 | 台账使用 `modality + capabilities`,不以摄像头作为唯一根实体;**Bell 拥有 Area 与 `capture_policy`,Sense 消费带版本投影并按设备 `captures_image` 能力在新增/启用写路径执行**。策略不可用时拒绝相关新变更并告警,已有链路不静默停用;M1 固化模型,M6 再增加非视频适配器 |
| 9 | 接入操作审计怎么进入 Bell | Sense 在同一事务写设备期望态和持久化 outbox,再由幂等 relay 异步汇入 Bell 全局审计;不得先执行高风险操作再尽力入队。字段、签名、重放和留存由独立 API/契约任务冻结 |
第 2、3、6、7、8 条是本文档新定的,若实施中发现更合适的切法,改这里并同步《02》《03》。
第 2、3、6、7、8、9 条是本文档新定的,若实施中发现更合适的切法,改这里并同步《02》《03》。
---
@@ -4,6 +4,7 @@
> 初始输入:Claude 评审草稿,2026-08-04
> 复核:Codex 从产品、全栈架构与 UI/UX 角度对照《02》《03》《08-三系统职责划分》、US/IX 和当前原型核验
> 状态:项目负责人已确认按本修订稿继续;最终行为已同步到 `docs/08-interaction-checklist.md`
> 后续:第二轮缺口复核与最终 IA 见 `11-Sense原型-第二轮缺口.md`、`12-Sense原型-待办清单.md`;若与本文原型范围冲突,以第二轮修订为准
---
+139
View File
@@ -0,0 +1,139 @@
# Sense 原型第二轮评审复核版
> 原始输入:Claude《Sense 原型第二轮评审》,2026-08-04
> 复核基准:`docs/design/sense/index.html`,提交 `e18a1ea`
> 复核视角:产品边界、全栈架构、M2 用户流程与 UI/UX
> 状态:项目负责人已确认按本复核结论继续修改
---
## 1. 总结
Claude 指出的对账运维、设备能力来源、非视频适配器阶段、待激活、凭据恢复、写入降级和边缘隧道等缺口方向成立;但原稿不能直接作为执行清单,主要有三类问题:
1. **基准落后**:设备分页和推理收敛成功态已经实现,分片数据也不再是原稿引用的 `8 / 32`。
2. **范围扩大**:T-004 当前关联 US-001、US-002、US-008 和部分 US-005,并未承诺一次画完完整 M2/M4 管理端。新增 P0 页面必须先同步 US/IX 与任务范围。
3. **系统归属混淆**:`routes.md` 的 `/sites`、`/operations`、`/audit` 是统一管理 Web 的候选页面职责,不等于都应成为 Sense 一级导航。Bell 拥有租户/RBAC、审计、配额真相源和管理端;Sense 负责设备接入与运行态运维。
因此,本轮采纳“补核心流程、收敛导航、冻结归属”的方案,不采用“Sense 导航机械扩为 8 项”。
---
## 2. 对原评审事实的纠正
| 原判断 | 当前事实 | 处理 |
| --- | --- | --- |
| 设备列表分页未实现 | 已有 5 条/页、上一页/下一页、页数和总数 | 标记完成;批量选择未来新增时再定义跨页语义 |
| “推理绑定 16/16”仍是紫色满格 | 已改为“推理配置收敛 16/16”绿色成功态 | 标记完成 |
| 分片仍显示 `8 / 32` | 当前为 `16 / 32`,并显示“当前 1 / 最多 4” | 只补“32 是可配置建议值、最终由压测确定” |
| T-004 `write_paths` 只有三个文件 | 已扩展到需求、架构、US/IX、评审稿和两个原型 | 更新清单基准 |
| modality 筛选包含 ONVIF/MQTT | modality 是 video/radar;ONVIF/RTSP/MQTT 属于 protocol | 文档严格区分模态、设备种类、协议和能力 |
---
## 3. 最终产品信息架构
### 3.1 Sense Edge Console
Sense 保持最多五个一级入口,移动端底部导航不超过五项:
```text
运行总览
实时监控
设备
接入任务
运维
├─ 对账与孤儿资源
├─ 媒体分片
├─ 边缘节点与隧道
├─ 运维告警
└─ 系统状态
```
原“系统状态”并入“运维”。总览的待收敛项、设备告警和分片状态均可下钻到运维的对应子页。Sense 顶栏显示当前租户、站点、用户和角色,并消费 Bell 下发的授权上下文,但不自建租户/RBAC 真相源。
### 3.2 Bell / 统一管理端
以下能力归 Bell/统一管理端,而不是在 Sense 重复建设一级页面:
- Tenant、Site、Area 管理和 RBAC。
- Area 的 `capture_policy` 编辑与已有成像设备的显式处置流程。
- 全局审计查询;Sense 设备操作记录只提供带设备筛选的深链。
- `site.max_video_channels` 的配置与真相源。
Area 与策略由 Bell 持有,Sense 使用带版本的只读投影执行新增/启用准入;投影未知或过期时 fail closed,只阻止新的成像变更,不中断已有链路。
---
## 4. 采纳的真实缺口
### 4.1 对账与运维中心(P0)
- 未收敛项展示期望态/实际态具体差异、重试次数、退避间隔和下次重试时间。
- 手动动作语义为“提高优先级并重新触发收敛”,不能绕过对账器直接修改实际资源。
- 展示孤儿资源、占比、10% 安全闸及人工处置提示;不提供绕过安全闸的一键删除。
- 展示媒体分片、边缘节点、控制隧道、数据面、补传队列和独立运维告警。
- 明确“控制隧道断开但已有视频数据面仍正常”的组合态。
### 4.2 设备能力来源(P0)
`capabilities` 必须来自探测/适配器结果并保存来源、探测时间与版本,不能由前端名称猜测。详情展示“已探测能力”和“当前生效能力”;重新探测显示新增/消失差异,能力降级时列出受影响的触发、Profile 或空间配置。
US-007 的采购白名单当前仍是版本化测试文档,无产品 UI;运行设备可只读显示“白名单型号/未验证型号”,不把白名单管理升级成 M2 P0 页面。
### 4.3 非视频适配器阶段态(P0)
M1~M5 可登记 radar 等模态以验证信息架构和隐私准入,但实际态必须是 `adapter_not_ready`(用户文案“适配器未就绪 · M6”),不得显示在线/离线或模拟 MQTT 遥测连接,也不计入视频配额。
### 4.4 设备生命周期(P0)
- 待激活作为可筛选状态,支持重新探测、校时、配额/策略复核、激活和逐项失败原因。
- 认证失败与离线分开;更新凭据只写不读、不回显旧值,更新后显示重试结果。
- 批量凭据仅允许安全导入逐设备凭据,不提供把同一密码覆盖到多台设备的一键操作。
- Profile 切换展示对当前观看、推理、区域校准与短暂中断的影响。
### 4.5 写入降级与权限(P0)
- 配额不可读:列表与已有视频可用,新增/启用被阻止。
- Area 策略投影不可读/过期:只阻止新的成像变更,已有链路保持现状并告警。
- 只读角色隐藏写操作;越权或资源不存在的深链使用统一安全拒绝态。
- 原型角色切换器只用于枚举状态,不代表 Sense 拥有账号或权限数据。
### 4.6 措辞与可观察结果(P0)
“暂停推理”统一为:
> 解除推理侧对该路的订阅;有其他观看者时上游继续拉流,无其他 reader 时上游按需停止。
执行结果同时显示推理订阅、reader 数和上游拉流状态,避免实现成推理插件内部直接 `return` 而持续浪费带宽。
---
## 5. 降级或后置的建议
| 原建议 | 最终处理 |
| --- | --- |
| Sense 新增“站点与区域”一级导航 | 不采纳;Bell/统一管理端持有管理页,Sense 顶栏只做站点上下文切换 |
| Sense 新增“审计”一级导航 | 不采纳;保留设备操作记录并深链 Bell 全局审计 |
| 直接批量覆盖同一设备密码 | 不采纳;仅做逐设备写入或安全文件导入 |
| M2 提供跨媒体分片迁移 | 后置 M4;本轮只展示分片详情与影响范围 |
| M2 提供节点升级/重启 | 后置独立高风险运维任务;本轮只展示状态 |
| 提前为推理绑定、pre-roll、设备型触发创建空页面 | 不采纳;能力模型与运维子导航已保留扩展,不创建无操作价值的占位页 |
---
## 6. 审计接口的独立架构任务
Bell 是全局审计真相源,但 T-004 不修改 `docs/api.md`。后续任务需要冻结 Sense → Bell 审计投递或等价投影契约,至少包含:
- Sense 在执行高风险写操作前,把操作意图与审计 outbox 在本地持久化;不能“先执行后尝试记录”。
- Bell 不可达时,只要本地 outbox 已持久化,业务操作可继续;恢复后幂等补投。
- 明确租户/站点范围、事件 ID、操作者、目标、请求结果、重试与保留策略。
此项属于 API/一致性设计,不用原型中的 toast 代替契约。
---
## 7. 本轮实施边界
T-004 重新生成 Sense 原型并补 Bell 管理端入口,同时更新正式 US/IX 和本复核稿。`docs/api.md`、生产代码、事件 v0.1 与真实认证实现不在本任务修改;人工确认前 T-004 继续保持 `DOING`。
+189
View File
@@ -0,0 +1,189 @@
# Sense 原型待办清单(第二轮复核后执行版)
> 来源:《09-Sense原型评审-IX草稿》《10-Sense原型-功能模块缺口》《11-Sense原型-第二轮缺口》
> 基准:Sense 提交 `e18a1ea`
> 适用任务:T-004
> 原则:按产品边界执行,不把 Sense 一级导航扩成 8 项
---
## 1. 状态说明
- `本轮`:T-004 第三轮原型必须完成。
- `后置`:已确认方向,但不在本轮画完整动作。
- `独立任务`:写路径或契约不同,不在 T-004 顺带修改。
- `完成`:当前原型已有,不重复实现。
---
## 2. 最终导航与系统归属
### Sense(五个一级入口)
```text
运行总览 · 实时监控 · 设备 · 接入任务 · 运维
```
“运维”内部使用二级 Tab:对账、分片、边缘节点、运维告警、系统状态。移动端底部导航保持五项。
### Bell / 统一管理端
- Tenant / Site / Area 与 `capture_policy` 管理。
- RBAC 与当前用户授权上下文。
- 全局审计查询和设备筛选深链。
- 站点视频配额真相源。
Sense 只消费上述上下文和带版本投影,不复制真相源。
---
## 3. 本轮执行项
### S-01 运维中心与对账队列 · P0 · 本轮
- 总览“待收敛项”可下钻。
- 展示期望态/实际态差异、重试次数、退避间隔、下次重试时间。
- “重新触发收敛”只提高任务优先级,不直接绕过对账器修改资源。
- 展示孤儿资源与 10% 安全闸;安全闸触发时禁止删除并说明人工恢复路径。
- 分片、边缘节点、运维告警和系统状态作为运维二级页。
验收:能打开某个未收敛项并查看差异;能看到安全闸和下一次重试时间。
### S-02 角色与安全拒绝态 · P0 · 本轮
- 顶栏显示当前用户和角色,提供“站点管理员/只读”原型切换。
- 只读模式隐藏新增、激活、停用、更新凭据、保存区域等写操作。
- 越权深链显示统一拒绝态,不泄露资源是否存在。
- 角色切换只是原型演示,授权真相源仍在 Bell。
### S-03 租户/站点上下文与 Area 归属 · P0 · 本轮
- Sense 顶栏显示当前租户并支持站点切换;切换后示例列表与计数同步更新。
- Bell 增加站点/Area 管理入口,展示配额来源与 `capture_policy`。
- 把已有视频设备的 Area 改为 `non_imaging_only` 时,要求先迁移设备、取消修改或进入显式处置流程,不静默停用。
### S-04 全局审计深链 · P1 · 本轮
- Sense 不新增一级审计页。
- 设备详情“操作记录”增加“在 Bell 查看全部审计”,携带 tenant/site/device 筛选上下文。
- Bell 审计页消费这些参数并显示来源为 Sense。
### S-05 capabilities 探测与差异 · P0 · 本轮
- 详情显示厂商、型号、固件、认证方式、Profile、校时与 ONVIF 事件订阅能力。
- 区分已探测能力与当前生效能力,记录上次探测时间。
- “重新探测”显示新增/消失能力及受影响配置,不自动覆盖。
- 只读显示采购白名单结果;白名单维护继续使用 M0 文档,不新增管理页。
### S-06 适配器未就绪 · P0 · 本轮
- radar 可登记并进入台账,但实际态为“适配器未就绪 · M6”。
- 不显示在线/离线、MQTT 实时遥测或测试连接成功。
- 不计入视频配额,仍参与 `non_imaging_only` 准入判断。
### S-07 待激活闭环 · P0 · 本轮
- 状态筛选包含待激活,列表至少有一条示例。
- 详情提供重新探测、校时、配额/区域策略复核和激活。
- 批量激活展示逐项成功/失败及可重试原因。
### S-08 认证失败与凭据更新 · P0 · 本轮
- 认证失败作为独立实际态。
- 凭据更新只写不读,不回显旧值;更新后自动重试并显示结果。
- 批量场景仅演示安全文件逐设备导入,不提供同一密码批量覆盖。
### S-09 写入降级态 · P0 · 本轮
- 状态演示新增“配额不可读”和“区域策略不可读”。
- 两态下实时监控和设备读取保持可用;新增/启用等相关写操作禁用并就地解释。
- 已有设备不被伪装为停用或已收敛。
### S-10 边缘节点与隧道 · P1 · 本轮
- 展示节点版本、在线时长、承载数、控制隧道、视频数据面和补传队列。
- 至少演示“隧道断开 / 视频数据面正常”和“恢复后补传”组合态。
- 节点升级/重启只显示为后置能力,不提供可执行按钮。
### S-11 Profile 切换 · P1 · 本轮
- 展示主/子 Profile 和当前用途。
- 切换前说明短暂中断、reader、推理和区域待校准影响。
- 原型不执行真实媒体操作。
### S-12 分片详情 · P1 · 本轮(迁移后置)
- 展示承载设备、实际码率、重连历史与单分片故障影响范围。
- 标注 32 为可配置建议容量,最终由压测确定。
- 跨分片迁移动作后置 M4,本轮不提供按钮。
### S-13 批量任务与逐项结果 · P0 · 本轮
- 任务列表、预校验结果、逐行失败原因、单行/失败项重试、错误清单导出。
- 明确部分成功语义:成功项已经生效,失败项可重试,不做整体回滚。
### S-14 运维告警 · P1 · 本轮
- 展示离线、认证失败、时间漂移、收敛失败、分片异常和隧道断开。
- 明确运维告警不会进入 Bell 业务预警队列或通知家属。
- 运维静默/通知配置后置,原型只显示入口归属。
### S-16 时间显示口径 · P1 · 本轮
- 默认显示服务器时间与时区。
- 相对时间同时提供完整时间戳;设备时间只在漂移语义中出现。
- 审计和对账示例使用同一口径。
### S-17 当前租户 · P1 · 本轮
- 顶栏显示当前租户;首期私有部署不提供租户切换器。
- 原型文案说明列表已经按认证上下文的 tenant/site 过滤。
### S-18 暂停推理措辞与结果 · P0 · 本轮
- 文案改为“解除推理侧订阅;无其他 reader 时上游按需停止”。
- 结果展示推理订阅、reader 数和上游拉流状态。
### S-19 分片容量标注 · P2 · 本轮
- `32` 标注为“当前配置/建议容量”,不是产品硬上限。
---
## 4. 已完成,不重复执行
| 编号 | 状态 | 证据 |
| --- | --- | --- |
| S-15 设备分页 | 完成 | 5 条/页、上一页/下一页、页数和总数已实现 |
| S-20 推理收敛视觉 | 完成 | 已改为绿色“推理配置收敛 16/16” |
| 第一轮 IX-014~IX-018 | 完成 | 模态/能力、三种停用、隐私准入、几何方向、草稿和深链均已落地 |
---
## 5. 后置与独立任务
| 编号 | 处理 |
| --- | --- |
| S-21 Sense → Bell 审计契约 | 独立任务修改 `docs/api.md`;采用本地持久化 transactional outbox + 幂等补投 |
| S-22 推理绑定运维 | M3 在“运维”内新增二级能力,不预建空页面 |
| S-23 pre-roll 运维 | M3 需求稳定后新增,不预建空页面 |
| S-24 设备型触发配置 | M6 随非视频适配器实现,不预建空页面 |
| 跨媒体分片迁移 | M4 容量/分片任务 |
| 节点升级/重启 | 独立高风险运维任务,需要权限、回滚和维护窗口 |
---
## 6. 总验收
- [x] Sense 一级导航保持 5 项,移动端不出现第 6 个底部入口。
- [x] 运维中心可查看未收敛差异、退避、孤儿与安全闸。
- [x] 只读角色下所有写操作消失,越权深链显示安全拒绝态。
- [x] radar 显示“适配器未就绪 · M6”,不显示在线或模拟遥测成功。
- [x] 能力重新探测显示差异,能力降级不自动覆盖依赖配置。
- [x] 待激活、认证失败、凭据更新和逐项激活结果可演示。
- [x] 配额/策略不可读时只阻止相关写操作,已有流与读取可用。
- [x] “暂停推理”展示订阅、reader 和上游拉流结果。
- [x] Bell 可管理 Site/Area 策略并显式处理已有成像设备。
- [x] Sense 设备审计深链到 Bell,并保留 tenant/site/device 筛选。
- [x] 1440×900、375×812、812×375 无页面级横向溢出;主要移动控件不小于 44px。
- [x] 运行时无异常,`git diff --check` 与 `./init.ps1` 通过。
+19 -7
View File
@@ -14,6 +14,8 @@ write_paths:
- docs/design/sense/index.html
- docs/design/bell/index.html
- docs/raw/09-Sense原型评审-IX草稿.md
- docs/raw/11-Sense原型-第二轮缺口.md
- docs/raw/12-Sense原型-待办清单.md
- docs/08-interaction-checklist.md
- docs/07-user-stories.md
- docs/02-requirements.md
@@ -31,31 +33,32 @@ Sense 与 Bell 尚无可供产品确认的页面结构。生产 UI 开工前,
## 关联需求与交互(如适用)
- 用户故事:Sense 关联 US-001、US-002、US-008,并为 US-005 提供视频设备空间配置;Bell 关联 US-003~US-006,并拥有 US-005 的业务规则工作流。
- 交互清单:Sense 关联 IX-001~IX-004、IX-014~IX-018,并提供 IX-010 所需的区域/警戒线几何;Bell 关联 IX-005~IX-013,负责 IX-010~IX-011 的规则继承、试运行和发布。
- 用户故事:Sense 关联 US-001、US-002、US-008、US-009,并为 US-005 提供视频设备空间配置;Bell 关联 US-003~US-006、US-010,并拥有 US-005 的业务规则工作流及 Tenant/Site/Area/RBAC/配额/全局审计真相。
- 交互清单:Sense 关联 IX-001~IX-004、IX-014~IX-020,并提供 IX-010 所需的区域/警戒线几何;Bell 关联 IX-005~IX-013、IX-016、IX-020,负责规则继承/试运行/发布、站点与 Area 策略、全局审计。
- 相关页面 / 路由:原型阶段为 `docs/design/sense/index.html` 与 `docs/design/bell/index.html`,不定义生产路由。
## 方案
1. 使用统一的 YoVision 深色安防控制台设计系统,Sense 与 Bell 保持品牌、状态色、控件和导航规律一致。
2. Sense 制作为单页可交互工作台:总览、监控墙、设备、接入任务和系统状态;不设置一级“区域规则”。统一设备台账使用 `modality + capabilities` 决定详情页和操作,M1~M5 完整展示视频设备,非视频设备以雷达原型验证信息架构,实际适配器留到 M6。
2. Sense 制作为单页可交互工作台:总览、监控墙、设备、接入任务和运维中心,移动端也固定为这 5 个入口;不设置一级“区域规则”或全局管理页。运维中心聚合对账、分片、边缘/隧道、运维告警和系统状态。统一设备台账使用 `modality + capabilities` 决定详情页和操作,M1~M5 完整展示视频设备,非视频设备在 M6 前明确显示 `adapter_not_ready`,不得虚构遥测。
3. 视频设备在“画面与检测区域”页签完成多边形、方向警戒线、显式完成、方向反转、逐点编辑、会话草稿恢复、乐观并发冲突和待校准反馈;Sense 只保存空间几何并只读显示关联规则,不提供业务规则绑定、时段、持续时间、试运行或正式发布。
4. Bell 制作为单页可交互值班台:除预警队列、事件证据、ack/处置、升级时间线、误报反馈和升级链外,规则编辑必须选择摄像头与检测区域版本,并负责继承、时段、持续时间、试运行、发布和回滚。
4. Bell 制作为单页可交互值班台和统一管理端:除预警队列、事件证据、ack/处置、升级时间线、误报反馈和升级链外,规则编辑必须选择摄像头与检测区域版本,并负责继承、时段、持续时间、试运行、发布和回滚;Bell 同时管理 Site/Area/配额/`capture_policy`,策略与已有成像设备冲突时必须显式迁移或取消。
5. Sense 摄像头详情与 Bell 规则草稿通过可演示的相对深链互相进入,携带摄像头/区域上下文;原型可以跨文件,但生产产品不得要求用户重新查找同一摄像头或丢失未完成草稿。
6. 两个原型均内联 CSS/JS,使用假数据,不加载真实视频、客户信息、生产地址或外部依赖;顶部固定标注原型用途。
7. 添加设备时先选择类型,依据能力渐进展示字段;区域的 `capture_policy` 负责隐私准入,视频设备在“仅非成像设备”区域中的新增或启用必须被阻止,策略不可用时对新写入失败关闭。
8. 完成桌面、窄屏、横屏、键盘焦点、加载/空态/错误切换和 `prefers-reduced-motion` 检查,提交人工评审;产品确认前不把任务标为 `DONE`。
8. 全局头部展示租户、站点、账号和角色;只读角色隐藏写操作,深链越权不泄露资源是否存在。Sense 设备日志带租户/站点/设备条件跳到 Bell 全局审计并可返回。
9. 完成桌面、窄屏、横屏、键盘焦点、加载/空态/错误/依赖降级切换和 `prefers-reduced-motion` 检查,提交人工评审;产品确认前不把任务标为 `DONE`。
## 不可变约束
- 阈值 / 数值边界:默认 16 路视频、单站点视频上限 128 路;非视频设备不占视频路数;单媒体分片建议上限 32 路、最多 4 个分片;静默最长 4 小时;原型不得暗示 128 路同时播放主码流。
- 阈值 / 数值边界:默认 16 路视频、单站点视频上限 128 路;非视频设备不占视频路数;单媒体分片初始配置建议 `max_streams=32`、当前示例承载 16、最多 4 个分片,最终以压测为准;静默最长 4 小时;原型不得暗示 128 路同时播放主码流。
- 判定式 / 状态转换:事件事实不可被处置改写;Alert 的首次投递、送达、看到、ack 和升级必须分开展示;检测区域版本与告警规则版本是不同对象;规则正式发布与试运行必须可区分,保存区域不得自动发布规则。
- 安全边界:不得展示摄像头密码、完整 RTSP/ONVIF 地址、token、真实客户/未成年人数据或人脸入口;破坏性动作须确认。
- 既有契约:原型不修改事件 v0.1、API、路由和生产技术栈,不作为生产实现代码。
## 验收要点
- 任务相关验证:两个 HTML 可本地打开、无外部依赖;关键按钮和状态切换可用;Sense 一级导航无“区域规则”,设备列表可按类型筛选/分页,视频详情可编辑检测区域,非视频详情不伪装成媒体流;Bell 可选择摄像头/区域并演示试运行与正式发布;双向深链保留 `camera + zone + return_to`;运行 HTML 结构检查、`git diff --check` 与 `./init.ps1`。
- 任务相关验证:两个 HTML 可本地打开、无外部依赖;关键按钮和状态切换可用;Sense 一级导航固定 5 项且无“区域规则”,设备列表可按类型/生命周期筛选与分页,视频详情可编辑检测区域,非视频适配器未交付时不伪装在线;运维中心覆盖对账/分片/边缘/运维告警/系统状态;Bell 可选择摄像头/区域并演示试运行与正式发布,且可管理 Site/Area 策略冲突;Sense↔Bell 规则深链保留 `camera + zone + return_to`,审计深链保留 `tenant + site + device + return_to`;运行 HTML 结构检查、`git diff --check` 与 `./init.ps1`。
- 完整门禁:本任务不修改 schema/API/路由,因此不触发契约与导航实现门禁;若范围变化则先更新任务。
- 人工 / 设备验收:必需。项目负责人分别打开 Sense 与 Bell 原型,确认页面结构、控件集合和主要工作流;未确认前不得标记 `DONE` 或合并为最终设计。
- 构建产物:`docs/design/sense/index.html`、`docs/design/bell/index.html`;双击浏览器打开,零构建依赖。
@@ -75,6 +78,15 @@ Sense 与 Bell 尚无可供产品确认的页面结构。生产 UI 开工前,
## 执行记录
### 2026-08-04 第二轮缺口复核与最终实现
- 复核 `raw/11`、`raw/12` 后保留对账、能力探测、设备生命周期、RBAC、写入降级、边缘隧道与运维告警等真实缺口;纠正“原型无分页”“分片 8 / 32”“雷达已有真实能力”“T-004 只允许 3 个写路径”等过期事实,并把 M4/M6 能力标为后续阶段而非本轮失败。
- 产品 IA 冻结为 Sense 5 个一级入口:运行总览、实时监控、设备、接入任务、运维中心。Tenant/Site/Area/RBAC/配额/全局审计归 Bell;Sense 仅呈现当前管理上下文并消费版本化配额/Area 策略投影。该决定已同步 US-009/US-010、IX-019/IX-020、正式需求、架构及三份原始分析文档。
- Sense 按显著改版规则整页重新生成:补齐待激活、认证失败、写入型凭据更新、能力探测来源/时间/生效差异、`adapter_not_ready`、逐项任务结果、暂停推理 reader/upstream 结果、对账差异/退避/孤儿安全闸、16/32 分片注释、边缘隧道/补传、独立运维告警、配额/策略不可用时只禁用相关写入,以及只读/越权状态。
- Bell 增加 Site/Area 管理、配额/策略投影状态与策略冲突显式迁移/取消;审计页能消费 Sense 的 `tenant + site + device + return_to` 上下文并返回设备详情。跨系统审计实现约定为 Sense 本地事务 outbox + 幂等 relay,但具体 API/签名/重放/留存仍需独立契约任务,本任务不修改 `docs/api/`。
- Node.js 内联脚本语法、唯一 ID、表单标签、按钮可访问名、Tab 契约和外部资源静态检查通过。Edge Headless/CDP 在 1440×900、375×812、812×375 验证上述关键流程:两页页面级无横向溢出,移动端可见主要控件不小于 44×44px,控制台与运行时均 0 exception;运维中心与 Bell Site/Area 页面截图已人工目检,临时 QA 文件未写入仓库。
- `./init.ps1` 通过:上下文/治理检查、16 个治理单测全部成功;`git diff --check` 通过。自动化验收已完成,任务继续保持 `DOING`,等待项目负责人打开原型进行必需人工确认;确认前不合并 PR。
### 2026-08-04 Claude 评审续修立项
- 项目负责人提供 `docs/raw/09-Sense原型评审-IX草稿.md` 并确认按复核后的最终建议继续修改。复核结论为:设备模态、停用收敛、隐私准入、几何编辑、草稿恢复与无障碍问题方向成立,但分片容量来源、页内草稿丢失和双向深链现状等证据需纠正。