diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index fb31bb682..702e285f3 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -697,144 +697,153 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 ### `profile_dashboard_state` - Rust 结构体:`ProfileDashboardState` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_daily_free_points` - Rust 结构体:`ProfileDailyFreePoints` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:每日免费泥点事实源。`day_key` 使用北京时间业务日,基础发放量读取 `profile_wallet_config.daily_free_points_per_day`(未配置时默认 `20`),`remaining_points` 保存当日剩余额度;跨业务日退款的每日免费消费部分会叠加到退款当日,使 `granted_points` 和 `remaining_points` 可暂时超过当前基础发放量,下一业务日首次触达时旧余额与叠加量一并失效并按当时最新配置重置。 ### `profile_feedback_submission` - Rust 结构体:`ProfileFeedbackSubmission` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_invite_code` - Rust 结构体:`ProfileInviteCode` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 生效时间:`starts_at` / `expires_at` 均可为空;两者同时存在时必须满足 `starts_at < expires_at`。开始时刻计入有效区间,截止时刻不计入有效区间。 ### `profile_code_operation` - Rust 结构体:`ProfileCodeOperation` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:后台兑换码 / 邀请码的持久操作记录,记录新增、更新、停用的码值、操作人和操作时间。 ### `profile_membership` - Rust 结构体:`ProfileMembership` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:会员有效期和当前周期限时泥点事实源。`started_at/expires_at` 表示会员有效期,`cycle_started_at/cycle_resets_at/cycle_period_days` 表示当前周期,`cycle_granted_points/cycle_remaining_points` 表示当前周期已发放和剩余限时泥点。 +### `profile_membership_plan` + +- Rust 结构体:`ProfileMembershipPlan` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` +- 作用:会员计划目录真相源,**主键是档位枚举** `RuntimeProfileMembershipPlan`(`Normal` / `Starter` / `Plus` / `Pro` / `Max`),一行一档,供后台按档位直接改价和改权益。 +- 字段:`month_price_cents` 与 `year_price_cents` 是两个独立可配置价格(年价不做月价 ×10 推导);`period_points` 是每期(一个自然月)额度,年付 = 12 次同额度发放;`model_access` 与 `concurrent_job_limit` 是档位权益,并发上限 `128` 表示“不设上限”。 +- 索引:主键 `plan`。 +- 说明:`Normal` 是不可购买的非会员占位档位(`enabled = false`);模型权限与并发上限当前只落字段、只用于展示,服务端不做拦截。 + ### `profile_recharge_product_config` - Rust 结构体:`ProfileRechargeProductConfig` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:泥点和会员充值商品配置真相源,供充值中心展示、下单校验、支付确认和后台“充值商品”页维护;当前公开充值中心只展示四档泥点商品,会员商品配置留存但不公开购买或升级入口。 - 字段补充:会员商品追加 `membership_period_points`、`membership_period_days`、`membership_queue_limit`、`membership_discount_bps`;泥点商品这些字段必须为 `0`。 ### `profile_played_world` - Rust 结构体:`ProfilePlayedWorld` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_recharge_order` - Rust 结构体:`ProfileRechargeOrder` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:账户充值订单事实源。`status` 包含 `pending`、`paid`、`failed`、`closed`、`refunded`、`expired`;过期补偿字段 `expired_at`、`expiration_checked_at`、`expiration_provider_state`、`expiration_last_error` 用于记录本地过期和微信查单结果。 ### `profile_recharge_refund` - Rust 结构体:`ProfileRechargeRefund` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:普通微信支付 V3 退款单聚合。以 `out_refund_no` 为主键、`provider_refund_id` 唯一,保存原订单/微信支付单、四个分金额、微信状态、最近观察、权益目标/已回收/未回收量和人工处理错误码。管理员读取契约同步暴露微信交易号、订单总额和人工复核获批错误码。交易号或订单总额冲突经管理员确认归属后,追加保存处理管理员、非空原因、处理时间和本次获批的错误码;四字段只写一次,作为重新执行正式结算的审计事实。 - 索引:`by_profile_recharge_refund_order_id`、`by_profile_recharge_refund_status_updated_at`。 ### `profile_recharge_refund_observation` - Rust 结构体:`ProfileRechargeRefundObservation` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:退款事实的追加观察记录。保存 callback / api_request / query / trade_bill 来源、按“来源 + 事实指纹”稳定生成的 observation ID、金额、状态、脱敏通知引用、事实指纹和 resolution code;同一事实从不同来源进入时使用不同 ID,不保存回调密文、签名、密钥、原始 CSV 或短时下载 URL。 - 索引:`by_profile_recharge_refund_observation_out_refund_no`、`by_profile_recharge_refund_observation_order_id`。 ### `profile_recharge_order_refund_settlement` - Rust 结构体:`ProfileRechargeOrderRefundSettlement` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:原充值订单维度的退款与权益结算摘要,保存累计成功退款金额、目标/已回收/未回收泥点、权益状态和钱包冻结事实。部分退款不改订单终态;累计全额才把订单改为 `refunded`。 - 索引:主键 `order_id`,`by_profile_recharge_order_refund_settlement_user_id` 用于钱包消费前检查退款欠款。 ### `profile_recharge_refund_hold` - Rust 结构体:`ProfileRechargeRefundHold` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:后台主动退款调用微信前的永久泥点占用。活动占用保护退款所需泥点但不改钱包总额;匹配退款成功后结算,关闭或确认未创建的退款释放。相同 `out_refund_no` 只允许内容完全一致的幂等重放。 - 索引:主键 `out_refund_no`,`by_profile_recharge_refund_hold_order_id`、`by_profile_recharge_refund_hold_user_id`、`by_profile_recharge_refund_hold_status`。 ### `profile_wallet_manual_restriction` - Rust 结构体:`ProfileWalletManualRestriction` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:后台人工钱包冻结当前态,保存冻结原因、创建/更新管理员和时间。它不承载退款欠账;退款欠账仍来自订单退款 settlement,两个来源任一有效都阻断普通消费。 - 索引:主键 `user_id`。 ### `profile_recharge_refund_bill_checkpoint` - Rust 结构体:`ProfileRechargeRefundBillCheckpoint` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:按交易账单日期记录已完成的退款 reconciliation,保存账单 SHA1(确认稳定无账单时保存 `NO_STATEMENT_EXIST`)、处理退款行数和完成时间。任一退款行失败时不写完成 checkpoint,成功前缀依赖 observation 幂等重放;重复下载、多实例执行或进程重启不会重复回收权益。 ### `profile_recharge_order_expiration_schedule` - Rust 结构体:`ProfileRechargeOrderExpirationSchedule` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:旧普通微信充值订单到期查单调度表,保留 schema 兼容但当前不再写入;当前过期路径改由原生 scheduled 表 `profile_recharge_order_expiration_timer` 触发。 ### `profile_recharge_order_expiration_timer` - Rust 结构体:`ProfileRechargeOrderExpirationTimer` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:充值订单原生 scheduled 表,`scheduled_at` 到点触发 `expire_profile_recharge_order_timer`;`order_id` 唯一,支付成功、本地关闭或 scheduled reducer 执行后删除对应行。 ### `profile_redeem_code` - Rust 结构体:`ProfileRedeemCode` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 生效时间:复用邀请码时间窗口语义,`starts_at` / `expires_at` 均可为空;两者同时存在时必须满足 `starts_at < expires_at`。开始时刻计入有效区间,截止时刻不计入有效区间。用户兑换时以后端接收的 `redeemed_at_micros` 判定:未到开始时间拒绝为“兑换码未生效”,到达或超过截止时间拒绝为“兑换码已过期”。 - 后台契约:`AdminUpsertProfileRedeemCodeRequest` 通过可空 `startsAt` / `expiresAt` 接收 RFC3339 时间,列表与保存响应同步返回这两个字段;后台页只负责输入、回填和显示,真正兑换判定留在后端事务路径。`reward_points` 是奖励泥点(兑换成功后按 `redeem_code_reward` 流水进入泥点钱包),不是金额;后台奖励输入与列表必须带泥点单位。 ### `profile_redeem_code_usage` - Rust 结构体:`ProfileRedeemCodeUsage` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_referral_relation` - Rust 结构体:`ProfileReferralRelation` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_save_archive` - Rust 结构体:`ProfileSaveArchive` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_task_config` - Rust 结构体:`ProfileTaskConfig` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_task_progress` - Rust 结构体:`ProfileTaskProgress` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_task_reward_claim` - Rust 结构体:`ProfileTaskRewardClaim` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `profile_wallet_ledger` @@ -852,7 +861,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 ### `asset_operation_wallet_settlement` - Rust 结构体:`AssetOperationWalletSettlement` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 说明:资产操作 consume/refund 配对结算事实表,主键为 consume ledger ID,并保存配对 refund ledger、用户、金额和结算时间。退款先到且 consume 尚不可见时,该表作为持久化取消 intent;迟到 consume 必须检测该行并拒绝扣费,避免 worker 崩溃重领期间双扣。 - 索引:主键 `consume_ledger_id`。 @@ -866,18 +875,18 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 ### `profile_wallet_config` - Rust 结构体:`ProfileWalletConfig` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 作用:账号钱包全局配置真相源,当前维护新账号注册初始泥点数;表为空时业务回退 `100` 泥点。 ### `public_work_like` - Rust 结构体:`PublicWorkLike` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### `public_work_play_daily_stat` - Rust 结构体:`PublicWorkPlayDailyStat` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` ### api-server 长期订阅读模型 @@ -913,7 +922,7 @@ private `asset_object` 不进入长期订阅;安全判断通过受限 procedur ### `tracking_daily_stat` - Rust 结构体:`TrackingDailyStat` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 写入:由单条或批量 tracking procedure 在同一事务中随 `tracking_event` 更新,作为运营查询和个人任务进度的聚合投影。 ### `agc_tracking_event` @@ -927,7 +936,7 @@ private `asset_object` 不进入长期订阅;安全判断通过受限 procedur ### `tracking_event` - Rust 结构体:`TrackingEvent` -- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 源码:`server-rs/crates/spacetime-module/src/runtime/active/profile.rs` - 写入:关键业务埋点同步调用单条 procedure;普通 HTTP route tracking 由 `api-server` 本机 outbox 批量调用 `record_tracking_events_and_return`。outbox 到达批量阈值时先封存 active 文件并切新 active,后台 worker 异步 flush sealed 文件,HTTP 请求线程不等待 SpacetimeDB。`FLUSH_INTERVAL_MS` 只负责兜底封存长时间未满批的 active 文件,`MAX_BYTES` 只做磁盘保护阈值。`event_id` 必须稳定且全局唯一,批量重试时用唯一索引做幂等跳过。 - 外部 API 失败:`event_key = external_api_call_failure` 使用同一张表落库;它是供应商失败审计事实,不新增 SpacetimeDB 表,查询时按 `module_key = 'external-api'` 或 `scope_kind = module AND scope_id = ''` 过滤。 diff --git a/server-rs/crates/spacetime-client/src/module_bindings.rs b/server-rs/crates/spacetime-client/src/module_bindings.rs index 0e2874cae..dfb4367c4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings.rs @@ -548,6 +548,8 @@ pub mod profile_feedback_submission_table; pub mod profile_feedback_submission_type; pub mod profile_invite_code_table; pub mod profile_invite_code_type; +pub mod profile_membership_plan_table; +pub mod profile_membership_plan_type; pub mod profile_membership_table; pub mod profile_membership_type; pub mod profile_played_world_table; @@ -647,6 +649,8 @@ 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_model_access_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; @@ -1349,6 +1353,8 @@ pub use profile_feedback_submission_table::*; pub use profile_feedback_submission_type::ProfileFeedbackSubmission; pub use profile_invite_code_table::*; pub use profile_invite_code_type::ProfileInviteCode; +pub use profile_membership_plan_table::*; +pub use profile_membership_plan_type::ProfileMembershipPlan; pub use profile_membership_table::*; pub use profile_membership_type::ProfileMembership; pub use profile_played_world_table::*; @@ -1448,6 +1454,8 @@ pub use runtime_profile_invite_code_admin_procedure_result_type::RuntimeProfileI 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_model_access_type::RuntimeProfileMembershipModelAccess; +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; @@ -1774,6 +1782,7 @@ pub struct DbUpdate { profile_feedback_submission: __sdk::TableUpdate, profile_invite_code: __sdk::TableUpdate, profile_membership: __sdk::TableUpdate, + profile_membership_plan: __sdk::TableUpdate, profile_played_world: __sdk::TableUpdate, profile_recharge_order: __sdk::TableUpdate, profile_recharge_order_expiration_schedule: @@ -2017,6 +2026,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate { "profile_membership" => db_update .profile_membership .append(profile_membership_table::parse_table_update(table_update)?), + "profile_membership_plan" => db_update.profile_membership_plan.append( + profile_membership_plan_table::parse_table_update(table_update)?, + ), "profile_played_world" => db_update.profile_played_world.append( profile_played_world_table::parse_table_update(table_update)?, ), @@ -2446,6 +2458,12 @@ impl __sdk::DbUpdate for DbUpdate { &self.profile_membership, ) .with_updates_by_pk(|row| &row.user_id); + diff.profile_membership_plan = cache + .apply_diff_to_table::( + "profile_membership_plan", + &self.profile_membership_plan, + ) + .with_updates_by_pk(|row| &row.plan); diff.profile_played_world = cache .apply_diff_to_table::( "profile_played_world", @@ -2789,6 +2807,9 @@ impl __sdk::DbUpdate for DbUpdate { "profile_membership" => db_update .profile_membership .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), + "profile_membership_plan" => db_update + .profile_membership_plan + .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), "profile_played_world" => db_update .profile_played_world .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), @@ -3063,6 +3084,9 @@ impl __sdk::DbUpdate for DbUpdate { "profile_membership" => db_update .profile_membership .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), + "profile_membership_plan" => db_update + .profile_membership_plan + .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), "profile_played_world" => db_update .profile_played_world .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), @@ -3231,6 +3255,7 @@ pub struct AppliedDiff<'r> { profile_feedback_submission: __sdk::TableAppliedDiff<'r, ProfileFeedbackSubmission>, profile_invite_code: __sdk::TableAppliedDiff<'r, ProfileInviteCode>, profile_membership: __sdk::TableAppliedDiff<'r, ProfileMembership>, + profile_membership_plan: __sdk::TableAppliedDiff<'r, ProfileMembershipPlan>, profile_played_world: __sdk::TableAppliedDiff<'r, ProfilePlayedWorld>, profile_recharge_order: __sdk::TableAppliedDiff<'r, ProfileRechargeOrder>, profile_recharge_order_expiration_schedule: @@ -3552,6 +3577,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { &self.profile_membership, event, ); + callbacks.invoke_table_row_callbacks::( + "profile_membership_plan", + &self.profile_membership_plan, + event, + ); callbacks.invoke_table_row_callbacks::( "profile_played_world", &self.profile_played_world, @@ -4423,6 +4453,7 @@ impl __sdk::SpacetimeModule for RemoteModule { profile_feedback_submission_table::register_table(client_cache); profile_invite_code_table::register_table(client_cache); profile_membership_table::register_table(client_cache); + profile_membership_plan_table::register_table(client_cache); profile_played_world_table::register_table(client_cache); profile_recharge_order_table::register_table(client_cache); profile_recharge_order_expiration_schedule_table::register_table(client_cache); @@ -4512,6 +4543,7 @@ impl __sdk::SpacetimeModule for RemoteModule { "profile_feedback_submission", "profile_invite_code", "profile_membership", + "profile_membership_plan", "profile_played_world", "profile_recharge_order", "profile_recharge_order_expiration_schedule", diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_plan_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_plan_table.rs new file mode 100644 index 000000000..b9560bde3 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_plan_table.rs @@ -0,0 +1,234 @@ +// 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 super::profile_membership_plan_type::ProfileMembershipPlan; +use super::runtime_profile_membership_model_access_type::RuntimeProfileMembershipModelAccess; +use super::runtime_profile_membership_plan_type::RuntimeProfileMembershipPlan; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `profile_membership_plan`. +/// +/// Obtain a handle from the [`ProfileMembershipPlanTableAccess::profile_membership_plan`] method on [`super::RemoteTables`], +/// like `ctx.db.profile_membership_plan()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.profile_membership_plan().on_insert(...)`. +pub struct ProfileMembershipPlanTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +/// Lifetime-aware accessor marker for the table `profile_membership_plan`. +pub struct ProfileMembershipPlanTableAccessor; + +impl __sdk::TableAccessor for ProfileMembershipPlanTableAccessor { + type Row = ProfileMembershipPlan; + type Handle<'db> = ProfileMembershipPlanTableHandle<'db>; + + fn get<'db>(db: &'db super::RemoteTables) -> Self::Handle<'db> { + db.profile_membership_plan() + } +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `profile_membership_plan`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ProfileMembershipPlanTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ProfileMembershipPlanTableHandle`], which mediates access to the table `profile_membership_plan`. + fn profile_membership_plan(&self) -> ProfileMembershipPlanTableHandle<'_>; +} + +impl ProfileMembershipPlanTableAccess for super::RemoteTables { + fn profile_membership_plan(&self) -> ProfileMembershipPlanTableHandle<'_> { + ProfileMembershipPlanTableHandle { + imp: self + .imp + .get_table::("profile_membership_plan"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ProfileMembershipPlanInsertCallbackId(__sdk::CallbackId); +pub struct ProfileMembershipPlanDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableLike for ProfileMembershipPlanTableHandle<'ctx> { + type Row = ProfileMembershipPlan; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } +} + +impl<'ctx> __sdk::Table for ProfileMembershipPlanTableHandle<'ctx> { + type Row = ProfileMembershipPlan; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ProfileMembershipPlanInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ProfileMembershipPlanInsertCallbackId { + ProfileMembershipPlanInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ProfileMembershipPlanInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ProfileMembershipPlanDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ProfileMembershipPlanDeleteCallbackId { + ProfileMembershipPlanDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ProfileMembershipPlanDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +impl<'ctx> __sdk::WithInsert for ProfileMembershipPlanTableHandle<'ctx> { + type InsertCallbackId = ProfileMembershipPlanInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ProfileMembershipPlanInsertCallbackId { + ProfileMembershipPlanInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ProfileMembershipPlanInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } +} + +impl<'ctx> __sdk::WithDelete for ProfileMembershipPlanTableHandle<'ctx> { + type DeleteCallbackId = ProfileMembershipPlanDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ProfileMembershipPlanDeleteCallbackId { + ProfileMembershipPlanDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ProfileMembershipPlanDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +pub struct ProfileMembershipPlanUpdateCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableWithPrimaryKey for ProfileMembershipPlanTableHandle<'ctx> { + type UpdateCallbackId = ProfileMembershipPlanUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> ProfileMembershipPlanUpdateCallbackId { + ProfileMembershipPlanUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: ProfileMembershipPlanUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +impl<'ctx> __sdk::WithUpdate for ProfileMembershipPlanTableHandle<'ctx> { + type UpdateCallbackId = ProfileMembershipPlanUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> ProfileMembershipPlanUpdateCallbackId { + ProfileMembershipPlanUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: ProfileMembershipPlanUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +/// Access to the `plan` unique index on the table `profile_membership_plan`, +/// which allows point queries on the field of the same name +/// via the [`ProfileMembershipPlanPlanUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.profile_membership_plan().plan().find(...)`. +pub struct ProfileMembershipPlanPlanUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> ProfileMembershipPlanTableHandle<'ctx> { + /// Get a handle on the `plan` unique index on the table `profile_membership_plan`. + pub fn plan(&self) -> ProfileMembershipPlanPlanUnique<'ctx> { + ProfileMembershipPlanPlanUnique { + imp: self + .imp + .get_unique_constraint::("plan"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> ProfileMembershipPlanPlanUnique<'ctx> { + /// Find the subscribed row whose `plan` column value is equal to `col_val`, + /// if such a row is present in the client cache. + pub fn find(&self, col_val: &RuntimeProfileMembershipPlan) -> Option { + self.imp.find(col_val) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("profile_membership_plan"); + _table.add_unique_constraint::("plan", |row| &row.plan); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} + +#[allow(non_camel_case_types)] +/// Extension trait for query builder access to the table `ProfileMembershipPlan`. +/// +/// Implemented for [`__sdk::QueryTableAccessor`]. +pub trait profile_membership_planQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `ProfileMembershipPlan`. + fn profile_membership_plan(&self) -> __sdk::__query_builder::Table; +} + +impl profile_membership_planQueryTableAccess for __sdk::QueryTableAccessor { + fn profile_membership_plan(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("profile_membership_plan") + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_plan_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_plan_type.rs new file mode 100644 index 000000000..848ab50fc --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_plan_type.rs @@ -0,0 +1,95 @@ +// 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 ProfileMembershipPlan { + 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, + pub created_by: String, + pub created_at: __sdk::Timestamp, + pub updated_by: String, + pub updated_at: __sdk::Timestamp, +} + +impl __sdk::InModule for ProfileMembershipPlan { + type Module = super::RemoteModule; +} + +/// Column accessor struct for the table `ProfileMembershipPlan`. +/// +/// Provides typed access to columns for query building. +pub struct ProfileMembershipPlanCols { + pub plan: __sdk::__query_builder::Col, + pub title: __sdk::__query_builder::Col, + pub rank: __sdk::__query_builder::Col, + pub month_price_cents: __sdk::__query_builder::Col, + pub year_price_cents: __sdk::__query_builder::Col, + pub period_points: __sdk::__query_builder::Col, + pub model_access: + __sdk::__query_builder::Col, + pub concurrent_job_limit: __sdk::__query_builder::Col, + pub sort_order: __sdk::__query_builder::Col, + pub enabled: __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, +} + +impl __sdk::__query_builder::HasCols for ProfileMembershipPlan { + type Cols = ProfileMembershipPlanCols; + fn cols(table_name: &'static str) -> Self::Cols { + ProfileMembershipPlanCols { + plan: __sdk::__query_builder::Col::new(table_name, "plan"), + title: __sdk::__query_builder::Col::new(table_name, "title"), + rank: __sdk::__query_builder::Col::new(table_name, "rank"), + month_price_cents: __sdk::__query_builder::Col::new(table_name, "month_price_cents"), + year_price_cents: __sdk::__query_builder::Col::new(table_name, "year_price_cents"), + period_points: __sdk::__query_builder::Col::new(table_name, "period_points"), + model_access: __sdk::__query_builder::Col::new(table_name, "model_access"), + concurrent_job_limit: __sdk::__query_builder::Col::new( + table_name, + "concurrent_job_limit", + ), + sort_order: __sdk::__query_builder::Col::new(table_name, "sort_order"), + enabled: __sdk::__query_builder::Col::new(table_name, "enabled"), + 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"), + } + } +} + +/// Indexed column accessor struct for the table `ProfileMembershipPlan`. +/// +/// Provides typed access to indexed columns for query building. +pub struct ProfileMembershipPlanIxCols { + pub plan: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for ProfileMembershipPlan { + type IxCols = ProfileMembershipPlanIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + ProfileMembershipPlanIxCols { + plan: __sdk::__query_builder::IxCol::new(table_name, "plan"), + } + } +} + +impl __sdk::__query_builder::CanBeLookupTable for ProfileMembershipPlan {} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_model_access_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_model_access_type.rs new file mode 100644 index 000000000..10eac3cf3 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_model_access_type.rs @@ -0,0 +1,18 @@ +// 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)] +#[derive(Copy, Eq, Hash)] +pub enum RuntimeProfileMembershipModelAccess { + Basic, + + Full, +} + +impl __sdk::InModule for RuntimeProfileMembershipModelAccess { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_type.rs new file mode 100644 index 000000000..9fc0c3c78 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_plan_type.rs @@ -0,0 +1,24 @@ +// 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)] +#[derive(Copy, Eq, Hash)] +pub enum RuntimeProfileMembershipPlan { + Normal, + + Starter, + + Plus, + + Pro, + + Max, +} + +impl __sdk::InModule for RuntimeProfileMembershipPlan { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-module/src/migration.rs b/server-rs/crates/spacetime-module/src/migration.rs index ba1738869..5018f86d8 100644 --- a/server-rs/crates/spacetime-module/src/migration.rs +++ b/server-rs/crates/spacetime-module/src/migration.rs @@ -188,6 +188,7 @@ macro_rules! migration_tables { public_work_like, // profile_membership / profile_recharge_product_config carry membership-cycle point fields. profile_membership, + profile_membership_plan, profile_recharge_product_config, profile_recharge_order, profile_recharge_refund, 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 39b91746a..03b8b388a 100644 --- a/server-rs/crates/spacetime-module/src/runtime/active/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/active/profile.rs @@ -486,6 +486,26 @@ pub struct ProfileRechargeProductConfig { pub(crate) membership_discount_bps: u32, } +#[spacetimedb::table(accessor = profile_membership_plan)] +#[derive(Clone)] +pub struct ProfileMembershipPlan { + #[primary_key] + pub(crate) plan: RuntimeProfileMembershipPlan, + pub(crate) title: String, + pub(crate) rank: u8, + pub(crate) month_price_cents: u64, + pub(crate) year_price_cents: u64, + pub(crate) period_points: u64, + pub(crate) model_access: RuntimeProfileMembershipModelAccess, + pub(crate) concurrent_job_limit: u32, + pub(crate) sort_order: i32, + pub(crate) enabled: bool, + pub(crate) created_by: String, + pub(crate) created_at: Timestamp, + pub(crate) updated_by: String, + pub(crate) updated_at: Timestamp, +} + #[spacetimedb::table( accessor = profile_recharge_order, index(accessor = by_profile_recharge_order_user_id, btree(columns = [user_id])),