| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- 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<typeof authClient.me.$get, 200>
- export type MiniLoginUser = InferResponseType<typeof authClient['mini-login']['$post'], 200>['user']
- type LoginRequest = InferRequestType<typeof authClient.login.$post>['json']
- type RegisterRequest = InferRequestType<typeof authClient.register.$post>['json']
- interface AuthContextType {
- user: User | null
- login: (data: LoginRequest) => Promise<User>
- logout: () => Promise<void>
- register: (data: RegisterRequest) => Promise<User>
- updateUser: (userData: Partial<User>) => void
- isLoading: boolean
- isLoggedIn: boolean
- }
- const AuthContext = createContext<AuthContextType | undefined>(undefined)
- const queryClient = new QueryClient()
- export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
- const queryClient = useQueryClient()
- const { data: user, isLoading } = useQuery<User | null, Error>({
- 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<MiniLoginUser | null, Error, void>({
- 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
- // 静默登录不请求用户信息
- }
- })
- 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<User, Error, LoginRequest>({
- 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<User, Error, RegisterRequest>({
- 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<void, Error>({
- 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<User, Error, Partial<User>>({
- 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<User | null>({
- // 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 <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
- }
- export const useAuth = () => {
- const context = useContext(AuthContext)
- if (context === undefined) {
- throw new Error('useAuth must be used within an AuthProvider')
- }
- return context
- }
- export { queryClient }
|