# 后端化与用户化改造方案 本文基于当前迁移期版本整理需要改动的范围。当前系统已完成主业务后端化,目标是继续把开发回退、审计、安全、部署和运维能力收紧为“多用户、可审计、可部署、可维护”的前后端系统。 ## 改造目标 当前系统已经有较完整的页面、业务流程和后端 API。仍需关注的迁移期边界包括: - 用户、密码、权限、工作台统计、报告、报告媒体关系、模板、字段库、模板图片、用户和部门权限已优先接入后端 API;`localStorage.users` 仅作为开发回退兼容缓存存在。 - 设置和部分兼容缓存仍存在 `localStorage`;生产构建默认关闭本地回退。 - 签名、模板图片资源、视频和关键帧文件已优先进入后端文件资源;报告媒体引用已通过 `ReportMedia` 关系表保存。 - AI Key 和讯飞语音密钥已进入后端代理链路。 - 权限判断主要在前端页面里完成。 后端化后应达到: - 登录认证由后端完成。 - 所有数据以用户、部门、医院/租户为边界存储。 - 权限在后端强校验,前端只做展示控制。 - 文件和图片从 Base64 本地存储迁移到后端文件服务或对象存储。 - AI 与语音等密钥由后端托管,前端不接触真实密钥。 - 报告查看、编辑、删除等关键行为有审计记录;报告导出按权限控制,不要求专门导出审计。 ## 技术栈决策 后端化第一阶段建议采用 TypeScript 全栈方案,优先保证权限边界、模块结构、数据迁移和测试可维护。 | 层级 | 选择 | 说明 | | --- | --- | --- | | 前端 | React 19 + TypeScript + Vite + Tailwind CSS | 保留现有页面主体,逐步把 `localStorage` 读写替换为 API 调用。 | | 后端框架 | NestJS + TypeScript | 适合按认证、报告、模板、用户、设置、文件、AI 等模块拆分;Guard/Interceptor/Service 结构适合集中权限控制和审计。 | | 数据库 | PostgreSQL | 适合报告、历史版本、权限关系、审计日志等关系型数据;支持事务、索引和后续统计。 | | ORM/迁移 | Prisma | 类型生成清晰,迁移和 seed 成本低,便于前后端共享 DTO 思路。 | | 认证 | HttpOnly Cookie Session,Access Token 仅作为可选扩展 | 前端不直接持久化敏感 token;后端维护会话、退出和失效。第一阶段可用数据库会话表,后续再引入 Redis。 | | 密码 | Argon2 哈希 | 禁止明文密码进入前端或日志。 | | 文件存储 | 本地文件目录 + `files` 表,预留 MinIO/S3 兼容接口 | 开发和院内部署先降低成本;生产可切换对象存储。 | | 输入校验 | Zod 或 NestJS DTO 校验 | 所有 API 入参都必须校验,尤其是 HTML、文件、权限相关参数。 | | 测试 | Vitest/Testing Library + Playwright + 后端单元/集成测试 | 保留现有前端测试;新增后端 policy/service/API 测试;E2E 已从 localStorage seed 改为真实后端 API seed。 | | 部署 | Docker Compose | 前端静态资源、NestJS API、PostgreSQL、可选 Redis/MinIO 分服务部署。 | 暂不建议第一阶段使用微服务、复杂租户系统或后端抽帧服务。抽帧可以先继续留在前端 canvas,生成关键帧后上传后端文件接口。 ## 当前后端骨架 已新增 `server/` 目录作为后端化第一阶段基础: - `server/src/main.ts`:NestJS 启动入口,设置 `/api` 前缀、CORS、Cookie Session、数据库 Session Store 和统一错误响应。 - `server/src/dashboard`:`GET /api/dashboard/stats`,按角色范围统计报告、模板、用户和趋势。 - `server/src/audit`:审计服务和查询 API,记录并查看登录、报告、模板、用户、设置和文件修改。 - `server/src/health`:`GET /api/health` 健康检查。 - `server/src/auth`:`POST /api/auth/login`、`GET /api/auth/me`、`POST /api/auth/logout` 认证接口。 - `server/src/reports`:`GET/POST/PATCH/DELETE /api/reports` 报告接口,含角色范围过滤、历史版本、软删除和报告媒体关系同步。 - `server/src/templates`:`GET/POST/PATCH/DELETE /api/templates` 模板接口,含 `access=use/manage`、部门模板、部门授权和个人模板。 - `server/src/users`:`GET/POST/PATCH/DELETE /api/users` 与 `/api/departments` 接口,含用户范围过滤、管理员唯一性、医生创建约束和部门模板授权。 - `server/src/settings`:`GET/PATCH /api/settings/system` 和重置接口,保存全局设置与个人默认模板。 - `server/src/files`:通用文件上传、列表、删除、受控读取接口;签名文件会额外关联 `User.signatureFileId`。 - `server/src/library`:`GET/PATCH /api/library/fields` 字段库接口,保存字段、时间格式和选项库。 - `server/src/ai`:`GET /api/ai/models` 和 `POST /api/ai/chat`,使用全局共用 Provider Key 代理 OpenAI 兼容接口。 - `server/src/speech`:`GET /api/speech/iat` WebSocket 代理,使用 Session 校验用户,后端生成讯飞鉴权 URL 并转发音频帧和识别结果。 - `server/src/prisma`:PrismaService,使用 Prisma 7 的 PostgreSQL driver adapter。 - `server/src/permissions`:报告、模板、用户和管理员创建权限策略,已配套单元测试。 - `server/prisma/schema.prisma`:PostgreSQL 数据模型,覆盖租户、部门、用户、业务 Session、`AppSession`、报告、报告媒体、历史、模板、文件、设置和审计日志。 - `server/prisma/seed.ts`:写入默认医院、部门和 `admin`、`manager`、`0001` 三个账号。 当前前端登录、Dashboard、报告读写、报告媒体关系、模板读写、字段库、模板图片资源、视频/关键帧文件、用户管理、部门模板授权、系统设置、签名文件、AI 对话代理和语音听写代理已接入后端认证/API。真实 PostgreSQL 服务集成测试已覆盖 Auth、Dashboard、Reports、ReportMedia、Templates 和 Files 核心服务,下一步应逐步替换剩余开发回退数据访问。 ## 总体架构建议 ### 前端 保留当前 React + Vite 页面主体,逐步把 `storage.get/set` 替换成 API 调用。 建议新增: - `src/api/`:统一 API client。 - `src/auth/`:登录态、Token、权限上下文。 - `src/hooks/`:数据获取和变更 hook。 - `src/services/`:报告、模板、用户、系统设置、文件、AI 服务封装。 ### 后端 建议先采用 NestJS 单体后端,避免一开始拆太复杂: - Node.js + NestJS + TypeScript。 - PostgreSQL + Prisma。 - Redis 可选,用于会话、限流、任务状态。 - 对象存储可选,院内部署可先用本地文件目录,生产建议 MinIO/S3 兼容存储。 ### 数据边界 后端需要引入明确的组织边界: - `tenant`:医院或部署实例。 - `department`:科室。 - `user`:用户账号。 - `role`:超级管理员、管理员、医生。 - `permission`:模板权限、报告权限、系统配置权限。 如果项目只给单医院内网使用,可以先不做复杂租户模型,但数据库字段建议预留 `tenant_id`。 ## 数据模型改造 ### 用户与认证 新增表: - `users` - `departments` - `roles` 或直接使用枚举字段 - `user_sessions` 或使用 JWT + refresh token - `user_template_permissions` 当前 `User` 需要变化: - `password` 改为 `password_hash`,禁止前端返回。 - `signature` 从 Base64 改为 `signature_file_id` 或 `signature_url`。 - `visibleTemplates/manageableTemplates` 拆成权限关联表。 - `status` 保留。 - 增加 `last_login_at`、`created_by`、`updated_by`。 ### 报告 新增表: - `reports` - `report_histories` - `report_media` - `report_exports` 可选 当前 `Report` 需要变化: - `content` 仍可保存 HTML,但必须后端清洗。 - `videos.url` 不再长期依赖浏览器对象 URL,后端保存 `ReportMedia.fileId/url` 并返回受控文件 URL。 - `capturedFrames.dataUrl` 改成 `ReportMedia.fileId/url` 派生出的图片 URL。 - `author/authorName` 改成 `author_id`,展示时 join 用户表。 - `createdAt/updatedAt` 统一为 ISO 时间戳。 - `status` 保留 `draft/completed`。 - `history` 拆到 `report_histories`。 ### 模板与字段 新增表: - `templates` - `template_versions` 可选 - `form_fields` - `template_fields` 可选 - `image_assets` 当前 `Template` 需要变化: - `content` 保存 HTML,但导入和保存时后端清洗。 - `fields` 不建议长期嵌在模板对象里,可拆出字段配置版本。 - `author` 改成 `author_id`。 - 增加 `department_id`、`tenant_id`、`is_global`、`status`。 ### 系统设置 新增表: - `system_settings` - `ai_provider_configs` - `speech_provider_configs` 当前 `SystemSettings` 需要变化: - AI Key、讯飞密钥只存后端,前端只看到是否已配置和脱敏信息;普通用户请求设置时不返回真实密钥。 - 抽帧设置可以按全局、科室或用户默认值分层。 - 默认模板可以改成用户级配置 `user_preferences.default_template_id`。 ### 文件资源 新增表: - `files` 字段建议: - `id` - `tenant_id` - `owner_id` - `kind`:`signature`、`template_asset`、`video`、`frame`、`report_export` - `filename` - `mime_type` - `size` - `storage_key` - `checksum` - `created_at` ## API 改造清单 ### 认证 - `POST /api/auth/login` - `POST /api/auth/logout` - `POST /api/auth/refresh` - `GET /api/auth/me` - `PATCH /api/auth/password` ### 用户 - `GET /api/users` - `POST /api/users` - `GET /api/users/:id` - `PATCH /api/users/:id` - `DELETE /api/users/:id` - `POST /api/users/:id/signature` - `GET /api/departments` - `POST /api/departments` - `PATCH /api/departments/:id` - `DELETE /api/departments/:id` - `PATCH /api/departments/:id/template-permissions` ### 模板 - `GET /api/templates` - `POST /api/templates` - `GET /api/templates/:id` - `PATCH /api/templates/:id` - `DELETE /api/templates/:id` - `POST /api/templates/import` - `GET /api/templates/:id/export` - `POST /api/templates/batch-export` - `GET /api/form-fields` - `PUT /api/form-fields` ### 报告 - `GET /api/reports` - `POST /api/reports` - `GET /api/reports/:id` - `PATCH /api/reports/:id` - `DELETE /api/reports/:id` - `POST /api/reports/:id/complete` - `GET /api/reports/:id/history` - `POST /api/reports/:id/restore` - `GET /api/reports/:id/export.json` - `POST /api/reports/export` ### 文件与视频 - `POST /api/files` - `GET /api/files` - `GET /api/files/:id` - `DELETE /api/files/:id` 当前第一版已经保留前端 canvas 抽帧,并把视频和生成的关键帧通过通用文件 API 上传为 `VIDEO` / `FRAME` 文件资源。后续如果要做后端视频服务,再新增专用 `videos` 模块,用于转码、异步抽帧、断点续传和媒体引用关系维护。 ### AI 与语音 - `POST /api/ai/chat` - `GET /api/ai/providers` - `PATCH /api/ai/providers/:id` - `POST /api/ai/providers/:id/test` - `GET /api/speech/config` - `PATCH /api/speech/config` - `POST /api/speech/token` 或 `GET /api/speech/ws-auth` 前端不再直接调用第三方 AI 接口,不再保存真实 API Key。 ### 系统设置 - `GET /api/settings` - `PATCH /api/settings` - `POST /api/settings/reset` 清空浏览器本地数据只作为开发/显式回退模式能力;如果未来需要“重置全部后端数据”,应作为独立维护接口谨慎保留,只允许超级管理员在维护模式下执行,并写审计日志。 ## 前端改造清单 ### 第一批必须改 1. 新增 API Client - 统一 base URL、认证头、错误处理、401 刷新或跳登录。 - 当前已新增 `src/api/client.ts`,支持 `{ data }` envelope 解包、Cookie credentials 和错误 envelope。 2. 替换登录逻辑 - 登录调用 `/api/auth/login`。 - 当前用户来自 `/api/auth/me`。 - 当前已完成登录 API 接入;默认模板、字段、图片和本地用户初始化仍暂时保留,供未迁移页面使用。 3. 替换 `currentUser` - 从 `localStorage.currentUser` 改成 Auth Context 或全局 store。 - 本地只保存 token 或 session 标识。 - 当前已新增 Auth Context,但仍会同步 `currentUser` 兼容旧页面;后续页面完成 API 迁移后再移除该缓存依赖。 4. 替换报告数据 - `ReportManage` 用 `GET /api/reports`。 - `ReportView` 用 `GET /api/reports/:id`。 - `ReportEditor` 保存用 `POST/PATCH /api/reports`。 - 历史恢复用后端历史 API。 - 当前前三项已接入;历史恢复仍通过返回的兼容 `history` 和 `sessionStorage.restore_${reportId}` 进入编辑器。 5. 替换模板数据 - `TemplateManage` 全部从 API 获取和保存。 - 模板权限由后端返回可见/可管理范围。 - 当前已接入模板列表、创建、编辑、保存内容、删除、医生个人模板、字段库和模板图片资源。 6. 替换用户管理 - `UserManage` 调用用户 API。 - 密码不再回显,也不进入前端用户对象。 - 当前已接入用户列表、新增、编辑、删除、部门模板授权和签名文件上传。 7. 替换系统设置 - 当前已接入 Settings API,保存全局设置和个人默认模板。 - 后续应只展示脱敏密钥;保存时发送新密钥,后端加密存储并由代理使用。 ### 第二批建议改 1. 文件上传 - 签名、模板图片、视频和关键帧已完成第一版文件 API;报告媒体引用已通过 `ReportMedia` 正规化,报告正文内手动插入图片仍需改为上传文件。 - 报告 HTML 中引用文件 URL 或受控下载 URL。 2. 报告内容清洗 - 前端仍可编辑 HTML。 - 保存前后端做白名单清洗。 - 渲染时避免直接信任历史 HTML。 3. 草稿同步 - 当前草稿在本地,可改成服务端 draft autosave。 - 防止换浏览器或清缓存丢草稿。 4. AI 代理 - `ReportEditor` 的 `fetch(chat/completions)` 改成 `/api/ai/chat`。 - 后端记录请求摘要和错误,避免前端暴露密钥。 - 当前已接入 `/api/ai/models` 和 `/api/ai/chat`,所有用户共用全局 Provider Key;仍待第三方调用摘要和错误追踪落库。 5. 审计日志 - 登录、编辑、完成、删除、重置、权限变更等关键操作写日志。 - 当前已提供审计日志查询页面/API;超级管理员看全部,管理员看本部门或自己相关日志。 - 报告导出不要求水印、导出原因、审批或专门导出审计。 ## 权限模型建议 ### 角色能力 | 能力 | 超级管理员 | 管理员 | 医生 | | --- | --- | --- | --- | | 管理全局设置 | 是 | 否 | 否 | | 管理 AI/语音密钥 | 是 | 否 | 否 | | 管理全部用户 | 是 | 否 | 否 | | 管理同部门医生 | 是 | 是 | 否 | | 管理模板 | 全部 | 授权模板 | 否 | | 使用模板 | 全部 | 可见模板 | 可见模板 | | 查看报告 | 全部 | 本部门 | 本人 | | 编辑报告 | 全部 | 本部门 | 本人,包括已完成报告 | | 删除报告 | 全部 | 本部门,包括已完成报告 | 本人,包括已完成报告 | | 导出报告 | 全部 | 本部门 | 本人 | 已确定权限规则以 [权限设计](./permissions.md) 为准: - 超级管理员全权限,可修改任何数据。 - 超级管理员默认属于 `admin` 部门。 - 管理员只能看、改、删本部门报告。 - 医生完成报告后可以继续修改,也可以删除本人已完成报告。 - 完成报告后的每次修改必须增加修订版本号。 - 模板按部门授权;管理员可修改本部门模板;医生可复制/新建个人模板。 - 一个部门只能有一个管理员,且只有超级管理员能创建或授权管理员。 - AI 只能接收当前报告内容作为上下文。 ### 后端强校验 每个 API 都必须根据当前用户进行后端权限判断。前端菜单隐藏只是体验优化,不能作为安全依据。 ## 安全改造清单 - 密码使用 bcrypt/argon2 哈希。 - Token 使用 HttpOnly Cookie 或短期 access token + refresh token。 - API 增加 CSRF 或 SameSite 策略。 - HTML 内容用 DOMPurify 或服务端白名单清洗。 - 文件上传限制 MIME、大小、扩展名,并做病毒扫描或隔离策略。 - AI/语音密钥后端加密存储。 - 敏感日志脱敏,禁止记录完整病历到普通日志。 - 报告导出当前不要求水印、原因、审批或专门审计;后续重点是权限控制、错误追踪和后端导出能力。 - 对报告、模板、用户操作增加审计日志。 - 增加数据库备份和恢复策略。 ## 测试改造清单 ### 后端测试 - Auth:登录、登出、刷新、密码错误、禁用用户。 - RBAC:不同角色访问用户、模板、报告、设置 API。 - Reports:新增、编辑、完成、历史、删除、导出。 - Templates:新增、导入、导出、权限过滤、字段配置。 - Files:上传、读取、权限、大小限制。 - Settings:密钥脱敏、配置保存、抽帧设置。 - AI Proxy:请求构造、错误处理、密钥不下发。 ### 前端测试 保留现有 Vitest 测试,并新增: - API client 401/错误处理。 - Auth Context 登录态恢复。 - 报告列表 API 数据渲染。 - 模板权限数据渲染。 - 用户管理表单 API 提交。 ### E2E 测试 当前 Playwright 已通过真实后端 API seed 覆盖: - 默认登录进入工作台。 - 报告按超级管理员、管理员、医生范围过滤。 - 已完成报告再次完成后递增修订版本并保留历史。 - 医生保存个人模板。 - 医生直进管理页被路由守卫拦截,超级管理员可查看审计日志。 后续可继续增加: - 管理员创建医生、分配模板的完整 UI 流程。 - 报告管理搜索、查看、导出。 - 模板新增、字段插入、报告套用模板。 - 视频上传和抽帧可用性。 ## 迁移步骤建议 ### 阶段 0:冻结当前功能 - 保留现有 `localStorage` 版本作为 baseline。 - 当前 Vitest 测试继续作为前端契约测试。 - 增加功能验收清单,避免后端化时漏功能。 ### 阶段 1:后端骨架 - 建后端项目、数据库迁移、基础表结构。 - 实现登录、当前用户、用户列表、模板列表、报告列表。 - 前端新增 API client,但先不大规模替换页面。 ### 阶段 2:认证和用户后端化 - 替换 `Login`、`Sidebar`、`UserManage`。 - 移除前端默认账号初始化。 - 前端不再保存密码和完整用户列表。 ### 阶段 3:模板和报告后端化 - 替换 `TemplateManage`、`ReportManage`、`ReportView`、`ReportEditor` 保存逻辑。 - 报告历史改为后端历史表。 - 字段配置由后端提供。 ### 阶段 4:文件和关键帧后端化 - 签名、图片资源、关键帧、视频统一上传。 - 报告 HTML 中从 Data URL 改为文件 URL 或受控资源地址。 ### 阶段 5:AI/语音代理与审计 - 前端调用后端 AI API。 - 后端托管 AI 密钥;语音密钥仍待代理化。 - 增加查看日志、第三方代理调用摘要和错误追踪;报告导出不要求专门审计,后端导出能力另行实现。 ### 阶段 6:生产化 - 部署数据库、对象存储、后端服务。 - 配置备份、监控、日志、权限审计。 - 做安全扫描、数据迁移演练和院内验收。 ## 需要优先决策的问题 1. 后端技术栈:Express、NestJS、Fastify,还是已有院内后端技术? 2. 数据库:PostgreSQL、MySQL,还是院内指定数据库? 3. 部署方式:单机 Docker Compose,还是院内 Kubernetes/服务器? 4. 文件存储:本地磁盘、MinIO、S3 兼容,还是院内文件系统? 5. 租户模型:只支持单医院,还是要支持多医院/多院区? 6. 报告权限:管理员是否只能看同部门,还是可以按模板/患者/手术组授权? 7. 完成报告后是否允许医生修改?是否需要审批或留痕版本? 8. AI 和语音是否必须在院内网代理,是否允许公网访问第三方模型?