import { createContext, useContext, PropsWithChildren, useState } from 'react' import Taro, { useLaunch } from '@tarojs/taro' import { authClient } from '../api' import { InferResponseType, InferRequestType } from 'hono' import { useMutation } from '@tanstack/react-query' import { isWeapp, isH5 } from './platform' // 用户类型定义 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 refreshUser: () => Promise setUser: (user: User | null) => void // 添加 setUser 方法类型 isLoading: boolean isLoggedIn: boolean } const AuthContext = createContext(undefined) export const AuthProvider: React.FC = ({ children }) => { // 用户状态 const [user, setUser] = useState(() => { // 从本地存储初始化用户信息 const userInfoStr = Taro.getStorageSync('userInfo') if (userInfoStr) { try { return JSON.parse(userInfoStr) } catch { return null } } return null }) // 获取用户信息的通用函数 const fetchUserInfo = async (): Promise => { // 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) { // Token失效时的处理逻辑 console.debug('Token失效,开始处理重新登录逻辑') if (isWeapp()) { // 小程序环境:使用静默登录重新获取token console.debug('小程序环境,尝试静默登录') try { const loginRes = await Taro.login() if (loginRes.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: newToken } = await response.json() Taro.setStorageSync('mini_token', newToken) // 重新获取完整的用户信息 const meResponse = await authClient.me.$get({}) if (meResponse.status === 200) { const user = await meResponse.json() Taro.setStorageSync('userInfo', JSON.stringify(user)) console.debug('静默登录成功,返回新用户信息') return user } } } } catch (silentLoginError) { console.debug('静默登录失败:', silentLoginError) } } else if (isH5()) { // H5环境:跳转到登录页 console.debug('H5环境,跳转到登录页') Taro.redirectTo({ url: '/pages/login/index' }) } // 如果重新登录失败或不是小程序环境,清除token并返回null Taro.removeStorageSync('mini_token') Taro.removeStorageSync('userInfo') return null } } // 静默登录mutation - 移除自动登录,改为需要用户主动同意 // 保留此函数供需要时手动调用(如用户主动点击登录按钮) const silentLoginMutation = useMutation({ mutationFn: async () => { // 使用统一的用户信息获取函数 return await fetchUserInfo() }, onSuccess: (user) => { if (user) { setUser(user) } } }) // 移除自动静默登录逻辑 - 小程序启动时不再自动登录 // 用户需要主动点击登录按钮并同意授权后才能登录 // 如需恢复自动登录,取消以下注释: // 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) => { setUser(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) => { setUser(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: () => { setUser(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) => { setUser(updatedUser) Taro.showToast({ title: '更新成功', icon: 'success', duration: 2000, }) }, onError: (error) => { Taro.showToast({ title: error.message || '更新失败,请重试', icon: 'none', duration: 2000, }) }, }) const updateUser = updateUserMutation.mutateAsync const refreshUser = async (): Promise => { try { const freshUser = await fetchUserInfo() if (freshUser) { setUser(freshUser) } else { setUser(null) } return freshUser } catch (error) { console.error('刷新用户信息失败:', error) return null } } const value = { user: user || null, login: loginMutation.mutateAsync, logout: logoutMutation.mutateAsync, register: registerMutation.mutateAsync, updateUser, refreshUser, setUser, // 导出 setUser 方法供外部更新用户状态 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 }