/** * 用户认证状态管理 Store * 管理 token、用户信息、VIP 状态,持久化到 localStorage */ import { create } from 'zustand'; import { api } from '@/lib/api'; export interface AuthState { // 状态 token: string | null; userId: number | null; username: string | null; isVip: boolean; vipExpireAt: string | null; dailyUsed: number; dailyRemaining: number | null; // null = VIP 无限制 dailyLimit: number | null; isLoading: boolean; isInitialized: boolean; // Actions login: (username: string, password: string) => Promise; register: (username: string, password: string) => Promise; logout: () => Promise; fetchUserInfo: () => Promise; fetchUsage: () => Promise; initialize: () => Promise; } const TOKEN_KEY = 'auth_token'; export const useAuthStore = create((set, get) => ({ token: null, userId: null, username: null, isVip: false, vipExpireAt: null, dailyUsed: 0, dailyRemaining: null, dailyLimit: null, isLoading: false, isInitialized: false, initialize: async () => { if (typeof window === 'undefined') { set({ isInitialized: true }); return; } const savedToken = localStorage.getItem(TOKEN_KEY); if (!savedToken) { set({ isInitialized: true }); return; } set({ token: savedToken, isLoading: true }); try { const userInfo = await api.me(savedToken); set({ userId: userInfo.user_id, username: userInfo.username, isVip: userInfo.is_vip, vipExpireAt: userInfo.vip_expire_at, isInitialized: true, isLoading: false, }); // 同时获取今日使用量 try { const usage = await api.getUsageToday(savedToken); set({ dailyUsed: usage.used, dailyRemaining: usage.remaining, dailyLimit: usage.limit }); } catch { /* 静默失败 */ } } catch { // Token 过期或无效,清除 localStorage.removeItem(TOKEN_KEY); set({ token: null, userId: null, username: null, isVip: false, vipExpireAt: null, isInitialized: true, isLoading: false, }); } }, login: async (username: string, password: string) => { set({ isLoading: true }); try { const res = await api.login(username, password); localStorage.setItem(TOKEN_KEY, res.token); set({ token: res.token, userId: res.user_id, username: res.username, isLoading: false, }); // Fetch VIP info and usage try { const vipInfo = await api.vipStatus(res.token); set({ isVip: vipInfo.is_vip, vipExpireAt: vipInfo.vip_expire_at }); const usage = await api.getUsageToday(res.token); set({ dailyUsed: usage.used, dailyRemaining: usage.remaining, dailyLimit: usage.limit }); } catch { // VIP/usage 查询失败不影响登录 } } catch (error) { set({ isLoading: false }); throw error; } }, register: async (username: string, password: string) => { set({ isLoading: true }); try { const res = await api.register(username, password); localStorage.setItem(TOKEN_KEY, res.token); set({ token: res.token, userId: res.user_id, username: res.username, isVip: false, vipExpireAt: null, isLoading: false, }); } catch (error) { set({ isLoading: false }); throw error; } }, logout: async () => { const { token } = get(); if (token) { try { await api.logout(token); } catch { // 忽略登出接口错误 } } localStorage.removeItem(TOKEN_KEY); set({ token: null, userId: null, username: null, isVip: false, vipExpireAt: null, dailyUsed: 0, dailyRemaining: null, dailyLimit: null, }); }, fetchUsage: async () => { const { token } = get(); if (!token) return; try { const usage = await api.getUsageToday(token); set({ dailyUsed: usage.used, dailyRemaining: usage.remaining, dailyLimit: usage.limit }); } catch { /* 静默失败 */ } }, fetchUserInfo: async () => { const { token } = get(); if (!token) return; try { const userInfo = await api.me(token); set({ userId: userInfo.user_id, username: userInfo.username, isVip: userInfo.is_vip, vipExpireAt: userInfo.vip_expire_at, }); } catch { // 静默失败 } }, }));