diff --git a/docs/superpowers/specs/2026-04-30-elderly-friendly-miniprogram-design.md b/docs/superpowers/specs/2026-04-30-elderly-friendly-miniprogram-design.md new file mode 100644 index 0000000..1f6acfe --- /dev/null +++ b/docs/superpowers/specs/2026-04-30-elderly-friendly-miniprogram-design.md @@ -0,0 +1,213 @@ +# HMS 小程序患者端 — 老年友好版本设计规格 + +> 日期: 2026-04-30 | 状态: 待实施 | 选定方案: A(健康 Tab 导航) + +## 1. 背景与目标 + +患者端小程序面向 60+ 慢病患者群体,当前版本信息密度高、字号小、导航层级深(5 Tab + 40 页),老年用户难以独立使用。基于多专家组(UX 无障碍、老年医学、产品策略)头脑风暴,选定方案 A 进行全面重设计。 + +**核心目标:** +- 老年患者能独立完成日常操作(记录体征、查看数据、预约挂号、服药提醒) +- 每个核心操作路径不超过 3 步 +- 满足 WCAG 2.1 AA 对比度要求 + +## 2. 导航结构 + +**4 Tab:首页 / 健康 / 消息 / 我的** + +| Tab | 名称 | 核心内容 | 页面文件 | +|-----|------|---------|---------| +| 1 | 首页 | 今日打卡 + 体征 2x2 + 快捷操作 | `pages/index/index.tsx` | +| 2 | 健康 | 体征录入 + 趋势图 + BLE 设备 | `pages/health/index.tsx`(重写) | +| 3 | 消息 | 咨询对话 + 系统通知 | `pages/messages/index.tsx`(新建) | +| 4 | 我的 | 个人信息 + 积分 + 菜单 | `pages/profile/index.tsx`(调整) | + +**Tab 迁移映射:** + +| 当前 Tab | 操作 | 去向 | +|----------|------|------| +| 首页 (index) | 保留,重写 | — | +| 上报 (health) | 重命名为"健康",重写 | 合并录入功能 | +| 咨询 (consultation) | 从 Tab 移除 | 合并到"消息"Tab 的"咨询"子 Tab | +| 商城 (mall) | 从 Tab 移除 | 降级为"我的"页菜单项,`pkg-mall` 子包保留 | +| 我的 (profile) | 保留,调整间距 | — | +| (新增) 消息 | 新建 Tab + 页面 | `pages/messages/index.tsx` | + +**说明:** `pages/consultation/` 目录及 `pages/consultation/detail/` 保留为路由页面(非 Tab),从消息页的"咨询"子 Tab 导航进入。`pkg-mall` 子包(兑换/订单/详情)全部保留,从"我的"页菜单入口进入。 + +## 3. 首页设计 + +5 个区域,从上到下: + +1. **问候 + 日期** — "上午好,张大爷" 24px 粗体 + 日期 + 消息红点 +2. **今日体征完成度** — 圆形进度环(72px)+ 完成百分比 + 4 指标胶囊(血压/心率/血糖/体重) + - **计算逻辑(纯前端):** 查询当日体征数据 API,4 个指标(血压/心率/血糖/体重)中当天有记录的计为完成,进度 = 已完成数 / 4 + - **与积分签到的区别:** 这是"今日体征记录完成度"(纯展示),不是积分商城的"每日签到"(`usePointsStore.checkinStatus`)。两者独立,不耦合。 + - **无需新后端 API:** 复用现有 `getHealthSummary` 或当日体征查询接口,前端计算百分比。 +3. **今日体征 2x2** — 白色卡片网格,数值 32px 衬线体,状态标签(正常/偏高/未记录) +4. **今日待办(≤3条)** — 预约/随访/异常提醒,带图标和副标题 +5. **快捷操作** — 两个大按钮:"记录体征"(实色)+ "预约挂号"(描边) + +**首页不放置的内容(从当前首页移除):** +- 设备快捷入口(血压计/血糖仪卡片)→ 移至"健康"Tab +- 6 项快捷服务网格(预约挂号/健康录入/趋势/告警/资讯/AI)→ 精简为 2 个核心按钮 +- 健康资讯文章列表 → 入口移至"健康"Tab 底部或"我的"页 + +## 4. 健康页设计 + +**功能定位:** 体征数据录入 + 趋势查看 + 设备管理 + +### 4.1 录入区 +- 顶部类型 Tab:血压 / 心率 / 血糖 / 体重(56px 高大 Tab,选中项 $pri 实色底白字) +- 血压:收缩压 + 舒张压两个大输入框(56px 高,数值 28px 衬线体),上下排列(非左右,防止老年用户填反) +- 心率/体重:单个大输入框 +- 血糖:数值 + 时段选择(空腹/餐后 2h,大按钮组) +- 每个输入框下方显示参考范围和实时状态(正常/偏高/偏低) +- 保存按钮:全宽 56px 高 $pri 实色 +- 异常值保存前弹出确认:"血压偏高 (160/95),确认提交?" + +### 4.2 趋势图 +- 复用现有 `getTrend` API +- 7 天柱状图,异常值用 $wrn 色,正常用 $pri 色 +- 底部显示周一到周日标签 + +### 4.3 BLE 设备 +- 从首页移入此区域 +- 连接状态卡片:设备图标 + 名称 + 连接状态 +- 一键同步按钮 +- 设备管理入口(跳转 `pkg-device-sync`) + +### 4.4 健康资讯入口 +- 从首页移入,放在页面底部 +- 单行卡片:"最新健康资讯 ›" 点击跳转文章列表 + +## 5. 消息页设计 + +**新页面,合并咨询 + 系统通知。** + +- 顶部 Tab 切换:"咨询" | "通知"(56px 大 Tab) +- 咨询列表:对话卡片 80px 高,文字 28px,未读红点 24px +- 通知列表:随访提醒、预约确认、异常告警、用药提醒 + +## 6. 我的页设计 + +保留现有结构,调整参数: +- 菜单项高度 48px → 64px +- 文字 28px +- 图标 40px +- 积分 + 连续打卡横排展示 + +## 7. 设计系统调整 + +### 7.1 字号 + +> **单位说明:** 代码库统一使用 `px`(Taro 的 `pxtransform` 编译时自动转为 rpx)。以下所有值均为 `px`。 + +| 用途 | 当前(px) | 调整为(px) | +|------|----------|-----------| +| 问候/标题 | 26 | 28 | +| 区块标题 | 24 | 26 | +| 正文 | 22 | 24 | +| 辅助文字 | 20 | 22 | +| 体征数值 | 44 | 48-64 | +| 按钮文字 | 24 | 28 | +| **最小字号** | 20 | **22** | + +### 7.2 间距与触控 + +| 参数 | 当前 | 调整为 | +|------|------|--------| +| 按钮最小高度 | — | 48px(120rpx) | +| 主按钮高度 | — | 56px(140rpx) | +| 菜单项高度 | 48px | 64px(160rpx) | +| 卡片圆角 | 12px | 16px | +| 卡片内间距 | 24-28px | 28-32px | +| 列表项间距 | 16px | 20px | + +### 7.3 色彩 + +> **对比度基于 `$bg` #F5F0EB 计算。** WCAG 2.1 AA 要求:正文(<24px)≥ 4.5:1,大字(≥24px)≥ 3:1。 + +| 变量 | 当前 | 调整为 | 对比度 | WCAG | +|------|------|--------|--------|------| +| `$tx2` | #7A756E | #5A554F | ~5.5:1 | AA 正文 | +| `$tx3` | #A8A29E | #78716C | ~4.6:1 | AA 正文 | +| `$pri` | #C4623A | **不变** | — | — | +| `$bg` | #F5F0EB | **不变** | — | — | + +**使用约束:** `$tx3` 仅用于 24px 及以上的文字或装饰性标签。正文一律使用 `$tx2` 或 `$tx`。 + +## 8. 功能优先级 + +| 优先级 | 功能 | 状态 | +|--------|------|------| +| P0 | 体征数据查看(2x2 卡片) | 重写首页 | +| P0 | 体征录入(大输入框) | 重写健康页 | +| P0 | 今日体征完成度(进度环) | 新增(纯前端,复用现有 API) | +| P1 | 今日待办(≤3 条) | 新增 | +| P1 | 消息中心(咨询+通知) | 新建页面 | +| P1 | BLE 设备同步 | 保留调整 | +| P2 | 趋势图简化 | 调整 | +| P2 | 用药提醒 | 保留 | +| P3 | 积分商城 | 降级为菜单项 | + +## 9. 页面路由变更 + +### TabBar 配置(app.config.ts) + +``` +5 Tab → 4 Tab +删除:上报(health)、商城(mall) +新增:消息(messages) +保留:首页(index)、健康(health → 重写)、我的(profile) +``` + +### 页面增减 + +| 操作 | 页面 | +|------|------| +| 新建 | `pages/messages/index.tsx` | +| 重写 | `pages/index/index.tsx`、`pages/health/index.tsx` | +| 调整 | `pages/profile/index.tsx` | +| 删除 Tab | `pages/mall/index.tsx`(降级为子页面) | +| 保留 | 所有 `pkg-*` 子包页面 | + +## 10. 关键文件 + +| 文件 | 改动类型 | +|------|---------| +| `apps/miniprogram/src/app.config.ts` | TabBar 5→4,路由调整 | +| `apps/miniprogram/src/styles/variables.scss` | 字号/色值/间距调整 | +| `apps/miniprogram/src/styles/mixins.scss` | 新增老年友好 mixin | +| `apps/miniprogram/src/pages/index/index.tsx` | 首页全面重写 | +| `apps/miniprogram/src/pages/index/index.scss` | 首页样式全面重写 | +| `apps/miniprogram/src/pages/health/index.tsx` | 健康页重写 | +| `apps/miniprogram/src/pages/health/index.scss` | 健康页样式重写 | +| `apps/miniprogram/src/pages/messages/index.tsx` | **新建**消息页 | +| `apps/miniprogram/src/pages/messages/index.scss` | **新建**消息页样式 | +| `apps/miniprogram/src/pages/profile/index.tsx` | 调整菜单和间距 | +| `apps/miniprogram/src/services/consultation.ts` | 消息页复用咨询列表 API | +| `apps/miniprogram/src/services/health.ts` | 复用体征数据查询(打卡进度) | +| `apps/miniprogram/src/stores/points.ts` | 签到/积分功能保留,不改动 | +| `apps/miniprogram/src/components/` | 新增 ProgressRing 组件 | + +## 11. 验证方式 + +1. `pnpm build` — 小程序编译通过 +2. 微信开发者工具预览 — 布局和字号正确 +3. 真机测试 — 在 60+ 患者手机上验证可读性和触控体验 +4. WCAG 2.1 AA — 色值对比度合规(≥4.5:1 正文,≥3:1 大字),使用 axe-core 或微信开发者工具无障碍审计 +5. 核心路径验证(每条 ≤ 3 步): + - 记录血压:首页 → 点击"记录体征" → 输入 → 保存(3 步) + - 查看趋势:健康 Tab → 查看趋势图(1 步) + - 预约挂号:首页 → 点击"预约挂号" → 选择时间 → 确认(3 步) + - 查看消息:消息 Tab → 点击对话(2 步) +6. 不同屏幕尺寸测试:特别是老年用户常用的小屏 Android 设备 +7. 字体回退验证:衬线体在低端 Android 设备上的回退(使用 `Georgia, "PingFang SC", serif`) + +## 12. 迁移与发布策略 + +- 一次性全量发布(非 A/B 测试),因为 5 Tab → 4 Tab 是结构性变更,无法共存 +- 旧路径 `pages/consultation/`、`pages/mall/` 保留为路由页面,但不再从 TabBar 进入 +- 小程序分享卡片可能指向旧路径——这些页面保留可访问,不受影响 +- 发布前在测试环境完整走一遍所有核心路径 diff --git a/docs/superpowers/specs/2026-05-01-action-inbox-design.md b/docs/superpowers/specs/2026-05-01-action-inbox-design.md new file mode 100644 index 0000000..589f682 --- /dev/null +++ b/docs/superpowers/specs/2026-05-01-action-inbox-design.md @@ -0,0 +1,401 @@ +# 行动收件箱与上下文线程设计规格 + +> 日期: 2026-05-01 | 状态: Draft | 关联: `erp-health`, `erp-ai`, Web 前端, 小程序 + +## 1. 背景与问题 + +### 1.1 问题陈述 + +HMS 平台功能完善(328 路由、25 事件类型、97.5% 测试通过率),但用户体验存在**系统性流程断裂**: + +- **告警 → 行动断裂**:医生看到告警后,无直接路径跳到查看患者→安排随访→记录处置 +- **数据录入 → 反馈断裂**:患者录入体征后无即时趋势对比和行动建议 +- **AI 建议 → 执行追踪断裂**:审批通过后,后续执行/追踪/结果反馈在 UI 上不可见 + +**根因**:前端交互模型是"功能陈列式"的——每个功能是死胡同,没有出口指向下一步。 + +### 1.2 设计目标 + +建立统一的**行动收件箱 + 上下文线程**机制,让每个"终点"变成"起点": + +1. 用户登录后第一眼看到待处理事项(按优先级排列) +2. 每个待办有清晰的时间线展示完整生命周期 +3. 时间线每一步都可直接跳转到对应功能页 +4. 操作按钮根据当前状态动态变化 +5. 架构可扩展——新增行动类型(告警/随访/异常)只需注册聚合器 + +### 1.3 范围 + +**Phase 1(本次)**:AI 建议闭环 — 后端已完整实现(10 次提交),补齐前端体验 +**Future**:告警处理闭环、体征反馈闭环、随访到期提醒 + +--- + +## 2. 设计概览 + +``` +事件源 → 行动收件箱服务 → 前端展示 + ├── Web: 顶栏铃铛 → 列表页 → Drawer + └── 小程序: "待办" Tab → 列表 → 半屏弹窗 +``` + +**核心抽象**:`ActionItem` — 统一的待办数据结构,不同类型共用相同接口 +**核心交互**:`Context Thread` — Drawer/半屏弹窗中的时间线,展示完整处理进度 + +--- + +## 3. 数据模型 + +### 3.1 ActionItem(聚合视图,不建新表) + +```typescript +interface ActionItem { + id: string; // 格式: "{type}:{source_id}" + type: "ai_suggestion" | "alert" | "followup" | "data_anomaly"; + priority: "urgent" | "high" | "medium" | "low"; + status: "pending" | "in_progress" | "completed" | "dismissed"; + title: string; + summary: string; + patient_id: string; + patient_name: string; + source_ref: string; // 指向原始实体 ID + created_at: string; + updated_at: string; +} +``` + +### 3.2 ThreadEvent(上下文线程事件) + +```typescript +interface ThreadEvent { + step: string; // "ai_analysis" | "doctor_approval" | "followup_scheduled" | "followup_completed" | "reanalysis" + label: string; // 展示文本 + status: "completed" | "in_progress" | "pending"; + detail?: string; // 补充说明 + timestamp?: string; // 完成时间 + operator?: string; // 操作人 + link_to?: string; // 跳转路径 +} +``` + +### 3.3 ActionDefinition(可用操作) + +```typescript +interface ActionDefinition { + key: string; // "approve" | "reject" | "complete_early" | "acknowledge" + label: string; // 按钮文本 + variant: "primary" | "danger" | "default"; + confirm_message?: string; // 确认弹窗文案 + api_endpoint: string; // 调用的 API +} +``` + +--- + +## 4. 后端 API 设计 + +### 4.1 GET /api/v1/action-inbox + +获取当前用户的行动收件箱列表。 + +**Query Params**: +- `status`: `pending` | `in_progress` | `completed` | `dismissed`(可选,默认全部) +- `type`: `ai_suggestion` | `alert` | `followup` | `data_anomaly`(可选,默认全部) +- `page` / `page_size`: 分页(默认 1/20) + +**Response**: `PaginatedResponse` + +**实现策略**:聚合查询,不建新表。Phase 1 只查 `ai_suggestion` 表: + +```sql +-- 注意:patient_id 在 ai_analysis 表上,不在 ai_suggestion 上,需三表 JOIN +SELECT s.id, s.suggestion_type, s.risk_level, s.status, s.params, + s.created_at, s.updated_at, + p.name as patient_name, a.patient_id, + a.result_content, a.analysis_type +FROM ai_suggestion s +JOIN ai_analysis a ON s.analysis_id = a.id +JOIN patient p ON a.patient_id = p.id +WHERE s.tenant_id = $1 + AND s.deleted_at IS NULL +ORDER BY + CASE s.risk_level WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END, + s.created_at DESC +``` + +### 4.2 GET /api/v1/action-inbox/:source_ref/thread + +获取某个行动项的上下文线程。 + +**Response**: + +```typescript +interface ThreadResponse { + action_item: ActionItem; + thread: ThreadEvent[]; + available_actions: ActionDefinition[]; +} +``` + +**线程拼装逻辑**(以 AI 建议为例): + +| 步骤 | 数据来源 | 状态判断 | +|------|---------|---------| +| AI 分析完成 | `ai_analysis.status = 'completed'` | 始终 completed | +| 医生审批 | `ai_suggestion.status` | Approved→completed, Pending→in_progress | +| 执行安排 | `ai_suggestion.workflow_instance_id IS NOT NULL` | 有值→completed | +| 等待随访 | `follow_up` 关联查询 | 根据随访状态判断 | +| 再分析对比 | `ai_suggestion.reanalysis_id IS NOT NULL` | 有值→completed | + +**操作按钮动态生成**: + +| 建议状态 | 可用操作 | +|---------|---------| +| Pending | 批准(→ `POST /ai/suggestions/:id/approve`)、拒绝 | +| Approved/执行中 | 提前完成随访、标记已知悉 | +| Executed | 查看前后对比报告 | +| Expired/Rejected/ParseFailed | 无操作(只读展示) | +| Executed | 查看前后对比报告 | +| Expired | 无操作 | + +### 4.3 代码归属与权限码 + +**代码位置**: +- `ActionInboxService` → `crates/erp-health/src/service/action_inbox_service.rs`(erp-health crate) +- `ActionInboxHandler` → `crates/erp-health/src/handler/action_inbox_handler.rs` +- 路由注册:在 `erp-health` 模块的 `protected_routes()` 中新增 +- 跨 crate 查询:通过 raw SQL 直接查 `ai_suggestion` + `ai_analysis` 表(与现有 `ai_suggestion_loader.rs` 模式一致,erp-health 已有跨 crate 读 ai 表的先例) + +**权限码**:新增独立权限码,注册在 `erp-health` 模块的 `permissions()` 中: +- `health.action_inbox.list` — 查看行动收件箱 +- `health.action_inbox.manage` — 执行操作(审批/拒绝/标记已知悉) + +理由:`action-inbox` 是聚合端点,未来包含告警/随访/异常等多种类型,不应绑定到 `ai.suggestion.*` 权限。用 `health.` 前缀与 erp-health crate 归属一致。 + +**用户范围**: +- Phase 1(AI 建议):返回当前租户下所有有权限查看的待办(`ai_suggestion` 无 `assigned_to` 字段) +- Future(告警/随访):告警有 `assigned_to` 关联的负责医生,随访有 `assigned_to` 字段,届时 API 增加 `scope` 参数(`all` | `mine`) + +--- + +## 5. Web 前端设计 + +### 5.1 入口改造:NotificationPanel 扩展 + +**现有**:`apps/web/src/components/NotificationPanel.tsx`(铃铛 + Popover,SSE 推送消息/告警) + +**改造**: +- Popover 中增加"待办"区域,显示最近 3 条 ActionItem +- 底部增加"查看全部待办"链接,跳转到 `/action-inbox` 页面 +- 角标数字改为:未读消息数 + 待处理 ActionItem 数 + +### 5.2 新增页面:ActionInboxPage + +**路由**:`/action-inbox` + +**组件**:`apps/web/src/pages/ActionInboxPage.tsx` + +**布局**: +- 顶部:筛选栏(状态 Tab + 类型下拉) +- 主体:Ant Design List 组件,每项显示: + - 类型标签(颜色区分) + - 标题 + 患者姓名 + - 优先级标记 + - 创建时间 + - 点击 → 打开 Drawer + +### 5.3 新增组件:ActionThreadDrawer + +**组件**:`apps/web/src/components/ActionThreadDrawer.tsx` + +**实现**:Ant Design Drawer + Ant Design Steps/Timeline + +**结构**: +``` +Drawer (width 480px) +├── 头部:标题 + 患者摘要卡(年龄/最近体征/关键指标) +├── 时间线:Ant Design Timeline 组件 +│ ├── 每个节点:状态图标 + 文本 + 时间 + 跳转链接 +│ └── 当前步骤高亮,未来步骤灰色 +├── 关联信息:可点击跳转的快捷链接 +└── 操作区:动态按钮组 +``` + +**状态设计**: +- **加载中**:Ant Design Skeleton(3 行卡片骨架屏) +- **数据获取失败**:Ant Design Result + 重试按钮 +- **空列表**:插图 + "暂无待办事项"文案 +- **操作按钮请求中**:按钮 loading 状态 + 禁用 + +### 5.4 实时更新策略 + +**Phase 1(轮询)**:用户手动刷新或切换页面时重新拉取列表。Drawer 打开时,操作按钮点击后主动调用 `GET /action-inbox/:id/thread` 刷新时间线。 + +**Phase 2(SSE 优化)**:复用 `apps/web/src/stores/message.ts` 中的 SSE 连接。后端在建议状态变更时,通过事件总线发布 → 消息服务推送到 SSE 流。前端监听 `suggestion.status_changed` 事件,触发列表/Drawer 刷新。 + +Phase 1 选择轮询的理由:现有 SSE 只推送 `message` 和 `alert` 两种类型,新增 `suggestion` 事件需要后端消息服务改造,且 Phase 1 的使用场景(医生不频繁审批)对实时性要求不高。 + +--- + +## 6. 小程序设计 + +### 6.1 TabBar 改造 + +**当前**:首页 | 健康 | 消息 | 我的(4 Tab) +**新**:首页 | 健康 | 待办 | 消息 | 我的(5 Tab) + +- 在"健康"和"消息"之间插入"待办"Tab +- Tab 图标:`todo-list` 或 `check-circle` +- 角标显示待处理数量(调用 `GET /action-inbox?status=pending` 获取计数) +- 修改文件:`apps/miniprogram/src/app.config.ts` + +### 6.2 新增页面:待办列表 + +**路径**:`apps/miniprogram/src/pages/action-inbox/index.tsx` + +**布局**: +- 顶部:状态筛选 Tab(全部 | 待处理 | 进行中 | 已完成) +- 列表:按优先级排序的待办卡片 +- 每张卡片:类型标签 + 标题 + 患者名 + 时间 + 箭头 +- 点击卡片 → 弹出半屏弹窗 + +### 6.3 新增组件:ActionHalfScreenDialog + +使用 Taro `half-screen-dialog` 组件或自定义半屏弹窗: + +``` +半屏弹窗 +├── 头部:拖拽条 + 类型标签 + 标题 +├── 摘要:关键信息(患者/风险等级/时间) +├── 时间线:简化版(只显示已完成和当前步骤) +├── 操作按钮 +└── 底部:跳转详情链接 +``` + +### 6.4 Service 层 + +**文件**:`apps/miniprogram/src/services/action-inbox.ts` + +```typescript +// 复用现有 listPendingSuggestions() +// 新增: +listActionItems(params: { status?: string; type?: string; page: number }) + → GET /api/v1/action-inbox + +getActionThread(sourceRef: string) + → GET /api/v1/action-inbox/:source_ref/thread +``` + +--- + +## 7. 数据映射详细规则 + +### 7.1 AI 建议 → ActionItem 映射 + +| ActionItem 字段 | 映射来源 | 规则 | +|----------------|---------|------| +| id | `"ai_suggestion:" + ai_suggestion.id` | 类型前缀 + 原 ID | +| type | 固定 `"ai_suggestion"` | — | +| priority | `ai_suggestion.risk_level` | High→urgent, Medium→high, Low→medium | +| status | `ai_suggestion.status` | Pending→pending, Approved→in_progress, Executed→completed, Expired/Rejected/ParseFailed→dismissed | +| title | `suggestion_type` + `risk_level` 拼接模板 | 如 `suggestion_type=Followup` → "建议安排随访";`suggestion_type=Alert` → "建议关注指标异常";从 `params` JSON 提取 `reason` 字段作为补充(若存在) | +| summary | `ai_analysis.result_content` | 截取前 100 字符 | +| patient_id | `ai_analysis.patient_id`(通过 `analysis_id` JOIN) | 三表 JOIN | +| patient_name | `patient.name` | JOIN 查询 | +| source_ref | `ai_suggestion.id` | 原始 ID | + +### 7.2 线程步骤映射 + +| ThreadEvent.step | 触发条件 | 数据来源 | +|-----------------|---------|---------| +| `ai_analysis` | 始终存在 | `ai_analysis` 表 | +| `doctor_approval` | `suggestion.status ∈ {Approved, Rejected}` | `suggestion.updated_at` 作为近似审批时间(无专用 `approved_at` 字段) | +| `followup_scheduled` | `workflow_instance_id IS NOT NULL` | `workflow_instance` 表。注意:`action_dispatched` 步骤已合并到此处——`ai_action_dispatcher` 是事件消费者,不回写时间戳到 `ai_suggestion`,其效果体现为 `workflow_instance_id` 被设置 | +| `followup_completed` | `follow_up.status = completed` | `follow_up` 表 | +| `reanalysis` | `reanalysis_id IS NOT NULL` | `ai_analysis` (reanalysis) 表 | + +--- + +## 8. 实施分阶段 + +### Phase 1A:后端 API(2-3 天) + +- [ ] 创建 `ActionInboxService` → `crates/erp-health/src/service/action_inbox_service.rs` +- [ ] 创建 `ActionInboxHandler` → `crates/erp-health/src/handler/action_inbox_handler.rs` +- [ ] 在 erp-health `protected_routes()` 中注册 2 个新路由 +- [ ] 实现三表 JOIN 聚合查询(`ai_suggestion` + `ai_analysis` + `patient`) +- [ ] 实现线程拼装逻辑(合并 `action_dispatched` 到 `followup_scheduled` 步骤) +- [ ] 实现操作按钮动态生成 +- [ ] 在 erp-health `permissions()` 中注册 `health.action_inbox.list` / `health.action_inbox.manage` + +### Phase 1B:Web 前端(2-3 天) + +- [ ] 扩展 `NotificationPanel`,增加待办区域 +- [ ] 创建 `ActionInboxPage` 路由页面 +- [ ] 创建 `ActionThreadDrawer` 组件 +- [ ] SSE 实时更新集成 +- [ ] 路由注册 + 菜单入口 + +### Phase 1C:小程序(2-3 天) + +- [ ] TabBar 改造(4 Tab → 5 Tab,在"健康"和"消息"间插入"待办") +- [ ] 创建待办列表页面 +- [ ] 创建半屏弹窗组件 +- [ ] Service 层对接 + +### Phase 1D:验证与收尾(1 天) + +- [ ] 三端联调测试 +- [ ] 更新 wiki 文档 +- [ ] 提交并推送 + +--- + +## 9. 验证方案 + +### 9.1 后端验证 + +```bash +# 1. 启动后端 +cd crates/erp-server && cargo run + +# 2. 创建 AI 分析 → 产生建议 +# 3. 调用 API 验证 +curl -H "Authorization: Bearer $TOKEN" \ + http://localhost:3000/api/v1/action-inbox + +curl -H "Authorization: Bearer $TOKEN" \ + http://localhost:3000/api/v1/action-inbox/ai_suggestion:{id}/thread +``` + +### 9.2 Web 验证 + +1. 登录 → 顶栏铃铛应显示待办数量 +2. 点击铃铛 → Popover 显示最近待办 +3. "查看全部" → 进入 ActionInboxPage +4. 点击待办项 → Drawer 打开,时间线正确 +5. 点击"批准" → 状态变更,时间线更新 +6. SSE 推送 → Drawer 实时更新 + +### 9.3 小程序验证 + +1. 底部 TabBar 显示"待办"Tab + 角标 +2. 进入待办页 → 列表正确 +3. 点击卡片 → 半屏弹窗弹出 +4. 操作后 → 状态更新,列表刷新 + +--- + +## 10. 未来扩展 + +新增行动类型只需: + +1. 在 `ActionInboxService` 中注册新的聚合查询器 +2. 定义 `type → ActionItem` 的映射规则 +3. 定义线程步骤模板 +4. 前端自动渲染(共用相同组件) + +**规划中的扩展类型**: +- `alert` — 告警处理闭环(告警→评估→处置→追踪) +- `followup` — 随访到期提醒(到期→执行→记录→再分析) +- `data_anomaly` — 体征异常反馈(异常→趋势对比→建议→操作) diff --git a/docs/superpowers/specs/2026-05-01-unified-workbench-design.md b/docs/superpowers/specs/2026-05-01-unified-workbench-design.md new file mode 100644 index 0000000..90bc9e5 --- /dev/null +++ b/docs/superpowers/specs/2026-05-01-unified-workbench-design.md @@ -0,0 +1,499 @@ +# 统一工作台设计规格 + +> 日期: 2026-05-01 | 状态: 草案 | 作者: brainstorming session + +## 目录 + +1. **背景与目标** — 为什么要做、要解决什么问题 +2. **设计决策** — 方案选择过程和最终决策 +3. **页面布局** — 医生视角和主任视角的详细布局 +4. **待办数据模型** — ActionItem 统一结构和类型定义 +5. **交互设计** — 弹窗/抽屉混合、筛选、排序、操作按钮 +6. **角色感知** — 医生 vs 主任的渲染差异 +7. **后端 API** — ActionInboxService 聚合查询接口 +8. **与现有模块的关系** — 复用 erp-health / erp-ai 已有能力 +9. **实施分步** — Phase 划分和依赖关系 +10. **验证标准** — 完成标准 + +--- + +## 1. 背景与目标 + +### 1.1 问题 + +HMS 平台功能完成度已达 83%,后端 AI 能力成熟(4 个 SSE 分析端点、建议系统、自动趋势分析、本地规则引擎),但审计揭示了一个核心矛盾:**后端能力远超前端覆盖**。 + +具体表现: +- **AI 分析"有脑无手"** — 后端 4 个 SSE 端点就绪,AI 建议可自动创建,但前端无统一入口展示和处理 +- **告警"有声无窗"** — 后端告警引擎完整,Web 端无集中展示 +- **待办分散** — AI 建议、告警、随访、数据异常分布在不同页面,医生需要逐一查看 +- **主任视角缺失** — 科室主任无法快速掌握团队工作负载和科室风险概况 + +### 1.2 目标 + +设计一个**统一工作台**,作为医生和科室主任的默认首页,实现: + +1. **一眼看到所有待办** — AI 建议、危急告警、随访、数据异常集中展示 +2. **一键操作** — 每条待办可直接处理(审批/联系/安排),不强制跳转 +3. **AI 洞察直达** — 右侧面板实时展示最新 AI 分析洞察 +4. **主任管理视角** — 团队工作负载、超时升级、科室风险分布一目了然 +5. **设计一致性** — 医生和主任使用相同的页面结构,通过角色感知差异化 + +### 1.3 范围 + +- **包含**:Web 端首页工作台重构、后端 ActionInboxService 聚合 API +- **不包含**:小程序端改造(独立迭代)、BPMN 工作流编排(AI 行动闭环 Phase 2+) + +--- + +## 2. 设计决策 + +### 2.1 方案选择过程 + +通过高保真 HTML 原型对比了三种工作台形态: + +| 方案 | 描述 | 优势 | 劣势 | +|------|------|------|------| +| **A · 首页即工作台** | 登录后默认首页,取代现有 Dashboard | 零点击触达待办,信息密度高 | 首页功能增多 | +| **B · 独立工作台** | 侧边栏入口,三栏布局(筛选+列表+详情) | 筛选能力强,详情展示充分 | 需要额外导航,脱离首页语境 | +| **C · 嵌入式体验** | 顶栏气泡+侧边迷你面板+浮动 AI 助手 | 信息无处不在,上下文关联强 | 实现复杂度高,分散注意力 | + +**最终选择:方案A — 首页即工作台** + +理由: +- 医生最核心的需求是"快速知道今天要做什么",方案A 零点击满足 +- 信息密度适中(左侧待办 + 右侧 AI 洞察),不会过载 +- 与现有 Home.tsx 的角色感知 Dashboard 结构兼容,改动最小 + +### 2.2 科室主任视角 + +对比了三种主任工作台方案: + +| 方案 | 描述 | 优势 | 劣势 | +|------|------|------|------| +| **D · 个人待办 + 团队概览卡** | 和医生同结构,右侧替换为团队概览 | 设计一致性最高 | 团队管理能力相对有限 | +| **E · 独立管理仪表盘** | 纯管理视角,6列统计+团队表+告警线 | 管理信息最全面 | 完全不同的页面,开发成本高 | +| **F · Tab 双视图** | 同一页面内 Tab 切换 | 兼顾两种需求 | Tab 切换增加认知负担 | + +**最终选择:方案D — 个人待办 + 团队概览卡** + +理由: +- 与医生方案保持设计一致性,同一页面结构 +- 团队概览卡提供足够的主任管理信息(每人工作负载、处理率、超时数) +- 实现成本最低,角色感知逻辑复用现有模式 + +### 2.3 AI 建议交互 + +**决策:弹窗 + 抽屉混合** + +| 操作类型 | 交互方式 | 原因 | +|---------|---------|------| +| 批准/拒绝/忽略 | Modal 弹窗确认 | 轻操作,一步完成 | +| 查看完整 AI 分析 | 右侧 Drawer 抽屉 | 需要展示长文本、基线指标、建议详情 | +| 修改后批准 | Drawer 内编辑 | 需要足够空间修改建议内容 | + +--- + +## 3. 页面布局 + +### 3.1 整体结构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 顶部 Tab 切换栏(仅原型用)│ 实际:MainLayout 侧边栏 + 顶栏 │ +├──────┬──────────────────────────────────────────────────────┤ +│ │ 欢迎语 + 日期 + 待办总数 │ +│ 侧 │ ┌──────┬──────┬──────┬──────┬──────┐ │ +│ 边 │ │统计卡1│统计卡2│统计卡3│统计卡4│(主任+1)│ │ +│ 导 │ └──────┴──────┴──────┴──────┴──────┘ │ +│ 航 │ ┌─────────────────────┬──────────────┐ │ +│ │ │ │ │ │ +│ 220px│ │ 待办任务列表 │ AI 洞察 / │ │ +│ │ │ (按紧急度排序) │ 团队概览 │ │ +│ │ │ │ (主任) │ │ +│ │ │ 类型筛选条 │ │ │ +│ │ │ │ │ │ +│ │ └─────────────────────┴──────────────┘ │ +│ │ 快捷操作区 │ +├──────┴──────────────────────────────────────────────────────┤ +``` + +### 3.2 医生视角(Doctor) + +**统计卡片(4 列)**: +| 序号 | 标签 | 数据源 | 颜色 | +|------|------|--------|------| +| 1 | 今日待办 | ActionItem count(assigned_to=me, status=pending) | 蓝 #2563EB | +| 2 | AI 建议待审 | ActionItem count(type=ai_suggestion, status=pending) | 紫 #7C3AED | +| 3 | 危急值告警 | ActionItem count(type=alert, severity=urgent) | 红 #DC2626 | +| 4 | 随访到期 | ActionItem count(type=followup, status=pending) | 橙 #D97706 | + +**待办列表**: +- 默认按紧急度排序:urgent > high > normal > low +- 顶部筛选条:全部 / AI 建议 / 告警 / 随访 / 数据异常 +- 每条显示:紧急度圆点 + 标题 + 摘要 + 类型标签 + 风险等级 + 时间 + 操作按钮 +- 点击条目 → Drawer 展开详情 + +**右侧 AI 洞察面板(340px)**: +- 顶部:AI 图标 + "智能洞察" 标题 + 实时标记 +- 最近 3 条 AI 分析洞察,每条包含:患者姓名/年龄/诊断、分析摘要、风险等级标签 +- 第一条洞察下方放快捷操作按钮(联系患者/安排急诊/调整用药) +- 底部:快捷操作区(查询患者/新建随访/AI 分析) + +### 3.3 科室主任视角(Director) + +与医生共享同一页面结构,差异化: + +**统计卡片(5 列,多 2 个)**: +| 序号 | 标签 | 说明 | +|------|------|------| +| 1 | 科室待办总计 | 全科室 pending 总数,副文本显示"我负责 N 项" | +| 2 | AI 建议待审 | 全科室范围 | +| 3 | 危急值告警 | 全科室范围 | +| 4 | 随访到期 | 全科室范围 | +| 5 | 今日处理率 | 已处理/总数,绿色 | + +**待办列表**: +- 仅显示分配给主任的待办:超时升级项、高风险 AI 建议审批、随访审核 +- 超时升级项带"超时升级"标签和红色高亮 + +**右侧团队概览面板(340px)**: +- 团队概览卡片:每位医生一行,显示姓名、职称、待办数、处理率进度条、超时数 +- 超时医生卡片左侧红色边框 + 红色背景高亮 +- 点击医生卡片 → 展开该医生的待办列表 +- 底部:科室患者风险分布(高/中/低危统计) + +--- + +## 4. 待办数据模型 + +### 4.1 ActionItem 统一结构 + +```typescript +interface ActionItem { + id: string; // UUID v7 + tenant_id: string; + type: 'ai_suggestion' | 'alert' | 'followup' | 'data_anomaly'; + severity: 'urgent' | 'high' | 'normal' | 'low'; + status: 'pending' | 'in_progress' | 'completed' | 'escalated'; + title: string; // "张伟 — 血压危急值告警" + summary: string; // 一句话摘要 + patient_id: string; + patient_name: string; + assigned_to: string; // user_id + source_id: string; // 来源记录 ID(ai_suggestion.id / alert.id 等) + source_type: string; // "ai_suggestion" / "alert" / "followup" / "data_anomaly" + created_at: string; + updated_at: string; + due_at?: string; // 超时时间(用于升级判断) + metadata: Record; // 类型特定的附加数据 +} +``` + +### 4.2 类型与来源映射 + +| type | 来源模块 | 触发条件 | 默认 severity | +|------|---------|---------|--------------| +| `ai_suggestion` | erp-ai | AI 分析完成 + structured_output 含 suggestions | 从 AI risk_level 映射 | +| `alert` | erp-health | 告警引擎触发危急值 | `urgent` | +| `followup` | erp-health | 随访计划到期 | `normal` | +| `data_anomaly` | erp-health | 规则引擎检测到异常 | 从规则配置映射 | + +### 4.3 严重度与升级规则 + +| severity | 圆点颜色 | 超时 | 升级行为 | +|----------|---------|------|---------| +| `urgent` | 红 #DC2626 | 4 小时 | 升级至科室主任 | +| `high` | 橙 #D97706 | 24 小时 | 升级至科室主任 | +| `normal` | 蓝 #2563EB | 72 小时 | 标记逾期 | +| `low` | 灰 #94A3B8 | 无 | 不升级 | + +### 4.4 不建新表 + +Phase 1 采用三表 JOIN 聚合查询,不创建独立的 `action_items` 表: +- `ai_suggestions` + `ai_analysis` + `patient` → AI 建议类待办 +- `alerts` + `patient` → 告警类待办 +- `followup_plans` + `patient` → 随访类待办 +- `device_readings`(异常) + `patient` → 数据异常类待办 + +理由:避免数据冗余和同步问题。各模块已有完整数据,工作台只做聚合展示。未来如需独立表,可作为 Phase 2 引入。 + +--- + +## 5. 交互设计 + +### 5.1 待办列表交互 + +**筛选**: +- 顶部筛选条:全部 / AI 建议 / 告警 / 随访 / 数据异常 +- 点击切换,无页面刷新,前端过滤已加载的数据 +- 默认显示"全部" + +**排序**: +- 默认按紧急度排序(urgent > high > normal > low) +- 同级别按创建时间倒序 +- 未来可扩展:按患者、按时间排序 + +**点击行为**: +- 点击待办条目 → 右侧 Drawer 抽屉展开(宽度 480px) +- Drawer 内容根据 `type` 不同渲染不同详情组件 + +### 5.2 Drawer 详情面板 + +**AI 建议 (ai_suggestion)**: +- 顶部:类型标签 + 风险等级 + 触发时间 +- 患者信息:姓名、年龄、性别、诊断、科室 +- AI 分析摘要(Markdown 渲染) +- 建议方案(高亮框) +- 基线指标网格(2x2:关键数值 + 趋势箭头) +- 操作按钮:批准 / 拒绝 / 修改后批准 + +**危急告警 (alert)**: +- 告警详情:触发指标、阈值、当前值 +- 患者基本信息 +- 快捷操作:联系患者 / 安排急诊 / 调整用药 +- 操作按钮:已处理 / 转交 + +**随访 (followup)**: +- 随访计划详情 +- 上次随访摘要 +- 操作按钮:开始随访 / 延期 / 标记完成 + +**数据异常 (data_anomaly)**: +- 异常指标详情 +- 历史趋势迷你图 +- 操作按钮:查看详情 / AI 分析 / 忽略 + +### 5.3 Modal 弹窗 + +**批准确认**: +- 显示建议摘要 +- "确认批准?" + 备注(可选) +- 按钮:确认 / 取消 + +**拒绝确认**: +- 必填拒绝原因 +- 按钮:确认拒绝 / 取消 + +**忽略确认**: +- "确认忽略此待办?" +- 按钮:确认 / 取消 + +### 5.4 右侧面板交互 + +**医生 — AI 洞察面板**: +- 显示最近 3 条 AI 分析洞察(按时间倒序) +- 第一条洞察下方显示快捷操作按钮 +- 点击洞察标题 → 打开对应患者的 AI 建议 Drawer + +**主任 — 团队概览面板**: +- 每位医生一行卡片,hover 高亮 +- 点击医生卡片 → Drawer 展开该医生的待办列表 +- 超时医生卡片:左侧红色边框 + 浅红背景 + 超时数红色高亮 +- 底部风险分布:高/中/低危三色统计块 + +--- + +## 6. 角色感知 + +### 6.1 渲染逻辑 + +``` +if user.role contains 'director' or 'department_head': + render DirectorDashboard() +else: + render DoctorDashboard() +``` + +共享组件: +- `WorkbenchLayout` — 整体布局框架 +- `StatCardGrid` — 统计卡片网格 +- `TodoList` — 待办列表 +- `ActionDrawer` — 详情抽屉 +- `ActionModal` — 操作弹窗 + +差异组件: +- `AiInsightPanel` — 医生专属,AI 洞察 +- `TeamOverviewPanel` — 主任专属,团队概览 +- `RiskDistribution` — 主任专属,风险分布 + +### 6.2 数据范围差异 + +| 维度 | 医生 | 主任 | +|------|------|------| +| 待办范围 | `assigned_to = my_id` | `assigned_to = my_id` + 超时升级项 | +| 统计范围 | 仅自己 | 全科室 | +| 团队概览 | 不显示 | 显示本科室所有医生 | +| 风险分布 | 不显示 | 全科室患者 | +| AI 洞察 | 自己负责的患者 | 不显示(用团队概览替代)| + +--- + +## 7. 后端 API + +### 7.1 ActionInboxService + +归属 crate:`erp-health` + +**核心方法**: + +| 方法 | 路由 | 说明 | 权限码 | +|------|------|------|--------| +| `list_action_items` | `GET /api/v1/health/action-inbox` | 聚合查询待办列表 | `health.action_inbox.list` | +| `get_action_thread` | `GET /api/v1/health/action-inbox/:id/thread` | 获取待办处理线程 | `health.action_inbox.list` | +| `get_team_overview` | `GET /api/v1/health/action-inbox/team` | 主任:团队概览 | `health.action_inbox.team` | +| `get_workbench_stats` | `GET /api/v1/health/action-inbox/stats` | 统计卡片数据 | `health.action_inbox.list` | + +### 7.2 list_action_items 查询参数 + +``` +GET /api/v1/health/action-inbox?type=ai_suggestion&severity=urgent&page=1&page_size=20 +``` + +| 参数 | 类型 | 说明 | +|------|------|------| +| `type` | string | 可选,筛选类型:ai_suggestion / alert / followup / data_anomaly | +| `severity` | string | 可选,筛选紧急度 | +| `status` | string | 可选,默认 pending | +| `patient_id` | uuid | 可选,按患者筛选 | +| `page` | number | 页码,默认 1 | +| `page_size` | number | 每页条数,默认 20 | + +### 7.3 聚合查询策略 + +不建独立表,通过 SQL UNION 聚合四类数据源: + +```sql +-- AI 建议类 +SELECT s.id, 'ai_suggestion' as type, s.risk_level as severity, + s.title, s.summary, s.patient_id, p.name as patient_name, + s.assigned_to, s.created_at, 'ai_suggestion' as source_type +FROM ai_suggestions s JOIN patients p ON s.patient_id = p.id +WHERE s.status = 'pending' AND s.tenant_id = $1 + +UNION ALL + +-- 告警类 +SELECT a.id, 'alert' as type, 'urgent' as severity, + a.title, a.summary, a.patient_id, p.name as patient_name, + a.assigned_to, a.created_at, 'alert' as source_type +FROM alerts a JOIN patients p ON a.patient_id = p.id +WHERE a.status = 'active' AND a.tenant_id = $1 + +-- ... 随访、异常类同理 +ORDER BY severity_priority, created_at DESC +``` + +### 7.4 get_team_overview 响应 + +```json +{ + "members": [ + { + "user_id": "uuid", + "name": "李明远", + "title": "副主任医师", + "pending_count": 5, + "completed_count": 3, + "overdue_count": 1, + "completion_rate": 0.6 + } + ], + "risk_distribution": { + "high": 3, + "medium": 8, + "low": 24 + }, + "total_pending": 18, + "total_completed": 12 +} +``` + +--- + +## 8. 与现有模块的关系 + +### 8.1 复用已有能力 + +| 能力 | 来源 | 复用方式 | +|------|------|---------| +| AI 建议数据 | `erp-ai` AiSuggestionService | 直接查询 ai_suggestions 表 | +| 告警数据 | `erp-health` AlertService | 直接查询 alerts 表 | +| 随访数据 | `erp-health` FollowupService | 直接查询 followup_plans 表 | +| 角色感知 | `Home.tsx` 已有模式 | 参考现有 role-based Dashboard 分发 | +| Drawer 组件 | `ActionThreadDrawer` 已存在 | 复用并扩展 | +| 建议审批 | `AiSuggestionTab` 已存在 | 复用审批逻辑 | +| SSE 客户端 | `analysisSse.ts` 已存在 | 不涉及(工作台不触发分析) | + +### 8.2 新增代码归属 + +| 文件 | crate/模块 | 说明 | +|------|-----------|------| +| `action_inbox_service.rs` | erp-health/service | 聚合查询服务 | +| `action_inbox_handler.rs` | erp-health/handler | API 路由处理 | +| `WorkbenchHome.tsx` | web/pages | 新首页组件(替代 Home.tsx 中的 Dashboard 部分) | +| `TodoList.tsx` | web/components | 待办列表组件 | +| `AiInsightPanel.tsx` | web/components | AI 洞察面板 | +| `TeamOverviewPanel.tsx` | web/components | 团队概览面板 | +| `ActionDetailDrawer.tsx` | web/components | 操作详情抽屉 | +| `actionInbox.ts` | web/api/health | API 客户端(已存在,需补充) | + +### 8.3 权限码 + +新增权限码(注册在 erp-health): + +| 权限码 | 说明 | +|--------|------| +| `health.action_inbox.list` | 查看待办列表 | +| `health.action_inbox.manage` | 处理待办(批准/拒绝/转交) | +| `health.action_inbox.team` | 查看团队概览(主任专属) | + +--- + +## 9. 实施分步 + +### Phase 1A:后端聚合 API(2-3 天) + +1. 创建 `action_inbox_service.rs` — 实现四类数据源 UNION 聚合查询 +2. 创建 `action_inbox_handler.rs` — 注册 4 个 API 端点 +3. 注册权限码 `health.action_inbox.*` +4. 编写单元测试 + +### Phase 1B:Web 前端组件(2-3 天) + +1. 创建 `TodoList.tsx` — 待办列表组件(筛选、排序、点击) +2. 创建 `AiInsightPanel.tsx` — AI 洞察面板 +3. 创建 `TeamOverviewPanel.tsx` — 团队概览面板 +4. 创建 `ActionDetailDrawer.tsx` — 详情抽屉(按类型渲染不同内容) +5. 补充 `actionInbox.ts` API 客户端 +6. 改造 `Home.tsx` — 用新组件替换现有 Dashboard + +### Phase 1C:联调验证(1 天) + +1. 前后端联调 +2. 角色感知测试(医生视角 vs 主任视角) +3. 四类待办数据验证 +4. `pnpm build` 生产构建通过 + +--- + +## 10. 验证标准 + +### 功能验证 + +- [ ] 医生登录后,首页显示工作台(4 个统计卡片 + 待办列表 + AI 洞察) +- [ ] 主任登录后,首页显示工作台(5 个统计卡片 + 我的待办 + 团队概览) +- [ ] 待办列表按紧急度正确排序 +- [ ] 类型筛选条正确过滤 +- [ ] 点击 AI 建议待办 → Drawer 展示完整分析 + 操作按钮 +- [ ] 批准/拒绝操作 → Modal 确认 → 调用后端 API 成功 +- [ ] 主任团队概览正确显示每位医生的工作负载 +- [ ] 超时升级项正确出现在主任的待办列表 + +### 技术验证 + +- [ ] `cargo check` 全 workspace 通过 +- [ ] `cargo test` 相关测试通过 +- [ ] `pnpm build` 前端生产构建通过 +- [ ] API 可通过 Swagger UI 测试 +- [ ] 权限码正确拦截未授权访问 diff --git a/docs/superpowers/specs/2026-05-02-doctor-operator-workbench-design.md b/docs/superpowers/specs/2026-05-02-doctor-operator-workbench-design.md new file mode 100644 index 0000000..a5fb580 --- /dev/null +++ b/docs/superpowers/specs/2026-05-02-doctor-operator-workbench-design.md @@ -0,0 +1,539 @@ +# 医生 & 运营 & 管理员工作台设计规格 + +> 日期: 2026-05-02 | 状态: 设计中 +> 原型参考: `_temp/workbench-doctor-A.html`(医生)、`_temp/workbench-operator-C.html`(运营)、`_temp/workbench-admin.html`(管理员) +> 关联文档: `2026-05-02-health-manager-workbench-design.md`(健康管家方案 B) + +## 1. 背景与目标 + +### 1.1 与健康管家工作台的关系 + +本规格定义健康管家之外的三个角色工作台。四者共享同一平台、同一全局侧边栏、同一路由入口 `/`(`Home.tsx`),通过 `useDashboardRole()` 按 `doctor` / `health_manager` / `operator` / `admin` 角色分发不同的工作台视图。 + +| 角色 | 方案 | 核心交互模式 | 本期优先级 | +|------|------|-------------|-----------| +| 健康管家 | B 工作流驱动 | 任务队列一件一件处理 | Phase 1(已设计) | +| 医生 | A 今日全景 | 仪表盘总览 + AI 建议一键审核 | Phase 1(本文档) | +| 运营 | C AI 指挥中心 | AI 洞察驱动运营决策 | Phase 1(本文档) | +| 管理员 | 系统管理中心 | 平台运维全局视角 | Phase 1(本文档) | + +### 1.2 医生角色目标 + +| 指标 | 当前 | 目标 | +|------|------|------| +| AI 建议审核响应时间 | 无专门入口,散落在 AI 分析列表 | 工作台直接展示,一键采纳/拒绝 | +| 患者咨询回复时间 | 需进入咨询列表才能看到未读 | 工作台右侧实时显示未回复咨询 | +| 关注患者认知负担 | 需逐个翻患者详情 | 聚合告警+AI分析的关注列表 | + +### 1.3 运营角色目标 + +| 指标 | 当前 | 目标 | +|------|------|------| +| 内容运营效率 | 各功能模块独立操作 | AI 推荐热点内容,一键关联发布 | +| 积分异常发现 | 被动审核订单 | AI 主动发现异常兑换模式 | +| 运营决策数据支撑 | 需单独查看统计报表 | 工作台实时展示核心指标 + 趋势 | + +### 1.4 范围 + +**本期(Phase 1):** +- 医生:4 统计卡片 + AI 建议待审 + 重点关注患者 + 日程 + 咨询摘要 +- 运营:AI 洞察卡片 + 4 数据指标 + 热门内容排行 + 待办列表 + 积分动态 + 内容矩阵 + +**不在本期:** +- 医生角色的方案 B/C 工作流模式 +- 运营角色的方案 A/B 仪表盘模式 +- 管理员的实时系统指标监控(CPU/内存/磁盘) +- 角色间的任务转交/协作流程(Phase 2) +- AI 洞察的 SSE 实时推送(Phase 2 使用 30 秒轮询) + +## 2. 角色定义 + +### 2.1 医生 — 李明辉 + +| 维度 | 描述 | +|------|------| +| 姓名 | 李明辉 | +| 职位 | 主治医师 · 肾内科 | +| 年龄 | 42 岁 | +| 工作经验 | 15 年临床,8 年肾内科 | +| 技术水平 | 基本操作无障碍,不追求花哨功能 | +| 日均任务量 | AI 建议审核 3-5 项、咨询回复 2-3 条、查看危急值 1-2 次 | + +#### 典型工作日 + +``` +07:30 晨间查房,查看住院患者 +08:00 门诊开始,打开工作台扫一眼今日概览 +08:30 门诊间隙审核 AI 建议(采纳/拒绝) +10:00 回复患者咨询消息 +10:30 多学科会诊 +14:00 下午门诊 +16:00 查看告警中心,确认危急值已处理 +17:00 下班 +``` + +#### 核心痛点 + +1. **AI 建议易遗忘**:AI 分析结果在独立列表中,不强制提醒,容易积压 +2. **咨询回复不及时**:不知道有新咨询,需要主动刷新咨询列表 +3. **患者上下文分散**:看 AI 建议时需要单独翻患者详情了解病史 + +### 2.2 运营 — 王美玲 + +| 维度 | 描述 | +|------|------| +| 姓名 | 王美玲 | +| 职位 | 健康运营专员 | +| 年龄 | 28 岁 | +| 工作经验 | 3 年互联网运营,1 年健康领域 | +| 技术水平 | 熟练使用各种运营工具,对数据敏感 | +| 日均任务量 | 文章管理 2-3 篇、积分审核 5-10 笔、活动跟进 1-2 个 | + +#### 典型工作日 + +``` +08:30 打开工作台,查看 AI 洞察和昨日数据 +09:00 审核积分兑换订单(AI 标记异常的优先) +10:00 发布新科普文章,关联热门话题 +11:00 跟进线下活动报名情况 +14:00 查看内容阅读数据,优化推送策略 +15:00 整理运营周报 +16:30 跟进沉默用户,发送关怀消息 +17:30 下班 +``` + +#### 核心痛点 + +1. **数据分散**:积分、内容、活动数据在不同页面,需来回切换 +2. **缺乏数据驱动**:发什么内容凭经验,不知道什么受欢迎 +3. **异常发现慢**:积分兑换异常靠人工发现,等到发现时已造成损失 + +### 2.3 管理员 — 陈建国 + +| 维度 | 描述 | +|------|------| +| 姓名 | 陈建国 | +| 职位 | 系统管理员 / 平台运维 | +| 年龄 | 35 岁 | +| 工作经验 | 8 年 IT 运维,3 年医疗信息化 | +| 技术水平 | 熟悉 Linux/Docker/数据库,能看懂 Rust 错误日志 | +| 日均任务量 | 用户管理 5-10 次、权限配置 1-2 次、系统巡检 3-4 次 | + +#### 核心痛点 + +1. **系统健康不可见**:不知道各模块是否正常运行,出了问题被动发现 +2. **操作追溯分散**:用户做了什么操作需要翻审计日志表,没有聚合视图 +3. **配置入口分散**:用户管理/角色权限/菜单/字典/插件分布在不同页面,来回切换 + +## 3. 信息架构 + +### 3.1 医生:方案 A「今日全景」布局 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ 全局侧边栏(已有) │ 医生工作台页面内部 │ +│ │ │ +│ 🏠 今日全景(高亮) │ ┌──────────────────────────────────────┐ │ +│ 🩺 患者管理 │ │ 欢迎栏:上午好,李医生 │ │ +│ 💬 患者咨询 │ ├──────────────────────────────────────┤ │ +│ 📅 预约排班 │ │ 4 统计卡片(危急值/待审/咨询/预约) │ │ +│ 🤖 AI 分析 │ ├──────────────────┬───────────────────┤ │ +│ ⚠️ 告警中心 │ │ 左主内容(flex) │ 右窄栏(340px) │ │ +│ 📊 统计报表 │ │ │ │ │ +│ │ │ AI 建议待审列表 │ 今日日程 │ │ +│ │ │ (采纳/拒绝按钮) │ 未回复咨询 │ │ +│ │ │ │ 快捷操作 │ │ +│ │ │ 重点关注患者列表 │ │ │ +│ │ │ │ │ │ +│ │ └──────────────────┴───────────────────┘ │ +└──────────────────────────────────────────────────────────────┘ +``` + +**布局参数**: +- 左主内容:`flex: 1`,包含 AI 建议和关注患者两个 Card +- 右窄栏:固定 340px,包含日程、咨询、快捷操作 + +### 3.2 运营:方案 C「AI 指挥中心」布局 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ 全局侧边栏(已有) │ 运营工作台页面内部 │ +│ │ │ +│ 🤖 AI 工作台(高亮) │ ┌──────────────────────────────────────┐ │ +│ 📝 内容管理 │ │ AI 洞察卡片(暖橙色渐变 Hero) │ │ +│ 🎁 积分商城 │ │ 3 条洞察 + 操作按钮 │ │ +│ 🎪 线下活动 │ ├──────────────────────────────────────┤ │ +│ 👥 患者管理 │ │ 4 数据指标卡片(活跃/阅读/积分/订单) │ │ +│ 📊 统计报表 │ ├──────────────────┬───────────────────┤ │ +│ ⚙️ 系统设置 │ │ 热门科普排行 │ 今日待办 │ │ +│ │ ├──────────────────┼───────────────────┤ │ +│ │ │ 积分动态 │ 内容矩阵 │ │ +│ │ └──────────────────┴───────────────────┘ │ +└──────────────────────────────────────────────────────────────┘ +``` + +**布局参数**: +- 顶部:AI Hero 卡片(全宽) +- 第二行:4 数据指标(grid 4 列) +- 第三行起:双栏卡片(grid 2 列等宽) +- 最大内容宽度 960px(`max-width`) + +### 3.3 管理员:系统管理中心布局 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ 全局侧边栏(已有) │ 管理员工作台页面内部 │ +│ │ │ +│ 🔧 系统管理(高亮) │ ┌──────────────────────────────────────┐ │ +│ 👤 用户管理 │ │ 系统健康条(6 项状态指示灯) │ │ +│ 🔑 角色权限 │ ├──────────────────────────────────────┤ │ +│ 📖 菜单管理 │ │ 4 统计卡片(用户/模块/操作/工单) │ │ +│ 📋 审计日志 │ ├──────────────────┬───────────────────┤ │ +│ ⚙️ 系统配置 │ │ 审计日志 │ 模块状态 │ │ +│ 🧩 插件管理 │ ├──────────────────┼───────────────────┤ │ +│ 📊 统计报表 │ │ 用户活跃度 │ 系统管理快捷入口 │ │ +│ │ └──────────────────┴───────────────────┘ │ +└──────────────────────────────────────────────────────────────┘ +``` + +**布局参数**: +- 顶部:系统健康条(全宽,flex 横向排列 6 项状态) +- 第二行:4 统计卡片(grid 4 列) +- 第三行起:双栏卡片(grid 2 列等宽) +- 最大内容宽度 1080px + +## 4. 组件清单 + +### 4.1 医生组件 + +| 组件 | 说明 | 数据源 | +|------|------|--------| +| **DoctorStatsRow** | 4 统计卡片:危急值/待审/咨询/预约 | `GET /health/action-inbox/stats` + `GET /health/doctor/dashboard` | +| **AiSuggestionList** | AI 建议待审列表,内嵌采纳/拒绝按钮 | `GET /health/action-inbox`(过滤 `type=ai_suggestion`) | +| **FocusedPatientList** | 重点关注患者(聚合告警+AI 分析) | `GET /health/action-inbox`(按患者聚合) | +| **TodaySchedule** | 今日日程(查房/门诊/会诊) | `GET /health/doctor-schedules`(过滤今日) | +| **ConsultSummary** | 未回复咨询摘要(侧栏紧凑版) | `GET /health/consultation-sessions`(过滤未回复) | +| **QuickActions** | 快捷入口(AI 分析/告警/患者查询) | 前端静态配置 | + +### 4.2 运营组件 + +| 组件 | 说明 | 数据源 | +|------|------|--------| +| **AiInsightHero** | AI 洞察 Hero 卡片(暖橙色渐变) | Phase 1 前端静态展示,Phase 2 接 AI 生成 | +| **OperatorStatsRow** | 4 数据指标(活跃/阅读/积分/订单) | `GET /health/admin/statistics/dashboard` + `GET /health/admin/points/statistics` | +| **HotContentRank** | 热门科普排行(阅读量排名) | `GET /health/articles`(按 view_count 排序) | +| **OperatorTodoList** | 今日待办(积分审核/发布文章/活动跟进等) | 前端状态管理(Phase 2 扩展为任务系统) | +| **PointsFeed** | 积分动态流(近期获得/兑换) | `GET /health/admin/points/patients/{id}/transactions`(聚合) | +| **ContentMatrix** | 内容矩阵(分类分布条形图) | `GET /health/articles`(按分类聚合统计) | + +### 4.3 共享组件 + +以下组件在两个角色中复用,与健康管家工作台共享: + +| 组件 | 复用场景 | +|------|---------| +| **StatCard** | 统计卡片基础样式(顶部色条 + 数值 + 副文本) | +| **Card** | 通用卡片容器(圆角 + 边框 + header + body) | + +### 4.4 管理员组件 + +| 组件 | 说明 | 数据源 | +|------|------|--------| +| **SystemHealthBar** | 6 项系统状态指示灯(API/DB/Redis/邮件/存储/定时任务) | Phase 1 前端静态(Phase 2 接 `/api/v1/system/health`) | +| **AdminStatsRow** | 4 统计卡片(注册用户/业务模块/今日操作/待处理工单) | `GET /api/v1/users`(总数)+ 前端计算 | +| **AuditLogList** | 最近操作记录(操作人+动作+时间) | `GET /api/v1/audit-logs`(最新 6 条) | +| **ModuleStatusList** | 模块状态列表(已启用/未启用) | 前端静态配置(模块列表固定) | +| **UserActivityChart** | 用户活跃度分布(今日/本周/本月/总注册 + 角色分布) | `GET /api/v1/users`(聚合统计) | +| **AdminQuickActions** | 系统管理快捷入口(8 个管理功能) | 前端静态配置 | + +### 4.5 数据优先级说明 + +**医生**:核心数据来自 `action-inbox` 系统(与健康管家共享),额外需要 `doctor/dashboard` API 提供预约和日程数据。 + +**运营**:核心数据来自 `statistics/dashboard` + `articles` + `points` API,数据维度完全不同,独立于 action-inbox 系统。 + +## 5. 数据流 + +### 5.1 医生数据源映射 + +| 组件 | API | 参数 | 返回 | +|------|-----|------|------| +| DoctorStatsRow | `GET /health/action-inbox/stats` | — | `WorkbenchStats`(pending_count 等) | +| DoctorStatsRow | `GET /health/doctor/dashboard` | — | 预约数、日程数 | +| AiSuggestionList | `GET /health/action-inbox` | `type=ai_suggestion, status=pending` | `ActionItem[]` | +| FocusedPatientList | `GET /health/action-inbox` | `status=pending`(前端按 patient_id 聚合) | `ActionItem[]` | +| TodaySchedule | `GET /health/doctor-schedules` | `date=today` | `ScheduleItem[]` | +| ConsultSummary | `GET /health/consultation-sessions` | `status=active, unread=true` | `Consultation[]` | + +**并行请求策略**:页面加载时使用 `Promise.allSettled` 并行请求 stats + list + schedule + consultations。 + +### 5.2 运营数据源映射 + +| 组件 | API | 参数 | 返回 | +|------|-----|------|------| +| OperatorStatsRow | `GET /health/admin/statistics/dashboard` | — | 综合统计数据 | +| OperatorStatsRow | `GET /health/admin/points/statistics` | — | 积分统计 | +| HotContentRank | `GET /health/articles` | `sort=-view_count, page_size=5` | `Article[]` | +| ContentMatrix | `GET /health/articles` | `page_size=1`(仅需 total) | 按分类聚合(前端或后端) | +| PointsFeed | `GET /health/admin/points/patients` | 最近交易(需后端扩展) | 积分流水 | + +**AI Hero 洞察**:Phase 1 使用前端静态文案(硬编码洞察示例)。Phase 2 接入 AI 生成接口。 + +### 5.3 管理员数据源映射 + +| 组件 | API | 参数 | 返回 | +|------|-----|------|------| +| AdminStatsRow | `GET /api/v1/users` | `page=1, page_size=1` | 用户总数(从 pagination.total) | +| AuditLogList | `GET /api/v1/audit-logs` | `page=1, page_size=6` | `AuditLogItem[]` | +| UserActivityChart | `GET /api/v1/users` | 聚合查询 | 角色分布统计 | +| SystemHealthBar | Phase 1 静态 | — | 前端硬编码状态 | +| ModuleStatusList | Phase 1 静态 | — | 前端硬编码模块列表 | +| AdminQuickActions | 前端静态 | — | 路由配置 | + +**管理员数据特点**:不依赖 `erp-health` 的任何 API,数据来自 ERP 基础模块(用户、审计、配置)。 + +### 5.4 与 action-inbox 的关系 + +| 角色 | 使用方式 | +|------|---------| +| 健康管家 | 完全基于 action-inbox,任务队列 + 详情处理 | +| 护士 | 复用健康管家工作台(同一任务队列),不做区分 | +| 医生 | 部分使用 action-inbox(仅读取 AI 建议和告警),**不做任务处理流程** | +| 运营 | 不使用 action-inbox,数据来源独立 | +| 管理员 | 不使用 action-inbox,数据来自 ERP 基础模块(用户/审计/配置) | + +医生角色的 action-inbox 使用是**只读展示**:在 AI 建议列表中直接嵌入「采纳/拒绝」按钮,调用 `/health/ai-analysis/:id/review`,不走 action-inbox 的 complete 流程。 + +## 6. 交互流程 + +### 6.1 医生:AI 建议审核 + +以「张伟 — 血压持续升高建议调整用药」为例: + +``` +1. 进入工作台 + └→ 4 统计卡片显示:待审 5 条、未回复 3 条 + +2. 浏览 AI 建议列表 + └→ 列表按风险等级排序(高→中→低) + └→ 每条建议显示:风险徽章 + 标题 + 摘要 + 采纳/拒绝按钮 + +3. 审核建议 + └→ 点击「采纳」→ 调用 POST /health/ai-analysis/:id/review?action=approve + └→ 或点击「拒绝」→ 弹出拒绝原因输入 → 提交 + └→ 操作完成后,该建议从列表移除 + +4. 查看关注患者 + └→ 下方列表聚合了有告警/AI 建议的患者 + └→ 点击患者名称 → 跳转患者详情页 + +5. 查看右侧信息 + └→ 日程卡片:了解今日安排 + └→ 咨询摘要:点击未回复咨询 → 跳转咨询详情页 +``` + +### 6.2 运营:AI 洞察驱动操作 + +以「透析饮食管理文章爆发」洞察为例: + +``` +1. 进入工作台 + └→ AI Hero 卡片展示 3 条洞察,每条附带操作按钮 + +2. 响应洞察 + └→ 点击「发布关联文章」→ 跳转文章编辑器(预填关联标签) + └→ 或点击「审核积分订单」→ 跳转积分订单列表(标记异常) + +3. 查看热门排行 + └→ 了解哪些内容受欢迎,指导后续内容策略 + +4. 处理今日待办 + └→ 待办列表展示优先级事项 + └→ 点击操作按钮 → 跳转对应管理页面 + +5. 监控积分动态 + └→ 实时查看积分发放/兑换流水 +``` + +### 6.3 管理员:系统巡检 + +``` +1. 进入工作台 + └→ 系统健康条显示 6 项服务状态(绿灯/黄灯/红灯) + +2. 确认系统正常 + └→ 所有绿灯 → 放心继续 + └→ 发现黄灯(如邮件队列积压)→ 点击进入邮件服务配置 + +3. 查看统计卡片 + └→ 注册用户/业务模块/今日操作/待处理工单 + +4. 翻看审计日志 + └→ 最近 6 条操作记录(谁做了什么) + └→ 发现异常操作 → 点击进入完整审计日志 + +5. 检查模块状态 + └→ 确认各模块运行中 + └→ 发现未启用模块 → 考虑是否启用 + +6. 用户活跃度 + └→ 查看今日/本周/本月活跃数 + └→ 角色分布确认人员配置合理 + +7. 快捷管理入口 + └→ 点击用户管理/角色权限/系统配置等 → 跳转对应管理页面 +``` + +### 6.4 空状态处理 + +| 角色 | 区域 | 空状态展示 | +|------|------|-----------| +| 医生 | AI 建议列表 | 「暂无待审 AI 建议」 | +| 医生 | 关注患者 | 「暂无重点关注患者」 | +| 医生 | 咨询摘要 | 「暂无未回复咨询」 | +| 医生 | 日程 | 「今日暂无安排」 | +| 运营 | AI Hero | 静态内容,不会为空 | +| 运营 | 热门排行 | 「暂无发布内容」 | +| 运营 | 待办列表 | 「今日暂无待办」 | +| 管理员 | 审计日志 | 「暂无操作记录」 | +| 管理员 | 系统健康 | 全绿灯,静态展示 | + +### 6.5 前端状态管理 + +三个角色各自独立数据获取,30 秒轮询刷新: + +```typescript +// 医生工作台 — 使用现有 workbenchStore + 额外请求 +const DoctorDashboard: React.FC = () => { + const { refreshTasks, refreshStats } = useWorkbenchStore(); + // 额外请求:日程、咨询 + const [schedules, setSchedules] = useState([]); + const [consults, setConsults] = useState([]); + + useEffect(() => { + refreshTasks(); + refreshStats(); + Promise.allSettled([ + doctorApi.getDashboard(), + consultationApi.list({ status: 'active', unread: true }), + ]).then(...); + }, []); +}; + +// 运营工作台 — 独立数据获取,不使用 workbenchStore +const OperatorDashboard: React.FC = () => { + const [stats, setStats] = useState(null); + const [articles, setArticles] = useState([]); + + useEffect(() => { + Promise.allSettled([ + statsApi.getDashboard(), + articlesApi.list({ sort: '-view_count', page_size: 5 }), + pointsApi.getStatistics(), + ]).then(...); + }, []); +}; + +// 管理员工作台 — 独立数据获取,不使用 workbenchStore +const AdminDashboard: React.FC = () => { + const [auditLogs, setAuditLogs] = useState([]); + const [userCount, setUserCount] = useState(0); + + useEffect(() => { + Promise.allSettled([ + auditLogApi.list({ page: 1, page_size: 6 }), + userApi.list({ page: 1, page_size: 1 }), // 只取 total + ]).then(...); + }, []); +}; +``` + +## 7. 与现有系统的关系 + +### 7.1 角色路由分发 + +在 `Home.tsx` 中通过 `useDashboardRole()` 分发不同工作台组件: + +```typescript +const HomePage = () => { + const role = useDashboardRole(); + + switch (role) { + case 'doctor': + return ; + case 'health_manager': + case 'nurse': + return ; // 方案 B TaskQueue + TaskDetail + case 'operator': + return ; + case 'admin': + return ; // 系统管理中心 + default: + return ; + } +}; +``` + +### 7.2 涉及文件 + +| 文件 | 改动类型 | 说明 | +|------|---------|------| +| `apps/web/src/pages/Home.tsx` | **改造** | 角色路由分发 + 导入各角色组件 | +| `apps/web/src/pages/health/components/workbench/DoctorDashboard.tsx` | **新增** | 医生方案 A 仪表盘 | +| `apps/web/src/pages/health/components/workbench/OperatorDashboard.tsx` | **新增** | 运营方案 C AI 指挥中心 | +| `apps/web/src/pages/health/components/workbench/AdminDashboard.tsx` | **新增** | 管理员系统管理中心 | +| `apps/web/src/pages/health/components/workbench/TaskQueue.tsx` | **已有** | 健康管家方案 B(Phase 1 已实现) | +| `apps/web/src/pages/health/components/workbench/TaskDetail.tsx` | **已有** | 健康管家方案 B(Phase 1 已实现) | + +### 7.3 数据库变更 + +Phase 1 **无需新建表**。所有数据来自现有 API: +- 医生:action-inbox + doctor/dashboard + consultation-sessions + doctor-schedules +- 运营:statistics/dashboard + articles + points/statistics +- 管理员:users + audit-logs + 前端静态配置 + +### 7.4 权限 + +沿用现有权限码,无需新增: + +| 角色 | 所需权限码 | +|------|-----------| +| 医生 | `health.action-inbox.list` + `health.ai-analysis.review` + `health.consultation.manage` + `health.doctor-schedule.list` | +| 运营 | `health.statistics.dashboard` + `health.articles.list` + `health.points.manage` | +| 管理员 | `system.users.list` + `system.audit-logs.list` + `system.settings.read` | + +### 7.5 验收标准 + +**医生工作台:** +- [ ] 工作台显示 4 项统计数据(危急值/待审/咨询/预约) +- [ ] AI 建议列表按风险排序,每条有采纳/拒绝按钮 +- [ ] 采纳/拒绝操作调用对应 API 并刷新列表 +- [ ] 重点关注患者列表聚合展示(告警+AI 建议) +- [ ] 右侧日程卡片显示今日排班 +- [ ] 右侧咨询摘要显示未回复消息 +- [ ] 空状态有合理展示 + +**运营工作台:** +- [ ] AI Hero 卡片展示 3 条洞察 + 操作按钮(Phase 1 静态内容) +- [ ] 4 项数据指标展示(活跃用户/阅读量/积分发放/待审订单) +- [ ] 热门科普排行按阅读量排序(前 5 名) +- [ ] 今日待办列表展示优先事项 +- [ ] 积分动态流展示近期交易 +- [ ] 内容矩阵展示分类分布 +- [ ] 空状态有合理展示 + +**管理员工作台:** +- [ ] 系统健康条展示 6 项服务状态(Phase 1 静态) +- [ ] 4 项统计卡片展示(注册用户/业务模块/今日操作/待处理工单) +- [ ] 审计日志展示最近 6 条操作记录 +- [ ] 模块状态列表展示已启用/未启用模块 +- [ ] 用户活跃度分布展示 +- [ ] 8 个系统管理快捷入口可点击跳转 +- [ ] 空状态有合理展示 + +**全局:** +- [ ] `useDashboardRole()` 正确分发五个角色的工作台视图 +- [ ] 30 秒自动刷新数据 +- [ ] `cargo check` 通过 +- [ ] `pnpm build` 通过 +- [ ] 浏览器中实际操作验证通过 diff --git a/docs/superpowers/specs/2026-05-02-hardcoding-cleanup-design.md b/docs/superpowers/specs/2026-05-02-hardcoding-cleanup-design.md new file mode 100644 index 0000000..9cef204 --- /dev/null +++ b/docs/superpowers/specs/2026-05-02-hardcoding-cleanup-design.md @@ -0,0 +1,415 @@ +# 系统硬编码清理设计规格 + +> 日期: 2026-05-02 | 状态: DRAFT | 范围: 前端硬编码清理 + 后端 API 补建 + 常量统一 + 医疗阈值配置化 + +--- + +## 1. 背景与动机 + +HMS 健康管理平台已进入关键节点 — 后端 328 条路由、45 个 Entity、772 个测试函数已就位。但前端工作台页面存在大量硬编码假数据,直接影响系统可信度: + +- **AdminDashboard** 中 70% 面板数据为假值(系统健康条、用户活跃度、模块状态、角色分布) +- **OperatorWorkbench** 中积分动态使用假姓名、内容矩阵数字硬编码、待办事项不可操作 +- 20+ 处状态映射在多个文件中重复定义(严重度映射 5 处、性别映射 4 处、设备类型 3 处) +- 医疗报警阈值(血压/心率/血糖)硬编码在小程序前端,不同患者/年龄段无法差异化 + +**目标**:清除所有 CRITICAL 级硬编码,确保用户看到的每个数字都来自真实 API,同时建立常量管理规范防止回退。 + +**原则**: +- 新建 API 全部遵循现有 `/api/v1/` 前缀 + `ApiResponse` 包装 + 多租户隔离 +- 常量统一采用混合策略:静态枚举收敛到 `constants/health.ts`,动态选项对接字典 API +- 医疗阈值复用现有 `critical_value_threshold` 表及 CRUD API,补充患者端只读接口 + +--- + +## 2. 影响范围与严重度矩阵 + +### 2.1 CRITICAL — 用户看到虚假数据 + +| ID | 文件 | 硬编码内容 | 影响面 | +|----|------|-----------|--------| +| C-1 | `AdminDashboard.tsx` 行 108-115 | 系统健康条 6 项全部假数据 | 所有管理员看到的"API服务正常""队列积压12"等均为虚假 | +| C-2 | `AdminDashboard.tsx` 行 230-258 | 用户活跃度 4 项 + 角色分布 5 项全部假数据 | 今日活跃 23 人、医生 12 人等均为虚假 | +| C-3 | `AdminDashboard.tsx` 行 41-50 | 模块状态 8 项硬编码 | "44 实体 · 328 路由"等描述不反映真实状态 | +| C-4 | `AdminDashboard.tsx` 行 87 | 待处理工单硬编码为 5 | 管理员误以为有 5 个工单待处理 | +| C-5 | `OperatorWorkbench.tsx` 行 40-44 | 积分动态 3 人假姓名 | 张伟/王建国/李秀英均为虚构 | +| C-6 | `OperatorWorkbench.tsx` 行 142-147 | 内容矩阵"已发布 24 / 草稿箱 3"硬编码 | 不反映真实文章数量 | +| C-7 | `OperatorWorkbench.tsx` 行 61 | AI Hero 卡片固定文案"3 个运营洞察" | 不随实际数据变化 | + +### 2.2 HIGH — 限制扩展性 + +| ID | 类别 | 数量 | 典型案例 | +|----|------|------|---------| +| H-1 | 重复状态映射 | 20+ 处 | SEVERITY_COLOR 在 5 个文件各自定义 | +| H-2 | 动态选项硬编码 | 6 类 | 科室/职称/设备类型/随访类型/咨询类型/家庭关系 | +| H-3 | 默认角色为 admin | 1 处 | `useDashboardRole.ts` 无角色时返回 admin | +| H-4 | 医疗阈值硬编码 | 2 处 | 血压 140/90、心率 100/60、血糖 6.1/7.8 | + +### 2.3 MEDIUM — 代码质量 + +| ID | 类别 | 说明 | +|----|------|------| +| M-1 | 小程序菜单用中文做 key | `MENU_PATHS` 用中文字符串做映射 key | +| M-2 | 待办模板硬编码 | 5 条固定待办文本,不可操作 | + +--- + +## 3. 轨道 1:工作台真实数据化 + +### 3.1 现有 API 清单与缺口分析 + +**已有且已被前端调用的 API:** + +| API | 路径 | 使用者 | +|-----|------|--------| +| 工作台统计 | `GET /health/action-inbox/stats` → `WorkbenchStats` | DoctorWorkbench, OperatorWorkbench | +| 团队概览 | `GET /health/action-inbox/team` → `TeamOverview` | DoctorWorkbench | +| 行动收件箱 | `GET /health/action-inbox` → `ActionItem[]` | DoctorWorkbench | +| 6 类统计 | `useStatsData` → patient/consultation/followup/points/healthData/dialysis | 三个工作台 | +| 个人统计 | `pointsApi.getPersonalStats` → `PersonalStats` | DoctorWorkbench | +| 审计日志 | `listAuditLogs` | AdminDashboard | + +**缺口 — 需要新建的 5 个 API:** + +### 3.2 新增后端 API 设计 + +#### API-1: 系统健康检查 + +``` +GET /health/admin/system-health +(完整路径: /api/v1/health/admin/system-health,前缀由 erp-server nest 自动添加) +权限: health.dashboard.manage(新增,需在 module.rs 权限描述符中注册) +响应: ApiResponse +``` + +```typescript +interface SystemHealth { + services: { + name: string; // "API 服务" / "数据库" / "Redis" / ... + status: 'healthy' | 'degraded' | 'down'; + message: string; // "正常" / "队列积压 12" / "连接超时" + response_ms?: number; + }[]; + checked_at: string; // ISO timestamp +} +``` + +实现:后端 handler 逐项检查 DB 连接(`SELECT 1`)、Redis PING、SMTP 配置状态、存储路径可用性、定时任务心跳。结果缓存 30 秒避免频繁检查。 + +#### API-2: 用户活跃度统计 + +``` +GET /health/admin/user-activity +权限: health.dashboard.manage(同 API-1) +响应: ApiResponse +``` + +```typescript +interface UserActivity { + daily_active: number; + weekly_active: number; + monthly_active: number; + total_registered: number; + by_role: { + role: string; // "医生" / "护士" / ... + count: number; + }[]; +} +``` + +实现:基于 `users` 表 `last_login_at` 字段统计,角色分布通过 `user_roles` JOIN `roles` 聚合。 + +#### API-3: 模块状态 + +``` +GET /health/admin/modules +权限: health.dashboard.manage(同 API-1) +响应: ApiResponse +``` + +```typescript +interface ModuleStatus { + name: string; // "erp-health" / "erp-auth" / ... + display_name: string; // "健康管理" / "身份权限" / ... + description: string; + active: boolean; + entity_count?: number; + route_count?: number; +} +``` + +实现:从 AppState 中读取已注册的 `ErpModule` 列表 + 查询 `plugins` 表补充插件状态。 + +#### API-4: 积分动态流 + +``` +GET /health/points/recent-activity?limit=10 +权限: health.points.list +响应: ApiResponse +``` + +> **命名约定**:TypeScript 接口字段使用 snake_case 镜像后端字段名,与本项目既有模式一致。 + +```typescript +interface PointsActivityItem { + id: string; + user_name: string; // 患者姓名 + detail: string; // "兑换 · 血压计袖带" / "每日上报 · 血压" + amount: string; // "+10" / "-500" + type: 'earn' | 'spend'; + created_at: string; +} +``` + +实现:查询 `points_account_transactions` 最新 N 条,JOIN `patients` 获取姓名。 + +#### API-5: 内容统计 + +``` +GET /health/articles/stats +权限: health.articles.list(注意复数形式,匹配 module.rs 注册的权限码) +响应: ApiResponse +``` + +```typescript +interface ArticleStats { + published: number; + draft: number; + pending_review: number; + rejected: number; + total_views: number; +} +``` + +实现:`SELECT status, COUNT(*) FROM articles WHERE tenant_id = $1 AND deleted_at IS NULL GROUP BY status`。 + +### 3.3 工作台页面改造方案 + +#### AdminDashboard 改造 + +| 面板 | 当前(假数据) | 改造后(真实数据) | +|------|---------------|-------------------| +| 系统健康条 | 6 项硬编码 | 调用 API-1 `systemHealth` | +| 统计卡片 - 注册用户 | useStatsData (已真实) | 保持不变 | +| 统计卡片 - 业务模块 | 硬编码 MODULES.length | 调用 API-3 计算活跃数 | +| 统计卡片 - 今日操作 | auditLogs.length (已真实) | 保持不变 | +| 统计卡片 - 待处理工单 | 硬编码 5 | 改用 actionInboxApi.stats().total_pending | +| 用户活跃度 | 4 项百分比 + 角色分布全部假 | 调用 API-2 `userActivity` | +| 模块状态 | MODULES 数组硬编码 | 调用 API-3 `modules` | +| 快捷管理 | QUICK_ACTIONS 硬编码 | 保留(属 UI 配置) | +| 问候语 "X主任" | 硬编码"主任" | 移除称呼后缀,只显示姓 | + +#### OperatorWorkbench 改造 + +| 面板 | 当前 | 改造后 | +|------|------|--------| +| AI Hero 卡片 | 固定文案 "3 个运营洞察" | 动态生成文案基于 stats 数据 | +| 统计卡片 | useStatsData + actionInbox (已真实) | 保持不变 | +| 今日待办 | 5 条硬编码 todo 模板 | 改用 actionInboxApi.list 筛选 pending 项 | +| 积分动态 | 3 人假姓名 | 调用 API-4 `recentActivity` | +| 内容矩阵 | "已发布 24 / 草稿箱 3" | 调用 API-5 `articleStats` | +| 问候语 "X美玲" | 硬编码"美玲" | 移除,只显示姓 | + +#### DoctorWorkbench + +基本已是真实数据(80%),仅需微调: +- 问候语 "X医生" 中"医生"后缀 → 移除,只显示姓 +- 确认 `personalStats.consultations_this_month` 字段后端已实现(否则用 0 占位) + +--- + +## 4. 轨道 2:常量统一 + +### 4.1 分类原则 + +| 分类 | 存放位置 | 判断标准 | 例子 | +|------|---------|---------|------| +| 静态映射 | `constants/health.ts` | 枚举固定,后端不可能新增项 | 性别、血型、严重度颜色、状态颜色 | +| 动态选项 | 后端字典 API | 业务运营可能新增 | 科室、职称、设备类型、随访类型 | +| UI 配置 | 各组件内部 | 纯前端行为,无后端对应 | 快捷操作按钮、Tab 标签文案 | + +### 4.2 静态映射收敛 + +统一以下映射组到 `constants/health.ts`,消除所有重复定义: + +| 导出名 | 当前分散位置 | 统一后 | +|--------|------------|--------| +| `SEVERITY_CONFIG` | AlertDashboard, AlertList, AlertRuleList, ActionInbox, DoctorDashboard (5处) | 1 处 | +| `GENDER_OPTIONS` | constants/health.ts, PatientDetail, PatientTagManage, PatientSelect (4处) | 1 处 | +| `DEVICE_TYPE_OPTIONS` + `DEVICE_TYPE_COLOR` | DeviceManage, DeviceReadingsTab, AlertRuleList (3处) | 1 处 | +| `ALERT_STATUS_CONFIG` | AlertDashboard, AlertList, StatusTag (3处) | 1 处 | +| `APPOINTMENT_STATUS_CONFIG` | AppointmentList (1处但含复杂流转) | 1 处 | +| `CONSULTATION_STATUS_CONFIG` | ConsultationList (1处) | 1 处 | +| `BLOOD_TYPE_OPTIONS` | constants/health.ts (1处) | 保持 | +| `STATUS_OPTIONS` (患者状态) | constants/health.ts (1处) | 保持 | + +每个映射组的统一格式: + +```typescript +export const SEVERITY_CONFIG: Record = { + critical: { label: '危急', color: '#DC2626', bg: '#FEF2F2' }, + high: { label: '高', color: '#D97706', bg: '#FFFBEB' }, + medium: { label: '中', color: '#2563EB', bg: '#EFF6FF' }, + low: { label: '低', color: '#6B7280', bg: '#F9FAFB' }, +}; +``` + +### 4.3 动态选项字典化 + +新增以下字典编码到 `erp-config` 的字典系统(后端已有 `GET /config/dictionaries/items?code=xxx` API,前端 `apps/web/src/api/dictionaries.ts` 已封装 `listItemsByCode`): + +| 字典编码 | 用途 | 种子数据来源 | 影响文件 | +|----------|------|-------------|---------| +| `health_department` | 科室列表 | DoctorList 现有 DEPARTMENT_OPTIONS | DoctorList, DoctorSchedule | +| `health_title` | 医护职称 | DoctorList 现有 TITLE_OPTIONS | DoctorList | +| `health_device_type` | 设备类型 | DeviceManage 现有 DEVICE_TYPE_OPTIONS | DeviceManage, DeviceReadingsTab, AlertRuleList | +| `health_follow_up_type` | 随访类型 | FollowUpTaskList 现有 FOLLOW_UP_TYPE_OPTIONS | FollowUpTaskList | +| `health_consultation_type` | 咨询类型 | ConsultationList 现有 CONSULTATION_TYPE_OPTIONS | ConsultationList | +| `health_relationship` | 家庭关系 | FamilyMembersTab 现有 RELATIONSHIP_OPTIONS | FamilyMembersTab, family-add(MP) | + +前端新增 `useDictionary(code)` hook 封装字典获取 + 缓存逻辑: + +```typescript +export function useDictionary(code: string) { + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + listItemsByCode(code) + .then(setItems) + .catch(() => setItems([])) + .finally(() => setLoading(false)); + }, [code]); + + return { items, loading }; +} +``` + +### 4.4 小程序联动 + +小程序对应的硬编码同步改为字典 API: + +| 文件 | 当前硬编码 | 改造 | +|------|-----------|------| +| `pages/pkg-health/input/index.tsx` INDICATORS | 6 个体征指标 | 从字典获取 + 缓存 | +| `pages/health/index.tsx` VITAL_TABS | 4 个体征 tab | 从字典获取 | +| `pages/pkg-profile/family-add/index.tsx` RELATION_OPTIONS | 3 个关系选项 | 从字典获取 | +| `pages/pkg-profile/family-add/index.tsx` GENDER_OPTIONS | 3 个性别选项 | 保留为静态映射(不在字典化范围) | +| `pages/mall/index.tsx` PRODUCT_TYPE_TABS | 商品类型 tab | 从字典获取 | + +小程序端新增 `useDict(code)` hook + Taro.storage 本地缓存(24h TTL),离线时使用缓存或内置默认值。 + +--- + +## 5. 轨道 3:医疗阈值配置 + +### 5.1 复用现有 `critical_value_threshold` 表 + +系统已有完整的危急值阈值基础设施,**无需新建表**: + +- **表**: `critical_value_threshold`(迁移 `m20260426_000060`) +- **Entity**: `crates/erp-health/src/entity/critical_value_threshold.rs` +- **Handler**: `crates/erp-health/src/handler/critical_value_threshold_handler.rs` +- **Service**: `crates/erp-health/src/service/critical_value_threshold_service.rs` +- **路由**: `/health/critical-value-thresholds`(CRUD 已注册,见 `module.rs` 行 602-609) +- **权限**: `health.critical-value-thresholds.list` + `health.critical-value-thresholds.manage`(已定义) +- **种子数据**: 8 条默认记录已存在 + +现有表字段:`indicator`、`direction`(高/低)、`threshold_value`、`level`(警告/危急)、`department`、`age_min`、`age_max`、`is_active` + 标准审计字段。 + +### 5.2 需补充的工作 + +| 工作项 | 说明 | +|--------|------| +| 补充 warning 级别种子数据 | 现有种子仅有 critical 级别,需新增 warning 级别阈值(血压 140/90、心率 100/60、血糖 6.1/7.8) | +| 新增患者端只读 API | `GET /health/critical-value-thresholds/public` — 认证即可,无需管理权限,返回当前租户所有 `is_active` 阈值 | +| Web 端阈值管理 UI | 在告警规则管理页面增加"阈值配置"Tab,复用现有 CRUD API | + +### 5.3 前端改造 + +**小程序改造流程:** + +1. App 启动时调用 `GET /health/critical-value-thresholds/public` 获取全量阈值 +2. 存入 `Taro.storage`(key: `health_thresholds`,TTL: 24h) +3. 体征页 `health/index.tsx` 从缓存读取阈值替代 `REF_RANGES` 和判断逻辑 +4. 输入页 `pkg-health/input/index.tsx` 从缓存读取阈值替代 `WARN_THRESHOLDS` +5. 缓存未命中时使用内置默认值(与当前硬编码值一致) + +### 5.4 补充种子数据(warning 级别) + +新增迁移脚本插入 warning 级别阈值(现有 critical 级别已由 `m20260426_000060` 种子覆盖): + +| indicator | level | direction | threshold_value | 说明 | +|-----------|-------|-----------|-----------------|------| +| blood_pressure_systolic | warning | high | 140 | 收缩压参考上限 | +| blood_pressure_diastolic | warning | high | 90 | 舒张压参考上限 | +| heart_rate | warning | high | 100 | 心率参考上限 | +| heart_rate | warning | low | 60 | 心率参考下限 | +| blood_sugar_fasting | warning | high | 6.1 | 空腹血糖参考上限 | +| blood_sugar_postprandial | warning | high | 7.8 | 餐后血糖参考上限 | + +共 6 条 warning 级别配置,与现有 8 条 critical 级别共同构成完整的阈值体系。 + +--- + +## 6. 跨切面关注点 + +### 6.1 错误处理 + +- 所有新建 API 遵循现有 `AppError` → `ApiResponse` 错误链 +- 前端调用失败时降级显示:统计卡片显示 "—",列表显示"暂无数据" +- 系统健康检查 API 本身失败时,前端显示"检查中..."而非假数据 + +### 6.2 测试策略 + +| 层级 | 测试内容 | 工具 | +|------|---------|------| +| 后端单元测试 | 每个 handler + service 函数 | `#[tokio::test]` | +| 后端集成测试 | 5 个新 API 的完整请求/响应 | Testcontainers | +| 前端单元测试 | `useDictionary` hook + 常量导出一致性 | vitest | +| 前端组件测试 | 工作台页面 loading/empty/data 三态渲染 | vitest + testing-library | +| E2E 测试 | 工作台页面加载无硬编码假数据 | playwright | + +### 6.3 迁移与部署顺序 + +三条轨道互不依赖,但建议按以下顺序部署: + +1. **轨道 2(常量统一)** — 纯前端重构,零后端改动,可立即部署 +2. **轨道 3(医疗阈值)** — 复用现有表,仅需补充种子数据 + 新增患者端只读 API,独立部署 +3. **轨道 1(工作台 API)** — 需要新建 5 个 API,最后部署 + +轨道 1 内部的 API 实现顺序: +1. API-5 内容统计(最简单,单表 COUNT) +2. API-4 积分动态(单表 JOIN) +3. API-3 模块状态(读取 AppState) +4. API-2 用户活跃度(多表聚合) +5. API-1 系统健康检查(外部连接检测) + +--- + +## 7. 验证清单 + +### 轨道 1 验证 +- [ ] AdminDashboard 系统健康条数据来自 API(不出现假数据) +- [ ] AdminDashboard 用户活跃度数据来自 API +- [ ] AdminDashboard 模块状态来自 API +- [ ] OperatorWorkbench 积分动态来自 API +- [ ] OperatorWorkbench 内容矩阵来自 API +- [ ] OperatorWorkbench 待办来自 actionInbox +- [ ] 全部 5 个新 API 在 Swagger UI 可测试 +- [ ] `cargo test --workspace` 全部通过 +- [ ] `pnpm build` 前端构建通过 + +### 轨道 2 验证 +- [ ] `SEVERITY_CONFIG` 仅在 `constants/health.ts` 定义一次,其余 4 处改为引用 +- [ ] `GENDER_OPTIONS` 仅在 `constants/health.ts` 定义一次 +- [ ] 6 个字典编码在后端种子数据中存在 +- [ ] `useDictionary` hook 可正常获取字典数据 +- [ ] 小程序 `useDict` hook + storage 缓存工作正常 + +### 轨道 3 验证 +- [ ] `critical_value_threshold` 表已有 + warning 级别种子数据 6 条正确插入 +- [ ] 患者端只读 API `GET /health/critical-value-thresholds/public` 可正常访问 +- [ ] 小程序体征页使用 API 阈值(非硬编码) +- [ ] 小程序输入页使用 API 阈值(非硬编码) +- [ ] 离线场景下降级到内置默认值 diff --git a/docs/superpowers/specs/2026-05-02-health-manager-workbench-design.md b/docs/superpowers/specs/2026-05-02-health-manager-workbench-design.md new file mode 100644 index 0000000..1b8018c --- /dev/null +++ b/docs/superpowers/specs/2026-05-02-health-manager-workbench-design.md @@ -0,0 +1,297 @@ +# 健康管家工作台 — 方案 B「工作流驱动」设计规格 + +> 日期: 2026-05-02 | 状态: 设计中(评审后修订 v2) +> 原型参考: `_temp/workbench-health-manager-B.html` +> 评审记录: 发现 7 CRITICAL + 5 IMPORTANT,已全部修复 + +## 1. 背景与目标 + +### 1.1 为什么要重新设计 + +现有工作台(方案 A/D/E/F)基于「临床医生管理透析团队」的假设设计。实际用户场景已明确: + +- 机构已有 HIS + 专用血透系统,HMS 不碰临床透析流程 +- HMS 定位是**患者与机构之间的纽带**:日常健康数据采集、随访干预、积分运营、健康科普 +- 工作台的第一用户是**健康管家/随访护士**,不是做透析的医生 + +### 1.2 目标 + +| 指标 | 当前 | 目标 | +|------|------|------| +| 健康管家每日任务处理效率 | 无专门工作台,散落在各页面 | 统一入口,一件一件处理 | +| 任务遗漏率 | 未知(无追踪) | ≤2%(系统强制排序 + 超时提醒) | +| AI 建议触达率 | 有 AI 洞察面板但无强制处理流程 | 任务流中自动注入,100% 触达 | +| 平均任务响应时间 | 无数据 | 危急 ≤30min,紧急 ≤2h,普通 ≤24h | + +### 1.3 范围 + +**分两期实施:** + +| 阶段 | 任务类型 | 数据源状态 | +|------|---------|-----------| +| **Phase 1(本期)** | 体征告警、AI 建议待审、到期随访 | ✅ 现有 `action_inbox_service.rs` 已聚合 | +| **Phase 2(后续)** | 患者咨询、积分审批、体征少报提醒、干预评估 | ⏳ 需扩展数据源或新建调度 | + +- **本期(Phase 1)**:健康管家/随访护士角色,3 种任务类型,三栏工作流布局 +- **不在本期**:医生角色、运营角色、方案 A/C、Phase 2 任务类型 + +## 2. 角色定义 — 健康管家「刘小燕」 + +### 2.1 人物画像 + +| 维度 | 描述 | +|------|------| +| 姓名 | 刘小燕 | +| 职位 | 主管护师 / 健康管家 | +| 年龄 | 32 岁 | +| 工作经验 | 8 年护理,3 年健康管理 | +| 技术水平 | 熟练使用手机 App,PC 端基本操作无障碍 | +| 日均任务量 | 20-30 项(随访 8-12、体征审核 4-6、AI 建议 3-5、其他 3-5) | + +### 2.2 典型工作日 + +``` +08:00 打开工作台,按优先级处理紧急体征告警 +08:30 逐个处理血压/血糖异常告警(电话随访 + 记录) +10:00 处理 AI 建议待审(审核并采纳或转给医生) +14:00 处理到期随访任务(电话/微信随访 + 记录) +16:00 回复患者咨询、处理积分兑换等(跳转对应模块) +17:00 检查今日完成情况,确认无遗漏后下班 +``` + +### 2.3 核心痛点 + +1. **任务分散**:随访、体征、AI 建议散落在不同菜单,频繁切换 +2. **优先级不清**:不知道该先处理哪个,容易遗漏紧急告警 +3. **上下文缺失**:处理一个告警时需要单独打开患者详情查看历史 +4. **AI 建议利用率低**:AI 面板在角落,不强制处理,容易被忽略 + +## 3. 信息架构 — 三栏布局 + +### 3.1 布局结构 + +**注意**:侧边栏由全局布局提供(`apps/web/src/layouts/`),工作台页面内部只有两栏(任务队列 + 详情面板)。 + +``` +┌──────────────────────────────────────────────────────────┐ +│ 全局侧边栏(已有,不动) │ 工作台页面内部 │ +│ │ │ +│ ... │ ┌────────────┬──────────────┐ │ +│ ⚡ 工作流(高亮) │ │ 任务队列 │ 详情处理面板 │ │ +│ 📋 随访管理 │ │ 340px │ flex:1 │ │ +│ 🩺 体征监测 │ │ │ │ │ +│ 💬 患者咨询 │ │ 头部统计 │ 患者信息栏 │ │ +│ ... │ │ Tab 切换 │ 异常数据 │ │ +│ │ │ 任务列表 │ AI 建议 │ │ +│ │ │ │ 互动记录 │ │ +│ │ │ │ 操作按钮 │ │ +│ │ └────────────┴──────────────┘ │ +└──────────────────────────────────────────────────────────┘ +``` + +### 3.2 两栏职责 + +| 区域 | 职责 | 交互 | +|------|------|------| +| **任务队列** | 按优先级排列的待处理任务 + 统计 | 点击选中 → 右侧加载详情 | +| **详情面板** | 当前任务的完整上下文 + 操作 | 处理完成后自动跳转下一任务 | + +### 3.3 全局侧边栏菜单调整 + +在现有全局侧边栏中,将「工作台」菜单项更名为「工作流」,点击进入方案 B 的三栏工作台。其他菜单项不变。 + +## 4. 组件清单 + +### 4.1 任务队列组件 + +| 组件 | 说明 | 数据源 | +|------|------|--------| +| **QueueHeader** | 队列统计(待处理/已完成) | `GET /health/action-inbox/stats`(现有) | +| **QueueTabs** | Tab 切换:待处理 / 已完成 | 前端状态 | +| **TaskItem** | 单个任务卡片:优先级圆点 + 标题 + 元信息 + 标签 + 时间 | 现有 `list_action_inbox` API | +| **TaskList** | 可滚动的任务列表容器 | Ant Design `List` + `InfiniteScroll` | + +### 4.2 详情面板组件 + +| 组件 | 适用任务类型 | 说明 | 数据源 | +|------|-------------|------|--------| +| **PatientBar** | 全部 | 患者头像 + 姓名 + 年龄 + 科室 + 病史标签 | `GET /health/patients/:id`(现有) | +| **VitalAlertDetail** | 体征异常 | 异常数值 + 近 7 天趋势图 + 参考范围 | `GET /health/action-inbox/:source_ref/thread`(现有)+ `GET /health/vital-signs/trends`(现有) | +| **AiSuggestionCard** | AI 建议 | 风险评估 + 建议措施 + 采纳/拒绝操作 | thread API(现有) | +| **FollowUpForm** | 到期随访 | 随访表单 + 上次随访摘要 | thread API(现有) | +| **InteractionTimeline** | 全部 | 患者近期互动记录 | `GET /health/action-inbox/:source_ref/thread`(现有) | +| **ActionBar** | 全部 | 底部操作按钮(因任务类型不同而变化) | — | + +### 4.3 任务类型与优先级(Phase 1 仅 3 种) + +| 优先级 | 类型 | 自动生成规则 | 目标响应时间 | 数据源 | +|--------|------|-------------|-------------|--------| +| **危急 (P0)** | 体征危急值告警 | 告警系统生成 | ≤30 分钟 | `alerts` 表 | +| **紧急 (P1)** | AI 建议待审 | AI 分析引擎生成 | ≤2 小时 | `ai_analysis` 表 | +| **普通 (P2)** | 到期随访 | 随访计划到期 | ≤24 小时 | `follow_up_task` 表 | + +### 4.4 任务 ID 格式 + +沿用现有 `action_inbox_service.rs` 的复合 ID 格式:`"{action_type}:{uuid}"` + +- 告警任务:`"alert:550e8400-..."` +- AI 建议:`"ai_suggestion:660e8400-..."` +- 随访任务:`"follow_up:770e8400-..."` + +操作分发逻辑:解析 `action_type` 前缀,路由到对应的底层服务。 + +## 5. 数据流与排序 + +### 5.1 任务生命周期 + +``` +[自动生成] → [进入队列] → [选中处理] → [执行操作] → [完成/转交] + ↑ │ + └── 超时未处理 → 升级优先级 ←──────────────┘ +``` + +### 5.2 现有 API 复用策略 + +不新建 API 路径。**改造现有 `action-inbox` 系列接口**: + +| 现有 API | 改造内容 | +|---------|---------| +| `GET /health/action-inbox` | 增加按优先级排序(改 SQL ORDER BY) | +| `GET /health/action-inbox/stats` | 增加「已完成」计数 | +| `GET /health/action-inbox/:source_ref/thread` | 增加 patient 上下文字段 | +| `POST /health/alerts/:id/acknowledge` | 沿用(完成告警任务) | +| `POST /health/ai-analysis/:id/review` | 沿用(采纳/拒绝 AI 建议) | +| `POST /health/follow-up/:id/complete` | 沿用(完成随访任务) | + +详情面板的患者信息、趋势图通过**前端并行调用**现有 API 获取: +- `GET /health/patients/:id` → PatientBar +- `GET /health/vital-signs/trends?patient_id=&days=7` → VitalAlertDetail +- `GET /health/action-inbox/:source_ref/thread` → InteractionTimeline + 核心任务数据 + +前端用 `Promise.allSettled` 并行请求,加载中显示 Skeleton。 + +### 5.3 排序规则修复 + +现有 SQL 使用 `created_at DESC`(新的在前),需改为 `created_at ASC`(早的在前,FIFO),与设计规格一致。优先级映射从 3 级扩展为 4 级: + +```sql +ORDER BY + CASE priority_raw + WHEN 'critical' THEN 0 -- P0 新增 + WHEN 'high' THEN 1 + WHEN 'urgent' THEN 1 + WHEN 'medium' THEN 2 + ELSE 3 + END, + created_at ASC -- 改为 ASC +``` + +## 6. 交互流程 — 处理一个任务的完整闭环 + +以「张伟 — 血压危急值 188/102」为例: + +``` +1. 进入工作台 + └→ 任务队列自动加载,张伟的告警排在最前(P0) + +2. 点击任务 + └→ 右侧详情面板加载(并行请求患者信息 + thread + 趋势): + - 患者信息栏:张伟 / 男 / 58岁 / 血透中心 / 高血压3级 + - 异常数据:收缩压 188 / 舒张压 102 / 心率 92 + - 近 7 天收缩压趋势图(柱状图) + - 近期互动记录 + +3. 执行操作 + └→ 点击「立即电话随访」 + └→ 弹出电话随访表单(预设患者电话号码) + └→ 填写随访记录:确认用药 / 伴随症状 / 处理建议 + └→ 提交 → 调用 POST /health/alerts/:id/acknowledge + +4. 任务完成 + └→ 任务从「待处理」移至「已完成」 + └→ 队列自动选中下一个任务 + └→ 右侧面板加载新任务的详情 + +5. 可选操作 + └→ 「转给医生」:更改 assigned_to → 对应医生(需 migration 加字段,Phase 2) + └→ 「稍后处理」:本地状态,置底并标记延迟时间 +``` + +### 6.1 操作按钮矩阵(Phase 1) + +| 任务类型 | 主操作 | 次要操作 | 底层 API | +|---------|--------|---------|---------| +| 体征异常告警 | 记录处理结果 + 确认 | 稍后处理 | `POST /health/alerts/:id/acknowledge` | +| AI 建议待审 | 采纳 / 拒绝 | 稍后处理 | `POST /health/ai-analysis/:id/review` | +| 到期随访 | 开始随访(电话/线上/面访) | 稍后处理 | `POST /health/follow-up/:id/complete` | + +### 6.2 空状态处理 + +- 队列为空时:显示「所有任务已处理完毕」+ 今日完成统计 +- 详情面板未选中任务时:显示空态引导「从左侧选择一个任务开始处理」 +- 加载中:Skeleton 占位 + +### 6.3 前端状态管理 + +使用 Zustand store `useWorkbenchStore`: + +```typescript +interface WorkbenchState { + tasks: ActionItem[]; // 任务列表 + selectedTaskId: string | null; // 当前选中任务 + completedCount: number; // 今日完成数 + tab: 'pending' | 'completed'; // 当前 Tab + + selectTask: (id: string) => void; + completeTask: (id: string) => Promise; + refreshTasks: () => Promise; +} +``` + +实时刷新策略:Phase 1 使用 30 秒轮询(`setInterval` + `refreshTasks`),Phase 2 考虑升级为 SSE。 + +## 7. 与现有系统的关系 + +### 7.1 改造策略 + +改造现有的 `Home.tsx` 工作台页面,替换当前仪表盘布局为方案 B 的任务队列 + 详情面板两栏布局。 + +**不新建页面**,在现有路由 `/health` 上直接改造。侧边栏不变。 + +### 7.2 涉及文件 + +| 文件 | 改动类型 | 说明 | +|------|---------|------| +| `apps/web/src/pages/health/Home.tsx` | **重写** | 从仪表盘布局改为任务队列 + 详情面板 | +| `apps/web/src/pages/health/components/TaskQueue.tsx` | **新增** | 任务队列组件 | +| `apps/web/src/pages/health/components/TaskDetail.tsx` | **新增** | 详情面板组件 | +| `apps/web/src/pages/health/components/PatientBar.tsx` | **新增** | 患者信息栏 | +| `apps/web/src/stores/workbenchStore.ts` | **新增** | Zustand store | +| `crates/erp-health/src/service/action_inbox_service.rs` | **改造** | 修复排序规则 + 扩展优先级 | +| `crates/erp-health/src/handler/device_reading_handler.rs` | **改造** | alert acknowledge 增加处理记录 | + +### 7.3 数据库变更 + +Phase 1 **无需新建表**。任务从现有 3 张表聚合: +- 体征告警 ← `alerts` 表(未确认的告警) +- AI 建议 ← `ai_analysis` 表(状态=pending_review) +- 到期随访 ← `follow_up_task` 表(状态=pending + 到期日<=今天) + +聚合逻辑沿用现有 `action_inbox_service.rs` 的 UNION ALL 方案。 + +### 7.4 权限 + +沿用现有权限码:`health.action-inbox.list` + `health.action-inbox.manage`。 + +### 7.5 验收标准 + +- [ ] 工作台展示 3 种任务类型(告警、AI 建议、随访),按优先级排序 +- [ ] 点击任务,右侧详情面板展示完整上下文(患者信息 + 原始数据 + 趋势 + 互动记录) +- [ ] 告警任务可确认并记录处理结果 +- [ ] AI 建议可采纳或拒绝 +- [ ] 随访任务可完成并填写随访记录 +- [ ] 任务完成后自动选中下一个 +- [ ] 空状态有合理展示 +- [ ] 30 秒自动刷新任务列表 +- [ ] `cargo check` 通过 +- [ ] 浏览器中实际操作验证通过 diff --git a/docs/superpowers/specs/2026-05-03-project-analysis-brainstorm-design.md b/docs/superpowers/specs/2026-05-03-project-analysis-brainstorm-design.md index 99622b5..780e471 100644 --- a/docs/superpowers/specs/2026-05-03-project-analysis-brainstorm-design.md +++ b/docs/superpowers/specs/2026-05-03-project-analysis-brainstorm-design.md @@ -1,8 +1,10 @@ # HMS 健康管理平台 — 全项目深度分析与多专家组头脑风暴 -> 日期: 2026-05-03 | 类型: 分析报告 | 数据截止: commit 554 | 状态: 草稿 +> 日期: 2026-05-03 | 类型: 分析报告 | 数据截止: commit 555 | 状态: 定稿 +> +> **如何使用本文档:** §1-2 是全景扫描,所有人应读。§3-7 是专家组深度分析,按需阅读。§8-10 是行动矩阵和风险传导,执行时参考。每个专家组独立成章,可跳读。 -**TL;DR:** HMS 项目整体健康度 B+。架构边界满分,安全基础扎实,文档体系完善。核心风险:前端测试 <5%、55% 事件孤立、审计整改率 8%、73 个未提交文件。建议立即止血(P0)→ 本周补短板(P1)→ 本月治本(P2)。 +**TL;DR:** HMS 项目整体健康度 B+。架构边界满分,安全基础扎实,文档体系完善。核心风险:前端测试 <5%、55% 事件孤立、审计整改率 8%、73 个未提交文件(截至文档编写时)。建议立即止血(P0)→ 本周补短板(P1)→ 本月治本(P2)。 --- @@ -10,7 +12,7 @@ ### 为什么做这次分析 -HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已进入功能基本完整的阶段。2026-04-30 完成的全系统审计发现 25 个问题(2 CRITICAL + 3 HIGH + 8 MEDIUM + 12 LOW),但审计后 3 天整改率仅 8%。此时需要一个全面的「体检」来: +HMS 项目经过 3 周密集开发(555 次提交,86 份设计规格),已进入功能基本完整的阶段。2026-04-30 完成的全系统审计发现 25 个问题(2 CRITICAL + 3 HIGH + 8 MEDIUM + 12 LOW),但审计后 3 天整改率仅 8%。此时需要一个全面的「体检」来: 1. 识别系统性风险而非单点问题 2. 从多维度(架构/安全/前端/质量/管理)交叉验证 @@ -21,10 +23,10 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 | 维度 | 覆盖范围 | |------|---------| | 后端架构 | 17 Rust crates / ~87k 行 / 484 源文件 | -| 前端 Web | 199 文件(113 TSX + 86 TS) | -| 微信小程序 | 113 文件 / 40 页面 / 5 TabBar | -| 数据库 | 103 迁移 / 77+ 表 | -| 文档体系 | 12 页 wiki + 41 specs + 38 plans + 18 discussions | +| 前端 Web | 151 文件(113 TSX + 38 TS) | +| 微信小程序 | 113 文件 / 38 页面 / 5 TabBar | +| 数据库 | 104 迁移 / 77+ 表 | +| 文档体系 | 12 页 wiki + 86 specs + 41 plans + 12 discussions | ### 方法论 @@ -42,14 +44,14 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 | 维度 | 数据 | 评估 | |------|------|------| | Rust 代码 | ~87k 行 / 17 crates / 484 源文件 | 中大型单体,模块边界清晰 | -| Web 前端 | 199 文件 (113 TSX + 86 TS) | 中等规模,API 层完整 | -| 微信小程序 | 113 文件 / 40 页面 / 5 TabBar | 功能丰富,分包合理 | -| 数据库 | 103 迁移 / 77+ 表 | 高频 schema 迭代 | +| Web 前端 | 151 文件 (113 TSX + 38 TS) | 中等规模,API 层完整 | +| 微信小程序 | 113 文件 / 38 页面 / 5 TabBar | 功能丰富,分包合理 | +| 数据库 | 104 迁移 / 77+ 表 | 高频 schema 迭代 | | API 路由 | 328 (8 公开 + 320 受保护) | 全面的业务覆盖 | | 测试 | 772 后端 + 11 前端 | 后端尚可,前端极低 | | 事件系统 | 25 类型 / 44 发布 / 14 消费者 | 设计精良但有孤立事件 | -| 设计文档 | 41 specs / 38 plans | 文档驱动,管理有序 | -| Git 历史 | 554 次提交 | AI 辅助密集开发 | +| 设计文档 | 86 specs / 41 plans | 文档驱动,管理有序 | +| Git 历史 | 555 次提交 | AI 辅助密集开发 | ### 2.2 架构评分矩阵 @@ -62,7 +64,7 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 | 前端架构 | ⭐⭐⭐⭐☆ | API/Store/Hook 三层分离,但零 i18n、低测试覆盖 | | 代码质量 | ⭐⭐⭐⭐☆ | 4 个 TODO、无 FIXME/HACK,但存在 6 个千行文件 | | 测试覆盖 | ⭐⭐⭐☆☆ | 后端 772 测试尚可,前端 <5%、小程序 0% | -| 文档完整性 | ⭐⭐⭐⭐☆ | 12 页 wiki + 41 specs,但 wiki 数据存在不一致 | +| 文档完整性 | ⭐⭐⭐⭐☆ | 12 页 wiki + 86 specs,但 wiki 数据存在不一致 | | 运维成熟度 | ⭐⭐⭐☆☆ | Docker + 原生双轨但不同步,单分支开发,大量未提交文件 | --- @@ -108,6 +110,8 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 **优先级:** P1 — 事件是模块间通信的核心,孤立事件 = 功能缺失 +> **交叉引用:** 安全组(§4.1)指出孤立事件会导致合规声明与实际行为不一致;质量组(§6.1)指出 event.rs 本身也是千行文件,重构时需要测试保障。 + ## 4. 专家组 2:安全与合规 **成员画像:** 安全工程师 + 医疗合规专家 + 隐私保护专家 @@ -133,6 +137,8 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 3. **P1 — AI 密钥统一:** 与 JWT 密钥一样使用 `__MUST_SET_VIA_ENV__` 占位符模式 4. **P2 — 审计日志写入:** fire-and-forget 模式可能导致静默丢失,改为带重试队列 +> **交叉引用:** 质量组(§6.3)指出 `unwrap()` 调用中 PluginHost::db panic 是同一类问题 — 关键路径缺乏容错。 + ### 4.2 医疗数据合规性 **已有能力:** @@ -171,7 +177,9 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 - Web:13 个 Playwright spec(患者旅程、预约、随访、体征、告警等核心流程) - 小程序:4 个 automator spec(商城、健康查看、积分、体征录入) -**核心判断:** 前端测试覆盖率 <5% 是项目最大的质量风险。后端有 772 个测试保障,但前端 163 个文件几乎裸跑。E2E 测试慢且脆弱,不能替代单元测试 — 任何重构都可能引入不可检测的回归。 +**核心判断:** 前端测试覆盖率 <5% 是项目最大的质量风险。后端有 772 个测试保障,但前端 151 个文件几乎裸跑。E2E 测试慢且脆弱,不能替代单元测试 — 任何重构都可能引入不可检测的回归。 + +> **交叉引用:** 4 个专家组独立得出「前端测试是最大短板」的结论 — 架构组(§3)、质量组(§6)、管理组(§7) 均标记。风险传导链(§10)说明低测试覆盖放大了所有其他重构风险。 **建议:** 1. **P1 — Store 测试:** 6 个 Zustand Store 是状态管理核心,每个至少 10 个测试 @@ -235,6 +243,8 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 **优先级:** P2 — 在下一次大改动时顺带重构 +> **交叉引用:** 前端组(§5.3)独立发现 Web 侧同样存在 5 个 500+ 行大组件。架构组(§3.1)指出 module.rs 路由注册瓶颈。后端 + 前端共 11 个大文件需要拆分,是跨维度共识。 + ### 6.2 前端错误处理统一化 **重复模式(18+ 文件):** @@ -251,7 +261,9 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 2. 批量替换 18 个文件中的内联模式 3. 建立 ESLint 规则禁止该模式 -**优先级:** P2 +**优先级:** P2 — 依赖 P1 Store 测试完成后再批量重构,避免无安全网的大规模替换 + +> **交叉引用:** 前端组(§5.1)指出前端测试 <5%,意味着这个 18 文件重构在没有测试保障下执行 = 高风险。应先完成 #7 Store 测试。 ### 6.3 TypeScript 类型安全 @@ -296,6 +308,8 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 **建议:** 立即执行分类提交 — 按功能 / 文档 / 配置分组,并清理临时文件。 +> **交叉引用:** 架构组(§3.1)指出 wiki 文档未提交,质量组(§6.3)指出过时注释未更新 — 三组数据均指向「闭环工作法未执行」的系统性问题。风险传导链(§10.2)说明未提交文件是人员单点故障的放大器。 + ### 7.2 审计整改进度 **整改率:** 25 个审计发现中仅 1-2 个已处理(~8%,3 天后) @@ -336,53 +350,121 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 **建议:** 安排 wiki 刷新会话,逐一校对数据。优先级 P2。 +> **交叉引用:** 本文档自身的数据已用代码库实际值校正(见 §1 头部注释)。但 wiki 中仍有 7 处不一致未修。架构组(§3.2)和本组均发现此问题 — 3 组独立验证,可信度高。 + ## 8. 优先级行动矩阵 ### 🔴 P0 — 立即处理(今天) -| # | 行动 | 工作量 | 影响 | -|---|------|--------|------| -| 1 | 提交并推送 73 个未提交文件 | 30 min | 防止工作丢失 | -| 2 | 修复 C2 告警权限码拼写 | 5 min | 消除最后一个 CRITICAL | -| 3 | 确认 C1 晚间血压修复并更新 wiki | 15 min | 关闭审计 CRITICAL | +| # | 行动 | 工作量 | 完成标准 | 阻塞 | +|---|------|--------|---------|------| +| 1 | 提交并推送 73 个未提交文件 | 30 min | `git status` 干净 + `git push` 成功 | 所有后续工作 | +| 2 | 修复 C2 告警权限码拼写 | 5 min | AlertList 页面按钮正常显示 + 无 403 | — | +| 3 | 确认 C1 晚间血压修复并更新 wiki | 15 min | 小程序录入晚间血压 → 数据库有记录 + wiki 症状导航已更新 | — | ### 🟠 P1 — 本周内 -| # | 行动 | 工作量 | 影响 | -|---|------|--------|------| -| 4 | 生产限流 fail-close 配置 | 1h | 生产安全 | -| 5 | 补全 erp-health/event.rs 测试 | 4h | 事件系统可靠性 | -| 6 | 孤立事件清理(14 个) | 8h | 消除功能断裂风险 | -| 7 | 前端 Store 单元测试(6 个 store) | 8h | 前端回归安全网 | -| 8 | 小程序透析模块(审计 HIGH H1) | 16h | 医疗完整性 | -| 9 | 小程序知情同意模块(审计 HIGH H2) | 8h | 合规底线 | -| 10 | health_data_service / action_inbox 补充 tracing | 4h | 可观测性 | +| # | 行动 | 工作量 | 完成 standard | 依赖 | +|---|------|--------|-------------|------| +| 4 | 生产限流 fail-close 配置 | 1h | `config/production.toml` 含 `fail_close = true` + 集成测试验证 | #1 | +| 5 | 补全 erp-health/event.rs 测试 | 4h | 每个消费者至少 1 个正向 + 1 个异常测试 | — | +| 6 | 孤立事件清理(14 个) | 8h | 每个事件有消费者或已删除,孤立率 <10% | #5 | +| 7 | 前端 Store 单元测试(6 个 store) | 8h | 每个 store ≥10 测试,覆盖核心 action 和 selector | #1 | +| 8 | 小程序透析模块(审计 HIGH H1) | 16h | 透析记录查看/新增/编辑页面可用 + 4 个 automator spec | #1 | +| 9 | 小程序知情同意模块(审计 HIGH H2) | 8h | 知情同意签署页面 + 签署后事件正常发布 | #1 | +| 10 | health_data_service / action_inbox 补充 tracing | 4h | 两个文件的关键路径均有 `tracing::info/error` | — | ### 🟡 P2 — 本月内 -| # | 行动 | 工作量 | 影响 | -|---|------|--------|------| -| 11 | 前端错误处理统一化(18 个文件) | 4h | 代码一致性 | -| 12 | 大文件拆分(后端 6 个千行文件) | 16h | 可维护性 | -| 13 | 前端 API 契约测试 | 8h | 集成可靠性 | -| 14 | Wiki 数据一致性刷新 | 4h | 文档可信度 | -| 15 | 数据库迁移治理策略 | 8h | 运维安全 | -| 16 | unwrap() 调用替换(2 处) | 2h | 生产稳定性 | +| # | 行动 | 工作量 | 完成 standard | 依赖 | +|---|------|--------|-------------|------| +| 11 | 前端错误处理统一化(18 个文件) | 4h | 0 处内联错误提取,全部用 `handleApiError` | #7 | +| 12 | 大文件拆分(后端 6 个千行文件) | 16h | 无 >1000 行的 service 文件 | — | +| 13 | 前端 API 契约测试 | 8h | 每个 API 模块 ≥3 测试(URL/Method/参数) | #7 | +| 14 | Wiki 数据一致性刷新 | 4h | wiki 中所有计数与代码库一致 | — | +| 15 | 数据库迁移治理策略 | 8h | 新迁移 <500 行 + 有 dry-run 脚本 | — | +| 16 | unwrap() 调用替换(2 处) | 2h | `grep -r "unwrap()" service/` 返回 0 结果 | — | ### 🟢 P3 — 季度规划 -| # | 行动 | 工作量 | 影响 | -|---|------|--------|------| -| 17 | 编译器警告清理(40 个) | 8h | 代码清洁度 | -| 18 | 前端大组件拆分(20 个文件) | 16h | 可维护性 | -| 19 | TypeScript any 消除 | 8h | 类型安全 | -| 20 | 小程序测试基础设施 | 16h | 小程序质量保障 | -| 21 | Docker 配置与文档对齐 | 4h | 部署一致性 | -| 22 | 数据保留策略设计 | 16h | 合规准备 | +| # | 行动 | 工作量 | 完成 standard | 依赖 | +|---|------|--------|-------------|------| +| 17 | 编译器警告清理(40 个) | 8h | `cargo check 2>&1 | grep warning` 返回 0 | — | +| 18 | 前端大组件拆分(20 个文件) | 16h | 无 >500 行的 TSX 文件 | #12 | +| 19 | TypeScript any 消除 | 8h | `grep -r ": any"` Web 返回 0 / 小程序 <5 | — | +| 20 | 小程序测试基础设施 | 16h | BLE + secure-storage + 核心页面单元测试 | #8 | +| 21 | Docker 配置与文档对齐 | 4h | compose 版本号 = wiki 声明 | #14 | +| 22 | 数据保留策略设计 | 16h | 设计规格文档 + 至少 1 个实体的自动化过期 | — | --- -## 9. 总结与建议 +## 9. 交叉验证:多组共识与分歧 + +> 5 个专家组独立分析同一代码库。本节记录各组发现的交叉印证和矛盾,增强结论可信度。 + +### 9.1 多组共识(≥3 组独立发现同一问题) + +| 共识问题 | 涉及专家组 | 核心引用 | +|----------|-----------|---------| +| **前端测试空白是最大风险** | 架构(§3) + 前端(§5.1) + 质量(§6) + 管理(§7) | 前端 151 文件仅 11 测试(<5%),4 组独立得出"不可接受"结论 | +| **大文件/大组件是可维护性瓶颈** | 架构(§3.1) + 前端(§5.3) + 质量(§6.1) | 后端 6 个千行文件 + 前端 5 个 500+ 行组件,3 组独立标记 | +| **Wiki 数据过时** | 架构(§3.2) + 质量(§6.3) + 管理(§7.3) | 迁移数/权限码/entity 计数多处不一致,3 组各自发现不同不一致 | +| **审计整改执行纪律缺失** | 管理(§7.2) + 安全(§4.1) + 质量(§6) | C2 五分钟改动 3 天未修,限流 fail-open 未配置,说明流程断裂 | + +### 9.2 组间互补(A 组发现问题,B 组补充影响) + +| 问题 | A 组发现 | B 组补充 | +|------|---------|---------| +| 孤立事件 | 架构组(§3.3): 55% 孤立率 | 安全组(§4.1): 事件无消费者 = 功能声明与实际不一致,合规审计时被发现 | +| 限流 fail-open | 安全组(§4.1): Redis 宕机无限流 | 架构组(§3.1): 单体架构下 Redis 是 SPOF,影响全局 | +| 前端错误处理重复 | 质量组(§6.2): 18 文件重复模式 | 前端组(§5.1): 无测试覆盖 = 重构时无法验证不破坏 | +| unwrap() 调用 | 质量组(§6.3): 2 处生产风险 | 安全组(§4.1): PluginHost::db panic = 整个插件子系统不可用 | +| 小程序页面空白 | 前端组(§5): 功能缺失 | 安全组(§4.2): 知情同意缺失 = 医疗合规底线问题 | + +### 9.3 无重大分歧 + +5 组之间无结论性矛盾 — 所有发现方向一致,差异仅在优先级判断上: +- i18n:前端组判 P4,安全组未提及。共识:不阻塞 +- 编译器警告:质量组判 P3,架构组未提及。共识:可延后 + +--- + +## 10. 风险传导链 + +> 风险不是孤立的。以下展示风险如何通过依赖关系互相放大,解释为什么某些 P0 看似小改动却必须立即处理。 + +### 10.1 核心传导路径 + +``` +未提交文件积压 (#1) + ├─→ 本地故障 = 工作丢失 → 阻塞所有后续工作 + └─→ 审计整改延迟 → C2 五分钟改动被搁置 3 天 + +孤立事件 55% (#6) + ├─→ 功能声明 ≠ 实际行为 → 用户不可见的业务断裂 + └─→ 无消费者测试 → 重构 event.rs 时无法验证 (#5) + +前端测试 <5% (#7) + ├─→ 错误处理统一化 (#11) 无法验证 → 技术债固化 + ├─→ 大组件拆分 (#18) 风险极高 → 不敢重构 + └─→ API 契约测试 (#13) 无回归安全网 → 新功能也可能破坏旧功能 + +限流 fail-open (#4) + └─→ Redis 宕机 → 无限流 → 恶意请求打垮服务 → 影响所有用户 +``` + +### 10.2 风险放大效应 + +| 放大器 | 被放大的风险 | 机制 | +|--------|------------|------| +| 低测试覆盖 | 重构风险 | 任何代码变更都无法验证不引入回归 | +| 孤立事件 | 功能验证盲区 | 发布的事件无消费者 = 变更的影响无法观测 | +| 未提交文件 | 人员单点故障 | 唯一开发者本地故障 = 全部工作丢失 | +| Wiki 过时 | 决策误导 | 基于过时 wiki 的新决策可能建立在错误前提上 | + +--- + +## 11. 总结与建议 ### 整体评分:B+(良好,有明确的提升空间) @@ -390,7 +472,7 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 1. **架构设计出色** — 分层清晰、模块边界严格、事件驱动解耦、零循环依赖 2. **安全基础扎实** — Argon2 + AES-256-GCM + PostgreSQL RLS + 审计哈希链 -3. **文档驱动开发** — 41 份设计规格、18 份讨论记录、12 页 wiki,决策有据可查 +3. **文档驱动开发** — 86 份设计规格、12 份讨论记录、12 页 wiki,决策有据可查 4. **API 层完整** — 328 路由覆盖所有业务,前端 API 层与后端 1:1 对应 5. **前端架构清晰** — API / Store / Hook 三层分离,无 god store、无硬编码数据、无 console.log 残留 @@ -398,10 +480,26 @@ HMS 项目经过 3 周密集开发(554 次提交,41 份设计规格),已 1. **前端测试空白** — <5% 覆盖率,任何重构都是冒险 2. **审计整改缓慢** — 3 天仅处理 8% 的审计发现,CRITICAL C2(5 分钟改动)仍未修复 -3. **工作积压** — 55+ 文件未提交,违反闭环工作法 +3. **工作积压** — 73 个文件未提交,违反闭环工作法 4. **孤立事件** — 55% 的事件无消费者,可能是功能断裂的信号 5. **可观测性不足** — 多个核心 service 文件仍缺 tracing 日志(patient_service 已补全) +### 成功度量 + +每个优先级阶段的验收标准: + +| 阶段 | 时间窗口 | 核心指标 | 目标 | +|------|---------|---------|------| +| P0 止血 | 今天 | CRITICAL 项关闭率 | 100%(2/2) | +| P0 止血 | 今天 | 未提交文件数 | 0 | +| P1 补短板 | 本周 | 审计 HIGH 关闭率 | ≥66%(2/3) | +| P1 补短板 | 本周 | 前端 Store 测试数 | ≥60 | +| P1 补短板 | 本周 | 事件孤立率 | <10% | +| P2 治本 | 本月 | 千行 service 文件 | 0 | +| P2 治本 | 本月 | Wiki 数据准确率 | 100% | +| P3 持续 | 季度 | 前端测试覆盖率 | >30% | +| P3 持续 | 季度 | 编译器警告数 | 0 | + ### 下一步方向 当前阶段应遵循 **「止血 → 补短板 → 治本」** 路径: