docs(ai): AI→行动闭环实施计划完成 — 25 Task / 3 Chunk
Chunk 1: 数据层+输出解析(Task 1-11) Chunk 2: 事件集成+BPMN+行动分发(Task 12-19) Chunk 3: 闭环对比+前端展示(Task 20-25)
This commit is contained in:
@@ -1025,3 +1025,688 @@ git push
|
||||
---
|
||||
|
||||
Chunk 1 完成。下一步进入 Chunk 2(事件集成 + BPMN 流程定义 + 行动分发)。
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2: 事件集成 + BPMN 流程 + 行动分发(Phase 2)
|
||||
|
||||
### Task 12: AI 行动分发服务
|
||||
|
||||
**Files:**
|
||||
- Create: `crates/erp-health/src/service/ai_action_dispatcher.rs`
|
||||
|
||||
- [ ] **Step 1: 编写分发器测试(TDD RED)**
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn route_low_risk_to_auto_execute() {
|
||||
let decision = dispatch_decision(&RiskLevel::Low, &SuggestionType::Alert);
|
||||
assert_eq!(decision.execution_mode, ExecutionMode::AutoExecute);
|
||||
assert_eq!(decision.response_timeout, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_medium_risk_to_doctor_review() {
|
||||
let decision = dispatch_decision(&RiskLevel::Medium, &SuggestionType::Followup);
|
||||
assert_eq!(decision.execution_mode, ExecutionMode::DoctorReview);
|
||||
assert_eq!(decision.response_timeout, Some(Duration::from_secs(86400))); // 24h
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_high_risk_to_urgent_confirm() {
|
||||
let decision = dispatch_decision(&RiskLevel::High, &SuggestionType::Alert);
|
||||
assert_eq!(decision.execution_mode, ExecutionMode::UrgentConfirm);
|
||||
assert_eq!(decision.response_timeout, Some(Duration::from_secs(14400))); // 4h
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试确认失败**
|
||||
|
||||
- [ ] **Step 3: 实现行动分发器**
|
||||
|
||||
```rust
|
||||
// crates/erp-health/src/service/ai_action_dispatcher.rs
|
||||
use std::time::Duration;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use erp_core::error::AppResult;
|
||||
use erp_core::events::EventBus;
|
||||
|
||||
/// 执行模式
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ExecutionMode {
|
||||
AutoExecute,
|
||||
DoctorReview,
|
||||
UrgentConfirm,
|
||||
}
|
||||
|
||||
/// 分发决策
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DispatchDecision {
|
||||
pub execution_mode: ExecutionMode,
|
||||
pub response_timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
/// 根据风险等级和建议类型生成执行决策
|
||||
pub fn dispatch_decision(risk_level: &str, suggestion_type: &str) -> DispatchDecision {
|
||||
match risk_level {
|
||||
"low" => DispatchDecision {
|
||||
execution_mode: ExecutionMode::AutoExecute,
|
||||
response_timeout: None,
|
||||
},
|
||||
"medium" => DispatchDecision {
|
||||
execution_mode: ExecutionMode::DoctorReview,
|
||||
response_timeout: Some(Duration::from_secs(86400)), // 24h
|
||||
},
|
||||
"high" => DispatchDecision {
|
||||
execution_mode: ExecutionMode::UrgentConfirm,
|
||||
response_timeout: Some(Duration::from_secs(14400)), // 4h
|
||||
},
|
||||
_ => DispatchDecision {
|
||||
execution_mode: ExecutionMode::DoctorReview,
|
||||
response_timeout: Some(Duration::from_secs(86400)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理 AI 建议事件:根据风险等级分发到不同执行路径
|
||||
pub async fn handle_ai_suggestions(
|
||||
db: &DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
tenant_id: Uuid,
|
||||
analysis_id: Uuid,
|
||||
patient_id: Uuid,
|
||||
doctor_id: Option<Uuid>,
|
||||
suggestions: &[serde_json::Value],
|
||||
risk_level: &str,
|
||||
) -> AppResult<()> {
|
||||
for suggestion in suggestions {
|
||||
let suggestion_type = suggestion["type"].as_str().unwrap_or("alert");
|
||||
let decision = dispatch_decision(risk_level, suggestion_type);
|
||||
|
||||
match decision.execution_mode {
|
||||
ExecutionMode::AutoExecute => {
|
||||
execute_action(db, event_bus, tenant_id, patient_id, suggestion_type, suggestion).await?;
|
||||
}
|
||||
ExecutionMode::DoctorReview | ExecutionMode::UrgentConfirm => {
|
||||
create_pending_action(
|
||||
db, event_bus, tenant_id, patient_id, doctor_id,
|
||||
suggestion_type, suggestion, risk_level, &decision,
|
||||
).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_action(
|
||||
db: &DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
tenant_id: Uuid,
|
||||
patient_id: Uuid,
|
||||
action_type: &str,
|
||||
params: &serde_json::Value,
|
||||
) -> AppResult<()> {
|
||||
match action_type {
|
||||
"alert" => {
|
||||
// 直接发送预警通知
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"health.ai_alert.sent",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"patient_id": patient_id,
|
||||
"alert_type": "ai_risk_warning",
|
||||
"severity": params.get("severity").and_then(|v| v.as_str()).unwrap_or("warning"),
|
||||
"message": params.get("message").and_then(|v| v.as_str()).unwrap_or(""),
|
||||
"source": "ai_analysis",
|
||||
}),
|
||||
);
|
||||
event_bus.publish(event, db).await?;
|
||||
}
|
||||
"followup" | "appointment" => {
|
||||
// 低风险的随访/预约也可以自动创建,但仍通知医生
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"health.ai_action.auto_executed",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"patient_id": patient_id,
|
||||
"action_type": action_type,
|
||||
"params": params,
|
||||
}),
|
||||
);
|
||||
event_bus.publish(event, db).await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_pending_action(
|
||||
db: &DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
tenant_id: Uuid,
|
||||
patient_id: Uuid,
|
||||
doctor_id: Option<Uuid>,
|
||||
action_type: &str,
|
||||
params: &serde_json::Value,
|
||||
risk_level: &str,
|
||||
decision: &DispatchDecision,
|
||||
) -> AppResult<()> {
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"health.ai_action.pending_approval",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"patient_id": patient_id,
|
||||
"doctor_id": doctor_id,
|
||||
"action_type": action_type,
|
||||
"risk_level": risk_level,
|
||||
"timeout_seconds": decision.response_timeout.map(|d| d.as_secs()),
|
||||
"params": params,
|
||||
}),
|
||||
);
|
||||
event_bus.publish(event, db).await?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 运行测试确认通过**
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-health/src/service/ai_action_dispatcher.rs
|
||||
git commit -m "feat(health): AI 行动分发器 — 风险分级路由到自动执行/医生审批/紧急确认"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 13: erp-health 事件消费者 — 订阅 ai.analysis.completed
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/erp-health/src/event.rs`
|
||||
|
||||
- [ ] **Step 1: 在 register_handlers_with_state 中新增消费者**
|
||||
|
||||
在 `crates/erp-health/src/event.rs` 的 `register_handlers_with_state` 函数中,在已有的 `ai.analysis.completed` 通知消费者之后,新增行动分发消费者:
|
||||
|
||||
```rust
|
||||
// 在现有的 ai_analysis_notifier 消费者之后
|
||||
|
||||
// AI→行动闭环消费者
|
||||
let action_db = state.db.clone();
|
||||
let action_event_bus = state.event_bus.clone();
|
||||
let (mut ai_action_rx, ai_action_handle) = event_bus.subscribe_filtered("ai.analysis.".to_string());
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(event) = ai_action_rx.recv().await {
|
||||
if event.event_type != "ai.analysis.completed" { continue; }
|
||||
|
||||
let consumer_id = "ai_action_dispatcher";
|
||||
if let Ok(true) = erp_core::events::is_event_processed(&action_db, event.id, consumer_id).await {
|
||||
continue;
|
||||
}
|
||||
|
||||
let tenant_id = event.tenant_id;
|
||||
let payload = &event.payload;
|
||||
let analysis_id = payload.get("analysis_id").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let patient_id = payload.get("patient_id").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let doctor_id = payload.get("doctor_id").and_then(|v| v.as_str());
|
||||
let risk_level = payload.get("risk_level").and_then(|v| v.as_str()).unwrap_or("medium");
|
||||
let suggestion_count = payload.get("suggestion_count").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||
|
||||
// 只有有建议时才触发行动分发
|
||||
if suggestion_count > 0 {
|
||||
// 从 ai_suggestion 表加载建议列表
|
||||
if let Ok(suggestions) = crate::service::ai_suggestion_loader::load_by_analysis(
|
||||
&action_db, tenant_id, analysis_id
|
||||
).await {
|
||||
let _ = crate::service::ai_action_dispatcher::handle_ai_suggestions(
|
||||
&action_db,
|
||||
&action_event_bus,
|
||||
tenant_id,
|
||||
analysis_id.parse().unwrap_or_default(),
|
||||
patient_id.parse().unwrap_or_default(),
|
||||
doctor_id.and_then(|s| s.parse().ok()),
|
||||
&suggestions,
|
||||
risk_level,
|
||||
).await;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = erp_core::events::mark_event_processed(&action_db, event.id, consumer_id).await;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 创建建议加载辅助函数**
|
||||
|
||||
在 `crates/erp-health/src/service/` 中新建 `ai_suggestion_loader.rs`,从 erp-ai 的 `ai_suggestion` 表读取建议列表:
|
||||
|
||||
```rust
|
||||
// 通过 raw SQL 跨 crate 读取 ai_suggestion 表
|
||||
pub async fn load_by_analysis(
|
||||
db: &DatabaseConnection,
|
||||
tenant_id: Uuid,
|
||||
analysis_id: &str,
|
||||
) -> AppResult<Vec<serde_json::Value>> {
|
||||
// 使用 sea_orm raw query 从 ai_suggestion 表查询
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 运行 `cargo check -p erp-health` 验证**
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-health/src/event.rs crates/erp-health/src/service/ai_suggestion_loader.rs
|
||||
git commit -m "feat(health): 订阅 ai.analysis.completed — 行动分发事件消费者"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 14: BPMN 流程定义 — 审计前置条件
|
||||
|
||||
**前置条件:** Spec 要求 Phase 2 启动前审计 erp-workflow BPMN 功能覆盖度。
|
||||
|
||||
- [ ] **Step 1: 审计 erp-workflow 支持的能力**
|
||||
|
||||
检查 `crates/erp-workflow/src/engine/` 和 `dto.rs`:
|
||||
- `ExclusiveGateway` — 条件分支:确认支持
|
||||
- `UserTask` — 医生审批节点:确认支持
|
||||
- `ServiceTask` — HTTP 调用节点:确认支持
|
||||
- 定时器边界事件:检查 `timeout.rs` 支持情况
|
||||
- 信号事件:检查是否支持
|
||||
|
||||
- [ ] **Step 2: 评估审计结果**
|
||||
|
||||
如果所有能力已支持 → 继续 Task 15-17
|
||||
如果关键能力缺失 → 回退到 Task 15-alt(Action Registry 模式,不依赖 BPMN)
|
||||
|
||||
---
|
||||
|
||||
### Task 15: BPMN 流程定义 — AI 随访流程
|
||||
|
||||
**Files:**
|
||||
- Create: `crates/erp-server/src/seed/ai_followup_workflow.rs`(或通过 API seed)
|
||||
- Reference: `crates/erp-workflow/src/service/definition_service.rs`
|
||||
|
||||
- [ ] **Step 1: 定义随访流程 JSON**
|
||||
|
||||
通过 `DefinitionService::create()` API 创建流程定义。节点设计:
|
||||
|
||||
```
|
||||
StartEvent → ExclusiveGateway(风险分级)
|
||||
→ [low] → ServiceTask(自动创建随访) → EndEvent
|
||||
→ [medium] → UserTask(医生审批) → [approve] → ServiceTask(创建随访) → EndEvent
|
||||
→ [reject] → EndEvent
|
||||
→ [high] → UserTask(紧急确认, 4h超时) → [confirm] → ServiceTask(创建随访) → EndEvent
|
||||
→ [reject] → EndEvent
|
||||
→ [timeout] → ServiceTask(升级通知) → EndEvent
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 通过 seed 或 migration 插入流程定义**
|
||||
|
||||
在 `erp-server/src/main.rs` 启动时检查是否存在 `ai_followup_workflow` 流程定义,不存在则创建。
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-server/src/seed/ai_followup_workflow.rs crates/erp-server/src/main.rs
|
||||
git commit -m "feat(workflow): AI 随访流程 BPMN 定义 — 风险分级 + 医生审批 + 自动创建"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 16: BPMN 流程定义 — AI 预约流程
|
||||
|
||||
**Files:**
|
||||
- Create: `crates/erp-server/src/seed/ai_appointment_workflow.rs`
|
||||
|
||||
- [ ] **Step 1: 定义预约流程 JSON**
|
||||
|
||||
```
|
||||
StartEvent → ExclusiveGateway(风险分级)
|
||||
→ [low] → ServiceTask(推荐预约时段) → EndEvent
|
||||
→ [medium] → UserTask(医生确认时段) → ServiceTask(创建预约) → EndEvent
|
||||
→ [high] → UserTask(紧急预约, 4h超时) → ServiceTask(创建预约) → EndEvent
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Seed 到数据库**
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-server/src/seed/ai_appointment_workflow.rs crates/erp-server/src/main.rs
|
||||
git commit -m "feat(workflow): AI 预约流程 BPMN 定义 — 智能时段推荐"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 17: BPMN 流程定义 — AI 预警流程
|
||||
|
||||
**Files:**
|
||||
- Create: `crates/erp-server/src/seed/ai_alert_workflow.rs`
|
||||
|
||||
- [ ] **Step 1: 定义预警流程 JSON**
|
||||
|
||||
```
|
||||
StartEvent → ExclusiveGateway(风险分级)
|
||||
→ [low] → ServiceTask(双通道发送通知) → EndEvent
|
||||
→ [medium] → ServiceTask(推送给医生) → UserTask(24h响应) → EndEvent
|
||||
→ [timeout] → ServiceTask(自动提醒) → EndEvent
|
||||
→ [high] → ServiceTask(即时推送+仪表盘标红) → UserTask(4h确认) → EndEvent
|
||||
→ [timeout] → ServiceTask(升级上级) → EndEvent
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Seed 到数据库**
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-server/src/seed/ai_alert_workflow.rs crates/erp-server/src/main.rs
|
||||
git commit -m "feat(workflow): AI 预警流程 BPMN 定义 — 分级通知+超时升级"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 18: 行动分发 → 工作流启动集成
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/erp-health/src/service/ai_action_dispatcher.rs`
|
||||
|
||||
- [ ] **Step 1: 在分发器中集成工作流启动**
|
||||
|
||||
在 `create_pending_action` 和需要审批的路径中,调用 `InstanceService::start()` 启动对应的 BPMN 流程实例:
|
||||
|
||||
```rust
|
||||
// 根据 suggestion_type 选择流程定义 key
|
||||
let workflow_key = match action_type {
|
||||
"followup" => "ai_followup_workflow",
|
||||
"appointment" => "ai_appointment_workflow",
|
||||
"alert" => "ai_alert_workflow",
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
// 通过 event 触发工作流启动(解耦,不直接依赖 erp-workflow)
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"workflow.ai_action.start_requested",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"workflow_key": workflow_key,
|
||||
"patient_id": patient_id,
|
||||
"doctor_id": doctor_id,
|
||||
"risk_level": risk_level,
|
||||
"action_type": action_type,
|
||||
"params": params,
|
||||
}),
|
||||
);
|
||||
event_bus.publish(event, db).await?;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在 erp-workflow 事件处理器中消费启动请求**
|
||||
|
||||
在 `crates/erp-workflow/src/module.rs` 的事件注册中,新增 `workflow.ai_action.start_requested` 消费者,调用 `InstanceService::start()`。
|
||||
|
||||
- [ ] **Step 3: 运行 `cargo check` 全 workspace 验证**
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-health/src/service/ai_action_dispatcher.rs crates/erp-workflow/src/module.rs
|
||||
git commit -m "feat(health+workflow): 行动分发→工作流启动集成 — 事件驱动 BPMN 实例化"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 19: Chunk 2 端到端验证
|
||||
|
||||
- [ ] **Step 1: 启动后端服务**
|
||||
|
||||
```bash
|
||||
cd crates/erp-server && cargo run
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 触发 AI 分析**
|
||||
|
||||
POST `/api/v1/ai/analyze/trends` → 确认结构化建议已创建
|
||||
|
||||
- [ ] **Step 3: 验证事件链**
|
||||
|
||||
检查日志确认:
|
||||
- `ai.analysis.completed` 事件已发布
|
||||
- `health.ai_action.pending_approval` 或 `health.ai_alert.sent` 事件已触发
|
||||
- BPMN 流程实例已创建
|
||||
|
||||
- [ ] **Step 4: 验证医生审批**
|
||||
|
||||
通过 Task API 完成医生审批任务,确认随访/预约已创建
|
||||
|
||||
- [ ] **Step 5: 推送**
|
||||
|
||||
```bash
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Chunk 2 完成。下一步进入 Chunk 3(闭环对比 + 前端展示)。
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3: 闭环对比 + 前端展示(Phase 3-4)
|
||||
|
||||
### Task 20: 随访完成 → 再分析触发
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/erp-health/src/event.rs`
|
||||
|
||||
- [ ] **Step 1: 扩展 follow_up.completed 事件消费**
|
||||
|
||||
在现有的 `follow_up.completed` 事件处理中,检查该随访是否由 AI 建议触发(通过 `follow_up_task` 的 `content_template` 字段包含 `ai_suggestion_id` 判断):
|
||||
|
||||
```rust
|
||||
// 在 follow_up.completed 消费者中添加
|
||||
if let Some(ai_suggestion_id) = extract_ai_suggestion_id(&task) {
|
||||
// 发布再分析请求
|
||||
let event = erp_core::events::DomainEvent::new(
|
||||
"ai.reanalysis.requested",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"original_suggestion_id": ai_suggestion_id,
|
||||
"patient_id": task.patient_id,
|
||||
"followup_id": task.id,
|
||||
"trigger": "loop_closure",
|
||||
}),
|
||||
);
|
||||
event_bus.publish(event, db).await?;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在 erp-ai 中消费再分析请求**
|
||||
|
||||
在 `crates/erp-ai/src/handler/mod.rs` 或独立的再分析服务中,订阅 `ai.reanalysis.requested` 事件:
|
||||
1. 加载原始建议的 `baseline_snapshot`
|
||||
2. 提取随访期间的新数据
|
||||
3. 执行趋势分析(带 baseline)
|
||||
4. 生成对比报告
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-health/src/event.rs crates/erp-ai/src/service/reanalysis.rs
|
||||
git commit -m "feat(health+ai): 随访完成→再分析触发 — 闭环核心链路"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 21: 前后对比报告生成
|
||||
|
||||
**Files:**
|
||||
- Create: `crates/erp-ai/src/service/comparison.rs`
|
||||
|
||||
- [ ] **Step 1: 实现对比报告生成**
|
||||
|
||||
```rust
|
||||
// crates/erp-ai/src/service/comparison.rs
|
||||
|
||||
pub struct ComparisonReport {
|
||||
pub baseline: serde_json::Value,
|
||||
pub current: serde_json::Value,
|
||||
pub changes: Vec<MetricChange>,
|
||||
pub overall_trend: TrendDirection,
|
||||
}
|
||||
|
||||
pub struct MetricChange {
|
||||
pub metric: String,
|
||||
pub baseline_value: f64,
|
||||
pub current_value: f64,
|
||||
pub change_percent: f64,
|
||||
pub trend: TrendDirection,
|
||||
}
|
||||
|
||||
pub enum TrendDirection { Improving, Stable, Worsening }
|
||||
|
||||
pub fn generate_comparison(
|
||||
baseline: &serde_json::Value,
|
||||
current: &serde_json::Value,
|
||||
) -> ComparisonReport {
|
||||
// 对比 baseline_summary 和当前指标
|
||||
// 计算每个指标的变化百分比和趋势方向
|
||||
// 综合判断整体趋势
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 在再分析流程中调用对比生成**
|
||||
|
||||
再分析完成后,调用 `generate_comparison()` 生成对比报告,存储到 `ai_analysis.result_metadata` 中,关联到原始建议的 `reanalysis_id`。
|
||||
|
||||
- [ ] **Step 3: 新增对比报告 API 端点**
|
||||
|
||||
`GET /ai/suggestions/{id}/comparison` — 返回前后对比报告
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add crates/erp-ai/src/service/comparison.rs
|
||||
git commit -m "feat(ai): 前后对比报告生成 — 闭环效果评估"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 22: Web 前端 — AI 建议面板
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/AiAnalysisList.tsx`
|
||||
|
||||
- [ ] **Step 1: 在分析详情展开行中增加建议面板**
|
||||
|
||||
在 `AiAnalysisList.tsx` 的展开行中,当分析有结构化建议时:
|
||||
- 显示风险等级标签(低/中/高,不同颜色)
|
||||
- 显示建议列表(类型图标 + 原因 + 状态标签)
|
||||
- 显示执行状态(待审批/已批准/已执行/已拒绝)
|
||||
|
||||
- [ ] **Step 2: 添加建议审批操作按钮**
|
||||
|
||||
对中/高风险建议,显示「批准」/「拒绝」按钮,调用 `POST /api/v1/ai/suggestions/{id}/approve`
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/web/src/pages/health/AiAnalysisList.tsx
|
||||
git commit -m "feat(web): AI 分析详情增加建议面板 — 风险等级+建议列表+审批操作"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 23: Web 前端 — 医生 AI 待办区域
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/PatientDetail.tsx`(或新建 Dashboard widget)
|
||||
|
||||
- [ ] **Step 1: 在患者详情页添加 AI 待办区域**
|
||||
|
||||
显示该患者待审批的 AI 建议,按风险等级排序,显示倒计时(中/高风险有超时)
|
||||
|
||||
- [ ] **Step 2: 添加前后对比报告展示**
|
||||
|
||||
当建议有闭环对比报告时,显示前后指标变化趋势图
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/web/src/pages/health/PatientDetail.tsx
|
||||
git commit -m "feat(web): 患者 AI 待办区域 + 前后对比趋势图"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 24: 小程序 — AI 建议卡片 + 预警通知
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/miniprogram/src/pages/health/index.tsx`
|
||||
- Modify: `apps/miniprogram/src/services/ai-analysis.ts`
|
||||
|
||||
- [ ] **Step 1: 健康页增加 AI 建议卡片**
|
||||
|
||||
在健康页面顶部显示最新的 AI 建议摘要卡片:
|
||||
- 风险等级颜色标签
|
||||
- 建议文字摘要
|
||||
- 点击跳转详情页
|
||||
|
||||
- [ ] **Step 2: 消息页增加风险预警通知类型**
|
||||
|
||||
在消息页面中识别 AI 预警通知,用醒目样式显示
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/miniprogram/src/pages/health/index.tsx apps/miniprogram/src/services/ai-analysis.ts
|
||||
git commit -m "feat(miniprogram): AI 建议卡片 + 风险预警通知"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 25: 全流程端到端验证
|
||||
|
||||
- [ ] **Step 1: 启动全栈服务**
|
||||
|
||||
```bash
|
||||
cd crates/erp-server && cargo run
|
||||
cd apps/web && pnpm dev
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 完整闭环测试**
|
||||
|
||||
1. 发起 AI 趋势分析 → 确认结构化建议生成
|
||||
2. 医生在 Web 端审批建议 → 确认随访计划已创建
|
||||
3. 患者小程序查看 AI 建议卡片 → 确认显示正常
|
||||
4. 模拟随访完成 → 确认再分析触发
|
||||
5. 查看前后对比报告 → 确认趋势对比正确
|
||||
|
||||
- [ ] **Step 3: 运行全量测试**
|
||||
|
||||
```bash
|
||||
cargo check
|
||||
cargo test --workspace
|
||||
pnpm --filter web build
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 最终推送**
|
||||
|
||||
```bash
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**计划完成。** 共 25 个 Task,分 3 个 Chunk:
|
||||
- Chunk 1 (Task 1-11): 数据层 + 输出解析 — 可独立执行
|
||||
- Chunk 2 (Task 12-19): 事件集成 + BPMN + 行动分发 — 依赖 Chunk 1
|
||||
- Chunk 3 (Task 20-25): 闭环对比 + 前端展示 — 依赖 Chunk 2
|
||||
|
||||
Reference in New Issue
Block a user