# 边缘人脸识别区域安全设计方案 ## 1. 当前需求结论 综合平台维护企业下面的区域树,并向第三方提供区域查询接口。 当前功能范围: - 一个企业可以维护多棵或多层区域树。 - 区域支持新增同级区域、新增下级区域、修改和删除。 - 区域包含区域名称、负责人和电子围栏坐标。 - 当前原型要求电子围栏由 4 个有顺序的坐标点组成。 - 摄像头主数据来自第三方接口,综合平台不维护摄像头主档。 - 综合平台保存区域与摄像头的绑定关系。 - 每个已绑定摄像头可以设置负责人。 - 选择区域后,右侧列表展示该区域直接绑定的摄像头。 - 人员、公司、部门、人脸和人员区域准入关系不属于本功能维护范围。 - 综合平台不主动推送,由第三方主动调用区域开放接口。 ## 2. 领域边界 ```text 企业(外部公共数据) └── 区域(综合平台维护,树结构) ├── 下级区域 └── 摄像头绑定(摄像头主数据来自第三方) └── 摄像头负责人 ``` ### 2.1 企业 - 企业作为区域树的根节点和数据隔离维度。 - 企业主数据不在区域表中重复维护,只保存 `corp_id`。 - 页面树由“企业虚拟根节点 + 本地区域节点”组合生成。 - 企业名称通过公共信息翻译或企业公共接口获取,不作为区域的权威数据保存。 ### 2.2 区域 区域是本功能的聚合根,负责: - 企业归属。 - 父子层级。 - 区域名称。 - 区域负责人。 - 电子围栏坐标。 - 区域状态和逻辑删除。 ### 2.3 摄像头 - 摄像头名称、所属单位、类型、等级、主管部门、在线状态等来自第三方接口。 - 本系统不成为摄像头主数据的权威维护方,但会冗余列表展示和查询所需的摄像头缓存字段。 - 本系统保存第三方摄像头唯一标识、来源系统、区域绑定、摄像头负责人,以及名称、所属单位、类型、等级、主管部门、在线状态等快照。 - 绑定摄像头时写入快照;后续可按查询或定时策略刷新,第三方数据始终是权威来源。 - 第三方临时不可用时可以展示本地最近快照,并显示最近同步时间;不得把缓存快照作为修改第三方摄像头的依据。 ## 3. 关键设计决策 ### 3.1 公司节点不进入区域表 区域表只存真实区域。顶级区域的 `parent_id` 为空或为 `0`,通过 `corp_id` 归属企业。这样不会把外部企业主数据和本地区域数据混为一种实体。 页面树建议返回统一节点结构: ```text nodeType = CORP 企业虚拟节点 nodeType = REGION 本地区域节点 ``` ### 3.2 区域树规则 - 同一企业下,同一父节点中的区域名称建议唯一。 - 子区域必须与父区域属于同一企业、租户和单位。 - 禁止把节点移动到自身或自己的子孙节点下面。 - 删除包含子区域的节点时,默认禁止删除,先迁移或删除子节点。 - 删除仍绑定摄像头的区域时,默认禁止删除,先解除或迁移绑定。 - 查询区域树时使用批量查询后在内存组树,禁止递归逐节点查询数据库。 是否允许跨层级移动区域、区域最大层级和区域排序规则仍需产品确认。 ### 3.3 四点电子围栏 建议数据库保存一个有顺序的坐标数组,而不是设计 `point1_x` 到 `point4_y` 八个固定字段: ```json [ {"seq": 1, "longitude": 113.123456, "latitude": 23.123456}, {"seq": 2, "longitude": 113.223456, "latitude": 23.123456}, {"seq": 3, "longitude": 113.223456, "latitude": 23.223456}, {"seq": 4, "longitude": 113.123456, "latitude": 23.223456} ] ``` 一期保存时强制校验: - 必须恰好 4 个点。 - 点序号不可重复,按顺时针或逆时针排序。 - 经度范围 `[-180, 180]`,纬度范围 `[-90, 90]`。 - 相邻点和首尾点不能重复。 - 四条边不能自相交。 - 围栏面积不能为 0。 - 必须明确坐标系,例如 WGS84、GCJ-02 或 BD-09;坐标系作为字段保存,禁止默认猜测。 使用 JSON 可以满足当前 4 点要求,同时允许未来扩展为多边形。如果后续需要“判断坐标是否在区域内”等空间检索,再增加 MySQL `POLYGON` 空间字段和空间索引。 ### 3.4 负责人 - 区域负责人保存用户 ID,不重复保存用户主档。 - 摄像头负责人保存在区域摄像头绑定记录中。 - 页面通过 `TranslateField` 批量翻译负责人姓名,禁止循环逐人查询。 - 原型中负责人是必填单选;是否允许多个负责人,需要产品确认。 - 人员被停用或删除后,负责人绑定如何处理,需要明确校验或提示规则。 ### 3.5 摄像头绑定 - 选择摄像头时调用第三方接口获取可选摄像头列表。 - 保存时只提交选中的第三方摄像头 ID,本系统必须再次校验摄像头存在性和调用方数据范围。 - 建议一期约束“一个来源系统中的一个摄像头只能绑定一个区域”。如果业务允许一个摄像头属于多个区域,再取消唯一约束。 - 重复选择已经绑定到当前区域的摄像头应幂等成功。 - 已绑定其他区域时,应返回明确提示,不静默迁移。 - 解绑只删除本地关系,不调用第三方删除摄像头。 - 在线状态等动态字段不写入本地绑定表,以第三方实时结果为准。 ## 4. 表结构草案 原型可以确定两张核心业务表。所有表都包含根目录 `AGENTS.md` 规定的公共字段。可执行评审的 MySQL 8.0 DDL 草案见 `web-infrastructure/src/main/resources/RegionManagementDDL.sql`。 ### 4.1 区域表 `security_region` | 字段 | 类型建议 | 说明 | | --- | --- | --- | | `id` | `bigint` | 区域主键 | | `region_code` | `varchar(64)` | 区域编码,建议提供稳定的对外标识 | | `region_name` | `varchar(100)` | 区域名称 | | `corp_id` | `bigint` | 所属企业 ID | | `parent_id` | `bigint` | 父区域 ID,顶级区域为空或 0 | | `tree_path` | `varchar(1000)` | 祖先路径,用于子树查询和防循环 | | `tree_level` | `int` | 区域层级,企业下顶级区域从 1 开始 | | `sort_no` | `int` | 同级排序 | | `leader_user_id` | `bigint` | 区域负责人用户 ID | | `coordinate_system` | `varchar(32)` | 坐标系,例如 GCJ02 | | `fence_points` | `json` | 有序电子围栏坐标点,一期固定 4 点 | | `region_status` | `varchar(32)` | `ENABLED/DISABLED` | 建议索引: - 唯一索引:`region_code`,是否叠加租户维度按编码生成规则确定。 - 同级名称唯一:`tenant_id + corp_id + parent_id + region_name + delete_enum`。由于可空字段会影响 MySQL 唯一约束,正式 DDL 需采用非空默认值或规范化唯一键。 - 树查询索引:`tenant_id + corp_id + parent_id + delete_enum + sort_no`。 - 负责人查询索引:`leader_user_id`,仅在存在反向查询需求时增加。 ### 4.2 区域摄像头绑定表 `security_region_camera` | 字段 | 类型建议 | 说明 | | --- | --- | --- | | `id` | `bigint` | 主键 | | `region_id` | `bigint` | 本地区域 ID | | `camera_source` | `varchar(64)` | 摄像头来源系统编码 | | `camera_id` | `varchar(128)` | 第三方摄像头唯一标识 | | `camera_name` | `varchar(255)` | 摄像头名称缓存 | | `camera_unit_id/name` | `varchar` | 所属单位标识及名称缓存 | | `camera_type_code/name` | `varchar` | 类型编码及名称缓存 | | `camera_level_code/name` | `varchar` | 等级编码及名称缓存 | | `camera_dept_id/name` | `varchar` | 主管部门标识及名称缓存 | | `online_status` | `varchar(32)` | 在线状态缓存 | | `camera_snapshot` | `json` | 第三方其他扩展字段快照 | | `camera_synced_at` | `datetime` | 缓存最近同步时间 | | `leader_user_id` | `bigint` | 摄像头负责人用户 ID | | `bind_status` | `varchar(32)` | `BOUND/UNBOUND`,若逻辑删除足够可不单设 | | `bound_time` | `datetime` | 绑定时间 | 建议索引: - 唯一索引:`tenant_id + camera_source + camera_id`,落实一个摄像头只绑定一个区域。 - 区域列表索引:`tenant_id + region_id + delete_enum`。 - 摄像头负责人索引:`leader_user_id`,仅在需要按负责人查询时增加。 当前只缓存已经绑定的摄像头,因此展示字段直接放在绑定表中即可。如果后续要求缓存全部第三方摄像头、未绑定摄像头也要离线搜索,或一个摄像头允许绑定多个区域,则应拆分为“摄像头缓存表 + 区域摄像头关系表”。 ## 5. 页面功能拆解 ### 5.1 区域树 - 按当前用户数据权限加载企业。 - 批量查询企业下全部区域并组装树。 - 支持新增顶级区域、同级区域和下级区域。 - 支持修改和删除区域。 - 选中区域后显示企业/区域面包屑。 ### 5.2 新增或修改区域 入参建议: - `parentId`:新增顶级区域时为空。 - `corpId`:必填。 - `regionName`:必填。 - `leaderUserId`:按当前原型必填。 - `coordinateSystem`:必填。 - `fencePoints`:必填且恰好 4 点。 - `sortNo`:可选。 ### 5.3 选择摄像头 - 显示当前区域路径。 - 调用第三方接口分页查询摄像头。 - 支持视频名称、等级、在线状态等第三方支持的条件。 - 已绑定当前区域的摄像头默认勾选。 - 已绑定其他区域的摄像头应禁选或明确标识。 - 确认后计算新增绑定和解除绑定差异,在一个本地事务中保存。 ### 5.4 区域摄像头列表 列表字段来自两部分: | 字段 | 来源 | | --- | --- | | 视频名称、所属单位、类型、等级、主管部门、在线状态 | 本地缓存字段展示,第三方接口负责刷新 | | 所属区域、摄像头负责人、绑定时间 | 本地绑定字段 | 列表操作:设置摄像头负责人、查看第三方摄像头详情、解除绑定。原型中的“删除”建议在业务文案中改为“解绑”,避免误解为删除第三方摄像头。 ## 6. 接口建议 ### 6.1 内部区域管理接口 | 能力 | 建议接口语义 | | --- | --- | | 区域树 | 按数据权限返回企业虚拟根和区域树 | | 新增区域 | 新增顶级或下级区域 | | 修改区域 | 修改名称、负责人、围栏和排序 | | 删除区域 | 校验子区域和摄像头绑定后逻辑删除 | | 区域摄像头分页 | 查询选中区域直接绑定的摄像头 | | 可选摄像头分页 | 代理/调用第三方摄像头查询能力 | | 保存摄像头绑定 | 批量计算并保存绑定差异 | | 设置摄像头负责人 | 更新绑定记录负责人 | | 解除摄像头绑定 | 逻辑删除本地绑定关系 | ### 6.2 第三方区域开放接口 综合平台作为区域数据提供方,建议至少提供: - 区域分页查询。 - 区域详情查询。 - 区域树查询。 开放接口是否返回区域绑定摄像头需要第三方确认。建议区域和摄像头绑定分别返回,避免区域树报文过大: - `RegionCo`:区域自身信息、层级、负责人、围栏。 - `RegionCameraCo`:区域 ID、第三方摄像头 ID、摄像头负责人。 第三方开放接口使用独立鉴权和数据范围,不直接复用页面登录权限。 ## 7. DDD 落位 | 模块 | 内容 | | --- | --- | | `web-client` | Region、RegionCamera 的 Cmd、Qry、Co 和服务接口 | | `web-adapter` | 区域管理 Controller、第三方区域开放接口 | | `web-app` | 区域增删改查、组树、摄像头绑定和负责人设置执行器 | | `web-domain` | `RegionE`、`RegionCameraBindingE`、树及围栏规则、Gateway | | `web-infrastructure` | DO、Mapper、Repository、GatewayImpl、第三方摄像头 Facade 适配 | | `start` | RPC Client 和配置装配;当前不需要推送定时任务 | 第三方摄像头访问能力在 Domain 定义 Gateway,在 Infrastructure 通过 Dubbo 或 HTTP 实现。外部摄像头 DTO 不进入 Domain 和 Client。 ## 8. 待确认问题 ### 区域 - 是否需要区域编码,对外唯一标识采用 ID 还是编码? - 顶级区域 `parent_id` 使用 `NULL` 还是 `0`? - 区域最大层级是多少,是否允许移动节点? - 区域负责人是否只能有一个? - 坐标使用哪种坐标系?四点是否永远固定,还是后续允许任意多边形? - 是否要求判断摄像头坐标或告警坐标是否处于区域内? - 区域是否需要启用/停用状态? ### 摄像头 - 摄像头第三方接口的系统、协议和 Common Facade 是什么? - 摄像头唯一主键是什么,是否跨企业唯一? - 是否支持分页及原型中的名称、等级、在线状态筛选? - 一个摄像头是否只能绑定一个区域? - 查询某区域时只展示直接绑定摄像头,还是包含所有子区域摄像头? - 解绑后负责人是否随绑定记录一起失效? - 第三方不可用时页面是报错,还是允许展示本地快照? ### 开放接口 - 第三方只查询区域,还是同时查询区域摄像头绑定? - 需要全量分页、区域树还是更新时间增量查询? - 是否需要返回逻辑删除区域和已解除绑定关系? - 鉴权、签名、IP 白名单及租户范围如何确定? ## 9. 当前不开发内容 - 人员、企业、部门、岗位和人脸主数据维护。 - 人员与区域准入关系。 - 摄像头主数据维护和删除。 - 主动推送、推送任务、回执和失败重试。 上一版人员推送 DDL 已删除。当前 `RegionManagementDDL.sql` 是根据原型形成的区域表草案;坐标系、摄像头唯一性和区域删除规则确认后,再作为正式迁移脚本执行。 ## 10. 第三方报警接收与处置 ### 10.1 接入方式 报警由第三方产生,建议综合平台提供报警接收回调接口,由第三方主动调用: ```text 第三方 -> 鉴权/验签 -> 幂等校验 -> 报警落库 -> 返回受理成功 └-> 用户后续处置 ``` 以 `sourceSystem + alarmNo` 作为幂等键。第三方重复推送同一报警编号时返回已受理,不重复新增。接收接口只负责报警可靠落库,不同步执行隐患长流程。 第三方至少传摄像头和区域信息,本系统校验并固化企业、区域及摄像头历史快照。区域或摄像头无法识别时是否拒绝接收,待双方接口协议确定。 报警业务报文只要求当前确认字段:报警编号、报警时间、报警地址、区域、摄像头和一张报警截图。来源系统、接收时间及处置状态属于综合平台接入和管理字段,不要求第三方提供。 ### 10.2 图片传输 当前暂定第三方在 JSON 报警报文中直接传单张截图 URL,综合平台在 `alarm_image_url` 中保存并用于详情展示,不接收文件流,也不保存 BLOB 或 Base64。 正式联调前需要确认 URL 是否长期有效、是否需要鉴权、是否允许综合平台服务端访问。如果 URL 是短期签名地址,应改为稳定文件 ID,或由综合平台接收后转存统一文件服务。 ### 10.3 处置状态机 ```text PENDING(未处置) ├─ 误报=是 -> DISPOSED └─ 误报=否 ├─ 隐患=否 -> 填写结果 -> DISPOSED └─ 隐患=是 -> 创建隐患 -> DISPOSED ``` - 误报直接闭环,不进入隐患流程。 - 非误报必须选择是否隐患。 - 非隐患必须填写处置结果,提交后闭环。 - 隐患调用 `ZcloudHiddenFacade.aiHiddenAndSave(HiddenAddCmd)`,以报警编号作为 `foreignKey`。 - 隐患流程和进度以隐患服务为准,本地不再维护报警闭环状态。 - 当前报警不经过派单、转办或审批,每条报警只提交一次处置结论;处置人、处置时间及公共更新审计字段用于留痕。 ### 10.4 数据表 区域、摄像头绑定、报警和处置统一维护在 `web-infrastructure/src/main/resources/RegionManagementDDL.sql`,报警部分包含: | 表 | 用途 | | --- | --- | | `edgeguard_alarm` | 报警事实、区域/摄像头快照、当前状态、统计维度 | | `edgeguard_alarm_disposal` | 是否误报、是否隐患、直接处置结果及关联隐患 ID | Common 已有 `ZcloudHiddenFacade` 的 `aiHiddenAndSave`、`listByForeignKey` 和 `queryHiddenCheckStatus` 等能力。本系统不复制隐患主表,也不缓存隐患状态、级别和进度。创建成功后只在报警处置表保存 `hidden_id`;详情页根据该 ID 调用隐患服务查询实时详情。 为处理隐患接口临时失败,处置表仅额外保存 `hidden_create_status` 和 `hidden_create_error`。它们属于调用可靠性字段,不是隐患业务数据。以报警编号作为 `foreignKey` 保证隐患创建幂等。 ### 10.5 统计 一期直接聚合报警主表,不建统计结果表: - 股份端按 `corp_id` 统计各分公司报警总数、未处置和已处置。 - 分公司端按 `region_id` 统计各区域报警总数、未处置和已处置。 - 可统计误报数、隐患数和非隐患处置数。 报警保存发生时的企业、区域和摄像头快照,后续改名或移动不影响历史台账。数据量较大后再增加按日汇总表。 ### 10.6 待确认 - 第三方报警接口的 JSON 报文、鉴权、签名及重试规则。 - 第三方报警 ID 是否在来源系统内永久唯一。 - 报警截图 URL 的有效期、访问鉴权和网络可达性。 - 处置结果字典。 - 误报是否仍需填写处置原因。 - 隐患级别、确认人、整改人和期限由谁填写。 - 是否允许修改处置结论、重新打开报警及是否需要审批。 ### 10.7 报警查看权限升级 当前需求只控制报警列表的可见范围,不需要增加业务表: - 摄像头负责人从报警入库开始即可查看该摄像头产生的报警。 - 报警保持 `PENDING` 且超过配置的升级时长后,所属区域负责人也可以查看。 - 当前升级时长为 10 分钟,必须配置化,禁止硬编码在查询 SQL 中。 - 超时基准使用综合平台 `received_time`,不使用第三方 `alarm_time`,避免延迟推送导致报警刚入库就被升级。 - 报警处置后进入台账的查看范围按页面数据权限确定,不再依赖 10 分钟规则。 查询逻辑: ```text 当前用户是摄像头负责人 OR 当前用户是区域负责人 AND disposal_status = PENDING AND received_time <= 当前时间 - 配置分钟数 ``` 摄像头负责人从 `edgeguard_region_camera.leader_user_id` 获取,区域负责人从 `edgeguard_region.leader_user_id` 获取。负责人发生变更后按当前负责人权限查询;如果未来要求报警产生时锁定负责人,再在报警表增加负责人快照字段。 本规则只表示“能够在列表看到”,不表示系统主动通知。若后续要求短信、消息中心或待办提醒,并需要记录发送结果和防止重复通知,再设计通知记录或升级标记。