7.5 KiB
设计文档
总体架构
应用是一个 Vite 构建的 React SPA。入口文件为 src/main.tsx,路由集中在 src/App.tsx。页面级功能位于 src/pages,公共侧边栏位于 src/components/Sidebar.tsx,数据类型集中在 src/types.ts,浏览器存储封装在 src/utils/storage.ts。
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 都必须以服务端校验为准。
后端化后的目标权限模型以“角色 + 部门 + 模板授权”为主,详细规则见 权限设计。当前已确定规则:
- 超级管理员拥有全权限,可以查看和修改所有用户、报告、模板、系统配置和权限配置。
- 管理员只能查看、编辑和删除本部门报告。
- 医生默认只能查看本人报告。
- 医生完成报告后仍允许修改,也可以删除本人已完成报告;完成后的每次修改必须保留历史版本并递增修订版本号。
- 模板按部门授权;管理员可以修改本部门模板;医生不可修改部门模板,但可以复制/新建个人模板。
- 一个部门只能有一个管理员,且只有超级管理员能创建或授权管理员。
- 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 导出入口已移除。报告导出保留浏览器打印/PDF;模板导出保留可回导 HTML 模板包和 PDF 打印预览,历史 JSON 模板包只作为导入兼容格式保留。HTML 模板包会在内嵌元数据中保存模板正文、模板对应字段和字段管理设置,导入时同步恢复字段库兼容缓存并尝试写回 /api/library/fields。
外部服务设计
- AI 服务通过后端
/api/ai/chat和/api/ai/models代理到 OpenAI 兼容/chat/completions和/models接口。 - 语音听写通过后端
/api/speech/iatWebSocket 代理到讯飞 IAT 接口,前端只采集 PCM 音频并发送到本系统。 - 当前 AI Key 和讯飞语音密钥均由后端代理使用;普通用户读取系统设置时不会返回真实密钥。