AI 视频分析任务的范围拆解与现实收敛:原型要的九项 API 都不支持

一个 AI 视频分析的「基本功能开发」任务,原型画得很完整:区域入侵、徘徊侦测、灵敏度调节、多边形绘制……真去对接上游 AI 网关的 API,发现原型要的九项能力一个都不支持。这篇记录任务范围怎么从「基本闭环」一步步收敛到「能跑的最小集」,以及三个联动(弹窗、信息屏、语音播报)各自的现实卡点。

任务范围

按原型(告警规则设置页)先跑通基本闭环:

  1. 规则只做入侵检测
  2. 触发联动只做三个:平台弹窗、信息屏发布、语音播报,其余模块后续再处理;
  3. 后端子任务拆解:
    • 3.1 确认入侵检测 API 支持 / 不支持的参数;
    • 3.2 平台弹窗(多路摄像头都要推送,前端暂定 socket);
    • 3.3 信息屏发布(旧版已有,需调试是否可用);
    • 3.4 语音播报 / 广播(旧版已有,需调试是否可用);
    • 3.5 其余联动(录像 / 消息通知 / 喊话对讲 / 联动报警),待 3.1~3.4 完成后再做。

平台架构:Spring Cloud Alibaba 微服务(security = 综合安防、warning = 告警、smart = 智慧、through = 便捷通行、integrate-iot = 物联网接入)。AI 算法上游是 Vplat 平台(AI 网关 / 盒子),信息发布屏上游是大华信发平台

监控任务既有闭环(背景知识)

后续开发都挂在下面这条存量链路上:

1
2
3
4
5
6
7
8
9
10
flowchart TD
A["建电子围栏"] --> B["建监控任务<br/>下发 AI 策略到 Vplat 网关"]
B --> C["AI 盒子实时分析产生告警"]
C --> D["schedule 每分钟拉告警<br/>integrate-iot /dataPull/pullAiAlarmDataByTiming"]
D --> E["推 security<br/>/securityEventSink/security-ai-alarm-input"]
E --> F["MonitoringRuleMatcher<br/>摄像头 + 星期 + 时段匹配<br/>规则缓存 60 秒"]
F --> G["生成监控结果<br/>resultId = MD5(任务id + 当天日期)<br/>一个任务一天一条, anomaly_num 累加"]
G --> H["异步 3 秒后分流"]
H --> I["越线 → warning inputIteration<br/>生成人员越界告警 + 自动建工单"]
H --> J["人脸 → through 通行布控<br/>黑名单才建告警 + 工单"]

前端监控任务结果列表按天看异常数、详情弹窗看时间 + 截图 + 感知设备,工单在 BPM 侧处置即闭环。这条链路是后面三个联动的地基。

3.1 入侵检测 API:原型要的九项都不支持

上游 Vplat AI 网关策略接口封装在 integrate-iot 的 AiClientImpl.java

操作 接口
新增策略 POST /api/vplat/VplatDeviceStrategyService
更新策略 POST .../saveUpdate
删除策略 POST .../saveMultiDelete
查询策略 GET .../pageQueryList
告警拉取 GET .../VplatDeviceStrategyResultService/pageQueryAlarmList(平台轮询,设备不主动推送)

支持的参数devId(一策略一摄像头)、enable 启用开关、name 策略名、strategyType(1 = 越线、3 = 人脸、13 = 安全帽、15 = 工作服;入侵检测 = 1 越线)、strategyInfo.rectInfo 矩形检测区域(归一化 0~1,现只下全屏)、strategyInfo.linePoints 越界线画点(归一化,最多 5 个点)、action 抓拍 / 录像开关、timeTemplate 布防时间(星期几 × 整点时段)、固定联动参数 linkedTime=10 / linkedVideo=0 / snapshot=1

原型要求但 API 不支持(请求模型无字段,共 9 项):

九项全不支持
图 1:原型要求的能力逐项对照 API,全部打叉——需要产品决策砍需求还是找设备侧其他能力。

# 原型要求 API 现状
1 区域入侵 / 进入区域 / 离开区域 / 徘徊侦测 入侵类只有越线一种
2 目标类型筛选(人体 / 车辆 / 非机动车) 无字段
3 多边形绘制 只有矩形 + 越线
4 灵敏度 1-10 级 无字段
5 停留时间阈值 无字段
6 去重间隔 无字段
7 目标尺寸过滤 无字段
8 置信度阈值 告警回报里有 confidence,但创建策略无法设阈值
9 布防计划的自定义日期范围 / 跨次日 只有「每天」或「按周几勾选」两种
10 分钟级时段 timeTemplate.valueint[] 小时数组,下发时 .split(":")[0] 直接丢分钟

第 10 项尤其值得注意:它不是「待实测」,而是代码层面就不支持MonitoringServiceImpl.java:148 下发时把时段字符串按 : 切分只取小时位,分钟位被直接丢弃。即便原型只配了整点时段,这个限制也得记进文档,避免后续误以为能配 09:30-10:00

结论建议:入侵检测闭环用 strategyType=1(越线)+ linePoints/rectInfo + timeTemplate 即可跑通;不支持的 9 项需产品确认砍需求还是找设备侧其他能力。

3.2 平台弹窗:发送链路在,订阅下架

详细的前后端实现另见这篇,这里只记结论。

  • 后端发送链路在:warning 暴露 STOMP 端点,告警触发且处理器开关打开 → 推 /topic/1024/alarmPopup
  • 前端订阅下架:现役 src/layout 无任何 SockJS/STOMP,订阅代码只在废弃副本里且 initWebSocket() 被注释。
  • VO 字段精简:旧 AlarmingPopupVO 没有 deviceCodealarmImg,前端拿不到设备编号和告警截图。

方案分两档:第一档文字弹窗闭环(前端重写订阅 + 队列);第二档带视频 + 权限(VO 补字段 + 园区隔离)。实际两轮都做了。

3.3 信息屏发布:卡在中间件 500

「旧版已有相关功能」属实——链路完整、前后端模块齐全。线上实测锁定卡点:中间件 10.152.64.7:9120 已配、网络通,但该中间件所有 /dh/* 接口返 500。不是代码问题,是中间件层故障

链路 5 跳:

1
2
3
4
5
6
flowchart LR
A["告警配置页<br/>绑定推送屏"] --> B["warning<br/>pushScreenDevice"]
B --> C["smart<br/>PushDeviceServiceImpl"]
C --> D["integrate-iot<br/>IotGatewayDhScreenProvider"]
D --> E["大华信发平台<br/>/dh/savePlayInfo"]
style E fill:#ffcdd2

只有大华(factory=2)有实现,海康 / 明景分支为空。

线上实测锁定卡点的四步排查

从「点同步报系统繁忙」开始,把锅从代码一路摘干净、钉到中间件:

  1. smart 同步报「系统繁忙」 → 查代码:ScreenInfoServiceImpl.syncMJScreenDeviceList(明景)无 try-catch,崩了掩盖大华真实结果switchScreen 大华路径 result.getData().success() 没 null 检查,NPE 把大华的错吞成笼统「系统繁忙」。
  2. 改测大华专用路径(只走大华不走明景)→ 仍「系统繁忙」→ 确认大华链路也断。
  3. 查 integrate-iot 日志operateDevicequeryPageDeviceList ResourceAccessException: HTTP 500,上游 http://10.152.64.7:9120
  4. 服务器直接 curl 中间件:响应体是 Spring Boot 标准 500 格式(timestamp/status/error/path),无 auth/token 字样/actuator/health/ 返 404(进程活着、actuator 没暴露)。

结论10.152.64.7:9120 是个 Spring Boot 中间件(大华信发网关,与 Vplat AI 网关 :9002 同机),进程活着(404 正常)但所有 /dh/* 业务接口返 500。host 已配、网络已通、请求已发出、不是鉴权问题——是中间件内部故障。

修正之前的误判:原写「host 全渠道未配置」——线上其实配了,dev 没配但线上有。卡点不是「地址没配」,是「配的中间件坏了」。

附带发现的两个代码 bug

  1. ScreenInfoServiceImpl.syncMJScreenDeviceList 无 try-catch——明景一崩,整个同步接口「系统繁忙」,掩盖大华结果
  2. switchScreen result.getData().success() 没 null 检查——大华失败时 NPE,把真实错误吞成笼统「系统繁忙」,排查极费劲。

这两个 bug 的形态和显示屏推送排查里那个「失败零日志」是同一类病:错误被笼统化吞掉,现场丢失

3.4 语音播报:骨架四截全断

详细拆解另见这篇,结论:「旧版已有」的是一副骨架,设备能力层可复用,Feign、触发、配置、素材持久化四层全断,当前完全不可用,需要补开发。

底层探活的一个间接证据:实时监控能看画面,说明视频平台活着、鉴权通;广播接口和视频流共一套鉴权,大概率也活。所以 3.4 的底层风险远低于 3.3(3.3 是整个中间件 500,3.4 平台已证实活着)。真卡点收窄到设备喇叭(哪台设备能发声)+ 广播接口是否单独启用。

进度状态总览

子项 状态 说明
3.1 入侵检测 API 参数确认 ✅ 已交付 结论发在任务评论
3.2 平台弹窗 ✅ 两轮完成 文字弹窗 + 视频弹窗 + 接收人定向 + 本地录像截图
3.3 信息屏发布 ⚠️ 线上实测闭合 链路代码完整;卡在中间件 /dh/* 全 500,甩给中间件维护方
3.4 语音播报 ⚠️ 部分前置 Feign + 数据层已写,触发 stub + 前端保存仍断
3.5 其余联动 🔒 暂缓 联动报警(报警主机)全库为零

三个联动的三种状态
图 2:三个联动三种命运——弹窗已转通、信息屏被中间件卡住、语音播报碎成四段。

IoT 联动的设计要点(API 文档提炼)

这次还顺带梳理了安防视频 API 的几个模块(录像、截图、摄像头预置位),其中两个设计点值得记下来。

预置位的事务一致性——IoT 下发与本地落库在同一事务:先下发、后落库、失败回滚

操作 流程 失败行为
新增 先下发 presetSet 到 IoT 平台 → 成功后落库 IoT 失败 → 抛异常,事务回滚,本地不记录
修改 先下发 presetSet(覆盖同槽位)→ 成功后更新本地 同上,整体回滚
删除 逐条下发 presetDel → 全部成功后软删本地 任一失败 → 整体回滚,本地也不删

因此「本地有记录」⟹「IoT 侧一定设置成功」;反向不保证。这是一个「指令先行、本地为主」的可靠模式:避免本地有记录而设备上不存在。

录像 / 截图的数据隔离——非超管按 create_id + park_id + tenant_id 三元组隔离,只能看自己创建的;超管查全量。

角色 分页可见范围 查看 / 改名 / 删除
普通用户 仅自己创建(三元组匹配) 仅自己的记录,越权整体拒绝
超管 全量(可用 createId 再过滤) 任意记录

小结

这个任务最大的教训是「原型 ≠ 能力」。原型画出来的功能,不一定上游 API 真的支持;「旧版已有」也不等于「能用」。几个收敛方法:

  • API 能力要逐项对照原型,把「支持 / 不支持 / 待实测」三类列清楚,不支持的就是要产品决策的。
  • 「待实测」和「代码层面不支持」要分开timeTemplate 丢分钟是代码写死的,不是「接上真实设备也许行」。
  • 「旧版已有」要逐层探活:信息屏是中间件坏了,语音播报是四层全断,弹窗是订阅下架——三个「旧版已有」三种病。
  • 错误别笼统化syncMJScreenDeviceList 吞异常、switchScreen NPE、pushScreenDevice 失败零日志,都是同一类病——把真实错误吞成「系统繁忙」,排查代价极高。

三个联动的现实卡点各不相同:弹窗是代码要重写、信息屏是中间件要修、语音播报是四层要重建。同一个「基本闭环」任务,三套完全不同的工作量和依赖方,这是任务描述「调通是否可用」六个字完全没法覆盖的复杂度。