Files
hms/docs/superpowers/plans/2026-04-26-performance-optimization.md
iven b410fa9f78
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
docs: 5 份实施计划 — 性能/安全/事件/前端/可观测性
对应 5 份设计规格,共 75 个 Task:

1. 性能优化 (12 Task) — 批量INSERT/N+1内联name/合并COUNT/按需重绘/chunk拆分
2. 安全纵深防御 (8 Task) — RLS/行级数据范围/Redis session_key/审计哈希链
3. 事件驱动架构 (10 Task) — 11个缺失事件补发/LISTEN+NOTIFY/schema版本化
4. 前端工程化 (10 Task) — hook统一/组件拆分/Bundle优化
5. 可观测性运维 (10 Task) — 深度健康检查/Prometheus/OTel/生产Docker/告警
2026-04-27 08:00:50 +08:00

125 lines
5.4 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.
# 性能优化实施计划
> 设计规格: `docs/superpowers/specs/2026-04-26-performance-optimization-design.md`
> 日期: 2026-04-26 | 状态: draft | 总周期: 3 周
---
## Phase 1: 后端批量插入优化Week 1, P0
### Task 1: device_reading_service batch_insert_readings 改为 SeaORM insert_many
**涉及文件**: `crates/erp-health/src/service/device_reading_service.rs`
**步骤**: 将 `batch_insert_readings()` 中 for 循环逐条 `model.insert(db).await` 替换为构建 `Vec<ActiveModel>` 后调用 `insert_many()` + `on_conflict(columns([...]).do_nothing())`。补充 50 条模拟数据的批量插入测试。
**验收**: `cargo test -p erp-health device_reading` 通过500 条插入延迟 < 100ms。
### Task 2: device_reading_service upsert_hourly_aggregates 批量化
**涉及文件**: `crates/erp-health/src/service/device_reading_service.rs`
**步骤**: 批量查出已存在的聚合记录(按 patient_id + device_type + hour分为"新增"和"更新"两组,分别 `insert_many` 和批量 `update`,事务包装。补充同一小时多次 upsert 的聚合精度测试。
**验收**: 聚合值avg/min/max精度与优化前一致`cargo test` 通过。
---
## Phase 2: 前端 N+1 根治Week 2, P0
### Task 3: 后端 appointment_service list 返回 patient_name/doctor_name
**涉及文件**: `crates/erp-health/src/service/appointment_service.rs`, `dto/mod.rs`, `handler/mod.rs`
**步骤**: 列表 DTO 增加 `patient_name/doctor_name` 字段service 查询中 `find_also_related` 或子查询关联 users 表获取 display_namehandler 层映射到响应 DTO。保持 Option 类型向后兼容。
**验收**: `GET /api/v1/health/appointments` 每条记录含 name 字段。
### Task 4: 后端 consultation_service / follow_up_service 同样内联 name
**涉及文件**: `consultation_service.rs`, `follow_up_service.rs`, `dto/mod.rs`
**步骤**: consultation list 添加 patient_name/doctor_namefollow_up list 添加 patient_name复用 Task 3 的 JOIN 模式。
**验收**: 两个列表 API 响应均含 name 字段;`cargo test` 通过。
### Task 5: 前端 AppointmentList 移除 nameCache改用内联字段
**涉及文件**: `apps/web/src/pages/health/AppointmentList.tsx`
**步骤**: 移除 `nameCache` useState 及逐条请求 name 的 useEffect表格列直接使用后端返回的 `patient_name/doctor_name`,消除 fetchData 对 nameCache 的依赖。
**验收**: Network 面板仅 1 个列表 API 请求;首屏 < 500ms20 条记录)。
### Task 6: 前端 ConsultationList / FollowUpTaskList 同样改造
**涉及文件**: `ConsultationList.tsx`, `FollowUpTaskList.tsx`
**步骤**: 两个页面移除 nameCache使用内联 name 字段。验证无 N+1 请求。
**验收**: 两个页面 Network 面板无 N+1 请求;`pnpm build` 通过。
---
## Phase 3: 后端查询优化Week 3, P1
### Task 7: stats_service 合并多次 COUNT 为 GROUP BY
**涉及文件**: `crates/erp-health/src/service/stats_service.rs`
**步骤**: 6 个统计函数从多次 COUNT 合并为 `SELECT status, COUNT(*) GROUP BY status` + 应用层 HashMap 聚合。`compute_avg_field` 用宏生成静态 SQL 常量替代 `format!` 拼接。编写对比测试确认 GROUP BY 与多次 COUNT 结果一致。
**验收**: `get_follow_up_statistics` 查询次数从 4 降为 1`compute_avg_field` 不再 format! 拼接。
### Task 8: patient_service get_health_summary 用 tokio::join! 并行化
**涉及文件**: `crates/erp-health/src/service/patient_service.rs`
**步骤**: `get_health_summary()` 中 4 次 `.await` 改为 `tokio::join!` 并行执行,各查询错误独立处理(未找到返回 None
**验收**: 并行化后返回数据与串行一致;`cargo test` 通过。
### Task 9: alert_engine 预加载规则批量评估
**涉及文件**: `crates/erp-health/src/service/alert_engine.rs`
**步骤**: 批量查询患者最近 cooldown 期间所有 alerts 构建 `HashSet<rule_id>`,按 device_type 批量查最新 hourly 记录后在内存匹配规则条件。重构为:批量加载 -> 内存过滤 -> 批量生成告警。
**验收**: 10 条规则评估查询次数从 ~20 降为 2-3`cargo test` 通过。
---
## Phase 4: 前端渲染优化Week 3, P2
### Task 10: PluginCRUDPage columns useMemo + 拆分子组件
**涉及文件**: `apps/web/src/pages/PluginCRUDPage.tsx`
**步骤**: `columns` 包裹 `useMemo(() => [...], [schema])`,搜索栏/分页/表格拆为独立子组件。
**验收**: 输入搜索时 columns 不重建;`pnpm build` 通过。
### Task 11: PluginGraphPage 按需重绘
**涉及文件**: `apps/web/src/pages/PluginGraphPage.tsx`
**步骤**: 移除持续 requestAnimationFrame 循环,改为数据变更 useEffect 触发单次重绘 + ResizeObserver 监听容器变化。
**验收**: 静态页面时 CPU 占用 < 1%`pnpm build` 通过。
### Task 12: vite.config.ts manualChunks 拆分 heavy deps
**涉及文件**: `apps/web/vite.config.ts`
**步骤**: `manualChunks` 配置 `vendor-charts`(@ant-design/charts) / `vendor-flow`(@xyflow/react) / `vendor-editor`(@wangeditor/editor),对应路由改用 `React.lazy` 动态加载。
**验收**: 主 bundle gzip 体积降低 200KB+;图表/流程图/编辑器按需加载。
---
## 执行原则
1. **每 Task 完成后立即提交** — 不积压,保持可追溯
2. **Phase 1-2 为 P0** — 批量插入和 N+1 根治直接影响生产性能
3. **cargo test + pnpm build 必须通过** — 每个 Task 完成后验证