hejianhao
2026-08-28 16a57a8fc6868cea9171a1119698572407a2fc03
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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)
}