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预约
|
||||
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::events::DomainEvent;
|
||||
use sea_orm::entity::prelude::*;
|
||||
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;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, operator_id, "appointment.created", "appointment")
|
||||
.with_resource_id(m.id),
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(AppointmentResp {
|
||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||
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)
|
||||
.map_err(|_| HealthError::VersionMismatch)?;
|
||||
|
||||
let old_status = model.status.clone();
|
||||
|
||||
let txn = state.db.begin().await?;
|
||||
|
||||
// 取消时释放排班名额(带下限保护)
|
||||
@@ -249,6 +259,16 @@ pub async fn update_appointment_status(
|
||||
);
|
||||
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 {
|
||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||
appointment_type: m.appointment_type, appointment_date: m.appointment_date,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! 咨询管理 Service — 会话管理、消息收发、会话关闭、导出
|
||||
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::events::DomainEvent;
|
||||
use sea_orm::entity::prelude::*;
|
||||
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;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, operator_id, "consultation.opened", "consultation")
|
||||
.with_resource_id(m.id),
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(SessionResp {
|
||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||
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;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, operator_id, "consultation.closed", "consultation")
|
||||
.with_resource_id(m.id),
|
||||
&state.db,
|
||||
).await;
|
||||
|
||||
Ok(SessionResp {
|
||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||
consultation_type: m.consultation_type, status: m.status,
|
||||
@@ -308,6 +322,12 @@ pub async fn create_message(
|
||||
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 {
|
||||
id: m.id, session_id: m.session_id, sender_id: m.sender_id,
|
||||
sender_role: m.sender_role, content_type: m.content_type,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! 医护档案 Service — CRUD
|
||||
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::{ActiveValue::Set, Condition, QueryOrder, QuerySelect};
|
||||
use uuid::Uuid;
|
||||
@@ -95,6 +97,13 @@ pub async fn create_doctor(
|
||||
};
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -144,6 +153,13 @@ pub async fn update_doctor(
|
||||
active.version = Set(next_ver);
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -165,6 +181,12 @@ pub async fn delete_doctor(
|
||||
active.version = Set(next_ver);
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! 随访管理 Service — 随访任务CRUD、随访记录、状态流转
|
||||
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::events::DomainEvent;
|
||||
use sea_orm::entity::prelude::*;
|
||||
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;
|
||||
|
||||
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 {
|
||||
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,
|
||||
@@ -295,6 +303,12 @@ pub async fn create_record(
|
||||
);
|
||||
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 {
|
||||
id: record.id, task_id: record.task_id, executed_by: record.executed_by,
|
||||
executed_date: record.executed_date, result: record.result,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! 健康数据 Service — 体征记录、化验报告、体检记录、趋势分析
|
||||
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::events::DomainEvent;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use sea_orm::entity::prelude::*;
|
||||
@@ -106,6 +108,13 @@ pub async fn create_vital_signs(
|
||||
version: Set(1),
|
||||
};
|
||||
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 {
|
||||
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,
|
||||
@@ -274,6 +283,12 @@ pub async fn create_lab_report(
|
||||
);
|
||||
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 {
|
||||
id: m.id, patient_id: m.patient_id, report_date: m.report_date,
|
||||
report_type: m.report_type, indicators: m.indicators,
|
||||
@@ -424,6 +439,13 @@ pub async fn create_health_record(
|
||||
version: Set(1),
|
||||
};
|
||||
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 {
|
||||
id: m.id, patient_id: m.patient_id, record_type: m.record_type,
|
||||
record_date: m.record_date, source: m.source,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! 患者管理 Service — CRUD、家庭成员、标签、医生关联、健康摘要
|
||||
|
||||
use chrono::Utc;
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::events::DomainEvent;
|
||||
use sea_orm::entity::prelude::*;
|
||||
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;
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -220,6 +228,12 @@ pub async fn update_patient(
|
||||
);
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -242,6 +256,12 @@ pub async fn delete_patient(
|
||||
active.version = Set(next_ver);
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -308,6 +328,12 @@ pub async fn manage_patient_tags(
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user