- Add FastAPI routes for surgery start/end, results, pending confirmation (WAV upload), and health checks. - Implement RTSP/Hikvision capture, consumable classification, session manager, MinIO/Baidu voice resolution, and DB persistence. - Add documentation (client API, video backends, staging checklist) and sample camera/RTSP config. - Add pytest suite (API contract, session manager, voice, repositories, pipeline persistence) and httpx dev dependency. - Replace deprecated HTTP_422_UNPROCESSABLE_ENTITY with HTTP_422_UNPROCESSABLE_CONTENT. - Fix SurgeryPipeline DB reads to use an explicit transaction with autobegin disabled. Made-with: Cursor
17 KiB
手术室监控服务 · 客户端手术通信接口说明
本文档描述客户端与手术室监控服务端之间,围绕「单台手术生命周期」进行通信的 HTTP 接口。便于联调、评审与对外同步;与 OpenAPI(Swagger)中的定义一致。
1. 概述
| 能力 | 说明 |
|---|---|
| 开始手术 | 请求开始手术;服务端启动摄像头录制,仅在确认开录完成后返回 HTTP 200。 |
| 结束手术 | 请求结束手术;服务端停止摄像头录制,仅在确认停录完成后返回 HTTP 200。 |
| 查询结果 | 根据手术 6 位号查询消耗明细与汇总;仅在已开录且至少已有一条消耗明细后返回 HTTP 200。 |
| 待确认耗材 | 低置信度时服务端排队一条待确认任务;客户端拉取话术(TTS)并在医生确认后回传,不阻塞后续视频推理。 |
约定:
- 开始 / 结束 使用
POST,请求体为 JSON(Content-Type: application/json)。 - 查询结果 使用
GET,无请求体;手术号放在 URL 路径 中(见 4.3),符合「只读资源用 GET」的惯例。 - 手术标识
**surgery_id**:必须为 恰好 6 位数字(正则^\d{6}$),例如123456。
2. 基础信息
| 项目 | 说明 |
|---|---|
| 协议 | HTTP/HTTPS |
| 请求体格式 | 开始/结束:application/json;查询结果:无 body |
| 响应体格式 | JSON |
| 路径前缀 | 服务端根路径下直接挂载,例如 https://<主机>:<端口>/client/surgeries/... |
| 默认服务端口(开发) | 38080(以实际部署为准) |
说明: 若生产环境存在网关或反向代理,请将上表中的「主机、端口、是否 HTTPS」替换为对外统一入口地址。
3. 接口列表
| 序号 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 1 | POST |
/client/surgeries/start |
开始手术 |
| 2 | POST |
/client/surgeries/end |
结束手术 |
| 3 | GET |
/client/surgeries/{surgery_id}/result |
查询手术结果 |
| 4 | GET |
/client/surgeries/{surgery_id}/pending-confirmation |
拉取一条待确认耗材 |
| 5 | POST |
/client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve |
提交医生确认结果 |
4. 接口详情
4.1 开始手术
用途: 在手术开始时,由客户端向服务端上报手术编号、参与采集的摄像头,以及本台手术可能涉及的耗材清单;服务端启动关联摄像头录制。
成功条件(HTTP 200): 仅在服务端确认摄像头已开始录制之后,才返回 HTTP 200。不得在「仅收到请求、尚未开录」时返回 200。
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | /client/surgeries/start |
请求体字段:
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
surgery_id |
string |
是 | 手术 6 位号,必须为 6 位数字。 |
camera_ids |
string[] |
是 | 摄像头 ID 列表,至少 1 个元素;须与服务端配置的 RTSP 映射键一致(示例见 app/resources/camera_rtsp_urls.sample.json)。 |
candidate_consumables |
string[] |
否 | 本台手术允许记账的耗材名称清单。为空或未传则不会写入任何消耗(仅拉流推理);非空时自动记账与待确认仅针对清单内名称。 |
说明: 若该 surgery_id 在服务端仍存在尚未写入数据库的上一台手术内存归档,开始新会话前会先尝试落库;落库失败则返回 503(RECORDING_CANNOT_START),避免静默丢失数据。
请求示例:
{
"surgery_id": "123456",
"camera_ids": ["or-cam-01", "or-cam-02"],
"candidate_consumables": ["纱布", "缝线", "止血钳"]
}
成功响应(HTTP 200): 表示开录已确认。
| 字段名 | 类型 | 说明 |
|---|---|---|
surgery_id |
string |
回显手术 6 位号。 |
status |
string |
处理状态(例如 accepted 表示开录已确认)。 |
message |
string |
人类可读的说明文案。 |
响应示例:
{
"surgery_id": "123456",
"status": "accepted",
"message": "摄像头录制已开始,手术已启动。"
}
重试: 开录调用失败时,服务端会按配置自动重试若干次(间隔若干秒);全部尝试仍失败后再返回 HTTP 503。环境变量:SURGERY_RECORDING_MAX_ATTEMPTS(默认 3,含首次)、SURGERY_RECORDING_RETRY_DELAY_SECONDS(默认 1.0)。
失败响应(HTTP 503): 重试用尽仍无法在约定条件下确认开录时返回。响应体见 §5.2(OpenAPI 模型 SurgeryClientErrorResponse,错误码示例:RECORDING_CANNOT_START);detail.message 中会注明已重试次数。
4.2 结束手术
用途: 在手术结束时,由客户端请求服务端结束该 surgery_id 对应手术:服务端须停止关联摄像头的录制。
成功条件(HTTP 200): 仅在服务端确认所有关联摄像头已停止录制之后,才返回 HTTP 200。不得在「仅收到请求、尚未停录」时返回 200。
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | /client/surgeries/end |
请求体字段:
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
surgery_id |
string |
是 | 手术 6 位号,必须为 6 位数字。 |
请求示例:
{
"surgery_id": "123456"
}
成功响应(HTTP 200): 表示停录已完成。
| 字段名 | 类型 | 说明 |
|---|---|---|
surgery_id |
string |
回显手术 6 位号。 |
status |
string |
处理状态(例如 accepted 表示停录已确认)。 |
message |
string |
人类可读的说明文案。 |
响应示例:
{
"surgery_id": "123456",
"status": "accepted",
"message": "摄像头录制已停止,手术已结束。"
}
重试: 停录调用失败时,服务端会按配置自动重试(与开始手术相同的环境变量);全部尝试仍失败后再返回 HTTP 503。
失败响应(HTTP 503): 重试用尽仍无法确认停录完成时返回。响应体见 §5.2(错误码示例:RECORDING_NOT_STOPPED);detail.message 中会注明已重试次数。
4.3 查询手术结果
用途: 根据 surgery_id 查询该台手术下耗材消耗明细及按物品汇总。
成功条件(HTTP 200): 仅在已开录且至少已有一条消耗明细(自动识别或医生确认)之后返回 HTTP 200 及 details / summary。若已开录但尚无明细,返回 503(见 §5.2,错误码 RESULT_NOT_READY)。
| 项目 | 内容 |
|---|---|
| 方法 | GET |
| 路径 | /client/surgeries/{surgery_id}/result |
路径参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
surgery_id |
string |
是 | 手术 6 位号,必须为 6 位数字,出现在 URL 路径中。 |
请求示例:
GET /client/surgeries/123456/result HTTP/1.1
Host: <主机>:<端口>
(浏览器或客户端直接访问完整 URL 即可,例如 https://<主机>:<端口>/client/surgeries/123456/result。)
成功响应(HTTP 200):
| 字段名 | 类型 | 说明 |
|---|---|---|
surgery_id |
string |
手术 6 位号。 |
status |
string |
成功时一般为 completed(以服务端约定为准)。 |
message |
string |
说明信息。 |
details |
数组 | 消耗明细:按事件发生,可能有多行;每行含物品、数量、医生、时间。 |
summary |
数组 | 按物品汇总:同一 item_id 在 details 中 quantity 的合计,便于客户端直接展示总计。 |
**details[] 中每一项(明细行):**
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
item_id |
string |
是 | 物品 ID。 |
item_name |
string |
是 | 物品名称。 |
quantity |
integer |
是 | 本条记录对应的消耗数量(非负整数)。 |
doctor_id |
string |
是 | 医生 ID。 |
timestamp |
string |
是 | 记录时间,ISO 8601(JSON 中为 ISO 格式字符串,与 OpenAPI 中 date-time 一致)。 |
source |
string |
否 | vision 自动识别;voice 医生通过待确认接口确认。 |
**summary[] 中每一项(汇总行):**
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
item_id |
string |
是 | 物品 ID。 |
item_name |
string |
是 | 物品名称(与明细中该 ID 首次出现时的名称一致,具体规则以服务端为准)。 |
total_quantity |
integer |
是 | 该物品在本台手术中的消耗数量合计。 |
约定: summary 应由服务端根据 details 按 item_id 汇总得到,保证与明细一致。
响应示例:
{
"surgery_id": "123456",
"status": "completed",
"message": "查询成功。",
"details": [
{
"item_id": "HC001",
"item_name": "纱布",
"quantity": 2,
"doctor_id": "D1001",
"timestamp": "2026-04-21T10:30:00+08:00"
},
{
"item_id": "HC001",
"item_name": "纱布",
"quantity": 1,
"doctor_id": "D1002",
"timestamp": "2026-04-21T11:05:00+08:00"
},
{
"item_id": "HC002",
"item_name": "缝线",
"quantity": 1,
"doctor_id": "D1001",
"timestamp": "2026-04-21T10:45:00+08:00"
}
],
"summary": [
{ "item_id": "HC001", "item_name": "纱布", "total_quantity": 3 },
{ "item_id": "HC002", "item_name": "缝线", "total_quantity": 1 }
]
}
4.4 拉取待确认耗材
用途: 当模型置信度不足但存在候选时,服务端将任务放入 FIFO 队列。客户端轮询本接口获取队首一条待确认项,使用 prompt_text 进行 TTS 播报,并由医生口述选择;服务端视频推理不等待本步骤。
成功条件(HTTP 200): 当前手术进行中且队列非空。
失败(HTTP 404): 无待确认项或手术未在进行。detail.code 示例:NO_PENDING_CONFIRMATION。
| 项目 | 内容 |
|---|---|
| 方法 | GET |
| 路径 | /client/surgeries/{surgery_id}/pending-confirmation |
响应字段(节选): confirmation_id、prompt_text、options[](label + confidence)、model_top1_label、model_top1_confidence、created_at。
4.5 提交耗材确认结果
用途: 客户端采集医生回答的 WAV 音频并上传;服务端将音频存入 MinIO、调用百度 ASR 识别、解析 4.4 返回的候选项;确认则记一条 source=voice 的消耗明细,否认则关闭该待确认项且不记账。
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | /client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve |
Content-Type |
multipart/form-data |
请求体(multipart):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
audio |
文件 | 是 | 医生语音 .wav;建议 16 kHz 单声道 PCM,其他格式服务端可尝试用 ffmpeg 转码。 |
成功响应(HTTP 200): SurgeryPendingConfirmationResolveResponse:resolved_label、rejected、asr_text、audio_object_key 等(与 OpenAPI 一致)。
错误: 404(项不存在或手术未活跃)、409(已处理)、422(空文件、非 .wav、ASR/解析失败等业务码见 detail.code)、503(MinIO/百度未配置或上传失败等)。
说明: 人工追问的 TTS 播报由客户端根据 4.4 的
prompt_text完成;服务端不要求部署扬声器/麦克风。
5. 错误与校验
5.1 参数校验(HTTP 422)
当参数不符合约束时(例如 surgery_id 不是 6 位数字、开始手术时 camera_ids 为空数组等),服务端通常返回 HTTP 422,响应体为 FastAPI/Pydantic 风格的校验错误详情。
建议在客户端侧对 surgery_id 先做本地校验,减少无效请求。
5.2 业务未就绪(HTTP 503)
当成功条件未满足(开录/停录未确认、或查询结果时算法结果尚未就绪)时,服务端返回 HTTP 503,响应体为 JSON,且 **detail 为对象**(与 OpenAPI 中的 **SurgeryClientErrorResponse** / **SurgeryClientErrorDetail** 一致):
| 字段 | 类型 | 说明 |
|---|---|---|
detail.code |
string |
业务错误码,如 RECORDING_CANNOT_START、RECORDING_NOT_STOPPED、RESULT_NOT_READY。 |
detail.message |
string |
人类可读说明。 |
detail.surgery_id |
string |
手术 6 位号。 |
示例:
{
"detail": {
"code": "RESULT_NOT_READY",
"message": "仅在已开录且算法已产生可查询的实时计算结果后返回 HTTP 200;当前条件不满足。",
"surgery_id": "123456"
}
}
6. 实现与演进说明(给阅读者)
- 开始 / 结束 / 查询结果 与录制、算法流水线的具体绑定以实现为准;未满足约定条件时不返回 200(见各节成功条件),与 OpenAPI(
/docs或/openapi.json) 中声明的 200 / 503 / 422 一致。 - 人工确认由客户端完成 TTS 与拾音(ASR);服务端只提供结构化候选与话术,不要求部署环境具备扬声器/麦克风。
- 接入真实子系统后,仍应保持:成功响应体与
SurgeryApiResponse、SurgeryResultResponse模型一致;503 与SurgeryClientErrorResponse一致。
联调时请以 OpenAPI 文档(如 /docs)为准,本文档与之同步维护。
7. 文档修订
| 版本 | 日期 | 说明 |
|---|---|---|
| 1.6 | 2026-04-21 | 待确认耗材接口;候选清单硬约束;查询结果需至少一条明细;客户端侧人工确认。 |
| 1.5 | 2026-04-21 | 开始/结束手术:录制流水线失败时重试,仍失败再 503;可配置 SURGERY_RECORDING_*。 |
| 1.4 | 2026-04-21 | 与 OpenAPI 对齐:开始/结束/查询的 200/503 条件及 SurgeryClientErrorResponse。 |
| 1.3 | 2026-04-21 | 结束手术:仅在实际停录确认后返回 HTTP 200;否则 503。 |
| 1.2 | 2026-04-21 | 查询结果响应增加 details(物品 id/名称/数量/医生/时间)与 summary(按物品汇总)。 |
| 1.1 | 2026-04-21 | 查询结果改为 GET /client/surgeries/{surgery_id}/result。 |
| 1.0 | 2026-04-21 | 初版,POST /client/surgeries/start、POST /client/surgeries/end。 |