auth.js 4.73 KB
/*
 * @Date: 2025-03-20 21:11:31
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-06-13 11:09:37
 * @FilePath: /mlaj/src/contexts/auth.js
 * @Description: 认证上下文管理模块,提供用户认证状态管理、登录登出功能
 */

// 导入Vue的组合式API相关函数
import { ref, provide, inject, onMounted } from 'vue'
import { logoutAPI, getUserInfoAPI } from '@/api/users'
import { getAuthInfoAPI } from '@/api/auth'

// 创建认证上下文的Symbol key,用于provide/inject依赖注入
// 使用Symbol确保key的唯一性,避免命名冲突
const authKey = Symbol('auth')

/**
 * 提供认证功能的组合式函数
 * 该函数创建并提供认证上下文,包含:
 * 1. 用户状态管理(currentUser)
 * 2. 加载状态管理(loading)
 * 3. 登录登出功能
 * 4. Token过期检查
 */
export function provideAuth() {
  // 当前用户状态,初始为null表示未登录
  const currentUser = ref(null)
  // 加载状态标志,用于控制加载动画等UI展示
  const loading = ref(true)

  /**
   * 检查登录token是否过期
   * @returns {boolean} true表示token有效,false表示token已过期
   */
  // const checkTokenExpiration = () => {
  //   const loginTimestamp = localStorage.getItem('loginTimestamp')
  //   if (loginTimestamp) {
  //     const now = Date.now()
  //     const diff = now - parseInt(loginTimestamp)
  //     // 检查是否超过24小时(24 * 60 * 60 * 1000 毫秒)
  //     if (diff > 24 * 60 * 60 * 1000) {
  //       // token过期,清理登录状态并返回false
  //       logout()
  //       return false
  //     }
  //   }
  //   return true
  // }

  /**
   * 组件挂载时初始化用户状态
   * 1. 从localStorage读取保存的用户信息
   * 2. 检查token是否过期
   * 3. 恢复用户状态或保持登出状态
   */
  onMounted(async () => {
    const savedUser = localStorage.getItem('currentUser')
    if (savedUser) {
      // // 检查token是否有效,有效则恢复用户状态
      // if (checkTokenExpiration()) {
      //   currentUser.value = JSON.parse(savedUser)
      // }
      // 从服务器获取用户信息并更新本地存储
      const { code, data } = await getUserInfoAPI();
      if (code) {
        currentUser.value = data.user
        localStorage.setItem('currentUser', JSON.stringify(currentUser.value))
      } else {
        logout()
      }
    } else {
      // 查询用户是否授权, 从服务器获取用户信息并更新本地存储
      const { code, data } = await getAuthInfoAPI();
      if(code) {
        if (data.openid_has) {
          currentUser.value = data.user
          localStorage.setItem('currentUser', JSON.stringify(currentUser.value))
        }
      }
    }
    // 初始化完成,关闭加载状态
    loading.value = false
  })

  /**
   * 用户登录函数
   * @param {Object} userData - 用户数据对象,包含用户基本信息
   * @returns {boolean} 登录是否成功
   */
  const login = (userData) => {
    // 更新当前用户状态
    currentUser.value = userData
    if (currentUser.value) {
      try {
        // 持久化存储用户信息和登录时间戳
        localStorage.setItem('currentUser', JSON.stringify(userData))
        // localStorage.setItem('loginTimestamp', Date.now().toString())
      } catch (error) {
        console.error('将用户数据保存到本地存储失败:', error)
      }
    }
    return true
  }

  /**
   * 用户登出函数
   * 清理用户状态和本地存储的认证信息
   */
  const logout = async () => {
    try {
      const { code } = await logoutAPI()
      if (code) {
        // 清空当前用户状态
        currentUser.value = null
        // 清理本地存储的用户信息和登录时间戳
        localStorage.removeItem('currentUser')
        // localStorage.removeItem('loginTimestamp')
      }
    } catch (error) {
      console.error('注销失败:', error)
    }
  }

  /**
   * 提供认证上下文给子组件使用
   * 包含:
   * - currentUser: 当前用户状态
   * - loading: 加载状态
   * - login: 登录函数
   * - logout: 登出函数
   */
  provide(authKey, {
    currentUser,
    loading,
    login,
    logout
  })

  // 返回认证相关的状态和方法,供组件直接使用
  return {
    currentUser,
    loading,
    login,
    logout
  }
}

/**
 * 在子组件中使用认证功能的组合式函数
 * @returns {Object} 认证上下文对象,包含用户状态和认证方法
 * @throws {Error} 如果在provideAuth范围外使用则抛出错误
 */
export function useAuth() {
  // 注入认证上下文
  const auth = inject(authKey)
  // 确保在provideAuth的范围内使用
  if (!auth) {
    throw new Error('useAuth 必须在 provideAuth 的范围内使用')
  }
  return auth
}