Files
Mdeical_Sur_Report/docs/api-contract.md

18 KiB
Raw Blame History

API 契约草案

本文档定义后端化时的接口语义和权限边界。当前项目已实现认证、报告、模板、用户/部门、设置和签名文件接口,其余接口仍用于指导后续后端化。

当前后端骨架已实现:

  • GET /api/health
  • POST /api/auth/login
  • GET /api/auth/me
  • POST /api/auth/logout
  • GET /api/reports
  • POST /api/reports
  • GET /api/reports/:id
  • PATCH /api/reports/:id
  • DELETE /api/reports/:id
  • GET /api/templates
  • POST /api/templates
  • GET /api/templates/:id
  • PATCH /api/templates/:id
  • DELETE /api/templates/:id
  • GET /api/users
  • POST /api/users
  • GET /api/users/:id
  • PATCH /api/users/:id
  • DELETE /api/users/:id
  • GET /api/departments
  • POST /api/departments
  • PATCH /api/departments/:id
  • DELETE /api/departments/:id
  • PATCH /api/departments/:id/template-permissions
  • GET /api/settings/system
  • PATCH /api/settings/system
  • POST /api/settings/system/reset
  • POST /api/users/:id/signature
  • DELETE /api/users/:id/signature
  • GET /api/files/:id/content
  • GET /api/ai/models
  • POST /api/ai/chat
  • GET /api/speech/iat WebSocket
  • GET /api/library/fields
  • PATCH /api/library/fields
  • GET /api/files
  • POST /api/files
  • DELETE /api/files/:id

报告媒体关系表、数据库 Session 和第一版审计日志已实现;审计日志查询 API 已提供给超级管理员和管理员使用;第三方调用摘要和后端导出接口仍是后续项。

通用约定

Base URL

/api

认证

建议使用短期 Access Token + HttpOnly Refresh Cookie或完整 HttpOnly Session Cookie。前端不应保存密码、完整用户列表或第三方服务密钥。

响应格式

成功:

{
  "data": {},
  "requestId": "req_xxx"
}

失败:

{
  "error": {
    "code": "FORBIDDEN",
    "message": "无权访问该资源"
  },
  "requestId": "req_xxx"
}

常用状态码

状态码 含义
200 请求成功。
201 创建成功。
204 删除或无响应体操作成功。
400 请求参数错误。
401 未登录或登录态失效。
403 已登录但无权限。
404 资源不存在,或当前用户无权感知该资源。
409 唯一约束或状态冲突。
422 业务校验失败。

核心类型

User

type UserRole = 'super' | 'admin' | 'doctor';

interface UserDTO {
  id: string;
  username: string;
  role: UserRole;
  name: string;
  departmentId: string;
  departmentName?: string;
  department?: string;
  status: 'active' | 'inactive';
  phone?: string;
  email?: string;
  signatureFileId?: string;
  signature?: string;
  visibleTemplates?: string[];
  manageableTemplates?: string[];
  createdAt: string;
  updatedAt: string;
}

后端不得返回 password_hash

Report

interface ReportDTO {
  id: string;
  title: string;
  patientName: string;
  hospitalId: string;
  departmentId: string;
  departmentName: string;
  authorId: string;
  authorName: string;
  status: 'draft' | 'completed';
  revision: number;
  content: string;
  videos?: Array<{
    id: string;
    name: string;
    url: string;
    duration: number;
    fileId?: string;
  }>;
  capturedFrames?: Array<{
    id: number;
    videoIndex: number;
    videoName: string;
    time: number;
    timeFormatted: string;
    dataUrl: string;
    fileId?: string;
  }>;
  createdAt: string;
  updatedAt: string;
}

已完成报告再次修改时,后端必须递增 revision 并创建历史版本。videoscapturedFrames 是前端兼容 DTO 字段,后端持久化到 ReportMedia 并关联 FileResource

Template

interface TemplateDTO {
  id: string;
  name: string;
  description?: string;
  scope: 'department' | 'personal';
  departmentId?: string;
  ownerUserId?: string;
  content: string;
  fields: FormFieldDTO[];
  canUse: boolean;
  canManage: boolean;
  createdAt: string;
  updatedAt: string;
}

医生个人模板必须只对本人可见和可用。

Auth API

POST /api/auth/login

请求:

{
  "username": "0001",
  "password": "123456"
}

响应:

{
  "data": {
    "user": {
      "id": "usr_0001",
      "username": "0001",
      "role": "doctor",
      "name": "张医生",
      "departmentId": "dept_surgery",
      "departmentName": "外科",
      "status": "active"
    }
  }
}

规则:

  • 禁用用户返回 403 FORBIDDEN
  • 用户名或密码错误返回 401 UNAUTHORIZED
  • 前端通过 HttpOnly Session Cookie 维持登录态,不保存 access token。

GET /api/auth/me

返回当前登录用户。前端启动后用它恢复登录态,并把后端 doctor 角色映射为当前前端兼容类型 user

POST /api/auth/logout

清除服务端会话或 refresh cookie。

Reports API

GET /api/reports

查询参数:

q?: string
status?: draft | completed
dateRange?: today | week | month
page?: number
pageSize?: number

权限过滤:

  • 超级管理员:返回全部报告。
  • 管理员:只返回本部门报告。
  • 医生:只返回本人报告。

响应:

{
  "data": {
    "items": [],
    "total": 0,
    "page": 1,
    "pageSize": 20
  }
}

POST /api/reports

创建报告。后端写入 authorIddepartmentIdrevision = 1。 当前实现接收前端兼容 Report 对象,核心字段进入 Report 表,视频/关键帧引用进入 ReportMedia 表,其他扩展字段进入 metadata

GET /api/reports/:id

权限:

  • 超级管理员可读所有报告。
  • 管理员可读本部门报告。
  • 医生可读本人报告。

无权限当前返回 403

PATCH /api/reports/:id

规则:

  • 超级管理员可编辑任何报告。
  • 管理员可编辑本部门报告。
  • 医生可编辑本人报告。
  • 如果原报告状态为 completed,每次保存递增 revision
  • 每次保存创建历史版本。

POST /api/reports/:id/complete

把报告标记为完成。完成前必须校验患者姓名、住院号等必填字段。 当前实现暂未单独开放该接口,前端通过 PATCH /api/reports/:id 传入 status = completed 完成报告。

DELETE /api/reports/:id

规则:

  • 超级管理员可删除全部报告。
  • 管理员可删除本部门报告。
  • 医生可删除本人报告,包括已完成报告。

建议先做软删除。 当前实现已做软删除,写入 deletedAtdeletedBy

GET /api/reports/:id/history

返回报告历史版本:

{
  "data": [
    {
      "id": "hist_1",
      "revision": 1,
      "content": "<p>旧内容</p>",
      "updatedBy": "张医生",
      "updatedAt": "2026-05-01T08:00:00.000Z",
      "action": "complete_report"
    }
  ]
}

GET /api/reports/:id/export.json

返回结构化报告 JSON。权限与查看报告一致。当前确定不需要水印、导出原因、审批或专门导出审计用户可见前端 Blob JSON 导出入口已移除,如后续需要结构化交换应由后端接口统一提供。

Templates API

GET /api/templates

权限过滤:

  • 超级管理员:全部模板。
  • 管理员:本部门模板和被授权模板。
  • 医生:本部门授权模板和自己的个人模板。

当前实现支持查询参数 access=use | manage,分别返回当前用户可使用或可管理的模板。

POST /api/templates

创建模板。

规则:

  • 超级管理员可创建部门模板并授权给部门。
  • 管理员可创建或修改本部门模板。
  • 医生只能创建个人模板,scope 必须为 personalownerUserId 必须是本人。

当前实现由后端根据 Session 用户写入归属部门或归属用户;前端不能伪造 ownerUserId

PATCH /api/templates/:id

规则:

  • 超级管理员可编辑任何模板。
  • 管理员可编辑本部门模板。
  • 医生只能编辑自己的个人模板。

DELETE /api/templates/:id

规则同编辑模板。删除部门模板时需确认是否已有报告引用;建议软删除。 当前实现会先把引用该模板的报告 templateId 置空,再删除模板。

POST /api/templates/:id/copy

医生复制部门模板为个人模板。

请求:

{
  "name": "我的腹腔镜模板"
}

响应返回新的 TemplateDTOscope = personal

Users API

GET /api/users

权限:

  • 超级管理员:全部用户。
  • 管理员:自己和本部门医生。
  • 医生:只返回自己。

当前实现返回前端兼容 User 结构,其中后端 doctor 会映射为前端历史角色 user,并带上由部门模板授权计算出来的 visibleTemplates/manageableTemplates

POST /api/users

规则:

  • 只有超级管理员可创建管理员。
  • 一个部门只能有一个管理员,冲突返回 409
  • 管理员只能创建本部门医生。
  • 创建医生前,该部门必须已有启用管理员。
  • 后端使用 Argon2 保存密码哈希,不返回明文密码。

PATCH /api/users/:id

规则:

  • 超级管理员可改任何用户。
  • 管理员可改本部门医生的基础信息、密码和状态。
  • 医生只能改自己的基础资料。
  • 只有超级管理员可修改角色、部门和管理员模板授权。

DELETE /api/users/:id

规则:

  • 禁止删除当前登录用户。
  • 禁止删除默认超级管理员 admin
  • 有报告、模板、审计等业务关联时返回 409,建议改为禁用账号。

Departments API

GET /api/departments

权限:

  • 超级管理员:全部部门。
  • 管理员和医生:本人所在部门。

POST /api/departments

只有超级管理员可创建部门。

PATCH /api/departments/:id

只有超级管理员可修改部门名称或编码。

DELETE /api/departments/:id

只有超级管理员可删除无业务关联的部门;不能删除当前登录用户所在部门。

PATCH /api/departments/:id/template-permissions

请求:

{
  "visibleTemplates": ["tpl_a"],
  "manageableTemplates": ["tpl_b"]
}

规则:

  • 只有超级管理员可维护部门模板授权。
  • manageableTemplates 会自动包含使用权。
  • 后端写入 template_department_permissions.canUse/canManage

Settings API

GET /api/settings/system

返回当前用户可用的系统设置。当前实现会合并全局设置和个人默认模板,并返回前端兼容 SystemSettings 对象。

注意AI 对话和讯飞语音均已改为后端代理。普通用户读取设置时不返回 AI Key、讯飞 APIKey 或讯飞 APISecret超级管理员仍可在设置页维护全局共用 Provider Key 和语音配置。

PATCH /api/settings/system

规则:

  • 超级管理员可修改全局设置、AI Provider、语音配置。
  • 医生可修改个人默认模板。
  • 管理员可修改个人默认模板。
  • 非超级管理员提交的其他字段会被忽略或拒绝,只保留个人默认模板。

POST /api/settings/system/reset

只有超级管理员可执行演示模式恢复出厂设置。当前实现不只是重置系统设置,而是把当前租户恢复为 demo mode

  • 用户只保留默认 adminmanager0001 三个账号,并重置为默认角色、部门、状态和密码。
  • 报告、报告历史、报告媒体、文件资源和审计日志会被清空。
  • 模板只保留默认“腹腔镜胆囊切除术报告”,模板 HTML 与图文报告生成的默认报告内容保持一致。
  • 系统设置恢复为演示默认值包含默认模板、抽帧策略、Kimi Provider 和讯飞语音代理配置。

前端必须做二次确认。该接口面向演示/测试环境,不应作为生产数据恢复或备份机制。

Signature Files API

POST /api/users/:id/signature

请求:

{
  "dataUrl": "data:image/jpeg;base64,...",
  "filename": "signature.jpg"
}

规则:

  • 用户本人、本部门管理员、超级管理员可上传或替换签名。
  • 当前支持 JPG、PNG、WebP大小限制 1MB。
  • 后端写入 FileResource.kind = SIGNATURE,并把 User.signatureFileId 指向新文件。

响应:

{
  "data": {
    "file": {
      "id": "file_xxx",
      "url": "/api/files/file_xxx/content",
      "mimeType": "image/jpeg",
      "size": 12345
    }
  }
}

DELETE /api/users/:id/signature

清除用户签名并删除后端文件资源。权限同上传。

GET /api/files/:id/content

返回文件二进制内容。当前已实现签名文件读取权限:

  • 本人可读自己的签名。
  • 本部门管理员可读本部门用户签名。
  • 超级管理员可读全部签名。
  • 报告相关文件预留继承报告权限。

AI API

GET /api/ai/models

后端读取全局共用 AI Provider 配置,请求 OpenAI 兼容 /models 并返回模型 ID 列表。前端“测试连接”使用该接口,不再直接携带 API Key 请求第三方服务。

POST /api/ai/chat

规则:

  • 后端托管第三方模型密钥。
  • 请求上下文只能包含当前报告内容和当前报告内用户有权访问的图片/关键帧。
  • 不允许跨部门检索报告作为上下文。
  • 当前实现接收 OpenAI 兼容 messages、温度等参数,后端会用全局 Provider 的 modelName 覆盖请求中的 model,所有用户共用同一套 key。
  • 报告编辑器 AI 写作请求会使用 OpenAI 兼容 JSON Moderesponse_format: { "type": "json_object" },同时在 prompt 中要求返回 JSON。
  • Kimi 默认主模型为 kimi-k2.6;对 K2.6/K2.5 请求会移除不兼容的采样参数,并默认传入 thinking: { "type": "disabled" } 以适配报告生成的低延迟场景。
  • Provider 可配置 fallbackModelName 作为备用模型;主模型在短暂重试后仍返回 429/5xx 或请求超时时,后端会自动改用备用模型再请求一次。
  • 上游模型返回过载、限流、5xx 等临时错误时,后端会对 /chat/completions 做短暂重试;无可用备用模型或备用模型仍失败时保留上游 HTTP 状态码,并通过错误码区分 AI_PROVIDER_OVERLOADEDAI_PROVIDER_RATE_LIMITEDAI_PROVIDER_QUOTA_EXCEEDEDAI_PROVIDER_UNAVAILABLEAI_PROVIDER_TIMEOUTAI_PROVIDER_ERROR。账户余额/额度不足不会触发重试或备用模型。

Speech API

GET /api/speech/iat WebSocket

浏览器通过当前登录 Session Cookie 发起 WebSocket upgrade。后端使用同一套 Session 中间件校验登录态,读取 Settings API 中的 xfSpeechConfig,连接讯飞 IAT WebSocket 并转发音频帧和识别结果。

客户端发送的首帧只需要包含音频 data

{
  "data": {
    "status": 0,
    "format": "audio/L16;rate=16000",
    "encoding": "raw",
    "audio": "base64-pcm"
  }
}

后端会在首帧补齐:

{
  "common": { "app_id": "server-side-app-id" },
  "business": { "language": "zh_cn", "domain": "iat", "accent": "mandarin" }
}

规则:

  • 未登录的 upgrade 返回 401
  • 未配置 APPID/APIKey/APISecret 时,代理向客户端返回错误消息并关闭连接。
  • 前端不得保存或拼接讯飞鉴权 URL。
  • 上游讯飞返回的识别消息原样转发给客户端。

Files API

GET /api/files

查询参数:

kind?: TEMPLATE_ASSET | VIDEO | FRAME | REPORT_EXPORT

返回当前租户内可读文件。TEMPLATE_ASSET 当前作为模板图片资源,登录用户可读取。

POST /api/files

通用文件上传接口。当前已用于模板图片资源、视频和关键帧,后续继续承载报告图片和导出文件。

请求:

{
  "kind": "TEMPLATE_ASSET",
  "filename": "logo.png",
  "dataUrl": "data:image/png;base64,...",
  "reportId": "optional_report_id"
}

返回:

{
  "data": {
    "file": {
      "id": "file_xxx",
      "filename": "logo.png",
      "mimeType": "image/png",
      "size": 1234,
      "url": "/api/files/file_xxx/content",
      "createdAt": "2026-05-02T00:00:00.000Z"
    }
  }
}

DELETE /api/files/:id

删除当前用户可管理的文件。超级管理员可删全部,同 owner 可删自己的文件,管理员可删除模板图片资源。

Library API

GET /api/library/fields

返回字段库:

interface FieldLibraryDTO {
  formFields: FormField[];
  customTimeFormats: string[];
  multiSelectOptions: Record<string, string[]>;
  anesthesiaOptions: string[];
}

PATCH /api/library/fields

保存字段库。当前第一版保存为租户全局 SystemSetting.key = fieldLibrary,前端仍会同步本地兼容缓存。

文件权限继承业务对象:

  • 报告文件继承报告权限。
  • 模板图片资源当前登录用户可读取,删除权限限制为超级管理员、管理员或 owner。
  • 签名文件已先行通过 POST /api/users/:id/signature 实现。

Audit API

GET /api/audit-logs

查询审计日志。超级管理员可查看当前租户全部日志;管理员可查看本部门或自己作为操作者的日志;医生不可访问。

查询参数:

page=1
pageSize=50
action=report.complete
targetType=Report
actor=admin

返回:

{
  "data": {
    "items": [
      {
        "id": "log_xxx",
        "actorUsername": "admin",
        "actorName": "超级管理员",
        "actorRole": "super",
        "action": "report.complete",
        "targetType": "Report",
        "targetId": "report_xxx",
        "departmentId": "dept_xxx",
        "metadata": { "title": "报告标题" },
        "createdAt": "2026-05-02T00:00:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 50
  }
}

后续测试落点

后端骨架建立后,应把本文档转为真实测试:

  • Auth/HTTP 集成测试。
  • RBAC policy 单元测试。
  • Reports API 按角色过滤测试。
  • Report revision 递增测试。
  • Templates API 部门模板和个人模板测试。
  • Settings API 保存和默认模板测试。
  • Signature Files API 上传和读取权限测试。
  • Library API 字段库保存测试。
  • Generic Files API 上传、列表和删除测试。
  • AI 代理上下文隔离测试。
  • Speech WebSocket 未登录拒绝、首帧注入和上游错误转发测试。