Files
soft/上线规划文档.md
MingNian d5f167167a feat: replace Redis with PostgreSQL for caching, rate limiting, SMS codes, and token blacklist
- Add 4 PG entities: VerificationCode, RateLimitEntry, TokenBlacklistEntry, CacheEntry
- Add 4 services: VerificationService, RateLimitService, TokenBlacklistService, CacheService
- Add CleanupBackgroundService for periodic expired data cleanup
- Add MigrationHelper for safe schema migration without data loss
- Update AuthController: real SMS code generation, rate limiting, logout endpoint with JWT blacklist
- Update JwtProvider: add JTI claim for token revocation
- Update Program.cs: register new services, JWT blacklist validation, DB migration
- Remove StackExchange.Redis NuGet package and all Redis config references
- Update start-dev.bat: 6→5 services, remove Redis startup
- Update docs: remove Redis references from all documentation
- Fix: logout button spacing on profile page
- Fix: .gitignore data/→/data/ to not ignore Infrastructure/Data/
2026-05-26 13:48:53 +08:00

14 KiB
Raw Blame History

上线全流程规划文档

当前项目是一个跑在本地的 Demo。本文档说明要成为一个真正可上线运营的产品,还需要做哪些工作。


目录

  1. 文件上传与存储
  2. 医生端查看与解读
  3. 历史记录与数据持久化
  4. 页面过渡动画
  5. 联网与 App 化
  6. 安全与鉴权
  7. 消息推送
  8. 运维与监控
  9. 开发流程规范化
  10. 完整上线检查清单

1. 文件上传与存储

现状

  • 患者点"上传报告",选了文件但没有真正存到服务器
  • 后端只记录了空的 imageUrls: []
  • 没有文件存储服务对接

需要做的事

1.1 MinIO 对接(已在本地装了,没接代码)

MinIO 是一个兼容 AWS S3 的对象存储服务适合存图片、PDF 等文件。

后端需要新增:

backend/src/HealthManager.Infrastructure/Services/FileStorageService.cs   ← 新建

核心功能:

  • UploadAsync(Stream fileStream, string fileName) → 上传到 MinIO返回文件 URL
  • GetFileUrl(string objectKey) → 获取文件访问地址
  • DeleteAsync(string objectKey) → 删除文件

流程:

患者选图片 → 前端调 POST /api/files/upload → 后端接收文件流
→ 上传到 MinIO → 得到 URL → 报告保存时把 URL 存入 imageUrls
→ 医生点查看 → 前端直接通过 URL 加载图片

1.2 新增文件上传接口

POST   /api/files/upload          ← 上传单个文件,返回 { url, key }
POST   /api/files/upload-multiple ← 上传多个文件
GET    /api/files/{key}           ← 下载/预览文件
DELETE /api/files/{key}           ← 删除文件

1.3 前端改造

ReportUploadPage.tsx:
  选文件 → 调上传接口 → 拿到 URL 数组
  → 调创建报告接口 { title, category, imageUrls: ["url1", "url2"] }
  → 显示上传进度条

1.4 文件大小和类型限制

限制项 建议值
单文件最大 10MB
允许格式 jpg, png, pdf
每人每天上限 20 个文件

2. 医生端查看与解读

现状

  • 医生能看到报告标题和状态
  • 但看不到图片(因为根本没存图)
  • 解读功能比较简陋

需要做的事

2.1 图片查看器

医生点报告后,应该能看到所有上传的图片:

ReportDetailPage.tsx:
  - 图片缩略图列表(来自 imageUrls
  - 点击放大查看
  - 支持左右翻页

2.2 报告解读界面增强

当前:只有一个文本框,写一句话
需要:
  - 逐项填写检查结果(血压、血糖、血脂...
  - 异常项自动标红
  - 风险等级(正常/关注/异常)
  - 建议措施的富文本编辑器
  - 解读完成后自动通知患者

2.3 报告历史

医生端需要:

  • 按患者查看历史报告列表
  • 按时间/类型筛选
  • 对比不同时间点的报告变化趋势

3. 历史记录与数据持久化

现状

  • 数据存在本地 PostgreSQL
  • 没有定期备份
  • 健康数据会无限增长

需要做的事

3.1 数据库备份

# 自动备份脚本Linux 服务器上跑)
pg_dump HealthManager > /backup/health_$(date +%Y%m%d).sql

# 每天凌晨 3 点自动执行crontab
0 3 * * * /opt/scripts/backup-db.sh

# 保留最近 30 天,旧自动删除

3.2 历史数据归档

健康记录表会越来越大,需要归档策略:

  • 1 年以上的健康数据 → 移到归档表或压缩存储
  • 3 年以上的 → 可选择性清理
  • 报告和相关文件 → 永久保留

3.3 数据导出

患者应该能导出自己的数据:

GET /api/health-records/export?format=csv
GET /api/health-records/export?format=pdf

4. 页面过渡动画

现状

  • 有 CSS 动画定义但未完全启用
  • 页面切换比较生硬

需要做的事

4.1 路由过渡动画

使用 framer-motion(已安装)实现:

// 页面进入:从右滑入 + 淡入
// 页面离开:向左滑出 + 淡出
// 返回时:从左滑入

<AnimatePresence mode="wait">
  <motion.div
    key={location.pathname}
    initial={{ opacity: 0, x: 50 }}
    animate={{ opacity: 1, x: 0 }}
    exit={{ opacity: 0, x: -50 }}
    transition={{ duration: 0.25 }}
  >
    <Outlet />
  </motion.div>
</AnimatePresence>

4.2 组件级动画

场景 动画
列表加载 骨架屏Skeleton闪烁数据来到后列表项依次弹出
卡片点击 缩放+阴影变化,给"按下去"的反馈
数字变化 健康数据变化时数字滚动动画
按钮点击 涟漪效果ripple effect
消息发送 气泡从输入框弹入对话区
下拉刷新 顶部旋转加载指示器
Toast 提示 从顶部滑入2.5 秒后滑出

4.3 页面切换保留状态

当前切换到别的 Tab 再回来,数据会重新加载。需要:

  • 首页数据缓存 30 秒,切回来直接用
  • 列表页记住滚动位置

5. 联网与 App 化

现状

  • 前端 npm run dev 跑在本地
  • 后端 dotnet run 跑在本地
  • 只能用 localhost 访问

需要做的事

5.1 部署到服务器

最简单方案:一台 Linux 云服务器(阿里云/腾讯云)

服务器配置建议:
  - 2核 CPU
  - 4GB 内存
  - 40GB SSD
  - Ubuntu 22.04

需要安装的东西和本地一样:

  • PostgreSQL 18
  • MinIO
  • .NET 10 Runtime
  • Nginx作为反代和静态文件服务

5.2 Nginx 配置

把所有服务统一到一个域名下:

https://api.chatmed.online  → 后端 API (5000端口)
https://patient.chatmed.online → 患者前端 (打包成静态文件)
https://doctor.chatmed.online  → 医生前端 (打包成静态文件)
https://files.chatmed.online   → MinIO (9000端口)

5.3 前端打包部署

现在跑的是开发模式Vite dev server上线要用生产模式

# 患者端
cd frontend-patient
npm run build          # 生成 dist/ 文件夹
# 把 dist/ 里的文件放到 Nginx 的目录下

# 医生端同理
cd frontend-doctor
npm run build

5.4 打包成手机 App

如果要在手机上像 App 一样用(不是浏览器打开),两种方案:

方案 APWA推荐最简单

在网页基础上加个配置文件,用户浏览器打开后可以"添加到主屏幕",就像真的 App

// public/manifest.json
{
  "name": "健康管家",
  "short_name": "健康管家",
  "start_url": "/",
  "display": "standalone",     // 全屏,没有浏览器地址栏
  "background_color": "#F2F5FA",
  "theme_color": "#1E6BFF",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192" },
    { "src": "/icon-512.png", "sizes": "512x512" }
  ]
}

再加一个 Service Worker 实现离线缓存——没网也能看历史数据。

方案 B套壳 App推荐给小白用户

用 Capacitor 把网页包成一个真实的 APK/IPA

npm install @capacitor/core @capacitor/cli
npx cap init
npx cap add android      # 生成 Android 项目
npx cap add ios          # 生成 iOS 项目
npx cap sync             # 把前端代码同步进去

然后分别在 Android Studio 和 Xcode 里打包成 .apk.ipa,可以上传到应用商店。

对比 PWA Capacitor 套壳
开发成本 低(只需配置) 中(需原生打包环境)
用户安装方式 网页上点"添加到桌面" 应用商店下载
离线支持
推送通知 iOS 支持有限 完整支持
应用商店上架 不能
建议 先做这个,快速验证 验证完再做这个

6. 安全与鉴权

现状

  • JWT 登录30 分钟过期
  • 密码是 SHA256Demo 够用,上线不够)
  • 没有 HTTPS
  • 没有接口限流
  • 手机验证码是假的(不验证)

需要做的事

6.1 HTTPS + 域名

# 用 Let's Encrypt 免费申请 SSL 证书
# 所有请求强制走 HTTPS
# 后端、前端、MinIO 全部 HTTPS

6.2 密码安全

当前方案SHA256(password)
上线方案bcrypt 或 Argon2加盐哈希防彩虹表攻击

6.3 真实短信验证码

接腾讯云短信 / 阿里云短信:

POST /api/auth/send-sms → 调短信平台API → 用户手机收到验证码
验证码 5 分钟过期
同一号码 60 秒内不能重复发送

6.4 接口安全

措施 说明
限流 同一 IP 每分钟最多 60 次请求
登录限流 同一手机号 5 次失败后锁定 15 分钟
敏感操作二次验证 修改手机号、删除数据需要再输入验证码
SQL 注入防护 EF Core 已自带参数化查询
XSS 防护 前端 React 已自带转义
CORS 只允许自己域名

6.5 数据脱敏

日志里不能记录手机号、密码、真实姓名
显示时手机号中间四位打星号138****8000

7. 消息推送

现状

  • 通知存在数据库里,前端轮询拉取
  • 用户不打开 App 就看不到新消息

需要做的事

7.1 WebSocket 实时推送

当前已有 SignalR/hubs/chat),需要扩展到:

  • 新消息实时弹窗
  • 医生解读完成 → 患者立即收到通知
  • 用药提醒时间到了 → 推送通知

7.2 App 推送(离线也能收到)

平台 推送服务
Android Firebase Cloud Messaging (FCM)
iOS Apple Push Notification Service (APNs)
国内 Android 华为/小米/OPPO 推送(不用 FCM

后端需要新增一个 PushService统一处理各类推送。


8. 运维与监控

需要做的事

8.1 日志系统

当前:控制台输出
需要Serilog 写入文件 + 集中收集
├── 请求日志(谁、什么时候、调了什么接口)
├── 错误日志(哪出错了、堆栈信息)
└── 慢查询日志(哪些 SQL 耗时超过 500ms

8.2 健康检查

GET /health → 检查数据库连接、MinIO连接
返回 { status: "healthy", db: "ok", minio: "ok" }

自动化监控:每 30 秒检查一次,挂了自动发短信/邮件报警。

8.3 性能监控

.NET:     Application Insights 或 Prometheus
PostgreSQL: pg_stat_statements 插件
前端:      Web Vitals (LCP, FID, CLS)

8.4 自动部署

# 当 git push 到 main 分支时,服务器自动:
1. git pull 拉取最新代码
2. dotnet build 编译后端
3. npm run build 编译前端
4. 重启服务

用 GitHub Actions 或 Jenkins 实现。


9. 开发流程规范化

当前

  • 直接在 main 分支上改代码
  • 没有测试
  • 没有代码审查

需要建立

9.1 分支管理

main        ← 生产环境,只有经过测试的代码才合并进来
develop     ← 开发环境
feature/*   ← 每个新功能一个分支(如 feature/file-upload
bugfix/*    ← 修 Bug 的分支
release/*   ← 准备上线的版本

9.2 提交规范

feat: 文件上传功能
fix: 修复医生看不到报告的bug
docs: 更新使用手册
style: 调整首页配色
refactor: 重构健康数据服务
test: 添加登录接口测试

9.3 自动化测试

单元测试:关键业务逻辑(如风险等级计算)
集成测试API 接口(用 xUnit + WebApplicationFactory
E2E 测试:核心用户流程(用 Playwright

9.4 代码审查

每个 PR 至少一个人 Review 后才能合并。


10. 完整上线检查清单

阶段一Demo 完善(当前 → 2周内

  • 后端 API 全功能
  • 患者前端全功能
  • 医生前端全功能
  • 文件上传到 MinIO
  • 医生端图片查看
  • 页面过渡动画
  • 上报 Bug 修复

阶段二内部测试2-4周

  • 部署到测试服务器
  • 配置 HTTPS + 域名
  • 手机号验证码真实对接
  • 密码改用 bcrypt
  • 接口限流
  • 3-5 个真实用户测试
  • 收集反馈,修复问题

阶段三上线准备4-6周

  • 部署到生产服务器2核4G起步
  • 数据库自动备份
  • 日志和监控就位
  • PWA 配置(可添加到手机桌面)
  • 性能压测(模拟 100 个用户同时用)
  • 隐私协议 + 用户协议页面
  • 操作手册终稿

阶段四正式运营6-8周+

  • 上线运营
  • 用户反馈渠道
  • 定期数据备份验证
  • 根据数据做功能迭代
  • 考虑 App 应用商店上架Capacitor 打包)
  • 如果需要,接微信小程序

总结Demo vs 上线 差异一览

维度 当前 Demo 上线产品
文件存储 没存imageUrls 为空 MinIO 存储,返回真实 URL
网络 localhost 域名 + HTTPS
鉴权 JWT + SHA256 密码 JWT + bcrypt + 短信验证码
推送 前端轮询 SignalR 实时 + App 离线推送
前端 Vite dev server Nginx 静态文件 / PWA / App
数据库 本地 PostgreSQL 服务器 PostgreSQL + 每日备份
日志 控制台 文件 + 集中收集
测试 单元 + 集成 + E2E
部署 手动 dotnet run 自动化 CI/CD
监控 健康检查 + 性能 + 报警
动画 基础 CSS framer-motion 全页面动画
安全 CORS 限流 + HTTPS + 脱敏 + 审计

最优先要做的三件事:① 文件上传存 MinIO → ② 部署到服务器 → ③ HTTPS + 真实短信