import { createContext, useContext, PropsWithChildren, useState } from 'react' import Taro from '@tarojs/taro' import { authClient } from '../api' import { InferResponseType, InferRequestType } from 'hono' import { QueryClient, QueryClientProvider, useQuery, useMutation, useQueryClient } from '@tanstack/react-query' // 用户类型定义 export type User = InferResponseType 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, // 网络重连不重新获取 }) 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 const value = { user: user || null, login: loginMutation.mutateAsync, logout: logoutMutation.mutateAsync, register: registerMutation.mutateAsync, updateUser, isLoading: isLoading || loginMutation.isPending || registerMutation.isPending || logoutMutation.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 }