四、门诊场景流程:端到端流程(Go 视角)
我们将详细描述一次完整的门诊,数据和 Go 服务是如何流动的。
Step 1: 医生准备,发起会话
- 医生操作:在医生工作站打开患者的就诊页面,点击“开始智能记录”。
-
前端动作:携带本地存储的
doctor_token,调用POST /api/v1/sessions。 -
gateway-service:- 校验
doctor_token的有效性。 - 通过
doctor_id从Casbin策略中检查,该医生是否有操作patient_id的权限。 - 请求转发至
session-service。
- 校验
-
session-service(gRPC):- 接收
CreateSessionRequest(包含patient_id,dept_id,outpatient类型)。 - 在 PostgreSQL 中创建一条
Session记录,状态为ACTIVE。 - 生成一个全局唯一的
session_id(e.g., UUID)。 - 构建录音文件的未来存储路径,如
recordings/2023/10/27/{session_id}.webm。 - 返回
CreateSessionResponse,包含session_id和为本次连接分配的 WebSocket URL。
- 接收
-
前端:收到响应后,使用
session_id建立到wss://gateway/asr/{session_id}的 WebSocket 连接。
Step 2: 问诊进行中,实时转写
-
音频采集:浏览器通过
getUserMediaAPI 获取麦克风音频流,编码为 Opus 或 PCM 格式,通过 WebSocket 实时发送。 -
gateway-service:仅做 WebSocket 连接的升级和负载均衡,将 TCP 连接代理到后端的asr-adapter-service实例。 -
asr-adapter-service(核心 Goroutine 逻辑):- 连接管理:为每个 WebSocket 连接创建一个 Goroutine。
- 音频转发:循环读取 WebSocket 音频帧,打包成 ASR 引擎(如阿里云)API 要求的格式,通过 HTTP Streaming 或 gRPC Stream 发送。
-
结果接收与处理:
- 异步接收 ASR 引擎返回的
partial_result(部分转写)和final_result(最终结果)。 -
partial_result:立即通过 WebSocket 推回前端,实现“实时字幕”效果。 -
final_result:这是一个完整的句子或段落。它会附带时间戳信息(相对于音频开始的时间)和可能的说话人标签(如果 ASR 支持声纹分离)。asr-adapter-service将其封装成一个TranscriptionEvent消息,发布到 NATS 的transcription.finished主题。
- 异步接收 ASR 引擎返回的
-
session-service(NATS Consumer):- 订阅
transcription.finished主题。 - 消费消息,解析出
session_id和final_result。 - 将这条转写记录作为一条
Turn插入到数据库的transcriptions表中,并与session_id关联。
- 订阅
Step 3: 问诊结束,触发 AI 生成
- 医生操作:点击“结束记录”按钮。
-
前端动作:发送一个 WebSocket Close 消帧,并调用
POST /api/v1/sessions/{sessionId}/end。 -
session-service:- 将
Session记录的状态更新为ENDED,并记录end_time。 - 向 NATS 的
session.ended主题发布一条消息,包含session_id。
- 将
-
nlp-medical-service(NATS Consumer):- 订阅
session.ended主题。 - 收到消息后,开始执行核心的病历生成流程。
-
数据拉取:调用
session-service的 gRPC 接口GetSessionWithTranscriptions(sessionId),获取完整的会话信息和所有转写文本Turn[]。 -
构建 Prompt:将
Turn[]拼接成对话字符串,并填充预设的 Prompt 模板。 -
调用 LLM:使用
llmClient.GenerateEMR()发送请求到 LLM 服务。这里需要设置一个合理的超时时间(e.g., 30s)。 -
结果处理:
- 成功:解析返回的 JSON,进行格式和业务校验。将生成的
EMRDraft(JSON格式)存入数据库的emr_drafts表,关联session_id。 - 失败(网络错误、LLM返回错误、JSON解析失败):记录错误日志,将
session状态标记为PROCESSING_FAILED,并通知医生前端“AI 生成失败,请手动录入”。
- 成功:解析返回的 JSON,进行格式和业务校验。将生成的
-
状态更新:成功后,将
session状态更新为***PLETED。
-
数据拉取:调用
- 订阅
Step 4: 医生审核与编辑
- 前端:通过轮询或 WebSocket 接收,获取到“草稿已生成”的通知。
-
前端:自动从
nlp-medical-service拉取EMRDraftJSON。 - 前端渲染:将 JSON 数据渲染到可编辑的表单中。每个字段(主诉、现病史等)都是独立的输入框或富文本编辑器。医生可以对比原始对话录音和转写文本,进行修改。
- 医生操作:修改完毕后,点击“提交”按钮。
Step 5: 写入 EMR,完成闭环
-
前端动作:调用
POST /api/v1/emr-drafts/{draftId}/submit,携带最终修改后的病历 JSON。 -
gateway-service->emr-integration-service:-
emr-integration-service接收到提交请求,验证doctor_id是否有权限提交此draft_id。 - 将此写入操作创建成一个异步任务(如使用
Asynq),任务
-