import { config } from '../config.js' import { clearLoginSession, getAuthToken } from './app-state' import { getCurrentLanguage } from './i18n' const API_BASE_URL = config.baseURL const FILE_BASE_URL = config.filePrefix const FIXED_PROVIDER_TYPE = 'WechatMiniProgram' function getRequestText(key = '') { const language = getCurrentLanguage() const isEn = language === 'en' const map = { requestFailed: isEn ? 'Request failed' : '请求失败', loginExpired: isEn ? 'Login expired. Please sign in again' : '登录已过期,请重新登录', tooManyRequests: isEn ? 'Too many requests. Please try again later' : '操作太频繁了,请稍后再试', networkFailed: isEn ? 'Network request failed' : '网络请求失败' } return map[key] || '' } function normalizeUrl(url = '') { if (/^https?:\/\//i.test(url)) { return url } if (!url) { return API_BASE_URL } if (url.startsWith('/')) { return `${API_BASE_URL}${url}` } return `${API_BASE_URL}/${url}` } function isPlainRequestObject(value) { return !!value && typeof value === 'object' && !Array.isArray(value) } function appendFixedProviderType(payload) { if (isPlainRequestObject(payload)) { return { ...payload, ProviderType: FIXED_PROVIDER_TYPE } } return { ProviderType: FIXED_PROVIDER_TYPE } } function hasBusinessCode(payload) { return !!payload && typeof payload === 'object' && Object.prototype.hasOwnProperty.call(payload, 'code') } function isBusinessSuccess(payload) { if (!hasBusinessCode(payload)) { return true } const code = String(payload.code) return code === '200' || code === '0' } function normalizeBusinessError(payload, fallback = {}) { const message = (payload && (payload.msg || payload.message)) || fallback.errMsg || getRequestText('requestFailed') return { ...fallback, ...(payload && typeof payload === 'object' ? payload : {}), message } } function getCurrentRoute() { const pages = getCurrentPages() if (!Array.isArray(pages) || !pages.length) { return '' } const currentPage = pages[pages.length - 1] || {} return currentPage.route || '' } function shouldSkipUnauthorizedRedirect() { const route = getCurrentRoute() return route === 'pkgAccount/login/login' || route === 'pages/login/index' || route === 'pages/login/login' } function normalizeRequestedLanguageHeaderValue(language) { return String(language || getCurrentLanguage() || 'zh').trim().toLowerCase() === 'en' ? 'en' : 'zh' } function buildRequestHeader(header = {}, token = '') { const normalizedHeader = { ...(header || {}) } const requestedLanguageHeaderKey = Object.keys(normalizedHeader).find(key => String(key || '').toLowerCase() === 'x-requested-language') if (requestedLanguageHeaderKey && requestedLanguageHeaderKey !== 'X-Requested-Language') { delete normalizedHeader[requestedLanguageHeaderKey] } normalizedHeader['X-Requested-Language'] = normalizeRequestedLanguageHeaderValue( requestedLanguageHeaderKey ? normalizedHeader[requestedLanguageHeaderKey] : normalizedHeader['X-Requested-Language'] ) return { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), ...normalizedHeader } } function handleUnauthorized(responseData) { clearLoginSession() if (shouldSkipUnauthorizedRedirect()) { return } uni.showToast({ title: getRequestText('loginExpired'), icon: 'none', duration: 2000 }) setTimeout(() => { uni.reLaunch({ url: '/pkgAccount/login/login' }) }, 500) } export function request(options = {}) { const { url = '', method = 'GET', data, params, header = {}, hideErrorToast = false, withAuth = true } = options return new Promise((resolve, reject) => { const token = withAuth ? getAuthToken() : '' const uppercaseMethod = String(method || 'GET').toUpperCase() const normalizedParams = appendFixedProviderType(params) const normalizedData = appendFixedProviderType(data) uni.request({ url: normalizeUrl(url), method: uppercaseMethod, data: uppercaseMethod === 'GET' ? (params !== undefined ? normalizedParams : normalizedData) : normalizedData, header: buildRequestHeader(header, token), success: (res) => { const { statusCode, data: responseData } = res if ((statusCode === 401) || (responseData && Number(responseData.code) === 401)) { handleUnauthorized(responseData) reject(normalizeBusinessError(responseData, res)) return } if (statusCode === 429) { if (!hideErrorToast) { uni.showToast({ title: getRequestText('tooManyRequests'), icon: 'none', duration: 2500 }) } reject({ ...normalizeBusinessError(responseData, res), message: getRequestText('tooManyRequests'), __toastShown: !hideErrorToast }) return } if (statusCode >= 200 && statusCode < 300) { if (isBusinessSuccess(responseData)) { resolve(responseData) return } const businessError = normalizeBusinessError(responseData, res) if (!hideErrorToast) { uni.showToast({ title: businessError.message || getRequestText('requestFailed'), icon: 'none', duration: 2000 }) } reject({ ...businessError, __toastShown: !hideErrorToast }) return } const requestError = normalizeBusinessError(responseData, res) if (!hideErrorToast) { uni.showToast({ title: requestError.message || getRequestText('requestFailed'), icon: 'none' }) } reject({ ...requestError, __toastShown: !hideErrorToast }) }, fail: (error) => { const requestError = normalizeBusinessError(null, error) if (!hideErrorToast) { uni.showToast({ title: requestError.message || getRequestText('networkFailed'), icon: 'none' }) } reject({ ...requestError, __toastShown: !hideErrorToast }) } }) }) } export { API_BASE_URL, FILE_BASE_URL } export default request