feat(health+core+ai): 业务流程全面修复 Phase 4-6 + 集成测试修复

Phase 4 — Dead-letter 重试 + 内容推送 + 安全加固:
- erp-core: retry_dead_letters() 定时重试 + PII payload 脱敏
- erp-core: audit_service 哈希链定时验证 + 写入失败告警
- erp-health: article.published 消费者匹配 patient_tag 推送消息
- erp-health: care_plan 事件消费者 (激活通知 + 完成积分)

Phase 5 — 患者批量操作 + 咨询增强 + 护理事件:
- patient: batch_import_patients + bind_by_phone + refer_patient
- consultation: rate_session 满意度评价 (rating + feedback)
- consent: patient_sign_consent 患者端签署
- validation: source 枚举 (7值) + relationship 枚举 (7值) + 12 单元测试

Phase 6 — 咨询文件上传 + AI 引用标注:
- consultation_message: media_id 附件上传端点
- ai_suggestion: references JSONB + [ref:id] 格式引用标注
- AI system prompt 增加引用指令 + output_parser 提取逻辑

迁移: 000161 (media_id + references) + 000162 (rating + feedback)
集成测试: consultation/follow_up/pii_encryption 新字段同步修复
讨论文档: 2026-05-20-business-process-brainstorm.md (10域审核报告)
This commit is contained in:
iven
2026-05-21 01:34:20 +08:00
parent 9033ec8ca2
commit 41a865cf68
37 changed files with 1929 additions and 14 deletions

View File

@@ -162,6 +162,8 @@ mod m20260520_000157_follow_up_source_and_points_rules;
mod m20260521_000158_alerts_add_source_columns;
mod m20260521_000159_patient_phone_and_consent_seed;
mod m20260521_000160_follow_up_task_template_id_and_record_form_data;
mod m20260521_000161_consultation_media_id_and_suggestion_references;
mod m20260521_000162_consultation_session_rating_feedback;
pub struct Migrator;
@@ -331,6 +333,8 @@ impl MigratorTrait for Migrator {
Box::new(m20260521_000158_alerts_add_source_columns::Migration),
Box::new(m20260521_000159_patient_phone_and_consent_seed::Migration),
Box::new(m20260521_000160_follow_up_task_template_id_and_record_form_data::Migration),
Box::new(m20260521_000161_consultation_media_id_and_suggestion_references::Migration),
Box::new(m20260521_000162_consultation_session_rating_feedback::Migration),
]
}
}

View File

@@ -0,0 +1,65 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[derive(DeriveIden)]
enum ConsultationMessage {
Table,
MediaId,
}
#[derive(DeriveIden)]
enum AiSuggestion {
Table,
References,
}
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// 咨询消息添加 media_id 字段(关联媒体库文件)
manager
.alter_table(
Table::alter()
.table(ConsultationMessage::Table)
.add_column(ColumnDef::new(ConsultationMessage::MediaId).uuid().null())
.to_owned(),
)
.await?;
// AI 建议添加 references 字段(存储引用来源 ID 数组)
manager
.alter_table(
Table::alter()
.table(AiSuggestion::Table)
.add_column(ColumnDef::new(AiSuggestion::References).json().null())
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(AiSuggestion::Table)
.drop_column(AiSuggestion::References)
.to_owned(),
)
.await?;
manager
.alter_table(
Table::alter()
.table(ConsultationMessage::Table)
.drop_column(ConsultationMessage::MediaId)
.to_owned(),
)
.await?;
Ok(())
}
}

View File

@@ -0,0 +1,33 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(
r#"
ALTER TABLE consultation_session ADD COLUMN IF NOT EXISTS rating SMALLINT;
ALTER TABLE consultation_session ADD COLUMN IF NOT EXISTS feedback TEXT;
"#,
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
.execute_unprepared(
r#"
ALTER TABLE consultation_session DROP COLUMN IF EXISTS rating;
ALTER TABLE consultation_session DROP COLUMN IF EXISTS feedback;
"#,
)
.await?;
Ok(())
}
}

View File

@@ -129,6 +129,7 @@ async fn test_consultation_message_send() {
session_id: session.id,
content_type: Some("text".to_string()),
content: "您好,有什么可以帮您?".to_string(),
media_id: None,
},
)
.await
@@ -161,6 +162,7 @@ async fn test_consultation_message_list() {
session_id: session.id,
content_type: None,
content: format!("消息{}", i + 1),
media_id: None,
},
)
.await

View File

@@ -17,6 +17,7 @@ fn default_create_task(patient_id: uuid::Uuid) -> CreateFollowUpTaskReq {
related_appointment_id: None,
source_type: None,
source_id: None,
template_id: None,
}
}
@@ -330,6 +331,7 @@ async fn test_follow_up_record_create() {
patient_condition: Some("血压正常".to_string()),
medical_advice: Some("继续服药".to_string()),
next_follow_up_date: Some(chrono::NaiveDate::from_ymd_opt(2026, 6, 16).unwrap()),
form_data: None,
},
)
.await

View File

@@ -264,6 +264,7 @@ async fn test_consultation_message_content_encrypted() {
session_id: session.id,
content_type: Some("text".to_string()),
content: plain_content.to_string(),
media_id: None,
},
)
.await
@@ -376,6 +377,7 @@ async fn test_follow_up_record_fields_encrypted() {
related_appointment_id: None,
source_type: None,
source_id: None,
template_id: None,
},
)
.await
@@ -393,6 +395,7 @@ async fn test_follow_up_record_fields_encrypted() {
patient_condition: Some("血压控制良好".to_string()),
medical_advice: Some("继续服药,定期复查".to_string()),
next_follow_up_date: None,
form_data: None,
},
)
.await