431 lines
19 KiB
Markdown
431 lines
19 KiB
Markdown
# 手术图文病历报告系统
|
||
|
||
## huijutec.cn / QNAP QTS 直接部署说明
|
||
|
||
本部署包已按 `https://sstwbg.huijutec.cn/` 和 QNAP 路径 `/share/Container/tuwen_system_v2` 预置,无需再手动修改 `docker-compose-Nas.yaml` 或 `frpc/frpc.nas.toml`。NAS 版 frpc 配置已直接内置在 `docker-compose-Nas.yaml` 的 `tuwen_frpc` 启动命令中,避免 QTS/Container Station 把配置文件挂载路径改到应用临时目录。
|
||
|
||
部署时请把本压缩包内容完整解压/复制到 NAS 的共享目录:
|
||
|
||
```text
|
||
/share/Container/tuwen_system_v2
|
||
```
|
||
|
||
对应 Windows 访问路径通常是:
|
||
|
||
```text
|
||
\\192.168.31.5\Container\tuwen_system_v2
|
||
```
|
||
|
||
目录根部必须能看到这些文件:
|
||
|
||
```text
|
||
Dockerfile
|
||
Dockerfile.server
|
||
docker-compose-Nas.yaml
|
||
package.json
|
||
package-lock.json
|
||
nginx.conf
|
||
index.html
|
||
```
|
||
|
||
在 QTS Container Station 中新建应用时,直接粘贴/选择 `docker-compose-Nas.yaml` 即可。该文件已经写死:
|
||
|
||
- 构建目录:`/share/Container/tuwen_system_v2`
|
||
- 数据目录:`/share/Container/tuwen_system_v2/data`
|
||
- 公网入口:`https://sstwbg.huijutec.cn`
|
||
- Web 端口:`4002:80`
|
||
- API 诊断端口:`4102:3100`
|
||
- frpc:默认启动 `tuwen_frpc`,映射 `TuWen_System_V2 -> tuwen_web:80 -> remotePort 4002`
|
||
- 构建/运行代理:`http://192.168.31.7:7893`
|
||
- 容器命名:不再写死 `container_name`,由 QTS/Compose 按应用名自动生成,减少旧容器名残留冲突。
|
||
- 镜像命名:`tuwen_api`、`tuwen_web` 使用 huijutec 专用镜像 tag,减少旧 web/api 镜像被复用导致 `nginx.conf` 更新不生效的概率。
|
||
|
||
正常启动后应出现 4 个容器:
|
||
|
||
```text
|
||
tuwen_system_v2-tuwen_db-1
|
||
tuwen_system_v2-tuwen_api-1
|
||
tuwen_system_v2-tuwen_web-1
|
||
tuwen_system_v2-tuwen_frpc-1
|
||
```
|
||
|
||
不同 QTS/Compose 版本可能使用下划线或短横线生成容器名,这是正常的;只要服务名能对应上 `tuwen_db`、`tuwen_api`、`tuwen_web`、`tuwen_frpc` 即可。
|
||
|
||
FRP 面板中 `TuWen_System_V2` 应显示 `Online`,随后访问:
|
||
|
||
```text
|
||
https://sstwbg.huijutec.cn/
|
||
```
|
||
|
||
手术图文病历报告系统是一个面向医院/科室场景的前端应用,用于撰写手术图文报告、管理报告模板、维护用户权限、从手术视频抽取关键帧,并通过 AI 辅助生成或改写报告内容。
|
||
|
||
当前系统已开始后端化:登录认证已接入 NestJS Session API 和数据库 Session Store,工作台统计、报告、报告媒体、模板、字段库、模板图片资源、视频/关键帧文件、用户、部门权限、系统设置、签名文件、AI 对话和讯飞语音听写已优先接入后端 API/代理。开发模式仍保留 `localStorage` 兼容回退,生产构建默认关闭本地回退。
|
||
|
||
## 功能概览
|
||
|
||
- 登录和角色导航:后端 Session 登录,前端按超级管理员、管理员、医生三类角色展示入口。
|
||
- 工作台:后端统计报告数量、用户数量、模板数量和近期报告趋势。
|
||
- 图文报告生成:模板化报告正文、智能字段绑定、富文本编辑、图片占位符、报告草稿、完成报告。
|
||
- 视频关键帧:本地预览视频、按百分比自动抽帧、手动截帧,视频和关键帧优先上传为后端文件资源后插入报告。
|
||
- AI 辅助撰写:通过后端 `/api/ai/chat` 代理 OpenAI 兼容接口,对报告内容进行对话或指定区域改写。
|
||
- 语音输入:通过后端 `/api/speech/iat` WebSocket 代理讯飞 IAT 听写,把识别文本写入 AI 输入框。
|
||
- 报告管理:后端权限过滤的列表/详情/保存/删除,支持搜索、筛选、查看、编辑、历史恢复和打印/PDF 导出。
|
||
- 模板管理:后端权限过滤的模板新增/编辑/删除,字段库和模板图片资源优先走后端 API,支持 AI 可编辑区域、可回导 HTML 模板包导入导出;HTML 包内嵌模板字段和字段管理设置。
|
||
- 用户管理:后端用户/部门权限 API,支持用户、角色、部门、模板授权、账号状态和电子签名文件。
|
||
- 系统设置:后端 Settings API,支持抽帧策略、默认模板、AI Provider、讯飞语音配置。
|
||
|
||
更细的功能真实性判断见 [docs/features.md](./docs/features.md)。
|
||
|
||
首次安装、端口规划、数据库初始化和常见问题见 [docs/installation.md](./docs/installation.md)。
|
||
|
||
## 技术栈
|
||
|
||
- React 19
|
||
- TypeScript 5.8
|
||
- Vite 6
|
||
- Tailwind CSS 4
|
||
- React Router DOM 7
|
||
- Lucide React
|
||
- Vitest + jsdom + Testing Library
|
||
- Playwright
|
||
- NestJS + Prisma + PostgreSQL 后端骨架
|
||
- Docker + Nginx
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
npm install
|
||
cp .env.example .env
|
||
npm run prisma:generate
|
||
npm run prisma:migrate
|
||
npm run prisma:seed
|
||
npm run server:dev
|
||
npm run dev
|
||
```
|
||
|
||
开发服务默认监听:
|
||
|
||
```text
|
||
http://localhost:3001
|
||
```
|
||
|
||
`npm run dev` 实际使用 `vite --port=3001 --host=0.0.0.0`,局域网内也可以通过机器 IP 访问。
|
||
开发模式下 Vite 会把 `/api` 代理到 `VITE_API_PROXY_TARGET`,默认是 `http://localhost:3100`。如果只启动 Docker Compose 中的 API,再用本机 Vite 前端调试,可把该变量改成 `http://localhost:3002`。
|
||
|
||
后端 API 开发服务默认监听:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
npm run prisma:generate
|
||
npm run server:dev
|
||
```
|
||
|
||
```text
|
||
http://localhost:3100/api/health
|
||
```
|
||
|
||
如果需要真实数据库,先启动 PostgreSQL 并配置 `DATABASE_URL`,再执行:
|
||
|
||
```bash
|
||
npm run prisma:migrate
|
||
npm run prisma:seed
|
||
```
|
||
|
||
## 默认测试账号
|
||
|
||
| 用户ID | 密码 | 角色 |
|
||
| --- | --- | --- |
|
||
| `admin` | `123456` | 超级管理员 |
|
||
| `manager` | `123456` | 管理员 |
|
||
| `0001` | `123456` | 医生 |
|
||
|
||
开发模式下,默认数据会在首次进入登录页时初始化到浏览器 `localStorage`,用于离线回退和旧数据兼容;当前 Playwright E2E 已改为真实后端 API seed。
|
||
账号认证由后端 `POST /api/auth/login` 完成;登录成功后前端会同步一份 `currentUser` 到 `localStorage`,供迁移期页面继续读取角色、部门和模板权限。生产构建默认不通过本地缓存恢复登录态。
|
||
|
||
## 常用命令
|
||
|
||
```bash
|
||
npm run dev # 启动开发服务器,端口 3001
|
||
npm run server:dev # 编译并启动 NestJS API,默认端口 3100
|
||
npm run server:build # 构建后端 TypeScript
|
||
npm run lint # TypeScript 类型检查
|
||
npm run test # Vitest 测试
|
||
npm run test:e2e # Playwright 端到端测试
|
||
npm run build # 生产构建
|
||
npm run preview # 预览构建产物
|
||
npm run clean # 删除 dist
|
||
npm run prisma:generate # 生成 Prisma Client
|
||
npm run prisma:migrate # 执行 Prisma 开发迁移
|
||
npm run prisma:seed # 写入默认医院、部门和账号
|
||
```
|
||
|
||
建议提交前运行:
|
||
|
||
```bash
|
||
npm run lint
|
||
npm run test
|
||
npm run build
|
||
```
|
||
|
||
## 环境变量
|
||
|
||
复制示例文件:
|
||
|
||
```bash
|
||
cp .env.example .env.local
|
||
```
|
||
|
||
当前环境变量:
|
||
|
||
- `API_PORT`:NestJS API 监听端口,本地直接运行默认 `3100`;Docker Compose 暴露到宿主机的默认端口是 `3002`。
|
||
- `API_BODY_LIMIT`:NestJS JSON/urlencoded 请求体上限,默认 `100mb`,用于报告 HTML、图片/关键帧和文件 Data URL 上传。
|
||
- `CORS_ORIGIN`:允许携带 Cookie 访问 API 的前端来源。
|
||
- `DATABASE_URL`:PostgreSQL 连接串。Docker Compose 暴露到宿主机的默认端口是 `5433`,容器内部仍使用 `db:5432`。
|
||
- `SESSION_SECRET`:后端 Session Cookie 签名密钥,生产环境必须替换。
|
||
- `SESSION_COOKIE_SECURE`:是否只通过 HTTPS 发送 Session Cookie。本地 HTTP/Compose 默认 `false`。
|
||
- `TRUST_PROXY`:是否信任反向代理传入的 `X-Forwarded-*` 头。`# XXX` 公网 HTTPS 经过 Nginx Proxy Manager、frpc/frps 或其他反向代理转发时建议设为 `true`,否则 `SESSION_COOKIE_SECURE=true` 时登录 Cookie 可能无法正确写入。
|
||
- `FILE_STORAGE_DIR`:后端文件存储目录。Docker Compose 默认挂载到 `/app/uploads`。
|
||
- `VITE_API_PROXY_TARGET`:Vite 开发服务器的 `/api` 代理目标。直接运行 `npm run server:dev` 时用 `http://localhost:3100`;连接 Docker Compose API 时用 `http://localhost:3002`。
|
||
- `VITE_ENABLE_LOCAL_FALLBACK`:是否允许生产构建继续使用浏览器本地兼容回退。开发模式默认启用,生产默认关闭。
|
||
|
||
注意:当前 AI/语音密钥由后端设置读取并在代理中使用,普通用户读取设置时不返回真实密钥。视频和关键帧文件已优先写入后端,报告内媒体引用通过 `ReportMedia` 关系表保存。
|
||
|
||
## 项目结构
|
||
|
||
```text
|
||
.
|
||
├── AGENTS.md # AI 编码助手项目上下文
|
||
├── README.md # 项目入口说明
|
||
├── package.json # 依赖和脚本
|
||
├── vite.config.ts # Vite、React、Tailwind、Vitest 配置
|
||
├── prisma.config.ts # Prisma 7 CLI 配置
|
||
├── playwright.config.ts # Playwright E2E 配置
|
||
├── tsconfig.json # TypeScript 配置
|
||
├── index.html # Vite HTML 入口
|
||
├── e2e/ # Playwright 端到端测试
|
||
├── public/ # favicon、Logo 等静态资源
|
||
├── src/
|
||
│ ├── App.tsx # 路由定义
|
||
│ ├── main.tsx # React 入口
|
||
│ ├── index.css # 全局样式和 Tailwind 主题
|
||
│ ├── types.ts # 核心类型和默认配置
|
||
│ ├── components/ # 公共组件
|
||
│ ├── pages/ # 页面模块
|
||
│ ├── utils/ # 存储、打印、默认模板等工具
|
||
│ └── test/ # 测试初始化
|
||
├── server/
|
||
│ ├── prisma/ # Prisma schema 和 seed
|
||
│ ├── src/ # NestJS API 源码
|
||
│ └── tsconfig.json # 后端构建配置
|
||
├── docs/ # 需求、设计、功能盘点、测试、后端化方案等文档
|
||
├── Dockerfile
|
||
├── Dockerfile.server
|
||
├── docker-compose.yaml
|
||
└── nginx.conf
|
||
```
|
||
|
||
核心页面:
|
||
|
||
- `src/pages/Login.tsx`:登录和默认数据初始化。
|
||
- `src/pages/Dashboard.tsx`:工作台。
|
||
- `src/pages/ReportEditor.tsx`:报告编辑器、抽帧、AI、语音、保存。
|
||
- `src/pages/ReportManage.tsx`:报告列表、筛选、历史和导出。
|
||
- `src/pages/ReportView.tsx`:报告查看和打印。
|
||
- `src/pages/TemplateManage.tsx`:模板和字段库管理。
|
||
- `src/pages/UserManage.tsx`:用户、角色、部门和模板权限。
|
||
- `src/pages/SystemSettings.tsx`:抽帧、AI、语音和默认模板设置。
|
||
|
||
## 文档
|
||
|
||
- [docs/README.md](./docs/README.md):文档入口。
|
||
- [docs/installation.md](./docs/installation.md):安装、初始设置和首次验收。
|
||
- [docs/requirements.md](./docs/requirements.md):需求文档。
|
||
- [docs/design.md](./docs/design.md):设计文档。
|
||
- [docs/component-structure.md](./docs/component-structure.md):前端组件结构和拆分边界。
|
||
- [docs/features.md](./docs/features.md):功能真实性盘点。
|
||
- [docs/testing.md](./docs/testing.md):测试文档。
|
||
- [docs/data-storage.md](./docs/data-storage.md):本地数据存储说明。
|
||
- [docs/docker.md](./docs/docker.md):Docker Compose 一键部署、初始化、健康检查和生产变量。
|
||
- [docs/security.md](./docs/security.md):安全边界和风险。
|
||
- [docs/backendization-plan.md](./docs/backendization-plan.md):后端化、用户化改造方案。
|
||
|
||
## 测试
|
||
|
||
当前测试使用 Vitest,已覆盖:
|
||
|
||
- 登录页默认数据初始化和后端禁用账号错误展示。
|
||
- API client、语音代理地址和后端用户映射。
|
||
- 侧边栏角色菜单过滤。
|
||
- 报告管理按角色过滤报告。
|
||
- 本地存储封装和系统设置兼容。
|
||
- 默认报告模板结构和字段配置。
|
||
- 模板 HTML 导出包字段库元数据。
|
||
- AI 区域扫描和报告编辑器加载后同步。
|
||
- 抽帧百分比两位小数、保序和按时间截图计划。
|
||
- 打印导出入口。
|
||
- 后端权限策略、AI 入参和语音代理帧处理。
|
||
|
||
运行:
|
||
|
||
```bash
|
||
npm run test
|
||
```
|
||
|
||
更多说明见 [docs/testing.md](./docs/testing.md)。
|
||
|
||
## Docker 部署
|
||
|
||
项目内置静态部署配置:
|
||
|
||
```bash
|
||
docker-compose up -d --build
|
||
```
|
||
|
||
默认访问:
|
||
|
||
```text
|
||
前端: http://localhost:4002
|
||
语音 HTTPS 演示入口: https://localhost:4443
|
||
API: http://localhost:3002/api/health
|
||
数据库: localhost:5433
|
||
```
|
||
|
||
停止:
|
||
|
||
```bash
|
||
docker-compose down
|
||
```
|
||
|
||
部署说明:
|
||
|
||
- `web` 服务使用 Nginx 托管前端 `dist/`。
|
||
- Docker 前端同时暴露 `http://localhost:4002` 和自签名证书的 `https://localhost:4443`;麦克风听写建议使用 HTTPS 演示入口。
|
||
- `api` 服务运行 NestJS 后端,启动时默认执行 `prisma migrate deploy` 和 `prisma db seed`,并把上传文件目录挂载到 `uploads_data` volume。
|
||
- `db` 服务运行 PostgreSQL 16。
|
||
- `frpc` 服务是可选公网隧道客户端。`# XXX` 默认 Docker Compose 通过 profile 关闭,配置文件在 `frpc/frpc.toml`;NAS 版为了适配 Container Station 图形界面会默认创建 `tuwen_frpc`,并在容器启动时自动写入 frpc 配置,不依赖宿主机文件挂载。
|
||
- `nginx.conf` 已配置 SPA 路由回退、`/api` 反向代理和 `100m` 请求体上限。
|
||
- `nginx.conf` 已支持 `/api/speech/iat` WebSocket upgrade。
|
||
完整 Docker 说明见 [docs/docker.md](./docs/docker.md)。
|
||
|
||
## 威联通 NAS 部署
|
||
|
||
本 huijutec.cn 适配包提供 [docker-compose-Nas.yaml](./docker-compose-Nas.yaml),用于在威联通 QTS Container Station 中直接部署完整图文报告系统。该文件已经写入你的当前部署参数:
|
||
|
||
- 项目源码目录固定为 `/share/Container/tuwen_system_v2`,避免 QTS 在临时目录执行 Compose 时找不到 `Dockerfile.server`。
|
||
- 使用 `/share/Container/tuwen_system_v2/data` 作为默认持久化目录。
|
||
- API 默认映射到 NAS 宿主机 `4102`,前端仍映射到 `4002`。
|
||
- 保留 `api` 网络别名,确保容器内 Nginx 可以继续把 `/api` 代理到后端。
|
||
- 使用 `127.0.0.1` 做 API 健康检查,规避部分 NAS 固件上的 IPv6 localhost 解析问题。
|
||
- 默认创建 `tuwen_frpc` 容器,在容器内自动写入 frpc 配置,把容器内 `tuwen_web:80` 映射到公网服务器 `82.157.255.195:4002`。
|
||
- 默认公网入口为 `https://sstwbg.huijutec.cn`。
|
||
- 默认构建和运行代理为 `http://192.168.31.7:7893`。
|
||
- 容器 Nginx 已对 `/api` 强制传递 `X-Forwarded-Proto: https`,确保后端能正常写入 `Secure` 登录 Cookie。
|
||
|
||
QTS Container Station 部署时,直接选择或粘贴 [docker-compose-Nas.yaml](./docker-compose-Nas.yaml) 内容即可。
|
||
|
||
NAS 终端部署也可以直接执行:
|
||
|
||
```bash
|
||
cd /share/Container/tuwen_system_v2
|
||
docker compose -f docker-compose-Nas.yaml up -d --build
|
||
|
||
curl http://127.0.0.1:4002/api/health
|
||
docker compose -f docker-compose-Nas.yaml ps
|
||
```
|
||
|
||
NAS 上的 frpc 隧道:
|
||
|
||
```bash
|
||
docker compose -f docker-compose-Nas.yaml up -d --build
|
||
docker compose -f docker-compose-Nas.yaml logs -f tuwen_frpc
|
||
```
|
||
|
||
`tuwen_frpc` 启动后,FRP 面板中 `TuWen_System_V2` 应显示 `Online`。公网访问地址为:
|
||
|
||
```text
|
||
https://sstwbg.huijutec.cn/
|
||
```
|
||
|
||
## 公网反向代理部署
|
||
|
||
本 huijutec.cn 适配包已按下面链路预置:
|
||
|
||
```text
|
||
浏览器 https://sstwbg.huijutec.cn
|
||
-> 公网服务器 Nginx Proxy Manager
|
||
-> frps/frpc remotePort 4002
|
||
-> NAS Docker tuwen_web:80
|
||
-> 容器 Nginx /api 代理
|
||
-> tuwen_api:3100
|
||
```
|
||
|
||
NAS 版 frpc 配置已经写入 [docker-compose-Nas.yaml](./docker-compose-Nas.yaml) 的 `tuwen_frpc` 服务启动命令:
|
||
|
||
```toml
|
||
serverAddr = "82.157.255.195"
|
||
serverPort = 7000
|
||
|
||
auth.method = "token"
|
||
auth.token = "en.xjtu.edu.cn"
|
||
|
||
[[proxies]]
|
||
name = "TuWen_System_V2"
|
||
type = "tcp"
|
||
localIP = "tuwen_web"
|
||
localPort = 80
|
||
remotePort = 4002
|
||
```
|
||
|
||
公网服务器 Nginx Proxy Manager 中对应 Proxy Host 应保持:
|
||
|
||
- `Domain Names`:`sstwbg.huijutec.cn`
|
||
- `Scheme`:`http`
|
||
- `Forward Hostname / IP`:frps 暴露 `remotePort` 的地址。
|
||
- `Forward Port`:`4002`
|
||
- `Websockets Support`:开启。语音识别使用 `/api/speech/iat` WebSocket,必须透传 Upgrade。
|
||
- `Block Common Exploits`:开启。
|
||
- `SSL`:绑定 `sstwbg.huijutec.cn` 证书,开启 `Force SSL`。浏览器麦克风权限要求 HTTPS。
|
||
|
||
Nginx Proxy Manager 的 `Advanced` 可加入:
|
||
|
||
```nginx
|
||
client_max_body_size 100m;
|
||
|
||
proxy_read_timeout 3600s;
|
||
proxy_send_timeout 3600s;
|
||
```
|
||
|
||
公网验收顺序:
|
||
|
||
```text
|
||
1. 打开 https://sstwbg.huijutec.cn/api/health,确认 API 健康检查可访问。
|
||
2. 打开 https://sstwbg.huijutec.cn 登录 admin / 123456。
|
||
3. 进入系统设置,确认 AI Provider 和讯飞语音配置有效。
|
||
4. 进入报告编辑页,测试上传视频、自动抽帧、报告保存、AI 对话和语音听写。
|
||
5. 浏览器控制台执行 window.isSecureContext,应返回 true;否则语音麦克风权限不会开放。
|
||
```
|
||
|
||
如果登录失败或刷新后掉登录,优先检查 `SESSION_SECRET` 是否稳定、`SESSION_COOKIE_SECURE=true` 时是否同时设置了 `TRUST_PROXY=true`,以及外层 NPM 是否把 `X-Forwarded-Proto: https` 传给后端链路。
|
||
如果只有语音识别失败,优先检查 NPM 的 `Websockets Support`、HTTPS 证书、浏览器麦克风权限、系统设置中的讯飞 APPID/APIKey/APISecret,以及后端容器是否能访问讯飞 `wss://iat-api.xfyun.cn/v2/iat`。
|
||
|
||
## 当前限制
|
||
|
||
- 前端登录、工作台统计、报告读写、报告媒体引用、模板读写、字段库、模板图片资源、视频/关键帧文件、用户管理、部门模板授权、系统设置、签名文件、AI 对话和语音听写已接入真实后端 Session/API/代理。
|
||
- 当前后端已有认证、数据库 Session、健康检查、Dashboard API、报告 API、模板 API、字段库 API、通用文件 API、用户/部门 API、设置 API、签名文件 API、AI 代理、语音代理、权限策略、HTML 清洗、审计日志查询和数据模型。
|
||
- 后端账号使用 Argon2 哈希;开发模式前端仍会初始化 `localStorage.users`,其中保留演示密码字段供旧页面兼容。
|
||
- 权限判断主要在前端,不能作为生产安全边界。
|
||
- 报告和模板 HTML 保存时已做服务端白名单清洗,但渲染仍使用 HTML,需要继续做安全评审。
|
||
- AI Key 和讯飞语音密钥已由后端代理使用;普通用户读取设置时不会拿到真实密钥。
|
||
- 当前 demo mode 后端默认值包含演示用第三方服务凭据,生产部署前必须替换或移除,并轮换曾经暴露过的密钥。
|
||
- 视频和关键帧已优先上传后端文件资源,报告保存时通过 `ReportMedia` 关系表关联;新建报告保存前仍依赖本地预览对象。
|
||
|
||
生产化方向见 [docs/backendization-plan.md](./docs/backendization-plan.md)。
|
||
|
||
## 后续方向
|
||
|
||
项目下一阶段建议后端化和用户化:
|
||
|
||
- 继续增强报告媒体模型,例如断点续传、转码、后端抽帧和独立媒体审计。
|
||
- 查看日志、第三方代理调用摘要、错误追踪和更细粒度操作审计。报告导出按权限控制,不要求专门导出审计。
|
||
- 后端 PDF 导出、对象存储、限流和备份恢复。
|