fix(health): 审计修复 — alert 时序 + outbox 幂等性
1. CRITICAL: check_vital_signs_alert 移至 insert 之后执行, 防止数据未持久化就触发告警 2. CRITICAL: send_system 添加 business_id 幂等检查, 防止 outbox relay 重放导致重复消息通知 3. 修复 consent_service unused_mut 警告
This commit is contained in:
@@ -23,7 +23,7 @@ pub async fn list_consents(
|
||||
let limit = page_size.min(100);
|
||||
let offset = page.saturating_sub(1) * limit;
|
||||
|
||||
let mut query = consent::Entity::find()
|
||||
let query = consent::Entity::find()
|
||||
.filter(consent::Column::TenantId.eq(tenant_id))
|
||||
.filter(consent::Column::PatientId.eq(patient_id))
|
||||
.filter(consent::Column::DeletedAt.is_null());
|
||||
|
||||
@@ -86,7 +86,7 @@ pub async fn create_vital_signs(
|
||||
.ok_or(HealthError::PatientNotFound)?;
|
||||
|
||||
let now = Utc::now();
|
||||
check_vital_signs_alert(state, tenant_id, patient_id, operator_id, req.clone()).await;
|
||||
let alert_req = req.clone();
|
||||
let active = vital_signs::ActiveModel {
|
||||
id: Set(Uuid::now_v7()),
|
||||
tenant_id: Set(tenant_id),
|
||||
@@ -112,11 +112,15 @@ pub async fn create_vital_signs(
|
||||
};
|
||||
let m = active.insert(&state.db).await?;
|
||||
|
||||
// 数据持久化成功后再触发危急值检测
|
||||
check_vital_signs_alert(state, tenant_id, patient_id, operator_id, alert_req).await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, operator_id, "vital_signs.created", "vital_signs")
|
||||
.with_resource_id(m.id),
|
||||
&state.db,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(VitalSignsResp {
|
||||
id: m.id, patient_id: m.patient_id, record_date: m.record_date,
|
||||
|
||||
@@ -148,6 +148,9 @@ impl MessageService {
|
||||
}
|
||||
|
||||
/// 系统发送消息(由事件处理器调用)。
|
||||
///
|
||||
/// 幂等保证:当 `business_id` 存在时,若同 tenant + recipient + business_id 的消息已存在,
|
||||
/// 直接返回已有消息,避免 outbox relay 重放导致重复通知。
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn send_system(
|
||||
tenant_id: Uuid,
|
||||
@@ -160,6 +163,27 @@ impl MessageService {
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
event_bus: &EventBus,
|
||||
) -> MessageResult<MessageResp> {
|
||||
// 幂等检查:防止 outbox relay 重放导致重复消息
|
||||
if let Some(bid) = business_id {
|
||||
let existing = message::Entity::find()
|
||||
.filter(message::Column::TenantId.eq(tenant_id))
|
||||
.filter(message::Column::RecipientId.eq(recipient_id))
|
||||
.filter(message::Column::BusinessId.eq(bid))
|
||||
.filter(message::Column::DeletedAt.is_null())
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| MessageError::Validation(e.to_string()))?;
|
||||
|
||||
if let Some(m) = existing {
|
||||
tracing::debug!(
|
||||
message_id = %m.id,
|
||||
business_id = %bid,
|
||||
"消息已存在,跳过重复创建(幂等保护)"
|
||||
);
|
||||
return Ok(Self::model_to_resp(&m));
|
||||
}
|
||||
}
|
||||
|
||||
let id = Uuid::now_v7();
|
||||
let now = Utc::now();
|
||||
let system_user = Uuid::nil();
|
||||
|
||||
Reference in New Issue
Block a user