Add rotating file logs, access noise filters, and longer RTSP start timeout.

Persist app logs under logs/app with 7-day rotation, suppress routine health/pending access lines, raise default VIDEO_OPEN_TIMEOUT_SEC to 90s, and document consumable codes plus client timeout guidance.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin
2026-05-26 13:11:30 +08:00
parent 5fd0851a44
commit ae6300b8b2
12 changed files with 584 additions and 71 deletions

View File

@@ -4,6 +4,23 @@
对接方请按部署版本核对本节;**已发布路径与字段语义以正文为准**,本节仅记录相对上一版的客户端需改动点。
### 2026-05-26
**行为 / 运维**
| 项 | 变更 |
| --- | --- |
| `POST /client/surgeries/start` | 默认 RTSP 开录等待由 45s 调至 **90s**;客户端 HTTP 超时建议 ≥ **300s** |
| 服务端日志 | 应用日志持久化至 `backend/logs/app/server.log`**7 天轮转/保留**access 日志隐藏 `GET /health` 200 与 pending 轮询 404 |
### 2026-05-26
**文档**
| 接口 | 变更 | 客户端需改动 |
| ---- | ---- | ------------ |
| `POST /internal/demo/offline-batch` 及配套 GET | **新增** §5.7§5.9:链路 3 离线 MP4 上传、耗时轮询、标注视频下载 | 非 RTSP 集成时可走上传视频路径;仍用 `GET /client/surgeries/{surgery_id}/result` 查消耗,**无需** `start` / `end` |
### 2026-05-25
**行为对齐(实现已与正文一致)**
@@ -16,8 +33,7 @@
**未变更**
- 路由路径、HTTP 方法、`surgery_id` 约束、成功体字段名与 §5 各节描述一致。
- 可新增 `/internal/demo/...` 等内部接口,不影响本文档契约。
- `/client/...` 路由路径、HTTP 方法、`surgery_id` 约束、成功体字段名与 §5 各节描述一致。
---
@@ -26,10 +42,11 @@
| **能力** | **说明** |
| --------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **探活** | `GET /health`,用于检查进程和数据库状态,详见 5.1 节。 |
| **开始手术** | `POST /client/surgeries/start`,只有在开录确认成功后才返回 `200`。 |
| **结束手术** | `POST /client/surgeries/end`,只有在停录确认成功后才返回 `200`。 |
| **查询结果** | `GET /client/surgeries/{surgery_id}/result`,至少存在一条消耗明细时返回 `200`;否则返回 `503`,常见错误码为 `RESULT_NOT_READY` |
| **待确认播报** | **官方浏览器客户端**(仓库 `clients/voice-confirmation/`,可独立部署):同一 WebSocket 上推送 `voice_pending`(载荷与 `GET .../pending-confirmation` 成功体一致,另含 `type`);无队首时 `voice_pending_empty`**不轮询 GET**。**第三方**仍可用 `GET .../pending-confirmation` 拉取队首。 |
| **开始手术** | `POST /client/surgeries/start`,只有在开录确认成功后才返回 `200`**链路 1 · 真 RTSP**。 |
| **结束手术** | `POST /client/surgeries/end`,只有在停录确认成功后才返回 `200`(链路 1。 |
| **上传视频** | `POST /internal/demo/offline-batch`,上传单路 MP4 离线推理(**链路 3****无需** `start` / `end`,不触发语音终端;详见 5.7 节。需运维开启 `DEMO_ORCHESTRATOR_ENABLED=true` |
| **查询结果** | `GET /client/surgeries/{surgery_id}/result`,至少存在一条消耗明细时返回 `200`;否则返回 `503``detail.code` 为 `RESULT_NOT_READY`。链路 1 / 3 共用。 |
| **待确认播报** | **官方浏览器客户端**(仓库 `clients/voice-confirmation/`,可独立部署):同一 WebSocket 上推送 `voice_pending`(载荷与 `GET .../pending-confirmation` 成功体一致,另含 `type`);无队首时 `voice_pending_empty`**不轮询 GET**。**第三方**仍可用 `GET .../pending-confirmation` 拉取队首。**链路 3 无待确认。** |
| **待确认答复** | `POST /client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve`,上传医生答复的 WAV 录音,服务端完成 ASR 后入账或关闭。该录音与播报音频无关。 |
## 1. 服务与基础信息
@@ -38,8 +55,9 @@
| ----------------------- | ------------------------------------------- |
| **协议** | `HTTP/HTTPS` |
| **端口** | `38080`,生产环境以实际入口为准 |
| **路由** | 无全局前缀;业务接口位于 `/client/...`,健康检查位于 `/health` |
| **路由** | 无全局前缀;业务接口位于 `/client/...`,健康检查位于 `/health`;链路 3 上传位于 `/internal/demo/...` |
| **`start` / `end` 请求体** | JSON |
| **链路 3 上传请求体** | `multipart/form-data`,字段见 5.7 节 |
| **`resolve` 请求体** | `multipart/form-data`,字段名为 `audio` |
| **在线文档** | `/docs``/redoc` |
| **语音确认官方页面** | 仓库 `clients/voice-confirmation/`(静态资源,与 API 分宿;需为浏览器配置 CORS见该目录 `README.md` |
@@ -48,7 +66,7 @@
## 2. 摄像头 ID 与 RTSP
RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运维,运维再写入服务端环境。客户端只在 `POST /client/surgeries/start` 中传 `camera_ids`
RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运维,运维再写入服务端环境。客户端只在 **链路 1**`POST /client/surgeries/start` 中传 `camera_ids`**链路 3 上传视频不需要 `camera_ids`。**
| **camera_id** | **RTSP** | **备注** |
| ------------- | -------- | -------- |
@@ -64,13 +82,17 @@ RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运
| **序号** | **方法** | **路径** | **说明** |
| ------ | ------ | ------------------------------------------------------------------------------- | ------- |
| 1 | `GET` | `/health` | 探活 |
| 2 | `POST` | `/client/surgeries/start` | 开始手术 |
| 3 | `POST` | `/client/surgeries/end` | 结束手术 |
| 4 | `GET` | `/client/surgeries/{surgery_id}/result` | 查询手术结果 |
| 5 | `GET` | `/client/surgeries/{surgery_id}/pending-confirmation` | 拉取待确认耗材 |
| 6 | `POST` | `/client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve` | 提交医生答复 |
| 7 | `GET` | `/client/voice-terminals/{terminal_id}/assignment` | 可选:查询当前指派(调试或简易集成;**官方浏览器客户端仅用 WebSocket** |
| 8 | `WS` | `/client/voice-terminals/ws?terminal_id=...` | 语音终端长连接,接收开录/停录指派(**推荐**;与 `clients/voice-confirmation` 共用) |
| 2 | `POST` | `/client/surgeries/start` | 开始手术(链路 1 |
| 3 | `POST` | `/client/surgeries/end` | 结束手术(链路 1 |
| 4 | `GET` | `/client/surgeries/{surgery_id}/result` | 查询手术结果(链路 1 / 3 共用) |
| 5 | `GET` | `/client/surgeries/{surgery_id}/pending-confirmation` | 拉取待确认耗材(链路 1 |
| 6 | `POST` | `/client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve` | 提交医生答复(链路 1 |
| 7 | `POST` | `/internal/demo/offline-batch` | **链路 3**:上传 MP4 离线推理(需 `DEMO_ORCHESTRATOR_ENABLED=true` |
| 8 | `GET` | `/internal/demo/offline-batch/{surgery_id}/timing` | 链路 3查询文本 / 标注视频各阶段耗时 |
| 9 | `GET` | `/internal/demo/offline-batch/{surgery_id}/visualization` | 链路 3下载标注 MP4可选 |
| 10 | `GET` | `/internal/demo/recording-modes-status` | 可选:探测链路 3 是否已启用 |
| 11 | `GET` | `/client/voice-terminals/{terminal_id}/assignment` | 可选:查询当前指派(调试或简易集成;**官方浏览器客户端仅用 WebSocket** |
| 12 | `WS` | `/client/voice-terminals/ws?terminal_id=...` | 语音终端长连接,接收开录/停录指派(**推荐**;与 `clients/voice-confirmation` 共用) |
| | | | |
**术间与语音终端绑定(服务端配置)**
@@ -84,7 +106,7 @@ RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运
## 4. 流程
### 4.1 时序图
### 4.1 时序图(链路 1 · 真 RTSP
```mermaid
sequenceDiagram
@@ -118,7 +140,7 @@ sequenceDiagram
Server-->>Client: 200持久化后可查时返回
```
### 4.2 状态图
### 4.2 状态图(链路 1
```mermaid
flowchart LR
@@ -130,6 +152,46 @@ flowchart LR
E -->|result 200| C
```
### 4.3 时序图(链路 3 · 上传 MP4
与 §4.1 并行存在,适用于**已有完整术间录像**、无需实时拉流与语音待确认的场景。
```mermaid
sequenceDiagram
participant Client as 客户端
participant Server as 服务端
opt 探测(可选)
Client->>Server: GET /internal/demo/recording-modes-status
Server-->>Client: demo_recording_modes_enabled
end
Client->>Server: POST /internal/demo/offline-batch
Note over Client,Server: multipart: surgery_id, video1, candidate_consumables_json, include_visualization
Server-->>Client: 200 acceptedTSV 推理已完成)
Client->>Server: GET /client/surgeries/{surgery_id}/result
Server-->>Client: 200 或 503 RESULT_NOT_READY
opt include_visualization=true
loop 直至 video_status=ready 或 failed
Client->>Server: GET .../offline-batch/{surgery_id}/timing
Server-->>Client: video_status pending / ready / failed
end
Client->>Server: GET .../offline-batch/{surgery_id}/visualization
Server-->>Client: video/mp4
end
```
**与链路 1 的差异**
| 项目 | 链路 1RTSP | 链路 3上传 MP4 |
| ---- | -------------- | ------------------ |
| 开录 / 停录 | 需要 `start` / `end` | **不需要** |
| 语音终端 / 待确认 | 有 | **无**(结果直接入库) |
| 查结果 | `GET .../result` | 同上 |
| 服务端开关 | RTSP 站点配置 | 另需 `DEMO_ORCHESTRATOR_ENABLED=true` |
## 5. 接口详情
@@ -143,7 +205,7 @@ flowchart LR
**业务错误响应**
多数业务失败在 `4xx` 或 `5xx` 下返回如下 JSON
`/client/...` 多数业务失败在 `4xx` 或 `5xx` 下返回如下 JSON
```
{
@@ -155,6 +217,8 @@ flowchart LR
}
```
§5.7§5.9(链路 3的部分校验失败返回 FastAPI 默认 `detail` **字符串**(无 `code` 包装),见各节状态码表。
### 5.1 探活
**基本信息**
@@ -185,9 +249,11 @@ flowchart LR
**业务说明**
- 服务端会为 `camera_ids` 中的每个摄像头建立拉流与推理任务,只有在确认开录成功(如首帧就绪)后才返回 HTTP `200`
- 本接口**同步阻塞**直至开录确认;默认 RTSP 等待约 **95 秒/次**、最多 **3 次重试**。客户端 HTTP 超时建议 **≥ 300 秒**Apipost 等工具默认 3060 秒易触发 `ESOCKETTIMEDOUT`)。服务端可通过 `VIDEO_OPEN_TIMEOUT_SEC` 调大(默认 90
- `candidate_consumables` 为空时,服务端会展开为目录中的全部耗材名
- 非空时,每项可为**耗材名称**或**产品编码**`label_id`);编码通过 `consumable_classifier_labels.yaml` 解析为类名。亦支持医院导出对象(见下表)。
- `candidate_consumables` 缺省或 `[]` 时,服务端会展开为 `consumable_classifier_labels.yaml` 中的全部类名(无有效 yaml 时开录失败)
- 非空时,每项可为**耗材名称**或**产品编码**`label_id`);编码通过 yaml 解析为类名后再参与推理与白名单。亦支持医院导出对象(见下表)。
- **41 类完整类名与产品编码对照**:见 [`docs/耗材产品编码与类名对照表.md`](耗材产品编码与类名对照表.md)(须用完整类名或编码,口语简称如「纱布」不会自动映射)。
**请求体JSON**
@@ -201,7 +267,7 @@ flowchart LR
| **形式** | **示例** | **说明** |
| -------- | -------- | -------- |
| 名称字符串 | `"纱布"` | 与 yaml / 模型类名一致 |
| 名称字符串 | `"医用纱布敷料"` | 与 yaml / 模型类名**完全一致**(见对照表) |
| 编码字符串 | `"14764-2-4"` | 与 yaml 中 `label_id` 一致,服务端解析为类名 |
| 导出对象(名称) | `{"消耗品编号":"14764-2-4","名称":"一次性使用手术单"}` | 以 `名称`(或 `name`)为准 |
| 导出对象(仅编号) | `{"消耗品编号":"14764-2-4"}` | 按编号解析为类名 |
@@ -311,6 +377,8 @@ flowchart LR
- 上述 `503``detail.code`**`RESULT_NOT_READY`**`detail.message` 说明具体原因(如未开始、进行中尚无明细、已结束无消耗等)。
- **链路 3**:离线 batch 完成后结果直接写入数据库,通过本接口查询;**无需**先调用 `start` / `end`。若 batch 识别结果为零条明细,本接口仍返回 `503`
**响应体200**
@@ -326,7 +394,7 @@ flowchart LR
| **字段** | **类型** | **说明** |
| ----------- | --------- | ---------------------------------- |
| `item_id` | `string` | 物品 ID目录时多为产品编码,否则通常与名称或模型类名一致 |
| `item_id` | `string` | 物品 ID yaml 时多为 `label_id`,否则通常与类名一致 |
| `item_name` | `string` | 物品名称 |
| `qty` | `integer` | 本条记录数量,当前恒为 `1`;一次识别或一次人工确认只追加一条明细 |
| `doctor_id` | `string` | 记账关联的医生或系统标识 |
@@ -422,8 +490,8 @@ flowchart LR
| **HTTP** | **说明** |
| -------- | ----------------------------------------------------- |
| `200` | 当前有一条待确认 |
| `404` | 无待确认或手术未活跃;常见错误码为 `NO_PENDING_CONFIRMATION` |
| `422` | 例如话术为空导致无法 TTS错误码见响应,如 `TTS_TEXT_EMPTY` |
| `404` | 无待确认或手术未活跃;`detail.code` 为 `NO_PENDING_CONFIRMATION` |
| `422` | 例如话术为空导致无法 TTS`detail.code` 如 `TTS_TEXT_EMPTY` |
| `503` | 语音服务未配置或 TTS 失败;例如 `BAIDU_NOT_CONFIGURED``TTS_ERROR` |
### 5.6 提交待确认结果(医生语音)
@@ -440,7 +508,7 @@ flowchart LR
| **参数** | **约束** | **说明** |
| ----------------- | ---------- | -------------------------------- |
| `surgery_id` | 6 位数字 | 同 5.0 节 |
| `surgery_id` | 6 位数字 | 同 §5 路径参数 |
| `confirmation_id` | 长度 1 到 128 | 与 5.5 节响应中的 `confirmation_id` 一致 |
**请求体multipart**
@@ -451,7 +519,7 @@ flowchart LR
**业务说明**
音频上传至对象存储后执行 ASR 和候选解析。若识别为确认某个候选项,则记一条消耗;若识别为否认全部候选,则不记消耗。
音频上传至对象存储后执行 ASR 和候选解析。若识别为确认某个候选项,则记一条消耗;若识别为否认全部候选,则不记消耗。ASR/解析可重试失败时队首待确认项**不弹出**,便于客户端重录。
**响应体200**
@@ -471,9 +539,9 @@ flowchart LR
| **HTTP** | **说明** |
| -------- | ------------------------------------------------------------------------------------- |
| `200` | 已受理并完成解析 |
| `404` | 确认项不存在或手术未活跃;如 `CONFIRMATION_NOT_FOUND` |
| `409` | 当前确认项已处理过;如 `CONFIRMATION_ALREADY_RESOLVED` |
| `422` | 空文件、非 `.wav``VOICE_AUDIO_INVALID`、**ASR/解析可重试失败**(如 `VOICE_ASR_FAILED``VOICE_TEXT_EMPTY``VOICE_PARSE_FAILED`)等,具体错误码见 `detail.code` |
| `404` | 确认项不存在或手术未活跃;`detail.code` 如 `CONFIRMATION_NOT_FOUND` |
| `409` | 当前确认项已处理过;`detail.code` 如 `CONFIRMATION_ALREADY_RESOLVED` |
| `422` | 空文件、非 `.wav``VOICE_AUDIO_INVALID`、**ASR/解析可重试失败**(如 `VOICE_ASR_FAILED``VOICE_TEXT_EMPTY``VOICE_PARSE_FAILED`)等 |
| `503` | MinIO、百度等依赖不可用例如 `MINIO_NOT_CONFIGURED``MINIO_UPLOAD_FAILED``BAIDU_NOT_CONFIGURED` |
**cURL 示例**
@@ -484,11 +552,159 @@ curl -sS -X POST \
-F "audio=@/path/to/voice.wav;type=audio/wav"
```
### 5.7 语音终端 assignmentHTTP可选
### 5.7 上传手术视频(链路 3 · 离线 batch
**基本信息**
| **项目** | **内容** |
| ---------------- | --------------------------------- |
| **方法** | `POST` |
| **路径** | `/internal/demo/offline-batch` |
| **Content-Type** | `multipart/form-data` |
**前置条件**
- 服务端环境变量 **`DEMO_ORCHESTRATOR_ENABLED=true`**。未开启时本路径返回 **`404`**`detail``Demo recording modes disabled (set DEMO_ORCHESTRATOR_ENABLED=true).`
- 可先调用 **`GET /internal/demo/recording-modes-status`** 查看 `demo_recording_modes_enabled` 是否为 `true`(该探测接口始终注册,不依赖上述开关)。
**业务说明**
- 上传**单路完整 MP4**(字段名 `video1`),服务端调用离线算法包(`algorithm_subprocesses/5.15/main.py`)完成整段推理,解析 TSV 后将消耗明细**直接写入数据库**。
- **不**启动 RTSP 实时会话,**不**调用 `start` / `end`**不**触发语音终端或待确认队列。
- 请求在服务端**同步阻塞**直至 TSV 推理结束才返回 `200`;长视频可能耗时较久,客户端应设置足够长的 HTTP 超时。
- 相同视频内容SHA-256+ 相同候选清单会命中结果缓存(跨 `surgery_id` 复用),响应 `message` 中含 `cache=hit``cache=miss`
- `candidate_consumables_json` 的语义与 5.2 节 `candidate_consumables` 一致(名称、产品编码、医院导出对象);缺省 `"[]"` 时展开为 yaml 全部类名。
**请求体multipart**
| **字段名** | **类型** | **必填** | **说明** |
| ------- | ------ | ------ | ------ |
| `surgery_id` | `string` | 是 | 6 位数字 |
| `video1` | `file` | 是 | 单路完整 MP4空文件返回 `422` |
| `candidate_consumables_json` | `string` | 否 | JSON 数组字符串,默认 `"[]"`;格式同 5.2 节候选清单 |
| `include_visualization` | `boolean` | 否 | 是否后台生成标注 MP4默认 `false`。为 `true` 时响应含 `visualization_url`,标注视频异步生成,需轮询 5.8 节 |
**响应体200**
| **字段** | **类型** | **说明** |
| -------- | -------- | -------- |
| `surgery_id` | `string` | 与请求一致 |
| `status` | `string` | 成功时为 `accepted` |
| `message` | `string` | 含 `rows=` 行数、`cache=hit|miss`、医生识别摘要等 |
| `visualization_url` | `string \| null` | `include_visualization=true` 时为相对路径 `/internal/demo/offline-batch/{surgery_id}/visualization`;否则为 `null` |
| `doctor_name` | `string \| null` | 算法输出的医生姓名(若识别到) |
| `doctor_id` | `string \| null` | 医生 ID若识别到 |
| `doctor_display` | `string \| null` | 展示用医生信息 |
| `text_duration_sec` | `number` | TSV / 主流程耗时(秒,保留 3 位小数) |
| `video_duration_sec` | `number \| null` | 标注视频耗时;未请求或仍在后台时为 `null` |
| `total_duration_sec` | `number` | 已完成阶段的合计耗时(秒) |
**状态码**
| **HTTP** | **说明** |
| -------- | -------- |
| `200` | 离线推理已完成,结果已入库(可能为零条明细) |
| `404` | Demo 模式未启用 |
| `422` | `surgery_id` 非 6 位、`candidate_consumables_json` 非法、`video1` 为空等 |
| `500` | 上传落盘失败 |
| `503` | 离线 batch 子进程失败;`detail` 形如 `offline batch failed: ...` |
**后续步骤**
1. 调用 **`GET /client/surgeries/{surgery_id}/result`**5.4 节)查询消耗明细。
2.`include_visualization=true`,轮询 **`GET .../offline-batch/{surgery_id}/timing`**5.8 节)直至 `video_status``ready``failed`,再 **`GET .../visualization`**5.9 节)下载 MP4。
**cURL 示例**
```
curl -sS -X POST "http://<主机>:38080/internal/demo/offline-batch" \
-F "surgery_id=123456" \
-F "video1=@/path/to/surgery.mp4;type=video/mp4" \
-F 'candidate_consumables_json=["14764-2-4","8036-5-22"]' \
-F "include_visualization=true"
```
**响应示例200**
```
{
"surgery_id": "123456",
"status": "accepted",
"message": "非实时精确视频处理完成rows=3 cache=miss医生=张三 (6611);标注视频后台生成中(完成后刷新 visualization URL24 小时内有效)",
"visualization_url": "/internal/demo/offline-batch/123456/visualization",
"doctor_name": "张三",
"doctor_id": "6611",
"doctor_display": "张三 (6611)",
"text_duration_sec": 842.5,
"video_duration_sec": null,
"total_duration_sec": 842.5
}
```
### 5.8 查询离线 batch 各阶段耗时
**基本信息**
| **项目** | **内容** |
| -------- | ------ |
| **方法** | `GET` |
| **路径** | `/internal/demo/offline-batch/{surgery_id}/timing` |
| **路径参数** | `surgery_id`6 位数字) |
| **请求体** | 无 |
**业务说明**
- 在 5.7 节上传完成后可用;用于轮询标注视频是否生成完毕。
- `video_status``skipped`(未请求标注视频)、`pending`(后台生成中)、`ready`(可下载)、`failed`(生成失败)。
**响应体200**
| **字段** | **类型** | **说明** |
| -------- | -------- | -------- |
| `surgery_id` | `string` | 手术号 |
| `text_duration_sec` | `number` | TSV 主流程耗时(秒) |
| `video_duration_sec` | `number \| null` | 标注视频耗时;未完成时为 `null` |
| `total_duration_sec` | `number` | 合计耗时(秒) |
| `video_status` | `string` | `skipped` / `pending` / `ready` / `failed` |
**状态码**
| **HTTP** | **说明** |
| -------- | -------- |
| `200` | 有该 `surgery_id` 的 timing 记录 |
| `404` | Demo 未启用,或尚无 timing 记录 |
| `422` | `surgery_id` 不符合约束 |
### 5.9 获取离线 batch 标注视频
**基本信息**
| **项目** | **内容** |
| -------- | ------ |
| **方法** | `GET` |
| **路径** | `/internal/demo/offline-batch/{surgery_id}/visualization` |
| **路径参数** | `surgery_id`6 位数字) |
| **请求体** | 无 |
**业务说明**
- 仅当 5.7 节 `include_visualization=true` 且后台生成成功时可用。
- 响应为 **`video/mp4`** 文件流,文件名 `{surgery_id}_result_vis.mp4`
- 标注文件默认保留 **24 小时**`VIDEO_BATCH_VIS_TTL_HOURS`),过期后返回 `404`
**状态码**
| **HTTP** | **说明** |
| -------- | -------- |
| `200` | MP4 文件流 |
| `404` | Demo 未启用、尚未生成或已过期 |
| `422` | `surgery_id` 不符合约束 |
### 5.10 语音终端 assignmentHTTP可选
**路径** `GET /client/voice-terminals/{terminal_id}/assignment`
仓库内 **手术室耗材语音确认浏览器客户端**`clients/voice-confirmation/`)仅通过 **§5.8 WebSocket** 接收指派,**不调用**本接口。此处供运维脚本、未实现 WS 的第三方临时拉取 `active_surgery_id`
仓库内 **手术室耗材语音确认浏览器客户端**`clients/voice-confirmation/`)仅通过 **§5.11 WebSocket** 接收指派,**不调用**本接口。此处供运维脚本、未实现 WS 的第三方临时拉取 `active_surgery_id`
**响应 200**
@@ -497,7 +713,7 @@ curl -sS -X POST \
| `voice_terminal_id` | `string` | 与路径一致 |
| `active_surgery_id` | `string \| null` | 当前指派手术 6 位号;无指派时为 `null` |
### 5.8 语音终端 WebSocket
### 5.11 语音终端 WebSocket
**路径** `GET ws://<主机>:<端口>/client/voice-terminals/ws?terminal_id=<终端ID>`HTTPS 部署时使用 `wss://`
@@ -505,4 +721,4 @@ curl -sS -X POST \
- 连接成功后,若服务端已有该终端的 assignment会立即收到一条 **`action":"start"`** 的 JSON与下文推送格式一致
- 术中由服务端在 **`start` / `end` 成功后** 向已连接终端推送 JSON`{"type":"voice_assignment","action":"start"|"end","surgery_id":"123456"}`
- 客户端可发送任意文本作心跳;服务端当前仅依赖 WebSocket 协议级 ping由网关或客户端库实现
- 客户端可发送任意文本作心跳;服务端当前仅依赖 WebSocket 协议级 ping由网关或客户端库实现

View File

@@ -0,0 +1,138 @@
# 耗材产品编码与类名对照表
本文档列出视觉耗材分类模型YOLO 耗材分类 + `consumable_classifier_labels.yaml`)支持的 **41 类**耗材。开录接口 `candidate_consumables`、离线批处理白名单、结果中的 `item_id` 均以此为准。
**数据源(随代码发布)**`backend/app/resources/consumable_classifier_labels.yaml`
**重要**
- 推理时类名以 **模型权重内嵌 `names`** 为准;本表与 yaml 保持一致。
- `candidate_consumables` 须传 **完整类名****产品编码label_id**;口语简称(如「纱布」「缝线」)**不会**自动映射,会导致算法白名单与模型类名无交集。
- 同一 `label_id` 若对应多个规格yaml 中以 `/` 连接;开录时可传其中任一段编码,服务端会解析为对应类名。
- `candidate_consumables` 缺省或 `[]` 时,服务端展开为下表 **全部 41 个类名**
---
## 全量对照表
| 序号 | 类名(`candidate_consumables` 可用) | 产品编码(`label_id` |
| ---: | --- | --- |
| 0 | MCuⅡ功能性宫内节育器 | `740-2-14` |
| 1 | 一次性中性电极板 | `4787-2-55` |
| 2 | 一次性使用乳胶导尿管 | `7386-10-89` |
| 3 | 一次性使用冲洗袋 | `1644-37-3` |
| 4 | 一次性使用医疗卫生用品 | `2241272` |
| 5 | 一次性使用单极手术电极 | `4805-2-50` |
| 6 | 一次性使用导尿管 | `735592/14556-4-18` |
| 7 | 一次性使用手术单 | `14764-2-4` |
| 8 | 一次性使用无菌敷贴 | `215-93-1` |
| 9 | 一次性使用无菌气管插管Tracheal Tube | `14780-3-5` |
| 10 | 一次性使用无菌注射器带针 | `1531-3-2/1531-3-1/1174-42-4/1174-42-1` |
| 11 | 一次性使用无菌采样拭子 | `15026-1-1` |
| 12 | 一次性使用气管插管 | `21444-1-2` |
| 13 | 一次性使用灭菌橡胶外科手套 | `10362-1-4` |
| 14 | 一次性使用牙垫 | `975961` |
| 15 | 一次性使用精密过滤输液器 带针 | `2295950` |
| 16 | 一次性使用肛门管 | `1518-22-4` |
| 17 | 一次性使用胃管 | `1518-34-17` |
| 18 | 一次性使用血液透析管路 | `14730-10-10` |
| 19 | 一次性使用输卵管导管 | `1380-15-1` |
| 20 | 一次性使用雾化器 | `5019-4-43` |
| 21 | 一次性使用静脉留置针 | `12591-1-184` |
| 22 | 一次性使用静脉输液针 | `129-5-30` |
| 23 | 一次性使用麻醉面罩 | `2003984` |
| 24 | 一次性内窥镜护套 | `521-31-1` |
| 25 | 一次性医用灭菌棉签 | `2237844/10183-1-29` |
| 26 | 一次性无菌喉罩 | `7386-61-46` |
| 27 | 医用凡士林敷料 | `10870-25-16` |
| 28 | 医用纱布敷料 | `19246-3-14` |
| 29 | 医用缝合针 | `583039/11207-1-64` |
| 30 | 医用脱脂棉纱布块 | `8028-4-39` |
| 31 | 可吸收性外科缝线 | `11765-1-101/1330-49-185` |
| 32 | 密闭式防针刺伤型静脉留置针 | `1281-39-3` |
| 33 | 导管固定器 | `1441340` |
| 34 | 气管切开插管 | `10869-30-7` |
| 35 | 结扎夹Ligating Clips | `14780-2-12` |
| 36 | 自粘性薄膜敷料 | `1819-4-1` |
| 37 | 血液净化装置的体外循环血路 | `739-2-1` |
| 38 | 负压引流器 | `1518-20-8` |
| 39 | 非吸收性外科缝线 | `4142-1-46` |
| 40 | 非吸收性外科缝线(蚕丝线) | `654032` |
---
## 常用口语与正确写法
下列简称 **不在** 模型类名表中,直接传入会导致算法白名单失效(日志:`allowed_names 与模型类名无交集`)。
| 口语/错误写法 | 建议改用(类名或编码) |
| --- | --- |
| 纱布 | `医用纱布敷料``19246-3-14`)或 `医用脱脂棉纱布块``8028-4-39` |
| 缝线 | `可吸收性外科缝线``11765-1-101` 等)或 `非吸收性外科缝线``4142-1-46`)或 `非吸收性外科缝线(蚕丝线)``654032` |
| 止血钳 | 表中无「止血钳」类;若业务需要请确认是否应对应其它器械类或扩展训练标签 |
| 垫单 | `一次性使用手术单``14764-2-4`);别名「一次性医用垫单」在服务端会归一化为该类名 |
---
## 开录请求示例
**按产品编码(推荐,与 HIS 编号一致)**
```json
{
"surgery_id": "123456",
"camera_ids": ["or-cam-01", "or-cam-03"],
"candidate_consumables": ["19246-3-14", "11765-1-101", "4142-1-46"]
}
```
**按完整类名**
```json
{
"surgery_id": "123456",
"camera_ids": ["or-cam-01"],
"candidate_consumables": ["医用纱布敷料", "可吸收性外科缝线", "非吸收性外科缝线"]
}
```
**医院导出对象(含消耗品编号)**
```json
{
"candidate_consumables": [
{ "消耗品编号": "14764-2-4", "名称": "一次性使用手术单" }
]
}
```
**不传候选(使用全部 41 类)**
```json
{
"surgery_id": "123456",
"camera_ids": ["or-cam-03"],
"candidate_consumables": []
}
```
---
## 多规格编码说明
部分 `label_id` 含多个规格,以 `/` 分隔。任一段均可作为 `candidate_consumables` 传入,例如:
| 类名 | 完整 label_id | 可单独传入的编码示例 |
| --- | --- | --- |
| 一次性使用无菌注射器带针 | `1531-3-2/1531-3-1/1174-42-4/1174-42-1` | `1531-3-2``1174-42-1` |
| 一次性使用导尿管 | `735592/14556-4-18` | `735592``14556-4-18` |
| 可吸收性外科缝线 | `11765-1-101/1330-49-185` | `11765-1-101``1330-49-185` |
| 一次性医用灭菌棉签 | `2237844/10183-1-29` | `2237844``10183-1-29` |
| 医用缝合针 | `583039/11207-1-64` | `583039``11207-1-64` |
---
## 相关文档
- 开录与结果接口:`docs/客户端手术通信接口说明.md` §5.2
- 候选清单行为说明:`docs/video-backends.md`