feat: 手术视频消耗、待确认与持久化改造

- 新增 Alembic 初始迁移、领域明细模型及归档持久化与重试链路\n- 拆分视频会话注册表、分类处理、推理时间窗聚合与流处理\n- 消耗日志:TSV/Markdown 含 top2/top3;item_id 优先产品编码;待确认记「待确认」行,语音确认后落正式行并更新汇总\n- 待确认时内存/DB 明细为占位行,确认后替换;拒绝时移除占位\n- 分类 probs 先 detach/cpu 再转 NumPy,修复 MPS/CUDA 上推理被静默跳过\n- 补充集成测试、归档与设备张量等单测

Made-with: Cursor
This commit is contained in:
Kevin
2026-04-23 20:42:21 +08:00
parent 69980d8073
commit 3d7bd70355
55 changed files with 4544 additions and 2050 deletions

View File

@@ -8,10 +8,10 @@
## RTSP 模式(默认)
1. 配置 **`camera_id` → RTSP URL** 映射,任选其一或组合使用:
- **`VIDEO_RTSP_URLS_JSON_FILE`**:指向 UTF-8 JSON 文件(对象键为与请求一致的 `camera_id`)。仓库示例:[`app/resources/camera_rtsp_urls.sample.json`](../app/resources/camera_rtsp_urls.sample.json)(示例 ID`or-cam-01``or-cam-02`)。
- **`VIDEO_RTSP_URLS_JSON`**:内联 JSON 字符串;与文件合并时**覆盖同键**。
- **`VIDEO_RTSP_URL_TEMPLATE`**:单模板,可用 `{camera_id}`
1. 配置 `**camera_id` → RTSP URL** 映射,任选其一或组合使用:
- `**VIDEO_RTSP_URLS_JSON_FILE`**:指向 UTF-8 JSON 文件(对象键为与请求一致的 `camera_id`)。仓库示例:`[app/resources/camera_rtsp_urls.sample.json](../app/resources/camera_rtsp_urls.sample.json)`(示例 ID`or-cam-01``or-cam-02`)。
- `**VIDEO_RTSP_URLS_JSON**`:内联 JSON 字符串;与文件合并时**覆盖同键**。
- `**VIDEO_RTSP_URL_TEMPLATE`**:单模板,可用 `{camera_id}`
2. 调用 `POST /client/surgeries/start` 时,`camera_ids` 必须能在上述配置中解析出 RTSP 地址。
3. **开录确认**:每路摄像头在超时内成功打开并读到**首帧**后,才认为该路已开录。
@@ -26,20 +26,20 @@ SDK **不作为构建期依赖**:将厂商提供的 Linux x86_64 动态库挂
行为概要:
1. 进程内对 `NET_DVR_Init` 使用引用计数;每路使用 SDK 的工作线程在登录后 `NET_DVR_Logout`,线程结束时配对 `NET_DVR_Cleanup`
2.`HIKVISION_SDK_FALLBACK_TO_RTSP=true`(默认),在**无法加载动态库**、**登录失败**或**未配置凭据**时,自动回退到 `VIDEO_RTSP_*` 映射拉流。
2.`HIKVISION_SDK_FALLBACK_TO_RTSP=true`(默认),在**无法加载动态库**、**登录失败**或**未配置凭据**时,自动回退到 `VIDEO_RTSP_`* 映射拉流。
**注意**`NET_DVR_Login_V30` 的设备信息结构体在不同 SDK 版本上可能存在差异;若登录异常,请优先使用 RTSP 回退或按厂商文档校对 ctypes 绑定。
## 推理与结果查询
- 开录后按 `VIDEO_INFERENCE_INTERVAL_SEC` 抽帧,依次调用耗材分类与撕扯动作模型。
- **候选耗材清单**开始手术请求体中的 `candidate_consumables`为**硬约束**:若为空,服务端**不会**写入任何消耗明细(仅拉流推理);非空时仅允许清单内标签自动记账
- **候选耗材清单**`candidate_consumables`:非空时**仅**清单内名称参与自动记账与待确认;**缺省或 `[]`** 时,用耗材目录 Excel **全部商品名**作为候选;无目录则用分类模型**全部类名**
- 当分类 Top1 置信度 **≥** `VIDEO_AUTO_CONFIRM_CONFIDENCE`**默认 0.9**)且标签在候选清单内时,自动写入一条 `source=vision` 的消耗明细;**低于**该线的识别需人工确认(在语音下沿之上且能展示候选项时入队)。
- 置信度在 \[`VIDEO_VOICE_CONFIRM_MIN_CONFIDENCE`, `VIDEO_AUTO_CONFIRM_CONFIDENCE`\) 等区间且存在可向医生展示的候选时,会生成**待确认**任务;客户端 `GET /client/surgeries/{surgery_id}/pending-confirmation`,确认后 `POST .../pending-confirmation/{id}/resolve` 等。
- 已有至少一条消耗明细后,`GET /client/surgeries/{surgery_id}/result` 返回 200已开录但尚未产生任何明细,返回 503 `RESULT_NOT_READY`
- 置信度在 `VIDEO_VOICE_CONFIRM_MIN_CONFIDENCE`, `VIDEO_AUTO_CONFIRM_CONFIDENCE` 等区间且存在可向医生展示的候选时,会生成**待确认**任务;客户端 `GET /client/surgeries/{surgery_id}/pending-confirmation`,确认后 `POST .../pending-confirmation/{id}/resolve` 等。
- `GET /client/surgeries/{surgery_id}/result` 仅在存在**至少一条**消耗明细时返回 200无明细(已开录但尚未记账、已结束但零消耗、或尚无归档等)返回 503 `RESULT_NOT_READY`
- 同类物品写入受 `VIDEO_DETAIL_COOLDOWN_SEC` 节流。
- RTSP 读帧连续失败达到 `VIDEO_READ_FAILURE_RECONNECT_THRESHOLD` 时会 `release` 并尝试重连,间隔 `VIDEO_RECONNECT_BACKOFF_SECONDS`
## 相关环境变量
详见仓库根目录 `.env.example` 中「视频RTSP + 可选海康 HCNetSDK」一节。
详见仓库根目录 `.env.example` 中「视频RTSP + 可选海康 HCNetSDK」一节。