- 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.
106 lines
7.2 KiB
Markdown
106 lines
7.2 KiB
Markdown
# 设计文档
|
||
|
||
## 总体架构
|
||
|
||
应用是一个 Vite 构建的 React SPA。入口文件为 `src/main.tsx`,路由集中在 `src/App.tsx`。页面级功能位于 `src/pages`,公共侧边栏位于 `src/components/Sidebar.tsx`,数据类型集中在 `src/types.ts`,浏览器存储封装在 `src/utils/storage.ts`。
|
||
|
||
```text
|
||
src/
|
||
App.tsx 路由定义
|
||
main.tsx React 挂载入口
|
||
index.css Tailwind 与全局样式
|
||
api/ 前端 API client
|
||
auth/ 登录态、后端用户兼容映射
|
||
types.ts 核心数据类型和默认配置
|
||
components/Sidebar.tsx 主导航与登出
|
||
pages/ 业务页面
|
||
utils/ 存储、打印、默认模板内容
|
||
```
|
||
|
||
## 路由设计
|
||
|
||
| 路由 | 页面 | 用途 |
|
||
| --- | --- | --- |
|
||
| `/` | `Login` | 登录和首次数据初始化。 |
|
||
| `/dashboard` | `Dashboard` | 工作台统计和快捷入口。 |
|
||
| `/report-editor` | `ReportEditor` | 新建报告,或通过 `?id=` 编辑报告。 |
|
||
| `/report-manage` | `ReportManage` | 报告检索、历史、导出和删除。 |
|
||
| `/report-view/:id` | `ReportView` | 报告只读查看和打印。 |
|
||
| `/template-manage` | `TemplateManage` | 模板和字段库维护。 |
|
||
| `/user-manage` | `UserManage` | 用户、角色、部门和模板权限管理。 |
|
||
| `/system-settings` | `SystemSettings` | 抽帧、AI、语音和默认模板设置。 |
|
||
|
||
未匹配路由会重定向到登录页。除登录页外,其余路由由 `RequireAuth` 包裹:优先等待 `AuthProvider` 从后端 `/api/auth/me` 恢复会话;没有后端会话和本地兼容缓存时回到 `/`。
|
||
|
||
## 数据设计
|
||
|
||
核心类型在 `src/types.ts`:
|
||
|
||
- `User`:用户账号、角色、部门、状态、模板权限和签名。
|
||
- `Report`:报告元信息、患者信息、HTML 正文、视频、关键帧、作者、状态和历史版本。
|
||
- `Template`:模板名称、描述、HTML 内容、作者和字段配置。
|
||
- `FormField`:表单字段定义,支持文本、单选、多选、日期、时间、签名、图片等类型。
|
||
- `SystemSettings`:抽帧策略、默认模板、AI Provider、语音配置和自动插入设置。
|
||
|
||
登录认证、报告读写、报告媒体引用、模板读写、字段库、模板图片资源、视频/关键帧文件、用户管理、部门模板授权、系统设置和签名文件已有服务端校验。报告媒体通过 `ReportMedia` 关系表关联 `FileResource`,页面逻辑仍承担一部分迁移期回退约束。
|
||
|
||
## 状态与持久化
|
||
|
||
应用使用 React 本地状态管理页面交互。认证态由后端 Session Cookie 维持,前端 `AuthProvider` 会把当前用户同步到 `localStorage.currentUser` 作为迁移期兼容缓存。报告读写已优先走后端 Reports API,并同步 `localStorage.reports` 作为开发/显式回退缓存;模板读写已优先走后端 Templates API,并同步 `localStorage.templates` 作为开发/显式回退缓存;字段库已优先走 Library API,并同步 `formFieldsConfig` 等兼容缓存;模板图片资源已优先走 Files API,并同步 `imageAssets` 兼容缓存;用户和部门模板授权已优先走 Users/Departments API,并同步 `localStorage.users` 作为兼容缓存;系统设置已优先走 Settings API,并同步 `localStorage.systemSettings` 作为开发/显式回退缓存。`storage.get/set` 对常规键做 JSON 读写,对 `systemSettings` 做简单 XOR + Base64 存储;这不是安全加密,只能降低直接可读性。生产构建默认关闭本地回退。
|
||
|
||
报告编辑器额外使用 `reportEditorDraft_${username}` 保存未提交草稿,使用 `sessionStorage` 的 `restore_${reportId}` 暂存历史版本恢复内容。
|
||
|
||
## 权限设计
|
||
|
||
当前迁移期权限检查分两层:登录、报告、模板、用户和部门模板授权由后端校验;页面入口和部分业务按钮权限仍保留前端 UI 展示层:
|
||
|
||
- 未登录用户跳回 `/`。
|
||
- 医生在报告管理和查看页只能访问本人报告。
|
||
- 模板管理不允许医生进入。
|
||
- 用户管理只允许超级管理员和管理员进入;后端再次校验可管理范围。
|
||
- 管理员只能管理同部门医生或自己,不能创建管理员或跨部门修改用户。
|
||
|
||
前端入口隐藏不能作为生产级安全边界,所有已后端化 API 都必须以服务端校验为准。
|
||
|
||
后端化后的目标权限模型以“角色 + 部门 + 模板授权”为主,详细规则见 [权限设计](./permissions.md)。当前已确定规则:
|
||
|
||
- 超级管理员拥有全权限,可以查看和修改所有用户、报告、模板、系统配置和权限配置。
|
||
- 管理员只能查看、编辑和删除本部门报告。
|
||
- 医生默认只能查看本人报告。
|
||
- 医生完成报告后仍允许修改,也可以删除本人已完成报告;完成后的每次修改必须保留历史版本并递增修订版本号。
|
||
- 模板按部门授权;管理员可以修改本部门模板;医生不可修改部门模板,但可以复制/新建个人模板。
|
||
- 一个部门只能有一个管理员,且只有超级管理员能创建或授权管理员。
|
||
- AI 只能接收当前报告内容作为上下文,不读取跨部门报告。
|
||
- 后端必须对每个 API 做强权限校验,前端菜单隐藏只作为体验优化。
|
||
|
||
后端查询层需要把权限规则落到数据过滤:
|
||
|
||
- 报告列表:超级管理员不加部门限制;管理员按 `department_id` 过滤;医生按 `author_id` 过滤。
|
||
- 报告详情/编辑/删除:读取报告后再次校验角色、部门和作者关系。
|
||
- 模板列表:超级管理员返回全部模板;管理员返回本部门模板和被授权模板;医生返回本部门授权模板和个人模板。
|
||
- 用户列表:超级管理员返回全部用户;管理员返回自己和本部门医生;医生只能读取自己。
|
||
- 部门模板授权:超级管理员维护部门可用/可管理模板;管理员和医生只读取本人部门。
|
||
|
||
## 报告编辑设计
|
||
|
||
报告正文是一个 `contentEditable` HTML 编辑器。模板中通过 `.field-value[data-bind="fieldKey"]` 标记智能字段,侧边表单更新时同步到正文;正文内字段被编辑时也会反向更新表单状态。
|
||
|
||
图片占位符通过 `.image-placeholder` 表示:
|
||
|
||
- `data-mode="frame"` 或无 `manual` 标记的占位符可接收关键帧。
|
||
- `data-mode="manual"` 用于 Logo、签名等静态图片,不接收视频关键帧拖入。
|
||
|
||
AI 可编辑区域通过 `.ai-region[data-ai-id]` 和内部 `.ai-content` 定位。AI 返回 `updatedHtml` 后,系统先显示差异确认,再注入目标区域。
|
||
|
||
## 打印导出设计
|
||
|
||
`src/utils/print.ts` 通过隐藏 iframe 写入报告 HTML 和打印样式,然后调用浏览器打印。PDF 导出依赖浏览器“另存为 PDF”,不是服务端生成 PDF。
|
||
|
||
JSON 导出使用 Blob 下载,报告导出主要包含元信息和 `DEFAULT_FORM_FIELDS` 中定义的字段。
|
||
|
||
## 外部服务设计
|
||
|
||
- AI 服务通过后端 `/api/ai/chat` 和 `/api/ai/models` 代理到 OpenAI 兼容 `/chat/completions` 和 `/models` 接口。
|
||
- 语音听写通过后端 `/api/speech/iat` WebSocket 代理到讯飞 IAT 接口,前端只采集 PCM 音频并发送到本系统。
|
||
- 当前 AI Key 和讯飞语音密钥均由后端代理使用;普通用户读取系统设置时不会返回真实密钥。
|