feat(admin): show user subscription plan and token quota in user pages

Expose active subscription summary on user list and detail so operators can see plan type, total/used/remaining tokens without opening billing subscriptions.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-07 16:22:08 +08:00
parent c1aa1ba459
commit e07686f29d
6 changed files with 193 additions and 10 deletions
+36
View File
@@ -0,0 +1,36 @@
import type { AdminSubscription, PlanDefinition, UserSubscriptionSummary } from '../../types';
export function formatTokenCount(value: number) {
return value.toLocaleString('zh-CN');
}
export function formatTokenQuota(limit: number, used: number) {
if (limit === 0) {
return `已用 ${formatTokenCount(used)} / 无限`;
}
const remaining = Math.max(0, limit - used);
return `${formatTokenCount(remaining)} / ${formatTokenCount(limit)}(已用 ${formatTokenCount(used)})`;
}
export function resolvePlanLabel(planType: string, plans: PlanDefinition[]) {
return plans.find((plan) => plan.planType === planType)?.name ?? planType;
}
export function subscriptionStatusLabel(status: AdminSubscription['status']) {
if (status === 'active') return '有效';
if (status === 'expired') return '已到期';
if (status === 'cancelled') return '已取消';
return status;
}
export function summarizeSubscription(
subscription: Pick<UserSubscriptionSummary, 'planType' | 'periodTokensLimit' | 'periodTokensUsed'> | null | undefined,
fallbackPlanType?: string,
) {
if (!subscription) {
return fallbackPlanType && fallbackPlanType !== 'free'
? `${fallbackPlanType}(无有效订阅)`
: '免费 / 无订阅';
}
return formatTokenQuota(subscription.periodTokensLimit, subscription.periodTokensUsed);
}