/** * 用户状态管理 Store * * 后端响应格式说明: * 所有API响应都被包装在标准格式中: * { * "success": true, * "data": { ... }, // 真正的数据在这里 * "message": "操作成功", * "requestId": "", * "timestamp": 1752469413 * } * * 使用示例: * * // 在组件中使用 * import { useUserStore } from '@/stores/user' * * const userStore = useUserStore() * * // 登录 * const loginData = { * method: 'sms', // 或 'password' * phone: '13800138000', * code: '123456', // 短信登录时需要 * password: 'password123' // 密码登录时需要 * } * const result = await userStore.login(loginData) * if (result.success) { * console.log('登录成功') * // result.data 包含: { user, access_token, token_type, expires_in, login_method } * } * * // 注册 * const registerData = { * phone: '13800138000', * password: 'password123', * confirmPassword: 'password123', * code: '123456' * } * const result = await userStore.register(registerData) * if (result.success) { * // result.data 包含注册成功的数据 * } * * // 发送验证码 * const result = await userStore.sendCode('13800138000', 'register') * if (result.success) { * // result.data 包含验证码发送结果 * } * * // 重置密码 * const resetData = { * phone: '13800138000', * newPassword: 'newpassword123', * confirmNewPassword: 'newpassword123', * code: '123456' * } * const result = await userStore.resetPassword(resetData) * if (result.success) { * // result.data 包含重置密码结果 * } * * // 获取用户信息 * const result = await userStore.fetchUserProfile() * if (result.success) { * // result.data 包含用户信息: { id, phone, created_at, updated_at } * } * * // 修改密码 * const passwordData = { * oldPassword: 'oldpassword123', * newPassword: 'newpassword123', * confirmNewPassword: 'newpassword123', * code: '123456' * } * const result = await userStore.changePassword(passwordData) * if (result.success) { * // result.data 包含修改密码结果 * } * * // 登出 * userStore.logout() * * // 检查登录状态 * const isAuth = await userStore.checkAuth() * * // 响应式状态 * console.log(userStore.isLoggedIn) // 是否已登录 * console.log(userStore.userInfo) // 用户信息: { id, phone, created_at, updated_at } * console.log(userStore.loading) // 加载状态 */ import { userApi } from '@/api' import router from '@/router' import { authEventBus } from '@/utils/request' import { generateSMSRequest } from '@/utils/smsSignature' import { clearLocalVersions, saveLocalVersions, VERSION_CONFIG, versionChecker } from '@/utils/version' import { ElMessage } from 'element-plus' export const useUserStore = defineStore('user', () => { // 状态 const user = ref(null) const accessToken = ref(localStorage.getItem('access_token') || '') const tokenType = ref(localStorage.getItem('token_type') || 'Bearer') const isAuthenticated = ref(false) const loading = ref(false) // 新增:初始化标志 const initialized = ref(false) // 版本管理 const tokenVersion = ref(localStorage.getItem('token_version') || VERSION_CONFIG.TOKEN_VERSION) const appVersion = ref(localStorage.getItem('app_version') || VERSION_CONFIG.APP_VERSION) // 计算属性 const isLoggedIn = computed(() => { return isAuthenticated.value && !!accessToken.value }) const userInfo = computed(() => user.value) // 新增:用户类型相关计算属性 const isAdmin = computed(() => { return user.value?.user_type === 'admin' }) const userType = computed(() => { return user.value?.user_type || 'user' }) // 新增:用户认证状态计算属性 const isCertified = computed(() => { return user.value?.is_certified || false }) // 检查用户信息是否完整 const isUserInfoComplete = computed(() => { return user.value && user.value.id && user.value.phone && user.value.user_type !== undefined }) // 强制刷新用户信息 const refreshUserProfile = async () => { if (!accessToken.value) { return { success: false, error: '未登录' } } loading.value = true try { const result = await fetchUserProfile() return result } catch (error) { return { success: false, error } } finally { loading.value = false } } const hasRole = (role) => { return userType.value === role } // 监听认证错误事件 const handleAuthError = (message) => { console.log('用户store收到认证错误事件:', message) logout() } // 处理版本退出登录事件 const handleVersionLogout = (event) => { console.log('收到版本退出登录事件:', event.detail) logout() ElMessage.error('系统已更新,请重新登录') router.push('/auth/login') } // 处理版本刷新事件 const handleVersionRefresh = (event) => { console.log('收到版本刷新事件:', event.detail) saveLocalVersions({ appVersion: VERSION_CONFIG.APP_VERSION }) ElMessage.success('应用已更新,正在刷新页面...') setTimeout(() => { window.location.reload() }, 1500) } // 版本检查 const checkVersions = () => { const currentTokenVersion = localStorage.getItem('token_version') || VERSION_CONFIG.TOKEN_VERSION const currentAppVersion = localStorage.getItem('app_version') || VERSION_CONFIG.APP_VERSION // 检查token版本 if (currentTokenVersion !== VERSION_CONFIG.TOKEN_VERSION) { console.warn('Token版本不匹配,需要重新登录', { current: currentTokenVersion, expected: VERSION_CONFIG.TOKEN_VERSION }) // 清除所有认证相关数据 logout() // 显示提示信息 ElMessage.error('系统已更新,请重新登录') // 跳转到登录页面 router.push('/auth/login') return false } // 检查应用版本 if (currentAppVersion !== VERSION_CONFIG.APP_VERSION) { console.log('应用版本已更新,刷新页面', { current: currentAppVersion, expected: VERSION_CONFIG.APP_VERSION }) // 更新本地存储的应用版本 saveLocalVersions({ appVersion: VERSION_CONFIG.APP_VERSION }) // 显示提示信息 ElMessage.success('应用已更新,正在刷新页面...') // 延迟刷新页面 setTimeout(() => { window.location.reload() }, 1500) return false } return true } // 登录 const login = async (loginData) => { loading.value = true try { let response if (loginData.method === 'sms') { response = await userApi.loginWithSMS({ phone: loginData.phone, code: loginData.code }) } else { response = await userApi.loginWithPassword({ phone: loginData.phone, password: loginData.password }) } // 后端返回格式: { success: true, data: { user, access_token, token_type, ... }, message, ... } // 真正的数据在 response.data 中 const responseData = response.data // 保存token和用户信息 accessToken.value = responseData.access_token tokenType.value = responseData.token_type user.value = responseData.user isAuthenticated.value = true // 保存到localStorage localStorage.setItem('access_token', responseData.access_token) localStorage.setItem('token_type', responseData.token_type) saveLocalVersions({ tokenVersion: VERSION_CONFIG.TOKEN_VERSION }) // 登录成功后,主动获取最新的用户信息以确保数据完整 try { const profileResult = await fetchUserProfile() if (profileResult.success) { console.log('登录后用户信息更新成功') // 如果获取到更完整的用户信息,更新用户数据 if (profileResult.data) { user.value = { ...user.value, ...profileResult.data } } } } catch (profileError) { console.warn('登录后获取用户信息失败:', profileError) // 即使获取用户信息失败,也不影响登录流程 } return { success: true, data: responseData } } catch (error) { console.error('登录失败:', error) return { success: false, error } } finally { loading.value = false } } // 注册 const register = async (registerData) => { loading.value = true try { const response = await userApi.register({ phone: registerData.phone, password: registerData.password, confirm_password: registerData.confirmPassword, code: registerData.code }) // 后端返回格式: { success: true, data: {...}, message, ... } return { success: true, data: response.data } } catch (error) { return { success: false, error } } finally { loading.value = false } } // 重置密码 const resetPassword = async (resetData) => { loading.value = true try { const response = await userApi.resetPassword({ phone: resetData.phone, new_password: resetData.newPassword, confirm_new_password: resetData.confirmNewPassword, code: resetData.code }) // 后端返回格式: { success: true, data: {...}, message, ... } return { success: true, data: response.data } } catch (error) { return { success: false, error } } finally { loading.value = false } } // 发送验证码(使用自定义编码和签名) const sendCode = async (phone, scene) => { try { // 1. 生成签名并编码请求数据 const encodedRequest = await generateSMSRequest(phone, scene) // 2. 发送编码后的请求(只包含data字段) const response = await userApi.sendCode(encodedRequest) // 后端返回格式: { success: true, data: {...}, message, ... } return { success: true, data: response.data } } catch (error) { console.error('发送验证码失败:', error) return { success: false, error } } } // 获取用户信息 const fetchUserProfile = async () => { loading.value = true try { const response = await userApi.getProfile() // 后端返回格式: { success: true, data: {...}, message, ... } const userData = response.data user.value = userData isAuthenticated.value = true // 记录认证状态变化 if (userData.is_certified !== undefined) { console.log('用户认证状态:', userData.is_certified ? '已认证' : '未认证') } return { success: true, data: userData } } catch (error) { return { success: false, error } } finally { loading.value = false } } // 修改密码 const changePassword = async (passwordData) => { loading.value = true try { const response = await userApi.changePassword({ old_password: passwordData.oldPassword, new_password: passwordData.newPassword, confirm_new_password: passwordData.confirmNewPassword, code: passwordData.code }) // 后端返回格式: { success: true, data: {...}, message, ... } return { success: true, data: response.data } } catch (error) { return { success: false, error } } finally { loading.value = false } } // 登出 const logout = () => { // 清除状态 user.value = null accessToken.value = '' tokenType.value = 'Bearer' isAuthenticated.value = false loading.value = false // 清除localStorage localStorage.removeItem('access_token') localStorage.removeItem('token_type') clearLocalVersions() } // 检查登录状态 const checkAuth = async () => { if (!accessToken.value) { return false } try { const result = await fetchUserProfile() return result.success } catch (error) { logout() return false } } // 初始化标志,防止重复初始化 let isInitializing = false // 初始化 const init = async () => { // 如果已经初始化完成,直接返回 if (initialized.value) { return } // 如果正在初始化,等待完成 if (isInitializing) { // 等待初始化完成 while (isInitializing) { await new Promise(resolve => setTimeout(resolve, 50)) } return } isInitializing = true try { // 监听认证错误事件(只注册一次) if (!authEventBus.listeners.includes(handleAuthError)) { authEventBus.onAuthError(handleAuthError) } // 监听版本更新事件(只注册一次) if (!window.hasVersionListeners) { window.addEventListener('version:logout', handleVersionLogout) window.addEventListener('version:refresh', handleVersionRefresh) window.hasVersionListeners = true } // 进行版本检查 if (!checkVersions()) { initialized.value = true return } if (accessToken.value && !user.value) { // 有token但无用户信息,自动拉取 loading.value = true try { const result = await fetchUserProfile() isAuthenticated.value = result.success // 如果认证成功,启动版本检查器 if (result.success) { versionChecker.startAutoCheck() } } catch { isAuthenticated.value = false logout() } finally { loading.value = false initialized.value = true } } else { // 如果已经认证,启动版本检查器 if (isAuthenticated.value) { versionChecker.startAutoCheck() } initialized.value = true } } finally { isInitializing = false } } // 清理 const cleanup = () => { // 停止版本检查器 versionChecker.stopAutoCheck() // 移除事件监听器 window.removeEventListener('version:logout', handleVersionLogout) window.removeEventListener('version:refresh', handleVersionRefresh) } return { // 状态 user, accessToken, tokenType, isAuthenticated, loading, initialized, // 新增 // 计算属性 isLoggedIn, userInfo, isAdmin, userType, isCertified, // 新增 isUserInfoComplete, // 新增 hasRole, // 方法 login, register, resetPassword, sendCode, fetchUserProfile, changePassword, logout, checkAuth, init, cleanup, refreshUserProfile // 新增 } })