import { API_BASE_URL, API_BASE_URL_CANDIDATES, SHOP_ID, ENABLE_DEFAULT_USER, DEFAULT_USER_ID } from './config.js' function buildUrl(path) { if (!path) return API_BASE_URL if (path.startsWith('http')) return path return API_BASE_URL + (path.startsWith('/') ? path : '/' + path) } function parseJwtClaims(token) { try { const parts = String(token || '').split('.') if (parts.length < 2) return {} const payload = JSON.parse(decodeURIComponent(escape(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'))))) return payload || {} } catch (_) { return {} } } function buildAuthHeaders(base = {}) { const headers = { ...base } try { const token = typeof uni !== 'undefined' ? (uni.getStorageSync('TOKEN') || '') : '' if (token) { headers['Authorization'] = `Bearer ${token}` const claims = parseJwtClaims(token) if (claims && claims.shopId) headers['X-Shop-Id'] = claims.shopId if (claims && claims.userId) headers['X-User-Id'] = claims.userId } } catch (_) { /* noop: 未登录不注入任何店铺/用户头 */ } return headers } function requestWithFallback(options, candidates, idx, resolve, reject) { const base = candidates[idx] || API_BASE_URL let url = options.url if (!/^https?:\/\//.test(url)) { url = base + (url.startsWith('/') ? '' : '/') + url } else { url = options.url.replace(/^https?:\/\/[^/]+/, base) } uni.request({ ...options, url, dataType: 'json', success: (res) => { const { statusCode, data } = res if (statusCode >= 200 && statusCode < 300) return resolve(data) const msg = (data && (data.message || data.error || data.msg)) || ('HTTP ' + statusCode) uni.showToast({ title: msg, icon: 'none' }) // 仅在 5xx 或网络错误时尝试下一个候选地址;4xx 不重试。支持通过 __noRetry 禁止重试(用于登录接口避免40163)。 if (!options.__noRetry && statusCode >= 500 && idx + 1 < candidates.length) { return requestWithFallback(options, candidates, idx + 1, resolve, reject) } reject(new Error(msg)) }, fail: (err) => { if (!options.__noRetry && idx + 1 < candidates.length) return requestWithFallback(options, candidates, idx + 1, resolve, reject) reject(err) } }) } export function get(path, params = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({}) const options = { url: buildUrl(path), method: 'GET', data: params, header: headers } requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject) }) } export function post(path, body = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({ 'Content-Type': 'application/json' }) const options = { url: buildUrl(path), method: 'POST', data: body, header: headers } const p = String(path || '') if (p.includes('/api/auth/wxmp/login') || p.includes('/api/auth/sms/login') || p.includes('/api/auth/sms/send') || p.includes('/api/auth/password/login') || p.includes('/api/auth/register')) options.__noRetry = true requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject) }) } export function put(path, body = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({ 'Content-Type': 'application/json' }) const options = { url: buildUrl(path), method: 'PUT', data: body, header: headers } requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject) }) } export function del(path, body = {}) { return new Promise((resolve, reject) => { const headers = buildAuthHeaders({ 'Content-Type': 'application/json' }) const options = { url: buildUrl(path), method: 'DELETE', data: body, header: headers } requestWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject) }) } function uploadWithFallback(options, candidates, idx, resolve, reject) { const base = candidates[idx] || API_BASE_URL const url = options.url.replace(/^https?:\/\/[^/]+/, base) const uploadOptions = { ...options, url } uni.uploadFile({ ...uploadOptions, success: (res) => { const statusCode = res.statusCode || 0 if (statusCode >= 200 && statusCode < 300) { try { const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data return resolve(data) } catch (e) { return resolve(res.data) } } if (idx + 1 < candidates.length) return uploadWithFallback(options, candidates, idx + 1, resolve, reject) reject(new Error('HTTP ' + statusCode)) }, fail: (err) => { if (idx + 1 < candidates.length) return uploadWithFallback(options, candidates, idx + 1, resolve, reject) reject(err) } }) } // 文件上传封装:自动注入租户/用户头并进行多地址回退 export function upload(path, filePath, formData = {}, name = 'file') { return new Promise((resolve, reject) => { const header = buildAuthHeaders({}) const options = { url: buildUrl(path), filePath, name, formData, header } uploadWithFallback(options, API_BASE_URL_CANDIDATES, 0, resolve, reject) }) }