2026-06-08 15:16:07 +08:00
|
|
|
|
# 医疗问诊 Agent 前端联调 API 文档
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
> 文档版本:2026-06-04
|
|
|
|
|
|
> 对应后端:FastAPI `main` 分支,提交 `b46e43a` 之后版本
|
2026-06-05 12:57:02 +08:00
|
|
|
|
> 本文档以当前真实代码行为为准,用于正式 Vue 前端功能联调。
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 联调地址
|
|
|
|
|
|
|
|
|
|
|
|
### 1.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` |
|
|
|
|
|
|
|
|
|
|
|
|
当前 `/health/ready` 已验证 MySQL、Redis 和生产配置处于就绪状态。
|
|
|
|
|
|
|
|
|
|
|
|
如果服务器关闭 Nginx 的 `/fastapi/` 公网代理,公网地址将不可用,前端需要改用局域网、VPN、SSH 隧道或新的测试网关。
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 Docker 主机内部地址
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
http://127.0.0.1:9000
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
该地址只用于服务器本机检查,不提供给远程前端。
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 前端接入规则
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 认证链路
|
|
|
|
|
|
|
|
|
|
|
|
医疗问诊 Agent 不实现登录注册,也不接受前端传入 `user_id`。
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
前端从宿主系统获得 access token
|
|
|
|
|
|
-> 请求 FastAPI 时携带 Authorization: Bearer <access_token>
|
|
|
|
|
|
-> FastAPI 将 token 转发给 Django /api/user/users/me/
|
|
|
|
|
|
-> Django 返回 200 和用户资料
|
|
|
|
|
|
-> FastAPI 使用 Django 返回的 id 隔离会话、检查、提交和评价记录
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端禁止传递或信任 `X-User-Id`。
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 通用请求头
|
|
|
|
|
|
|
|
|
|
|
|
除健康检查外,所有业务接口必须携带:
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
Authorization: Bearer <access_token>
|
|
|
|
|
|
X-Entry-Scene: vue_frontend
|
|
|
|
|
|
X-Request-Id: <可选的请求追踪ID>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
| Header | 必填 | 说明 |
|
|
|
|
|
|
|---|---:|---|
|
|
|
|
|
|
| `Authorization` | 是 | Django 用户中心 access token。 |
|
|
|
|
|
|
| `X-Entry-Scene` | 否 | 入口场景,例如 `vue_frontend`、`production_vue`。 |
|
|
|
|
|
|
| `X-Request-Id` | 否 | 前端生成的请求追踪 ID。 |
|
|
|
|
|
|
|
|
|
|
|
|
### 2.3 统一响应
|
|
|
|
|
|
|
|
|
|
|
|
除 SSE 流式问诊外,接口统一返回:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端必须同时判断 HTTP 状态码和业务 `code`:
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
if (!response.ok || body.code !== "OK") {
|
|
|
|
|
|
throw new Error(body.message || body.code || "request failed");
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
### 2.4 CORS 配置
|
|
|
|
|
|
|
|
|
|
|
|
正式前端通过 `http://8.160.178.88/app/` 访问 API 时,与 `/fastapi/` 同源,不会触发跨域限制。
|
|
|
|
|
|
|
|
|
|
|
|
Mac 上使用 Vite 开发服务器直接调用公网 API 时,服务器 `.env` 必须允许前端 Origin。例如:
|
|
|
|
|
|
|
|
|
|
|
|
```env
|
|
|
|
|
|
CORS_ALLOW_ORIGINS=http://localhost:5173,http://192.168.2.100:5173
|
|
|
|
|
|
CORS_ALLOW_ORIGIN_REGEX=
|
|
|
|
|
|
```
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
修改服务器 `.env` 后重新创建 FastAPI 容器:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd /home/code/medical-ai
|
|
|
|
|
|
docker compose up -d --force-recreate fastapi
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
不需要重新构建镜像。
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 当前接口总览
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
### 3.1 无需认证
|
|
|
|
|
|
|
|
|
|
|
|
| Method | Path | 用途 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `GET` | `/health/live` | FastAPI 进程存活检查。 |
|
|
|
|
|
|
| `GET` | `/health/ready` | MySQL、Redis 和关键配置就绪检查。 |
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 需要 Bearer Token
|
|
|
|
|
|
|
|
|
|
|
|
以下路径均以 `/api/v1` 为前缀。
|
|
|
|
|
|
|
|
|
|
|
|
| Method | Path | 用途 | 前端范围 |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| `GET` | `/auth/me` | 校验 token 并获取当前用户。 | 必须 |
|
|
|
|
|
|
| `GET` | `/agent/hello` | 获取 Agent 能力开关。 | 必须 |
|
|
|
|
|
|
| `GET` | `/cases` | 获取病例列表。 | 必须 |
|
|
|
|
|
|
| `GET` | `/cases/{case_id}` | 获取病例入口详情。 | 必须 |
|
2026-06-08 15:16:07 +08:00
|
|
|
|
| `GET` | `/training-config/recommended` | 获取训练页推荐病人初始化配置。 | 必须 |
|
|
|
|
|
|
| `GET` | `/training-config/options` | 获取训练页自定义配置选项。 | 必须 |
|
2026-06-05 12:57:02 +08:00
|
|
|
|
| `POST` | `/sessions` | 创建训练会话。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/chat` | 非流式问诊。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/chat/stream` | SSE 流式问诊。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/hints` | 练习模式提示。 | 必须 |
|
2026-06-08 15:16:07 +08:00
|
|
|
|
| `POST` | `/sessions/{session_id}/hints/stream` | SSE 流式练习提示。 | 必须 |
|
2026-06-05 12:57:02 +08:00
|
|
|
|
| `GET` | `/sessions/{session_id}/order-items` | 获取病例可申请检查项。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/orders` | 申请检查并返回结果。 | 必须 |
|
|
|
|
|
|
| `GET` | `/sessions/{session_id}/physical-exams` | 获取体格检查列表。 | 必须 |
|
|
|
|
|
|
| `GET` | `/sessions/{session_id}/auxiliary-exams` | 获取辅助检查列表。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/physical-exams/{item_code}` | 获取体格检查某项结果。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/auxiliary-exams/{item_code}` | 获取辅助检查某项结果。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/complete-inquiry` | 完成问诊。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/diagnosis` | 提交诊断。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/treatment` | 提交治疗方案。 | 必须 |
|
|
|
|
|
|
| `POST` | `/sessions/{session_id}/evaluation` | 生成 AI 评价。 | 必须 |
|
|
|
|
|
|
| `GET` | `/evaluations` | 查询当前用户历史评价。 | 必须 |
|
|
|
|
|
|
| `GET` | `/evaluations/{evaluation_id}` | 查询评价详情和评分明细。 | 必须 |
|
|
|
|
|
|
| `POST` | `/evaluations/{evaluation_id}/export-pdf` | 生成 PDF 报告。 | 必须 |
|
2026-06-08 15:16:07 +08:00
|
|
|
|
| `GET` | `/evaluations/{evaluation_id}/download-pdf` | 生成并下载 PDF 报告文件流。 | 必须 |
|
2026-06-05 12:57:02 +08:00
|
|
|
|
| `GET` | `/knowledge/search` | 调试知识检索。 | 调试 |
|
|
|
|
|
|
| `POST` | `/llm/test/deepseek-fast` | 测试快速模型。 | 调试 |
|
|
|
|
|
|
| `POST` | `/llm/test/deepseek-reason` | 测试 Reason 模型。 | 调试 |
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 前端完整业务流程
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
GET /auth/me
|
|
|
|
|
|
-> GET /agent/hello
|
|
|
|
|
|
-> GET /cases
|
|
|
|
|
|
-> GET /cases/{case_id}
|
|
|
|
|
|
-> POST /sessions
|
|
|
|
|
|
-> 多轮问诊、提示、检查申请
|
|
|
|
|
|
-> POST /sessions/{session_id}/complete-inquiry
|
|
|
|
|
|
-> POST /sessions/{session_id}/diagnosis
|
|
|
|
|
|
-> POST /sessions/{session_id}/treatment
|
|
|
|
|
|
-> POST /sessions/{session_id}/evaluation
|
|
|
|
|
|
-> GET /evaluations/{evaluation_id}
|
|
|
|
|
|
-> POST /evaluations/{evaluation_id}/export-pdf
|
2026-06-08 15:16:07 +08:00
|
|
|
|
-> GET /evaluations/{evaluation_id}/download-pdf
|
2026-06-05 12:57:02 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
会话状态流转:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
inquiry -> diagnosis -> treatment -> evaluating -> completed
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
| 状态 | 允许操作 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `inquiry` | 问诊、提示、检查申请、完成问诊。 |
|
|
|
|
|
|
| `diagnosis` | 提交诊断、继续申请检查。 |
|
|
|
|
|
|
| `treatment` | 提交治疗、继续申请检查。 |
|
|
|
|
|
|
| `evaluating` | 生成评价。 |
|
|
|
|
|
|
| `completed` | 查看评价、导出 PDF、查看历史记录。 |
|
|
|
|
|
|
|
|
|
|
|
|
当前没有“获取会话详情”“恢复聊天记录”接口。页面刷新或中断后,前端不能恢复短期对话,应重新创建训练会话。
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
### 4.1 训练页面当前使用接口明细
|
|
|
|
|
|
|
|
|
|
|
|
下表为正式前端训练页面当前需要直接调用的接口。`url` 为公网联调完整地址,`api` 为前端代码中拼接的相对路径。除健康检查外,以下接口都需要携带 `Authorization: Bearer <access_token>`;`X-Entry-Scene` 建议固定传 `vue_frontend` 或实际入口场景。
|
|
|
|
|
|
|
|
|
|
|
|
| 接口名称 | url | api | methods | params(入参) | response(返回参数) |
|
|
|
|
|
|
|---|---|---|---|---|---|
|
|
|
|
|
|
| 新建会话 | `http://8.160.178.88/fastapi/api/v1/sessions` | `/api/v1/sessions` | `POST` | Header:`Authorization` 必填,`X-Entry-Scene` 建议传;Body:`case_id` 必填,`training_type` 必填,`mode` 必填,`score_type` 可选,`patient_config` 可选。 | `code`、`message`、`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` 返回 `delta`;`message_done` 返回 `latency_ms`、`first_token_ms`、`model`、`fallback_used`;异常返回 `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`。仅 `practice` 且 `inquiry` 状态可调用。 | SSE:`hint_delta` 返回一句话提示增量;`hint_done` 返回耗时;异常返回 `error`。 |
|
|
|
|
|
|
| 体格检查列表获取 | `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` 必填。`item_code` 必须属于体格检查项。 | `data.item_code`、`item_name`、`item_type`、`result_text`、`result_structured`、`is_key`、`is_abnormal`、`context_written`、`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` 必填。`item_code` 必须属于辅助检查项。 | 同体格检查结果。检查结果来自数据库,并写入本次会话短期 memory。重复申请返回已有结果,`already_ordered=true`。 |
|
|
|
|
|
|
| 完成问诊 | `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`、`score_type`、`total_score`、`dimension_scores`、`score_details`、`errors`、`improvement_plan`、`evidence_summary`、`guideline_refs`、`overall_comment`。 |
|
|
|
|
|
|
| 获取评价(详情) | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}` | `/api/v1/evaluations/{evaluation_id}` | `GET` | Path:`evaluation_id` 必填。只允许当前 token 对应用户访问自己的评价。 | 评价完整详情,包含 `session_id`、`case_id`、`case_title`、`created_at`、`pdf_file_path`。 |
|
|
|
|
|
|
| 生成 PDF | `http://8.160.178.88/fastapi/api/v1/evaluations/{evaluation_id}/export-pdf` | `/api/v1/evaluations/{evaluation_id}/export-pdf` | `POST` | Path:`evaluation_id` 必填。只允许当前 token 对应用户导出自己的评价。 | `data.export_id`、`data.file_path`。 |
|
|
|
|
|
|
| 下载 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` 必填,`X-Entry-Scene` 建议传。只允许当前 token 对应用户下载自己的评价报告。 | 成功时返回 `application/pdf` 文件流,响应头包含 `Content-Disposition: attachment`;失败时返回统一错误 JSON。 |
|
|
|
|
|
|
|
|
|
|
|
|
常见错误码:
|
|
|
|
|
|
|
|
|
|
|
|
| code | 场景 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `AUTH_CREDENTIAL_REQUIRED` | 缺少 `Authorization`。 |
|
|
|
|
|
|
| `SESSION_NOT_FOUND` | 会话不存在或不属于当前用户。 |
|
|
|
|
|
|
| `SESSION_STATUS_INVALID` | 当前会话状态不允许该操作。 |
|
|
|
|
|
|
| `INQUIRY_REQUIRED` | 未完成至少一轮医生问诊就完成问诊。 |
|
|
|
|
|
|
| `ORDER_ITEM_NOT_FOUND` | 当前病例下不存在该检查项。 |
|
|
|
|
|
|
| `ORDER_ITEM_TYPE_MISMATCH` | 用体格检查接口申请辅助检查,或用辅助检查接口申请体格检查。 |
|
|
|
|
|
|
| `TREATMENT_REQUIRED` | 未提交治疗就生成评价。 |
|
|
|
|
|
|
| `EVALUATION_NOT_FOUND` | 评价不存在或不属于当前用户。 |
|
|
|
|
|
|
| `PDF_EXPORT_FAILED` | PDF 生成失败。 |
|
|
|
|
|
|
|
2026-06-05 12:57:02 +08:00
|
|
|
|
## 5. 认证与 Agent
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 获取当前用户
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/auth/me
|
|
|
|
|
|
Authorization: Bearer <access_token>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
成功响应:
|
|
|
|
|
|
|
|
|
|
|
|
```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": null,
|
|
|
|
|
|
"gender": 0,
|
|
|
|
|
|
"institution": 1,
|
|
|
|
|
|
"institution_id": 1,
|
|
|
|
|
|
"institution_name": "测试机构",
|
|
|
|
|
|
"department": 2,
|
|
|
|
|
|
"department_id": 2,
|
|
|
|
|
|
"department_name": "儿科",
|
|
|
|
|
|
"title_name": null,
|
|
|
|
|
|
"major": null,
|
|
|
|
|
|
"training_stage": null,
|
|
|
|
|
|
"learning_target": null,
|
|
|
|
|
|
"competency_profile": {},
|
|
|
|
|
|
"weak_dimensions": [],
|
|
|
|
|
|
"strong_dimensions": [],
|
|
|
|
|
|
"ai_preference": {},
|
|
|
|
|
|
"total_training_count": 0,
|
|
|
|
|
|
"total_case_count": 0,
|
|
|
|
|
|
"current_level": null,
|
|
|
|
|
|
"status": 1
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端规则:
|
|
|
|
|
|
|
|
|
|
|
|
- 进入 Agent 后首先调用本接口。
|
|
|
|
|
|
- 返回 200 且 `code=OK` 才允许进入训练页面。
|
|
|
|
|
|
- 后续所有请求继续携带相同 token。
|
|
|
|
|
|
- `user_id` 只用于展示或前端状态标识,不能由前端覆盖。
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 获取 Agent 能力
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/agent/hello
|
|
|
|
|
|
Authorization: Bearer <access_token>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应核心结构:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"user": {
|
|
|
|
|
|
"user_id": "37",
|
|
|
|
|
|
"role": "student",
|
|
|
|
|
|
"source": "django_user_center",
|
|
|
|
|
|
"username": "13700000099",
|
|
|
|
|
|
"display_name": "测试用户"
|
|
|
|
|
|
},
|
|
|
|
|
|
"features": {
|
|
|
|
|
|
"stream_chat": true,
|
|
|
|
|
|
"score_types": ["percentage", "five_point"],
|
|
|
|
|
|
"pdf_export": true,
|
|
|
|
|
|
"knowledge_search": true,
|
|
|
|
|
|
"llm_mock_enabled": false,
|
|
|
|
|
|
"llm_mode": "real",
|
|
|
|
|
|
"llm_fallback_to_mock": false,
|
|
|
|
|
|
"llm_fast_model": "deepseek-chat",
|
|
|
|
|
|
"llm_reason_model": "deepseek-reasoner",
|
|
|
|
|
|
"runtime_memory_backend": "redis",
|
|
|
|
|
|
"auth_validate_enabled": true,
|
|
|
|
|
|
"auth_source": "django_user_center"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 病例接口
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 病例列表
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/cases?department_id=2&mode=practice
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Query 参数:
|
|
|
|
|
|
|
|
|
|
|
|
| 参数 | 类型 | 必填 | 说明 |
|
|
|
|
|
|
|---|---|---:|---|
|
|
|
|
|
|
| `department_id` | integer | 否 | 科室 ID。 |
|
|
|
|
|
|
| `training_type` | string | 否 | `case_analysis`、`diagnosis_treatment`、`consultation`。 |
|
|
|
|
|
|
| `mode` | string | 否 | `practice` 或 `teaching`。 |
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"items": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": 2,
|
|
|
|
|
|
"case_code": "SRC_2",
|
|
|
|
|
|
"department_id": 2,
|
|
|
|
|
|
"title": "支气管肺炎 - 6岁男性患儿",
|
|
|
|
|
|
"difficulty": "medium",
|
|
|
|
|
|
"chief_complaint": "发热、咳嗽4天,喘息1天",
|
|
|
|
|
|
"supported_training_type": "diagnosis_treatment",
|
|
|
|
|
|
"supported_mode": "free_chat",
|
|
|
|
|
|
"has_teaching_video": false,
|
|
|
|
|
|
"has_knowledge_points": true,
|
|
|
|
|
|
"has_quiz": false
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 病例详情
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/cases/{case_id}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应不会返回标准诊断、标准治疗、隐藏病史、评分规则和检查结果:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"id": 2,
|
|
|
|
|
|
"case_code": "SRC_2",
|
|
|
|
|
|
"title": "支气管肺炎 - 6岁男性患儿",
|
|
|
|
|
|
"department": "儿科",
|
|
|
|
|
|
"difficulty": "medium",
|
|
|
|
|
|
"patient": {
|
|
|
|
|
|
"name": null,
|
|
|
|
|
|
"age": 6,
|
|
|
|
|
|
"gender": "male",
|
|
|
|
|
|
"occupation": null
|
|
|
|
|
|
},
|
|
|
|
|
|
"chief_complaint": "发热、咳嗽4天,喘息1天",
|
|
|
|
|
|
"supported_training_type": "diagnosis_treatment",
|
|
|
|
|
|
"supported_mode": "free_chat",
|
|
|
|
|
|
"has_teaching_video": false,
|
|
|
|
|
|
"has_knowledge_points": true,
|
|
|
|
|
|
"has_quiz": false,
|
|
|
|
|
|
"order_item_types": ["imaging", "lab", "vital_sign"]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
### 6.3 推荐配置信息
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
推荐配置信息用于训练页初始化病人沟通风格。后端会读取病例主表内容,根据病例年龄、标题、主诉、描述和标签推断默认值。例如儿科患儿病例默认推荐 `age_group=child`;急诊、危重、低氧等关键词会推荐 `visit_environment=emergency` 和 `personality=anxious`。
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/training-config/recommended?case_id=2
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
入参:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"case_id": 2
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"case_id": 2,
|
|
|
|
|
|
"recommended": {
|
|
|
|
|
|
"visit_environment": "outpatient",
|
2026-06-08 15:16:07 +08:00
|
|
|
|
"age_group": "child",
|
2026-06-05 12:57:02 +08:00
|
|
|
|
"education_level": "higher",
|
|
|
|
|
|
"personality": "calm"
|
|
|
|
|
|
},
|
|
|
|
|
|
"recommended_labels": {
|
|
|
|
|
|
"visit_environment": "门诊",
|
2026-06-08 15:16:07 +08:00
|
|
|
|
"age_group": "儿童",
|
2026-06-05 12:57:02 +08:00
|
|
|
|
"education_level": "高等教育",
|
|
|
|
|
|
"personality": "平和"
|
|
|
|
|
|
},
|
2026-06-08 15:16:07 +08:00
|
|
|
|
"options": {}
|
2026-06-05 12:57:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
### 6.4 训练配置信息
|
|
|
|
|
|
|
|
|
|
|
|
训练配置信息用于前端渲染自定义配置页面,返回推荐值和全部可选项。前端可以直接使用 `recommended` 作为默认选中项,用户修改后把 `patient_config` 传给创建会话接口。
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/training-config/options?case_id=2
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
返回字段与推荐配置信息一致,`options` 包含以下可选值:
|
|
|
|
|
|
|
|
|
|
|
|
| 配置字段 | 可选值 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `visit_environment` | `outpatient`、`emergency`、`ward` | 门诊、急诊、病房。 |
|
|
|
|
|
|
| `age_group` | `child`、`youth`、`middle_aged`、`elderly` | 儿童、青年、中年、老年。 |
|
|
|
|
|
|
| `education_level` | `primary_or_below`、`secondary`、`higher` | 小学及以下、中等教育、高等教育。 |
|
|
|
|
|
|
| `personality` | `calm`、`anxious`、`impatient`、`cooperative`、`suspicious` | 平和、焦虑、急躁、配合、多疑。 |
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
## 7. 创建训练会话
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"case_id": 2,
|
|
|
|
|
|
"training_type": "diagnosis_treatment",
|
|
|
|
|
|
"mode": "practice",
|
|
|
|
|
|
"score_type": "percentage",
|
|
|
|
|
|
"patient_config": {
|
|
|
|
|
|
"visit_environment": "outpatient",
|
2026-06-08 15:16:07 +08:00
|
|
|
|
"age_group": "child",
|
2026-06-05 12:57:02 +08:00
|
|
|
|
"education_level": "higher",
|
|
|
|
|
|
"personality": "calm"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 允许值 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `training_type` | `case_analysis`、`diagnosis_treatment`、`consultation` | 训练类别。 |
|
|
|
|
|
|
| `mode` | `practice`、`teaching` | 正式前端使用的两种模式。 |
|
|
|
|
|
|
| `score_type` | `percentage`、`five_point` | 百分制或五分制。 |
|
2026-06-08 15:16:07 +08:00
|
|
|
|
| `patient_config.visit_environment` | `outpatient`、`emergency`、`ward` | 就诊环境,影响 AI 病人沟通节奏。 |
|
|
|
|
|
|
| `patient_config.age_group` | `child`、`youth`、`middle_aged`、`elderly` | 年龄段,影响病人或家属表达方式。 |
|
|
|
|
|
|
| `patient_config.education_level` | `primary_or_below`、`secondary`、`higher` | 文化程度,影响医学术语理解和表达清晰度。 |
|
|
|
|
|
|
| `patient_config.personality` | `calm`、`anxious`、`impatient`、`cooperative`、`suspicious` | 性格,影响情绪和配合度。 |
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
后端仍接受旧值 `novice`,但会自动转换为 `practice`。正式前端不要继续使用 `novice`。
|
2026-06-08 15:16:07 +08:00
|
|
|
|
`patient_config` 为可选字段;未传时后端会根据当前病例自动使用推荐配置。前端训练页已调用推荐配置接口时,应把用户最终选中的配置传入本接口。
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"session_id": 10,
|
|
|
|
|
|
"session_code": "sess_20260604100000_abcd1234",
|
|
|
|
|
|
"status": "inquiry",
|
|
|
|
|
|
"patient_opening": "家长:医生,孩子发热咳嗽好几天了,昨天开始喘得厉害,精神也不太好。",
|
|
|
|
|
|
"patient_config": {
|
|
|
|
|
|
"values": {
|
|
|
|
|
|
"visit_environment": "outpatient",
|
2026-06-08 15:16:07 +08:00
|
|
|
|
"age_group": "child",
|
2026-06-05 12:57:02 +08:00
|
|
|
|
"education_level": "higher",
|
|
|
|
|
|
"personality": "calm"
|
|
|
|
|
|
},
|
|
|
|
|
|
"labels": {
|
|
|
|
|
|
"visit_environment": "门诊",
|
2026-06-08 15:16:07 +08:00
|
|
|
|
"age_group": "儿童",
|
2026-06-05 12:57:02 +08:00
|
|
|
|
"education_level": "高等教育",
|
|
|
|
|
|
"personality": "平和"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端创建新会话时必须清空上一轮 Chat、检查结果、诊断、治疗和评价状态。
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 问诊接口
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 非流式问诊
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/chat
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"message": "孩子发热几天了?最高体温多少?"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"reply": "发热有4天了,最高烧到39度多,吃了退烧药能降下来,但过几个小时又会烧。",
|
|
|
|
|
|
"latency_ms": 2500,
|
|
|
|
|
|
"model": "deepseek-chat",
|
|
|
|
|
|
"fallback_used": false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 SSE 流式问诊
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/chat/stream
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
Accept: text/event-stream
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求体与非流式接口相同。
|
|
|
|
|
|
|
|
|
|
|
|
事件格式:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
event: message_delta
|
|
|
|
|
|
data: {"delta":"发热有4天了,"}
|
|
|
|
|
|
|
|
|
|
|
|
event: message_done
|
|
|
|
|
|
data: {"latency_ms":3200,"first_token_ms":800,"model":"deepseek-chat","fallback_used":false}
|
|
|
|
|
|
|
|
|
|
|
|
event: error
|
|
|
|
|
|
data: {"code":"LLM_STREAM_TIMEOUT","message":"AI 病人首段回复超时,请重试或关闭流式模式"}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端必须处理:
|
|
|
|
|
|
|
|
|
|
|
|
| 事件 | 处理 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `message_delta` | 将 `delta` 追加到当前 AI 气泡。 |
|
|
|
|
|
|
| `message_done` | 结束 pending,启用发送按钮。 |
|
|
|
|
|
|
| `error` | 结束 pending,显示错误信息。 |
|
|
|
|
|
|
| 请求中断或流结束但没有 `message_done` | 结束 pending,提示重试。 |
|
|
|
|
|
|
|
|
|
|
|
|
流式请求使用 `fetch`,不要使用原生 `EventSource`,因为该接口是 `POST` 且需要请求体和 Authorization。
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
async function streamChat(baseUrl: string, token: string, sessionId: number, message: string) {
|
|
|
|
|
|
const response = await fetch(`${baseUrl}/sessions/${sessionId}/chat/stream`, {
|
|
|
|
|
|
method: "POST",
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
|
Accept: "text/event-stream",
|
|
|
|
|
|
Authorization: `Bearer ${token}`,
|
|
|
|
|
|
"X-Entry-Scene": "vue_frontend",
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify({ message }),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok || !response.body) {
|
|
|
|
|
|
throw new Error(`stream request failed: ${response.status}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const reader = response.body.getReader();
|
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
|
|
let buffer = "";
|
|
|
|
|
|
let completed = false;
|
|
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
const { value, done } = await reader.read();
|
|
|
|
|
|
if (done) break;
|
|
|
|
|
|
buffer += decoder.decode(value, { stream: true });
|
|
|
|
|
|
const blocks = buffer.split("\n\n");
|
|
|
|
|
|
buffer = blocks.pop() || "";
|
|
|
|
|
|
|
|
|
|
|
|
for (const block of blocks) {
|
|
|
|
|
|
const event = block.match(/^event:\s*(.+)$/m)?.[1];
|
|
|
|
|
|
const rawData = block.match(/^data:\s*(.+)$/m)?.[1];
|
|
|
|
|
|
if (!event || !rawData) continue;
|
|
|
|
|
|
const data = JSON.parse(rawData);
|
|
|
|
|
|
|
|
|
|
|
|
if (event === "message_delta") {
|
|
|
|
|
|
// appendAiText(data.delta)
|
|
|
|
|
|
} else if (event === "message_done") {
|
|
|
|
|
|
completed = true;
|
|
|
|
|
|
} else if (event === "error") {
|
|
|
|
|
|
throw new Error(data.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!completed) throw new Error("AI 流式回复未正常结束");
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.3 练习提示
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/hints
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"last_user_message": "孩子发热几天了?最高体温多少?",
|
|
|
|
|
|
"scope": "current_conversation"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"hints": ["可以继续追问热型、退热药反应和呼吸困难表现。"],
|
|
|
|
|
|
"missing_dimensions": ["既往史", "严重程度评估"],
|
|
|
|
|
|
"next_questions": [
|
|
|
|
|
|
"孩子以前有没有喘息、哮喘或过敏史?",
|
|
|
|
|
|
"孩子现在有没有呼吸困难或口唇发紫?"
|
|
|
|
|
|
],
|
|
|
|
|
|
"recommended_orders": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"item_code": "spo2",
|
|
|
|
|
|
"reason": "用于评估低氧和病情严重程度"
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
约束:
|
|
|
|
|
|
|
|
|
|
|
|
- 仅 `practice` 模式可调用。
|
|
|
|
|
|
- 仅 `inquiry` 状态可调用。
|
|
|
|
|
|
- 前端默认不自动展示提示,由用户点击后调用。
|
|
|
|
|
|
|
|
|
|
|
|
## 9. 检查与检验
|
|
|
|
|
|
|
|
|
|
|
|
### 9.1 获取当前病例检查项
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/sessions/{session_id}/order-items
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"items": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"item_code": "blood_routine",
|
|
|
|
|
|
"item_name": "血常规",
|
|
|
|
|
|
"item_type": "lab"
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端不得写死检查项编码。不同病例拥有不同检查项,必须使用本接口返回的 `item_code`。
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
### 9.2 申请检查
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/orders
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"item_code": "blood_routine"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```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 生成。
|
|
|
|
|
|
- 检查结果会写入本次会话上下文和评分依据。
|
|
|
|
|
|
- 同一会话重复申请相同 `item_code` 时,返回已有结果并设置 `already_ordered=true`。
|
|
|
|
|
|
- 前端按 `item_code` 去重;点击后立即禁用,避免重复请求。
|
|
|
|
|
|
- 检查申请允许在 `inquiry`、`diagnosis`、`treatment` 状态执行。
|
|
|
|
|
|
|
|
|
|
|
|
## 10. 阶段提交
|
|
|
|
|
|
|
|
|
|
|
|
### 10.1 完成问诊
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/complete-inquiry
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求体:无。
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"session_id": 10,
|
|
|
|
|
|
"status": "diagnosis"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
至少完成一轮医生提问,否则返回 `INQUIRY_REQUIRED`。
|
|
|
|
|
|
|
|
|
|
|
|
### 10.2 提交诊断
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/diagnosis
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"primary_diagnosis": "支气管肺炎",
|
|
|
|
|
|
"differential_diagnoses": [
|
|
|
|
|
|
"支气管哮喘急性发作",
|
|
|
|
|
|
"上呼吸道感染"
|
|
|
|
|
|
],
|
|
|
|
|
|
"diagnosis_basis": "结合发热、咳嗽、喘息、肺部体征、炎症指标、胸片和血氧结果,符合儿童支气管肺炎表现。"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
成功响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"status": "treatment"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 10.3 提交治疗
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/treatment
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"treatment_principle": "抗感染、止咳平喘、改善氧合并严密观察病情变化。",
|
|
|
|
|
|
"treatment_measures": "根据病情进行抗感染治疗,必要时雾化缓解喘息,监测体温、呼吸和血氧。",
|
|
|
|
|
|
"risk_plan": "关注低氧、呼吸困难加重、持续高热、精神反应差和脱水。",
|
|
|
|
|
|
"communication": "向家属说明病情、用药注意事项、危险信号和复诊指征。",
|
|
|
|
|
|
"follow_up": "治疗后复查体温、呼吸、血氧和必要的炎症指标。"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
成功响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"status": "evaluating"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 11. AI 评价、历史记录与 PDF
|
|
|
|
|
|
|
|
|
|
|
|
### 11.1 生成评价
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/sessions/{session_id}/evaluation
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"score_type": "percentage"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"evaluation_id": 101,
|
|
|
|
|
|
"score_type": "percentage",
|
|
|
|
|
|
"total_score": 82,
|
|
|
|
|
|
"dimension_scores": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"dimension": "信息获取",
|
|
|
|
|
|
"score": 18,
|
|
|
|
|
|
"max_score": 25,
|
|
|
|
|
|
"comment": "已覆盖主要症状,但既往喘息史追问不足。",
|
|
|
|
|
|
"evidence": ["询问发热天数和最高体温"],
|
|
|
|
|
|
"deductions": ["未充分询问既往喘息史"],
|
|
|
|
|
|
"improvement": "补充既往史、过敏史和严重程度评估。"
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"score_details": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"id": 501,
|
|
|
|
|
|
"record_id": 101,
|
|
|
|
|
|
"rule_id": 1,
|
|
|
|
|
|
"dimension": "信息获取",
|
|
|
|
|
|
"score": 18,
|
|
|
|
|
|
"deducted_reason": "未充分询问既往喘息史",
|
|
|
|
|
|
"evidence_message_ids": ["询问发热天数和最高体温"],
|
|
|
|
|
|
"ai_confidence": 0.85,
|
|
|
|
|
|
"comment": "补充既往史和过敏史。"
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"errors": [],
|
|
|
|
|
|
"improvement_plan": ["加强儿童肺炎严重程度评估训练。"],
|
|
|
|
|
|
"evidence_summary": ["检查结果已写入评分依据。"],
|
|
|
|
|
|
"guideline_refs": [],
|
|
|
|
|
|
"overall_comment": "诊断方向正确,问诊完整性和沟通细节仍需加强。"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
评价成功后:
|
|
|
|
|
|
|
|
|
|
|
|
- 会话状态变为 `completed`。
|
|
|
|
|
|
- 写入 `training_record` 和 `training_score_detail`。
|
|
|
|
|
|
- 当前 Redis 短期聊天 memory 被释放。
|
|
|
|
|
|
- 相同会话再次调用评价接口时返回已存在的评价,不重复创建。
|
|
|
|
|
|
|
|
|
|
|
|
### 11.2 历史评价列表
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/evaluations
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"items": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"evaluation_id": 101,
|
|
|
|
|
|
"case_title": "支气管肺炎 - 6岁男性患儿",
|
|
|
|
|
|
"score_type": "percentage",
|
|
|
|
|
|
"total_score": 82,
|
|
|
|
|
|
"created_at": "2026-06-04T10:00:00",
|
|
|
|
|
|
"pdf_exported": true
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 11.3 评价详情
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/evaluations/{evaluation_id}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
返回字段包含完整评价结构,并额外包含:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"session_id": 10,
|
|
|
|
|
|
"case_id": 2,
|
|
|
|
|
|
"case_title": "支气管肺炎 - 6岁男性患儿",
|
|
|
|
|
|
"created_at": "2026-06-04T10:00:00",
|
|
|
|
|
|
"pdf_file_path": "/app/storage/reports/training_record_101_percentage_xxx.pdf"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 11.4 导出 PDF
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/evaluations/{evaluation_id}/export-pdf
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "OK",
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"export_id": 101,
|
|
|
|
|
|
"file_path": "/app/storage/reports/training_record_101_percentage_xxx.pdf"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
该接口只生成 PDF 并返回服务器内部文件路径,适合前端展示“导出成功”和保存导出记录。
|
|
|
|
|
|
|
|
|
|
|
|
### 11.5 下载 PDF 文件流
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/evaluations/{evaluation_id}/download-pdf
|
|
|
|
|
|
Authorization: Bearer <access_token>
|
|
|
|
|
|
X-Entry-Scene: vue_frontend
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
成功响应不是统一 JSON 包装,而是 PDF 文件流:
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
|
|
Content-Type: application/pdf
|
|
|
|
|
|
Content-Disposition: attachment; filename="training_record_101_percentage_xxx.pdf"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
错误响应仍然使用统一 JSON:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": "EVALUATION_NOT_FOUND",
|
|
|
|
|
|
"message": "evaluation not found",
|
|
|
|
|
|
"data": null
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端通过浏览器直接下载时不能使用普通 `<a href>` 携带 Bearer token,应使用 `fetch` 或 `axios` 的 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 12. 管理与调试接口
|
|
|
|
|
|
|
|
|
|
|
|
### 12.1 病例 SQL 预检
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/imports/case-sql/preview
|
|
|
|
|
|
Content-Type: multipart/form-data
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
FormData:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
file=<病例SQL文件>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
只读取以下四张源表:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
case_base
|
|
|
|
|
|
traditional_case
|
|
|
|
|
|
teaching_case
|
|
|
|
|
|
scoring_rule
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
预检不会写入数据库。
|
|
|
|
|
|
|
|
|
|
|
|
### 12.2 确认导入
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/imports/case-sql/apply
|
|
|
|
|
|
Content-Type: multipart/form-data
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
后端根据导入病例生成或更新 `case_exam_item`,导入后病例列表可立即查询。
|
|
|
|
|
|
|
|
|
|
|
|
### 12.3 病例删除
|
|
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/cases/{case_id}/delete-preview
|
|
|
|
|
|
DELETE /api/v1/cases/{case_id}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
删除请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"confirm": true,
|
|
|
|
|
|
"delete_training_data": true
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
删除会级联清理病例、病例扩展数据、检查项、评分规则以及相关训练数据。学生前端不得开放该功能。
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
### 12.4 知识检索
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
GET /api/v1/knowledge/search?department_id=2&training_type=diagnosis_treatment&q=肺炎,血氧
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-08 15:16:07 +08:00
|
|
|
|
### 12.5 LLM 测试
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
|
POST /api/v1/llm/test/deepseek-fast
|
|
|
|
|
|
POST /api/v1/llm/test/deepseek-reason
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
请求:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"message": "请用一句话说明医疗问诊训练的用途。"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 13. 前端 Axios 封装示例
|
|
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
|
import axios from "axios";
|
|
|
|
|
|
|
|
|
|
|
|
export const apiClient = axios.create({
|
|
|
|
|
|
baseURL: "http://8.160.178.88/fastapi/api/v1",
|
|
|
|
|
|
timeout: 60000,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
apiClient.interceptors.request.use((config) => {
|
|
|
|
|
|
const token = sessionStorage.getItem("access_token");
|
|
|
|
|
|
if (token) config.headers.Authorization = `Bearer ${token}`;
|
|
|
|
|
|
config.headers["X-Entry-Scene"] = "vue_frontend";
|
|
|
|
|
|
config.headers["X-Request-Id"] = crypto.randomUUID();
|
|
|
|
|
|
return config;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
apiClient.interceptors.response.use(
|
|
|
|
|
|
(response) => {
|
|
|
|
|
|
if (response.data?.code !== "OK") {
|
|
|
|
|
|
return Promise.reject(new Error(response.data?.message || response.data?.code));
|
|
|
|
|
|
}
|
|
|
|
|
|
return response;
|
|
|
|
|
|
},
|
|
|
|
|
|
(error) => {
|
|
|
|
|
|
if (error.response?.status === 401) {
|
|
|
|
|
|
// 返回宿主系统登录页或触发 token 刷新
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.reject(error);
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 14. 常见错误码
|
|
|
|
|
|
|
|
|
|
|
|
| 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` | 当前病例不存在该检查项。 |
|
|
|
|
|
|
| 404 | `EVALUATION_NOT_FOUND` | 评价不存在或不属于当前用户。 |
|
|
|
|
|
|
| 504 | `LLM_CALL_TIMEOUT` | 非流式问诊超时。 |
|
|
|
|
|
|
| SSE error | `LLM_STREAM_TIMEOUT` | 流式问诊首段或总耗时超时。 |
|
|
|
|
|
|
| SSE error | `LLM_STREAM_FAILED` | 流式模型调用失败。 |
|
|
|
|
|
|
| SSE error | `LLM_EMPTY_RESPONSE` | 模型未返回有效文本。 |
|
|
|
|
|
|
| 500 | `PDF_EXPORT_FAILED` | PDF 生成失败。 |
|
2026-06-08 15:16:07 +08:00
|
|
|
|
| 400 | `CASE_SQL_IMPORT_INVALID` | 病例 SQL 预检或导入失败。 |
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
## 15. 前端功能验收顺序
|
|
|
|
|
|
|
|
|
|
|
|
1. 打开 `/health/ready`,确认返回 `status=ready`。
|
|
|
|
|
|
2. 使用真实 token 调用 `/auth/me`,确认返回 Django 用户 `id`。
|
|
|
|
|
|
3. 调用 `/agent/hello`,确认 `llm_mode=real`、`runtime_memory_backend=redis`。
|
|
|
|
|
|
4. 获取病例列表和病例详情。
|
|
|
|
|
|
5. 创建 `practice` 会话。
|
|
|
|
|
|
6. 测试普通问诊和 SSE 流式问诊。
|
|
|
|
|
|
7. 点击查看提示,确认返回动态提示。
|
|
|
|
|
|
8. 动态读取检查项并申请检查,重复申请时确认 `already_ordered=true`。
|
|
|
|
|
|
9. 完成问诊、提交诊断、提交治疗。
|
|
|
|
|
|
10. 生成评价,确认包含 `dimension_scores` 和 `score_details`。
|
|
|
|
|
|
11. 查询历史列表和评价详情。
|
2026-06-08 15:16:07 +08:00
|
|
|
|
12. 生成 PDF,确认接口返回文件路径。
|
|
|
|
|
|
13. 下载 PDF 文件流,确认浏览器触发文件下载。
|
2026-06-05 12:57:02 +08:00
|
|
|
|
|
|
|
|
|
|
## 16. 当前前端必须了解的限制
|
|
|
|
|
|
|
|
|
|
|
|
- 无会话详情和聊天恢复接口,页面刷新后无法恢复短期问诊。
|
|
|
|
|
|
- 问诊聊天只保存在 Redis 短期 memory 中,评价完成后释放。
|
2026-06-08 15:16:07 +08:00
|
|
|
|
- PDF 支持两种调用:`export-pdf` 返回服务器文件路径,`download-pdf` 返回 `application/pdf` 文件流并触发浏览器下载。
|
|
|
|
|
|
- 病例导入和删除接口尚未增加管理员角色授权,学生前端必须隐藏。
|
2026-06-05 12:57:02 +08:00
|
|
|
|
- 所有检查项必须从 `/order-items` 动态读取,不能写死。
|
|
|
|
|
|
- 正式训练模式只有 `practice` 和 `teaching`;`novice` 仅为旧接口兼容值。
|