From c391352ff49bd114538ba4f25af8995b1c3d6872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sun, 4 Oct 2026 10:30:38 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E4=BC=9A=E5=91=98):=20=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E4=B8=8A=E9=99=90=E6=8C=89=E6=9C=89=E6=95=88=E6=A1=A3=E4=BD=8D?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=EF=BC=8C=E8=BF=87=E6=9C=9F=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E5=9B=9E=E8=90=BD=20Normal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - effective_profile_concurrent_job_limit 原先直接读 profile_membership.plan;该列到期后仍保留最近购买档位,过期 Max 会继续拿 128 不限并发哨兵 - 补 expires_at 有效性过滤,与 build_profile_membership_snapshot_from_row 的对外口径一致;缺会员行 / 已过期按 Normal --- .../spacetime-module/src/runtime/active/profile.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 94fa85e50..4747f3201 100644 --- a/server-rs/crates/spacetime-module/src/runtime/active/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/active/profile.rs @@ -9529,9 +9529,13 @@ fn membership_plan_row( ctx.db.profile_membership_plan().plan().find(&plan) } -/// 账号当前有效的并发上限:`profile_membership.plan` → 目录行 `concurrent_job_limit`。 +/// 账号当前有效的并发上限:会员行**当前有效档位** → 目录行 `concurrent_job_limit`。 /// -/// 缺会员行按 `Normal`(非会员);缺目录行**失败关闭**到 `Normal`(=1), +/// `profile_membership.plan` 保留最近一次购买的档位(到期后仍供展示「已失效的套餐」), +/// 所以这里必须像 [`build_profile_membership_snapshot_from_row`] 一样先按 `expires_at` 判断有效性: +/// 过期行回落 `Normal`,否则过期 `Max` 会继续拿到 `128`(不限并发)哨兵。 +/// +/// 缺会员行 / 已过期按 `Normal`(非会员);缺目录行**失败关闭**到 `Normal`(=1), /// `128` 哨兵由 [`is_unlimited_concurrency`] 在调用方解释。 pub(crate) fn effective_profile_concurrent_job_limit( ctx: &ReducerContext, @@ -9542,6 +9546,7 @@ pub(crate) fn effective_profile_concurrent_job_limit( .profile_membership() .user_id() .find(&owner_user_id.to_string()) + .filter(|row| active_membership_row_at(row, ctx.timestamp)) .map(|row| row.plan) .unwrap_or(RuntimeProfileMembershipPlan::Normal); membership_plan_row(ctx, plan)