import React, { createContext, useContext, useState, useEffect, type ReactNode } from 'react' import Taro from '@tarojs/taro' import type { User } from '@/api/system/user/model' import { getUserInfo } from '@/api/layout' import { saveStorageByLoginUser, clearStorageByLoginUser } from '@/utils/server' import { isUserDisabled, blockDisabledUser } from '@/utils/auth' interface UserContextType { user: User | null isLoggedIn: boolean loading: boolean loginUser: (token: string, userInfo: User) => void logoutUser: () => void refreshUser: () => Promise syncFromStorage: () => boolean } const UserContext = createContext(undefined) export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const [user, setUser] = useState(null) const [loading, setLoading] = useState(true) useEffect(() => { const init = async () => { const token = Taro.getStorageSync('access_token') const userData = Taro.getStorageSync('User') // 情况1: 有本地token和用户数据,先用缓存快速显示,再异步校验状态 if (token && userData) { const parsed = typeof userData === 'string' ? JSON.parse(userData) : userData setUser(parsed as User) setLoading(false) // 异步从服务端校验用户是否被禁用(防止被禁用后仍可使用) verifyUserStatusFromServer() return } // 情况2: 有token但没有用户数据,尝试从接口获取 if (token) { try { const userInfo = await getUserInfo() if (isUserDisabled(userInfo)) { blockDisabledUser(userInfo) setUser(null) setLoading(false) return } setUser(userInfo) setLoading(false) return } catch { // token无效,清除 clearStorageByLoginUser() } } // 无本地token,保持未登录状态 setLoading(false) } init() }, []) /** 异步从服务端拉取最新用户信息,校验是否被禁用 */ const verifyUserStatusFromServer = async () => { try { const userInfo = await getUserInfo() if (isUserDisabled(userInfo)) { // 用户已被禁用,拦截 blockDisabledUser(userInfo) setUser(null) } else { // 状态正常,更新本地缓存和状态 const token = Taro.getStorageSync('access_token') if (token) { saveStorageByLoginUser(token, userInfo) } setUser(userInfo) } } catch { // 网络错误或token失效,静默处理(已有缓存的登录态可用) } } const loginUser = (token: string, userInfo: User) => { if (isUserDisabled(userInfo)) { blockDisabledUser(userInfo) return } saveStorageByLoginUser(token, userInfo) setUser(userInfo) } const logoutUser = () => { clearStorageByLoginUser() setUser(null) } /** 从 storage 快速同步用户状态(无需网络请求),供页面 useDidShow 调用 */ const syncFromStorage = (): boolean => { const token = Taro.getStorageSync('access_token') const userData = Taro.getStorageSync('User') if (token && userData) { const parsed = typeof userData === 'string' ? JSON.parse(userData) : userData // 快速检查缓存中的 status,如果已禁用则拦截 if (isUserDisabled(parsed)) { blockDisabledUser(parsed) setUser(null) return false } setUser(parsed as User) return true } if (!token) { setUser(null) } return false } const refreshUser = async (): Promise => { if (!Taro.getStorageSync('access_token')) return null try { const userInfo = await getUserInfo() if (isUserDisabled(userInfo)) { blockDisabledUser(userInfo) setUser(null) return null } const token = Taro.getStorageSync('access_token') if (token) { saveStorageByLoginUser(token, userInfo) } setUser(userInfo) return userInfo } catch { return null } } return ( {children} ) } export const useUserContext = (): UserContextType => { const context = useContext(UserContext) if (!context) { throw new Error('useUserContext must be used within UserProvider') } return context }