diff --git a/server-rs/crates/module-runtime/src/domain.rs b/server-rs/crates/module-runtime/src/domain.rs index 94860d76b..6477930ac 100644 --- a/server-rs/crates/module-runtime/src/domain.rs +++ b/server-rs/crates/module-runtime/src/domain.rs @@ -1731,6 +1731,26 @@ pub struct RuntimeProfileMembershipSnapshotProcedureResult { pub error_message: Option, } +/// 账号当前有效的 AGC 模型权限档。 +/// +/// 与写库刷新的充值中心快照不同,它只读会员账期投影与档位目录,不触发换期 / 免费点刷新,供 LLM 代理热路径使用。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileAgcModelAccessSnapshot { + pub user_id: String, + pub plan: RuntimeProfileMembershipPlan, + pub model_access: RuntimeProfileMembershipModelAccess, +} + +/// 只读 AGC 模型权限 procedure 的结果:成功时 `record` 为解析后的权限档,失败时给出 `error_message`。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileAgcModelAccessProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct RuntimeProfileRechargeOrderSnapshot { diff --git a/server-rs/crates/spacetime-client/src/active/mapper/runtime_profile.rs b/server-rs/crates/spacetime-client/src/active/mapper/runtime_profile.rs index 73a65cdc2..fda444938 100644 --- a/server-rs/crates/spacetime-client/src/active/mapper/runtime_profile.rs +++ b/server-rs/crates/spacetime-client/src/active/mapper/runtime_profile.rs @@ -779,6 +779,22 @@ pub(crate) fn map_runtime_profile_membership_snapshot_procedure_result( )) } +pub(crate) fn map_runtime_profile_agc_model_access_procedure_result( + result: RuntimeProfileAgcModelAccessProcedureResult, +) -> Result { + if !result.ok { + return Err(SpacetimeClientError::procedure_failed(result.error_message)); + } + let record = result + .record + .ok_or_else(|| SpacetimeClientError::missing_snapshot("profile AGC model access 快照"))?; + Ok(module_runtime::RuntimeProfileAgcModelAccessSnapshot { + user_id: record.user_id, + plan: map_runtime_profile_membership_plan_back(record.plan), + model_access: map_runtime_profile_membership_model_access_back(record.model_access), + }) +} + pub(crate) fn map_runtime_profile_recharge_order_procedure_result( result: RuntimeProfileRechargeCenterProcedureResult, ) -> Result< diff --git a/server-rs/crates/spacetime-client/src/active/runtime.rs b/server-rs/crates/spacetime-client/src/active/runtime.rs index 99e3f4bf6..dd1d32724 100644 --- a/server-rs/crates/spacetime-client/src/active/runtime.rs +++ b/server-rs/crates/spacetime-client/src/active/runtime.rs @@ -422,6 +422,34 @@ impl SpacetimeClient { .await } + /// 只读 AGC 模型权限:不刷新账期 / 免费点,供 LLM 代理热路径使用。 + pub async fn get_profile_agc_model_access( + &self, + user_id: String, + ) -> Result { + let procedure_input = build_runtime_profile_membership_get_input(user_id) + .map_err(SpacetimeClientError::validation_failed)? + .into(); + + self.call_after_connect( + "get_profile_agc_model_access_and_return", + move |connection, sender| { + connection + .procedures() + .get_profile_agc_model_access_and_return_then( + procedure_input, + move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_runtime_profile_agc_model_access_procedure_result); + send_once(&sender, mapped); + }, + ); + }, + ) + .await + } + pub async fn create_profile_recharge_order( &self, user_id: String, diff --git a/server-rs/crates/spacetime-client/src/module_bindings.rs b/server-rs/crates/spacetime-client/src/module_bindings.rs index d7ef52226..55a56de2b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings.rs @@ -488,6 +488,7 @@ pub mod get_game_distribution_my_review_and_return_procedure; pub mod get_game_distribution_version_and_return_procedure; pub mod get_llm_router_account_and_return_procedure; pub mod get_owner_game_distribution_version_and_return_procedure; +pub mod get_profile_agc_model_access_and_return_procedure; pub mod get_payment_order_and_return_procedure; pub mod get_payment_order_by_checkout_token_and_return_procedure; pub mod get_profile_dashboard_procedure; @@ -691,6 +692,8 @@ pub mod runtime_profile_admin_wallet_detail_procedure_result_type; pub mod runtime_profile_admin_wallet_get_input_type; pub mod runtime_profile_admin_wallet_procedure_result_type; pub mod runtime_profile_admin_wallet_snapshot_type; +pub mod runtime_profile_agc_model_access_procedure_result_type; +pub mod runtime_profile_agc_model_access_snapshot_type; pub mod runtime_profile_code_operation_snapshot_type; pub mod runtime_profile_daily_free_points_snapshot_type; pub mod runtime_profile_dashboard_get_input_type; @@ -1366,6 +1369,7 @@ pub use get_game_distribution_my_review_and_return_procedure::get_game_distribut pub use get_game_distribution_version_and_return_procedure::get_game_distribution_version_and_return; pub use get_llm_router_account_and_return_procedure::get_llm_router_account_and_return; pub use get_owner_game_distribution_version_and_return_procedure::get_owner_game_distribution_version_and_return; +pub use get_profile_agc_model_access_and_return_procedure::get_profile_agc_model_access_and_return; pub use get_payment_order_and_return_procedure::get_payment_order_and_return; pub use get_payment_order_by_checkout_token_and_return_procedure::get_payment_order_by_checkout_token_and_return; pub use get_profile_dashboard_procedure::get_profile_dashboard; @@ -1569,6 +1573,8 @@ pub use runtime_profile_admin_wallet_detail_procedure_result_type::RuntimeProfil pub use runtime_profile_admin_wallet_get_input_type::RuntimeProfileAdminWalletGetInput; pub use runtime_profile_admin_wallet_procedure_result_type::RuntimeProfileAdminWalletProcedureResult; pub use runtime_profile_admin_wallet_snapshot_type::RuntimeProfileAdminWalletSnapshot; +pub use runtime_profile_agc_model_access_procedure_result_type::RuntimeProfileAgcModelAccessProcedureResult; +pub use runtime_profile_agc_model_access_snapshot_type::RuntimeProfileAgcModelAccessSnapshot; pub use runtime_profile_code_operation_snapshot_type::RuntimeProfileCodeOperationSnapshot; pub use runtime_profile_daily_free_points_snapshot_type::RuntimeProfileDailyFreePointsSnapshot; pub use runtime_profile_dashboard_get_input_type::RuntimeProfileDashboardGetInput; diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_agc_model_access_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_agc_model_access_and_return_procedure.rs new file mode 100644 index 000000000..eca693a53 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_agc_model_access_and_return_procedure.rs @@ -0,0 +1,59 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::runtime_profile_agc_model_access_procedure_result_type::RuntimeProfileAgcModelAccessProcedureResult; +use super::runtime_profile_membership_get_input_type::RuntimeProfileMembershipGetInput; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct GetProfileAgcModelAccessAndReturnArgs { + pub input: RuntimeProfileMembershipGetInput, +} + +impl __sdk::InModule for GetProfileAgcModelAccessAndReturnArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `get_profile_agc_model_access_and_return`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait get_profile_agc_model_access_and_return { + fn get_profile_agc_model_access_and_return(&self, input: RuntimeProfileMembershipGetInput) { + self.get_profile_agc_model_access_and_return_then(input, |_, _| {}); + } + + fn get_profile_agc_model_access_and_return_then( + &self, + input: RuntimeProfileMembershipGetInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl get_profile_agc_model_access_and_return for super::RemoteProcedures { + fn get_profile_agc_model_access_and_return_then( + &self, + input: RuntimeProfileMembershipGetInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp + .invoke_procedure_with_callback::<_, RuntimeProfileAgcModelAccessProcedureResult>( + "get_profile_agc_model_access_and_return", + GetProfileAgcModelAccessAndReturnArgs { input }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_agc_model_access_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_agc_model_access_procedure_result_type.rs new file mode 100644 index 000000000..f95d34ce8 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_agc_model_access_procedure_result_type.rs @@ -0,0 +1,19 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::runtime_profile_agc_model_access_snapshot_type::RuntimeProfileAgcModelAccessSnapshot; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct RuntimeProfileAgcModelAccessProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +impl __sdk::InModule for RuntimeProfileAgcModelAccessProcedureResult { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_agc_model_access_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_agc_model_access_snapshot_type.rs new file mode 100644 index 000000000..ecbf88e77 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_agc_model_access_snapshot_type.rs @@ -0,0 +1,20 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::runtime_profile_membership_model_access_type::RuntimeProfileMembershipModelAccess; +use super::runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct RuntimeProfileAgcModelAccessSnapshot { + pub user_id: String, + pub plan: RuntimeProfileMembershipPlan, + pub model_access: RuntimeProfileMembershipModelAccess, +} + +impl __sdk::InModule for RuntimeProfileAgcModelAccessSnapshot { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-module/src/runtime/active/profile.rs b/server-rs/crates/spacetime-module/src/runtime/active/profile.rs index 7e1a9e4db..94fa85e50 100644 --- a/server-rs/crates/spacetime-module/src/runtime/active/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/active/profile.rs @@ -1727,6 +1727,55 @@ pub fn get_profile_membership_snapshot_and_return( } } +/// 账号当前有效的 AGC 模型权限:只读账期投影 + 档位目录 `model_access`,不刷新、不写库。 +/// +/// 供 api-server 的 LLM 代理热路径使用;缺会员行 / 已过期按 `Normal`,缺目录行失败关闭到 `Basic`。 +#[spacetimedb::procedure] +pub fn get_profile_agc_model_access_and_return( + ctx: &mut ProcedureContext, + input: RuntimeProfileMembershipGetInput, +) -> RuntimeProfileAgcModelAccessProcedureResult { + let caller = ctx.sender(); + match ctx.try_with_tx(|tx| { + crate::editor_project_storage::require_editor_generation_runtime_service_identity( + tx, caller, + )?; + let validated = build_runtime_profile_membership_get_input(input.user_id.clone()) + .map_err(|error| error.to_string())?; + Ok(build_profile_agc_model_access_snapshot_readonly( + tx, + &validated.user_id, + )) + }) { + Ok(record) => RuntimeProfileAgcModelAccessProcedureResult { + ok: true, + record: Some(record), + error_message: None, + }, + Err(message) => RuntimeProfileAgcModelAccessProcedureResult { + ok: false, + record: None, + error_message: Some(message), + }, + } +} + +/// 纯读解析:用只读账期投影拿当前档位,再取档位目录的 `model_access`;缺目录行失败关闭到 `Basic`。 +fn build_profile_agc_model_access_snapshot_readonly( + ctx: &ReducerContext, + user_id: &str, +) -> RuntimeProfileAgcModelAccessSnapshot { + let membership = build_profile_membership_snapshot_readonly(ctx, user_id); + let model_access = membership_plan_row(ctx, membership.plan) + .map(|row| row.model_access) + .unwrap_or(RuntimeProfileMembershipModelAccess::Basic); + RuntimeProfileAgcModelAccessSnapshot { + user_id: membership.user_id, + plan: membership.plan, + model_access, + } +} + #[spacetimedb::procedure] pub fn get_profile_recharge_order_and_return( ctx: &mut ProcedureContext,