/** * 统一的 HTTP 请求封装 * 自动添加 tenantId、token 等公共参数 */ import { getApiUrl, getApiEnv } from './config.js' /** * 获取 tenantId(从本地存储) */ function getTenantId() { try { return uni.getStorageSync('backend-tenant-id') || '' } catch (e) { console.error('获取 tenantId 失败:', e) return '' } } /** * 跳转到登录页面 */ function redirectToLogin() { const loginPage = '/uni_modules/uni-id-pages/pages/login/login-withpwd' const pages = getCurrentPages() // 如果当前已经在登录页,不重复跳转 if (pages.length > 0) { const currentPage = pages[pages.length - 1] if (currentPage.route && currentPage.route.includes('login')) { return } } uni.reLaunch({ url: loginPage, fail: (err) => { console.error('跳转登录页失败:', err) } }) } /** * 检查是否为登录相关接口(不需要租户ID的接口) */ function isLoginApi(url) { if (!url) return false // 登录接口不需要租户ID return url.includes('/api/sys/auth/login') } /** * 获取 token(从本地存储) */ function getToken() { try { return uni.getStorageSync('backend-token') || '' } catch (e) { console.error('获取 token 失败:', e) return '' } } /** * 获取 roleName(从本地存储) */ function getRoleName() { try { return uni.getStorageSync('backend-role-name') || '' } catch (e) { console.error('获取 roleName 失败:', e) return '' } } /** * 获取 scenario(从本地存储) */ function getScenario() { try { return uni.getStorageSync('backend-scenario') || '' } catch (e) { console.error('获取 scenario 失败:', e) return '' } } /** * 统一的请求方法 * @param {Object} options - uni.request 的配置选项 * @param {string} options.url - 请求地址(会自动使用 getApiUrl 处理) * @param {string} options.method - 请求方法,默认 'GET' * @param {Object} options.data - 请求数据 * @param {Object} options.header - 请求头(会自动合并公共 header) * @param {number} options.timeout - 超时时间 * @param {boolean} options.needTenantId - 是否需要添加 tenantId,默认 true * @param {boolean} options.needToken - 是否需要添加 token,默认 true * @returns {Promise} 请求结果 */ export function request(options = {}) { const { url, method = 'GET', data = {}, header = {}, timeout = 10000, needTenantId = true, needToken = true, ...restOptions } = options // 处理 URL(自动添加 baseUrl) const fullUrl = url.startsWith('http') ? url : getApiUrl(url) const apiEnv = typeof getApiEnv === 'function' ? getApiEnv() : '' // 详细的请求日志 console.log('[Request] 开始处理请求:', { originalUrl: url, fullUrl: fullUrl, method: method.toUpperCase(), isLoginApi: isLoginApi(fullUrl), apiEnv: apiEnv, needTenantId, needToken }) // 每次请求打印完整 URL,格式示例: // H5环境(local)直接使用完整URL: http://localhost:8091/api/audioManagement/list console.log(`[Request] 完整请求URL: ${fullUrl}`) // 构建请求头 const requestHeader = { 'Content-Type': 'application/json', ...header } // 每次请求都自动添加 token(登录接口除外) // 并在需要 token 却不存在时,直接拦截请求并跳转登录页 if (needToken && !isLoginApi(fullUrl)) { const token = getToken() if (!token) { console.warn('[Request] token 为空,拦截请求并跳转到登录页面') redirectToLogin() // 直接抛错中断后续逻辑(包括 uni.request) return Promise.reject(new Error('未登录或登录已失效,请先登录')) } // 每次请求都添加 token 到 Authorization header requestHeader['Authorization'] = `Bearer ${token}` } // 添加 roleName 和 scenario 到 header(从登录响应中获取) const roleName = getRoleName() if (roleName) { requestHeader['X-Role-Name'] = roleName } const scenario = getScenario() if (scenario) { requestHeader['X-Scenario'] = scenario } // 处理请求数据 let requestData = { ...data } // 添加 tenantId 到 X-Tenant-Id header // 技术方案:登录返回的 LoginResponse 中包含 tenantId,前端保存后自动添加到所有请求的 header 中 if (needTenantId) { const tenantId = getTenantId() // 检查是否需要登录:如果不是登录接口且租户ID为空,则跳转到登录页 if (!isLoginApi(fullUrl) && !tenantId) { console.warn('[Request] 租户ID为空,跳转到登录页面') redirectToLogin() // 返回一个被拒绝的Promise,阻止请求继续执行 return Promise.reject(new Error('未登录,请先登录')) } if (tenantId) { // 将 tenantId 放入 X-Tenant-Id header(适用于所有请求类型:GET、POST、PUT、DELETE) requestHeader['X-Tenant-Id'] = tenantId } } // 发起请求 console.log('[Request] 准备发起 uni.request:', { url: fullUrl, method: method.toUpperCase(), hasData: !!requestData, hasHeaders: !!requestHeader, timeout: timeout }) return new Promise((resolve, reject) => { console.log('[Request] 调用 uni.request 开始执行') uni.request({ url: fullUrl, method: method.toUpperCase(), data: requestData, header: requestHeader, timeout, ...restOptions, success: (res) => { console.log('[Request] 收到响应:', { url: fullUrl, method: method.toUpperCase(), statusCode: res.statusCode, hasData: !!res.data, dataType: typeof res.data }) // 检查HTTP状态码,如果是401(未授权),跳转到登录页面 if (res.statusCode === 401) { console.warn('[Request] 收到401状态码,跳转到登录页面') redirectToLogin() reject(new Error('登录已过期,请重新登录')) return } resolve(res) }, fail: (err) => { console.error('[Request] 请求失败:', { url: fullUrl, method: method.toUpperCase(), error: err, errorMessage: err.errMsg || '未知错误', errorCode: err.code || '无错误码' }) reject(err) } }) }) } /** * GET 请求快捷方法 */ export function get(url, data = {}, options = {}) { return request({ url, method: 'GET', data, ...options }) } /** * POST 请求快捷方法 */ export function post(url, data = {}, options = {}) { return request({ url, method: 'POST', data, ...options }) } /** * PUT 请求快捷方法 */ export function put(url, data = {}, options = {}) { return request({ url, method: 'PUT', data, ...options }) } /** * DELETE 请求快捷方法 */ export function del(url, data = {}, options = {}) { return request({ url, method: 'DELETE', data, ...options }) }