Files
MBTI_wang/miniprogram/utils/request.js
卡若 ce55c48c64 feat: 同步今日小程序与后台迭代版本
集中提交今日 API、管理端、小程序与部署文档调整,确保 Gitea 主分支与本地最新开发版本一致。

Made-with: Cursor
2026-04-20 11:07:55 +08:00

189 lines
4.9 KiB
JavaScript
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.

/**
* 统一请求封装:自动拼接 baseURL、携带 token、处理 401
* 401 时先尝试静默登录一次并重试,减少 token 过期/首屏竞态导致的误 401
*/
function getAppSafe() {
try {
return getApp()
} catch (e) {
return null
}
}
function getToken() {
const app = getAppSafe()
const g = app && app.globalData && app.globalData.token
if (g) return g
try {
const st = wx.getStorageSync('token')
if (st && app && app.globalData) {
app.globalData.token = st
}
return st || ''
} catch (e) {
return ''
}
}
function getApiBase() {
const app = getAppSafe()
return (app && app.globalData && app.globalData.apiBase) || ''
}
function clearLoginState() {
const app = getAppSafe()
if (app && app.globalData) {
app.globalData.token = null
app.globalData.userInfo = null
app.globalData.openId = null
}
try {
wx.removeStorageSync('token')
wx.removeStorageSync('userInfo')
} catch (e) {}
}
/**
* @param {Object} options - 同 wx.request
* @param {boolean} options.needAuth - 是否带 Authorization默认 true
* @param {boolean} options.optionalAuth
* @param {boolean} options.allow401 - 401 且已带 Bearer 时是否清登录态(重试失败后),默认 true
*/
function request(options) {
const apiBase = getApiBase()
const {
__didAuthRetry,
success: userSuccess,
fail: userFail,
complete: userComplete,
url: optUrl,
...rest
} = options
const fullUrl = optUrl.startsWith('http')
? optUrl
: `${apiBase.replace(/\/$/, '')}${optUrl.startsWith('/') ? '' : '/'}${optUrl}`
const needAuth = options.needAuth !== false
const optionalAuth = options.optionalAuth === true
const allow401 = options.allow401 !== false
const didRetry = __didAuthRetry === true
const header = {
'Content-Type': 'application/json',
...(options.header || {})
}
if (needAuth) {
const token = getToken()
if (token) header['Authorization'] = `Bearer ${token}`
} else if (optionalAuth) {
const token = getToken()
if (token) header['Authorization'] = `Bearer ${token}`
}
const authHeaderSent = !!header['Authorization']
wx.request({
...rest,
url: fullUrl,
header,
success(res) {
if (
res.statusCode === 401 &&
allow401 &&
authHeaderSent &&
needAuth &&
!didRetry
) {
const app = getAppSafe()
if (app && typeof app.silentLogin === 'function') {
app
.silentLogin()
.then((loginOk) => {
if (loginOk && getToken()) {
request({ ...options, __didAuthRetry: true })
} else {
clearLoginState()
if (userSuccess) userSuccess(res)
}
})
.catch(() => {
clearLoginState()
if (userSuccess) userSuccess(res)
})
return
}
}
if (res.statusCode === 401 && allow401 && authHeaderSent) {
clearLoginState()
}
if (userSuccess) userSuccess(res)
},
fail(err) {
if (userFail) userFail(err)
},
complete(res) {
if (userComplete) userComplete(res)
}
})
}
function requestPromiseOnce(options) {
return new Promise((resolve, reject) => {
request({
timeout: options.timeout || 15000,
...options,
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res)
} else {
let msg = '请求失败'
const d = res.data
if (d && typeof d === 'object' && d.message) msg = d.message
else if (typeof d === 'string') {
try {
const o = JSON.parse(d)
if (o && o.message) msg = o.message
} catch (e) {
if (res.statusCode === 401) msg = '未登录或登录已过期'
}
}
const err = new Error(msg)
err.statusCode = res.statusCode
reject(err)
}
},
fail(err) {
const e = new Error((err && err.errMsg) || '网络异常')
e.isNetworkError = true
reject(e)
}
})
})
}
function requestPromise(options) {
const retry = options.retry == null ? 2 : Number(options.retry) || 0
const retryDelayMs = options.retryDelayMs == null ? 400 : Number(options.retryDelayMs) || 0
let attempt = 0
const run = () =>
requestPromiseOnce(options).catch((err) => {
const retriable = err && (err.isNetworkError || (err.statusCode >= 500 && err.statusCode < 600))
if (!retriable || attempt >= retry) {
return Promise.reject(err)
}
attempt += 1
const wait = retryDelayMs * Math.pow(2, attempt - 1)
return new Promise((r) => setTimeout(r, wait)).then(run)
})
return run()
}
module.exports = {
request,
requestPromise,
requestPromiseOnce,
getToken,
getApiBase,
clearLoginState
}