use axum::Extension; use axum::extract::{FromRef, Json, State}; use axum::response::Json as JsonResponse; use erp_core::error::AppError; use erp_core::rbac::require_permission; use erp_core::types::{ApiResponse, TenantContext}; use crate::config_state::ConfigState; use crate::dto::{SetSettingParams, ThemeResp}; use crate::error::ConfigError; use crate::service::setting_service::SettingService; /// 默认主题配置。 fn default_theme() -> ThemeResp { ThemeResp { primary_color: None, logo_url: None, sidebar_style: None, } } #[utoipa::path( get, path = "/api/v1/themes", responses( (status = 200, description = "成功", body = ApiResponse), (status = 401, description = "未授权"), (status = 403, description = "权限不足"), ), security(("bearer_auth" = [])), tag = "主题设置" )] /// GET /api/v1/theme /// /// 获取当前租户的主题配置。 /// 主题配置存储在 settings 表中,key 为 "theme",scope 为 "tenant"。 /// 当没有任何主题配置时,返回默认主题(所有字段为 null)。 /// 需要 `theme.read` 权限。 pub async fn get_theme( State(state): State, Extension(ctx): Extension, ) -> Result>, AppError> where ConfigState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "theme.read")?; let theme = match SettingService::get("theme", "tenant", &None, ctx.tenant_id, &state.db).await { Ok(setting) => serde_json::from_value(setting.setting_value) .map_err(|e| AppError::Validation(format!("主题配置解析失败: {e}")))?, Err(ConfigError::NotFound(_)) => default_theme(), Err(e) => return Err(e.into()), }; Ok(JsonResponse(ApiResponse::ok(theme))) } #[utoipa::path( put, path = "/api/v1/themes", request_body = ThemeResp, responses( (status = 200, description = "成功", body = ApiResponse), (status = 401, description = "未授权"), (status = 403, description = "权限不足"), ), security(("bearer_auth" = [])), tag = "主题设置" )] /// PUT /api/v1/theme /// /// 更新当前租户的主题配置。 /// 将主题配置序列化为 JSON 存储到 settings 表。 /// 需要 `theme.update` 权限。 pub async fn update_theme( State(state): State, Extension(ctx): Extension, Json(req): Json, ) -> Result>, AppError> where ConfigState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "theme.update")?; let value = serde_json::to_value(&req) .map_err(|e| AppError::Validation(format!("主题配置序列化失败: {e}")))?; SettingService::set( SetSettingParams { key: "theme".to_string(), scope: "tenant".to_string(), scope_id: None, value, version: None, }, ctx.tenant_id, ctx.user_id, &state.db, &state.event_bus, ) .await?; Ok(JsonResponse(ApiResponse::ok(req))) }