Update consumable pipeline, client API docs, and deployment config

- Refine effective candidate consumables and classifier labels
- Adjust vision algorithm, TSV logging, and video session wiring
- Refresh client surgery HTTP contract doc and staging/video docs
- Update settings, docker-compose prod, tests, and uv.lock

Made-with: Cursor
This commit is contained in:
Kevin
2026-04-24 11:05:17 +08:00
parent 3d7bd70355
commit 557fcee803
15 changed files with 529 additions and 636 deletions

View File

@@ -12,7 +12,7 @@
## 主流程
1. **开始手术** `POST /client/surgeries/start`
- 请求体含 6 位 `surgery_id``camera_ids``candidate_consumables` 可空(空则全量目录/模型类名)
- 请求体含 6 位 `surgery_id``camera_ids``candidate_consumables` 可空(空则 yaml 全量类名/模型类名)
- 返回 `200`,日志中各路 RTSP 首帧就绪
2. **进行中查询(可选)** `GET /client/surgeries/{id}/result`
- 至少一条明细时 `200`;无明细(开录后尚无、已归档但零消耗等)为 `503 RESULT_NOT_READY`

View File

@@ -33,7 +33,7 @@ SDK **不作为构建期依赖**:将厂商提供的 Linux x86_64 动态库挂
## 推理与结果查询
- 开录后按 `VIDEO_INFERENCE_INTERVAL_SEC` 抽帧,依次调用耗材分类与撕扯动作模型。
- **候选耗材清单**`candidate_consumables`):非空时**仅**清单内名称参与自动记账与待确认;**缺省或 `[]`** 时,用耗材目录 Excel **全部商品名**作为候选;无目录则用分类模型**全部类名**
- **候选耗材清单**`candidate_consumables`):非空时**仅**清单内名称参与自动记账与待确认;**缺省或 `[]`** 时,用 `consumable_classifier_labels.yaml` **全部名**作为候选;yaml 无有效 `names` 时用分类模型**全部类名**
- 当分类 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`

View File

@@ -1,180 +1,47 @@
<style>
/*
* Obsidian 单文件修复:
* 只复制本 Markdown 到 Obsidian 时,也尽量保证中文在预览和 PDF 导出中可见。
*/
.markdown-rendered,
.markdown-preview-view,
.markdown-source-view.mod-cm6 .cm-contentContainer,
.markdown-source-view.mod-cm6 .cm-scroller,
.mermaid,
.mermaid svg,
.mermaid svg text,
.mermaid svg tspan,
.mermaid svg foreignObject {
font-family:
"PingFang SC",
"Hiragino Sans GB",
"Noto Sans CJK SC",
"Source Han Sans SC",
"Microsoft YaHei",
sans-serif !important;
font-variant-ligatures: none !important;
}
.markdown-rendered code,
.markdown-rendered pre,
.markdown-preview-view code,
.markdown-preview-view pre,
code,
pre,
kbd,
samp {
font-family:
"Sarasa Mono SC",
"Maple Mono SC",
"SF Mono",
"Menlo",
"Monaco",
"Consolas",
"PingFang SC",
monospace !important;
font-variant-ligatures: none !important;
}
@media print {
body,
.markdown-preview-view,
.markdown-rendered,
.markdown-preview-section,
.markdown-rendered p,
.markdown-rendered li,
.markdown-rendered blockquote,
.markdown-rendered table,
.markdown-rendered thead,
.markdown-rendered tbody,
.markdown-rendered tr,
.markdown-rendered th,
.markdown-rendered td,
.markdown-rendered strong,
.markdown-rendered em,
.markdown-rendered span,
.markdown-rendered div,
.markdown-rendered h1,
.markdown-rendered h2,
.markdown-rendered h3,
.markdown-rendered h4,
.markdown-rendered h5,
.markdown-rendered h6,
.markdown-rendered .callout,
.markdown-rendered .callout-title,
.markdown-rendered .callout-content,
.mermaid,
.mermaid svg,
.mermaid svg text,
.mermaid svg tspan,
.mermaid svg foreignObject {
font-family:
"PingFang SC",
"Hiragino Sans GB",
"Noto Sans CJK SC",
"Source Han Sans SC",
"Microsoft YaHei",
sans-serif !important;
font-variant-ligatures: none !important;
}
code,
pre,
kbd,
samp,
.markdown-rendered code,
.markdown-rendered pre,
.markdown-rendered pre code,
.markdown-rendered .inline-code {
font-family:
"Sarasa Mono SC",
"Maple Mono SC",
"SF Mono",
"Menlo",
"Monaco",
"Consolas",
"PingFang SC",
monospace !important;
font-variant-ligatures: none !important;
}
}
</style>
# 手术室监控服务:客户端手术通信接口说明
面向对接本 FastAPI 服务的客户端HIS、手麻、工作站等。字段、状态码与返回模型以 `/docs``/openapi.json` 为准,本文用于摘要和流程说明。
> [!summary] 常用响应模型
> - `SurgeryApiResponse`
> - `SurgeryResultResponse`
> - `SurgeryPendingConfirmationResponse`
> - 业务错误外形见 `SurgeryClientErrorResponse`
> - 内部演示页:`scripts/demo_client/`(仅供演示,不作为对外契约)
## 能力概览
- 探活:`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`
- 待确认播报:`GET /client/surgeries/{surgery_id}/pending-confirmation`,拉取队首低置信度任务,返回话术文本和 MP3 Base64。
- 待确认答复:`POST /client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve`,上传医生答复的 WAV 录音,服务端完成 ASR 后入账或关闭。该录音与播报音频无关。
> [!important] HTTP 约定
> - `start` 和 `end` 使用 `POST` + `application/json`
> - `result` 使用 `GET`
> - `resolve` 使用 `POST` + `multipart/form-data`
> - `surgery_id` 固定为 6 位数字,正则为 `^\d{6}$`
> - `resolve` 路径中的 `confirmation_id` 必须与待确认接口返回值一致
> - `camera_ids` 必须与第 2 节清单及运维配置完全一致
| **能力** | **说明** |
| --------- | -------------------------------------------------------------------------------------------------------------------------------- |
| **探活** | `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`。 |
| **待确认播报** | `GET /client/surgeries/{surgery_id}/pending-confirmation`,拉取队首低置信度任务,返回话术文本和 MP3 Base64。 |
| **待确认答复** | `POST /client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve`,上传医生答复的 WAV 录音,服务端完成 ASR 后入账或关闭。该录音与播报音频无关。 |
## 1. 服务与基础信息
- 协议:`HTTP/HTTPS`
- 端口:`38080`,生产环境以实际入口为准
- 路由:无全局前缀;业务接口位于 `/client/...`,健康检查位于 `/health`
- `start` / `end` 请求体JSON
- `resolve` 请求体:`multipart/form-data`,字段名为 `audio`
- 在线文档:`/docs``/redoc`
| **项目** | **说明** |
| ----------------------- | ------------------------------------------- |
| **协议** | `HTTP/HTTPS` |
| **端口** | `38080`,生产环境以实际入口为准 |
| **路由** | 无全局前缀;业务接口位于 `/client/...`,健康检查位于 `/health` |
| **`start` / `end` 请求体** | JSON |
| **`resolve` 请求体** | `multipart/form-data`,字段名为 `audio` |
| **在线文档** | `/docs``/redoc` |
## 2. 摄像头 ID 与 RTSP
RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运维,运维再写入服务端环境(例如 JSON 映射或环境变量)。业务程序不在客户端保存 RTSP客户端只在 `POST /client/surgeries/start` 中传 `camera_ids`
RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运维,运维再写入服务端环境客户端只在 `POST /client/surgeries/start` 中传 `camera_ids`
配置格式示例见 `app/resources/camera_rtsp_urls.sample.json`。配置项细节见 `.env.example``docs/video-backends.md`
**摄像头映射示例**
- `or-cam-01`
- RTSP`rtsp://...`(由现场或 NVR 文档整理后交给运维)
- 备注:术间、机位
- `or-cam-02`
- RTSP`...`
- 备注:`...`
> [!warning] 对接要求
> - `camera_ids` 必须与运维配置中的 key 完全一致
> - RTSP 不应硬编码在客户端业务程序中
> [!tip] 联调建议
> - 运维配置完成后,客户端使用上面清单中的 `camera_id` 调用 `start` 验证是否返回 `200`
> - 若返回 `503` 且 `detail.code = RECORDING_CANNOT_START`,优先核对 ID 拼写以及监控服务器侧网络连通性
| **camera_id** | **RTSP** | **备注** |
| ------------- | -------------------------------- | ------ |
| `or-cam-01` | `rtsp://...`(由现场或 NVR 文档整理后交给运维) | 术间、机位 |
| `or-cam-02` | `...` | `...` |
## 3. HTTP 路由一览
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`提交待确认结果WAV
| **序号** | **方法** | **路径** | **说明** |
| ------ | ------ | ------------------------------------------------------------------------------- | ------- |
| 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` | 提交医生答复 |
| | | | |
## 4. 流程
@@ -182,34 +49,34 @@ RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运
```mermaid
sequenceDiagram
participant Client as 客户系统
participant API as 监控服务 API
participant Client as 客户
participant Server as 服务端
Client->>API: POST /client/surgeries/start
Note over Client,API: body: surgery_id, camera_ids, candidate_consumables
API-->>Client: 200 accepted开录已确认
Client->>Server: POST /client/surgeries/start
Note over Client,Server: body: surgery_id, camera_ids, candidate_consumables
Server-->>Client: 200 accepted开录已确认
par 术中
loop 轮询结果
Client->>API: GET .../result
API-->>Client: 200 或 503 RESULT_NOT_READY
Client->>Server: GET .../result
Server-->>Client: 200 或 503 RESULT_NOT_READY
end
loop 轮询待确认(若启用)
Client->>API: GET .../pending-confirmation
API-->>Client: 200 或 404
loop 轮询待确认
Client->>Server: GET .../pending-confirmation
Server-->>Client: 200 或 404
opt 有待确认
Client->>Client: 播放 prompt_audio_mp3_base64
Client->>API: POST .../resolvemultipart audio
API-->>Client: 200 accepted
Client->>Server: POST .../resolvemultipart audio
Server-->>Client: 200 accepted
end
end
end
Client->>API: POST /client/surgeries/end
API-->>Client: 200 accepted停录已确认
Client->>Server: POST /client/surgeries/end
Server-->>Client: 200 accepted停录已确认
Client->>API: GET .../result
API-->>Client: 200持久化后可查时返回
Client->>Server: GET .../result
Server-->>Client: 200持久化后可查时返回
```
### 4.2 状态图
@@ -226,21 +93,20 @@ flowchart LR
## 5. 接口详情
以下按“基本信息 -> 请求 -> 响应 -> 状态码”组织,与 OpenAPI 中 `tags: client``health` 一致。
### 5.0 通用约定
**路径参数 `surgery_id`**
**路径参数 `surgery_id`**
- 长度:固定 `6`
- 字符集:仅数字
- 正则:`^\d{6}$`
| **约束项** | **说明** |
| ------- | --------- |
| **长度** | 固定 `6` |
| **字符集** | 仅数字 |
| **正则** | `^\d{6}$` |
**业务错误响应**
多数业务失败在 `4xx``5xx` 下返回如下 JSON
多数业务失败在 `4xx` 或 `5xx` 下返回如下 JSON
```json
```
{
"detail": {
"code": "错误码字符串",
@@ -254,69 +120,63 @@ flowchart LR
**基本信息**
- 方法:`GET`
- 路径:`/health`
- 请求体:无
| **项目** | **内容** |
| ------- | --------- |
| **方法** | `GET` |
| **路径** | `/health` |
| **请求体** | 无 |
**响应说明**
- `200`
- 说明:进程正常且数据库可连通
- 响应体示例:`{"status":"ok","database":"connected"}`
- `503`
- 说明:数据库不可用(降级)
- 响应体示例:`{"status":"degraded","database":"unavailable"}`
| **HTTP** | **说明** | **响应体示例** |
| -------- | ----------- | ------------------------------------------------ |
| `200` | 进程正常且数据库可连通 | `{"status":"ok","database":"connected"}` |
| `503` | 数据库不可用(降级) | `{"status":"degraded","database":"unavailable"}` |
### 5.2 开始手术
**基本信息**
- 方法:`POST`
- 路径:`/client/surgeries/start`
- Content-Type`application/json; charset=utf-8`
| **项目** | **内容** |
| ---------------- | --------------------------------- |
| **方法** | `POST` |
| **路径** | `/client/surgeries/start` |
| **Content-Type** | `application/json; charset=utf-8` |
**业务说明**
- 服务端会为 `camera_ids` 中的每个摄像头建立拉流与推理任务,只有在确认开录成功(如首帧就绪)后才返回 HTTP `200`
- 若同一 `surgery_id` 存在尚未落库的历史归档,服务端会先尝试写入数据库;失败时可能返回 `503`(如 `RECORDING_CANNOT_START`),以避免静默丢数据
- `candidate_consumables` 为空时,服务端会展开为目录 Excel 中的全部商品名,或在未配置目录时展开为分类模型的全部类名
- 服务端会为 `camera_ids` 中的每个摄像头建立拉流与推理任务,只有在确认开录成功(如首帧就绪)后才返回 HTTP `200`
- `candidate_consumables` 为空时,服务端会展开为目录中的全部耗材名。
**请求体JSON**
- `surgery_id`
- 类型:`string`
- 必填:是
- 说明6 位数字,与路径规则一致
- `camera_ids`
- 类型:`string[]`
- 必填:是
- 说明:至少 1 个;必须与运维配置的摄像头 ID 完全一致,见第 2 节
- `candidate_consumables`
- 类型:`string[]`
- 必填:否
- 说明:非空时仅这些名称参与自动记账与待确认;缺省或 `[]` 时使用全部候选
| **字段** | **类型** | **必填** | **说明** |
| ----------------------- | ---------- | ------ | ----------------------------------- |
| `surgery_id` | `string` | 是 | 6 位数字 |
| `camera_ids` | `string[]` | 是 | 至少 1 个;必须与运维配置的摄像头 ID 完全一致,见第 2 节 |
| `candidate_consumables` | `string[]` | 否 | 非空时仅这些名称参与自动记账与待确认;缺省或 `[]` 时使用全部候选 |
**响应体200**
- `surgery_id`
- 类型:`string`
- 说明:与请求一致
- `status`
- 类型:`string`
- 说明:成功时通常为 `accepted`
- `message`
- 类型:`string`
- 说明:说明文案
| **字段** | **类型** | **说明** |
| ------------ | -------- | ----------------- |
| `surgery_id` | `string` | 与请求一致 |
| `status` | `string` | 成功时通常为 `accepted` |
| `message` | `string` | 说明文案 |
**状态码**
- `200`:开录已确认
- `422`:参数校验失败,例如 `surgery_id` 非 6 位或 `camera_ids` 为空数组
- `503`开录确认或录制子系统故障;`detail.code` 常见为 `RECORDING_CANNOT_START`
| **HTTP** | **说明** |
| -------- | -------------------------------------------------------- |
| `200` | 开录确认 |
| `422` | 参数校验失败,例如 `surgery_id` 非 6 位或 `camera_ids` 为空数组 |
| `503` | 开录未确认或录制子系统故障;`detail.code` 常见为 `RECORDING_CANNOT_START` |
**请求示例**
```json
```
{
"surgery_id": "123456",
"camera_ids": ["or-cam-01", "or-cam-02"],
@@ -326,7 +186,7 @@ flowchart LR
**响应示例200**
```json
```
{
"surgery_id": "123456",
"status": "accepted",
@@ -338,34 +198,37 @@ flowchart LR
**基本信息**
- 方法:`POST`
- 路径:`/client/surgeries/end`
- Content-Type`application/json; charset=utf-8`
| **项目** | **内容** |
| ---------------- | --------------------------------- |
| **方法** | `POST` |
| **路径** | `/client/surgeries/end` |
| **Content-Type** | `application/json; charset=utf-8` |
**业务说明**
停止该 `surgery_id` 关联的全部摄像头任务,只有在确认停录完成后才返回 `200`
停止该 `surgery_id` 关联的全部摄像头任务,只有在确认停录完成后才返回 `200`
**请求体JSON**
- `surgery_id`
- 类型:`string`
- 必填:是
- 说明6 位数字
| **字段** | **类型** | **必填** | **说明** |
| ------------ | -------- | ------ | ------ |
| `surgery_id` | `string` | 是 | 6 位数字 |
**响应体200**
字段含义与 5.2 节一致,`message` 示例为 `摄像头录制已停止,手术已结束。`
字段含义与 5.2 节一致,`message` 示例为 `摄像头录制已停止,手术已结束。`
**状态码**
- `200`:停录已确认
- `422`:参数校验失败
- `503`停录确认或故障;`detail.code` 常见为 `RECORDING_NOT_STOPPED`
| **HTTP** | **说明** |
| -------- | -------------------------------------------------- |
| `200` | 停录确认 |
| `422` | 参数校验失败 |
| `503` | 停录未确认或故障;`detail.code` 常见为 `RECORDING_NOT_STOPPED` |
**请求示例**
```json
```
{
"surgery_id": "123456"
}
@@ -375,91 +238,78 @@ flowchart LR
**基本信息**
- 方法:`GET`
- 路径:`/client/surgeries/{surgery_id}/result`
- 路径参数:`surgery_id`
- 请求体:无
| **项目** | **内容** |
| -------- | --------------------------------------- |
| **方法** | `GET` |
| **路径** | `/client/surgeries/{surgery_id}/result` |
| **路径参数** | `surgery_id` |
| **请求体** | 无 |
**业务说明**
- 仅当存在至少一条消耗明细时返回 `200`
- 无明细(包括已归档但零消耗)、手术未开始、未成功开录或当前尚不可查时,返回 `503`
- 上述 `503` 场景的常见错误码为 `RESULT_NOT_READY`
- 仅当存在至少一条消耗明细时返回 `200`
- 无明细(包括已归档但零消耗)、手术未开始、未成功开录或当前尚不可查时,返回 `503`
- 上述 `503` 场景的常见错误码为 `RESULT_NOT_READY`
**响应体200**
- `surgery_id`
- 类型:`string`
- 说明:手术号
- `status`
- 类型:`string`
- 说明:成功时通常为 `completed`
- `message`
- 类型:`string`
- 说明:说明
- `details`
- 类型:`array`
- 说明:消耗明细列表,字段见下文
- `summary`
- 类型:`array`
- 说明:按 `item_id` 汇总的结果,字段见下文
| **字段** | **类型** | **说明** |
| ------------ | -------- | ----------------------- |
| `surgery_id` | `string` | 手术号 |
| `status` | `string` | 成功时通常为 `completed` |
| `message` | `string` | 说明 |
| `details` | `array` | 消耗明细列表,字段见下文 |
| `summary` | `array` | 按 `item_id` 汇总的结果,字段见下文 |
**`details[]` 元素**
**`details[]` 元素**
- `item_id`
- 类型:`string`
- 说明:物品 ID有目录时多为产品编码否则通常与名称或模型类名一致
- `item_name`
- 类型:`string`
- 说明:物品名称
- `qty`
- 类型:`integer`
- 说明:本条记录数量,当前恒为 `1`;一次识别或一次人工确认只追加一条明细
- `doctor_id`
- 类型:`string`
- 说明:记账关联的医生或系统标识
- `timestamp`
- 类型:`string`
- 说明ISO 8601 时间(`date-time`
| **字段** | **类型** | **说明** |
| ----------- | --------- | ---------------------------------- |
| `item_id` | `string` | 物品 ID有目录时多为产品编码否则通常与名称或模型类名一致 |
| `item_name` | `string` | 物品名称 |
| `qty` | `integer` | 本条记录数量,当前恒为 `1`;一次识别或一次人工确认只追加一条明细 |
| `doctor_id` | `string` | 记账关联的医生或系统标识 |
| `timestamp` | `string` | ISO 8601 时间(`date-time` |
**`summary[]` 元素**
**`summary[]` 元素**
- `item_id`
- 类型:`string`
- 说明:与明细一致
- `item_name`
- 类型:`string`
- 说明:名称,通常取该 `item_id` 首条明细中的名称
- `total_quantity`
- 类型:`integer`
- 说明:该物品在本台手术中的合计数量,`>= 0`
| **字段** | **类型** | **说明** |
| ---------------- | --------- | -------------------------- |
| `item_id` | `string` | 与明细一致 |
| `item_name` | `string` | 名称,通常取该 `item_id` 首条明细中的名称 |
| `total_quantity` | `integer` | 该物品在本台手术中的合计数量,`>= 0` |
**状态码**
- `200`:至少有一条明细
- `422``surgery_id` 路径不符合约束
- `503``RESULT_NOT_READY`,当前无可用明细或不可查
| **HTTP** | **说明** |
| -------- | ------------------------------ |
| `200` | 至少有一条明细 |
| `422` | `surgery_id` 路径不符合约束 |
| `503` | `RESULT_NOT_READY`,当前无可用明细或不可查 |
**响应示例200**
```json
```
{
"surgery_id": "123456",
"status": "completed",
"message": "查询成功。",
"details": [
{
"item_id": "HC001",
"item_name": "纱布",
"item_id": "19246-3-14",
"item_name": "医用纱布敷料",
"qty": 1,
"doctor_id": "D1001",
"doctor_id": "6611",
"timestamp": "2026-04-21T10:30:00+08:00"
}
],
"summary": [
{
"item_id": "HC001",
"item_name": "纱布",
"item_id": "19246-3-14",
"item_name": "医用纱布敷料",
"total_quantity": 1
}
]
@@ -470,83 +320,73 @@ flowchart LR
**基本信息**
- 方法:`GET`
- 路径:`/client/surgeries/{surgery_id}/pending-confirmation`
- 路径参数:`surgery_id`
- 请求体:无
| **项目** | **内容** |
| -------- | ----------------------------------------------------- |
| **方法** | `GET` |
| **路径** | `/client/surgeries/{surgery_id}/pending-confirmation` |
| **路径参数** | `surgery_id` |
| **请求体** | 无 |
**业务说明**
- 返回当前 FIFO 队首的一条低置信度识别任务
- `prompt_audio_mp3_base64``prompt_text` 内容一致,为标准 Base64 的 MP3 字符串(无换行)
- 客户端解码后应按 `audio/mpeg` 播放
- 返回当前 FIFO 队首的一条低置信度识别任务
- `prompt_audio_mp3_base64` 与 `prompt_text` 内容一致,为标准 Base64 的 MP3 字符串(无换行)。
- 客户端解码后应按 `audio/mpeg` 播放。
**响应体200**
- `surgery_id`
- 类型:`string`
- 说明:手术号
- `confirmation_id`
- 类型:`string`
- 说明:待确认项 ID提交 5.6 节接口时原样放入路径
- `prompt_text`
- 类型:`string`
- 说明:播报或展示用语,与 MP3 内容一致
- `prompt_audio_mp3_base64`
- 类型:`string`
- 说明MP3 的 Base64
- `options`
- 类型:`array`
- 说明:候选项列表,字段见下文
- `model_top1_label`
- 类型:`string`
- 说明:模型原始 Top1 类名,可能不在本台候选内
- `model_top1_confidence`
- 类型:`number`
- 说明Top1 置信度
- `created_at`
- 类型:`string`
- 说明创建时间ISO 8601
| **字段** | **类型** | **说明** |
| ------------------------- | -------- | ------------------------- |
| `surgery_id` | `string` | 手术号 |
| `confirmation_id` | `string` | 待确认项 ID提交 5.6 节接口时原样放入路径 |
| `prompt_text` | `string` | 播报或展示用语,与 MP3 内容一致 |
| `prompt_audio_mp3_base64` | `string` | MP3 的 Base64 |
| `options` | `array` | 候选项列表,字段见下文 |
| `model_top1_label` | `string` | 模型原始 Top1 类名,可能不在本台候选内 |
| `model_top1_confidence` | `number` | Top1 置信度 |
| `created_at` | `string` | 创建时间ISO 8601 |
**`options[]` 元素**
**`options[]` 元素**
- `label`
- 类型:`string`
- 说明:展示给医生的选项名称
- `confidence`
- 类型:`number`
- 说明:该选项对应的置信度
| **字段** | **类型** | **说明** |
| ------------ | -------- | ---------- |
| `label` | `string` | 展示给医生的选项名称 |
| `confidence` | `number` | 该选项对应的置信度 |
**状态码**
- `200`:当前有一条待确认
- `404`:无待确认或手术未活跃;常见错误码为 `NO_PENDING_CONFIRMATION`
- `422`:例如话术为空导致无法 TTS错误码见响应`TTS_TEXT_EMPTY`
- `503`:语音服务未配置或 TTS 失败;例如 `BAIDU_NOT_CONFIGURED``TTS_ERROR`
| **HTTP** | **说明** |
| -------- | ----------------------------------------------------- |
| `200` | 当前有一条待确认 |
| `404` | 无待确认或手术未活跃;常见错误码为 `NO_PENDING_CONFIRMATION` |
| `422` | 例如话术为空导致无法 TTS错误码见响应 `TTS_TEXT_EMPTY` |
| `503` | 语音服务未配置或 TTS 失败;例如 `BAIDU_NOT_CONFIGURED``TTS_ERROR` |
### 5.6 提交待确认结果(医生语音)
**基本信息**
- 方法:`POST`
- 路径:`/client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve`
- Content-Type`multipart/form-data`
| **项目** | **内容** |
| ---------------- | ------------------------------------------------------------------------------- |
| **方法** | `POST` |
| **路径** | `/client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve` |
| **Content-Type** | `multipart/form-data` |
**路径参数**
- `surgery_id`
- 约束6 位数字
- 说明:同 5.0 节
- `confirmation_id`
- 约束:长度 1 到 128
- 说明:与 5.5 节响应中的 `confirmation_id` 一致
| **参数** | **约束** | **说明** |
| ----------------- | ---------- | -------------------------------- |
| `surgery_id` | 6 位数字 | 同 5.0 节 |
| `confirmation_id` | 长度 1 到 128 | 与 5.5 节响应中的 `confirmation_id` 一致 |
**请求体multipart**
- `audio`
- 类型:`file`
- 必填:是
- 说明:单个 `.wav` 文件;建议使用 16 kHz 单声道 PCM`.wav` 扩展名会返回 `422`
| **字段名** | **类型** | **必填** | **说明** |
| ------- | ------ | ------ | ------------------------------------------------------ |
| `audio` | `file` | 是 | 单个 `.wav` 文件;建议使用 16 kHz 单声道 PCM `.wav` 扩展名会返回 `422` |
**业务说明**
@@ -554,43 +394,31 @@ flowchart LR
**响应体200**
- `surgery_id`
- 类型:`string`
- 说明:手术号
- `confirmation_id`
- 类型:`string`
- 说明:待确认 ID
- `status`
- 类型:`string`
- 说明:成功时为 `accepted`
- `message`
- 类型:`string`
- 说明:说明
- `resolved_label`
- 类型:`string | null`
- 说明:确认后的耗材名称;否认全部候选时为 `null`
- `rejected`
- 类型:`boolean`
- 说明:是否否认全部候选,不记消耗时为 `true`
- `asr_text`
- 类型:`string | null`
- 说明:语音识别文本
- `audio_object_key`
- 类型:`string | null`
- 说明:对象存储中的原始 WAV 键,便于追溯
| **字段** | **类型** | **说明** |
| ------------------ | ---------------- | ------------------------ |
| `surgery_id` | `string` | 手术号 |
| `confirmation_id` | `string` | 待确认 ID |
| `status` | `string` | 成功时为 `accepted` |
| `message` | `string` | 说明 |
| `resolved_label` | `string \| null` | 确认后的耗材名称;否认全部候选时为 `null` |
| `rejected` | `boolean` | 是否否认全部候选,不记消耗时为 `true` |
| `asr_text` | `string \| null` | 语音识别文本 |
| `audio_object_key` | `string \| null` | 对象存储中的原始 WAV 键,便于追溯 |
**状态码**
- `200`:已受理并完成解析
- `404`:确认项不存在或手术未活跃;例如 `CONFIRMATION_NOT_FOUND`
- `409`:当前确认项已处理过;例如 `CONFIRMATION_ALREADY_RESOLVED`
- `422`:空文件、非 `.wav``VOICE_AUDIO_INVALID`、ASR/解析失败等,具体错误码见响应
- `503`MinIO、百度等依赖不可用;例如 `MINIO_NOT_CONFIGURED``MINIO_UPLOAD_FAILED``BAIDU_NOT_CONFIGURED`
| **HTTP** | **说明** |
| -------- | ------------------------------------------------------------------------------------- |
| `200` | 已受理并完成解析 |
| `404` | 确认项不存在或手术未活跃;例如 `CONFIRMATION_NOT_FOUND` |
| `409` | 当前确认项已处理过;例如 `CONFIRMATION_ALREADY_RESOLVED` |
| `422` | 空文件、非 `.wav``VOICE_AUDIO_INVALID`、ASR/解析失败等,具体错误码见响应 |
| `503` | MinIO、百度等依赖不可用例如 `MINIO_NOT_CONFIGURED``MINIO_UPLOAD_FAILED``BAIDU_NOT_CONFIGURED` |
**cURL 示例**
```bash
```
curl -sS -X POST \
"http://<主机>:38080/client/surgeries/123456/pending-confirmation/<confirmation_id>/resolve" \
-F "audio=@/path/to/voice.wav;type=audio/wav"
```
```