Files
erp/crates/erp-config/src/handler/language_handler.rs
iven 0baaf5f7ee feat(config): add system configuration module (Phase 3)
Implement the complete erp-config crate with:
- Data dictionaries (CRUD + items management)
- Dynamic menus (tree structure with role filtering)
- System settings (hierarchical: platform > tenant > org > user)
- Numbering rules (concurrency-safe via PostgreSQL advisory_lock)
- Theme and language configuration (via settings store)
- 6 database migrations (dictionaries, menus, settings, numbering_rules)
- Frontend Settings page with 5 tabs (dictionary, menu, numbering, settings, theme)

Refactor: move RBAC functions (require_permission) from erp-auth to erp-core
to avoid cross-module dependencies.

Add 20 new seed permissions for config module operations.
2026-04-11 08:09:19 +08:00

102 lines
2.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use axum::Extension;
use axum::extract::{FromRef, Json, Path, State};
use axum::response::Json as JsonResponse;
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, Pagination, TenantContext};
use crate::config_state::ConfigState;
use crate::dto::{LanguageResp, UpdateLanguageReq};
use crate::service::setting_service::SettingService;
/// GET /api/v1/languages
///
/// 获取当前租户的语言配置列表。
/// 查询 scope 为 "platform" 的设置,过滤 key 以 "language." 开头的记录。
/// 需要 `language.list` 权限。
pub async fn list_languages<S>(
State(state): State<ConfigState>,
Extension(ctx): Extension<TenantContext>,
) -> Result<JsonResponse<ApiResponse<Vec<LanguageResp>>>, AppError>
where
ConfigState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "language.list")?;
let pagination = Pagination {
page: Some(1),
page_size: Some(100),
};
let (settings, _total) = SettingService::list_by_scope(
"platform",
&None,
ctx.tenant_id,
&pagination,
&state.db,
)
.await?;
let languages: Vec<LanguageResp> = settings
.into_iter()
.filter(|s| s.setting_key.starts_with("language."))
.filter_map(|s| {
let code = s.setting_key.strip_prefix("language.")?.to_string();
let name = code.clone(); // 默认使用 code 作为名称
let is_active = s
.setting_value
.get("is_active")
.and_then(|v| v.as_bool())
.unwrap_or(true);
Some(LanguageResp {
code,
name,
is_active,
})
})
.collect();
Ok(JsonResponse(ApiResponse::ok(languages)))
}
/// PUT /api/v1/languages/:code
///
/// 更新指定语言配置的激活状态。
/// 语言配置存储在 settings 表中key 为 "language.{code}"scope 为 "platform"。
/// 需要 `language.update` 权限。
pub async fn update_language<S>(
State(state): State<ConfigState>,
Extension(ctx): Extension<TenantContext>,
Path(code): Path<String>,
Json(req): Json<UpdateLanguageReq>,
) -> Result<JsonResponse<ApiResponse<LanguageResp>>, AppError>
where
ConfigState: FromRef<S>,
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "language.update")?;
let key = format!("language.{}", code);
let value = serde_json::json!({"is_active": req.is_active});
SettingService::set(
&key,
"platform",
&None,
value,
ctx.tenant_id,
ctx.user_id,
&state.db,
&state.event_bus,
)
.await?;
Ok(JsonResponse(ApiResponse::ok(LanguageResp {
code,
name: String::new(),
is_active: req.is_active,
})))
}