Files
PartsInquiry/frontend/common/http.js
2025-09-24 20:35:15 +08:00

127 lines
5.0 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.

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)
})
}