MediLink API 调试手册

面向联调/测试人员。五路由面(/api B端 / /pt 患者 / /pub 匿名 / /webhooks 供应商 / SSE),dev 端口 18031、e2e 18032。curl 示例以 dev 为准;token 获取链路见各节。 权威行为=FSD(prd/fsd-medilink/);完整断言链=scripts/dev/e2e.sh(83 处断言可直接抄)。

0 通用约定

1 /pub 匿名面(限流 60/min/IP)

# 1. 签发访客 token(无鉴权,bootstrap)
curl -s -X POST :18031/pub/v1/guest-session
# → {"token":"…","expires_in":604800}; gsid 在 token 内

# 2. 医院列表/详情(需 guest token)
curl -s -H "Authorization: Bearer $T" ':18031/pub/v1/hospitals?city=北京&page=1&page_size=10'
curl -s -H "Authorization: Bearer $T" :18031/pub/v1/hospitals/1

# 3. 建聊天会话 + 发消息
curl -s -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
  -d '{"hospital_id":1}' :18031/pub/v1/chat/session
curl -s -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
  -d '{"content":"Hello, I need help."}' :18031/pub/v1/chat/messages
# → 201/200; soft_limit_reached=true 表示已到 50 条软上限

# 4. 病历直传三步
curl -s -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
  -d '{"mime":"application/pdf"}' :18031/pub/v1/documents/presign
curl -X PUT --upload-file report.pdf "<upload_url>"
curl -s -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
  -d '{"key":"medical/xx","label":"血检报告","file_name":"report.pdf"}' :18031/pub/v1/documents/confirm

# 5. 注册(guest token 内的 gsid 绑定会话+文档)
curl -s -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' -d '{
  "first_name":"Hao","last_name":"Chen","nationality":"US","gender":"MALE","age":35,
  "phone":"+12025550123","email":"pat@example.com","password":"Passw0rd!",
  "chief_complaint":"Persistent cough","expect_city":"北京","expect_window":"2026-11",
  "consent":true,"document_ids":[1,2]}' :18031/pub/v1/register
# → {"token":"<pt jwt>","patient_id":..,"ticket":{"id":..,"ticket_no":"T…"}}

# 6. 患者登录(同一 401 语义防枚举)
curl -s -X POST -H 'Content-Type: application/json' \
  -d '{"email":"pat@example.com","password":"Passw0rd!"}' :18031/pub/v1/login

2 /api B 端面(RBAC 权限码在路由级)

# 两段式登录(CONSULTANT/DOCTOR/ADMIN)
curl -s -X POST -H 'Content-Type: application/json' \
  -d '{"email":"consultant@medilink.local","password":"Consultant#2026"}' :18031/api/auth/login
# → {"mfa_required":true,"mfa_token":"…"}(TOTP 启用者)
curl -s -X POST -H 'Content-Type: application/json' \
  -d '{"mfa_token":"…","code":"<现算6位>"}' :18031/api/auth/mfa
# → {"token":"…","role":"CONSULTANT","permissions":[...]}

# 工单:列表/详情/分配/流转/文档
curl -s -H "Authorization: Bearer $B" ':18031/api/tickets?status=PENDING_ASSIGN'
curl -s -X POST -H "Authorization: Bearer $B" -H 'Content-Type: application/json' \
  -d '{"assignee_id":9001}' :18031/api/tickets/1/assign
curl -s -X POST -H "Authorization: Bearer $B" -H 'Content-Type: application/json' \
  -d '{"to":"IN_TREATMENT","reason":"患者已到达"}' :18031/api/tickets/1/transition

# 医生预诊断
curl -s -H "Authorization: Bearer $D" :18031/api/diagnosis/pending
curl -s -H "Authorization: Bearer $D" ':18031/api/diagnosis/experts?dept_id=5'
curl -s -X POST -H "Authorization: Bearer $D" -H 'Content-Type: application/json' -d '{
  "prediagnosis_summary":"…","plan":"…","dept_id":5,"expert_id":3,
  "estimate_days":14,"estimate_amount":8000,"estimate_currency":"USD"}' :18031/api/diagnosis/1
curl -s -X POST -H "Authorization: Bearer $D" -H 'Content-Type: application/json' \
  -d '{"reason":"病历不完整"}' :18031/api/diagnosis/1/reject

# 看板/审计/对账(ops:read)/关单(ops:manage,仅 ADMIN)
curl -s -H "Authorization: Bearer $B" :18031/api/board
curl -s -H "Authorization: Bearer $B" ':18031/api/operation-logs?entity_type=ticket&entity_id=1'
curl -s -H "Authorization: Bearer $A" :18031/api/payments/pending
curl -s -X POST -H "Authorization: Bearer $A" -H 'Content-Type: application/json' \
  -d '{"reason":"对账确认孤儿单"}' :18031/api/payments/9/close

# 诊疗阶段与出行
curl -s -X POST -H "Authorization: Bearer $B" -H 'Content-Type: application/json' \
  -d '{"to":"ARRIVAL","reason":"患者已抵达"}' :18031/api/care/1/stage
curl -s -X POST -H "Authorization: Bearer $B" -H 'Content-Type: application/json' \
  -d '{"ticket_id":1,"kind":"HOTEL","title":"协和国际部旁酒店","detail":{"nights":7}}' :18031/api/trips

3 /pt 患者面

PT="Authorization: Bearer <register/login 返回的 pt token>"
curl -s -H "$PT" :18031/pt/me
curl -s -H "$PT" :18031/pt/progress          # 派生八步
curl -s -H "$PT" :18031/pt/plan              # 查看方案
curl -s -X POST -H "$PT" :18031/pt/plan/confirm
curl -s -X POST -H "$PT" :18031/pt/payments  # 创建支付单(fake 档返本地 URL)
curl -s -H "$PT" :18031/pt/payments          # 支付单列表
curl -s -H "$PT" :18031/pt/payments/1/receipt # 凭证 PDF(409=未就绪)
curl -s -H "$PT" :18031/pt/trips             # 出行只读
curl -s -H "$PT" :18031/pt/me/export         # GDPR 数据导出

4 /webhooks 与 SSE

# Stripe webhook(fake 档无真回调——e2e.sh 用本地 HMAC 构造密文,scheme 同 stripe-go)
curl -s -X POST -H "Stripe-Signature: t=<ts>,v1=<hmac>" -H 'Content-Type: application/json' \
  --data @event.json :18031/webhooks/stripe   # 200=受理(含幂等重复);400=验签失败;503=未配 secret

# SSE(query token;补拉参数 after_id / Last-Event-ID 头二选一)
curl -N -H "Authorization: Bearer $T" ':18031/pub/stream?after_id=0' &   # 访客流
curl -N -H "Authorization: Bearer $PT" ':18031/pt/stream?after_id=0' &   # 患者流
curl -N "Bearer $B" ':18031/api/stream?access_token=<bo>' &              # B 端广播流
# 事件:msg / msgTranslated / read / ticket / ping(注释行, 25s)

5 排障速查

现象 根因
受保护面全部 401 jwt.secret 未配置(起服日志 Warn)
登录一直 401 且码确认正确 90s 内重放(TOTP 防重放 SETNX)——等下一码
/pub 全部 429 限流窗耗尽(60/min/IP)——清 rl:<库名>:* 或等窗口
凭证下载 409 非 SUCCEEDED 或 receiptIO 未装配(storage 未就绪)
webhook 503 payment.webhook_secret 未配置
医院「编辑」后富文本消失 已知缺陷 DIF-F2-1(FSD 99-附录 B)