feat(core): implement optimistic locking across all entities
Add VersionMismatch error variant and check_version() helper to erp-core. All 13 mutable entities now enforce version checking on update/delete: - erp-auth: user, role, organization, department, position - erp-config: dictionary, dictionary_item, menu, setting, numbering_rule - erp-workflow: process_definition, process_instance, task - erp-message: message, message_subscription Update DTOs to expose version in responses and require version in update requests. HTTP 409 Conflict returned on version mismatch.
This commit is contained in:
@@ -7,6 +7,7 @@ use uuid::Uuid;
|
||||
use crate::dto::SettingResp;
|
||||
use crate::entity::setting;
|
||||
use crate::error::{ConfigError, ConfigResult};
|
||||
use erp_core::error::check_version;
|
||||
use erp_core::events::EventBus;
|
||||
use erp_core::types::Pagination;
|
||||
|
||||
@@ -89,11 +90,17 @@ impl SettingService {
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
if let Some(model) = existing {
|
||||
// Update existing record
|
||||
// Update existing record — 乐观锁校验
|
||||
let next_version = match params.version {
|
||||
Some(v) => check_version(v, model.version).map_err(|_| ConfigError::VersionMismatch)?,
|
||||
None => model.version + 1,
|
||||
};
|
||||
|
||||
let mut active: setting::ActiveModel = model.into();
|
||||
active.setting_value = Set(params.value.clone());
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(operator_id);
|
||||
active.version = Set(next_version);
|
||||
|
||||
let updated = active
|
||||
.update(db)
|
||||
@@ -181,12 +188,14 @@ impl SettingService {
|
||||
}
|
||||
|
||||
/// Soft-delete a setting by setting the `deleted_at` timestamp.
|
||||
/// Performs optimistic locking via version check.
|
||||
pub async fn delete(
|
||||
key: &str,
|
||||
scope: &str,
|
||||
scope_id: &Option<Uuid>,
|
||||
tenant_id: Uuid,
|
||||
operator_id: Uuid,
|
||||
version: i32,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> ConfigResult<()> {
|
||||
let model = setting::Entity::find()
|
||||
@@ -205,10 +214,14 @@ impl SettingService {
|
||||
))
|
||||
})?;
|
||||
|
||||
let next_version =
|
||||
check_version(version, model.version).map_err(|_| ConfigError::VersionMismatch)?;
|
||||
|
||||
let mut active: setting::ActiveModel = model.into();
|
||||
active.deleted_at = Set(Some(Utc::now()));
|
||||
active.updated_at = Set(Utc::now());
|
||||
active.updated_by = Set(operator_id);
|
||||
active.version = Set(next_version);
|
||||
active
|
||||
.update(db)
|
||||
.await
|
||||
@@ -283,6 +296,7 @@ impl SettingService {
|
||||
scope_id: model.scope_id,
|
||||
setting_key: model.setting_key.clone(),
|
||||
setting_value: model.setting_value.clone(),
|
||||
version: model.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user