Files
hms/docs/superpowers/specs/2026-05-01-action-inbox-design.md
iven c208dcc6f5 docs(specs): 7 份设计规格 — 工作台/适老化/硬编码清理/项目分析
新增: 适老化小程序/Action Inbox/统一工作台/医生操作台/
硬编码清理/健康管理台/全项目深度分析报告
2026-05-03 19:32:25 +08:00

402 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 行动收件箱与上下文线程设计规格
> 日期: 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<ActionItem>`
**实现策略**聚合查询不建新表。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 1AI 建议):返回当前租户下所有有权限查看的待办(`ai_suggestion``assigned_to` 字段)
- Future告警/随访):告警有 `assigned_to` 关联的负责医生,随访有 `assigned_to` 字段,届时 API 增加 `scope` 参数(`all` | `mine`
---
## 5. Web 前端设计
### 5.1 入口改造NotificationPanel 扩展
**现有**`apps/web/src/components/NotificationPanel.tsx`(铃铛 + PopoverSSE 推送消息/告警)
**改造**
- 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 Skeleton3 行卡片骨架屏)
- **数据获取失败**Ant Design Result + 重试按钮
- **空列表**:插图 + "暂无待办事项"文案
- **操作按钮请求中**:按钮 loading 状态 + 禁用
### 5.4 实时更新策略
**Phase 1轮询**用户手动刷新或切换页面时重新拉取列表。Drawer 打开时,操作按钮点击后主动调用 `GET /action-inbox/:id/thread` 刷新时间线。
**Phase 2SSE 优化)**:复用 `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后端 API2-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 1BWeb 前端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` — 体征异常反馈(异常→趋势对比→建议→操作)