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

Made-with: Cursor
2026-04-23 20:42:21 +08:00

16 KiB
Raw Blame History

<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 约定

  • startend 使用 POST + application/json
  • result 使用 GET
  • resolve 使用 POST + multipart/form-data
  • surgery_id 固定为 6 位数字,正则为 ^\d{6}$
  • resolve 路径中的 confirmation_id 必须与待确认接口返回值一致
  • camera_ids 必须与第 2 节清单及运维配置完全一致

1. 服务与基础信息

  • 协议: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

配置格式示例见 app/resources/camera_rtsp_urls.sample.json。配置项细节见 .env.exampledocs/video-backends.md

摄像头映射示例

  • or-cam-01
    • RTSPrtsp://...(由现场或 NVR 文档整理后交给运维)
    • 备注:术间、机位
  • or-cam-02
    • RTSP...
    • 备注:...

[!warning] 对接要求

  • camera_ids 必须与运维配置中的 key 完全一致
  • RTSP 不应硬编码在客户端业务程序中

[!tip] 联调建议

  • 运维配置完成后,客户端使用上面清单中的 camera_id 调用 start 验证是否返回 200
  • 若返回 503detail.code = RECORDING_CANNOT_START,优先核对 ID 拼写以及监控服务器侧网络连通性

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

4. 流程

4.1 时序图

sequenceDiagram
    participant Client as 客户系统
    participant API as 监控服务 API

    Client->>API: POST /client/surgeries/start
    Note over Client,API: body: surgery_id, camera_ids, candidate_consumables
    API-->>Client: 200 accepted开录已确认

    par 术中
        loop 轮询结果
            Client->>API: GET .../result
            API-->>Client: 200 或 503 RESULT_NOT_READY
        end
        loop 轮询待确认(若启用)
            Client->>API: GET .../pending-confirmation
            API-->>Client: 200 或 404
            opt 有待确认
                Client->>Client: 播放 prompt_audio_mp3_base64
                Client->>API: POST .../resolvemultipart audio
                API-->>Client: 200 accepted
            end
        end
    end

    Client->>API: POST /client/surgeries/end
    API-->>Client: 200 accepted停录已确认

    Client->>API: GET .../result
    API-->>Client: 200持久化后可查时返回

4.2 状态图

flowchart LR
    A[未开始] -->|start 200| B[录制 / 推理中]
    B -->|result 200| C[有消耗数据可查]
    B -->|pending 200| D[待确认]
    D -->|resolve| B
    B -->|end 200| E[已结束]
    E -->|result 200| C

5. 接口详情

以下按“基本信息 -> 请求 -> 响应 -> 状态码”组织,与 OpenAPI 中 tags: clienthealth 一致。

5.0 通用约定

路径参数 surgery_id

  • 长度:固定 6
  • 字符集:仅数字
  • 正则:^\d{6}$

业务错误响应

多数业务失败在 4xx5xx 下返回如下 JSON

{
  "detail": {
    "code": "错误码字符串",
    "message": "人类可读说明",
    "surgery_id": "123456"
  }
}

5.1 探活

基本信息

  • 方法:GET
  • 路径:/health
  • 请求体:无

响应说明

  • 200
    • 说明:进程正常且数据库可连通
    • 响应体示例:{"status":"ok","database":"connected"}
  • 503
    • 说明:数据库不可用(降级)
    • 响应体示例:{"status":"degraded","database":"unavailable"}

5.2 开始手术

基本信息

  • 方法:POST
  • 路径:/client/surgeries/start
  • Content-Typeapplication/json; charset=utf-8

业务说明

  • 服务端会为 camera_ids 中的每个摄像头建立拉流与推理任务,只有在确认开录成功(如首帧就绪)后才返回 HTTP 200
  • 若同一 surgery_id 存在尚未落库的历史归档,服务端会先尝试写入数据库;失败时可能返回 503(如 RECORDING_CANNOT_START),以避免静默丢数据
  • candidate_consumables 为空时,服务端会展开为目录 Excel 中的全部商品名,或在未配置目录时展开为分类模型的全部类名

请求体JSON

  • surgery_id
    • 类型:string
    • 必填:是
    • 说明6 位数字,与路径规则一致
  • camera_ids
    • 类型:string[]
    • 必填:是
    • 说明:至少 1 个;必须与运维配置的摄像头 ID 完全一致,见第 2 节
  • candidate_consumables
    • 类型:string[]
    • 必填:否
    • 说明:非空时仅这些名称参与自动记账与待确认;缺省或 [] 时使用全部候选

响应体200

  • surgery_id
    • 类型:string
    • 说明:与请求一致
  • status
    • 类型:string
    • 说明:成功时通常为 accepted
  • message
    • 类型:string
    • 说明:说明文案

状态码

  • 200:开录已确认
  • 422:参数校验失败,例如 surgery_id 非 6 位或 camera_ids 为空数组
  • 503:开录未确认或录制子系统故障;detail.code 常见为 RECORDING_CANNOT_START

请求示例

{
  "surgery_id": "123456",
  "camera_ids": ["or-cam-01", "or-cam-02"],
  "candidate_consumables": ["纱布", "缝线", "止血钳"]
}

响应示例200

{
  "surgery_id": "123456",
  "status": "accepted",
  "message": "摄像头录制已开始,手术已启动。"
}

5.3 结束手术

基本信息

  • 方法:POST
  • 路径:/client/surgeries/end
  • Content-Typeapplication/json; charset=utf-8

业务说明

停止该 surgery_id 关联的全部摄像头任务,只有在确认停录完成后才返回 200

请求体JSON

  • surgery_id
    • 类型:string
    • 必填:是
    • 说明6 位数字

响应体200

字段含义与 5.2 节一致,message 示例为 摄像头录制已停止,手术已结束。

状态码

  • 200:停录已确认
  • 422:参数校验失败
  • 503:停录未确认或故障;detail.code 常见为 RECORDING_NOT_STOPPED

请求示例

{
  "surgery_id": "123456"
}

5.4 查询手术结果

基本信息

  • 方法:GET
  • 路径:/client/surgeries/{surgery_id}/result
  • 路径参数:surgery_id
  • 请求体:无

业务说明

  • 仅当存在至少一条消耗明细时返回 200
  • 无明细(包括已归档但零消耗)、手术未开始、未成功开录或当前尚不可查时,返回 503
  • 上述 503 场景的常见错误码为 RESULT_NOT_READY

响应体200

  • surgery_id
    • 类型:string
    • 说明:手术号
  • status
    • 类型:string
    • 说明:成功时通常为 completed
  • message
    • 类型:string
    • 说明:说明
  • details
    • 类型:array
    • 说明:消耗明细列表,字段见下文
  • summary
    • 类型:array
    • 说明:按 item_id 汇总的结果,字段见下文

details[] 元素

  • item_id
    • 类型:string
    • 说明:物品 ID有目录时多为产品编码否则通常与名称或模型类名一致
  • item_name
    • 类型:string
    • 说明:物品名称
  • qty
    • 类型:integer
    • 说明:本条记录数量,当前恒为 1;一次识别或一次人工确认只追加一条明细
  • doctor_id
    • 类型:string
    • 说明:记账关联的医生或系统标识
  • timestamp
    • 类型:string
    • 说明ISO 8601 时间(date-time

summary[] 元素

  • item_id
    • 类型:string
    • 说明:与明细一致
  • item_name
    • 类型:string
    • 说明:名称,通常取该 item_id 首条明细中的名称
  • total_quantity
    • 类型:integer
    • 说明:该物品在本台手术中的合计数量,>= 0

状态码

  • 200:至少有一条明细
  • 422surgery_id 路径不符合约束
  • 503RESULT_NOT_READY,当前无可用明细或不可查

响应示例200

{
  "surgery_id": "123456",
  "status": "completed",
  "message": "查询成功。",
  "details": [
    {
      "item_id": "HC001",
      "item_name": "纱布",
      "qty": 1,
      "doctor_id": "D1001",
      "timestamp": "2026-04-21T10:30:00+08:00"
    }
  ],
  "summary": [
    {
      "item_id": "HC001",
      "item_name": "纱布",
      "total_quantity": 1
    }
  ]
}

5.5 拉取待确认耗材

基本信息

  • 方法:GET
  • 路径:/client/surgeries/{surgery_id}/pending-confirmation
  • 路径参数:surgery_id
  • 请求体:无

业务说明

  • 返回当前 FIFO 队首的一条低置信度识别任务
  • prompt_audio_mp3_base64prompt_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

options[] 元素

  • label
    • 类型:string
    • 说明:展示给医生的选项名称
  • confidence
    • 类型:number
    • 说明:该选项对应的置信度

状态码

  • 200:当前有一条待确认
  • 404:无待确认或手术未活跃;常见错误码为 NO_PENDING_CONFIRMATION
  • 422:例如话术为空导致无法 TTS错误码见响应TTS_TEXT_EMPTY
  • 503:语音服务未配置或 TTS 失败;例如 BAIDU_NOT_CONFIGUREDTTS_ERROR

5.6 提交待确认结果(医生语音)

基本信息

  • 方法:POST
  • 路径:/client/surgeries/{surgery_id}/pending-confirmation/{confirmation_id}/resolve
  • Content-Typemultipart/form-data

路径参数

  • surgery_id
    • 约束6 位数字
    • 说明:同 5.0 节
  • confirmation_id
    • 约束:长度 1 到 128
    • 说明:与 5.5 节响应中的 confirmation_id 一致

请求体multipart

  • audio
    • 类型:file
    • 必填:是
    • 说明:单个 .wav 文件;建议使用 16 kHz 单声道 PCM.wav 扩展名会返回 422

业务说明

音频上传至对象存储后执行 ASR 和候选解析。若识别为确认某个候选项,则记一条消耗;若识别为否认全部候选,则不记消耗。

响应体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 键,便于追溯

状态码

  • 200:已受理并完成解析
  • 404:确认项不存在或手术未活跃;例如 CONFIRMATION_NOT_FOUND
  • 409:当前确认项已处理过;例如 CONFIRMATION_ALREADY_RESOLVED
  • 422:空文件、非 .wavVOICE_AUDIO_INVALID、ASR/解析失败等,具体错误码见响应
  • 503MinIO、百度等依赖不可用例如 MINIO_NOT_CONFIGUREDMINIO_UPLOAD_FAILEDBAIDU_NOT_CONFIGURED

cURL 示例

curl -sS -X POST \
  "http://<主机>:38080/client/surgeries/123456/pending-confirmation/<confirmation_id>/resolve" \
  -F "audio=@/path/to/voice.wav;type=audio/wav"