feat(health): 注入审计日志覆盖所有写入操作
17 个方法全覆盖:patient(4)、appointment(2)、consultation(3)、 follow_up(2)、doctor(3)、health_data(3)。使用 fire-and-forget 模式。
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
//! 预约排班 Service — 预约CRUD、排班管理、日历视图、原子CAS预约
|
//! 预约排班 Service — 预约CRUD、排班管理、日历视图、原子CAS预约
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use erp_core::audit::AuditLog;
|
||||||
|
use erp_core::audit_service;
|
||||||
use erp_core::events::DomainEvent;
|
use erp_core::events::DomainEvent;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect, TransactionTrait};
|
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect, TransactionTrait};
|
||||||
@@ -175,6 +177,12 @@ pub async fn create_appointment(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "appointment.created", "appointment")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(AppointmentResp {
|
Ok(AppointmentResp {
|
||||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||||
appointment_type: m.appointment_type, appointment_date: m.appointment_date,
|
appointment_type: m.appointment_type, appointment_date: m.appointment_date,
|
||||||
@@ -206,6 +214,8 @@ pub async fn update_appointment_status(
|
|||||||
let next_ver = check_version(expected_version, model.version)
|
let next_ver = check_version(expected_version, model.version)
|
||||||
.map_err(|_| HealthError::VersionMismatch)?;
|
.map_err(|_| HealthError::VersionMismatch)?;
|
||||||
|
|
||||||
|
let old_status = model.status.clone();
|
||||||
|
|
||||||
let txn = state.db.begin().await?;
|
let txn = state.db.begin().await?;
|
||||||
|
|
||||||
// 取消时释放排班名额(带下限保护)
|
// 取消时释放排班名额(带下限保护)
|
||||||
@@ -249,6 +259,16 @@ pub async fn update_appointment_status(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "appointment.status_changed", "appointment")
|
||||||
|
.with_resource_id(m.id)
|
||||||
|
.with_changes(
|
||||||
|
Some(serde_json::json!({ "status": old_status })),
|
||||||
|
Some(serde_json::json!({ "status": m.status })),
|
||||||
|
),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(AppointmentResp {
|
Ok(AppointmentResp {
|
||||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||||
appointment_type: m.appointment_type, appointment_date: m.appointment_date,
|
appointment_type: m.appointment_type, appointment_date: m.appointment_date,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! 咨询管理 Service — 会话管理、消息收发、会话关闭、导出
|
//! 咨询管理 Service — 会话管理、消息收发、会话关闭、导出
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use erp_core::audit::AuditLog;
|
||||||
|
use erp_core::audit_service;
|
||||||
use erp_core::events::DomainEvent;
|
use erp_core::events::DomainEvent;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect};
|
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect};
|
||||||
@@ -65,6 +67,12 @@ pub async fn create_session(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "consultation.opened", "consultation")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(SessionResp {
|
Ok(SessionResp {
|
||||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||||
consultation_type: m.consultation_type, status: m.status,
|
consultation_type: m.consultation_type, status: m.status,
|
||||||
@@ -154,6 +162,12 @@ pub async fn close_session(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "consultation.closed", "consultation")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(SessionResp {
|
Ok(SessionResp {
|
||||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||||
consultation_type: m.consultation_type, status: m.status,
|
consultation_type: m.consultation_type, status: m.status,
|
||||||
@@ -308,6 +322,12 @@ pub async fn create_message(
|
|||||||
return Err(HealthError::VersionMismatch);
|
return Err(HealthError::VersionMismatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "consultation.message_sent", "consultation")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(MessageResp {
|
Ok(MessageResp {
|
||||||
id: m.id, session_id: m.session_id, sender_id: m.sender_id,
|
id: m.id, session_id: m.session_id, sender_id: m.sender_id,
|
||||||
sender_role: m.sender_role, content_type: m.content_type,
|
sender_role: m.sender_role, content_type: m.content_type,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! 医护档案 Service — CRUD
|
//! 医护档案 Service — CRUD
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use erp_core::audit::AuditLog;
|
||||||
|
use erp_core::audit_service;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect};
|
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -95,6 +97,13 @@ pub async fn create_doctor(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let model = active.insert(&state.db).await?;
|
let model = active.insert(&state.db).await?;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "doctor.created", "doctor")
|
||||||
|
.with_resource_id(model.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(model_to_resp(model))
|
Ok(model_to_resp(model))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +153,13 @@ pub async fn update_doctor(
|
|||||||
active.version = Set(next_ver);
|
active.version = Set(next_ver);
|
||||||
|
|
||||||
let updated = active.update(&state.db).await?;
|
let updated = active.update(&state.db).await?;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "doctor.updated", "doctor")
|
||||||
|
.with_resource_id(updated.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(model_to_resp(updated))
|
Ok(model_to_resp(updated))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +181,12 @@ pub async fn delete_doctor(
|
|||||||
active.version = Set(next_ver);
|
active.version = Set(next_ver);
|
||||||
active.update(&state.db).await?;
|
active.update(&state.db).await?;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "doctor.deleted", "doctor")
|
||||||
|
.with_resource_id(id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! 随访管理 Service — 随访任务CRUD、随访记录、状态流转
|
//! 随访管理 Service — 随访任务CRUD、随访记录、状态流转
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use erp_core::audit::AuditLog;
|
||||||
|
use erp_core::audit_service;
|
||||||
use erp_core::events::DomainEvent;
|
use erp_core::events::DomainEvent;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::{ActiveValue::Set, QueryOrder, QuerySelect, TransactionTrait};
|
use sea_orm::{ActiveValue::Set, QueryOrder, QuerySelect, TransactionTrait};
|
||||||
@@ -126,6 +128,12 @@ pub async fn create_task(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "follow_up_task.created", "follow_up_task")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(FollowUpTaskResp {
|
Ok(FollowUpTaskResp {
|
||||||
id: m.id, patient_id: m.patient_id, assigned_to: m.assigned_to,
|
id: m.id, patient_id: m.patient_id, assigned_to: m.assigned_to,
|
||||||
follow_up_type: m.follow_up_type, planned_date: m.planned_date,
|
follow_up_type: m.follow_up_type, planned_date: m.planned_date,
|
||||||
@@ -295,6 +303,12 @@ pub async fn create_record(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "follow_up_record.created", "follow_up_record")
|
||||||
|
.with_resource_id(record.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(FollowUpRecordResp {
|
Ok(FollowUpRecordResp {
|
||||||
id: record.id, task_id: record.task_id, executed_by: record.executed_by,
|
id: record.id, task_id: record.task_id, executed_by: record.executed_by,
|
||||||
executed_date: record.executed_date, result: record.result,
|
executed_date: record.executed_date, result: record.result,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! 健康数据 Service — 体征记录、化验报告、体检记录、趋势分析
|
//! 健康数据 Service — 体征记录、化验报告、体检记录、趋势分析
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use erp_core::audit::AuditLog;
|
||||||
|
use erp_core::audit_service;
|
||||||
use erp_core::events::DomainEvent;
|
use erp_core::events::DomainEvent;
|
||||||
use num_traits::cast::ToPrimitive;
|
use num_traits::cast::ToPrimitive;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
@@ -106,6 +108,13 @@ pub async fn create_vital_signs(
|
|||||||
version: Set(1),
|
version: Set(1),
|
||||||
};
|
};
|
||||||
let m = active.insert(&state.db).await?;
|
let m = active.insert(&state.db).await?;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "vital_signs.created", "vital_signs")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(VitalSignsResp {
|
Ok(VitalSignsResp {
|
||||||
id: m.id, patient_id: m.patient_id, record_date: m.record_date,
|
id: m.id, patient_id: m.patient_id, record_date: m.record_date,
|
||||||
systolic_bp_morning: m.systolic_bp_morning, diastolic_bp_morning: m.diastolic_bp_morning,
|
systolic_bp_morning: m.systolic_bp_morning, diastolic_bp_morning: m.diastolic_bp_morning,
|
||||||
@@ -274,6 +283,12 @@ pub async fn create_lab_report(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "lab_report.created", "lab_report")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(LabReportResp {
|
Ok(LabReportResp {
|
||||||
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
||||||
report_type: m.report_type, indicators: m.indicators,
|
report_type: m.report_type, indicators: m.indicators,
|
||||||
@@ -424,6 +439,13 @@ pub async fn create_health_record(
|
|||||||
version: Set(1),
|
version: Set(1),
|
||||||
};
|
};
|
||||||
let m = active.insert(&state.db).await?;
|
let m = active.insert(&state.db).await?;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "health_record.created", "health_record")
|
||||||
|
.with_resource_id(m.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(HealthRecordResp {
|
Ok(HealthRecordResp {
|
||||||
id: m.id, patient_id: m.patient_id, record_type: m.record_type,
|
id: m.id, patient_id: m.patient_id, record_type: m.record_type,
|
||||||
record_date: m.record_date, source: m.source,
|
record_date: m.record_date, source: m.source,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! 患者管理 Service — CRUD、家庭成员、标签、医生关联、健康摘要
|
//! 患者管理 Service — CRUD、家庭成员、标签、医生关联、健康摘要
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use erp_core::audit::AuditLog;
|
||||||
|
use erp_core::audit_service;
|
||||||
use erp_core::events::DomainEvent;
|
use erp_core::events::DomainEvent;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect};
|
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect};
|
||||||
@@ -135,6 +137,12 @@ pub async fn create_patient(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "patient.created", "patient")
|
||||||
|
.with_resource_id(model.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(model_to_resp(model))
|
Ok(model_to_resp(model))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,6 +228,12 @@ pub async fn update_patient(
|
|||||||
);
|
);
|
||||||
state.event_bus.publish(event, &state.db).await;
|
state.event_bus.publish(event, &state.db).await;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "patient.updated", "patient")
|
||||||
|
.with_resource_id(updated.id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(model_to_resp(updated))
|
Ok(model_to_resp(updated))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,6 +256,12 @@ pub async fn delete_patient(
|
|||||||
active.version = Set(next_ver);
|
active.version = Set(next_ver);
|
||||||
active.update(&state.db).await?;
|
active.update(&state.db).await?;
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "patient.deleted", "patient")
|
||||||
|
.with_resource_id(id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +328,12 @@ pub async fn manage_patient_tags(
|
|||||||
rel.insert(&state.db).await?;
|
rel.insert(&state.db).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audit_service::record(
|
||||||
|
AuditLog::new(tenant_id, operator_id, "patient.tags_updated", "patient")
|
||||||
|
.with_resource_id(patient_id),
|
||||||
|
&state.db,
|
||||||
|
).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user