import { useState, useEffect, useMemo } from 'react'; import { App as AntApp } from 'antd'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { MembershipAPI, MembershipInfo } from '../../api/membership'; export interface PricingTier { id: string; name: string; badge?: string; description: string; billing: { monthly: { price: number; originalPrice?: number; text: string; subtext?: string; discountText?: string; discountRate?: number; }; yearly?: { price: number; originalPrice?: number; text: string; subtext?: string; discountText?: string; discountRate?: number; }; }; features: Array<{ icon: string; text: string; }>; details: string[]; limits: { tier: string; name: string; maxSubAccounts: number; maxTokens: number; maxAgents: number; maxKBSize: number; }; type: 'personal' | 'enterprise'; } const TIER_WEIGHTS: Record = { 'trial': 1, 'pro': 2, 'ultra': 3, 'ent_basic': 10, 'ent_standard': 11, 'custom': 99 }; const CUSTOM_PLAN: PricingTier = { id: 'custom', name: '按需定制', badge: '专享', description: '适合大型团队 / 行业方案', billing: { monthly: { price: 0, text: '面议', subtext: '专属优惠与支持', discountRate: 1 } }, features: [ { icon: 'Zap', text: '独立部署 / 专属网络环境' }, { icon: 'Check', text: '统一登录 / 操作记录可追溯' }, { icon: 'Crown', text: '行业专属插件与技能' }, { icon: 'Infinity', text: '服务保障与 7×24 专属支持' } ], details: [ '企业积分池 面议', '高质量模型配额 不限量', '子账号上限 不限量' ], limits: { tier: 'custom', name: '按需定制', maxSubAccounts: -1, maxTokens: -1, maxAgents: -1, maxKBSize: -1 }, type: 'enterprise' }; export function usePricingLogic() { const navigate = useNavigate(); const { tierId } = useParams(); const [searchParams] = useSearchParams(); const cycle = searchParams.get('cycle') || 'monthly'; const [activeTab, setActiveTab] = useState<'personal' | 'enterprise'>('personal'); const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>(cycle as any); const [membership, setMembership] = useState(null); const [plans, setPlans] = useState([]); const [loading, setLoading] = useState(false); const [orderInfo, setOrderInfo] = useState<{ order_id: string; pay_url: string; pay_expire_at: string; pay_is_expired: boolean; } | null>(null); const [payStatus, setPayStatus] = useState<'PENDING' | 'SUCCESS' | 'CLOSED' | 'FAIL' | 'EXPIRED'>('PENDING'); const [timeLeft, setTimeLeft] = useState(0); const { message } = AntApp.useApp(); // 分类方案 const PERSONAL_TIERS = useMemo(() => plans.filter(p => p.type === 'personal'), [plans]); const ENTERPRISE_TIERS = useMemo(() => plans.filter(p => p.type === 'enterprise'), [plans]); // 根据 URL 参数计算当前的支付信息 const tierInfo = useMemo(() => { if (!tierId || plans.length === 0) return null; const tier = plans.find(t => t.id === tierId); if (!tier) return null; return { tier, duration: billingCycle === 'monthly' ? 30 : 365 }; }, [tierId, billingCycle, plans]); // 综合支付信息 const paymentInfo = useMemo(() => { if (!tierInfo || !orderInfo) return null; return { ...tierInfo, ...orderInfo, status: payStatus, }; }, [tierInfo, orderInfo, payStatus]); useEffect(() => { loadMembership(); loadPlans(); }, []); useEffect(() => { if (activeTab === 'enterprise') { setBillingCycle('monthly'); } }, [activeTab]); const loadPlans = async () => { setLoading(true); try { const res = await MembershipAPI.getPlans(); // res 结构: { categories: [ { id: 'personal', plans: [...] }, ... ] } const categories = res.categories || []; const allPlans: PricingTier[] = []; categories.forEach((cat: any) => { const catPlans = (cat.plans || []).map((p: any) => ({ ...p, type: cat.id // 注入 personal 或 enterprise })); allPlans.push(...catPlans); }); // 追加静态的“按需定制”方案到企业版 if (!allPlans.find(p => p.id === 'custom')) { allPlans.push(CUSTOM_PLAN); } setPlans(allPlans); } catch (e) { console.error('Failed to load plans', e); message.error('加载会员方案失败'); } finally { setLoading(false); } }; // 当进入支付路由时,发起订阅请求 useEffect(() => { if (tierId && tierInfo && !orderInfo) { createOrder(); } }, [tierId, tierInfo]); // 倒计时逻辑 useEffect(() => { let timer: any; if (orderInfo && timeLeft > 0 && payStatus === 'PENDING') { timer = setInterval(() => { setTimeLeft((prev) => { if (prev <= 1) { setPayStatus('EXPIRED'); return 0; } return prev - 1; }); }, 1000); } return () => clearInterval(timer); }, [orderInfo, timeLeft, payStatus]); // 轮询支付状态 useEffect(() => { let pollTimer: any; if (orderInfo && payStatus === 'PENDING') { pollTimer = setInterval(async () => { try { const res = await MembershipAPI.queryPayStatus(orderInfo.order_id); if (res.status === 'SUCCESS') { setPayStatus('SUCCESS'); message.success('支付成功!'); loadMembership(); // 刷新会员信息 clearInterval(pollTimer); } else if (res.status === 'CLOSED' || res.status === 'FAIL') { setPayStatus(res.status); message.error(`支付失败: ${res.status}`); clearInterval(pollTimer); } else if (res.pay_is_expired) { setPayStatus('EXPIRED'); message.warning('支付已过期,请重新下单'); clearInterval(pollTimer); } } catch (e) { console.error('Polling payment status failed', e); } }, 2000); } return () => clearInterval(pollTimer); }, [orderInfo, payStatus]); const createOrder = async () => { if (!tierId || !tierInfo) return; setLoading(true); try { const res = await MembershipAPI.subscribe({ tier: tierId, durationDays: tierInfo.duration, }); setOrderInfo(res); setPayStatus('PENDING'); // 计算剩余秒数 const expireTime = new Date(res.pay_expire_at).getTime(); const now = new Date().getTime(); const diff = Math.floor((expireTime - now) / 1000); setTimeLeft(diff > 0 ? diff : 0); if (res.pay_is_expired) { setPayStatus('EXPIRED'); } } catch (e: any) { message.error(e.response?.data?.message || '创建订单失败,请稍后重试'); navigate('/pricing'); } finally { setLoading(false); } }; const loadMembership = async () => { try { const info = await MembershipAPI.getMe(); setMembership(info); } catch (e) { console.error('Failed to load membership', e); } }; const handleSubscribe = async (tier: PricingTier) => { // 基础校验 if (tier.id === 'custom') { message.info('请联系您的专属大客户经理或拨打客服热线进行面议'); return; } const price = billingCycle === 'yearly' && tier.billing.yearly ? tier.billing.yearly.price : tier.billing.monthly.price; if (tier.id === 'trial' && membership?.tier === 'trial') { message.info('您当前已在试用期内'); return; } // 使用子路由跳转 navigate(`/pricing/pay/${tier.id}?cycle=${billingCycle}`); }; const cancelPayment = async () => { if (orderInfo) { try { await MembershipAPI.closePayOrder(orderInfo.order_id); } catch (e) { console.error('Failed to close order', e); } } setOrderInfo(null); setPayStatus('PENDING'); navigate('/pricing'); }; const getTierStatus = (targetTierId: string) => { if (!membership) return 'none'; const currentWeight = TIER_WEIGHTS[membership.tier] || 0; const targetWeight = TIER_WEIGHTS[targetTierId] || 0; if (membership.tier === targetTierId) return 'current'; if (currentWeight > targetWeight) return 'included'; return 'none'; }; return { activeTab, setActiveTab, billingCycle, setBillingCycle, membership, loading, handleSubscribe, paymentInfo, timeLeft, cancelPayment, PERSONAL_TIERS, ENTERPRISE_TIERS, getTierStatus, }; } export type PricingLogicOutput = ReturnType;