Files
fastapi/docs/03_api_design.md
T
2026-06-08 16:49:45 +08:00

436 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 医疗问诊 Agent API 文档
> 当前文档只描述前端联调需要的后端能力。
## 1. 联调地址
| 项目 | 地址 |
|---|---|
| 公网网关 | `http://8.160.178.88/fastapi` |
| API Base URL | `http://8.160.178.88/fastapi/api/v1` |
| Swagger | `http://8.160.178.88/fastapi/docs` |
| OpenAPI JSON | `http://8.160.178.88/fastapi/openapi.json` |
| 存活检查 | `http://8.160.178.88/fastapi/health/live` |
| 就绪检查 | `http://8.160.178.88/fastapi/health/ready` |
## 2. 通用规则
除健康检查外,所有业务接口都需要携带:
```http
Authorization: Bearer <access_token>
X-Entry-Scene: vue_frontend
X-Request-Id: <>
```
普通 JSON 接口统一返回:
```json
{
"code": "OK",
"message": "success",
"data": {}
}
```
前端判断规则:
- HTTP 状态码为 `2xx`
- `code` 等于 `OK`
- 业务数据从 `data` 读取
SSE 流式接口不返回上述 JSON 包装,而是返回 `event + data` 事件流。
## 3. 前置接口
### 3.1 当前用户
| 接口名称 | url | api | methods | params(入参) | response(返回参数) |
|---|---|---|---|---|---|
| 当前用户 | `http://8.160.178.88/fastapi/api/v1/auth/me` | `/api/v1/auth/me` | `GET` | Header`Authorization` 必填,格式 `Bearer <access_token>``X-Entry-Scene` 建议传。 | `data.user_id``data.username``data.display_name``data.role``data.phone``data.institution_id``data.department_id``data.status` 等 Django 用户中心字段。 |
请求示例:
```http
GET /api/v1/auth/me
Authorization: Bearer <access_token>
X-Entry-Scene: vue_frontend
```
成功返回示例:
```json
{
"code": "OK",
"message": "success",
"data": {
"user_id": "37",
"source": "django_user_center",
"username": "13700000099",
"display_name": "测试用户",
"tenant_id": "1",
"role": "student",
"phone": "13700000099",
"avatar": "",
"gender": 0,
"institution_id": 1,
"institution_name": "测试机构",
"department_id": 2,
"department_name": "儿科",
"status": 1
}
}
```
### 3.2 病例读取
| 接口名称 | url | api | methods | params(入参) | response(返回参数) |
|---|---|---|---|---|---|
| 病例列表 | `http://8.160.178.88/fastapi/api/v1/cases` | `/api/v1/cases` | `GET` | Query`department_id` 选填,科室 ID`training_type` 选填;`mode` 选填,允许 `practice``teaching`。 | `data.items[]`,包含 `id``title``department_id``difficulty``chief_complaint``has_teaching_video``has_quiz`。 |
| 病例详情 | `http://8.160.178.88/fastapi/api/v1/cases/{case_id}` | `/api/v1/cases/{case_id}` | `GET` | Path`case_id` 必填,病例 ID。 | 病例入口展示信息,不返回标准答案、隐藏病史、检查结果和评分规则。 |
## 4. 训练页面接口
| 接口名称 | url | api | methods | params(入参) | response(返回参数) |
|---|---|---|---|---|---|
| 推荐配置信息 | `http://8.160.178.88/fastapi/api/v1/training-config/recommended?case_id={case_id}` | `/api/v1/training-config/recommended` | `GET` | Query`case_id` 必填,病例 ID。 | `data.recommended` 默认配置;`data.recommended_labels` 中文标签。 |
| 训练配置信息 | `http://8.160.178.88/fastapi/api/v1/training-config/options?case_id={case_id}` | `/api/v1/training-config/options` | `GET` | Query`case_id` 必填,病例 ID。 | `data.options` 全部可选项;`data.recommended` 推荐默认值。 |
| 新建会话 | `http://8.160.178.88/fastapi/api/v1/sessions` | `/api/v1/sessions` | `POST` | Body`case_id` 必填;`training_type` 必填;`mode` 必填,当前训练填 `practice``score_type` 选填,允许 `percentage``five_point``patient_config` 选填。 | `data.session_id``data.session_code``data.status``data.patient_opening``data.patient_config`。 |
| 流式会话 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/chat/stream` | `/api/v1/sessions/{session_id}/chat/stream` | `POST` | Path`session_id` 必填;Body`message` 必填,医学生问句。 | SSE`message_delta` 返回患者回复增量;`message_done` 返回耗时;`error` 返回错误。 |
| 王主任练习提示 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/hints/stream` | `/api/v1/sessions/{session_id}/hints/stream` | `POST` | Path`session_id` 必填;Body`last_user_message` 选填;`scope` 选填,默认 `current_conversation`。 | SSE`hint_delta` 返回一句话提示增量;`hint_done` 返回结束事件。 |
| 体格检查列表获取 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/physical-exams` | `/api/v1/sessions/{session_id}/physical-exams` | `GET` | Path`session_id` 必填。 | `data.items[]`,包含体格检查项 `item_code``item_name``item_type`。不返回结果。 |
| 辅助检查列表获取 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/auxiliary-exams` | `/api/v1/sessions/{session_id}/auxiliary-exams` | `GET` | Path`session_id` 必填。 | `data.items[]`,包含辅助检查项 `item_code``item_name``item_type`。不返回结果。 |
| 体格检查某项结果 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/physical-exams/{item_code}` | `/api/v1/sessions/{session_id}/physical-exams/{item_code}` | `POST` | Path`session_id` 必填;`item_code` 必填,必须属于体格检查。 | `data.result_text``data.result_structured``data.context_written``data.already_ordered`。 |
| 辅助检查某项结果 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/auxiliary-exams/{item_code}` | `/api/v1/sessions/{session_id}/auxiliary-exams/{item_code}` | `POST` | Path`session_id` 必填;`item_code` 必填,必须属于辅助检查。 | `data.result_text``data.result_structured``data.context_written``data.already_ordered`。 |
| 完成问诊 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/complete-inquiry` | `/api/v1/sessions/{session_id}/complete-inquiry` | `POST` | Path`session_id` 必填。至少完成一轮医生问诊。 | `data.session_id``data.status=diagnosis`。 |
| 提交诊断 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/diagnosis` | `/api/v1/sessions/{session_id}/diagnosis` | `POST` | Path`session_id` 必填;Body`primary_diagnosis` 必填;`diagnosis_basis` 必填;`differential_diagnoses` 选填数组。 | `data.status=treatment`。 |
| 提交治疗 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/treatment` | `/api/v1/sessions/{session_id}/treatment` | `POST` | Path`session_id` 必填;Body`treatment_principle``treatment_measures` 必填;`risk_plan``communication``follow_up` 选填。 | `data.status=evaluating`。 |
| 生成评价 | `http://8.160.178.88/fastapi/api/v1/sessions/{session_id}/evaluation` | `/api/v1/sessions/{session_id}/evaluation` | `POST` | Path`session_id` 必填;Body`score_type` 选填,允许 `percentage``five_point`。必须已提交治疗。 | `data.evaluation_id``data.total_score``data.dimension_scores[]``data.score_details[]``data.overall_comment`。 |
| 获取评价(详情) | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}` | `/api/v1/evaluations/{evaluation_id}` | `GET` | Path`evaluation_id` 必填。只允许当前用户访问自己的评价。 | 评价详情,包含病例、总分、维度评分、评分明细、改进建议、PDF 路径。 |
| 下载 PDF | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}/download-pdf` | `/api/v1/evaluations/{evaluation_id}/download-pdf` | `GET` | Path`evaluation_id` 必填。Header`Authorization` 必填。 | 成功时返回 `application/pdf` 文件流,浏览器可下载;失败时返回统一错误 JSON。 |
### 4.1 训练配置入参和返回示例
请求:
```http
GET /api/v1/training-config/options?case_id=2
Authorization: Bearer <access_token>
X-Entry-Scene: vue_frontend
```
返回:
```json
{
"code": "OK",
"message": "success",
"data": {
"case_id": 2,
"recommended": {
"visit_environment": "outpatient",
"age_group": "child",
"education_level": "higher",
"personality": "calm"
},
"recommended_labels": {
"visit_environment": "门诊",
"age_group": "儿童",
"education_level": "高等教育",
"personality": "平和"
},
"options": {
"visit_environment": [
{"value": "outpatient", "label": "门诊"},
{"value": "emergency", "label": "急诊"},
{"value": "ward", "label": "病房"}
],
"age_group": [
{"value": "child", "label": "儿童"},
{"value": "youth", "label": "青年"},
{"value": "middle_aged", "label": "中年"},
{"value": "elderly", "label": "老年"}
],
"education_level": [
{"value": "primary_or_below", "label": "小学及以下"},
{"value": "secondary", "label": "中等教育"},
{"value": "higher", "label": "高等教育"}
],
"personality": [
{"value": "calm", "label": "平和"},
{"value": "anxious", "label": "焦虑"},
{"value": "impatient", "label": "急躁"},
{"value": "cooperative", "label": "配合"},
{"value": "suspicious", "label": "多疑"}
]
}
}
}
```
### 4.2 新建会话入参和返回示例
请求:
```json
{
"case_id": 2,
"training_type": "diagnosis_treatment",
"mode": "practice",
"score_type": "percentage",
"patient_config": {
"visit_environment": "outpatient",
"age_group": "child",
"education_level": "higher",
"personality": "calm"
}
}
```
返回:
```json
{
"code": "OK",
"message": "success",
"data": {
"session_id": 12,
"session_code": "sess_20260608120000_xxxx",
"status": "inquiry",
"patient_opening": "家长:医生,孩子发热咳嗽好几天了,昨天开始喘得厉害,精神也不太好。",
"patient_config": {
"values": {
"visit_environment": "outpatient",
"age_group": "child",
"education_level": "higher",
"personality": "calm"
},
"labels": {
"visit_environment": "门诊",
"age_group": "儿童",
"education_level": "高等教育",
"personality": "平和"
}
}
}
}
```
### 4.3 SSE 流式会话返回格式
请求:
```json
{
"message": "孩子发热几天了?最高体温多少?"
}
```
返回:
```text
event: message_delta
data: {"delta":"发热有4天了,"}
event: message_delta
data: {"delta":"最高烧到39度多。"}
event: message_done
data: {"latency_ms":1800,"first_token_ms":500,"model":"deepseek-chat","fallback_used":false}
```
### 4.4 检查结果返回示例
```json
{
"code": "OK",
"message": "success",
"data": {
"item_code": "blood_routine",
"item_name": "血常规",
"item_type": "lab",
"result_text": "WBC 12.5×10^9/L,中性粒细胞比例72%,提示感染及炎症反应。",
"result_structured": {
"wbc": "12.5×10^9/L",
"neutrophil": "72%"
},
"is_key": true,
"is_abnormal": true,
"context_written": true,
"already_ordered": false
}
}
```
检查规则:
- 检查结果只来自数据库,不由 LLM 生成。
- 检查结果会写入本次会话短期 memory 和评分依据。
- 同一会话重复申请相同 `item_code` 时返回已有结果,`already_ordered=true`
### 4.5 诊断和治疗提交示例
提交诊断:
```json
{
"primary_diagnosis": "支气管肺炎",
"differential_diagnoses": ["支气管哮喘急性发作", "上呼吸道感染"],
"diagnosis_basis": "结合发热、咳嗽、喘息、肺部湿啰音、胸片异常、炎症指标升高和血氧情况,符合儿童支气管肺炎表现。"
}
```
提交治疗:
```json
{
"treatment_principle": "抗感染、止咳平喘、改善氧合并严密观察病情变化。",
"treatment_measures": "根据病情进行抗感染治疗,必要时雾化吸入缓解喘息,监测体温、呼吸和血氧。",
"risk_plan": "关注低氧、呼吸困难加重、持续高热、精神反应差和脱水。",
"communication": "向家属说明病情、用药注意事项、危险信号和复诊指征。",
"follow_up": "治疗后复查体温、呼吸、血氧和必要炎症指标。"
}
```
## 5. 教学互动接口
| 接口名称 | url | api | methods | params(入参) | response(返回参数) |
|---|---|---|---|---|---|
| 获取教学列表(题目 选项 答案 解析文本 视频) | `http://8.160.178.88/fastapi/api/v1/teaching/cases/{case_id}/items` | `/api/v1/teaching/cases/{case_id}/items` | `GET` | Path`case_id` 必填,必须存在 `teaching_case` 数据。 | `data.case` 病例摘要;`data.questions[]` 题目列表,包含 `stem``options[]``answer``analysis``video`。 |
| 生成评价 | `http://8.160.178.88/fastapi/api/v1/teaching/evaluation` | `/api/v1/teaching/evaluation` | `POST` | Body`case_id` 必填;`answers[]` 必填;`answers[].question_id` 必填;`answers[].selected_answer` 必填;`score_type` 选填。 | `data.session_id``data.evaluation_id``data.total_score``data.dimension_scores[]``data.score_details[]``data.overall_comment`。 |
| 获取评价(详情) | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}` | `/api/v1/evaluations/{evaluation_id}` | `GET` | Path`evaluation_id` 必填。 | 教学互动评价详情,结构与训练评价一致。 |
| 下载 PDF | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}/download-pdf` | `/api/v1/evaluations/{evaluation_id}/download-pdf` | `GET` | Path`evaluation_id` 必填。 | 返回 `application/pdf` 文件流。 |
获取教学列表返回示例:
```json
{
"code": "OK",
"message": "success",
"data": {
"case": {
"case_id": 2,
"title": "支气管肺炎 - 6岁男性患儿",
"chief_complaint": "发热、咳嗽4天,喘息1天"
},
"teaching_goal": "围绕儿科肺炎问诊、检查选择、诊断依据、治疗决策和医患沟通完成互动训练。",
"questions": [
{
"question_id": "q1",
"question_type": "single_choice",
"stem": "该患儿最需要优先关注的病情严重程度指标是?",
"options": [
{"key": "A", "text": "体温峰值"},
{"key": "B", "text": "血氧饱和度"}
],
"answer": "B",
"analysis": "血氧饱和度能帮助判断低氧和肺炎严重程度。",
"video": {"title": "儿童肺炎教学示例视频", "url": ""},
"knowledge_points": ["严重程度评估", "血氧判断"]
}
]
}
}
```
生成教学评价请求示例:
```json
{
"case_id": 2,
"score_type": "percentage",
"answers": [
{"question_id": "q1", "selected_answer": "B"},
{"question_id": "q2", "selected_answer": "A"}
]
}
```
## 6. 个人中心接口
| 接口名称 | url | api | methods | params(入参) | response(返回参数) |
|---|---|---|---|---|---|
| 训练记录列表 | `http://8.160.178.88/fastapi/api/v1/evaluations` | `/api/v1/evaluations` | `GET` | Header`Authorization` 必填。 | `data.items[]` 当前用户完整训练后的评价记录,包含 `evaluation_id``case_title``score_type``total_score``created_at``pdf_exported`。 |
| 训练记录详情(评价详情) | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}` | `/api/v1/evaluations/{evaluation_id}` | `GET` | Path`evaluation_id` 必填。 | 完整评价详情,训练和教学互动评价共用。 |
训练记录列表返回示例:
```json
{
"code": "OK",
"message": "success",
"data": {
"items": [
{
"evaluation_id": 101,
"case_title": "支气管肺炎 - 6岁男性患儿",
"score_type": "percentage",
"total_score": 82,
"created_at": "2026-06-08T12:00:00",
"pdf_exported": true
}
]
}
}
```
## 7. PDF 下载前端写法
因为下载接口需要 Bearer token,前端不能直接使用普通 `<a href>`。使用 `fetch` 获取 blob 后触发下载:
```ts
async function downloadEvaluationPdf(baseUrl: string, token: string, evaluationId: number) {
const response = await fetch(`${baseUrl}/evaluations/${evaluationId}/download-pdf`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"X-Entry-Scene": "vue_frontend",
},
});
if (!response.ok) {
const error = await response.json().catch(() => null);
throw new Error(error?.message || `PDF 下载失败:${response.status}`);
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `evaluation_${evaluationId}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
```
## 8. 常见错误码
| HTTP | code | 说明 |
|---:|---|---|
| 401 | `AUTH_CREDENTIAL_REQUIRED` | 缺少 Authorization。 |
| 401 | `AUTH_USER_INVALID` | token 无效、过期或 Django 返回非 200。 |
| 403 | `AUTH_USER_DISABLED` | Django 用户状态被禁用。 |
| 503 | `AUTH_USER_CENTER_UNAVAILABLE` | Django 用户中心超时或不可达。 |
| 404 | `CASE_NOT_FOUND` | 病例不存在、未发布或已停用。 |
| 404 | `SESSION_NOT_FOUND` | 会话不存在或不属于当前用户。 |
| 400 | `SESSION_STATUS_INVALID` | 当前状态不允许执行该操作。 |
| 400 | `INQUIRY_REQUIRED` | 完成问诊前没有医生提问。 |
| 400 | `DIAGNOSIS_REQUIRED` | 提交治疗前没有提交诊断。 |
| 400 | `TREATMENT_REQUIRED` | 生成评价前没有提交治疗。 |
| 404 | `ORDER_ITEM_NOT_FOUND` | 当前病例不存在该检查项。 |
| 400 | `ORDER_ITEM_TYPE_MISMATCH` | 检查接口类型和检查项类型不匹配。 |
| 404 | `EVALUATION_NOT_FOUND` | 评价不存在或不属于当前用户。 |
| SSE error | `LLM_STREAM_TIMEOUT` | 流式问诊首段或总耗时超时。 |
| SSE error | `LLM_STREAM_FAILED` | 流式模型调用失败。 |
| 500 | `PDF_EXPORT_FAILED` | PDF 生成失败。 |
## 9. 当前保留接口清单
当前后端业务模块:
- 训练页面:推荐配置、训练配置、新建会话、流式会话、王主任练习提示、检查列表、检查结果、完成问诊、诊断、治疗、评价、评价详情、PDF 下载。
- 教学互动:教学列表、教学评价、评价详情、PDF 下载。
- 个人中心:训练记录列表、训练记录详情。