use crate::*; #[spacetimedb::table(accessor = runtime_setting)] pub struct RuntimeSetting { #[primary_key] pub(crate) user_id: String, pub(crate) music_volume: f32, pub(crate) platform_theme: RuntimePlatformTheme, pub(crate) created_at: Timestamp, pub(crate) updated_at: Timestamp, } // procedure 面向 Axum 同步读取设置;若没有持久化记录则返回默认值快照,但不产生额外写入。 #[spacetimedb::procedure] pub fn get_runtime_setting_or_default( ctx: &mut ProcedureContext, input: RuntimeSettingGetInput, ) -> RuntimeSettingProcedureResult { match ctx.try_with_tx(|tx| get_runtime_setting_snapshot(tx, input.clone())) { Ok(record) => RuntimeSettingProcedureResult { ok: true, record: Some(record), error_message: None, }, Err(message) => RuntimeSettingProcedureResult { ok: false, record: None, error_message: Some(message), }, } } // procedure 面向 Axum 同步写入设置,并返回最终归一化后的持久化结果。 #[spacetimedb::procedure] pub fn upsert_runtime_setting_and_return( ctx: &mut ProcedureContext, input: RuntimeSettingUpsertInput, ) -> RuntimeSettingProcedureResult { match ctx.try_with_tx(|tx| upsert_runtime_setting(tx, input.clone())) { Ok(record) => RuntimeSettingProcedureResult { ok: true, record: Some(record), error_message: None, }, Err(message) => RuntimeSettingProcedureResult { ok: false, record: None, error_message: Some(message), }, } } fn get_runtime_setting_snapshot( ctx: &ReducerContext, input: RuntimeSettingGetInput, ) -> Result { let validated_input = build_runtime_setting_get_input(input.user_id).map_err(|error| error.to_string())?; if let Some(existing) = ctx .db .runtime_setting() .user_id() .find(&validated_input.user_id) { return Ok(RuntimeSettingSnapshot { user_id: existing.user_id, music_volume: existing.music_volume, platform_theme: existing.platform_theme, created_at_micros: existing.created_at.to_micros_since_unix_epoch(), updated_at_micros: existing.updated_at.to_micros_since_unix_epoch(), }); } Ok(RuntimeSettingSnapshot { user_id: validated_input.user_id, music_volume: DEFAULT_MUSIC_VOLUME, platform_theme: DEFAULT_PLATFORM_THEME, created_at_micros: 0, updated_at_micros: 0, }) } fn upsert_runtime_setting( ctx: &ReducerContext, input: RuntimeSettingUpsertInput, ) -> Result { let validated_input = build_runtime_setting_upsert_input( input.user_id, input.music_volume, input.platform_theme, input.updated_at_micros, ) .map_err(|error| error.to_string())?; let updated_at = Timestamp::from_micros_since_unix_epoch(validated_input.updated_at_micros); let snapshot = match ctx .db .runtime_setting() .user_id() .find(&validated_input.user_id) { Some(existing) => { ctx.db.runtime_setting().user_id().delete(&existing.user_id); ctx.db.runtime_setting().insert(RuntimeSetting { user_id: existing.user_id.clone(), music_volume: validated_input.music_volume, platform_theme: validated_input.platform_theme, created_at: existing.created_at, updated_at, }); RuntimeSettingSnapshot { user_id: existing.user_id, music_volume: validated_input.music_volume, platform_theme: validated_input.platform_theme, created_at_micros: existing.created_at.to_micros_since_unix_epoch(), updated_at_micros: validated_input.updated_at_micros, } } None => { ctx.db.runtime_setting().insert(RuntimeSetting { user_id: validated_input.user_id.clone(), music_volume: validated_input.music_volume, platform_theme: validated_input.platform_theme, created_at: updated_at, updated_at, }); RuntimeSettingSnapshot { user_id: validated_input.user_id, music_volume: validated_input.music_volume, platform_theme: validated_input.platform_theme, created_at_micros: validated_input.updated_at_micros, updated_at_micros: validated_input.updated_at_micros, } } }; Ok(snapshot) }