From e98564e13c5f44b1f25a220bd32fe42ff33a8eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 2 Oct 2026 16:59:34 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84(=E4=BC=9A=E5=91=98):=20?= =?UTF-8?q?=E4=BC=9A=E5=91=98=E7=9B=AE=E5=BD=95=E4=B8=8E=E8=87=AA=E7=84=B6?= =?UTF-8?q?=E6=9C=88=E8=B4=A6=E6=9C=9F=E8=90=BD=E5=88=B0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E4=B8=8E=E9=A2=86=E5=9F=9F=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 领域层新增会员档位枚举、自然月账期、目录快照与补差报价入参,删除旧 tier 相关函数 - profile_membership 表以 plan 与自然月期数取代 tier 与固定 30 天周期 - profile_recharge_order 末位追加会员权益变更快照,供人工复核退款 - profile_membership_plan 目录表成为价格与权益的唯一真相源,新增后台读写接口 - 充点商品收敛为六档且取消首充赠送,旧播种行按新规则迁移 - 充值中心改为输出会员目录,不再输出会员商品行与权益矩阵 - 重新生成 SpacetimeDB 绑定并同步客户端映射与 facade Co-authored-by: Junie --- .../crates/module-runtime/src/commands.rs | 63 +++ server-rs/crates/module-runtime/src/domain.rs | 66 +++ server-rs/crates/module-runtime/src/errors.rs | 4 + .../crates/spacetime-client/src/active.rs | 2 + .../src/active/mapper/runtime_profile.rs | 268 ++++++---- .../spacetime-client/src/active/runtime.rs | 90 +++- .../spacetime-client/src/module_bindings.rs | 36 +- ...list_profile_membership_plans_procedure.rs | 61 +++ ...psert_profile_membership_plan_procedure.rs | 62 +++ ...bution_user_review_and_return_procedure.rs | 16 +- ...ribution_my_review_and_return_procedure.rs | 16 +- ...ution_user_reviews_and_return_procedure.rs | 16 +- .../list_agc_tracking_events_procedure.rs | 16 +- ...ution_review_games_and_return_procedure.rs | 16 +- ...ution_user_reviews_and_return_procedure.rs | 16 +- ...bution_user_review_and_return_procedure.rs | 16 +- .../profile_membership_table.rs | 3 +- .../profile_membership_type.rs | 22 +- .../profile_recharge_order_table.rs | 3 + .../profile_recharge_order_type.rs | 105 ++++ .../profile_recharge_product_config_table.rs | 1 - .../profile_recharge_product_config_type.rs | 29 -- ...me_profile_membership_change_kind_type.rs} | 20 +- ...ime_profile_membership_cycle_kind_type.rs} | 17 +- ...e_membership_order_change_snapshot_type.rs | 28 + ...e_membership_plan_admin_list_input_type.rs | 15 + ...p_plan_admin_list_procedure_result_type.rs | 19 + ...ership_plan_admin_procedure_result_type.rs | 19 + ...membership_plan_admin_upsert_input_type.rs | 28 + ...ime_profile_membership_plan_record_type.rs | 27 + ...untime_profile_membership_snapshot_type.rs | 9 +- ...e_profile_recharge_center_snapshot_type.rs | 5 +- ...echarge_order_admin_entry_snapshot_type.rs | 2 + ...echarge_product_admin_upsert_input_type.rs | 6 - ...e_recharge_product_config_snapshot_type.rs | 6 - ..._profile_recharge_product_snapshot_type.rs | 6 - ...ribution_my_review_and_return_procedure.rs | 16 +- .../upload_agc_analytics_batch_procedure.rs | 16 +- .../src/runtime/active/profile.rs | 492 ++++++++++-------- 39 files changed, 1176 insertions(+), 482 deletions(-) create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_membership_plans_procedure.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_membership_plan_procedure.rs rename server-rs/crates/spacetime-client/src/module_bindings/{runtime_profile_membership_tier_type.rs => runtime_profile_membership_change_kind_type.rs} (68%) rename server-rs/crates/spacetime-client/src/module_bindings/{runtime_profile_membership_benefit_snapshot_type.rs => runtime_profile_membership_cycle_kind_type.rs} (50%) create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_order_change_snapshot_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_input_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_procedure_result_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_procedure_result_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_upsert_input_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_record_type.rs diff --git a/server-rs/crates/module-runtime/src/commands.rs b/server-rs/crates/module-runtime/src/commands.rs index bab1dff70..ce8f521f3 100644 --- a/server-rs/crates/module-runtime/src/commands.rs +++ b/server-rs/crates/module-runtime/src/commands.rs @@ -436,6 +436,69 @@ pub fn build_runtime_profile_recharge_product_admin_upsert_input( }) } +pub fn build_runtime_profile_membership_plan_admin_list_input( + admin_user_id: String, +) -> Result { + let admin_user_id = normalize_runtime_profile_user_id(admin_user_id)?; + Ok(RuntimeProfileMembershipPlanAdminListInput { admin_user_id }) +} + +/// 后台会员档位改价 / 改权益入参校验。 +/// +/// `normal` 是不可购买的非会员占位档位:它的价格与额度没有意义,一律按 0 落库。 +#[allow(clippy::too_many_arguments)] +pub fn build_runtime_profile_membership_plan_admin_upsert_input( + admin_user_id: String, + plan: RuntimeProfileMembershipPlan, + title: String, + month_price_cents: u64, + year_price_cents: u64, + period_points: u64, + model_access: RuntimeProfileMembershipModelAccess, + concurrent_job_limit: u32, + enabled: bool, + sort_order: i32, + updated_at_micros: i64, +) -> Result { + let admin_user_id = normalize_runtime_profile_user_id(admin_user_id)?; + let title = + normalize_required_string(title).ok_or(RuntimeProfileFieldError::MissingProductTitle)?; + + if !plan.is_purchasable() { + return Ok(RuntimeProfileMembershipPlanAdminUpsertInput { + admin_user_id, + plan, + title, + month_price_cents: 0, + year_price_cents: 0, + period_points: 0, + model_access, + concurrent_job_limit, + enabled: false, + sort_order, + updated_at_micros, + }); + } + + if month_price_cents == 0 || year_price_cents == 0 || concurrent_job_limit == 0 { + return Err(RuntimeProfileFieldError::InvalidMembershipPlan); + } + + Ok(RuntimeProfileMembershipPlanAdminUpsertInput { + admin_user_id, + plan, + title, + month_price_cents, + year_price_cents, + period_points, + model_access, + concurrent_job_limit, + enabled, + sort_order, + updated_at_micros, + }) +} + pub fn build_runtime_profile_recharge_order_paid_input( order_id: String, paid_at_micros: i64, diff --git a/server-rs/crates/module-runtime/src/domain.rs b/server-rs/crates/module-runtime/src/domain.rs index 973adad40..6f48a750d 100644 --- a/server-rs/crates/module-runtime/src/domain.rs +++ b/server-rs/crates/module-runtime/src/domain.rs @@ -1698,6 +1698,30 @@ pub struct RuntimeProfileRechargeOrderAdminEntrySnapshot { pub refunds: Vec, pub active_hold: Option, pub wallet: RuntimeProfileAdminWalletSnapshot, + /// 会员购买 / 升级的权益变更快照;人工复核退款时据此还原变更前后的档位、周期、补点与有效期。 + pub membership_change: Option, +} + +/// 会员订单的权益变更快照(只读投影,字段与 `profile_recharge_order` 的末位快照列一一对应)。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipOrderChangeSnapshot { + /// `purchase` 或 `upgrade`。 + pub change_kind: String, + pub plan_before: Option, + pub plan_after: Option, + pub cycle_kind_before: Option, + pub cycle_kind_after: Option, + pub period_points_before: Option, + pub period_points_after: Option, + pub granted_points_delta: i64, + pub cycle_index: Option, + pub expires_at_before_micros: Option, + pub expires_at_after_micros: Option, + pub cycle_resets_at_before_micros: Option, + pub cycle_resets_at_after_micros: Option, + /// 订单价格拆分 JSON(目标档位价、当前档位价、实付金额),仅用于人工核对。 + pub price_breakdown_json: Option, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] @@ -1780,6 +1804,48 @@ pub struct RuntimeProfileRechargeProductAdminProcedureResult { pub error_message: Option, } +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipPlanAdminListInput { + pub admin_user_id: String, +} + +/// 后台会员档位改价 / 改权益入参。 +/// +/// `rank` 不在入参里:升级比较基准由代码内的档位目录决定,后台不能改。 +/// `title` 与 `sort_order` 仍可编辑,`normal` 档位的价格与额度会被忽略(它不可购买)。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipPlanAdminUpsertInput { + pub admin_user_id: String, + pub plan: RuntimeProfileMembershipPlan, + pub title: String, + pub month_price_cents: u64, + pub year_price_cents: u64, + pub period_points: u64, + pub model_access: RuntimeProfileMembershipModelAccess, + pub concurrent_job_limit: u32, + pub enabled: bool, + pub sort_order: i32, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipPlanAdminListProcedureResult { + pub ok: bool, + pub entries: Vec, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipPlanAdminProcedureResult { + 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 RuntimeProfileRechargeCenterGetInput { diff --git a/server-rs/crates/module-runtime/src/errors.rs b/server-rs/crates/module-runtime/src/errors.rs index e8f1f0457..f282bd0c4 100644 --- a/server-rs/crates/module-runtime/src/errors.rs +++ b/server-rs/crates/module-runtime/src/errors.rs @@ -91,6 +91,7 @@ pub enum RuntimeProfileFieldError { InvalidRechargeProductDuration, InvalidRechargeProductKind, InvalidRechargeProductTier, + InvalidMembershipPlan, MissingPaymentChannel, #[cfg(any())] MissingWorldKey, @@ -186,6 +187,9 @@ impl std::fmt::Display for RuntimeProfileFieldError { } Self::InvalidRechargeProductKind => f.write_str("充值商品类型无效"), Self::InvalidRechargeProductTier => f.write_str("会员商品 tier 无效"), + Self::InvalidMembershipPlan => { + f.write_str("会员档位配置无效:月价、年价、每期泥点与并发上限必须有效") + } Self::MissingPaymentChannel => f.write_str("recharge.payment_channel 不能为空"), #[cfg(any())] Self::MissingWorldKey => f.write_str("profile.world_key 不能为空"), diff --git a/server-rs/crates/spacetime-client/src/active.rs b/server-rs/crates/spacetime-client/src/active.rs index b0ee057ca..86921c9ca 100644 --- a/server-rs/crates/spacetime-client/src/active.rs +++ b/server-rs/crates/spacetime-client/src/active.rs @@ -87,6 +87,8 @@ use module_runtime::{ build_runtime_profile_feedback_submission_record, build_runtime_profile_invite_code_admin_list_input, build_runtime_profile_invite_code_admin_upsert_input, build_runtime_profile_invite_code_record, + build_runtime_profile_membership_plan_admin_list_input, + build_runtime_profile_membership_plan_admin_upsert_input, build_runtime_profile_recharge_center_get_input, build_runtime_profile_recharge_center_record, build_runtime_profile_recharge_order_close_input, build_runtime_profile_recharge_order_create_input, 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 b92650e66..b61af2978 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 @@ -563,11 +563,6 @@ impl From duration_days: input.duration_days, badge_label: input.badge_label, description: input.description, - tier: map_runtime_profile_membership_tier(input.tier), - membership_period_points: input.membership_period_points, - membership_period_days: input.membership_period_days, - membership_queue_limit: input.membership_queue_limit, - membership_discount_bps: input.membership_discount_bps, enabled: input.enabled, sort_order: input.sort_order, updated_at_micros: input.updated_at_micros, @@ -1381,11 +1376,6 @@ pub(crate) fn map_runtime_profile_recharge_product_snapshot( duration_days: snapshot.duration_days, badge_label: snapshot.badge_label, description: snapshot.description, - tier: map_runtime_profile_membership_tier_back(snapshot.tier), - membership_period_points: snapshot.membership_period_points, - membership_period_days: snapshot.membership_period_days, - membership_queue_limit: snapshot.membership_queue_limit, - membership_discount_bps: snapshot.membership_discount_bps, } } @@ -1401,15 +1391,10 @@ pub(crate) fn map_runtime_profile_recharge_center_snapshot( .into_iter() .map(map_runtime_profile_recharge_product_snapshot) .collect(), - membership_products: snapshot - .membership_products + membership_plans: snapshot + .membership_plans .into_iter() - .map(map_runtime_profile_recharge_product_snapshot) - .collect(), - benefits: snapshot - .benefits - .into_iter() - .map(map_runtime_profile_membership_benefit_snapshot) + .map(map_runtime_profile_membership_plan_record) .collect(), latest_order: snapshot .latest_order @@ -1434,11 +1419,6 @@ pub(crate) fn map_runtime_profile_recharge_product_config_snapshot( duration_days: snapshot.duration_days, badge_label: snapshot.badge_label, description: snapshot.description, - tier: map_runtime_profile_membership_tier_back(snapshot.tier), - membership_period_points: snapshot.membership_period_points, - membership_period_days: snapshot.membership_period_days, - membership_queue_limit: snapshot.membership_queue_limit, - membership_discount_bps: snapshot.membership_discount_bps, enabled: snapshot.enabled, sort_order: snapshot.sort_order, created_by: snapshot.created_by, @@ -1448,19 +1428,20 @@ pub(crate) fn map_runtime_profile_recharge_product_config_snapshot( } } -pub(crate) fn map_runtime_profile_membership_benefit_snapshot( - snapshot: RuntimeProfileMembershipBenefitSnapshot, -) -> module_runtime::RuntimeProfileMembershipBenefitSnapshot { - module_runtime::RuntimeProfileMembershipBenefitSnapshot { - benefit_name: snapshot.benefit_name, - normal_value: snapshot.normal_value, - month_value: snapshot.month_value, - season_value: snapshot.season_value, - year_value: snapshot.year_value, - starter_value: snapshot.starter_value, - basic_value: snapshot.basic_value, - pro_value: snapshot.pro_value, - ultimate_value: snapshot.ultimate_value, +pub(crate) fn map_runtime_profile_membership_plan_record( + record: RuntimeProfileMembershipPlanRecord, +) -> module_runtime::RuntimeProfileMembershipPlanRecord { + module_runtime::RuntimeProfileMembershipPlanRecord { + plan: map_runtime_profile_membership_plan_back(record.plan), + title: record.title, + rank: record.rank, + month_price_cents: record.month_price_cents, + year_price_cents: record.year_price_cents, + period_points: record.period_points, + model_access: map_runtime_profile_membership_model_access_back(record.model_access), + concurrent_job_limit: record.concurrent_job_limit, + sort_order: record.sort_order, + enabled: record.enabled, } } @@ -1470,7 +1451,7 @@ pub(crate) fn map_runtime_profile_membership_snapshot( module_runtime::RuntimeProfileMembershipSnapshot { user_id: snapshot.user_id, status: map_runtime_profile_membership_status_back(snapshot.status), - tier: map_runtime_profile_membership_tier_back(snapshot.tier), + plan: map_runtime_profile_membership_plan_back(snapshot.plan), started_at_micros: snapshot.started_at_micros, expires_at_micros: snapshot.expires_at_micros, updated_at_micros: snapshot.updated_at_micros, @@ -1478,7 +1459,9 @@ pub(crate) fn map_runtime_profile_membership_snapshot( cycle_resets_at_micros: snapshot.cycle_resets_at_micros, cycle_granted_points: snapshot.cycle_granted_points, cycle_remaining_points: snapshot.cycle_remaining_points, - cycle_period_days: snapshot.cycle_period_days, + cycle_index: snapshot.cycle_index, + cycle_count: snapshot.cycle_count, + cycle_kind: map_runtime_profile_membership_cycle_kind_back(snapshot.cycle_kind), } } @@ -1635,6 +1618,9 @@ pub(crate) fn map_runtime_profile_recharge_order_admin_entry_snapshot( .active_hold .map(map_runtime_profile_recharge_refund_hold_snapshot), wallet: map_runtime_profile_admin_wallet_snapshot(snapshot.wallet), + membership_change: snapshot + .membership_change + .map(map_runtime_profile_membership_order_change_snapshot), } } @@ -1956,37 +1942,168 @@ pub(crate) fn map_runtime_profile_recharge_product_kind_back( } } -pub(crate) fn map_runtime_profile_membership_tier( - value: module_runtime::RuntimeProfileMembershipTier, -) -> crate::module_bindings::RuntimeProfileMembershipTier { +pub(crate) fn map_runtime_profile_membership_plan( + value: module_runtime::RuntimeProfileMembershipPlan, +) -> crate::module_bindings::RuntimeProfileMembershipPlan { match value { - module_runtime::RuntimeProfileMembershipTier::Normal => { - crate::module_bindings::RuntimeProfileMembershipTier::Normal + module_runtime::RuntimeProfileMembershipPlan::Normal => { + crate::module_bindings::RuntimeProfileMembershipPlan::Normal } - module_runtime::RuntimeProfileMembershipTier::Month => { - crate::module_bindings::RuntimeProfileMembershipTier::Month + module_runtime::RuntimeProfileMembershipPlan::Starter => { + crate::module_bindings::RuntimeProfileMembershipPlan::Starter } - module_runtime::RuntimeProfileMembershipTier::Season => { - crate::module_bindings::RuntimeProfileMembershipTier::Season + module_runtime::RuntimeProfileMembershipPlan::Plus => { + crate::module_bindings::RuntimeProfileMembershipPlan::Plus } - module_runtime::RuntimeProfileMembershipTier::Year => { - crate::module_bindings::RuntimeProfileMembershipTier::Year + module_runtime::RuntimeProfileMembershipPlan::Pro => { + crate::module_bindings::RuntimeProfileMembershipPlan::Pro } - module_runtime::RuntimeProfileMembershipTier::Starter => { - crate::module_bindings::RuntimeProfileMembershipTier::Starter - } - module_runtime::RuntimeProfileMembershipTier::Basic => { - crate::module_bindings::RuntimeProfileMembershipTier::Basic - } - module_runtime::RuntimeProfileMembershipTier::Pro => { - crate::module_bindings::RuntimeProfileMembershipTier::Pro - } - module_runtime::RuntimeProfileMembershipTier::Ultimate => { - crate::module_bindings::RuntimeProfileMembershipTier::Ultimate + module_runtime::RuntimeProfileMembershipPlan::Max => { + crate::module_bindings::RuntimeProfileMembershipPlan::Max } } } +pub(crate) fn map_runtime_profile_membership_plan_back( + value: crate::module_bindings::RuntimeProfileMembershipPlan, +) -> module_runtime::RuntimeProfileMembershipPlan { + match value { + crate::module_bindings::RuntimeProfileMembershipPlan::Normal => { + module_runtime::RuntimeProfileMembershipPlan::Normal + } + crate::module_bindings::RuntimeProfileMembershipPlan::Starter => { + module_runtime::RuntimeProfileMembershipPlan::Starter + } + crate::module_bindings::RuntimeProfileMembershipPlan::Plus => { + module_runtime::RuntimeProfileMembershipPlan::Plus + } + crate::module_bindings::RuntimeProfileMembershipPlan::Pro => { + module_runtime::RuntimeProfileMembershipPlan::Pro + } + crate::module_bindings::RuntimeProfileMembershipPlan::Max => { + module_runtime::RuntimeProfileMembershipPlan::Max + } + } +} + +pub(crate) fn map_runtime_profile_membership_cycle_kind_back( + value: crate::module_bindings::RuntimeProfileMembershipCycleKind, +) -> module_runtime::RuntimeProfileMembershipCycleKind { + match value { + crate::module_bindings::RuntimeProfileMembershipCycleKind::Monthly => { + module_runtime::RuntimeProfileMembershipCycleKind::Monthly + } + crate::module_bindings::RuntimeProfileMembershipCycleKind::Yearly => { + module_runtime::RuntimeProfileMembershipCycleKind::Yearly + } + } +} + +pub(crate) fn map_runtime_profile_membership_model_access( + value: module_runtime::RuntimeProfileMembershipModelAccess, +) -> crate::module_bindings::RuntimeProfileMembershipModelAccess { + match value { + module_runtime::RuntimeProfileMembershipModelAccess::Basic => { + crate::module_bindings::RuntimeProfileMembershipModelAccess::Basic + } + module_runtime::RuntimeProfileMembershipModelAccess::Full => { + crate::module_bindings::RuntimeProfileMembershipModelAccess::Full + } + } +} + +pub(crate) fn map_runtime_profile_membership_model_access_back( + value: crate::module_bindings::RuntimeProfileMembershipModelAccess, +) -> module_runtime::RuntimeProfileMembershipModelAccess { + match value { + crate::module_bindings::RuntimeProfileMembershipModelAccess::Basic => { + module_runtime::RuntimeProfileMembershipModelAccess::Basic + } + crate::module_bindings::RuntimeProfileMembershipModelAccess::Full => { + module_runtime::RuntimeProfileMembershipModelAccess::Full + } + } +} + +pub(crate) fn map_runtime_profile_membership_order_change_snapshot( + snapshot: RuntimeProfileMembershipOrderChangeSnapshot, +) -> module_runtime::RuntimeProfileMembershipOrderChangeSnapshot { + module_runtime::RuntimeProfileMembershipOrderChangeSnapshot { + change_kind: snapshot.change_kind, + plan_before: snapshot.plan_before, + plan_after: snapshot.plan_after, + cycle_kind_before: snapshot.cycle_kind_before, + cycle_kind_after: snapshot.cycle_kind_after, + period_points_before: snapshot.period_points_before, + period_points_after: snapshot.period_points_after, + granted_points_delta: snapshot.granted_points_delta, + cycle_index: snapshot.cycle_index, + expires_at_before_micros: snapshot.expires_at_before_micros, + expires_at_after_micros: snapshot.expires_at_after_micros, + cycle_resets_at_before_micros: snapshot.cycle_resets_at_before_micros, + cycle_resets_at_after_micros: snapshot.cycle_resets_at_after_micros, + price_breakdown_json: snapshot.price_breakdown_json, + } +} + +impl From + for RuntimeProfileMembershipPlanAdminListInput +{ + fn from(input: module_runtime::RuntimeProfileMembershipPlanAdminListInput) -> Self { + Self { + admin_user_id: input.admin_user_id, + } + } +} + +impl From + for RuntimeProfileMembershipPlanAdminUpsertInput +{ + fn from(input: module_runtime::RuntimeProfileMembershipPlanAdminUpsertInput) -> Self { + Self { + admin_user_id: input.admin_user_id, + plan: map_runtime_profile_membership_plan(input.plan), + title: input.title, + month_price_cents: input.month_price_cents, + year_price_cents: input.year_price_cents, + period_points: input.period_points, + model_access: map_runtime_profile_membership_model_access(input.model_access), + concurrent_job_limit: input.concurrent_job_limit, + enabled: input.enabled, + sort_order: input.sort_order, + updated_at_micros: input.updated_at_micros, + } + } +} + +pub(crate) fn map_runtime_profile_membership_plan_admin_list_procedure_result( + result: RuntimeProfileMembershipPlanAdminListProcedureResult, +) -> Result, SpacetimeClientError> { + if !result.ok { + return Err(SpacetimeClientError::procedure_failed(result.error_message)); + } + + Ok(result + .entries + .into_iter() + .map(map_runtime_profile_membership_plan_record) + .collect()) +} + +pub(crate) fn map_runtime_profile_membership_plan_admin_procedure_result( + result: RuntimeProfileMembershipPlanAdminProcedureResult, +) -> Result { + if !result.ok { + return Err(SpacetimeClientError::procedure_failed(result.error_message)); + } + + let record = result + .record + .ok_or_else(|| SpacetimeClientError::missing_snapshot("membership plan 快照"))?; + + Ok(map_runtime_profile_membership_plan_record(record)) +} + pub(crate) fn map_runtime_profile_membership_status_back( value: crate::module_bindings::RuntimeProfileMembershipStatus, ) -> module_runtime::RuntimeProfileMembershipStatus { @@ -2000,37 +2117,6 @@ pub(crate) fn map_runtime_profile_membership_status_back( } } -pub(crate) fn map_runtime_profile_membership_tier_back( - value: crate::module_bindings::RuntimeProfileMembershipTier, -) -> module_runtime::RuntimeProfileMembershipTier { - match value { - crate::module_bindings::RuntimeProfileMembershipTier::Normal => { - module_runtime::RuntimeProfileMembershipTier::Normal - } - crate::module_bindings::RuntimeProfileMembershipTier::Month => { - module_runtime::RuntimeProfileMembershipTier::Month - } - crate::module_bindings::RuntimeProfileMembershipTier::Season => { - module_runtime::RuntimeProfileMembershipTier::Season - } - crate::module_bindings::RuntimeProfileMembershipTier::Year => { - module_runtime::RuntimeProfileMembershipTier::Year - } - crate::module_bindings::RuntimeProfileMembershipTier::Starter => { - module_runtime::RuntimeProfileMembershipTier::Starter - } - crate::module_bindings::RuntimeProfileMembershipTier::Basic => { - module_runtime::RuntimeProfileMembershipTier::Basic - } - crate::module_bindings::RuntimeProfileMembershipTier::Pro => { - module_runtime::RuntimeProfileMembershipTier::Pro - } - crate::module_bindings::RuntimeProfileMembershipTier::Ultimate => { - module_runtime::RuntimeProfileMembershipTier::Ultimate - } - } -} - pub(crate) fn map_runtime_profile_recharge_order_status_back( value: crate::module_bindings::RuntimeProfileRechargeOrderStatus, ) -> module_runtime::RuntimeProfileRechargeOrderStatus { diff --git a/server-rs/crates/spacetime-client/src/active/runtime.rs b/server-rs/crates/spacetime-client/src/active/runtime.rs index 119393fb1..866d78b5c 100644 --- a/server-rs/crates/spacetime-client/src/active/runtime.rs +++ b/server-rs/crates/spacetime-client/src/active/runtime.rs @@ -1652,11 +1652,6 @@ impl SpacetimeClient { duration_days: u32, badge_label: String, description: String, - tier: module_runtime::RuntimeProfileMembershipTier, - membership_period_points: u64, - membership_period_days: u32, - membership_queue_limit: u32, - membership_discount_bps: u32, enabled: bool, sort_order: i32, updated_at_micros: i64, @@ -1672,11 +1667,6 @@ impl SpacetimeClient { duration_days, badge_label, description, - tier, - membership_period_points, - membership_period_days, - membership_queue_limit, - membership_discount_bps, enabled, sort_order, updated_at_micros, @@ -1705,6 +1695,86 @@ impl SpacetimeClient { .await } + /// 后台读取会员档位目录(价格与权益的唯一真相源)。 + pub async fn admin_list_profile_membership_plans( + &self, + admin_user_id: String, + ) -> Result, SpacetimeClientError> { + let procedure_input = build_runtime_profile_membership_plan_admin_list_input(admin_user_id) + .map_err(SpacetimeClientError::validation_failed)? + .into(); + + self.call_after_connect( + "admin_list_profile_membership_plans", + move |connection, sender| { + connection + .procedures() + .admin_list_profile_membership_plans_then(procedure_input, move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then( + map_runtime_profile_membership_plan_admin_list_procedure_result, + ); + send_once(&sender, mapped); + }); + }, + ) + .await + } + + /// 后台按档位改会员价格与权益;`rank` 由代码内目录决定,后台不能改。 + #[allow(clippy::too_many_arguments)] + pub async fn admin_upsert_profile_membership_plan( + &self, + admin_user_id: String, + plan: module_runtime::RuntimeProfileMembershipPlan, + title: String, + month_price_cents: u64, + year_price_cents: u64, + period_points: u64, + model_access: module_runtime::RuntimeProfileMembershipModelAccess, + concurrent_job_limit: u32, + enabled: bool, + sort_order: i32, + updated_at_micros: i64, + ) -> Result { + let procedure_input = build_runtime_profile_membership_plan_admin_upsert_input( + admin_user_id, + plan, + title, + month_price_cents, + year_price_cents, + period_points, + model_access, + concurrent_job_limit, + enabled, + sort_order, + updated_at_micros, + ) + .map_err(SpacetimeClientError::validation_failed)? + .into(); + + self.call_after_connect( + "admin_upsert_profile_membership_plan", + move |connection, sender| { + connection + .procedures() + .admin_upsert_profile_membership_plan_then( + procedure_input, + move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then( + map_runtime_profile_membership_plan_admin_procedure_result, + ); + send_once(&sender, mapped); + }, + ); + }, + ) + .await + } + pub async fn admin_upsert_profile_redeem_code( &self, admin_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 dfb4367c4..3615b2453 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings.rs @@ -38,6 +38,7 @@ pub mod admin_initialize_profile_wallet_consumption_projections_and_return_proce pub mod admin_list_editor_assets_and_return_procedure; pub mod admin_list_editor_showcase_assets_and_return_procedure; pub mod admin_list_profile_invite_codes_procedure; +pub mod admin_list_profile_membership_plans_procedure; pub mod admin_list_profile_recharge_orders_and_return_procedure; pub mod admin_list_profile_recharge_products_procedure; pub mod admin_list_profile_redeem_codes_procedure; @@ -45,6 +46,7 @@ pub mod admin_list_profile_task_configs_procedure; pub mod admin_reconcile_profile_wallet_consumption_and_return_procedure; pub mod admin_review_editor_showcase_asset_and_return_procedure; pub mod admin_upsert_profile_invite_code_procedure; +pub mod admin_upsert_profile_membership_plan_procedure; pub mod admin_upsert_profile_recharge_product_procedure; pub mod admin_upsert_profile_redeem_code_procedure; pub mod admin_upsert_profile_task_config_procedure; @@ -648,12 +650,18 @@ pub mod runtime_profile_invite_code_admin_list_procedure_result_type; pub mod runtime_profile_invite_code_admin_procedure_result_type; pub mod runtime_profile_invite_code_admin_upsert_input_type; pub mod runtime_profile_invite_code_snapshot_type; -pub mod runtime_profile_membership_benefit_snapshot_type; +pub mod runtime_profile_membership_change_kind_type; +pub mod runtime_profile_membership_cycle_kind_type; pub mod runtime_profile_membership_model_access_type; +pub mod runtime_profile_membership_order_change_snapshot_type; +pub mod runtime_profile_membership_plan_admin_list_input_type; +pub mod runtime_profile_membership_plan_admin_list_procedure_result_type; +pub mod runtime_profile_membership_plan_admin_procedure_result_type; +pub mod runtime_profile_membership_plan_admin_upsert_input_type; +pub mod runtime_profile_membership_plan_record_type; pub mod runtime_profile_membership_plan_type; pub mod runtime_profile_membership_snapshot_type; pub mod runtime_profile_membership_status_type; -pub mod runtime_profile_membership_tier_type; pub mod runtime_profile_recharge_center_get_input_type; pub mod runtime_profile_recharge_center_procedure_result_type; pub mod runtime_profile_recharge_center_snapshot_type; @@ -843,6 +851,7 @@ pub use admin_initialize_profile_wallet_consumption_projections_and_return_proce pub use admin_list_editor_assets_and_return_procedure::admin_list_editor_assets_and_return; pub use admin_list_editor_showcase_assets_and_return_procedure::admin_list_editor_showcase_assets_and_return; pub use admin_list_profile_invite_codes_procedure::admin_list_profile_invite_codes; +pub use admin_list_profile_membership_plans_procedure::admin_list_profile_membership_plans; pub use admin_list_profile_recharge_orders_and_return_procedure::admin_list_profile_recharge_orders_and_return; pub use admin_list_profile_recharge_products_procedure::admin_list_profile_recharge_products; pub use admin_list_profile_redeem_codes_procedure::admin_list_profile_redeem_codes; @@ -850,6 +859,7 @@ pub use admin_list_profile_task_configs_procedure::admin_list_profile_task_confi pub use admin_reconcile_profile_wallet_consumption_and_return_procedure::admin_reconcile_profile_wallet_consumption_and_return; pub use admin_review_editor_showcase_asset_and_return_procedure::admin_review_editor_showcase_asset_and_return; pub use admin_upsert_profile_invite_code_procedure::admin_upsert_profile_invite_code; +pub use admin_upsert_profile_membership_plan_procedure::admin_upsert_profile_membership_plan; pub use admin_upsert_profile_recharge_product_procedure::admin_upsert_profile_recharge_product; pub use admin_upsert_profile_redeem_code_procedure::admin_upsert_profile_redeem_code; pub use admin_upsert_profile_task_config_procedure::admin_upsert_profile_task_config; @@ -1453,12 +1463,18 @@ pub use runtime_profile_invite_code_admin_list_procedure_result_type::RuntimePro pub use runtime_profile_invite_code_admin_procedure_result_type::RuntimeProfileInviteCodeAdminProcedureResult; pub use runtime_profile_invite_code_admin_upsert_input_type::RuntimeProfileInviteCodeAdminUpsertInput; pub use runtime_profile_invite_code_snapshot_type::RuntimeProfileInviteCodeSnapshot; -pub use runtime_profile_membership_benefit_snapshot_type::RuntimeProfileMembershipBenefitSnapshot; +pub use runtime_profile_membership_change_kind_type::RuntimeProfileMembershipChangeKind; +pub use runtime_profile_membership_cycle_kind_type::RuntimeProfileMembershipCycleKind; pub use runtime_profile_membership_model_access_type::RuntimeProfileMembershipModelAccess; +pub use runtime_profile_membership_order_change_snapshot_type::RuntimeProfileMembershipOrderChangeSnapshot; +pub use runtime_profile_membership_plan_admin_list_input_type::RuntimeProfileMembershipPlanAdminListInput; +pub use runtime_profile_membership_plan_admin_list_procedure_result_type::RuntimeProfileMembershipPlanAdminListProcedureResult; +pub use runtime_profile_membership_plan_admin_procedure_result_type::RuntimeProfileMembershipPlanAdminProcedureResult; +pub use runtime_profile_membership_plan_admin_upsert_input_type::RuntimeProfileMembershipPlanAdminUpsertInput; +pub use runtime_profile_membership_plan_record_type::RuntimeProfileMembershipPlanRecord; pub use runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; pub use runtime_profile_membership_snapshot_type::RuntimeProfileMembershipSnapshot; pub use runtime_profile_membership_status_type::RuntimeProfileMembershipStatus; -pub use runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; pub use runtime_profile_recharge_center_get_input_type::RuntimeProfileRechargeCenterGetInput; pub use runtime_profile_recharge_center_procedure_result_type::RuntimeProfileRechargeCenterProcedureResult; pub use runtime_profile_recharge_center_snapshot_type::RuntimeProfileRechargeCenterSnapshot; @@ -3992,19 +4008,19 @@ impl __sdk::SubscriptionHandle for SubscriptionHandle { /// either a [`DbConnection`] or an [`EventContext`] and operate on either. pub trait RemoteDbContext: __sdk::DbContext< - DbView = RemoteTables, - Reducers = RemoteReducers, - SubscriptionBuilder = __sdk::SubscriptionBuilder, - > + DbView = RemoteTables, + Reducers = RemoteReducers, + SubscriptionBuilder = __sdk::SubscriptionBuilder, +> { } impl< - Ctx: __sdk::DbContext< + Ctx: __sdk::DbContext< DbView = RemoteTables, Reducers = RemoteReducers, SubscriptionBuilder = __sdk::SubscriptionBuilder, >, -> RemoteDbContext for Ctx + > RemoteDbContext for Ctx { } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_membership_plans_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_membership_plans_procedure.rs new file mode 100644 index 000000000..6f1dc3cd1 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_membership_plans_procedure.rs @@ -0,0 +1,61 @@ +// 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_plan_admin_list_input_type::RuntimeProfileMembershipPlanAdminListInput; +use super::runtime_profile_membership_plan_admin_list_procedure_result_type::RuntimeProfileMembershipPlanAdminListProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct AdminListProfileMembershipPlansArgs { + pub input: RuntimeProfileMembershipPlanAdminListInput, +} + +impl __sdk::InModule for AdminListProfileMembershipPlansArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `admin_list_profile_membership_plans`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait admin_list_profile_membership_plans { + fn admin_list_profile_membership_plans( + &self, + input: RuntimeProfileMembershipPlanAdminListInput, + ) { + self.admin_list_profile_membership_plans_then(input, |_, _| {}); + } + + fn admin_list_profile_membership_plans_then( + &self, + input: RuntimeProfileMembershipPlanAdminListInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl admin_list_profile_membership_plans for super::RemoteProcedures { + fn admin_list_profile_membership_plans_then( + &self, + input: RuntimeProfileMembershipPlanAdminListInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileMembershipPlanAdminListProcedureResult>( + "admin_list_profile_membership_plans", + AdminListProfileMembershipPlansArgs { input, }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_membership_plan_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_membership_plan_procedure.rs new file mode 100644 index 000000000..3ff1b58f7 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_membership_plan_procedure.rs @@ -0,0 +1,62 @@ +// 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_plan_admin_procedure_result_type::RuntimeProfileMembershipPlanAdminProcedureResult; +use super::runtime_profile_membership_plan_admin_upsert_input_type::RuntimeProfileMembershipPlanAdminUpsertInput; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct AdminUpsertProfileMembershipPlanArgs { + pub input: RuntimeProfileMembershipPlanAdminUpsertInput, +} + +impl __sdk::InModule for AdminUpsertProfileMembershipPlanArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `admin_upsert_profile_membership_plan`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait admin_upsert_profile_membership_plan { + fn admin_upsert_profile_membership_plan( + &self, + input: RuntimeProfileMembershipPlanAdminUpsertInput, + ) { + self.admin_upsert_profile_membership_plan_then(input, |_, _| {}); + } + + fn admin_upsert_profile_membership_plan_then( + &self, + input: RuntimeProfileMembershipPlanAdminUpsertInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl admin_upsert_profile_membership_plan for super::RemoteProcedures { + fn admin_upsert_profile_membership_plan_then( + &self, + input: RuntimeProfileMembershipPlanAdminUpsertInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp + .invoke_procedure_with_callback::<_, RuntimeProfileMembershipPlanAdminProcedureResult>( + "admin_upsert_profile_membership_plan", + AdminUpsertProfileMembershipPlanArgs { input }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_admin_game_distribution_user_review_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_admin_game_distribution_user_review_and_return_procedure.rs index fc6340e6e..644d782d8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_admin_game_distribution_user_review_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_admin_game_distribution_user_review_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait get_admin_game_distribution_user_review_and_return { input: GameDistributionAdminUserReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl get_admin_game_distribution_user_review_and_return for super::RemoteProcedu input: GameDistributionAdminUserReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, GameDistributionAdminUserReviewResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_game_distribution_my_review_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_game_distribution_my_review_and_return_procedure.rs index 5d41df632..035b88be5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_game_distribution_my_review_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_game_distribution_my_review_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait get_game_distribution_my_review_and_return { input: GameDistributionMyReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_game_distribution_my_review_and_return for super::RemoteProcedures { input: GameDistributionMyReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, GameDistributionUserReviewResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_admin_game_distribution_user_reviews_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_admin_game_distribution_user_reviews_and_return_procedure.rs index 854482048..0b1536fb3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_admin_game_distribution_user_reviews_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_admin_game_distribution_user_reviews_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait list_admin_game_distribution_user_reviews_and_return { input: GameDistributionAdminUserReviewListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl list_admin_game_distribution_user_reviews_and_return for super::RemoteProce input: GameDistributionAdminUserReviewListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, GameDistributionAdminUserReviewResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_agc_tracking_events_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_agc_tracking_events_procedure.rs index 6244fd337..5daaa8425 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_agc_tracking_events_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_agc_tracking_events_procedure.rs @@ -28,10 +28,10 @@ pub trait list_agc_tracking_events { query_json: String, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, __sdk::InternalError>, + ) + Send + + 'static, ); } @@ -41,10 +41,10 @@ impl list_agc_tracking_events for super::RemoteProcedures { query_json: String, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, __sdk::InternalError>, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Result>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_review_games_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_review_games_and_return_procedure.rs index 7843651e5..2d2b893c0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_review_games_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_review_games_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait list_game_distribution_review_games_and_return { input: GameDistributionReviewGameListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl list_game_distribution_review_games_and_return for super::RemoteProcedures input: GameDistributionReviewGameListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, GameDistributionAdminUserReviewResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_user_reviews_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_user_reviews_and_return_procedure.rs index 253592e7e..1abc81320 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_user_reviews_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_game_distribution_user_reviews_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait list_game_distribution_user_reviews_and_return { input: GameDistributionUserReviewListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl list_game_distribution_user_reviews_and_return for super::RemoteProcedures input: GameDistributionUserReviewListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, GameDistributionUserReviewResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/moderate_game_distribution_user_review_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/moderate_game_distribution_user_review_and_return_procedure.rs index 57af37e0d..071918ac1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/moderate_game_distribution_user_review_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/moderate_game_distribution_user_review_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait moderate_game_distribution_user_review_and_return { input: GameDistributionReviewModerationInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl moderate_game_distribution_user_review_and_return for super::RemoteProcedur input: GameDistributionReviewModerationInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, GameDistributionAdminUserReviewResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_table.rs index a06543e88..b1b70a4db 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_table.rs @@ -3,8 +3,9 @@ #![allow(unused, clippy::all)] use super::profile_membership_type::ProfileMembership; +use super::runtime_profile_membership_cycle_kind_type::RuntimeProfileMembershipCycleKind; +use super::runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; use super::runtime_profile_membership_status_type::RuntimeProfileMembershipStatus; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; /// Table handle for the table `profile_membership`. diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs index 8d1757038..01c8e27d6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs @@ -4,15 +4,15 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; +use super::runtime_profile_membership_cycle_kind_type::RuntimeProfileMembershipCycleKind; +use super::runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; use super::runtime_profile_membership_status_type::RuntimeProfileMembershipStatus; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct ProfileMembership { pub user_id: String, pub status: RuntimeProfileMembershipStatus, - pub tier: RuntimeProfileMembershipTier, pub started_at: __sdk::Timestamp, pub expires_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, @@ -20,7 +20,10 @@ pub struct ProfileMembership { pub cycle_resets_at: Option<__sdk::Timestamp>, pub cycle_granted_points: u64, pub cycle_remaining_points: u64, - pub cycle_period_days: u32, + pub plan: RuntimeProfileMembershipPlan, + pub cycle_index: u32, + pub cycle_count: u32, + pub cycle_kind: RuntimeProfileMembershipCycleKind, } impl __sdk::InModule for ProfileMembership { @@ -33,7 +36,6 @@ impl __sdk::InModule for ProfileMembership { pub struct ProfileMembershipCols { pub user_id: __sdk::__query_builder::Col, pub status: __sdk::__query_builder::Col, - pub tier: __sdk::__query_builder::Col, pub started_at: __sdk::__query_builder::Col, pub expires_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, @@ -41,7 +43,11 @@ pub struct ProfileMembershipCols { pub cycle_resets_at: __sdk::__query_builder::Col>, pub cycle_granted_points: __sdk::__query_builder::Col, pub cycle_remaining_points: __sdk::__query_builder::Col, - pub cycle_period_days: __sdk::__query_builder::Col, + pub plan: __sdk::__query_builder::Col, + pub cycle_index: __sdk::__query_builder::Col, + pub cycle_count: __sdk::__query_builder::Col, + pub cycle_kind: + __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for ProfileMembership { @@ -50,7 +56,6 @@ impl __sdk::__query_builder::HasCols for ProfileMembership { ProfileMembershipCols { user_id: __sdk::__query_builder::Col::new(table_name, "user_id"), status: __sdk::__query_builder::Col::new(table_name, "status"), - tier: __sdk::__query_builder::Col::new(table_name, "tier"), started_at: __sdk::__query_builder::Col::new(table_name, "started_at"), expires_at: __sdk::__query_builder::Col::new(table_name, "expires_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), @@ -64,7 +69,10 @@ impl __sdk::__query_builder::HasCols for ProfileMembership { table_name, "cycle_remaining_points", ), - cycle_period_days: __sdk::__query_builder::Col::new(table_name, "cycle_period_days"), + plan: __sdk::__query_builder::Col::new(table_name, "plan"), + cycle_index: __sdk::__query_builder::Col::new(table_name, "cycle_index"), + cycle_count: __sdk::__query_builder::Col::new(table_name, "cycle_count"), + cycle_kind: __sdk::__query_builder::Col::new(table_name, "cycle_kind"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_table.rs index 3869911a9..ae9030d06 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_table.rs @@ -3,6 +3,9 @@ #![allow(unused, clippy::all)] use super::profile_recharge_order_type::ProfileRechargeOrder; +use super::runtime_profile_membership_change_kind_type::RuntimeProfileMembershipChangeKind; +use super::runtime_profile_membership_cycle_kind_type::RuntimeProfileMembershipCycleKind; +use super::runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; use super::runtime_profile_recharge_order_status_type::RuntimeProfileRechargeOrderStatus; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs index f12c3f8d0..573be0d91 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs @@ -4,6 +4,9 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; +use super::runtime_profile_membership_change_kind_type::RuntimeProfileMembershipChangeKind; +use super::runtime_profile_membership_cycle_kind_type::RuntimeProfileMembershipCycleKind; +use super::runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; use super::runtime_profile_recharge_order_status_type::RuntimeProfileRechargeOrderStatus; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; @@ -27,6 +30,20 @@ pub struct ProfileRechargeOrder { pub expiration_checked_at: Option<__sdk::Timestamp>, pub expiration_provider_state: Option, pub expiration_last_error: Option, + pub membership_change_kind: Option, + pub membership_plan_before: Option, + pub membership_plan_after: Option, + pub membership_cycle_kind_before: Option, + pub membership_cycle_kind_after: Option, + pub membership_period_points_before: Option, + pub membership_period_points_after: Option, + pub membership_granted_points_delta: i64, + pub membership_cycle_index: Option, + pub membership_expires_at_before: Option<__sdk::Timestamp>, + pub membership_expires_at_after: Option<__sdk::Timestamp>, + pub membership_cycle_resets_at_before: Option<__sdk::Timestamp>, + pub membership_cycle_resets_at_after: Option<__sdk::Timestamp>, + pub membership_price_breakdown_json: Option, } impl __sdk::InModule for ProfileRechargeOrder { @@ -58,6 +75,38 @@ pub struct ProfileRechargeOrderCols { pub expiration_provider_state: __sdk::__query_builder::Col>, pub expiration_last_error: __sdk::__query_builder::Col>, + pub membership_change_kind: __sdk::__query_builder::Col< + ProfileRechargeOrder, + Option, + >, + pub membership_plan_before: + __sdk::__query_builder::Col>, + pub membership_plan_after: + __sdk::__query_builder::Col>, + pub membership_cycle_kind_before: __sdk::__query_builder::Col< + ProfileRechargeOrder, + Option, + >, + pub membership_cycle_kind_after: __sdk::__query_builder::Col< + ProfileRechargeOrder, + Option, + >, + pub membership_period_points_before: + __sdk::__query_builder::Col>, + pub membership_period_points_after: + __sdk::__query_builder::Col>, + pub membership_granted_points_delta: __sdk::__query_builder::Col, + pub membership_cycle_index: __sdk::__query_builder::Col>, + pub membership_expires_at_before: + __sdk::__query_builder::Col>, + pub membership_expires_at_after: + __sdk::__query_builder::Col>, + pub membership_cycle_resets_at_before: + __sdk::__query_builder::Col>, + pub membership_cycle_resets_at_after: + __sdk::__query_builder::Col>, + pub membership_price_breakdown_json: + __sdk::__query_builder::Col>, } impl __sdk::__query_builder::HasCols for ProfileRechargeOrder { @@ -96,6 +145,62 @@ impl __sdk::__query_builder::HasCols for ProfileRechargeOrder { table_name, "expiration_last_error", ), + membership_change_kind: __sdk::__query_builder::Col::new( + table_name, + "membership_change_kind", + ), + membership_plan_before: __sdk::__query_builder::Col::new( + table_name, + "membership_plan_before", + ), + membership_plan_after: __sdk::__query_builder::Col::new( + table_name, + "membership_plan_after", + ), + membership_cycle_kind_before: __sdk::__query_builder::Col::new( + table_name, + "membership_cycle_kind_before", + ), + membership_cycle_kind_after: __sdk::__query_builder::Col::new( + table_name, + "membership_cycle_kind_after", + ), + membership_period_points_before: __sdk::__query_builder::Col::new( + table_name, + "membership_period_points_before", + ), + membership_period_points_after: __sdk::__query_builder::Col::new( + table_name, + "membership_period_points_after", + ), + membership_granted_points_delta: __sdk::__query_builder::Col::new( + table_name, + "membership_granted_points_delta", + ), + membership_cycle_index: __sdk::__query_builder::Col::new( + table_name, + "membership_cycle_index", + ), + membership_expires_at_before: __sdk::__query_builder::Col::new( + table_name, + "membership_expires_at_before", + ), + membership_expires_at_after: __sdk::__query_builder::Col::new( + table_name, + "membership_expires_at_after", + ), + membership_cycle_resets_at_before: __sdk::__query_builder::Col::new( + table_name, + "membership_cycle_resets_at_before", + ), + membership_cycle_resets_at_after: __sdk::__query_builder::Col::new( + table_name, + "membership_cycle_resets_at_after", + ), + membership_price_breakdown_json: __sdk::__query_builder::Col::new( + table_name, + "membership_price_breakdown_json", + ), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_table.rs index cf0d11d37..d5a19ee07 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_table.rs @@ -3,7 +3,6 @@ #![allow(unused, clippy::all)] use super::profile_recharge_product_config_type::ProfileRechargeProductConfig; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_type.rs index e25730490..e3c80e159 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_product_config_type.rs @@ -4,7 +4,6 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -19,17 +18,12 @@ pub struct ProfileRechargeProductConfig { pub duration_days: u32, pub badge_label: String, pub description: String, - pub tier: RuntimeProfileMembershipTier, pub enabled: bool, pub sort_order: i32, pub created_by: String, pub created_at: __sdk::Timestamp, pub updated_by: String, pub updated_at: __sdk::Timestamp, - pub membership_period_points: u64, - pub membership_period_days: u32, - pub membership_queue_limit: u32, - pub membership_discount_bps: u32, } impl __sdk::InModule for ProfileRechargeProductConfig { @@ -52,18 +46,12 @@ pub struct ProfileRechargeProductConfigCols { pub duration_days: __sdk::__query_builder::Col, pub badge_label: __sdk::__query_builder::Col, pub description: __sdk::__query_builder::Col, - pub tier: - __sdk::__query_builder::Col, pub enabled: __sdk::__query_builder::Col, pub sort_order: __sdk::__query_builder::Col, pub created_by: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, pub updated_by: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, - pub membership_period_points: __sdk::__query_builder::Col, - pub membership_period_days: __sdk::__query_builder::Col, - pub membership_queue_limit: __sdk::__query_builder::Col, - pub membership_discount_bps: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for ProfileRechargeProductConfig { @@ -79,29 +67,12 @@ impl __sdk::__query_builder::HasCols for ProfileRechargeProductConfig { duration_days: __sdk::__query_builder::Col::new(table_name, "duration_days"), badge_label: __sdk::__query_builder::Col::new(table_name, "badge_label"), description: __sdk::__query_builder::Col::new(table_name, "description"), - tier: __sdk::__query_builder::Col::new(table_name, "tier"), enabled: __sdk::__query_builder::Col::new(table_name, "enabled"), sort_order: __sdk::__query_builder::Col::new(table_name, "sort_order"), created_by: __sdk::__query_builder::Col::new(table_name, "created_by"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_by: __sdk::__query_builder::Col::new(table_name, "updated_by"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), - membership_period_points: __sdk::__query_builder::Col::new( - table_name, - "membership_period_points", - ), - membership_period_days: __sdk::__query_builder::Col::new( - table_name, - "membership_period_days", - ), - membership_queue_limit: __sdk::__query_builder::Col::new( - table_name, - "membership_queue_limit", - ), - membership_discount_bps: __sdk::__query_builder::Col::new( - table_name, - "membership_discount_bps", - ), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_tier_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_change_kind_type.rs similarity index 68% rename from server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_tier_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_change_kind_type.rs index f8c9e0748..5c6d03e00 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_tier_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_change_kind_type.rs @@ -7,24 +7,12 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] #[derive(Copy, Eq, Hash)] -pub enum RuntimeProfileMembershipTier { - Normal, +pub enum RuntimeProfileMembershipChangeKind { + Purchase, - Month, - - Season, - - Year, - - Starter, - - Basic, - - Pro, - - Ultimate, + Upgrade, } -impl __sdk::InModule for RuntimeProfileMembershipTier { +impl __sdk::InModule for RuntimeProfileMembershipChangeKind { type Module = super::RemoteModule; } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_benefit_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_cycle_kind_type.rs similarity index 50% rename from server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_benefit_snapshot_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_cycle_kind_type.rs index 675038d3f..7de7247b1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_benefit_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_cycle_kind_type.rs @@ -6,18 +6,13 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -pub struct RuntimeProfileMembershipBenefitSnapshot { - pub benefit_name: String, - pub normal_value: String, - pub month_value: String, - pub season_value: String, - pub year_value: String, - pub starter_value: String, - pub basic_value: String, - pub pro_value: String, - pub ultimate_value: String, +#[derive(Copy, Eq, Hash)] +pub enum RuntimeProfileMembershipCycleKind { + Monthly, + + Yearly, } -impl __sdk::InModule for RuntimeProfileMembershipBenefitSnapshot { +impl __sdk::InModule for RuntimeProfileMembershipCycleKind { type Module = super::RemoteModule; } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_order_change_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_order_change_snapshot_type.rs new file mode 100644 index 000000000..bbef8b8d4 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_order_change_snapshot_type.rs @@ -0,0 +1,28 @@ +// 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}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct RuntimeProfileMembershipOrderChangeSnapshot { + pub change_kind: String, + pub plan_before: Option, + pub plan_after: Option, + pub cycle_kind_before: Option, + pub cycle_kind_after: Option, + pub period_points_before: Option, + pub period_points_after: Option, + pub granted_points_delta: i64, + pub cycle_index: Option, + pub expires_at_before_micros: Option, + pub expires_at_after_micros: Option, + pub cycle_resets_at_before_micros: Option, + pub cycle_resets_at_after_micros: Option, + pub price_breakdown_json: Option, +} + +impl __sdk::InModule for RuntimeProfileMembershipOrderChangeSnapshot { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_input_type.rs new file mode 100644 index 000000000..9727106a1 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_input_type.rs @@ -0,0 +1,15 @@ +// 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}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct RuntimeProfileMembershipPlanAdminListInput { + pub admin_user_id: String, +} + +impl __sdk::InModule for RuntimeProfileMembershipPlanAdminListInput { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_procedure_result_type.rs new file mode 100644 index 000000000..52c8aa417 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_list_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_membership_plan_record_type::RuntimeProfileMembershipPlanRecord; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct RuntimeProfileMembershipPlanAdminListProcedureResult { + pub ok: bool, + pub entries: Vec, + pub error_message: Option, +} + +impl __sdk::InModule for RuntimeProfileMembershipPlanAdminListProcedureResult { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_procedure_result_type.rs new file mode 100644 index 000000000..78ee5407e --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_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_membership_plan_record_type::RuntimeProfileMembershipPlanRecord; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct RuntimeProfileMembershipPlanAdminProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +impl __sdk::InModule for RuntimeProfileMembershipPlanAdminProcedureResult { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_upsert_input_type.rs new file mode 100644 index 000000000..9e38b8171 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_admin_upsert_input_type.rs @@ -0,0 +1,28 @@ +// 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 RuntimeProfileMembershipPlanAdminUpsertInput { + pub admin_user_id: String, + pub plan: RuntimeProfileMembershipPlan, + pub title: String, + pub month_price_cents: u64, + pub year_price_cents: u64, + pub period_points: u64, + pub model_access: RuntimeProfileMembershipModelAccess, + pub concurrent_job_limit: u32, + pub enabled: bool, + pub sort_order: i32, + pub updated_at_micros: i64, +} + +impl __sdk::InModule for RuntimeProfileMembershipPlanAdminUpsertInput { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_record_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_record_type.rs new file mode 100644 index 000000000..73361a190 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_record_type.rs @@ -0,0 +1,27 @@ +// 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 RuntimeProfileMembershipPlanRecord { + pub plan: RuntimeProfileMembershipPlan, + pub title: String, + pub rank: u8, + pub month_price_cents: u64, + pub year_price_cents: u64, + pub period_points: u64, + pub model_access: RuntimeProfileMembershipModelAccess, + pub concurrent_job_limit: u32, + pub sort_order: i32, + pub enabled: bool, +} + +impl __sdk::InModule for RuntimeProfileMembershipPlanRecord { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs index aff6da4ef..87eecbec4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs @@ -4,15 +4,16 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; +use super::runtime_profile_membership_cycle_kind_type::RuntimeProfileMembershipCycleKind; +use super::runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; use super::runtime_profile_membership_status_type::RuntimeProfileMembershipStatus; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct RuntimeProfileMembershipSnapshot { pub user_id: String, pub status: RuntimeProfileMembershipStatus, - pub tier: RuntimeProfileMembershipTier, + pub plan: RuntimeProfileMembershipPlan, pub started_at_micros: Option, pub expires_at_micros: Option, pub updated_at_micros: Option, @@ -20,7 +21,9 @@ pub struct RuntimeProfileMembershipSnapshot { pub cycle_resets_at_micros: Option, pub cycle_granted_points: u64, pub cycle_remaining_points: u64, - pub cycle_period_days: u32, + pub cycle_index: u32, + pub cycle_count: u32, + pub cycle_kind: RuntimeProfileMembershipCycleKind, } impl __sdk::InModule for RuntimeProfileMembershipSnapshot { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs index 747739cc1..504e1269d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs @@ -5,7 +5,7 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use super::runtime_profile_daily_free_points_snapshot_type::RuntimeProfileDailyFreePointsSnapshot; -use super::runtime_profile_membership_benefit_snapshot_type::RuntimeProfileMembershipBenefitSnapshot; +use super::runtime_profile_membership_plan_record_type::RuntimeProfileMembershipPlanRecord; use super::runtime_profile_membership_snapshot_type::RuntimeProfileMembershipSnapshot; use super::runtime_profile_recharge_order_snapshot_type::RuntimeProfileRechargeOrderSnapshot; use super::runtime_profile_recharge_product_snapshot_type::RuntimeProfileRechargeProductSnapshot; @@ -17,8 +17,7 @@ pub struct RuntimeProfileRechargeCenterSnapshot { pub wallet_balance: u64, pub membership: RuntimeProfileMembershipSnapshot, pub point_products: Vec, - pub membership_products: Vec, - pub benefits: Vec, + pub membership_plans: Vec, pub latest_order: Option, pub has_points_recharged: bool, pub daily_free_points: RuntimeProfileDailyFreePointsSnapshot, diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_admin_entry_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_admin_entry_snapshot_type.rs index dcc036214..d5a26cf9a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_admin_entry_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_admin_entry_snapshot_type.rs @@ -5,6 +5,7 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use super::runtime_profile_admin_wallet_snapshot_type::RuntimeProfileAdminWalletSnapshot; +use super::runtime_profile_membership_order_change_snapshot_type::RuntimeProfileMembershipOrderChangeSnapshot; use super::runtime_profile_recharge_order_refund_settlement_snapshot_type::RuntimeProfileRechargeOrderRefundSettlementSnapshot; use super::runtime_profile_recharge_order_snapshot_type::RuntimeProfileRechargeOrderSnapshot; use super::runtime_profile_recharge_refund_hold_snapshot_type::RuntimeProfileRechargeRefundHoldSnapshot; @@ -18,6 +19,7 @@ pub struct RuntimeProfileRechargeOrderAdminEntrySnapshot { pub refunds: Vec, pub active_hold: Option, pub wallet: RuntimeProfileAdminWalletSnapshot, + pub membership_change: Option, } impl __sdk::InModule for RuntimeProfileRechargeOrderAdminEntrySnapshot { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_admin_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_admin_upsert_input_type.rs index 6713a169b..a3babfb24 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_admin_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_admin_upsert_input_type.rs @@ -4,7 +4,6 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -20,11 +19,6 @@ pub struct RuntimeProfileRechargeProductAdminUpsertInput { pub duration_days: u32, pub badge_label: String, pub description: String, - pub tier: RuntimeProfileMembershipTier, - pub membership_period_points: u64, - pub membership_period_days: u32, - pub membership_queue_limit: u32, - pub membership_discount_bps: u32, pub enabled: bool, pub sort_order: i32, pub updated_at_micros: i64, diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_config_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_config_snapshot_type.rs index 04654580f..44904194e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_config_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_config_snapshot_type.rs @@ -4,7 +4,6 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -19,11 +18,6 @@ pub struct RuntimeProfileRechargeProductConfigSnapshot { pub duration_days: u32, pub badge_label: String, pub description: String, - pub tier: RuntimeProfileMembershipTier, - pub membership_period_points: u64, - pub membership_period_days: u32, - pub membership_queue_limit: u32, - pub membership_discount_bps: u32, pub enabled: bool, pub sort_order: i32, pub created_by: String, diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs index 9b70f76cd..9a2bd0234 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs @@ -4,7 +4,6 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -19,11 +18,6 @@ pub struct RuntimeProfileRechargeProductSnapshot { pub duration_days: u32, pub badge_label: String, pub description: String, - pub tier: RuntimeProfileMembershipTier, - pub membership_period_points: u64, - pub membership_period_days: u32, - pub membership_queue_limit: u32, - pub membership_discount_bps: u32, } impl __sdk::InModule for RuntimeProfileRechargeProductSnapshot { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/save_game_distribution_my_review_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/save_game_distribution_my_review_and_return_procedure.rs index 5e331c484..271415f65 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/save_game_distribution_my_review_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/save_game_distribution_my_review_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait save_game_distribution_my_review_and_return { input: GameDistributionUserReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl save_game_distribution_my_review_and_return for super::RemoteProcedures { input: GameDistributionUserReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, GameDistributionUserReviewResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upload_agc_analytics_batch_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upload_agc_analytics_batch_procedure.rs index c6fb47968..cc583dde6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upload_agc_analytics_batch_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upload_agc_analytics_batch_procedure.rs @@ -28,10 +28,10 @@ pub trait upload_agc_analytics_batch { payload_json: String, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, __sdk::InternalError>, + ) + Send + + 'static, ); } @@ -41,10 +41,10 @@ impl upload_agc_analytics_batch for super::RemoteProcedures { payload_json: String, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, __sdk::InternalError>, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Result>( 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 d2f9939e4..e6da80f97 100644 --- a/server-rs/crates/spacetime-module/src/runtime/active/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/active/profile.rs @@ -1153,6 +1153,45 @@ pub fn admin_upsert_profile_recharge_product( } } +// 会员档位目录的后台读写:后台按档位直接改价与改权益,充值中心与下单重算都以这张表为准。 +#[spacetimedb::procedure] +pub fn admin_list_profile_membership_plans( + ctx: &mut ProcedureContext, + input: RuntimeProfileMembershipPlanAdminListInput, +) -> RuntimeProfileMembershipPlanAdminListProcedureResult { + match ctx.try_with_tx(|tx| list_profile_membership_plan_records(tx, input.clone())) { + Ok(entries) => RuntimeProfileMembershipPlanAdminListProcedureResult { + ok: true, + entries, + error_message: None, + }, + Err(message) => RuntimeProfileMembershipPlanAdminListProcedureResult { + ok: false, + entries: Vec::new(), + error_message: Some(message), + }, + } +} + +#[spacetimedb::procedure] +pub fn admin_upsert_profile_membership_plan( + ctx: &mut ProcedureContext, + input: RuntimeProfileMembershipPlanAdminUpsertInput, +) -> RuntimeProfileMembershipPlanAdminProcedureResult { + match ctx.try_with_tx(|tx| upsert_profile_membership_plan_record(tx, input.clone())) { + Ok(record) => RuntimeProfileMembershipPlanAdminProcedureResult { + ok: true, + record: Some(record), + error_message: None, + }, + Err(message) => RuntimeProfileMembershipPlanAdminProcedureResult { + ok: false, + record: None, + error_message: Some(message), + }, + } +} + // 新用户注册赠送由后端注册链路调用;流水 ID 固定,保证重试不重复发放。 #[spacetimedb::procedure] pub fn grant_new_user_registration_wallet_reward( @@ -3281,6 +3320,20 @@ mod tests { expiration_checked_at: None, expiration_provider_state: None, expiration_last_error: None, + membership_change_kind: None, + membership_plan_before: None, + membership_plan_after: None, + membership_cycle_kind_before: None, + membership_cycle_kind_after: None, + membership_period_points_before: None, + membership_period_points_after: None, + membership_granted_points_delta: 0, + membership_cycle_index: None, + membership_expires_at_before: None, + membership_expires_at_after: None, + membership_cycle_resets_at_before: None, + membership_cycle_resets_at_after: None, + membership_price_breakdown_json: None, } } @@ -3611,24 +3664,26 @@ mod tests { } #[test] - fn point_recharge_display_is_resolved_per_product() { + fn point_recharge_display_has_no_first_charge_bonus_anywhere() { let products = runtime_profile_recharge_point_products(); - let repeated = resolve_profile_recharge_product_display(products[0].clone(), true); - let untouched = resolve_profile_recharge_product_display(products[1].clone(), false); - - assert_eq!(repeated.product_id, "points_60"); - assert_eq!(repeated.bonus_points, 0); - assert_eq!(repeated.badge_label, ""); - assert_eq!(repeated.description, "60泥点"); - assert_eq!(untouched.product_id, "points_180"); - assert_eq!(untouched.bonus_points, 90); - assert_eq!(untouched.badge_label, "首充加赠"); - assert_eq!(untouched.description, "首充加赠90泥点"); + assert_eq!(products.len(), 6); + for product in &products { + assert_eq!(product.bonus_points, 0, "六档充点商品都没有首充赠送"); + assert_eq!(product.badge_label, ""); + assert_eq!(product.price_cents, product.points_amount * 10); + let displayed = resolve_profile_recharge_product_display(product.clone(), true); + assert_eq!(displayed.bonus_points, 0); + assert_eq!(displayed.badge_label, ""); + assert_eq!(displayed.description, product.title); + } + assert_eq!(products[0].product_id, "points_60"); + assert_eq!(products[5].product_id, "points_3280"); } #[test] - fn legacy_default_recharge_products_migrate_without_overwriting_admin_config() { + fn legacy_default_recharge_products_migrate_without_first_charge_bonus() { let timestamp = Timestamp::from_micros_since_unix_epoch(1_000_000); + // 旧播种行:面额赠送 +「首充双倍」标签 +「首充送N泥点」说明。 let build_row = |points_amount: u64| ProfileRechargeProductConfig { product_id: format!("points_{points_amount}"), title: format!("{points_amount}泥点"), @@ -3639,41 +3694,44 @@ mod tests { duration_days: 0, badge_label: "首充双倍".to_string(), description: format!("首充送{points_amount}泥点"), - tier: RuntimeProfileMembershipTier::Normal, enabled: true, sort_order: 0, created_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(), created_at: timestamp, updated_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(), updated_at: timestamp, - membership_period_points: 0, - membership_period_days: 0, - membership_queue_limit: 0, - membership_discount_bps: 0, }; - assert_eq!( - resolve_default_point_product_migration(&build_row(180)), - Some(DefaultPointProductMigration { - bonus_points: 90, - badge_label: "首充加赠", - description: "首充加赠90泥点", - enabled: true, - }), - ); - assert_eq!( - resolve_default_point_product_migration(&build_row(1_280)), - Some(DefaultPointProductMigration { - bonus_points: 1_280, - badge_label: "首充双倍", - description: "", - enabled: false, - }), - ); + let migration = + resolve_default_point_product_migration(&build_row(180)).expect("旧播种行应被迁移"); + assert_eq!(migration.bonus_points, 0); + assert_eq!(migration.badge_label, ""); + assert_eq!(migration.description, "180泥点"); + assert!(migration.enabled); + // 中文注释:¥128 / ¥328 两档旧版本是停用的首充行,现在改为在售且无赠送。 + let disabled_seed = ProfileRechargeProductConfig { + bonus_points: 0, + badge_label: "首充双倍".to_string(), + enabled: false, + ..build_row(1_280) + }; + let migration = resolve_default_point_product_migration(&disabled_seed) + .expect("停用的 ¥128 档应被启用"); + assert!(migration.enabled); + assert_eq!(migration.bonus_points, 0); + + // 中文注释:后台改过的行(updated_by 已变)一律不动。 let mut admin_row = build_row(300); admin_row.updated_by = "admin-1".to_string(); - assert_eq!(resolve_default_point_product_migration(&admin_row), None); + assert!(resolve_default_point_product_migration(&admin_row).is_none()); + + // 中文注释:已经迁移过的行不重复迁移。 + let mut migrated_row = build_row(680); + migrated_row.bonus_points = 0; + migrated_row.badge_label = String::new(); + migrated_row.description = "680泥点".to_string(); + assert!(resolve_default_point_product_migration(&migrated_row).is_none()); } #[test] @@ -3934,173 +3992,64 @@ mod tests { } #[test] - fn membership_upgrade_amount_only_charges_price_difference() { - assert_eq!(membership_upgrade_amount_cents(6_990, 1_990), 5_000); - assert_eq!(membership_upgrade_amount_cents(19_990, 6_990), 13_000); - } - - #[test] - fn legacy_membership_tiers_share_new_pricing_ranks() { + fn membership_plan_window_recomputes_each_period_from_the_original_anchor() { + // 中文注释:1/31 开通 → 第 1 期到 2/28,第 2 期到 3/31;每期都从原始锚点重算。 + let anchor = beijing_local_micros(2026, 1, 31, 0); assert_eq!( - canonical_membership_pricing_tier(RuntimeProfileMembershipTier::Month), - RuntimeProfileMembershipTier::Starter, - ); - assert_eq!( - canonical_membership_pricing_tier(RuntimeProfileMembershipTier::Season), - RuntimeProfileMembershipTier::Basic, - ); - assert_eq!( - canonical_membership_pricing_tier(RuntimeProfileMembershipTier::Year), - RuntimeProfileMembershipTier::Pro, - ); - } - - #[test] - fn legacy_membership_tiers_can_migrate_to_new_equivalent_tiers_as_renewals() { - assert_eq!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Month, - RuntimeProfileMembershipTier::Starter, - ), - Ok(ActiveMembershipPurchaseMode::Renew), - ); - assert_eq!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Season, - RuntimeProfileMembershipTier::Basic, - ), - Ok(ActiveMembershipPurchaseMode::Renew), - ); - assert_eq!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Year, - RuntimeProfileMembershipTier::Pro, - ), - Ok(ActiveMembershipPurchaseMode::Renew), - ); - assert_eq!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Starter, - RuntimeProfileMembershipTier::Starter, - ), - Ok(ActiveMembershipPurchaseMode::Renew), - ); - assert_eq!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Month, - RuntimeProfileMembershipTier::Basic, - ), - Ok(ActiveMembershipPurchaseMode::Upgrade), - ); - assert_eq!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Season, - RuntimeProfileMembershipTier::Pro, - ), - Ok(ActiveMembershipPurchaseMode::Upgrade), - ); - assert_eq!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Year, - RuntimeProfileMembershipTier::Ultimate, - ), - Ok(ActiveMembershipPurchaseMode::Upgrade), - ); - assert!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Basic, - RuntimeProfileMembershipTier::Starter, + membership_cycle_window(anchor, 1), + ( + beijing_local_micros(2026, 1, 31, 0), + beijing_local_micros(2026, 2, 28, 0) ) - .is_err() ); - assert!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Year, - RuntimeProfileMembershipTier::Basic, - ) - .is_err() - ); - assert!( - resolve_active_membership_purchase_mode( - RuntimeProfileMembershipTier::Basic, - RuntimeProfileMembershipTier::Season, - ) - .is_err() - ); - } - - #[test] - fn legacy_membership_migration_renews_and_preserves_cycle_timing() { - let started_at = Timestamp::from_micros_since_unix_epoch(PROFILE_RUNTIME_DAY_MICROS); - let expires_at = Timestamp::from_micros_since_unix_epoch(10 * PROFILE_RUNTIME_DAY_MICROS); - let current_reset_at = - Timestamp::from_micros_since_unix_epoch(5 * PROFILE_RUNTIME_DAY_MICROS); - let purchased_at = Timestamp::from_micros_since_unix_epoch(2 * PROFILE_RUNTIME_DAY_MICROS); - let row = apply_active_membership_renew_row( - ProfileMembership { - user_id: "user-1".to_string(), - status: RuntimeProfileMembershipStatus::Active, - tier: RuntimeProfileMembershipTier::Month, - started_at, - expires_at, - updated_at: started_at, - cycle_started_at: Some(started_at), - cycle_resets_at: Some(current_reset_at), - cycle_granted_points: 0, - cycle_remaining_points: 0, - cycle_period_days: 90, - }, - RuntimeProfileMembershipTier::Starter, - 30, - purchased_at, - ); - - assert_eq!(row.tier, RuntimeProfileMembershipTier::Starter); assert_eq!( - row.expires_at, - Timestamp::from_micros_since_unix_epoch(40 * PROFILE_RUNTIME_DAY_MICROS), + membership_cycle_window(anchor, 2), + ( + beijing_local_micros(2026, 2, 28, 0), + beijing_local_micros(2026, 3, 31, 0) + ) ); - assert_eq!(row.cycle_resets_at, Some(current_reset_at)); - assert_eq!(row.cycle_period_days, 90); - assert_eq!(row.cycle_granted_points, 0); - assert_eq!(row.cycle_remaining_points, 0); - assert_eq!(row.updated_at, purchased_at); } #[test] - fn active_membership_upgrade_switches_next_cycle_period_without_moving_reset() { - let started_at = Timestamp::from_micros_since_unix_epoch(1_000); - let expires_at = Timestamp::from_micros_since_unix_epoch(90_000); - let current_reset_at = Timestamp::from_micros_since_unix_epoch(30_000); - let updated_at = Timestamp::from_micros_since_unix_epoch(2_000); - let (row, period_points_delta) = apply_active_membership_upgrade_row( - ProfileMembership { - user_id: "user-1".to_string(), - status: RuntimeProfileMembershipStatus::Active, - tier: RuntimeProfileMembershipTier::Basic, - started_at, - expires_at, - updated_at: started_at, - cycle_started_at: Some(started_at), - cycle_resets_at: Some(current_reset_at), - cycle_granted_points: 800, - cycle_remaining_points: 120, - cycle_period_days: 90, - }, - RuntimeProfileMembershipTier::Pro, - 7, - 2_500, - updated_at, - ); + fn membership_cycle_advance_only_writes_when_a_new_period_starts() { + let anchor = beijing_local_micros(2026, 1, 31, 0); + let day = PROFILE_RUNTIME_DAY_MICROS; + let row = || ProfileMembership { + user_id: "user-1".to_string(), + status: RuntimeProfileMembershipStatus::Active, + started_at: Timestamp::from_micros_since_unix_epoch(anchor), + expires_at: Timestamp::from_micros_since_unix_epoch(anchor + 400 * day), + updated_at: Timestamp::from_micros_since_unix_epoch(anchor), + cycle_started_at: Some(Timestamp::from_micros_since_unix_epoch(anchor)), + cycle_resets_at: Some(Timestamp::from_micros_since_unix_epoch(anchor + 28 * day)), + cycle_granted_points: 400, + cycle_remaining_points: 120, + plan: RuntimeProfileMembershipPlan::Starter, + cycle_index: 1, + cycle_count: 12, + cycle_kind: RuntimeProfileMembershipCycleKind::Yearly, + }; - assert_eq!(row.tier, RuntimeProfileMembershipTier::Pro); - assert_eq!(row.expires_at, expires_at); - assert_eq!(row.cycle_resets_at, Some(current_reset_at)); - assert_eq!(row.cycle_period_days, 7); - assert_eq!(row.cycle_granted_points, 2_500); - assert_eq!(row.cycle_remaining_points, 1_820); - assert_eq!(row.updated_at, updated_at); - assert_eq!(period_points_delta, 1_700); + let same_period = advance_profile_membership_cycle_to( + row(), + Timestamp::from_micros_since_unix_epoch(anchor + 10 * day), + 400, + ); + assert!(!same_period.advanced, "仍在本期时不应写账本"); + assert_eq!(same_period.row.cycle_index, 1); + assert_eq!(same_period.row.cycle_remaining_points, 120); + + let next_period = advance_profile_membership_cycle_to( + row(), + Timestamp::from_micros_since_unix_epoch(anchor + 28 * day), + 400, + ); + assert!(next_period.advanced); + assert_eq!(next_period.previous_remaining_points, 120); + assert_eq!(next_period.row.cycle_index, 2); + assert_eq!(next_period.row.cycle_granted_points, 400); + assert_eq!(next_period.row.cycle_remaining_points, 400); } #[test] @@ -4983,9 +4932,49 @@ fn build_profile_recharge_order_admin_entry_snapshot( active_hold: active_profile_recharge_refund_hold_for_order(ctx, &row.order_id) .map(|value| build_profile_recharge_refund_hold_snapshot_from_row(&value)), wallet: build_profile_admin_wallet_snapshot(ctx, &row.user_id), + membership_change: build_profile_membership_change_snapshot_from_order_row(row), } } +/// 订单行的会员变更快照 → 只读投影;非会员订单(`membership_change_kind` 为空)返回 `None`。 +fn build_profile_membership_change_snapshot_from_order_row( + row: &ProfileRechargeOrder, +) -> Option { + let change_kind = row.membership_change_kind?; + Some(RuntimeProfileMembershipOrderChangeSnapshot { + change_kind: change_kind.as_str().to_string(), + plan_before: row + .membership_plan_before + .map(|plan| plan.as_str().to_string()), + plan_after: row + .membership_plan_after + .map(|plan| plan.as_str().to_string()), + cycle_kind_before: row + .membership_cycle_kind_before + .map(|kind| kind.as_str().to_string()), + cycle_kind_after: row + .membership_cycle_kind_after + .map(|kind| kind.as_str().to_string()), + period_points_before: row.membership_period_points_before, + period_points_after: row.membership_period_points_after, + granted_points_delta: row.membership_granted_points_delta, + cycle_index: row.membership_cycle_index, + expires_at_before_micros: row + .membership_expires_at_before + .map(|value| value.to_micros_since_unix_epoch()), + expires_at_after_micros: row + .membership_expires_at_after + .map(|value| value.to_micros_since_unix_epoch()), + cycle_resets_at_before_micros: row + .membership_cycle_resets_at_before + .map(|value| value.to_micros_since_unix_epoch()), + cycle_resets_at_after_micros: row + .membership_cycle_resets_at_after + .map(|value| value.to_micros_since_unix_epoch()), + price_breakdown_json: row.membership_price_breakdown_json.clone(), + }) +} + fn preview_profile_recharge_refund_hold( ctx: &ReducerContext, input: RuntimeProfileRechargeRefundHoldPreviewInput, @@ -7995,11 +7984,6 @@ fn upsert_profile_recharge_product_config_record( input.duration_days, input.badge_label, input.description, - input.tier, - input.membership_period_points, - input.membership_period_days, - input.membership_queue_limit, - input.membership_discount_bps, input.enabled, input.sort_order, input.updated_at_micros, @@ -8033,7 +8017,6 @@ fn upsert_profile_recharge_product_config_record( duration_days: validated_input.duration_days, badge_label: validated_input.badge_label, description: validated_input.description, - tier: validated_input.tier, enabled: validated_input.enabled, sort_order: validated_input.sort_order, created_by: existing @@ -8046,16 +8029,82 @@ fn upsert_profile_recharge_product_config_record( .unwrap_or(updated_at), updated_by: validated_input.admin_user_id, updated_at, - membership_period_points: validated_input.membership_period_points, - membership_period_days: validated_input.membership_period_days, - membership_queue_limit: validated_input.membership_queue_limit, - membership_discount_bps: validated_input.membership_discount_bps, }); Ok(build_profile_recharge_product_config_snapshot_from_row( &inserted, )) } +fn list_profile_membership_plan_records( + ctx: &ReducerContext, + input: RuntimeProfileMembershipPlanAdminListInput, +) -> Result, String> { + build_runtime_profile_membership_plan_admin_list_input(input.admin_user_id) + .map_err(|error| error.to_string())?; + Ok(membership_plan_records(ctx)) +} + +/// 后台改价 / 改权益。`rank` 与 `plan` 不可改:`plan` 是主键(即身份),`rank` 是升级比较基准。 +fn upsert_profile_membership_plan_record( + ctx: &ReducerContext, + input: RuntimeProfileMembershipPlanAdminUpsertInput, +) -> Result { + let validated = build_runtime_profile_membership_plan_admin_upsert_input( + input.admin_user_id, + input.plan, + input.title, + input.month_price_cents, + input.year_price_cents, + input.period_points, + input.model_access, + input.concurrent_job_limit, + input.enabled, + input.sort_order, + input.updated_at_micros, + ) + .map_err(|error| error.to_string())?; + ensure_default_profile_membership_plan(ctx); + + let updated_at = Timestamp::from_micros_since_unix_epoch(validated.updated_at_micros); + let existing = ctx + .db + .profile_membership_plan() + .plan() + .find(&validated.plan); + // 代码内目录是 `rank` 的唯一来源:后台改价、改权益、改上下架都不会动升级比较基准。 + let rank = resolve_runtime_profile_membership_plan_row(validated.plan) + .map(|row| row.rank) + .unwrap_or(0); + + let inserted = ProfileMembershipPlan { + plan: validated.plan, + title: validated.title, + rank, + month_price_cents: validated.month_price_cents, + year_price_cents: validated.year_price_cents, + period_points: validated.period_points, + model_access: validated.model_access, + concurrent_job_limit: validated.concurrent_job_limit, + sort_order: validated.sort_order, + enabled: validated.enabled, + created_by: existing + .as_ref() + .map(|row| row.created_by.clone()) + .unwrap_or_else(|| validated.admin_user_id.clone()), + created_at: existing + .as_ref() + .map(|row| row.created_at) + .unwrap_or(updated_at), + updated_by: validated.admin_user_id, + updated_at, + }; + if let Some(row) = existing { + ctx.db.profile_membership_plan().plan().delete(&row.plan); + } + let inserted = ctx.db.profile_membership_plan().insert(inserted); + Ok(membership_plan_record_from_row(&inserted)) +} + fn upsert_profile_task_config_record( ctx: &ReducerContext, input: RuntimeProfileTaskConfigAdminUpsertInput, @@ -8816,7 +8865,6 @@ struct MembershipCycleAdvance { advanced: bool, previous_remaining_points: u64, cycle_started_at_micros: i64, - cycle_resets_at_micros: i64, } fn active_membership_row_at(row: &ProfileMembership, at: Timestamp) -> bool { @@ -8839,13 +8887,35 @@ fn membership_plan_period_points(ctx: &ReducerContext, plan: RuntimeProfileMembe .unwrap_or(0) } +/// 目录表行 → 领域目录记录(字段一一对应,仅做类型搬运)。 +fn membership_plan_record_from_row( + row: &ProfileMembershipPlan, +) -> RuntimeProfileMembershipPlanRecord { + RuntimeProfileMembershipPlanRecord { + plan: row.plan, + title: row.title.clone(), + rank: row.rank, + month_price_cents: row.month_price_cents, + year_price_cents: row.year_price_cents, + period_points: row.period_points, + model_access: row.model_access, + concurrent_job_limit: row.concurrent_job_limit, + sort_order: row.sort_order, + enabled: row.enabled, + } +} + fn membership_plan_snapshot( ctx: &ReducerContext, plan: RuntimeProfileMembershipPlan, cycle_kind: RuntimeProfileMembershipCycleKind, ) -> Option { - membership_plan_row(ctx, plan) - .map(|row| build_runtime_profile_membership_plan_snapshot(&row, cycle_kind)) + membership_plan_row(ctx, plan).map(|row| { + build_runtime_profile_membership_plan_snapshot( + &membership_plan_record_from_row(&row), + cycle_kind, + ) + }) } /// 充值中心与后台共用的目录行读取,按 `sort_order` 升序。 @@ -8908,7 +8978,6 @@ fn advance_profile_membership_cycle_to( advanced, previous_remaining_points, cycle_started_at_micros: window.0, - cycle_resets_at_micros: window.1, } } @@ -8950,7 +9019,10 @@ fn build_membership_order_product( plan: RuntimeProfileMembershipPlan, cycle_kind: RuntimeProfileMembershipCycleKind, ) -> RuntimeProfileRechargeProductSnapshot { - let snapshot = build_runtime_profile_membership_plan_snapshot(row, cycle_kind); + let snapshot = build_runtime_profile_membership_plan_snapshot( + &membership_plan_record_from_row(row), + cycle_kind, + ); let cycle_label = match cycle_kind { RuntimeProfileMembershipCycleKind::Monthly => "月付", RuntimeProfileMembershipCycleKind::Yearly => "年付",