| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- 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<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
- refreshUser: () => Promise<User | null>
- setUser: (user: User | null) => void // 添加 setUser 方法类型
- isLoading: boolean
- isLoggedIn: boolean
- }
- const AuthContext = createContext<AuthContextType | undefined>(undefined)
- export const AuthProvider: React.FC<PropsWithChildren> = ({ children }) => {
- // 用户状态
- const [user, setUser] = useState<User | null>(() => {
- // 从本地存储初始化用户信息
- const userInfoStr = Taro.getStorageSync('userInfo')
- if (userInfoStr) {
- try {
- return JSON.parse(userInfoStr)
- } catch {
- return null
- }
- }
- return null
- })
- // 获取用户信息的通用函数
- const fetchUserInfo = async (): Promise<User | null> => {
- // 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<User | null, Error, void>({
- mutationFn: async () => {
- // 使用统一的用户信息获取函数
- return await fetchUserInfo()
- },
- onSuccess: (user) => {
- if (user) {
- setUser(user)
- }
- }
- })
- // 移除自动静默登录逻辑 - 小程序启动时不再自动登录
- // 用户需要主动点击登录按钮并同意授权后才能登录
- // 如需恢复自动登录,取消以下注释:
- // 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) => {
- setUser(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) => {
- setUser(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: () => {
- setUser(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) => {
- 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<User | null> => {
- 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 <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
- }
|