chore: apply cargo fmt across workspace and update docs
- Run cargo fmt on all Rust crates for consistent formatting - Update CLAUDE.md with WASM plugin commands and dev.ps1 instructions - Update wiki: add WASM plugin architecture, rewrite dev environment docs - Minor frontend cleanup (unused imports)
This commit is contained in:
@@ -2,5 +2,5 @@ pub mod dictionary;
|
||||
pub mod dictionary_item;
|
||||
pub mod menu;
|
||||
pub mod menu_role;
|
||||
pub mod setting;
|
||||
pub mod numbering_rule;
|
||||
pub mod setting;
|
||||
|
||||
@@ -22,9 +22,7 @@ pub enum ConfigError {
|
||||
impl From<sea_orm::TransactionError<ConfigError>> for ConfigError {
|
||||
fn from(err: sea_orm::TransactionError<ConfigError>) -> Self {
|
||||
match err {
|
||||
sea_orm::TransactionError::Connection(err) => {
|
||||
ConfigError::Validation(err.to_string())
|
||||
}
|
||||
sea_orm::TransactionError::Connection(err) => ConfigError::Validation(err.to_string()),
|
||||
sea_orm::TransactionError::Transaction(inner) => inner,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,14 +97,8 @@ where
|
||||
{
|
||||
require_permission(&ctx, "dictionary.update")?;
|
||||
|
||||
let dictionary = DictionaryService::update(
|
||||
id,
|
||||
ctx.tenant_id,
|
||||
ctx.user_id,
|
||||
&req,
|
||||
&state.db,
|
||||
)
|
||||
.await?;
|
||||
let dictionary =
|
||||
DictionaryService::update(id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?;
|
||||
|
||||
Ok(Json(ApiResponse::ok(dictionary)))
|
||||
}
|
||||
@@ -185,14 +179,8 @@ where
|
||||
req.validate()
|
||||
.map_err(|e| AppError::Validation(e.to_string()))?;
|
||||
|
||||
let item = DictionaryService::add_item(
|
||||
dict_id,
|
||||
ctx.tenant_id,
|
||||
ctx.user_id,
|
||||
&req,
|
||||
&state.db,
|
||||
)
|
||||
.await?;
|
||||
let item =
|
||||
DictionaryService::add_item(dict_id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?;
|
||||
|
||||
Ok(Json(ApiResponse::ok(item)))
|
||||
}
|
||||
@@ -214,20 +202,12 @@ where
|
||||
require_permission(&ctx, "dictionary.update")?;
|
||||
|
||||
// 验证 item_id 属于 dict_id
|
||||
let item = DictionaryService::update_item(
|
||||
item_id,
|
||||
ctx.tenant_id,
|
||||
ctx.user_id,
|
||||
&req,
|
||||
&state.db,
|
||||
)
|
||||
.await?;
|
||||
let item = DictionaryService::update_item(item_id, ctx.tenant_id, ctx.user_id, &req, &state.db)
|
||||
.await?;
|
||||
|
||||
// 确保 item 属于指定的 dictionary
|
||||
if item.dictionary_id != dict_id {
|
||||
return Err(AppError::Validation(
|
||||
"字典项不属于指定的字典".to_string(),
|
||||
));
|
||||
return Err(AppError::Validation("字典项不属于指定的字典".to_string()));
|
||||
}
|
||||
|
||||
Ok(Json(ApiResponse::ok(item)))
|
||||
|
||||
@@ -30,14 +30,9 @@ where
|
||||
page_size: Some(100),
|
||||
};
|
||||
|
||||
let (settings, _total) = SettingService::list_by_scope(
|
||||
"platform",
|
||||
&None,
|
||||
ctx.tenant_id,
|
||||
&pagination,
|
||||
&state.db,
|
||||
)
|
||||
.await?;
|
||||
let (settings, _total) =
|
||||
SettingService::list_by_scope("platform", &None, ctx.tenant_id, &pagination, &state.db)
|
||||
.await?;
|
||||
|
||||
let languages: Vec<LanguageResp> = settings
|
||||
.into_iter()
|
||||
@@ -83,7 +78,7 @@ where
|
||||
|
||||
SettingService::set(
|
||||
SetSettingParams {
|
||||
key,
|
||||
key: key.clone(),
|
||||
scope: "platform".to_string(),
|
||||
scope_id: None,
|
||||
value,
|
||||
@@ -96,9 +91,20 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
// 从返回的 SettingResp 中读取实际值
|
||||
let updated = SettingService::get(&key, "platform", &None, ctx.tenant_id, &state.db).await?;
|
||||
|
||||
// 尝试从 value 中提取 name,否则用 code 作为默认名称
|
||||
let name = updated
|
||||
.setting_value
|
||||
.get("name")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or(&code)
|
||||
.to_string();
|
||||
|
||||
Ok(JsonResponse(ApiResponse::ok(LanguageResp {
|
||||
code,
|
||||
name: String::new(),
|
||||
name,
|
||||
is_active: req.is_active,
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -142,8 +142,7 @@ where
|
||||
role_ids: item.role_ids.clone(),
|
||||
version,
|
||||
};
|
||||
MenuService::update(id, ctx.tenant_id, ctx.user_id, &update_req, &state.db)
|
||||
.await?;
|
||||
MenuService::update(id, ctx.tenant_id, ctx.user_id, &update_req, &state.db).await?;
|
||||
}
|
||||
None => {
|
||||
let create_req = CreateMenuReq {
|
||||
|
||||
@@ -91,8 +91,7 @@ where
|
||||
{
|
||||
require_permission(&ctx, "numbering.update")?;
|
||||
|
||||
let rule =
|
||||
NumberingService::update(id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?;
|
||||
let rule = NumberingService::update(id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?;
|
||||
|
||||
Ok(Json(ApiResponse::ok(rule)))
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ where
|
||||
{
|
||||
require_permission(&ctx, "theme.read")?;
|
||||
|
||||
let setting =
|
||||
SettingService::get("theme", "tenant", &None, ctx.tenant_id, &state.db).await?;
|
||||
let setting = SettingService::get("theme", "tenant", &None, ctx.tenant_id, &state.db).await?;
|
||||
|
||||
let theme: ThemeResp = serde_json::from_value(setting.setting_value)
|
||||
.map_err(|e| AppError::Validation(format!("主题配置解析失败: {e}")))?;
|
||||
|
||||
@@ -50,8 +50,7 @@ impl ConfigModule {
|
||||
)
|
||||
.route(
|
||||
"/config/dictionaries/{dict_id}/items/{item_id}",
|
||||
put(dictionary_handler::update_item)
|
||||
.delete(dictionary_handler::delete_item),
|
||||
put(dictionary_handler::update_item).delete(dictionary_handler::delete_item),
|
||||
)
|
||||
// Menu routes
|
||||
.route(
|
||||
@@ -62,8 +61,7 @@ impl ConfigModule {
|
||||
)
|
||||
.route(
|
||||
"/config/menus/{id}",
|
||||
put(menu_handler::update_menu)
|
||||
.delete(menu_handler::delete_menu),
|
||||
put(menu_handler::update_menu).delete(menu_handler::delete_menu),
|
||||
)
|
||||
// Setting routes
|
||||
.route(
|
||||
@@ -93,10 +91,7 @@ impl ConfigModule {
|
||||
get(theme_handler::get_theme).put(theme_handler::update_theme),
|
||||
)
|
||||
// Language routes
|
||||
.route(
|
||||
"/config/languages",
|
||||
get(language_handler::list_languages),
|
||||
)
|
||||
.route("/config/languages", get(language_handler::list_languages))
|
||||
.route(
|
||||
"/config/languages/{code}",
|
||||
put(language_handler::update_language),
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use chrono::Utc;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set,
|
||||
};
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::{DictionaryItemResp, DictionaryResp};
|
||||
@@ -133,15 +131,25 @@ impl DictionaryService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"dictionary.created",
|
||||
tenant_id,
|
||||
serde_json::json!({ "dictionary_id": id, "code": code }),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"dictionary.created",
|
||||
tenant_id,
|
||||
serde_json::json!({ "dictionary_id": id, "code": code }),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary.create", "dictionary")
|
||||
.with_resource_id(id),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"dictionary.create",
|
||||
"dictionary",
|
||||
)
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -198,8 +206,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),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"dictionary.update",
|
||||
"dictionary",
|
||||
)
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -244,15 +257,25 @@ impl DictionaryService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"dictionary.deleted",
|
||||
tenant_id,
|
||||
serde_json::json!({ "dictionary_id": id }),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"dictionary.deleted",
|
||||
tenant_id,
|
||||
serde_json::json!({ "dictionary_id": id }),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "dictionary.delete", "dictionary")
|
||||
.with_resource_id(id),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"dictionary.delete",
|
||||
"dictionary",
|
||||
)
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -315,8 +338,13 @@ impl DictionaryService {
|
||||
.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),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"dictionary_item.create",
|
||||
"dictionary_item",
|
||||
)
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -376,8 +404,13 @@ impl DictionaryService {
|
||||
.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),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"dictionary_item.update",
|
||||
"dictionary_item",
|
||||
)
|
||||
.with_resource_id(item_id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -423,8 +456,13 @@ impl DictionaryService {
|
||||
.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),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"dictionary_item.delete",
|
||||
"dictionary_item",
|
||||
)
|
||||
.with_resource_id(item_id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use chrono::Utc;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, Set,
|
||||
};
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, Set};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::{CreateMenuReq, MenuResp};
|
||||
@@ -58,19 +56,13 @@ impl MenuService {
|
||||
|
||||
// 3. 按 parent_id 分组构建 HashMap
|
||||
let filtered: Vec<&menu::Model> = match &visible_menu_ids {
|
||||
Some(ids) => all_menus
|
||||
.iter()
|
||||
.filter(|m| ids.contains(&m.id))
|
||||
.collect(),
|
||||
Some(ids) => all_menus.iter().filter(|m| ids.contains(&m.id)).collect(),
|
||||
None => all_menus.iter().collect(),
|
||||
};
|
||||
|
||||
let mut children_map: HashMap<Option<Uuid>, Vec<&menu::Model>> = HashMap::new();
|
||||
for m in &filtered {
|
||||
children_map
|
||||
.entry(m.parent_id)
|
||||
.or_default()
|
||||
.push(*m);
|
||||
children_map.entry(m.parent_id).or_default().push(*m);
|
||||
}
|
||||
|
||||
// 4. 递归构建树形结构(从 parent_id == None 的根节点开始)
|
||||
@@ -152,15 +144,19 @@ impl MenuService {
|
||||
Self::assign_roles(id, role_ids, tenant_id, operator_id, db).await?;
|
||||
}
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"menu.created",
|
||||
tenant_id,
|
||||
serde_json::json!({ "menu_id": id, "title": req.title }),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"menu.created",
|
||||
tenant_id,
|
||||
serde_json::json!({ "menu_id": id, "title": req.title }),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.create", "menu")
|
||||
.with_resource_id(id),
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.create", "menu").with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -235,8 +231,7 @@ impl MenuService {
|
||||
}
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.update", "menu")
|
||||
.with_resource_id(id),
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.update", "menu").with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -285,15 +280,19 @@ impl MenuService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"menu.deleted",
|
||||
tenant_id,
|
||||
serde_json::json!({ "menu_id": id }),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"menu.deleted",
|
||||
tenant_id,
|
||||
serde_json::json!({ "menu_id": id }),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.delete", "menu")
|
||||
.with_resource_id(id),
|
||||
AuditLog::new(tenant_id, Some(operator_id), "menu.delete", "menu").with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -370,10 +369,7 @@ impl MenuService {
|
||||
nodes
|
||||
.iter()
|
||||
.map(|m| {
|
||||
let children = children_map
|
||||
.get(&Some(m.id))
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let children = children_map.get(&Some(m.id)).cloned().unwrap_or_default();
|
||||
MenuResp {
|
||||
id: m.id,
|
||||
parent_id: m.parent_id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use chrono::{Datelike, NaiveDate, Utc};
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set,
|
||||
Statement, ConnectionTrait, DatabaseBackend, TransactionTrait,
|
||||
ActiveModelTrait, ColumnTrait, ConnectionTrait, DatabaseBackend, EntityTrait, PaginatorTrait,
|
||||
QueryFilter, Set, Statement, TransactionTrait,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -41,10 +41,7 @@ impl NumberingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
let resps: Vec<NumberingRuleResp> = models
|
||||
.iter()
|
||||
.map(Self::model_to_resp)
|
||||
.collect();
|
||||
let resps: Vec<NumberingRuleResp> = models.iter().map(Self::model_to_resp).collect();
|
||||
|
||||
Ok((resps, total))
|
||||
}
|
||||
@@ -89,7 +86,10 @@ impl NumberingService {
|
||||
seq_start: Set(seq_start),
|
||||
seq_current: Set(seq_start as i64),
|
||||
separator: Set(req.separator.clone().unwrap_or_else(|| "-".to_string())),
|
||||
reset_cycle: Set(req.reset_cycle.clone().unwrap_or_else(|| "never".to_string())),
|
||||
reset_cycle: Set(req
|
||||
.reset_cycle
|
||||
.clone()
|
||||
.unwrap_or_else(|| "never".to_string())),
|
||||
last_reset_date: Set(Some(Utc::now().date_naive())),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
@@ -103,15 +103,25 @@ impl NumberingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"numbering_rule.created",
|
||||
tenant_id,
|
||||
serde_json::json!({ "rule_id": id, "code": req.code }),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"numbering_rule.created",
|
||||
tenant_id,
|
||||
serde_json::json!({ "rule_id": id, "code": req.code }),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "numbering_rule.create", "numbering_rule")
|
||||
.with_resource_id(id),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"numbering_rule.create",
|
||||
"numbering_rule",
|
||||
)
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -126,7 +136,10 @@ impl NumberingService {
|
||||
seq_start,
|
||||
seq_current: seq_start as i64,
|
||||
separator: req.separator.clone().unwrap_or_else(|| "-".to_string()),
|
||||
reset_cycle: req.reset_cycle.clone().unwrap_or_else(|| "never".to_string()),
|
||||
reset_cycle: req
|
||||
.reset_cycle
|
||||
.clone()
|
||||
.unwrap_or_else(|| "never".to_string()),
|
||||
last_reset_date: Some(Utc::now().date_naive().to_string()),
|
||||
version: 1,
|
||||
})
|
||||
@@ -181,8 +194,13 @@ impl NumberingService {
|
||||
.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),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"numbering_rule.update",
|
||||
"numbering_rule",
|
||||
)
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
@@ -219,15 +237,25 @@ impl NumberingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"numbering_rule.deleted",
|
||||
tenant_id,
|
||||
serde_json::json!({ "rule_id": id }),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"numbering_rule.deleted",
|
||||
tenant_id,
|
||||
serde_json::json!({ "rule_id": id }),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "numbering_rule.delete", "numbering_rule")
|
||||
.with_resource_id(id),
|
||||
AuditLog::new(
|
||||
tenant_id,
|
||||
Some(operator_id),
|
||||
"numbering_rule.delete",
|
||||
"numbering_rule",
|
||||
)
|
||||
.with_resource_id(id),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use chrono::Utc;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set,
|
||||
};
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::dto::SettingResp;
|
||||
@@ -46,9 +44,7 @@ impl SettingService {
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> ConfigResult<SettingResp> {
|
||||
// 1. Try exact match
|
||||
if let Some(resp) =
|
||||
Self::find_exact(key, scope, scope_id, tenant_id, db).await?
|
||||
{
|
||||
if let Some(resp) = Self::find_exact(key, scope, scope_id, tenant_id, db).await? {
|
||||
return Ok(resp);
|
||||
}
|
||||
|
||||
@@ -81,12 +77,18 @@ impl SettingService {
|
||||
event_bus: &EventBus,
|
||||
) -> ConfigResult<SettingResp> {
|
||||
// Look for an existing non-deleted record
|
||||
let existing = setting::Entity::find()
|
||||
let mut query = setting::Entity::find()
|
||||
.filter(setting::Column::TenantId.eq(tenant_id))
|
||||
.filter(setting::Column::Scope.eq(¶ms.scope))
|
||||
.filter(setting::Column::ScopeId.eq(params.scope_id))
|
||||
.filter(setting::Column::SettingKey.eq(¶ms.key))
|
||||
.filter(setting::Column::DeletedAt.is_null())
|
||||
.filter(setting::Column::DeletedAt.is_null());
|
||||
|
||||
query = match params.scope_id {
|
||||
Some(id) => query.filter(setting::Column::ScopeId.eq(id)),
|
||||
None => query.filter(setting::Column::ScopeId.is_null()),
|
||||
};
|
||||
|
||||
let existing = query
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
@@ -94,7 +96,9 @@ impl SettingService {
|
||||
if let Some(model) = existing {
|
||||
// Update existing record — 乐观锁校验
|
||||
let next_version = match params.version {
|
||||
Some(v) => check_version(v, model.version).map_err(|_| ConfigError::VersionMismatch)?,
|
||||
Some(v) => {
|
||||
check_version(v, model.version).map_err(|_| ConfigError::VersionMismatch)?
|
||||
}
|
||||
None => model.version + 1,
|
||||
};
|
||||
|
||||
@@ -109,15 +113,20 @@ impl SettingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"setting.updated",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"setting_id": updated.id,
|
||||
"key": params.key,
|
||||
"scope": params.scope,
|
||||
}),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"setting.updated",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"setting_id": updated.id,
|
||||
"key": params.key,
|
||||
"scope": params.scope,
|
||||
}),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "setting.upsert", "setting")
|
||||
@@ -150,15 +159,20 @@ impl SettingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
event_bus.publish(erp_core::events::DomainEvent::new(
|
||||
"setting.created",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"setting_id": id,
|
||||
"key": params.key,
|
||||
"scope": params.scope,
|
||||
}),
|
||||
), db).await;
|
||||
event_bus
|
||||
.publish(
|
||||
erp_core::events::DomainEvent::new(
|
||||
"setting.created",
|
||||
tenant_id,
|
||||
serde_json::json!({
|
||||
"setting_id": id,
|
||||
"key": params.key,
|
||||
"scope": params.scope,
|
||||
}),
|
||||
),
|
||||
db,
|
||||
)
|
||||
.await;
|
||||
|
||||
audit_service::record(
|
||||
AuditLog::new(tenant_id, Some(operator_id), "setting.upsert", "setting")
|
||||
@@ -179,12 +193,17 @@ impl SettingService {
|
||||
pagination: &Pagination,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> ConfigResult<(Vec<SettingResp>, u64)> {
|
||||
let paginator = setting::Entity::find()
|
||||
let mut query = setting::Entity::find()
|
||||
.filter(setting::Column::TenantId.eq(tenant_id))
|
||||
.filter(setting::Column::Scope.eq(scope))
|
||||
.filter(setting::Column::ScopeId.eq(*scope_id))
|
||||
.filter(setting::Column::DeletedAt.is_null())
|
||||
.paginate(db, pagination.limit());
|
||||
.filter(setting::Column::DeletedAt.is_null());
|
||||
|
||||
query = match scope_id {
|
||||
Some(id) => query.filter(setting::Column::ScopeId.eq(*id)),
|
||||
None => query.filter(setting::Column::ScopeId.is_null()),
|
||||
};
|
||||
|
||||
let paginator = query.paginate(db, pagination.limit());
|
||||
|
||||
let total = paginator
|
||||
.num_items()
|
||||
@@ -197,8 +216,7 @@ impl SettingService {
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
|
||||
let resps: Vec<SettingResp> =
|
||||
models.iter().map(Self::model_to_resp).collect();
|
||||
let resps: Vec<SettingResp> = models.iter().map(Self::model_to_resp).collect();
|
||||
|
||||
Ok((resps, total))
|
||||
}
|
||||
@@ -214,20 +232,23 @@ impl SettingService {
|
||||
version: i32,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> ConfigResult<()> {
|
||||
let model = setting::Entity::find()
|
||||
let mut query = setting::Entity::find()
|
||||
.filter(setting::Column::TenantId.eq(tenant_id))
|
||||
.filter(setting::Column::Scope.eq(scope))
|
||||
.filter(setting::Column::ScopeId.eq(*scope_id))
|
||||
.filter(setting::Column::SettingKey.eq(key))
|
||||
.filter(setting::Column::DeletedAt.is_null())
|
||||
.filter(setting::Column::DeletedAt.is_null());
|
||||
|
||||
query = match scope_id {
|
||||
Some(id) => query.filter(setting::Column::ScopeId.eq(*id)),
|
||||
None => query.filter(setting::Column::ScopeId.is_null()),
|
||||
};
|
||||
|
||||
let model = query
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?
|
||||
.ok_or_else(|| {
|
||||
ConfigError::NotFound(format!(
|
||||
"设置 '{}' 在 '{}' 作用域下不存在",
|
||||
key, scope
|
||||
))
|
||||
ConfigError::NotFound(format!("设置 '{}' 在 '{}' 作用域下不存在", key, scope))
|
||||
})?;
|
||||
|
||||
let next_version =
|
||||
@@ -264,12 +285,19 @@ impl SettingService {
|
||||
tenant_id: Uuid,
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
) -> ConfigResult<Option<SettingResp>> {
|
||||
let model = setting::Entity::find()
|
||||
let mut query = setting::Entity::find()
|
||||
.filter(setting::Column::TenantId.eq(tenant_id))
|
||||
.filter(setting::Column::Scope.eq(scope))
|
||||
.filter(setting::Column::ScopeId.eq(*scope_id))
|
||||
.filter(setting::Column::SettingKey.eq(key))
|
||||
.filter(setting::Column::DeletedAt.is_null())
|
||||
.filter(setting::Column::DeletedAt.is_null());
|
||||
|
||||
// SQL 中 `= NULL` 永远返回 false,必须用 IS NULL 匹配 NULL 值
|
||||
query = match scope_id {
|
||||
Some(id) => query.filter(setting::Column::ScopeId.eq(*id)),
|
||||
None => query.filter(setting::Column::ScopeId.is_null()),
|
||||
};
|
||||
|
||||
let model = query
|
||||
.one(db)
|
||||
.await
|
||||
.map_err(|e| ConfigError::Validation(e.to_string()))?;
|
||||
@@ -301,9 +329,7 @@ impl SettingService {
|
||||
(SCOPE_TENANT.to_string(), Some(tenant_id)),
|
||||
(SCOPE_PLATFORM.to_string(), None),
|
||||
]),
|
||||
SCOPE_TENANT => {
|
||||
Ok(vec![(SCOPE_PLATFORM.to_string(), None)])
|
||||
}
|
||||
SCOPE_TENANT => Ok(vec![(SCOPE_PLATFORM.to_string(), None)]),
|
||||
SCOPE_PLATFORM => Ok(vec![]),
|
||||
_ => Err(ConfigError::Validation(format!(
|
||||
"不支持的作用域类型: '{}'",
|
||||
|
||||
Reference in New Issue
Block a user