import { createContext, useContext, PropsWithChildren } from 'react' import Taro, { useLaunch } from '@tarojs/taro' import { authClient } from '../api' import { InferResponseType, InferRequestType } from 'hono' import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query' // 用户类型定义 export type User = InferResponseType export type MiniLoginUser = InferResponseType['user'] type LoginRequest = InferRequestType['json'] type RegisterRequest = InferRequestType['json'] interface AuthContextType { user: User | null login: (data: LoginRequest) => Promise logout: () => Promise register: (data: RegisterRequest) => Promise updateUser: (userData: Partial) => void isLoading: boolean isLoggedIn: boolean } const AuthContext = createContext(undefined) const queryClient = new QueryClient() export const AuthProvider: React.FC = ({ children }) => { const queryClient = useQueryClient() const { data: user, isLoading } = useQuery({ queryKey: ['currentUser'], queryFn: async () => { const token = Taro.getStorageSync('mini_token') if (!token) { return null } try { const response = await authClient.me.$get({}) if (response.status !== 200) { throw new Error('获取用户信息失败') } const user = await response.json() Taro.setStorageSync('userInfo', JSON.stringify(user)) return user } catch (error) { Taro.removeStorageSync('mini_token') Taro.removeStorageSync('userInfo') return null } }, staleTime: Infinity, // 用户信息不常变动,设为无限期 refetchOnWindowFocus: false, // 失去焦点不重新获取 refetchOnReconnect: false, // 网络重连不重新获取 }) // 静默登录mutation - 应用启动时自动尝试登录 const silentLoginMutation = useMutation({ mutationFn: async () => { try { // 尝试静默登录 const loginRes = await Taro.login() if (!loginRes.code) { return null // 静默登录失败,但不抛出错误 } // 使用小程序code进行静默登录 const response = await authClient['mini-login'].$post({ json: { code: loginRes.code, // 静默登录不请求用户信息 tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1 } }) if (response.status === 200) { const { token, user } = await response.json() Taro.setStorageSync('mini_token', token) Taro.setStorageSync('userInfo', JSON.stringify(user)) return user } return null // 静默登录失败 } catch (error) { // 静默登录失败不抛出错误,不影响用户体验 console.debug('静默登录失败:', error) return null } }, onSuccess: (user) => { if (user) { queryClient.setQueryData(['currentUser'], user) } } }) // 在小程序启动时执行静默登录,刷新token和sessionKey useLaunch(() => { silentLoginMutation.mutate() }) const loginMutation = useMutation({ mutationFn: async (data) => { const response = await authClient.login.$post({ json: data }) if (response.status !== 200) { throw new Error('登录失败') } const { token, user } = await response.json() Taro.setStorageSync('mini_token', token) Taro.setStorageSync('userInfo', JSON.stringify(user)) return user }, onSuccess: (newUser) => { queryClient.setQueryData(['currentUser'], newUser) }, onError: (error) => { Taro.showToast({ title: error.message || '登录失败,请检查用户名和密码', icon: 'none', duration: 2000, }) }, }) const registerMutation = useMutation({ mutationFn: async (data) => { const response = await authClient.register.$post({ json: data }) if (response.status !== 201) { throw new Error('注册失败') } const { token, user } = await response.json() Taro.setStorageSync('mini_token', token) Taro.setStorageSync('userInfo', JSON.stringify(user)) return user }, onSuccess: (newUser) => { queryClient.setQueryData(['currentUser'], newUser) }, onError: (error) => { Taro.showToast({ title: error.message || '注册失败,请重试', icon: 'none', duration: 2000, }) }, }) const logoutMutation = useMutation({ mutationFn: async () => { try { const response = await authClient.logout.$post({}) if (response.status !== 200) { throw new Error('登出失败') } } catch (error) { console.error('Logout error:', error) } finally { Taro.removeStorageSync('mini_token') Taro.removeStorageSync('userInfo') } }, onSuccess: () => { queryClient.setQueryData(['currentUser'], null) Taro.redirectTo({ url: '/pages/login/index' }) }, onError: (error) => { Taro.showToast({ title: error.message || '登出失败', icon: 'none', duration: 2000, }) }, }) const updateUserMutation = useMutation>({ mutationFn: async (userData) => { const response = await authClient.me.$put({ json: userData }) if (response.status !== 200) { throw new Error('更新用户信息失败') } const updatedUser = await response.json() Taro.setStorageSync('userInfo', JSON.stringify(updatedUser)) return updatedUser }, onSuccess: (updatedUser) => { queryClient.setQueryData(['currentUser'], updatedUser) Taro.showToast({ title: '更新成功', icon: 'success', duration: 2000, }) }, onError: (error) => { Taro.showToast({ title: error.message || '更新失败,请重试', icon: 'none', duration: 2000, }) }, }) const updateUser = updateUserMutation.mutateAsync // // 使用React Query获取用户信息 // const { data: user } = useQuery({ // queryKey: ['currentUser'], // queryFn: async () => { // // 直接从本地存储获取用户信息 // const userInfoStr = Taro.getStorageSync('userInfo') // if (userInfoStr) { // try { // return JSON.parse(userInfoStr) // } catch { // return null // } // } // return null // }, // staleTime: Infinity, // 用户信息不常变动,设为无限期 // refetchOnWindowFocus: false, // 失去焦点不重新获取 // refetchOnReconnect: false, // 网络重连不重新获取 // enabled: false, // 不自动执行,由静默登录和登录mutation更新 // }) const value = { user: user || null, login: loginMutation.mutateAsync, logout: logoutMutation.mutateAsync, register: registerMutation.mutateAsync, updateUser, isLoading: loginMutation.isPending || registerMutation.isPending || logoutMutation.isPending || silentLoginMutation.isPending, isLoggedIn: !!user, } return {children} } export const useAuth = () => { const context = useContext(AuthContext) if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider') } return context } export { queryClient }