Files
operating-room-monitor-server/docs/客户端手术通信接口说明.md
Kevin 6b3adb4ad8 feat: 站点 JSON、语音终端 WebSocket 指派与客户端联调
- 用 OR_SITE_CONFIG_JSON_FILE 统一术间配置(video_rtsp_urls + voice_or_room_bindings)
- VoiceTerminalHub:assignment、WS 推送与 HTTP 查询;开录/停录后 notify
- 一键联调 orchestrate-and-start 与 /client/surgeries/start 共用指派逻辑,修复 demo 路径不发 WS
- 语音桌面端:SIGINT 退出、shutdown 清理、仅 WS 指派、固定 pending 轮询间隔、界面仅保留录音时长
- 新增/调整契约与绑定测试,文档与示例配置同步

Made-with: Cursor
2026-04-27 11:21:16 +08:00

21 KiB
Raw Permalink Blame History

手术室监控服务:客户端手术通信接口说明

能力概览

能力 说明
探活 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

2. 摄像头 ID 与 RTSP

RTSP 地址、账号、口令等由客户端对接工程师提供给服务端运维,运维再写入服务端环境。客户端只在 POST /client/surgeries/start 中传 camera_ids

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 提交医生答复
7 GET /client/voice-terminals/{terminal_id}/assignment 可选:查询当前指派(调试或简易集成;官方桌面客户端仅用 WebSocket
8 WS /client/voice-terminals/ws?terminal_id=... 语音桌面终端长连接,接收开录/停录指派(推荐

术间与语音终端绑定(服务端配置)

  • 唯一配置源:环境变量 OR_SITE_CONFIG_JSON_FILE 指向手术室 站点 JSONUTF-8须同时包含 video_rtsp_urlsvoice_or_room_bindings(见仓库 app/resources/or_site_config.sample.json)。voice_or_room_bindings 为数组,每项含 or_room_idcamera_idsvoice_terminal_idcamera_ids 在数组内须唯一,voice_terminal_id 全局唯一。
  • POST /client/surgeries/startHTTP 200 且开录已成功 后:用请求体中的 camera_idsvoice_or_room_bindings 中解析终端(精确匹配术间 camera 集合,或 开录路集为某术间 camera 集合的子集 时匹配该术间);命中则向对应 voice_terminal_id 推送 action":"start"(并更新 assignment未配置站点文件、或数组为空、或未命中则仅打日志不影响 200。
  • POST /client/surgeries/end 在停录 HTTP 200 后:向该手术会话记录的终端推送 action":"end"(并清除 assignment
  • 推送 JSON 形如:{"type":"voice_assignment","action":"start"|"end","surgery_id":"123456"}
  • 多 worker:当前实现为进程内内存;多 Uvicorn worker 时需 sticky session 或 Redis 等另行同步。

4. 流程

4.1 时序图

sequenceDiagram
    participant Client as 客户端
    participant Server as 服务端

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

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

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

    Client->>Server: GET .../result
    Server-->>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. 接口详情

路径参数 surgery_id

约束项 说明
长度 固定 6
字符集 仅数字
正则 ^\d{6}$

业务错误响应

多数业务失败在 4xx 或 5xx 下返回如下 JSON

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

5.1 探活

基本信息

项目 内容
方法 GET
路径 /health
请求体

响应说明

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

5.2 开始手术

基本信息

项目 内容
方法 POST
路径 /client/surgeries/start
Content-Type application/json; charset=utf-8

业务说明

  • 服务端会为 camera_ids 中的每个摄像头建立拉流与推理任务,只有在确认开录成功(如首帧就绪)后才返回 HTTP 200

  • candidate_consumables 为空时,服务端会展开为目录中的全部耗材名。

请求体JSON

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

响应体200

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

状态码

HTTP 说明
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-Type application/json; charset=utf-8

业务说明

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

请求体JSON

字段 类型 必填 说明
surgery_id string 6 位数字

响应体200

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

状态码

HTTP 说明
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

状态码

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

响应示例200

{
  "surgery_id": "123456",
  "status": "completed",
  "message": "查询成功。",
  "details": [
    {
      "item_id": "19246-3-14",
      "item_name": "医用纱布敷料",
      "qty": 1,
      "doctor_id": "6611",
      "timestamp": "2026-04-21T10:30:00+08:00"
    }
  ],
  "summary": [
    {
      "item_id": "19246-3-14",
      "item_name": "医用纱布敷料",
      "total_quantity": 1
    }
  ]
}

5.5 拉取待确认耗材

基本信息

项目 内容
方法 GET
路径 /client/surgeries/{surgery_id}/pending-confirmation
路径参数 surgery_id
请求体

业务说明

  • 返回当前 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

options[] 元素

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

状态码

HTTP 说明
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-Type multipart/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 键,便于追溯

状态码

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

5.7 语音终端 assignmentHTTP可选

路径 GET /client/voice-terminals/{terminal_id}/assignment

仓库内 手术室耗材语音确认桌面客户端 仅通过 §5.8 WebSocket 接收指派,不调用本接口。此处供运维脚本、未实现 WS 的第三方临时拉取 active_surgery_id

响应 200

字段 类型 说明
voice_terminal_id string 与路径一致
active_surgery_id string | null 当前指派手术 6 位号;无指派时为 null

5.8 语音终端 WebSocket

路径 GET ws://<主机>:<端口>/client/voice-terminals/ws?terminal_id=<终端ID>HTTPS 部署时使用 wss://

说明

  • 连接成功后,若服务端已有该终端的 assignment会立即收到一条 action":"start" 的 JSON与下文推送格式一致
  • 术中由服务端在 start / end 成功后 向已连接终端推送 JSON{"type":"voice_assignment","action":"start"|"end","surgery_id":"123456"}
  • 客户端可发送任意文本作心跳;服务端当前仅依赖 WebSocket 协议级 ping由网关或客户端库实现