Files
Mdeical_Sur_Report/README.md
admin e159d18f01 Polish generic NAS deployment template
- Rework docker-compose-Nas.yaml as a QNAP/NAS-friendly generic template with placeholders for NAS IP, public domain, proxy, and secrets.

- Preserve QNAP-specific fixes including relative build context, api network alias, NAS persistent paths, and 127.0.0.1 health checks.

- Keep optional frpc profile while avoiding real server addresses or tokens in the reusable template.

- Replace residual deployment examples with generic your-domain.example, NAS_IP, PROXY_IP, and LAN_CIDR placeholders in README and Docker docs.
2026-05-09 03:46:56 +08:00

422 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 手术图文病历报告系统
手术图文病历报告系统是一个面向医院/科室场景的前端应用,用于撰写手术图文报告、管理报告模板、维护用户权限、从手术视频抽取关键帧,并通过 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` 默认通过 Compose profile 关闭,配置文件在 `frpc/frpc.toml`,需要映射到公网服务器时再启用。
- `nginx.conf` 已配置 SPA 路由回退、`/api` 反向代理和 `100m` 请求体上限。
- `nginx.conf` 已支持 `/api/speech/iat` WebSocket upgrade。
完整 Docker 说明见 [docs/docker.md](./docs/docker.md)。
## 威联通 NAS 部署
<!-- # XXX NAS 部署新增:适用于 QNAP/威联通 Container Station 或 NAS 终端 docker compose 部署。 -->
仓库提供 [docker-compose-Nas.yaml](./docker-compose-Nas.yaml),用于在威联通 NAS 上直接部署完整图文报告系统。它与默认 Compose 的主要区别:
- 使用 `/share/Container/tuwen_system_v2/data` 作为默认持久化目录。`# XXX` 如 NAS 共享目录不同,修改 `NAS_DATA_ROOT` 或 compose 文件中的路径。
- API 默认映射到 NAS 宿主机 `4102`,前端仍映射到 `4002`
- 保留 `api` 网络别名,确保容器内 Nginx 可以继续把 `/api` 代理到后端。
- 使用 `127.0.0.1` 做 API 健康检查,规避部分 NAS 固件上的 IPv6 localhost 解析问题。
- 内置可选 `frpc` profile可复用 [frpc/frpc.toml](./frpc/frpc.toml) 把 `4002` 映射到公网服务器。
NAS 终端部署:
```bash
# XXX 进入 NAS 上的项目目录后执行。
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 `.env` 或 Container Station 环境变量:
```bash
# XXX 必改:生产环境不要继续使用默认 Session 密钥。
SESSION_SECRET=替换为足够长的随机字符串
# XXX 如果公网入口是 https://your-domain.example建议开启这两项。
SESSION_COOKIE_SECURE=true
TRUST_PROXY=true
CORS_ORIGIN=http://NAS_IP:4002,https://your-domain.example
# XXX 如需代理构建或让后端访问外网,可按 NAS 网络环境配置。
HTTP_PROXY=http://PROXY_IP:PROXY_PORT
HTTPS_PROXY=http://PROXY_IP:PROXY_PORT
NO_PROXY=localhost,127.0.0.1,LAN_CIDR,tuwen_db,tuwen_api,tuwen_web,api,db,web
```
启用 NAS 上的 frpc 隧道:
```bash
# XXX 先编辑 frpc/frpc.toml替换 serverAddr 和 auth.token。
docker compose -f docker-compose-Nas.yaml --profile frpc up -d --build
docker compose -f docker-compose-Nas.yaml logs -f frpc
```
`# XXX` NAS 公网访问仍然只需要把 `4002` 通过 frpc 映射到公网服务器,再由 Nginx Proxy Manager 给 `your-domain.example` 提供 HTTPS。`4443` 仍只是本机自签名 HTTPS 演示入口,不建议映射到公网域名。
## 公网反向代理部署
<!-- # XXX 公网部署新增:适用于本机 Docker 4002 端口通过 frpc 映射到公网服务器,再由 Nginx Proxy Manager 绑定 your-domain.example 的部署链路。 -->
推荐公网链路:
```text
浏览器 https://your-domain.example
-> 公网服务器 Nginx Proxy Manager
-> frps/frpc 映射端口
-> 本机 Docker web:4002
-> 容器 Nginx /api 代理
-> api:3100
```
本机部署并确认 Docker 服务正常:
```bash
# XXX 在部署机器上使用当前最新版分支。
git fetch origin
git checkout surclaw-system-backendized-20260502
git pull --ff-only origin surclaw-system-backendized-20260502
# XXX 公网 HTTPS 部署建议替换 Session 密钥,并信任外层反向代理。
export SESSION_SECRET="替换为足够长的随机字符串"
export SESSION_COOKIE_SECURE="true"
export TRUST_PROXY="true"
docker-compose up -d --build
curl http://127.0.0.1:4002/api/health
```
如果使用 frpc 把本机 `4002` 映射到公网服务器,先编辑仓库内置配置:
```toml
# frpc/frpc.toml
# XXX 替换公网 frps 地址和 token不要把正式 token 提交到仓库。
serverAddr = "XX.XX.XX.XX"
serverPort = 7000
auth.method = "token"
auth.token = "XXX"
[[proxies]]
name = "TuWen_System_V2"
type = "tcp"
localIP = "127.0.0.1"
localPort = 4002
remotePort = 4002
```
`# XXX` 当前 `docker-compose.yaml` 已内置可选 `frpc` 服务,并使用 `network_mode: host` 让 frpc 容器可以访问本机 `127.0.0.1:4002`。启动完整系统和 frpc
```bash
docker-compose --profile frpc up -d --build
docker-compose logs -f frpc
```
如果只想先启动系统、不启用公网隧道,继续使用:
```bash
docker-compose up -d --build
```
`# XXX` 公网正式访问只需要映射 `4002` 到公网服务器,再由公网 Nginx Proxy Manager 提供 `https://your-domain.example`。不建议把 `4443` 映射到公网域名;`4443` 是本机自签名 HTTPS 演示入口,主要用于 `https://localhost:4443` 测试麦克风。
Nginx Proxy Manager 中为 `your-domain.example` 新建 Proxy Host
- `Domain Names``your-domain.example`
- `Scheme``http`
- `Forward Hostname / IP`frps 可访问到的映射地址,通常是 `127.0.0.1`、公网服务器内网 IP 或 frps 指定监听地址。
- `Forward Port`frpc 暴露出来的 `remotePort`,例如 `4002`
- `Websockets Support`:开启。`# XXX` 语音识别使用 `/api/speech/iat` WebSocket必须透传 Upgrade。
- `Block Common Exploits`:开启。
- `SSL`:申请或绑定 `your-domain.example` 证书,开启 `Force SSL``# XXX` 浏览器麦克风权限要求 HTTPS普通公网 HTTP 下语音识别不可用。
Nginx Proxy Manager 的 `Advanced` 可加入:
```nginx
# XXX 图文报告、关键帧和模板图片可能较大,公网代理请求体上限需与容器 Nginx/API 保持一致。
client_max_body_size 100m;
# XXX WebSocket 语音听写需要较长连接时间。
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
```
公网验收顺序:
```text
1. 打开 https://your-domain.example/api/health确认 API 健康检查可访问。
2. 打开 https://your-domain.example 登录 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 导出、对象存储、限流和备份恢复。