# 性能优化实施计划 > 设计规格: `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` 后调用 `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_name,handler 层映射到响应 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_name,follow_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 请求;首屏 < 500ms(20 条记录)。 ### 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`,按 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 完成后验证