# expert **Repository Path**: fishfourfar/expert ## Basic Information - **Project Name**: expert - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-16 - **Last Updated**: 2026-09-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 医院专家数据库管理系统 ## 技术栈 | 层级 | 技术 | |------|------| | 前端 | React 18 + TypeScript + Ant Design + Vite | | 后端 | FastAPI + SQLAlchemy + Pydantic | | 数据库 | PostgreSQL 16 | | 部署 | Docker + Docker Compose + Nginx | ## 本地开发 ### 前置条件 - Docker Desktop ### 1. 准备环境变量 ```bash cp .env.example .env ``` 模板里几个密钥**刻意留空**,需要填上(不填 compose 会直接拒绝启动并提示缺哪个): ```bash # 生成两个随机值 openssl rand -base64 24 # → 填到 DB_PASSWORD openssl rand -hex 32 # → 填到 SECRET_KEY ``` `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` 本地随便填一对(比如 `minioadmin` / `minioadmin123`)即可;`ADMIN_USERNAME` / `ADMIN_PASSWORD` 本地不用填 —— 演示数据会创建 `admin` / `expert` 两个账号。 ### 2. 一键启动 ```bash ./dev.sh # 起 db / minio / backend / frontend 四个服务 ./dev.sh --build # 改了 front/ 下的代码后用这个(前端是构建产物,不是 vite dev) ``` 前端 http://localhost ,后端 http://localhost:8000 ,API 文档 http://localhost:8000/api/docs 改后端代码存盘即生效(容器内 `uvicorn --reload`);改前端要 `./dev.sh --build`。 本地这套在 `docker-compose.yml` 之上叠加了 `docker-compose.dev.yml`(端口映射、 演示数据、热重载),生产部署不带它。 ### 默认账号(仅本地开发) 这两个账号由演示数据创建,只在开启 `SEED_DEMO_DATA=true`(即 `./dev.sh`)时存在。 生产环境不会创建它们,改由 `ADMIN_USERNAME` / `ADMIN_PASSWORD` 引导首个管理员。 | 角色 | 用户名 | 密码 | |------|--------|------| | 管理员 | admin | admin123 | | 专家 | expert | expert123 | --- ## 生产部署(Docker Compose) ```bash # 1. 复制环境变量 cp .env.example .env # 编辑 .env,把这几项填上(模板里刻意留空,不填会直接启动失败并提示缺哪个): # - DB_PASSWORD 数据库密码,openssl rand -base64 24 # - SECRET_KEY JWT 密钥,openssl rand -hex 32 # - MINIO_ACCESS_KEY / MINIO_SECRET_KEY 对象存储凭据 # - ADMIN_USERNAME / ADMIN_PASSWORD 初始管理员,否则后台无法登录 # - SEED_DEMO_DATA 保持 false # 2. 一键启动所有服务 docker compose up -d --build # 3. 用 ADMIN_USERNAME 那个账号登录,立刻改密码,然后把 ADMIN_PASSWORD 从 .env 删掉 # (账号已存在时后端不会改动它的密码,但别把密码留在环境里) # 4. 查看状态 docker compose ps docker compose logs -f ``` 访问:http://your-server-ip > **默认只对外暴露 80 端口。** `db`(5432) / `minio`(9000、9001) / `backend`(8000) > 在生产配置里都不映射到宿主机 —— 外部流量一律经 nginx,暴露 8000 就等于绕过 nginx > 直打后端。本地开发需要的端口映射在 `docker-compose.dev.yml` 里(走 `./dev.sh`), > 且都绑在 `127.0.0.1`。 > **密钥缺失会拒绝启动。** 这几个变量都用 `docker compose` 的 `${VAR:?...}` 强制要求, > 后端还会额外校验 `SECRET_KEY` 不是示例值(见 `backend/config.py`)。 > 这是故意的:宁可起不来,也不要静默地用开发默认密钥跑起来 —— 那个密钥能签 JWT, > 泄露即可伪造管理员。 > **生产环境不灌演示数据。** 只有 `SEED_DEMO_DATA=true` 才会写入演示专家/组织/审批/任务。 > 不要在生产开启:演示专家的编号(`E001`…)与真实专家共用同一套编号规则 > (`generate_expert_no` = `E{max_id+1:03d}`),而灌入逻辑是按编号匹配后 > **逐字段覆盖**,会把撞号的真实专家资料洗掉。 > 本地开发由 `docker-compose.dev.yml` 打开这个开关(走 `./dev.sh`)。 > **别把本地的 `.env` 拷到服务器。** `docker compose` 会自动读取项目根的 `.env` 做变量 > 替换(跟用哪个 `-f` 无关),而本地那份含本机路径的 `DATABASE_URL` 和真实的 > `SILICONFLOW_API_KEY`。服务器上从 `.env.example` 重新生成。 ### HTTPS 仓库里备好了配置,但**证书要你自己申请**(需要域名解析到服务器)。分两种走法: **走法 A:nginx 终止 TLS(推荐,单台 CVM 最省事)** 1. 在腾讯云申请免费 DV 证书(SSL 证书 → 申请免费证书),下载 **nginx 格式**, 得到 `<域名>.crt` 和 `<域名>.key`; 2. 放到项目根新建的 `certs/` 目录,命名成 nginx 配置认的两个固定名字 (改名或软链都行,目录已 gitignore,证书不会进仓库): ```bash mkdir -p certs cp ~/Downloads/<域名>.crt certs/fullchain.pem cp ~/Downloads/<域名>.key certs/privkey.pem ``` 3. 带上 TLS 覆盖层启动: ```bash docker compose -f docker-compose.yml -f docker-compose.tls.yml up -d --build ``` 它会多开 443,并把 `front/nginx.ssl.conf` 挂载覆盖掉镜像里的 nginx 配置 —— 不用改 Dockerfile、不用重建镜像。80 端口会 301 跳到 HTTPS。 安全组记得放行 443。 **走法 B:在腾讯云 CLB 上终止 TLS** 用负载均衡监听 443、绑定证书,后端转发到 CVM 的 80。此时**不要**用 `docker-compose.tls.yml`,只跑 `docker compose up -d --build` 即可 (注意:CLB 是另收费的,单台机器一般没必要)。 > 换用 certbot 自动续期也可以,但需要在 `front/nginx.ssl.conf` 的 80 块里补一个 > `/.well-known/acme-challenge/` 的 location 并把 webroot 挂进容器;用云厂商的免费证书 > 到期手动换一次更简单。 ### 备份与恢复 ```bash # 手动备份(数据库 + 上传文件 → ./backups/,保留 7 天) ./backup.sh # 恢复(覆盖式,会二次确认) ./restore.sh backups/db-20260917-031700.dump ./restore.sh backups/db-20260917-031700.dump backups/minio-20260917-031700.tar.gz ``` 配 cron 每天自动跑(时间刻意避开整点,减少和别的任务撞在一起): ```bash crontab -e # 每天 03:17 备份,日志追加到 backups/backup.log 17 3 * * * cd /opt/expert && ./backup.sh >> backups/backup.log 2>&1 ``` 保留天数可用 `RETENTION_DAYS=14 ./backup.sh` 覆盖。 > ⚠️ **备份落在本机磁盘上,机器整个挂掉就一起没了。** 重要数据请把 `backups/` > 再同步一份到对象存储(如腾讯云 COS)或另一台机器上。 > > `restore.sh` 会先停掉 `backend`(它的连接会挡住 `pg_restore` 的 `DROP TABLE`), > 恢复完自动拉起来;带第二个参数恢复上传文件时也会停 `minio`。 --- ## 云平台部署建议 ### 阿里云 / 腾讯云 / AWS ``` 架构: 负载均衡 (SLB/CLB/ALB) ↓ ECS/CVM/EC2 集群 ├── frontend (Nginx) ← 托管前端静态文件 + 反向代理 └── backend (FastAPI) ← 4 workers ↓ RDS PostgreSQL ← 托管数据库 ↓ OSS/COS/S3 ← 上传文件存储(替换本地 uploads/) ``` ### 环境变量(生产) ```bash DATABASE_URL=postgresql://hospital:<强密码>@db:5432/hospital_expert SECRET_KEY= DEBUG=false CORS_ORIGINS=https://your-domain.com ``` (前端走 nginx 同源代理 `/api/`,一般不需要 CORS;配了域名后用逗号分隔追加即可。) ### 升级建议 1. **文件存储**:将 `uploads/` 目录替换为 OSS/COS/S3,修改 `routers/approvals.py` 的上传逻辑 2. **Redis 缓存**:加 Redis 缓存 dashboard 统计数据(高并发场景) 3. **数据库备份**:见上面「备份与恢复」,已提供 `backup.sh` / `restore.sh`; 上 RDS 后可改用 RDS 自动备份 4. **HTTPS**:见上面「HTTPS」,配置已备好 5. **监控**:接入 Prometheus + Grafana,或云厂商监控服务 6. **备份异地存放**:把 `backups/` 同步到 COS 等对象存储(见「备份与恢复」的提醒) --- ## 项目结构 ``` expert/ ├── backend/ # FastAPI 后端 │ ├── routers/ # 路由模块 │ │ ├── auth.py # 认证 │ │ ├── experts.py # 专家管理 │ │ ├── approvals.py # 审批流程 │ │ ├── tasks.py # 任务管理 │ │ ├── messages.py # 消息中心 │ │ ├── organizations.py # 组织管理 │ │ ├── roles.py # 角色权限 │ │ ├── users.py # 用户管理 │ │ └── analytics.py # 数据统计 │ ├── models.py # 数据库模型 │ ├── schemas.py # Pydantic 数据验证 │ ├── auth.py # JWT 鉴权 │ ├── config.py # 配置管理 │ ├── database.py # 数据库连接 │ ├── seed.py # 初始化数据 │ ├── main.py # 应用入口 │ ├── requirements.txt │ └── Dockerfile ├── front/ # React 前端 │ ├── src/app/ │ │ ├── api/ # API 请求层 │ │ ├── context/ # 状态管理 │ │ ├── layouts/ # 布局组件 │ │ └── pages/ # 页面组件 │ ├── Dockerfile │ ├── nginx.conf # HTTP 配置(默认) │ └── nginx.ssl.conf # HTTPS 配置(走法 A 时挂载覆盖) ├── docker-compose.yml # 基础配置:生产安全(只开 80) ├── docker-compose.dev.yml # 本地开发覆盖层(端口映射 + 演示数据 + 热重载) ├── docker-compose.tls.yml # HTTPS 覆盖层(可选) ├── backup.sh # 备份数据库与上传文件 ├── restore.sh # 从备份恢复 ├── .env.example # 环境变量模板 └── README.md ```