consumer-app/api/index.js

320 lines
8.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 接口请求封装
* 统一处理请求拦截、响应拦截、错误处理、token管理等
*/
// 基础URL配置注意末尾不要加斜杠
const BASE_URL = 'https://siji.chenjuncn.top'
// 是否正在刷新token防止并发刷新
let isRefreshing = false
// 等待刷新完成的请求队列
let refreshSubscribers = []
/**
* 刷新token
* @param {String} refreshToken 刷新令牌
* @returns {Promise} 返回刷新结果
*/
function refreshAccessToken(refreshToken) {
if (isRefreshing) {
// 如果正在刷新返回一个Promise等待刷新完成
return new Promise((resolve, reject) => {
refreshSubscribers.push({ resolve, reject })
})
}
isRefreshing = true
return new Promise((resolve, reject) => {
// 构建完整URL
const fullUrl = `${BASE_URL}/app-api/member/auth/refresh`
// 构建请求头
const requestHeader = {
'Content-Type': 'application/json',
'tenant-id': '1'
}
// 发起刷新请求
uni.request({
url: fullUrl,
method: 'POST',
data: { refreshToken },
header: requestHeader,
success: (res) => {
if (res.statusCode === 200 && res.data) {
// 处理响应数据
const responseData = res.data
if (responseData.code === 0 || responseData.code === 200) {
const data = responseData.data || responseData
// 保存新的token
const token = data?.accessToken || data?.token || data?.access_token
if (token) {
uni.setStorageSync('token', token)
}
// 保存新的refreshToken如果有
const newRefreshToken = data?.refreshToken
if (newRefreshToken) {
uni.setStorageSync('refreshToken', newRefreshToken)
}
// 保存新的过期时间
const expiresTime = data?.expiresTime
if (expiresTime) {
uni.setStorageSync('tokenExpiresTime', expiresTime)
}
// 通知所有等待的请求
refreshSubscribers.forEach(subscriber => subscriber.resolve())
refreshSubscribers = []
isRefreshing = false
resolve(data)
} else {
// 刷新失败
const errorMsg = responseData.message || responseData.msg || '刷新token失败'
refreshSubscribers.forEach(subscriber => subscriber.reject(new Error(errorMsg)))
refreshSubscribers = []
isRefreshing = false
reject(new Error(errorMsg))
}
} else {
// 刷新失败
refreshSubscribers.forEach(subscriber => subscriber.reject(new Error('刷新token失败')))
refreshSubscribers = []
isRefreshing = false
reject(new Error('刷新token失败'))
}
},
fail: (err) => {
// 刷新失败
refreshSubscribers.forEach(subscriber => subscriber.reject(err))
refreshSubscribers = []
isRefreshing = false
reject(err)
}
})
})
}
/**
* 统一请求方法
* @param {Object} options 请求配置
* @param {String} options.url 请求地址相对路径会自动拼接BASE_URL
* @param {String} options.method 请求方法默认GET
* @param {Object} options.data 请求参数
* @param {Object} options.header 请求头
* @param {Boolean} options.showLoading 是否显示loading默认false
* @param {Boolean} options.needAuth 是否需要token认证默认true
* @returns {Promise} 返回处理后的响应数据
*/
export function request(options = {}) {
return new Promise((resolve, reject) => {
const {
url,
method = 'GET',
data = {},
header = {},
showLoading = false,
needAuth = true
} = options
// 显示loading
if (showLoading) {
uni.showLoading({
title: '加载中...',
mask: true
})
}
// 构建完整URL处理BASE_URL末尾斜杠问题
const fullUrl = url.startsWith('http') ? url : `${BASE_URL}${url.startsWith('/') ? url : '/' + url}`
// 构建请求头
const requestHeader = {
'Content-Type': 'application/json',
'tenant-id': '1', // 统一添加租户ID
...header
}
// 添加token如果需要认证
if (needAuth) {
const token = uni.getStorageSync('token')
if (token) {
requestHeader['Authorization'] = `Bearer ${token}`
}
}
// 发起请求
uni.request({
url: fullUrl,
method: method.toUpperCase(),
data: data,
header: requestHeader,
success: (res) => {
// 隐藏loading
if (showLoading) {
uni.hideLoading()
}
// 统一处理响应
if (res.statusCode === 200) {
// 根据实际后端返回的数据结构处理
// 如果后端返回格式为 { code: 200, data: {}, message: '' }
if (res.data && typeof res.data === 'object') {
// 如果后端有统一的code字段
if (res.data.code !== undefined) {
// 处理业务错误码401账号未登录
if (res.data.code === 401) {
// 显示登录弹窗
uni.showModal({
title: '提示',
content: res.data.msg || res.data.message || '账号未登录,请前往登录',
showCancel: false,
confirmText: '去登录',
success: (modalRes) => {
if (modalRes.confirm) {
// 清除本地存储的登录信息
uni.removeStorageSync('token')
uni.removeStorageSync('refreshToken')
uni.removeStorageSync('tokenExpiresTime')
uni.removeStorageSync('userId')
uni.removeStorageSync('userInfo')
// 跳转到登录页面
uni.navigateTo({
url: '/pages/login/login'
})
}
}
})
reject(new Error('未授权'))
return
}
if (res.data.code === 200 || res.data.code === 0) {
resolve(res.data.data || res.data)
} else {
// 业务错误
const errorMsg = res.data.message || res.data.msg || '请求失败'
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
})
reject(new Error(errorMsg))
}
} else {
// 没有code字段直接返回data
resolve(res.data)
}
} else {
resolve(res.data)
}
} else if (res.statusCode === 401) {
// token过期或未登录尝试使用refreshToken刷新
const refreshToken = uni.getStorageSync('refreshToken')
if (refreshToken) {
// 尝试刷新token
refreshAccessToken(refreshToken)
.then(() => {
// 刷新成功,重新发起原请求
return request(options)
})
.then(resolve)
.catch(() => {
// 刷新失败,显示登录弹窗
const errorMsg = res.data?.msg || res.data?.message || '账号未登录,请前往登录'
uni.showModal({
title: '提示',
content: errorMsg,
showCancel: false,
confirmText: '去登录',
success: (modalRes) => {
if (modalRes.confirm) {
// 清除token跳转到登录页
uni.removeStorageSync('token')
uni.removeStorageSync('refreshToken')
uni.removeStorageSync('tokenExpiresTime')
uni.removeStorageSync('userId')
uni.removeStorageSync('userInfo')
uni.navigateTo({
url: '/pages/login/login'
})
}
}
})
reject(new Error('未授权'))
})
} else {
// 没有refreshToken显示登录弹窗
const errorMsg = res.data?.msg || res.data?.message || '账号未登录,请前往登录'
uni.showModal({
title: '提示',
content: errorMsg,
showCancel: false,
confirmText: '去登录',
success: (modalRes) => {
if (modalRes.confirm) {
// 清除token跳转到登录页
uni.removeStorageSync('token')
uni.removeStorageSync('refreshToken')
uni.removeStorageSync('tokenExpiresTime')
uni.removeStorageSync('userId')
uni.removeStorageSync('userInfo')
uni.navigateTo({
url: '/pages/login/login'
})
}
}
})
reject(new Error('未授权'))
}
} else if (res.statusCode >= 500) {
// 服务器错误
uni.showToast({
title: '服务器错误,请稍后重试',
icon: 'none'
})
reject(new Error('服务器错误'))
} else {
// 其他错误
const errorMsg = res.data?.message || res.data?.msg || `请求失败(${res.statusCode})`
uni.showToast({
title: errorMsg,
icon: 'none'
})
reject(new Error(errorMsg))
}
},
fail: (err) => {
// 隐藏loading
if (showLoading) {
uni.hideLoading()
}
// 网络错误处理
let errorMsg = '网络请求失败'
if (err.errMsg) {
if (err.errMsg.includes('timeout')) {
errorMsg = '请求超时,请检查网络'
} else if (err.errMsg.includes('fail')) {
errorMsg = '网络连接失败,请检查网络设置'
}
}
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
})
reject(err)
}
})
})
}