Initialize backendized SurClaw report system

- Add React/Vite frontend for login, dashboard, reports, templates, users, settings, AI, speech, and media workflows.

- Add NestJS/Prisma/PostgreSQL backend with auth, dashboard stats, reports, templates, users, departments, settings, files, AI, speech, audit logs, and HTML sanitization.

- Add Prisma schema, migrations, seed data, persistent app sessions, Docker/Nginx deployment files, and upload volume configuration.

- Add Vitest, Playwright, backend integration tests, and project documentation for requirements, design, permissions, API contracts, testing, deployment, security, and progress.

- Configure production local fallback switch and remove unused Gemini direct dependency/env wiring.
This commit is contained in:
2026-05-02 01:37:20 +08:00
commit 014aca8619
162 changed files with 27116 additions and 0 deletions

461
docs/backendization-plan.md Normal file
View File

@@ -0,0 +1,461 @@
# 后端化与用户化改造方案
本文基于当前迁移期版本整理需要改动的范围。当前系统已完成主业务后端化,目标是继续把开发回退、审计、安全、部署和运维能力收紧为“多用户、可审计、可部署、可维护”的前后端系统。
## 改造目标
当前系统已经有较完整的页面、业务流程和后端 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 SessionAccess Token 仅作为可选扩展 | 前端不直接持久化敏感 token后端维护会话、退出和失效。第一阶段可用数据库会话表后续再引入 Redis。 |
| 密码 | Argon2 哈希 | 禁止明文密码进入前端或日志。 |
| 文件存储 | 本地文件目录 + `files` 表,预留 MinIO/S3 兼容接口 | 开发和院内部署先降低成本;生产可切换对象存储。 |
| 输入校验 | Zod 或 NestJS DTO 校验 | 所有 API 入参都必须校验,尤其是 HTML、文件、权限相关参数。 |
| 测试 | Vitest/Testing Library + Playwright + 后端单元/集成测试 | 保留现有前端测试;新增后端 policy/service/API 测试E2E 后续从 localStorage seed 改为 API/test DB 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`:第一版审计服务,记录登录、报告、模板、用户、设置和文件修改。
- `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. 审计日志
- 登录、编辑、完成、删除、重置、权限变更等关键操作写日志。
- 报告导出不要求水印、导出原因、审批或专门导出审计。
## 权限模型建议
### 角色能力
| 能力 | 超级管理员 | 管理员 | 医生 |
| --- | --- | --- | --- |
| 管理全局设置 | 是 | 否 | 否 |
| 管理 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
- 管理员登录、创建医生、分配模板。
- 医生登录、新建报告、保存草稿、完成报告。
- 报告管理搜索、查看、导出。
- 模板新增、字段插入、报告套用模板。
- 视频上传和抽帧可用性。
## 迁移步骤建议
### 阶段 0冻结当前功能
- 保留现有 `localStorage` 版本作为 baseline。
- 当前 Vitest 测试继续作为前端契约测试。
- 增加功能验收清单,避免后端化时漏功能。
### 阶段 1后端骨架
- 建后端项目、数据库迁移、基础表结构。
- 实现登录、当前用户、用户列表、模板列表、报告列表。
- 前端新增 API client但先不大规模替换页面。
### 阶段 2认证和用户后端化
- 替换 `Login``Sidebar``UserManage`
- 移除前端默认账号初始化。
- 前端不再保存密码和完整用户列表。
### 阶段 3模板和报告后端化
- 替换 `TemplateManage``ReportManage``ReportView``ReportEditor` 保存逻辑。
- 报告历史改为后端历史表。
- 字段配置由后端提供。
### 阶段 4文件和关键帧后端化
- 签名、图片资源、关键帧、视频统一上传。
- 报告 HTML 中从 Data URL 改为文件 URL 或受控资源地址。
### 阶段 5AI/语音代理与审计
- 前端调用后端 AI API。
- 后端托管 AI 密钥;语音密钥仍待代理化。
- 增加查看日志、第三方代理调用摘要和错误追踪;报告导出不要求专门审计,后端导出能力另行实现。
### 阶段 6生产化
- 部署数据库、对象存储、后端服务。
- 配置备份、监控、日志、权限审计。
- 做安全扫描、数据迁移演练和院内验收。
## 需要优先决策的问题
1. 后端技术栈Express、NestJS、Fastify还是已有院内后端技术
2. 数据库PostgreSQL、MySQL还是院内指定数据库
3. 部署方式:单机 Docker Compose还是院内 Kubernetes/服务器?
4. 文件存储本地磁盘、MinIO、S3 兼容,还是院内文件系统?
5. 租户模型:只支持单医院,还是要支持多医院/多院区?
6. 报告权限:管理员是否只能看同部门,还是可以按模板/患者/手术组授权?
7. 完成报告后是否允许医生修改?是否需要审批或留痕版本?
8. AI 和语音是否必须在院内网代理,是否允许公网访问第三方模型?