fix(后台会员): 并发上限展示与提示改用后端下发值
- ProfileMembershipPlanAdminListResponse 增加 unlimitedConcurrencyThreshold,会员档位列表接口下发 MEMBERSHIP_UNLIMITED_CONCURRENCY。 - admin-web 的档位响应补上 unlimitedConcurrency,列表“不设上限”展示改用该标志,表单提示与校验改用后端阈值,删除页面硬编码的 128。 - 本地假后台同步补 unlimitedConcurrency 与 unlimitedConcurrencyThreshold。
This commit is contained in:
@@ -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}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -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` 不在请求里,升级比较基准由代码内目录决定。
|
||||
|
||||
Reference in New Issue
Block a user