import { createContext, useContext, PropsWithChildren } from 'react' import Taro from '@tarojs/taro' import { enterpriseAuthClient } from '../api' import { InferResponseType, InferRequestType } from 'hono' import { QueryClient, useQuery, useMutation, useQueryClient } from '@tanstack/react-query' // 用户类型定义 - 使用企业用户认证 export type User = InferResponseType type LoginRequest = InferRequestType['json'] // 企业用户注册可能由管理员创建,前端不提供注册接口 type RegisterRequest = { username: string; password: string } 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('enterprise_token') if (!token) { return null } try { const response = await enterpriseAuthClient.me.$get({}) if (response.status !== 200) { throw new Error('获取用户信息失败') } const user = await response.json() Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user)) return user } catch (error) { Taro.removeStorageSync('enterprise_token') Taro.removeStorageSync('enterpriseUserInfo') return null } }, staleTime: Infinity, // 用户信息不常变动,设为无限期 refetchOnWindowFocus: false, // 失去焦点不重新获取 refetchOnReconnect: false, // 网络重连不重新获取 }) const loginMutation = useMutation({ mutationFn: async (data) => { const response = await enterpriseAuthClient.login.$post({ json: data }) if (response.status !== 200) { throw new Error('登录失败') } const { token, user } = await response.json() Taro.setStorageSync('enterprise_token', token) Taro.setStorageSync('enterpriseUserInfo', JSON.stringify(user)) // if (refresh_token) { // Taro.setStorageSync('enterprise_refresh_token', refresh_token) // } return user }, onSuccess: (newUser) => { queryClient.setQueryData(['currentUser'], newUser) }, onError: (error) => { Taro.showToast({ title: error.message || '登录失败,请检查用户名和密码', icon: 'none', duration: 2000, }) }, }) const registerMutation = useMutation({ mutationFn: async () => { // 企业用户注册由管理员创建,前端不提供注册接口 throw new Error('企业用户注册请联系管理员创建账户') }, onSuccess: (newUser) => { queryClient.setQueryData(['currentUser'], newUser) }, onError: (error) => { Taro.showToast({ title: error.message || '企业用户注册请联系管理员', icon: 'none', duration: 3000, }) }, }) const logoutMutation = useMutation({ mutationFn: async () => { try { const response = await enterpriseAuthClient.logout.$post({}) if (response.status !== 200) { throw new Error('登出失败') } } catch (error) { console.error('Logout error:', error) } finally { Taro.removeStorageSync('enterprise_token') Taro.removeStorageSync('enterprise_refresh_token') Taro.removeStorageSync('enterpriseUserInfo') } }, 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 () => { // 企业用户信息更新可能由管理员管理,前端不提供更新接口 throw new Error('企业用户信息更新请联系管理员') }, 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 }