From e2af9d1719ad0b82101fdd80b138916fb8de769a Mon Sep 17 00:00:00 2001 From: hwj Date: Thu, 2 Jul 2026 14:23:45 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A030=E5=88=86?= =?UTF-8?q?=E9=92=9Ftoken=E8=BF=87=E6=9C=9F=E5=90=8E=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=96=B0token=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manifest.json | 6 +- src/store/modules/user.ts | 4 +- src/utils/auth.ts | 26 ++++- src/utils/request.ts | 232 ++++++++++++++++++++++++++------------ 4 files changed, 190 insertions(+), 78 deletions(-) diff --git a/src/manifest.json b/src/manifest.json index 069ad36..e0e91a4 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -23,7 +23,8 @@ }, /* 模块配置 */ "modules" : { - "Camera" : {} + "Camera" : {}, + "Push" : {} }, /* 应用发布信息 */ "distribute" : { @@ -55,7 +56,8 @@ }, /* SDK配置 */ "sdkConfigs" : { - "ad" : {} + "ad" : {}, + "push" : {} }, "icons" : { "android" : { diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index fa8f1b2..9267fe6 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -39,7 +39,7 @@ const useUserStore = defineStore("user", { return new Promise((resolve, reject) => { login(username, password, code, uuid,tenantName, captchaVerification,rememberMe) .then((res: any) => { - setToken(res.data.accessToken); + setToken(res.data); this.token = res.data.accessToken; this.permissionInfoLoaded = false; clearPermissionInfoCache(); @@ -74,7 +74,7 @@ const useUserStore = defineStore("user", { initAllDict().catch(() => {}); resolve(res); }) - .catch((error) => { + .catch((error: any) => { this.permissionInfoLoaded = false; reject(error); }); diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 7a2d227..16ad71a 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,13 +1,31 @@ const TokenKey = 'App-Token' +const RefreshTokenKey = 'App-Refresh-Token' -export function getToken():string { +export function getToken(): string { return uni.getStorageSync(TokenKey) } -export function setToken(token:string) { - return uni.setStorageSync(TokenKey, token) +export function getRefreshToken(): string { + return uni.getStorageSync(RefreshTokenKey) +} + +interface TokenInfo { + accessToken: string + refreshToken?: string +} + +export function setToken(token: string | TokenInfo) { + if (typeof token === 'string') { + return uni.setStorageSync(TokenKey, token) + } + + uni.setStorageSync(TokenKey, token.accessToken) + if (token.refreshToken) { + uni.setStorageSync(RefreshTokenKey, token.refreshToken) + } } export function removeToken() { - return uni.removeStorageSync(TokenKey) + uni.removeStorageSync(TokenKey) + uni.removeStorageSync(RefreshTokenKey) } diff --git a/src/utils/request.ts b/src/utils/request.ts index 425503b..e527d6c 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -1,11 +1,18 @@ import config from '@/config' -import { getToken, removeToken } from '@/utils/auth' +import { getToken, getRefreshToken, removeToken, setToken } from '@/utils/auth' import errorCode from '@/utils/errorCode' -import { toast, showConfirm, tansParams } from '@/utils/common' +import { toast, tansParams } from '@/utils/common' import { RequestConfig, ResponseData } from '@/types/request' import modal from '@/plugins/modal' let timeout = 300000 +let isRefreshToken = false +let isRelogin = false +let requestList: Array<{ + config: RequestConfig + resolve: (value: ResponseData) => void + reject: (reason?: any) => void +}> = [] export const SERVER_BASE_URL_STORAGE_KEY = 'serverBaseUrl' @@ -13,92 +20,177 @@ export function getBaseUrl() { return uni.getStorageSync(SERVER_BASE_URL_STORAGE_KEY) || config.baseUrl } -const request = (config: RequestConfig): Promise> => { - // 是否需要设置 token - const isToken = (config.headers || {}).isToken === false - config.header = config.header || {} - config.header['tenantId'] = '1' +function prepareConfig(config: RequestConfig): RequestConfig { + const requestConfig: RequestConfig = { + ...config, + header: { + ...(config.header || {}) + } + } + + const isToken = (requestConfig.headers || {}).isToken === false + requestConfig.header!['tenantId'] = '1' if (getToken() && !isToken) { - config.header['Authorization'] = 'Bearer ' + getToken() + requestConfig.header!['Authorization'] = 'Bearer ' + getToken() } - // POST/PUT 请求明确设置 Content-Type 为 application/json - if ((config.method === 'POST' || config.method === 'PUT') && config.data) { - config.header['Content-Type'] = 'application/json' + + const method = (requestConfig.method || 'GET').toUpperCase() + if ((method === 'POST' || method === 'PUT') && requestConfig.data) { + requestConfig.header!['Content-Type'] = 'application/json' } - // get请求映射params参数 - if (config.params) { - let url = config.url + '?' + tansParams(config.params) + + if (requestConfig.params) { + let url = requestConfig.url + '?' + tansParams(requestConfig.params) url = url.slice(0, -1) - config.url = url + requestConfig.url = url } - return new Promise((resolve, reject) => { - const shouldShowLoading = config.showLoading !== false - if (shouldShowLoading) { - modal.loading(config.loadingText || '加载中') - } + + return requestConfig +} + +function handleRequestError(error: any) { + let { message } = error + if (message === 'Network Error') { + message = '后端接口连接异常' + } else if (message && message.includes('timeout')) { + message = '系统接口请求超时' + } else if (message && message.includes('Request failed with status code')) { + message = '系统接口' + message.substr(message.length - 3) + '异常' + } + toast(message) +} + +function handleAuthorized() { + if (!isRelogin) { + isRelogin = true + removeToken() + uni.reLaunch({ + url: '/pages/login', + complete: () => { + isRelogin = false + } + }) + } + return Promise.reject('无效的会话,或者会话已过期,请重新登录。') +} + +function refreshToken() { + const refreshTokenValue = getRefreshToken() + return new Promise((resolve, reject) => { uni.request({ - method: config.method || 'GET', - timeout: config.timeout || timeout, - url: (config.baseUrl || getBaseUrl()) + config.url, - data: config.data, - header: config.header, + method: 'POST', + timeout, + url: getBaseUrl() + '/admin-api/system/auth/refresh-token?refreshToken=' + encodeURIComponent(refreshTokenValue), + header: { + tenantId: '1' + }, dataType: 'json' }).then(response => { - /* let [error, res] = response - if (error) { - toast('后端接口连接异常') - reject('后端接口连接异常') - return - } */ - const res = response - const data: ResponseData = res.data as ResponseData + const data = (response as any).data const code = data.code || 200 - // @ts-ignore - const msg: string = errorCode[code] || data.msg || errorCode['default'] - if (code === 401) { - // showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => { - // if (res.confirm) { - // useUserStore().logOut().then(res => { - // uni.reLaunch({ url: '/pages/login' }) - // }) - // } - // }) - reject('无效的会话,或者会话已过期,请重新登录。') - removeToken() - uni.reLaunch({ url: '/pages/login' }) - return - } else if (code === 500) { - toast(msg) - // 将完整的 data 对象 reject,便于调用方拿到后端错误信息 - reject(data) - return - } else if (code !== 200) { - toast(msg) - reject(code) + if (code === 200 || code === 0) { + resolve(data.data) return } - resolve(data) + reject(data) }) .catch(error => { - let { message } = error - if (message === 'Network Error') { - message = '后端接口连接异常' - } else if (message.includes('timeout')) { - message = '系统接口请求超时' - } else if (message.includes('Request failed with status code')) { - message = '系统接口' + message.substr(message.length - 3) + '异常' - } - toast(message) reject(error) }) - .finally(() => { - if (shouldShowLoading) { - modal.closeLoading() - } - }) }) } +function retryRequestList() { + requestList.forEach(item => { + sendRequest(item.config).then(item.resolve).catch(item.reject) + }) + requestList = [] +} + +function rejectRequestList(error: any) { + requestList.forEach(item => { + item.reject(error) + }) + requestList = [] +} + +async function handleRefreshToken(config: RequestConfig): Promise> { + if (!getRefreshToken()) { + return handleAuthorized() + } + + if (!isRefreshToken) { + isRefreshToken = true + try { + const tokenData = await refreshToken() + setToken(tokenData) + isRefreshToken = false + retryRequestList() + return sendRequest(config) + } catch (error) { + rejectRequestList(error) + return handleAuthorized() + } finally { + isRefreshToken = false + } + } + + return new Promise((resolve, reject) => { + requestList.push({ + config, + resolve: resolve as (value: ResponseData) => void, + reject + }) + }) +} + +async function sendRequest(config: RequestConfig): Promise> { + const requestConfig = prepareConfig(config) + let response: any + + try { + response = await uni.request({ + method: (requestConfig.method || 'GET').toUpperCase() as any, + timeout: requestConfig.timeout || timeout, + url: (requestConfig.baseUrl || getBaseUrl()) + requestConfig.url, + data: requestConfig.data, + header: requestConfig.header, + dataType: 'json' + }) + } catch (error) { + handleRequestError(error) + throw error + } + + const data: ResponseData = response.data as ResponseData + const code = data.code || 200 + // @ts-ignore + const msg: string = errorCode[code] || data.msg || errorCode['default'] + if (code === 401) { + return handleRefreshToken(config) + } else if (code === 500) { + toast(msg) + throw data + } else if (code !== 200 && code !== 0) { + toast(msg) + throw code + } + return data +} + +const request = (config: RequestConfig): Promise> => { + const shouldShowLoading = config.showLoading !== false + if (shouldShowLoading) { + modal.loading(config.loadingText || '加载中...') + } + return sendRequest(config) + .finally(() => { + if (shouldShowLoading) { + modal.closeLoading() + } + }) +} + export function postAction(url: string, data?: any, isToken: boolean = true) { return request({ data, url, method: 'POST', headers: { isToken }, }) }