Files
webgame-frontend/src/stores/modules/account.ts
2026-06-16 16:16:47 +08:00

170 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineStore } from 'pinia'
import { ref } from 'vue'
import {
login as loginApi,
register as registerApi,
refresh as refreshApi,
type LoginRequest,
type RegisterRequest
} from '@/api/modules/account'
import { getUserInfo } from '@/api/modules/user'
/** 账户信息 */
export interface AccountState {
/** 用户昵称 */
nickName?: string
/** 头像 */
avatar?: string | null
/** 个性签名 */
signature?: string | null
/** 年龄 */
age?: number | null
/** 性别1=男2=女 */
sex?: number | null
/** 生日 */
birthday?: string | null
}
export const useAccountStore = defineStore(
'account',
() => {
/** 账户信息 */
const accountInfo = ref<AccountState | null>(null)
/** Access Token30 分钟有效,每次请求通过 saToken 头携带) */
const accessToken = ref('')
/** Refresh Token30 天有效,仅用于刷新 Access Token */
const refreshToken = ref('')
/** token 是否已验证有效 */
const isTokenValid = ref(false)
/** 是否已完成初始验证(防止重复请求) */
const isInitialized = ref(false)
/**
* 判断当前是否已登录
* token 存在且已验证有效才算已登录
*/
const isLoggedIn = () => isTokenValid.value
/**
* 初始化认证状态
*
* 用持久化的 Access Token 向服务端验证是否仍然有效。
* 只会在首次调用时真正发请求,后续调用直接跳过。
*
* - 无 token → 无操作
* - token 有效 → 拉取用户信息,标记已登录
* - token 过期 → 尝试用 Refresh Token 刷新,刷新失败则清除
*/
const initAuth = async () => {
// 已验证过,跳过
if (isInitialized.value) return
isInitialized.value = true
// 没有持久化的 Access Token
if (!accessToken.value) return
try {
await fetchUserInfo()
// 成功获取用户信息token 有效
isTokenValid.value = true
} catch {
// Access Token 可能过期,尝试用 Refresh Token 刷新
if (refreshToken.value) {
try {
await doRefresh()
isTokenValid.value = true
} catch {
// 刷新也失败,清除
clearAuth()
}
} else {
clearAuth()
}
}
}
/**
* 用户登录
* 调用登录 API成功后存储双 Token 并获取用户信息
* @param params - 登录请求参数(用户名、密码)
*/
const login = async (params: LoginRequest) => {
const res = await loginApi(params)
accessToken.value = res.data.accessToken
refreshToken.value = res.data.refreshToken
await fetchUserInfo()
isTokenValid.value = true
}
/**
* 用户注册
* 调用注册 API成功后自动登录存储双 Token 并获取用户信息)
* @param params - 注册请求参数(用户名、密码)
*/
const register = async (params: RegisterRequest) => {
const res = await registerApi(params)
accessToken.value = res.data.accessToken
refreshToken.value = res.data.refreshToken
await fetchUserInfo()
isTokenValid.value = true
}
/**
* 退出登录
* 清除令牌、账户信息和验证状态
*/
const logout = () => {
clearAuth()
}
/**
* 获取当前登录用户信息
* 需要先有 Access Token 才能调用
*/
const fetchUserInfo = async () => {
const res = await getUserInfo()
accountInfo.value = res.data
}
/**
* 使用 Refresh Token 刷新 Access Token
* 成功后更新 accessTokenRefresh Token 不变(由服务端行为决定)
*/
const doRefresh = async () => {
const res = await refreshApi({ refreshToken: refreshToken.value })
accessToken.value = res.data.accessToken
// 如果服务端返回了新的 refreshToken 则更新
if (res.data.refreshToken) {
refreshToken.value = res.data.refreshToken
}
await fetchUserInfo()
}
/** 清除所有认证状态 */
const clearAuth = () => {
accessToken.value = ''
refreshToken.value = ''
accountInfo.value = null
isTokenValid.value = false
isInitialized.value = false
}
return {
accountInfo,
accessToken,
refreshToken,
isLoggedIn,
initAuth,
login,
register,
logout,
fetchUserInfo
}
},
{
persist: {
pick: ['accessToken', 'refreshToken']
}
}
)