import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react' import { licenseApi, type LicenseInfo } from '@/api/license' import { getAccessToken } from '@/lib/auth-storage' interface LicenseState { pro: boolean info: LicenseInfo | null reload: () => void } const LicenseContext = createContext({ pro: false, info: null, reload: () => {} }) /** * 授权状态 Provider。 * * ⚠️ 必须只在**已登录**时请求:`/system/license` 是鉴权接口, * 未登录访问会 401,而 401 会触发全局"跳登录"逻辑。 * 之前没有这道判断,登录页一挂载就请求 → 401 → 跳 /login(整页重载) * → 重新挂载 → 再 401,形成死循环:登录页一直转圈+闪屏 * (线上实测 10 分钟 1566 次 401)。三处修复: * 1) 这里没有 token 就不请求 * 2) 请求带 silent,401 不触发全局跳转副作用 * 3) http.ts 的 redirectToLogin 在已处于 /login 时不再重载 * * 登录成功后 token 才出现,所以用一个轻量的定时探测(只读 localStorage, * 不发请求)在 token 出现时补拉一次。 */ export function LicenseProvider({ children }: { children: React.ReactNode }) { const [state, setState] = useState<{ pro: boolean; info: LicenseInfo | null }>({ pro: false, info: null }) const loadedRef = useRef(false) const load = useCallback(() => { // 未登录(登录页)直接跳过,避免 401 → 跳登录 → 重载 的死循环 if (!getAccessToken()) return licenseApi .status() .then(res => { loadedRef.current = true setState({ pro: Boolean(res.pro), info: res.license }) }) .catch(() => { // 拿不到授权信息时按免费版处理,不打断页面 setState({ pro: false, info: null }) }) }, []) useEffect(() => { load() // 登录后 token 才写入 localStorage,用 1 秒一次的低成本探测补拉(只读本地存储) const timer = window.setInterval(() => { if (loadedRef.current) { window.clearInterval(timer) return } if (getAccessToken()) { window.clearInterval(timer) load() } }, 1000) return () => window.clearInterval(timer) }, [load]) return ( {children} ) } export function useLicense() { return useContext(LicenseContext) }