feat: 渠道展示、订阅套餐、系统配置全功能

- 新增 Channel / SubscriptionPlan / SystemConfig 三个数据模型
- Order 模型扩展支持订阅订单(order_type, plan_id, subscription_group_id)
- Sub2API client 新增分组查询、订阅分配/续期、用户订阅查询
- 订单服务支持订阅履约流程(CAS 锁 + 分组消失安全处理)
- 管理后台:渠道管理、订阅套餐管理、系统配置、Sub2API 分组同步
- 用户页面:双 Tab UI(按量付费/包月订阅)、渠道卡片、充值弹窗、订阅确认
- PaymentForm 支持 fixedAmount 固定金额模式
- 订单状态 API 返回 failedReason 用于订阅异常展示
- 数据库迁移脚本
This commit is contained in:
erio
2026-03-13 19:06:25 +08:00
parent 9f621713c3
commit eafb7e49fa
38 changed files with 5376 additions and 289 deletions

View File

@@ -0,0 +1,54 @@
'use client';
import React from 'react';
import type { Locale } from '@/lib/locale';
import { pickLocaleText } from '@/lib/locale';
interface MainTabsProps {
activeTab: 'topup' | 'subscribe';
onTabChange: (tab: 'topup' | 'subscribe') => void;
showSubscribeTab: boolean;
isDark: boolean;
locale: Locale;
}
export default function MainTabs({ activeTab, onTabChange, showSubscribeTab, isDark, locale }: MainTabsProps) {
if (!showSubscribeTab) return null;
const tabs: { key: 'topup' | 'subscribe'; label: string }[] = [
{ key: 'topup', label: pickLocaleText(locale, '按量付费', 'Pay-as-you-go') },
{ key: 'subscribe', label: pickLocaleText(locale, '包月套餐', 'Subscription') },
];
return (
<div
className={[
'inline-flex rounded-xl p-1',
isDark ? 'bg-slate-800' : 'bg-slate-100',
].join(' ')}
>
{tabs.map((tab) => {
const isActive = activeTab === tab.key;
return (
<button
key={tab.key}
type="button"
onClick={() => onTabChange(tab.key)}
className={[
'rounded-lg px-5 py-2 text-sm font-medium transition-all',
isActive
? isDark
? 'bg-slate-700 text-slate-100 shadow-sm'
: 'bg-white text-slate-900 shadow-sm'
: isDark
? 'text-slate-400 hover:text-slate-200'
: 'text-slate-500 hover:text-slate-700',
].join(' ')}
>
{tab.label}
</button>
);
})}
</div>
);
}