const TOKEN_KEY = 'token' const USER_KEY = 'appUserInfo' const TOKEN_EXPIRE_AT_KEY = 'tokenExpireAt' const LOGOUT_MODAL_KEY = 'showLogoutModal' const REDIRECT_META_KEY = 'loginRedirectMeta' const PENDING_PHONE_BIND_KEY = 'pendingPhoneBind' /** * 读取当前缓存中的 APP token。 */ export function getStoredToken() { const token = uni.getStorageSync(TOKEN_KEY) return typeof token === 'string' ? token.trim() : '' } /** * 判断当前是否存在本地登录态。 */ export function hasStoredSession() { return !!getStoredToken() } /** * 判断当前是否存在完整 APP 登录态。 */ export function hasCompletedAuthSession() { return hasStoredSession() && hasBoundPhone() && !hasPendingPhoneBindSession() } /** * 读取当前缓存中的 APP 用户信息。 */ export function getStoredAppUser() { const user = uni.getStorageSync(USER_KEY) return user && typeof user === 'object' ? user : {} } /** * 判断当前缓存用户是否已完成手机号绑定。 */ export function hasBoundPhone() { const user = getStoredAppUser() return typeof user.phone === 'string' && user.phone.trim() !== '' } /** * 判断当前缓存是否只是待绑定手机号的第三方临时会话。 */ export function hasPendingPhoneBindSession() { return uni.getStorageSync(PENDING_PHONE_BIND_KEY) === 1 } /** * 读取完整登录态可用的鉴权头;临时绑定会话不对普通业务接口暴露 token。 */ export function getCompletedAuthHeader() { if (!hasCompletedAuthSession()) { return '' } return getStoredToken() } /** * 从登录响应中持久化 APP 登录态。 */ export function saveAuthSession(loginData, extras = {}) { if (!loginData || typeof loginData !== 'object') { return } persistSession(loginData, extras) clearPendingBindSession() } /** * 保存仅用于绑定手机号流程的第三方临时会话。 */ export function savePendingBindSession(loginData, extras = {}) { if (!loginData || typeof loginData !== 'object') { return } persistSession(loginData, extras) uni.setStorageSync(PENDING_PHONE_BIND_KEY, 1) } /** * 清除待绑定手机号临时会话标记。 */ export function clearPendingBindSession() { uni.removeStorageSync(PENDING_PHONE_BIND_KEY) } /** * 持久化 APP 会话公共字段。 */ function persistSession(loginData, extras = {}) { const tokenValue = typeof loginData.token === 'string' ? loginData.token.trim() : '' if (tokenValue) { const tokenType = typeof loginData.tokenType === 'string' && loginData.tokenType.trim() ? loginData.tokenType.trim() : 'Bearer' uni.setStorageSync(TOKEN_KEY, `${tokenType} ${tokenValue}`.trim()) } const userInfo = loginData.user && typeof loginData.user === 'object' ? loginData.user : {} uni.setStorageSync(USER_KEY, { ...userInfo, ...extras.userOverrides, }) uni.setStorageSync(LOGOUT_MODAL_KEY, 1) persistTokenExpireAt(loginData.expiresIn) if (extras.redirectMeta) { uni.setStorageSync(REDIRECT_META_KEY, extras.redirectMeta) } } /** * 根据 expiresIn 持久化 token 预计过期时间戳。 */ export function persistTokenExpireAt(expiresIn) { const expireSeconds = Number(expiresIn) // 后端 APP 永久 token 返回 -1,此时不保存过期时间,后续请求也不会触发定时刷新。 if (!Number.isFinite(expireSeconds) || expireSeconds <= 0) { uni.removeStorageSync(TOKEN_EXPIRE_AT_KEY) return } uni.setStorageSync(TOKEN_EXPIRE_AT_KEY, Date.now() + expireSeconds * 1000) } /** * 读取当前 token 过期时间戳。 */ export function getTokenExpireAt() { const rawValue = Number(uni.getStorageSync(TOKEN_EXPIRE_AT_KEY) || 0) return Number.isFinite(rawValue) && rawValue > 0 ? rawValue : 0 } /** * 判断当前 token 是否即将过期。 */ export function willTokenExpireWithin(bufferMs = 0) { const expireAt = getTokenExpireAt() if (!expireAt) { return false } return expireAt - Date.now() <= bufferMs } /** * 清理本地 APP 登录态缓存。 */ export function clearAuthSession() { uni.removeStorageSync(TOKEN_KEY) uni.removeStorageSync(USER_KEY) uni.removeStorageSync(TOKEN_EXPIRE_AT_KEY) uni.removeStorageSync(REDIRECT_META_KEY) uni.removeStorageSync(PENDING_PHONE_BIND_KEY) uni.setStorageSync(LOGOUT_MODAL_KEY, 0) }