use crate::auth::get_auth_user; use crate::graphql::guards::RequireRole; use crate::graphql::guards::RequireWritePermission; use crate::graphql::types::ConfigUpdateResultType; use crate::graphql::types::UpdateDocsSupportInput; use crate::graphql::types::UpdateModalAnnouncementInput; use crate::graphql::types::UpdateNoticeConfigInput; use crate::graphql::types::UpdateOpsConfigInput; use crate::graphql::types::UpdateSiteConfigInput; use crate::graphql::types::*; use crate::models::page_block::*; use crate::models::settings::{CreateSetting, UpdateSetting}; use crate::models::user::Role; use crate::models::user::User; use crate::services::casbin_service::CasbinService; use crate::services::invite_code_service::InviteCodeService; use crate::services::page_block_service::PageBlockService; use crate::services::settings_service::SettingsService; use crate::services::user_service::UserService; use async_graphql::{Context, Object, Result}; use uuid::Uuid; pub struct MutationRoot; #[Object] impl MutationRoot { async fn register(&self, ctx: &Context<'_>, input: RegisterInput) -> Result { let user_service = ctx.data::()?; user_service.register(input).await } #[graphql(guard = "RequireRole(Role::Admin)")] async fn create_user(&self, ctx: &Context<'_>, input: CreateUserInput) -> Result { let user_service = ctx.data::()?; user_service.create_user(input).await } async fn login(&self, ctx: &Context<'_>, input: LoginInput) -> Result { let user_service = ctx.data::()?; user_service.login(input).await } async fn create_invite_code( &self, ctx: &Context<'_>, input: CreateInviteCodeInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let invite_code_service = ctx.data::()?; let code = invite_code_service .create_invite_code(auth_user.id, input) .await?; // Get the invite code details to return expires_at let invite_codes = invite_code_service .get_invite_codes_by_creator(auth_user.id) .await?; let invite_code = invite_codes .into_iter() .find(|ic| ic.code == code) .ok_or_else(|| async_graphql::Error::new("Failed to retrieve created invite code"))?; Ok(InviteCodeResponse { code, expires_at: invite_code.expires_at, }) } async fn initialize_admin( &self, ctx: &Context<'_>, input: InitializeAdminInput, ) -> Result { let user_service = ctx.data::()?; match user_service .initialize_admin(input.username, input.email, input.password) .await { Ok(user) => Ok(InitializeAdminResponse { success: true, message: "Admin user initialized successfully".to_string(), user: Some(user), }), Err(e) => Ok(InitializeAdminResponse { success: false, message: e.message, user: None, }), } } // Settings mutations #[graphql(guard = "RequireRole(Role::Admin)")] async fn create_setting( &self, ctx: &Context<'_>, input: CreateSettingInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let create_setting = CreateSetting { key: input.key, value: input.value, value_type: input.value_type, description: input.description, category: input.category, is_encrypted: input.is_encrypted, is_system: input.is_system, is_editable: input.is_editable, }; let setting = settings_service .create_setting(create_setting, auth_user.id) .await?; Ok(SettingType { id: setting.id, key: setting.key, value: setting.value, value_type: setting.value_type, description: setting.description, category: setting.category, is_encrypted: setting.is_encrypted, is_system: setting.is_system, is_editable: setting.is_editable, created_at: setting.created_at, updated_at: setting.updated_at, created_by: setting.created_by, updated_by: setting.updated_by, }) } #[graphql(guard = "RequireRole(Role::Admin)")] async fn update_setting( &self, ctx: &Context<'_>, id: Uuid, input: UpdateSettingInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let update_setting = UpdateSetting { value: input.value, description: input.description, category: input.category, is_editable: input.is_editable, }; let setting = settings_service .update_setting(id, update_setting, auth_user.id) .await?; Ok(SettingType { id: setting.id, key: setting.key, value: setting.value, value_type: setting.value_type, description: setting.description, category: setting.category, is_encrypted: setting.is_encrypted, is_system: setting.is_system, is_editable: setting.is_editable, created_at: setting.created_at, updated_at: setting.updated_at, created_by: setting.created_by, updated_by: setting.updated_by, }) } #[graphql(guard = "RequireRole(Role::Admin)")] async fn delete_setting(&self, ctx: &Context<'_>, id: Uuid) -> Result { let settings_service = ctx.data::()?; let deleted = settings_service.delete_setting(id).await?; Ok(deleted) } #[graphql(guard = "RequireRole(Role::Admin)")] async fn batch_update_settings( &self, ctx: &Context<'_>, input: BatchUpdateSettingsInput, ) -> Result> { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let updates: Vec<(String, serde_json::Value)> = input .updates .into_iter() .map(|item| { ( item.key, item.value .map(|v| serde_json::Value::String(v)) .unwrap_or(serde_json::Value::Null), ) }) .collect(); let settings = settings_service .batch_update_settings(updates, auth_user.id) .await?; Ok(settings .into_iter() .map(|s| SettingType { id: s.id, key: s.key, value: s.value, value_type: s.value_type, description: s.description, category: s.category, is_encrypted: s.is_encrypted, is_system: s.is_system, is_editable: s.is_editable, created_at: s.created_at, updated_at: s.updated_at, created_by: s.created_by, updated_by: s.updated_by, }) .collect()) } // Page Block mutations #[graphql(guard = "RequireRole(Role::Admin)")] async fn create_page(&self, ctx: &Context<'_>, input: CreatePageInputType) -> Result { let auth_user = get_auth_user(ctx).await?; let page_block_service = ctx.data::()?; let create_input = CreatePageInput { title: input.title, slug: input.slug, description: input.description, is_active: input.is_active, }; let page = page_block_service .create_page(create_input, auth_user.id) .await?; Ok(PageType { id: page.id, title: page.title, slug: page.slug, description: page.description, is_active: page.is_active, created_at: page.created_at, updated_at: page.updated_at, created_by: page.created_by, updated_by: page.updated_by, }) } #[graphql(guard = "RequireRole(Role::Admin)")] async fn create_text_block( &self, ctx: &Context<'_>, input: CreateTextBlockInputType, ) -> Result { let page_block_service = ctx.data::()?; let create_input = CreateTextBlockInput { page_id: input.page_id, block_order: input.block_order, title: input.title, markdown: input.markdown, is_active: input.is_active, }; let block = page_block_service.create_text_block(create_input).await?; Ok(TextBlockType { id: block.id, page_id: block.page_id, block_order: block.block_order, title: block.title, markdown: block.markdown, is_active: block.is_active, created_at: block.created_at, updated_at: block.updated_at, }) } #[graphql(guard = "RequireRole(Role::Admin)")] async fn create_chart_block( &self, ctx: &Context<'_>, input: CreateChartBlockInputType, ) -> Result { let page_block_service = ctx.data::()?; let create_input = CreateChartBlockInput { page_id: input.page_id, block_order: input.block_order, title: input.title, chart_type: input.chart_type, series: input .series .into_iter() .map(|dp| CreateDataPointInput { x: dp.x, y: dp.y, label: dp.label, color: dp.color, }) .collect(), config: input.config, is_active: input.is_active, }; let block = page_block_service.create_chart_block(create_input).await?; Ok(ChartBlockType { id: block.id, page_id: block.page_id, block_order: block.block_order, title: block.title, chart_type: block.chart_type, series: block .series .into_iter() .map(|dp| DataPointType { id: dp.id, chart_block_id: dp.chart_block_id, x: dp.x, y: dp.y, label: dp.label, color: dp.color, }) .collect(), config: block.config, is_active: block.is_active, created_at: block.created_at, updated_at: block.updated_at, }) } #[graphql(guard = "RequireRole(Role::Admin)")] async fn create_settings_block( &self, ctx: &Context<'_>, input: CreateSettingsBlockInputType, ) -> Result { let page_block_service = ctx.data::()?; let create_input = CreateSettingsBlockInput { page_id: input.page_id, block_order: input.block_order, title: input.title, category: input.category, editable: input.editable, display_mode: input.display_mode, is_active: input.is_active, }; let block = page_block_service .create_settings_block(create_input) .await?; Ok(SettingsBlockType { id: block.id, page_id: block.page_id, block_order: block.block_order, title: block.title, category: block.category, editable: block.editable, display_mode: block.display_mode, is_active: block.is_active, created_at: block.created_at, updated_at: block.updated_at, }) } #[graphql(guard = "RequireRole(Role::Admin)")] async fn delete_page(&self, ctx: &Context<'_>, page_id: Uuid) -> Result { let page_block_service = ctx.data::()?; page_block_service.delete_page(page_id).await?; Ok(true) } // Enhanced Settings mutations for settings center #[graphql(guard = "RequireRole(Role::Admin)")] async fn update_settings( &self, ctx: &Context<'_>, input: BatchUpdateSettingsInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; // 开始事务 let mut tx = settings_service.get_pool().begin().await?; for update in input.updates { // 更新设置 let update_input = UpdateSetting { value: update.value, description: update.description, category: None, is_editable: None, }; // 这里需要先获取设置的ID,然后更新 // 暂时跳过,因为update_setting_by_key方法不存在 // TODO: 实现通过key更新设置的功能 tracing::warn!("跳过设置更新: {}", update.key); } // 记录变更原因到历史表 if let Some(reason) = input.reason { // 这里可以添加变更历史记录逻辑 tracing::info!("批量更新设置,原因: {}", reason); } // 提交事务 tx.commit().await?; Ok(true) } // 权限管理相关的 Mutation #[graphql(guard = "RequireWritePermission::new(\"permissions\")")] async fn assign_role_to_user( &self, ctx: &Context<'_>, user_id: Uuid, role_name: String, ) -> Result { let casbin_service = ctx.data::()?; casbin_service .assign_role(&user_id.to_string(), &role_name) .await?; Ok(true) } #[graphql(guard = "RequireWritePermission::new(\"permissions\")")] async fn remove_role_from_user( &self, ctx: &Context<'_>, user_id: Uuid, role_name: String, ) -> Result { let casbin_service = ctx.data::()?; casbin_service .remove_role(&user_id.to_string(), &role_name) .await?; Ok(true) } #[graphql(guard = "RequireWritePermission::new(\"permissions\")")] async fn add_policy( &self, ctx: &Context<'_>, role_name: String, resource: String, action: String, ) -> Result { let casbin_service = ctx.data::()?; casbin_service .add_policy(&role_name, &resource, &action) .await?; Ok(true) } #[graphql(guard = "RequireWritePermission::new(\"permissions\")")] async fn remove_policy( &self, ctx: &Context<'_>, role_name: String, resource: String, action: String, ) -> Result { let casbin_service = ctx.data::()?; casbin_service .remove_policy(&role_name, &resource, &action) .await?; Ok(true) } #[graphql(guard = "RequireWritePermission::new(\"permissions\")")] async fn reload_policies(&self, ctx: &Context<'_>) -> Result { let casbin_service = ctx.data::()?; casbin_service.reload_policy().await?; Ok(true) } // 站点与运营配置变更方法 /// 更新站点配置 #[graphql(guard = "RequireWritePermission::new(\"settings\")")] async fn update_site_config( &self, ctx: &Context<'_>, input: UpdateSiteConfigInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let mut updated_settings = Vec::new(); // 更新站点名称 if let Some(name) = input.name { if let Some(setting) = settings_service.get_setting_by_key("site.name").await? { let update = UpdateSetting { value: Some(name), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新默认语言 if let Some(locale) = input.locale_default { if let Some(setting) = settings_service .get_setting_by_key("site.locale_default") .await? { let update = UpdateSetting { value: Some(locale), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新支持的语言列表 if let Some(locales) = input.locales_supported { if let Some(setting) = settings_service .get_setting_by_key("site.locales_supported") .await? { let value = serde_json::to_string(&locales)?; let update = UpdateSetting { value: Some(value), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新Logo URL if let Some(logo_url) = input.logo_url { if let Some(setting) = settings_service .get_setting_by_key("site.brand.logo_url") .await? { let update = UpdateSetting { value: Some(logo_url), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新主题色 if let Some(primary_color) = input.primary_color { if let Some(setting) = settings_service .get_setting_by_key("site.brand.primary_color") .await? { let update = UpdateSetting { value: Some(primary_color), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新暗黑模式默认设置 if let Some(dark_mode_default) = input.dark_mode_default { if let Some(setting) = settings_service .get_setting_by_key("site.brand.dark_mode_default") .await? { let update = UpdateSetting { value: Some(dark_mode_default.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新页脚链接 if let Some(footer_links) = input.footer_links { if let Some(setting) = settings_service .get_setting_by_key("site.footer_links") .await? { let value = serde_json::to_string(&footer_links)?; let update = UpdateSetting { value: Some(value), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } Ok(ConfigUpdateResultType { success: true, message: "站点配置更新成功".to_string(), updated_settings: updated_settings .into_iter() .map(|s| SettingType { id: s.id, key: s.key, value: s.value, value_type: s.value_type, description: s.description, category: s.category, is_encrypted: s.is_encrypted, is_system: s.is_system, is_editable: s.is_editable, created_at: s.created_at, updated_at: s.updated_at, created_by: s.created_by, updated_by: s.updated_by, }) .collect(), }) } /// 更新公告配置 #[graphql(guard = "RequireWritePermission::new(\"settings\")")] async fn update_notice_config( &self, ctx: &Context<'_>, input: UpdateNoticeConfigInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let mut updated_settings = Vec::new(); // 更新横幅公告开关 if let Some(enabled) = input.banner_enabled { if let Some(setting) = settings_service .get_setting_by_key("notice.banner.enabled") .await? { let update = UpdateSetting { value: Some(enabled.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新横幅公告文本 if let Some(text) = input.banner_text { if let Some(setting) = settings_service .get_setting_by_key("notice.banner.text") .await? { let value = serde_json::to_string(&text)?; let update = UpdateSetting { value: Some(value), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新维护窗口配置 if let (Some(enabled), start_time, end_time, message) = ( input.maintenance_enabled, input.maintenance_start_time, input.maintenance_end_time, input.maintenance_message, ) { if let Some(setting) = settings_service .get_setting_by_key("maintenance.window") .await? { let mut config = if let Ok(existing_config) = setting.get_json() { existing_config } else { serde_json::json!({ "enabled": false, "start_time": null, "end_time": null, "message": {"zh-CN": "系统维护中,请稍后再试", "en": "System maintenance in progress"} }) }; // 更新配置 if let Some(obj) = config.as_object_mut() { obj.insert("enabled".to_string(), serde_json::Value::Bool(enabled)); if let Some(start) = start_time { obj.insert( "start_time".to_string(), serde_json::Value::String(start.to_rfc3339()), ); } if let Some(end) = end_time { obj.insert( "end_time".to_string(), serde_json::Value::String(end.to_rfc3339()), ); } if let Some(msg) = message { obj.insert("message".to_string(), serde_json::to_value(msg)?); } } let value = serde_json::to_string(&config)?; let update = UpdateSetting { value: Some(value), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } Ok(ConfigUpdateResultType { success: true, message: "公告配置更新成功".to_string(), updated_settings: updated_settings .into_iter() .map(|s| SettingType { id: s.id, key: s.key, value: s.value, value_type: s.value_type, description: s.description, category: s.category, is_encrypted: s.is_encrypted, is_system: s.is_system, is_editable: s.is_editable, created_at: s.created_at, updated_at: s.updated_at, created_by: s.created_by, updated_by: s.updated_by, }) .collect(), }) } /// 更新弹窗公告 #[graphql(guard = "RequireWritePermission::new(\"settings\")")] async fn update_modal_announcement( &self, ctx: &Context<'_>, input: UpdateModalAnnouncementInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let mut updated_settings = Vec::new(); if let Some(setting) = settings_service .get_setting_by_key("modal.announcements") .await? { let mut announcements = if let Ok(existing) = setting.get_json() { existing .as_array() .map(|arr| arr.to_vec()) .unwrap_or_default() } else { Vec::new() }; // 查找并更新指定的公告 let mut found = false; for announcement in &mut announcements { if let Some(id) = announcement.get("id").and_then(|v| v.as_str()) { if id == input.id { found = true; // 更新标题 if let Some(title) = &input.title { announcement["title"] = serde_json::to_value(title)?; } // 更新内容 if let Some(content) = &input.content { announcement["content"] = serde_json::to_value(content)?; } // 更新时间 if let Some(start_time) = input.start_time { announcement["start_time"] = serde_json::Value::String(start_time.to_rfc3339()); } if let Some(end_time) = input.end_time { announcement["end_time"] = serde_json::Value::String(end_time.to_rfc3339()); } // 更新受众 if let Some(audience) = &input.audience { announcement["audience"] = serde_json::to_value(audience)?; } // 更新优先级 if let Some(priority) = &input.priority { announcement["priority"] = serde_json::Value::String(priority.clone()); } break; } } } if !found { return Err(async_graphql::Error::new("未找到指定的弹窗公告")); } let value = serde_json::to_string(&announcements)?; let update = UpdateSetting { value: Some(value), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } Ok(ConfigUpdateResultType { success: true, message: "弹窗公告更新成功".to_string(), updated_settings: updated_settings.into_iter().map(|s| s.into()).collect(), }) } /// 更新文档支持配置 #[graphql(guard = "RequireWritePermission::new(\"settings\")")] async fn update_docs_support_config( &self, ctx: &Context<'_>, input: UpdateDocsSupportInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let mut updated_settings = Vec::new(); // 更新文档链接 if let Some(links) = input.links { if let Some(setting) = settings_service.get_setting_by_key("docs.links").await? { let value = serde_json::to_string(&links)?; let update = UpdateSetting { value: Some(value), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新支持渠道 if let (Some(email), Some(ticket_system), Some(working_hours)) = (input.email, input.ticket_system, input.working_hours) { if let Some(setting) = settings_service .get_setting_by_key("support.channels") .await? { let mut channels = if let Ok(existing) = setting.get_json() { existing } else { serde_json::json!({ "email": "support@mapp.com", "ticket_system": "/support/tickets", "chat_groups": [], "working_hours": {"zh-CN": "周一至周五 9:00-18:00", "en": "Mon-Fri 9:00-18:00"} }) }; if let Some(obj) = channels.as_object_mut() { obj.insert("email".to_string(), serde_json::Value::String(email)); obj.insert( "ticket_system".to_string(), serde_json::Value::String(ticket_system), ); obj.insert( "working_hours".to_string(), serde_json::to_value(working_hours)?, ); } let value = serde_json::to_string(&channels)?; let update = UpdateSetting { value: Some(value), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } Ok(ConfigUpdateResultType { success: true, message: "文档支持配置更新成功".to_string(), updated_settings: updated_settings.into_iter().map(|s| s.into()).collect(), }) } /// 更新运营配置 #[graphql(guard = "RequireWritePermission::new(\"settings\")")] async fn update_ops_config( &self, ctx: &Context<'_>, input: UpdateOpsConfigInput, ) -> Result { let auth_user = get_auth_user(ctx).await?; let settings_service = ctx.data::()?; let mut updated_settings = Vec::new(); // 更新功能开关 if let Some(enabled) = input.registration_enabled { if let Some(setting) = settings_service .get_setting_by_key("ops.features.registration_enabled") .await? { let update = UpdateSetting { value: Some(enabled.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } if let Some(required) = input.invite_code_required { if let Some(setting) = settings_service .get_setting_by_key("ops.features.invite_code_required") .await? { let update = UpdateSetting { value: Some(required.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } if let Some(verification) = input.email_verification { if let Some(setting) = settings_service .get_setting_by_key("ops.features.email_verification") .await? { let update = UpdateSetting { value: Some(verification.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新限制配置 if let Some(max_users) = input.max_users { if let Some(setting) = settings_service .get_setting_by_key("ops.limits.max_users") .await? { let update = UpdateSetting { value: Some(max_users.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } if let Some(max_codes) = input.max_invite_codes_per_user { if let Some(setting) = settings_service .get_setting_by_key("ops.limits.max_invite_codes_per_user") .await? { let update = UpdateSetting { value: Some(max_codes.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } if let Some(timeout) = input.session_timeout_hours { if let Some(setting) = settings_service .get_setting_by_key("ops.limits.session_timeout_hours") .await? { let update = UpdateSetting { value: Some(timeout.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } // 更新通知配置 if let Some(welcome) = input.welcome_email { if let Some(setting) = settings_service .get_setting_by_key("ops.notifications.welcome_email") .await? { let update = UpdateSetting { value: Some(welcome.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } if let Some(announcements) = input.system_announcements { if let Some(setting) = settings_service .get_setting_by_key("ops.notifications.system_announcements") .await? { let update = UpdateSetting { value: Some(announcements.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } if let Some(alerts) = input.maintenance_alerts { if let Some(setting) = settings_service .get_setting_by_key("ops.notifications.maintenance_alerts") .await? { let update = UpdateSetting { value: Some(alerts.to_string()), description: None, category: None, is_editable: None, }; let updated = settings_service .update_setting(setting.id, update, auth_user.id) .await?; updated_settings.push(updated); } } Ok(ConfigUpdateResultType { success: true, message: "运营配置更新成功".to_string(), updated_settings: updated_settings .into_iter() .map(|s| SettingType { id: s.id, key: s.key, value: s.value, value_type: s.value_type, description: s.description, category: s.category, is_encrypted: s.is_encrypted, is_system: s.is_system, is_editable: s.is_editable, created_at: s.created_at, updated_at: s.updated_at, created_by: s.created_by, updated_by: s.updated_by, }) .collect(), }) } }