From 44bbc846ae3b25ac606268a9d80887e3c92ca04c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 3 Oct 2026 17:45:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=90=8E=E5=8F=B0=E4=BC=9A=E5=91=98):=20?= =?UTF-8?q?=E5=B9=B6=E5=8F=91=E4=B8=8A=E9=99=90=E5=B1=95=E7=A4=BA=E4=B8=8E?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E6=94=B9=E7=94=A8=E5=90=8E=E7=AB=AF=E4=B8=8B?= =?UTF-8?q?=E5=8F=91=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProfileMembershipPlanAdminListResponse 增加 unlimitedConcurrencyThreshold,会员档位列表接口下发 MEMBERSHIP_UNLIMITED_CONCURRENCY。 - admin-web 的档位响应补上 unlimitedConcurrency,列表“不设上限”展示改用该标志,表单提示与校验改用后端阈值,删除页面硬编码的 128。 - 本地假后台同步补 unlimitedConcurrency 与 unlimitedConcurrencyThreshold。 --- apps/admin-web/src/api/adminApiTypes.ts | 4 +++ .../src/pages/AdminMembershipPlanPage.tsx | 25 ++++++++++++------- scripts/admin-web-fake-api.mjs | 5 +++- .../crates/api-server/src/runtime_profile.rs | 3 ++- .../crates/shared-contracts/src/runtime.rs | 2 ++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/apps/admin-web/src/api/adminApiTypes.ts b/apps/admin-web/src/api/adminApiTypes.ts index 3751b80e8..8f96fa180 100644 --- a/apps/admin-web/src/api/adminApiTypes.ts +++ b/apps/admin-web/src/api/adminApiTypes.ts @@ -795,11 +795,15 @@ export interface ProfileMembershipPlanAdminResponse { modelAccess: ProfileMembershipModelAccess; /** 并发上限;`128` 表示不设上限。 */ concurrentJobLimit: number; + /** 是否不设并发上限;由后端按哨兵值派生,前端不再自己比较 `128`。 */ + unlimitedConcurrency: boolean; enabled: boolean; } export interface ProfileMembershipPlanAdminListResponse { entries: ProfileMembershipPlanAdminResponse[]; + /** 并发哨兵阈值:`concurrentJobLimit >= 该值` 即不设上限;由后端下发,前端不硬编码。 */ + unlimitedConcurrencyThreshold: number; } /** 后台按档位改价 / 改权益;`rank` 不在请求里,升级比较基准由代码内目录决定。 */ diff --git a/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx b/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx index 606330914..0dc67a2bd 100644 --- a/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx +++ b/apps/admin-web/src/pages/AdminMembershipPlanPage.tsx @@ -32,9 +32,6 @@ interface AdminMembershipPlanPageProps { onResultChange: (result: ProfileMembershipPlanAdminResponse) => void; } -/** 并发上限哨兵:与后端 `MEMBERSHIP_UNLIMITED_CONCURRENCY` 一致,128 表示不设上限。 */ -const UNLIMITED_CONCURRENT_JOB_LIMIT = 128; - const modelAccessOptions: Array<{ value: ProfileMembershipModelAccess; label: string; @@ -67,6 +64,8 @@ export function AdminMembershipPlanPage({ const [modelAccess, setModelAccess] = useState('basic'); const [concurrentJobLimit, setConcurrentJobLimit] = useState('1'); + const [unlimitedConcurrencyThreshold, setUnlimitedConcurrencyThreshold] = + useState(0); const [enabled, setEnabled] = useState(false); const [rankLabel, setRankLabel] = useState('0'); const [isLoading, setIsLoading] = useState(false); @@ -86,6 +85,7 @@ export function AdminMembershipPlanPage({ try { const response = await listProfileMembershipPlans(token); setEntries(response.entries); + setUnlimitedConcurrencyThreshold(response.unlimitedConcurrencyThreshold); const firstEntry = response.entries.find((entry) => entry.plan === plan) ?? response.entries[0]; @@ -122,7 +122,9 @@ export function AdminMembershipPlanPage({ } if (!normalizedConcurrentJobLimit) { setErrorMessage( - `并发上限必须是大于 0 的整数;${UNLIMITED_CONCURRENT_JOB_LIMIT} 表示不设上限`, + unlimitedConcurrencyThreshold > 0 + ? `并发上限必须是大于 0 的整数;${unlimitedConcurrencyThreshold} 表示不设上限` + : '并发上限必须是大于 0 的整数', ); return; } @@ -289,7 +291,13 @@ export function AdminMembershipPlanPage({ onChange={(event) => setPeriodPoints(event.target.value)} /> - + 0 + ? `并发上限(${unlimitedConcurrencyThreshold} = 不设上限)` + : '并发上限' + } + > = UNLIMITED_CONCURRENT_JOB_LIMIT - ? '不设上限' - : `${entry.concurrentJobLimit} 个`; + const concurrency = entry.unlimitedConcurrency + ? '不设上限' + : `${entry.concurrentJobLimit} 个`; return `${modelLabel} · 并发 ${concurrency}`; } diff --git a/scripts/admin-web-fake-api.mjs b/scripts/admin-web-fake-api.mjs index f6ecef174..2ebb1b7b5 100644 --- a/scripts/admin-web-fake-api.mjs +++ b/scripts/admin-web-fake-api.mjs @@ -756,6 +756,7 @@ function route(pathname, method, url) { periodPoints, modelAccess, concurrentJobLimit, + unlimitedConcurrency: concurrentJobLimit >= 128, enabled, }); const plans = [ @@ -766,7 +767,9 @@ function route(pathname, method, url) { plan('max', '旗舰会员', 4, 69_900, 699_000, 8_650, 'full', 128, true), ]; // 本地预览不解析请求体:POST 统一回显 starter,仅用于页面回填。 - return method === 'POST' ? plans[1] : { entries: plans }; + return method === 'POST' + ? plans[1] + : { entries: plans, unlimitedConcurrencyThreshold: 128 }; } if (pathname === '/admin/api/profile/recharge-products') { const product = ( diff --git a/server-rs/crates/api-server/src/runtime_profile.rs b/server-rs/crates/api-server/src/runtime_profile.rs index 287cae8a6..fbdd8bdc2 100644 --- a/server-rs/crates/api-server/src/runtime_profile.rs +++ b/server-rs/crates/api-server/src/runtime_profile.rs @@ -9,7 +9,7 @@ use axum::{ }; use hmac::{Hmac, Mac}; use module_runtime::{ - AnalyticsGranularity, PROFILE_MEMBERSHIP_RECOMMENDED_PLAN, + AnalyticsGranularity, MEMBERSHIP_UNLIMITED_CONCURRENCY, PROFILE_MEMBERSHIP_RECOMMENDED_PLAN, PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_H5, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_JSAPI, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_MINI_PROGRAM, @@ -1182,6 +1182,7 @@ pub async fn admin_list_profile_membership_plans( .into_iter() .map(build_profile_membership_plan_response) .collect(), + unlimited_concurrency_threshold: MEMBERSHIP_UNLIMITED_CONCURRENCY, }, )) } diff --git a/server-rs/crates/shared-contracts/src/runtime.rs b/server-rs/crates/shared-contracts/src/runtime.rs index 7bf133256..4036e3d25 100644 --- a/server-rs/crates/shared-contracts/src/runtime.rs +++ b/server-rs/crates/shared-contracts/src/runtime.rs @@ -715,6 +715,8 @@ pub struct ProfileMembershipUpgradeQuoteResponse { #[serde(rename_all = "camelCase")] pub struct ProfileMembershipPlanAdminListResponse { pub entries: Vec, + /// 并发哨兵阈值:`concurrent_job_limit >= 该值` 即不设上限;由后端下发,前端不硬编码。 + pub unlimited_concurrency_threshold: u32, } /// 后台按档位改会员价格与权益;`rank` 不在请求里,升级比较基准由代码内目录决定。