diff --git a/docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md b/docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md index 00e7ca41f..50bb79dd0 100644 --- a/docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md +++ b/docs/technical/【技术设计】泥点三池与会员计费后端设计-2026-10-02.md @@ -442,8 +442,33 @@ flowchart TD | 14 | 下单接口 | 复用 `POST /api/profile/recharge/orders` | | 15 | `walletBalance` | 用户面不暴露 | -## 10. 仍需你确认的输入 +## 10. 评审输入与实施清单 -- **并发与模型权限本期不执行**:目录字段与投影照常暴露,但服务端拦截留到后续里程碑——如需本期就拦,请明确。 -- **`profile_recharge_product_config` 原地删列**:若 SpacetimeDB 本地发布拒绝,回退为「保留列 + 默认值 + 不读」,是否接受该回退。 -- **旧副本 `runtime/profile.rs`**:确认删除。 +### 10.1 已确认输入(本轮拍板) + +- **并发与模型权限**:只落字段、只用于展示,服务端本期不做拦截。 +- **`profile_recharge_product_config` 原地删列**:若 SpacetimeDB 本地发布拒绝删列,接受回退为「保留列 + 写死默认值 + 运行时不读」,并在生成绑定与表目录里标注为废弃残留。 +- **旧副本 `server-rs/crates/spacetime-module/src/runtime/profile.rs`**:确认删除(已删除)。 +- **档位命名**:列名与 wire 字段统一为 `plan`,不再使用 `plan_id` / `tier`。 + +### 10.2 实施状态 + +已落地(各自独立可编译): + +- 会员领域模块 `module-runtime/src/membership/{catalog,cycle,limits,upgrade}.rs`,公历基元抽为 `civil_calendar`。 +- 账本来源枚举末尾追加 `MembershipUpgradeGrant`,并同步契约常量、后台索引映射、前端来源标签与生成绑定。 +- 新增表 `profile_membership_plan`(主键为档位枚举 `RuntimeProfileMembershipPlan`),同步 `migration.rs`、表目录与生成绑定。 +- 会员档位目录播种 `ensure_default_profile_membership_plan`:表为空时按代码内目录整表播种,表非空时保留后台改价。 +- 删除未被引用的旧副本 `runtime/profile.rs`。 + +待落地——**第 1、2 项必须作为同一个提交落地**,否则工作区不可编译: + +1. `module-runtime` 领域层清退:删 `RuntimeProfileMembershipTier`、会员权益快照/记录、`cycle_period_days`、`tier` 与充点商品上的四个 `membership_*` 字段;会员快照/记录改用 `plan` + `cycle_index` / `cycle_count` / `cycle_kind`;充值中心改用 `membership_plans`。 +2. `spacetime-module`:两张表的列增删、充值中心装配、后台商品 upsert 收敛为泥点商品、会员目录价格与自然月周期逻辑改走 `plan` + 目录表。 +3. `spacetime-client` mapper / facade 与 `api-server` DTO / 后台接口同步;重新生成绑定。 +4. 三池余额投影 `ProfileMudPointBalanceResponse`、`ProfileMembershipResponse.plan`、`POST /api/profile/membership/upgrade-quote` 与下单重算。 +5. 充点 6 档落库与 `resolve_default_point_product_migration` 中 `points_1280` / `points_3280` 的历史反例修正。 +6. `packages/shared` 契约与共享组件同步。 +7. 门禁:`SPACETIME_SCHEMA_GUARD_ALLOW_BREAKING=1 npm run check:spacetime-schema`、`npm run check:generated-bindings`、`npm run check:encoding`、`git diff --check`。 + +> 约束来源:`scripts/check-spacetime-schema-guard.mjs` 只做源码静态比对,破坏性变更必须显式带 `SPACETIME_SCHEMA_GUARD_ALLOW_BREAKING=1` 才会放行;而 `module-runtime` 与 `spacetime-module` 相互依赖,任一侧单独清退都会让另一侧无法编译,所以领域层清退必须与 schema 变更同批提交。 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 03b8b388a..03e973fe9 100644 --- a/server-rs/crates/spacetime-module/src/runtime/active/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/active/profile.rs @@ -8436,7 +8436,37 @@ fn ensure_default_profile_task_config(ctx: &ReducerContext) -> ProfileTaskConfig }) } +/// 中文注释:会员档位目录以代码内目录为种子真相源,表为空时整表播种一次;后台改价后不再覆盖。 +fn ensure_default_profile_membership_plan(ctx: &ReducerContext) { + if ctx.db.profile_membership_plan().count() > 0 { + return; + } + + let now = ctx.timestamp; + for row in runtime_profile_membership_plan_catalog() { + ctx.db + .profile_membership_plan() + .insert(ProfileMembershipPlan { + plan: row.plan, + title: row.title, + 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, + created_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(), + created_at: now, + updated_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(), + updated_at: now, + }); + } +} + fn ensure_default_profile_recharge_product_config(ctx: &ReducerContext) { + ensure_default_profile_membership_plan(ctx); if ctx.db.profile_recharge_product_config().count() > 0 { migrate_legacy_default_profile_recharge_product_config(ctx); return;