Files
hms/docs/superpowers/specs/2026-04-24-health-module-iteration-design.md
iven 07f4ba41ba
Some checks failed
CI / frontend-build (push) Has been cancelled
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / security-audit (push) Has been cancelled
fix(health): 穷尽审计修复 — 权限同步/编译错误/前端bug/审计日志
审计发现并修复的问题:

HIGH:
- H1: ConsultationDetail 使用 getSession(id) 替代错误的列表搜索
- H2: SessionResp 添加 version/updated_at 字段
- H3: 移除 FollowUpRecordList 调用不存在的导出端点
- H4: 新增 articles.ts 前端 API 模块

MEDIUM:
- M1: article delete 添加乐观锁 (expected_version)
- M2: 取消预约排班释放传播错误 (log::warn -> ?)
- M3: FollowUpTaskList 日期格式 Dayjs -> string
- M4: 补充 15 个缺失审计日志

LOW:
- L1: 替换 follow_up_service 中的 .unwrap()
- L2: PatientListItem 添加 version 字段

CRITICAL (新发现):
- 权限未同步: 健康模块 14 个权限从未写入数据库,添加启动时自动同步
- migration 表名错误: patients -> patient
- 编译错误: health_trend entity 未导入, ToPrimitive trait 未导入
- HealthError 缺少 From<AppError> 实现
2026-04-25 08:58:58 +08:00

26 KiB
Raw Blame History

健康管理模块全面迭代设计

文档版本: 1.0 日期: 2026-04-24 状态: 待评审 基于: 5 位专家(后端架构/前端架构/医疗业务/安全质量/产品策略)深度审查


0. 审查发现总览

0.1 V1 发布阻塞项

# 阻塞项 来源 影响
B1 Web 健康模块 10 页面未实现 前端架构/产品策略 无法演示和交付
B2 医疗数据安全不合规 安全质量 零 sanitize / 零审计 / 身证明文 / 零测试
B3 数据一致性缺陷 医疗业务/后端架构 排班可超额 / 名额释放可能失败 / 随访逾期未实现
B4 事件处理器空壳 后端架构 随访状态/咨询消息不联动

0.2 当前完成度

层级 模块 完成度
后端 erp-health16 实体/8 服务/7 handler/40+ API 95%
后端 事件处理器业务逻辑 0%(框架已搭建,需填充 db 操作)
后端 sanitize / 审计 / 加密 0%
后端 测试覆盖 0%
Web 前端 健康模块页面 0%
Web 前端 健康模块 API 服务层 0%
小程序 初版 21 页面 85%

1. 安全省基(阶段 11.5-2 周)

1.1 sanitize 全覆盖

问题: erp-health 模块没有任何对 strip_html_tags 的调用,攻击者可在患者姓名、病史等字段注入 XSS payload。

参考实现: crates/erp-auth/src/dto.rs 第 96-118 行,CreateUserReqUpdateUserReq 已实现 sanitize() 方法。

修复方案: 为每个 DTO 的字符串输入字段添加 sanitize。

覆盖字段清单:

DTO 文件 字段
patient_dto.rs CreatePatientReq / UpdatePatientReq name, notes, allergy_history, medical_history_summary, emergency_contact_name, source
patient_dto.rs FamilyMemberReqcreate + update 共用) name, notes
patient_handler.rs AssignDoctorReq位于 handler 非 dto — (无字符串字段)
health_data_dto.rs CreateVitalSignsReq notes
health_data_dto.rs CreateLabReportReq doctor_interpretation
health_data_dto.rs CreateHealthRecordReq source, overall_assessment, notes
appointment_dto.rs CreateAppointmentReq notes, cancel_reason
follow_up_dto.rs CreateFollowUpTaskReq / UpdateFollowUpTaskReq content_template
follow_up_dto.rs CreateFollowUpRecordReq patient_condition, medical_advice
consultation_dto.rs CreateMessageReq content
consultation_dto.rs CreateSessionReq — (无字符串字段)
doctor_dto.rs CreateDoctorReq / UpdateDoctorReq department, title, specialty, bio

实现模式:

// 封装 sanitize 辅助函数(与 erp-auth 的 sanitize_option 模式一致)
fn sanitize_option_string(opt: Option<String>) -> Option<String> {
    opt.map(|s| strip_html_tags(&s))
}

// 在每个 DTO 的 impl 中添加 sanitize 方法
impl CreatePatientReq {
    pub fn sanitize(&mut self) {
        self.name = strip_html_tags(&self.name);
        self.notes = sanitize_option_string(self.notes.take());
        self.allergy_history = sanitize_option_string(self.allergy_history.take());
        self.medical_history_summary = sanitize_option_string(self.medical_history_summary.take());
        // ...
    }
}

// 在 handler 调用 service 前执行
async fn create_patient(/* ... */) -> AppResult<Json<ApiResponse<PatientResp>>> {
    let mut req: CreatePatientReq = Json(req).0;
    req.sanitize();
    // ...
}

前端安全: ChatBubble 组件必须使用 React 默认 JSX 转义渲染文本内容(不使用 dangerouslySetInnerHTML),图片消息 URL 需做白名单校验。

1.2 审计日志注入

问题: erp-health 整个模块没有任何对 audit_service::record 的调用。

参考实现: crates/erp-auth/src/service/auth_service.rs 第 168-177 行。

修复方案: 在所有写入操作的 service 层添加审计记录。

覆盖操作清单:

Service 操作 审计 action
patient_service create_patient patient.created
patient_service update_patient patient.updated
patient_service delete_patient patient.deleted
patient_service manage_patient_tags patient.tags_updated
health_data_service create_vital_signs vital_signs.created
health_data_service create_lab_report lab_report.created
health_data_service create_health_record health_record.created
appointment_service create_appointment appointment.created
appointment_service update_appointment_status appointment.status_changed
follow_up_service create_task follow_up_task.created
follow_up_service create_record follow_up_record.created
consultation_service create_session consultation.opened
consultation_service close_session consultation.closed
consultation_service create_message consultation.message_sent
doctor_service create/update/delete_doctor doctor.*

审计日志内容: tenant_id、user_id、action、resource_type、resource_id、变更前后值摘要。

注意: 当前 audit_service::record 是 fire-and-forget审计日志丢失对医疗合规不可接受。修复方案

  1. 新增 record_in_txn(log: AuditLog, txn: &DatabaseTransaction) 方法,在事务内 await 写入
  2. 保留原 record 方法用于不要求事务保证的场景
  3. erp-health 的关键写入操作使用 record_in_txn,失败时回滚整个事务
  4. 需要改为事务包裹的 service 方法create_patient、update_patient、delete_patient、create_appointment、update_appointment_status、create_record随访、create_message咨询

1.3 身份证号加密存储

问题: patient.id_number 明文存储在数据库中,违反《个人信息保护法》。

方案: AES-256-GCM 应用层加密。

新增文件: crates/erp-health/src/crypto.rs

pub struct HealthCrypto { key: [u8; 32] }

impl HealthCrypto {
    pub fn from_env() -> Self { /* 从 ERP__HEALTH__ENCRYPTION_KEY 读取 */ }
    pub fn encrypt(&self, plaintext: &str) -> AppResult<String> { /* AES-256-GCM + Base64 */ }
    pub fn decrypt(&self, ciphertext: &str) -> AppResult<String> { /* 解密 */ }
}

集成点:

  • patient_service::create_patient — 加密 id_number 后存储
  • patient_service::update_patient — 同上
  • patient_service::get_patient — 解密后返回
  • patient_service::list_patients — 列表不返回 id_number脱敏

密钥管理: 环境变量 ERP__HEALTH__ENCRYPTION_KEY32 字节 hex必须在 default.toml 中标记为 __MUST_SET_VIA_ENV__

搜索兼容: patient.id_number 的模糊搜索(contains)改为精确匹配(eq),在加密后使用 HMAC 索引做等值查询。

HMAC 索引详情:

  • 新增数据库列 id_number_hash VARCHAR(64),存储 HMAC-SHA256 哈希
  • HMAC 密钥独立于 AES 密钥,从环境变量 ERP__HEALTH__HMAC_KEY 读取
  • 创建/更新患者时同时写入 hash 列,等值查询使用 WHERE id_number_hash = hmac(输入值)
  • 迁移 SQL新增列 → 批量加密现有明文 → 删除原明文列(可选)

数据迁移方案:

  1. 停机窗口(预估 1-2 小时,视数据量)
  2. 迁移脚本:SELECT id, id_number FROM patients WHERE id_number IS NOT NULL AND deleted_at IS NULL → 批量加密 → UPDATE patients SET id_number = $encrypted WHERE id = $id
  3. 同步写入 id_number_hash
  4. 验证脚本:抽样解密比对原值
  5. 回滚方案:保留明文备份表 patients_id_number_backup72 小时后确认无误再删除

问题: 列表接口直接返回完整身份证号、病史等敏感字段。

修复方案: 拆分响应 DTO。

// 列表用 — 不含敏感字段
pub struct PatientListResp {
    pub id: Uuid,
    pub name: String,
    pub gender: Option<String>,
    pub birth_date: Option<NaiveDate>,
    pub status: String,
    pub tags: Vec<TagResp>,
    // 无 id_number, allergy_history, medical_history_summary, emergency_contact_phone 等
}

// 详情用 — 敏感字段掩码
pub struct PatientDetailResp {
    // ... 全部字段
    pub id_number: Option<String>,          // "320***********1234"
    pub emergency_contact_phone: Option<String>, // "138****1234"
}

2. 后端补完(阶段 21.5 周)

2.1 事件处理器实现

问题: event.rs 中两个事件处理器只有 tracing::info,无实际业务逻辑。且 handler 中没有 DatabaseConnection,无法执行数据库操作。

方案: 在 HealthModule::on_startup 中创建 HealthState 并注册需要数据库访问的事件处理器。将现有 register_event_handlers 中的空壳代码迁移到 on_startupregister_event_handlers 改为空实现。

修改 crates/erp-health/src/module.rs:

// register_event_handlers 改为空实现
fn register_event_handlers(&self, _bus: &EventBus) {
    // 事件处理器迁移到 on_startup此处不再注册
}

// on_startup 中注册带 db 的事件处理器
async fn on_startup(&self, ctx: &ModuleContext) -> AppResult<()> {
    let state = HealthState {
        db: ctx.db.clone(),
        event_bus: ctx.event_bus.clone(),
    };
    crate::event::register_handlers_with_state(state);
    Ok(())
}

修改 crates/erp-health/src/event.rs:

新增 register_handlers_with_state(state: HealthState) 函数替代原有 register_handlers

事件处理器业务逻辑:

workflow.task.completed:

  1. 从 payload 中提取 task_id
  2. 查询 follow_up_task WHERE related_appointment_id 或通过 payload 映射
  3. 更新随访任务状态为 completed

message.sent:

  1. 从 payload 中提取 session_id(或通过 sender/recipient 关联)
  2. 更新 consultation_session SET last_message_at = NOW(), unread_count = unread_count + 1
  3. 使用 check_version 乐观锁

2.2 数据一致性修复

2.2.1 排班名额保护

问题: update_schedule 可以将 max_appointments 改为小于 current_appointments 的值。

修复: 在 appointment_service.rsupdate_schedule 方法中增加校验:

if req.max_appointments < model.current_appointments {
    return Err(HealthError::Validation(
        "max_appointments 不能小于当前已预约数".into()
    ).into());
}

2.2.2 取消预约名额释放

问题: update_appointment_status 中取消时名额释放失败只 log error 不回滚。

修复: 将名额释放作为事务的一部分,失败时回滚整个操作(包括状态更新)。

2.2.3 咨询消息原子性

问题: create_message 中消息已插入,但后续 CAS 更新 session 失败时返回错误 — 消息已持久化但 session 元数据未更新。

修复: 将消息 INSERT + session CAS 更新放在同一个事务中。

2.3 随访逾期定时任务

问题: 设计规格定义了 overdue 状态和定时任务自动标记,但代码中:

  • validation.rs 不允许转换到 overdue
  • 没有后台定时任务

修复:

  1. validation.rs 中添加 overdue 转换规则:pending -> overdue(仅限系统自动触发)
  2. erp-server/src/main.rs 后台任务区增加逾期检查器,使用与现有 start_timeout_checker 一致的 tokio::spawn + loop + tokio::time::interval 模式(每 6 小时执行一次,非 cron 表达式):
// erp-server/src/main.rs 后台任务区
tokio::spawn(async move {
    let mut interval = tokio::time::interval(Duration::from_secs(6 * 3600));
    loop {
        interval.tick().await;
        // 调用 health module 的 check_overdue_tasks
    }
});
  1. erp-health module 中添加一个公开方法 check_overdue_tasks 供定时任务调用。

2.4 article 管理 CRUD

问题: 权限声明中有 health.articles.manage,但 service/handler 只有 list 和 get。

修复: 在 article_service.rsarticle_handler.rs 中补充 create/update/delete 方法。在 module.rs 中添加路由。工时估算: 0.5 天。


3. Web 前端 10 页面(阶段 33.5-4 周)

3.1 页面文件组织

apps/web/src/
├── api/health/
│   ├── patients.ts        # 12 端点
│   ├── healthData.ts      # 13 端点
│   ├── appointments.ts    # 6 端点
│   ├── followUp.ts        # 6 端点
│   ├── consultations.ts   # 6 端点
│   └── doctors.ts         # 4 端点
├── pages/health/
│   ├── PatientList.tsx          # 患者列表
│   ├── PatientDetail.tsx        # 患者详情5 Tab
│   ├── PatientTagManage.tsx     # 标签管理
│   ├── DoctorList.tsx           # 医护列表
│   ├── AppointmentList.tsx      # 预约管理
│   ├── DoctorSchedule.tsx       # 排班管理
│   ├── FollowUpTaskList.tsx     # 随访任务
│   ├── FollowUpRecordList.tsx   # 随访台账
│   ├── ConsultationList.tsx     # 会话管理
│   ├── ConsultationDetail.tsx   # 对话详情
│   └── components/
│       ├── StatusTag.tsx         # 通用状态标签
│       ├── PatientSelect.tsx     # 患者搜索选择器
│       ├── DoctorSelect.tsx      # 医护选择器
│       ├── VitalSignsChart.tsx   # ECharts 趋势图
│       ├── CalendarView.tsx      # 日历视图
│       ├── ChatBubble.tsx        # 聊天气泡
│       ├── ImagePreview.tsx      # 图片预览
│       └── ExportButton.tsx      # 导出按钮

3.2 API 服务层设计

每个 service 文件遵循现有 api/users.ts 的解构模式:

// api/health/patients.ts
import client from '../client';

export interface Patient {
  id: string;
  name: string;
  gender?: string;
  birth_date?: string;
  status: string;
  tags: Tag[];
  // ...
}

export interface CreatePatientReq {
  name: string;
  gender?: string;
  // ...
}

export const patientApi = {
  list: async (params: ListParams) => {
    const { data } = await client.get<{ success: boolean; data: PaginatedResponse<Patient> }>(
      '/health/patients', { params }
    );
    return data.data;
  },
  get: async (id: string) => {
    const { data } = await client.get<{ success: boolean; data: Patient }>(
      `/health/patients/${id}`
    );
    return data.data;
  },
  create: async (req: CreatePatientReq) => {
    const { data } = await client.post<{ success: boolean; data: Patient }>(
      '/health/patients', req
    );
    return data.data;
  },
  // ...
};

3.3 路由注册

App.tsx 中新增:

// lazy imports
const PatientList = lazy(() => import('./pages/health/PatientList'));
const PatientDetail = lazy(() => import('./pages/health/PatientDetail'));
// ... 共 10 个路由组件

// Routes 内
<Route path="/health/patients" element={<PatientList />} />
<Route path="/health/patients/:id" element={<PatientDetail />} />
<Route path="/health/tags" element={<PatientTagManage />} />
<Route path="/health/doctors" element={<DoctorList />} />
<Route path="/health/appointments" element={<AppointmentList />} />
<Route path="/health/schedules" element={<DoctorSchedule />} />
<Route path="/health/follow-up-tasks" element={<FollowUpTaskList />} />
<Route path="/health/follow-up-records" element={<FollowUpRecordList />} />
<Route path="/health/consultations" element={<ConsultationList />} />
<Route path="/health/consultations/:id" element={<ConsultationDetail />} />

3.4 侧边栏菜单

MainLayout.tsx 中新增 healthMenuItems 数组(参照现有 bizMenuItems 模式),使用 @ant-design/icons 图标(如 MedicineBoxOutlinedHeartOutlinedCalendarOutlinedPhoneOutlinedCommentOutlinedTagsOutlined

侧边栏布局:
├── 首页 (HomeOutlined)
├── 用户管理 (UserOutlined)
├── 权限管理 (SafetyOutlined)
├── 工作流 (ApartmentOutlined)
├── 消息中心 (BellOutlined)
├── ─────────
├── 健康管理 (MedicineBoxOutlined) ← 新增组
│   ├── 患者管理 (TeamOutlined)
│   ├── 医护管理 (HeartOutlined)
│   ├── 预约排班 (CalendarOutlined)
│   ├── 随访管理 (PhoneOutlined)
│   ├── 咨询管理 (CommentOutlined)
│   └── 标签管理 (TagsOutlined)
├── ─────────
├── 插件管理 (AppstoreOutlined)
├── 系统设置 (SettingOutlined)

3.5 前端权限集成

后端已有完整权限体系14 个权限码),前端 V1 阶段采用以下策略:

  1. 路由级权限: 所有健康模块路由在 PrivateRoute 内(已实现),后端 require_permission 拦截无权限请求返回 403
  2. 按钮级权限V1 简化): 不做前端按钮级权限控制,依赖后端 403 响应。后续可扩展 usePermission hook
  3. 菜单可见性: 健康模块菜单组始终显示,但无权限用户点击任何页面会收到 403 提示

3.5 13 页面逐一设计

PatientList.tsx中复杂度1.5 天)

  • Ant Design Table 组件(与 Users.tsx 模式一致,不使用 ProTable
  • 搜索:姓名模糊 + 状态筛选 + 标签多选筛选
  • 每行显示患者标签为 Tag 组件列表
  • 行点击跳转 /health/patients/:id
  • 批量操作:批量打标
  • 导出功能

PatientDetail.tsx高复杂度3 天)

  • 顶部:患者摘要卡片(姓名/性别/年龄/状态/标签)
  • Ant Design Tabs 5 个 Tab
    1. 基本信息Descriptions 展示 + 编辑 Modal
    2. 健康趋势VitalSignsChart 组件 + 时间范围选择器
    3. 化验报告 — 报告卡片列表 + ImagePreview 指标详情
    4. 就诊记录 — 嵌套列表(体检/门诊/住院)
    5. 随访记录 — 嵌套列表 + 关联的随访记录

PatientTagManage.tsx低复杂度0.5 天)

  • 标准 CRUD 表格
  • 颜色选择器Ant Design ColorPicker
  • 批量打标功能

DoctorList.tsx低复杂度0.5 天)

  • 标准 CRUD 表格
  • 科室筛选 + 在线状态 Badgeonline=绿/busy=黄/offline=灰)
  • 详情 Drawer

AppointmentList.tsx中复杂度2 天)

  • Segmented 切换列表/日历视图
  • 列表模式:表格 + 状态筛选 + 日期筛选
  • 日历模式:Calendar + cellRender 显示当日预约数
  • 状态流转 Dropdownpending → confirmed → completed/no_show/cancelled
  • 创建预约 Modal选择患者 + 医生 + 日期时段 + 检查排班余量)

DoctorSchedule.tsx高复杂度2.5 天)

  • 选择医生后展示其排班
  • 周视图(自定义 7 列网格,每列显示一天的排班时段)
  • 月视图Ant Design Calendar
  • 批量创建排班(选择日期范围 + 时段模板)
  • 显示已预约/最大预约数

FollowUpTaskList.tsx中复杂度1.5 天)

  • 表格 + 状态筛选pending/in_progress/completed/overdue/cancelled
  • 分配给医护(DoctorSelect
  • 创建任务 Modal
  • 快捷"填写随访记录"按钮打开子 Modal

FollowUpRecordList.tsx低复杂度0.5 天)

  • 纯只读台账
  • 筛选:日期范围、患者、任务、结果
  • 导出功能(ExportButton

ConsultationList.tsx中复杂度1 天)

  • 表格 + 状态筛选waiting/active/closed
  • 未读消息数 Badge
  • 最后消息时间
  • 关闭会话操作
  • 点击跳转 /health/consultations/:id

ConsultationDetail.tsx高复杂度2 天)

  • ChatBubble 组件渲染聊天气泡
  • 根据 sender_role 区分左右对齐
  • 支持内容类型text / imageImagePreview/ voice / file
  • 消息按时间排列,支持滚动加载更多(分页)
  • 导出按钮

3.6 技术难点方案

ECharts 趋势图

使用已安装的 @ant-design/chartsLine 组件。

  • 后端 API /patients/:id/trends/:indicator 返回时序数据
  • 前端转换为 { date: string, value: number }[]
  • 支持多指标叠加(血压收缩压/舒张压双线)
  • 封装为 VitalSignsChart,接收 patientId + indicators 参数
  • 时间范围选择器7天/30天/90天

日历视图

Ant Design Calendar + 自定义 cellRender

  • DoctorSchedule每个日期格显示排班时段标签
  • AppointmentList每个日期格显示预约数量气泡

聊天 UI

自定义 ChatBubble 组件,基于 Ant Design Typography.Paragraph + Avatar

  • 根据 sender_role 区分样式
  • 只读模式PC 后台只查看不发送)
  • 图片消息使用 Image.PreviewGroup

导出

后端 blob 导出 + 前端触发下载,参照 PluginCRUDPage 中已有的 exportPluginDataAsBlob 模式。

文件上传/预览

  • 上传Ant Design Upload.Dragger,上传到后端文件接口
  • 图片预览Ant Design Image.PreviewGroup
  • PDF 预览新窗口打开V1 简化方案)

3.7 开发顺序

Phase 内容 天数 依赖
1 API 层 6 文件 + 通用组件 + 路由菜单 1.5
2 PatientList + PatientTagManage + PatientDetail 基本信息Tab 2 Phase 1
3 VitalSignsChart + 健康趋势 Tab + LabReportList + HealthRecordList 3 Phase 2
4 DoctorList + AppointmentList + DoctorSchedule 3 Phase 1
5 FollowUpTaskList + FollowUpRecordList + ConsultationList + ConsultationDetail 3 Phase 1
6 打磨(暗色主题 + 响应式 + 联调) 1 Phase 2-5
合计 13.5 天

4. 测试策略(阶段 2-3 交叉进行)

4.1 优先级排序

优先级 测试目标 预估用例数 工作量
P0 validation.rs 纯函数 20-30 1 天
P0 appointment_service CAS + 状态流转 15-20 2 天
P0 patient_service CRUD + 状态机 15-20 2 天
P1 consultation_service 消息原子性 10-15 2 天
P1 health_data_service 指标数据 10-15 1 天
P2 follow_up_service 链式任务 10 1 天

4.2 测试基础设施

erp-health/Cargo.toml 中添加 [dev-dependencies]

  • tokiotestmacros feature
  • sea-ormmock feature用于简单单元测试如 validation 纯函数)

对于涉及事务和 CAS 的集成测试(预约并发、消息原子性),使用 testcontainers-postgreSQL 做真实数据库测试,因为 SeaORM 的 MockDatabaseConnection 不支持复杂事务模拟。

创建 tests/test_helpers.rs 提供:

  • create_test_health_state() — 带 mock db 的 HealthState单元测试用
  • create_integration_db() — testcontainers PostgreSQL 实例(集成测试用)
  • 共享 fixture 工厂

4.3 关键测试场景

预约 CAS 并发:

  • 排班已满 → 创建预约失败
  • 排班有余 → CAS 成功 + 名额减 1
  • 并发创建 → 只有 max_appointments 个成功

状态机转换:

  • 合法转换pending → confirmed → completed
  • 非法转换completed → pending → 拒绝
  • 取消:任意状态 → cancelled填 cancel_reason

随访链式任务:

  • next_follow_up_date 不为空 → 自动创建新任务
  • 新任务的 assigned_to 沿用当前医护
  • next_follow_up_date 为空 → 不创建新任务

5. 实施路线图

5.1 总时间线(调整为 7 周)

Week 1-2   | 安全地基1.5-2 周)
           | ├── sanitize 全覆盖2 天)
           | ├── 审计日志注入2 天)
           | ├── 身份证号加密 + HMAC 索引 + 数据迁移3-4 天)
           | └── 字段级脱敏1-2 天)

Week 2-4   | 后端补完 + 测试1.5-2 周)
           | ├── 事件处理器实现2 天)
           | ├── 数据一致性修复2 天)
           | ├── 随访逾期定时任务1 天)
           | ├── article CRUD0.5 天)
           | └── 核心路径测试5-6 天)

Week 4-7   | Web 前端3.5-4 周)
           | ├── Phase 1: API 层 + 通用组件 + 路由菜单1.5 天)
           | ├── Phase 2: 核心入口页面2 天)
           | ├── Phase 3: 健康数据页面3 天)
           | ├── Phase 4: 预约排班页面3 天)
           | ├── Phase 5: 随访咨询页面3 天)
           | └── Phase 6: 打磨联调1 天)

Week 7-8   | 端到端验证1 周)
           | ├── 小程序联调
           | ├── 种子数据填充
           | ├── Docker 演示环境
           | └── 文档更新

5.2 里程碑

里程碑 交付物 验收标准
M1 安全省基完成 sanitize + 审计 + 加密 + 脱敏全部到位cargo test 通过
M2 后端功能完整 事件处理器 + 数据一致性 + 测试覆盖cargo test 通过
M3 Web 3 核心页面 PatientList + AppointmentList + DoctorSchedule 可操作
M4 Web 10 页面完成 所有页面功能可用pnpm build 通过
M5 端到端验证 Web + 小程序 + 后端全链路可演示

5.3 风险和缓解

风险 概率 缓解
ECharts 集成复杂度高 使用 @ant-design/charts 已安装,降低自研成本
身份证加密影响现有查询 HMAC 索引 + 数据迁移脚本 + 备份表 + 回滚方案
10 页面开发时间超预期 按优先级裁剪MVP 先做 3 核心页面
文件上传能力未就绪 V1 先支持 URL 存储,文件上传推迟到 V1.1

6. 不在本设计范围内(推迟到 V2

  • 积分商城
  • 数据统计中心 / 运营驾驶舱
  • AI 辅助诊断/报告解读
  • 实时 WebSocket 在线咨询
  • 咨询消息按月分区
  • 事件幂等性processed_events 去重表)
  • Polling Outbox 重试机制
  • HealthState 扩展 Redis 缓存
  • 国际化(英文等多语言)
  • 小程序医护端