fix(后台会员): 并发上限展示与提示改用后端下发值

- ProfileMembershipPlanAdminListResponse 增加 unlimitedConcurrencyThreshold,会员档位列表接口下发 MEMBERSHIP_UNLIMITED_CONCURRENCY。
- admin-web 的档位响应补上 unlimitedConcurrency,列表“不设上限”展示改用该标志,表单提示与校验改用后端阈值,删除页面硬编码的 128。
- 本地假后台同步补 unlimitedConcurrency 与 unlimitedConcurrencyThreshold。
This commit is contained in:
2026-10-03 17:45:30 +08:00
parent 0c93256b8c
commit 44bbc846ae
5 changed files with 28 additions and 11 deletions
+4
View File
@@ -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` 不在请求里,升级比较基准由代码内目录决定。 */
@@ -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<ProfileMembershipModelAccess>('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)}
/>
</AdminField>
<AdminField label="并发上限(128 = 不设上限)">
<AdminField
label={
unlimitedConcurrencyThreshold > 0
? `并发上限(${unlimitedConcurrencyThreshold} = 不设上限)`
: '并发上限'
}
>
<input
min={1}
step={1}
@@ -437,10 +445,9 @@ function sortPlanEntries(entries: ProfileMembershipPlanAdminResponse[]) {
function formatPlanLimits(entry: ProfileMembershipPlanAdminResponse) {
const modelLabel = entry.modelAccess === 'full' ? '基础+高性能' : '仅基础';
const concurrency =
entry.concurrentJobLimit >= UNLIMITED_CONCURRENT_JOB_LIMIT
? '不设上限'
: `${entry.concurrentJobLimit} 个`;
const concurrency = entry.unlimitedConcurrency
? '不设上限'
: `${entry.concurrentJobLimit} 个`;
return `${modelLabel} · 并发 ${concurrency}`;
}
+4 -1
View File
@@ -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 = (
@@ -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,
},
))
}
@@ -715,6 +715,8 @@ pub struct ProfileMembershipUpgradeQuoteResponse {
#[serde(rename_all = "camelCase")]
pub struct ProfileMembershipPlanAdminListResponse {
pub entries: Vec<ProfileMembershipPlanResponse>,
/// 并发哨兵阈值:`concurrent_job_limit >= 该值` 即不设上限;由后端下发,前端不硬编码。
pub unlimited_concurrency_threshold: u32,
}
/// 后台按档位改会员价格与权益;`rank` 不在请求里,升级比较基准由代码内目录决定。