Files
smartDriveEEUniApp/common/request.js
2026-02-01 10:55:35 +08:00

280 lines
6.5 KiB
JavaScript
Raw Permalink 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.

/**
* 统一的 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:8090/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) {
// 每次请求都添加 token 到 Authorization header
requestHeader['Authorization'] = `Bearer ${token}`
} else {
// 如果没有 token移除可能存在的旧 Authorization header
delete requestHeader['Authorization']
}
}
// 添加 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
})
}