feat(core): add audit logging to all mutation operations
Create audit_log SeaORM entity and audit_service::record() helper. Integrate audit recording into 35 mutation endpoints across all modules: - erp-auth: user/role/organization/department/position CRUD (15 actions) - erp-config: dictionary/menu/setting/numbering_rule CRUD (15 actions) - erp-workflow: definition/instance/task operations (8 actions) - erp-message: send/system/mark_read/delete (5 actions) Uses fire-and-forget pattern — audit failures logged but non-blocking.
This commit is contained in:
@@ -7,6 +7,8 @@ use uuid::Uuid;
|
||||
use crate::dto::{DictionaryItemResp, DictionaryResp};
|
||||
use crate::entity::{dictionary, dictionary_item};
|
||||
use crate::error::{ConfigError, ConfigResult};
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::error::check_version;
|
||||
use erp_core::events::EventBus;
|
||||
use erp_core::types::Pagination;
|
||||
@@ -137,6 +139,13 @@ impl DictionaryService {
|
||||
serde_json::json!({ "dictionary_id": id, "code": code }),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary.create", "dictionary")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(DictionaryResp {
|
||||
id,
|
||||
name: name.to_string(),
|
||||
@@ -188,6 +197,13 @@ impl DictionaryService {
|
||||
|
||||
let items = Self::fetch_items(updated.id, tenant_id, db).await?;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary.update", "dictionary")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(DictionaryResp {
|
||||
id: updated.id,
|
||||
name: updated.name.clone(),
|
||||
@@ -234,6 +250,13 @@ impl DictionaryService {
|
||||
serde_json::json!({ "dictionary_id": id }),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary.delete", "dictionary")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -291,6 +314,13 @@ impl DictionaryService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary_item.create", "dictionary_item")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(DictionaryItemResp {
|
||||
id,
|
||||
dictionary_id,
|
||||
@@ -345,6 +375,13 @@ impl DictionaryService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary_item.update", "dictionary_item")
|
||||
.with_resource_id(item_id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(DictionaryItemResp {
|
||||
id: updated.id,
|
||||
dictionary_id: updated.dictionary_id,
|
||||
@@ -385,6 +422,13 @@ impl DictionaryService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary_item.delete", "dictionary_item")
|
||||
.with_resource_id(item_id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ use uuid::Uuid;
|
||||
use crate::dto::{CreateMenuReq, MenuResp};
|
||||
use crate::entity::{menu, menu_role};
|
||||
use crate::error::{ConfigError, ConfigResult};
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::error::check_version;
|
||||
use erp_core::events::EventBus;
|
||||
|
||||
@@ -156,6 +158,13 @@ impl MenuService {
|
||||
serde_json::json!({ "menu_id": id, "title": req.title }),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.create", "menu")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(MenuResp {
|
||||
id,
|
||||
parent_id: req.parent_id,
|
||||
@@ -225,6 +234,13 @@ impl MenuService {
|
||||
Self::assign_roles(id, role_ids, tenant_id, operator_id, db).await?;
|
||||
}
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.update", "menu")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(MenuResp {
|
||||
id: updated.id,
|
||||
parent_id: updated.parent_id,
|
||||
@@ -275,6 +291,13 @@ impl MenuService {
|
||||
serde_json::json!({ "menu_id": id }),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.delete", "menu")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ use uuid::Uuid;
|
||||
use crate::dto::{CreateNumberingRuleReq, GenerateNumberResp, NumberingRuleResp};
|
||||
use crate::entity::numbering_rule;
|
||||
use crate::error::{ConfigError, ConfigResult};
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::error::check_version;
|
||||
use erp_core::events::EventBus;
|
||||
use erp_core::types::Pagination;
|
||||
@@ -107,6 +109,13 @@ impl NumberingService {
|
||||
serde_json::json!({ "rule_id": id, "code": req.code }),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "numbering_rule.create", "numbering_rule")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(NumberingRuleResp {
|
||||
id,
|
||||
name: req.name.clone(),
|
||||
@@ -171,6 +180,13 @@ impl NumberingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "numbering_rule.update", "numbering_rule")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(Self::model_to_resp(&updated))
|
||||
}
|
||||
|
||||
@@ -209,6 +225,13 @@ impl NumberingService {
|
||||
serde_json::json!({ "rule_id": id }),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "numbering_rule.delete", "numbering_rule")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ use uuid::Uuid;
|
||||
use crate::dto::SettingResp;
|
||||
use crate::entity::setting;
|
||||
use crate::error::{ConfigError, ConfigResult};
|
||||
use erp_core::audit::AuditLog;
|
||||
use erp_core::audit_service;
|
||||
use erp_core::error::check_version;
|
||||
use erp_core::events::EventBus;
|
||||
use erp_core::types::Pagination;
|
||||
@@ -117,6 +119,13 @@ impl SettingService {
|
||||
}),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "setting.upsert", "setting")
|
||||
.with_resource_id(updated.id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(Self::model_to_resp(&updated))
|
||||
} else {
|
||||
// Insert new record
|
||||
@@ -151,6 +160,13 @@ impl SettingService {
|
||||
}),
|
||||
));
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "setting.upsert", "setting")
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(Self::model_to_resp(&inserted))
|
||||
}
|
||||
}
|
||||
@@ -217,6 +233,7 @@ impl SettingService {
|
||||
let next_version =
|
||||
check_version(version, model.version).map_err(|_| ConfigError::VersionMismatch)?;
|
||||
|
||||
let setting_id = model.id;
|
||||
let mut active: setting::ActiveModel = model.into();
|
||||
active.deleted_at = Set(Some(Utc::now()));
|
||||
active.updated_at = Set(Utc::now());
|
||||
@@ -227,6 +244,13 @@ impl SettingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "setting.delete", "setting")
|
||||
.with_resource_id(setting_id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user