/** * 网络请求封装 */ import { useUserStore } from '@/store/user' // 根据环境自动切换API地址 // 开发环境:统一使用本地服务器 // 生产环境:使用线上服务器 // 开发模式标志(手动切换) const IS_DEV = true // 改为 true 则使用本地开发环境 // 根据开发模式选择BASE_URL const BASE_URL = IS_DEV ? 'http://localhost:8089' // 开发环境 - 本地服务器 : 'https://px.ddn-ai.cloud' // 生产环境 - 线上服务器 console.log('[Request] 当前环境:', IS_DEV ? '开发环境' : '生产环境') console.log('[Request] BASE_URL:', BASE_URL) const LOGIN_EXCLUDE_URLS = [ '/api/auth/login', '/api/auth/wx-login', '/api/user/login/wechat', '/api/auth/register', '/api/auth/reapply', '/api/auth/send-code', '/api/auth/refresh-token' ] let isHandlingAuthExpired = false function buildAuthHeader(token) { if (!token) return '' return token.startsWith('Bearer ') ? token : `Bearer ${token}` } function normalizeToken(token) { return buildAuthHeader(token) } function shouldHandleAuthExpired(url) { return !LOGIN_EXCLUDE_URLS.includes(url) } /** * 发送请求 */ function request(options) { return new Promise((resolve, reject) => { // 获取租户ID const tenantId = uni.getStorageSync('currentTenantId') // 获取token const token = uni.getStorageSync('token') const requestToken = token // 设置请求头 const header = { 'Content-Type': 'application/json;charset=UTF-8', ...options.header } // 添加租户ID到请求头 if (tenantId) { header['X-Tenant-Id'] = tenantId } // 添加token到请求头 if (token) { const authHeader = buildAuthHeader(token) header['Authorization'] = authHeader console.log('[Request] 发送请求,token:', token) console.log('[Request] Authorization头:', authHeader) } // 处理 GET 请求参数(拼接到 URL) let url = BASE_URL + options.url if (options.method === 'GET' && options.params) { const queryString = Object.keys(options.params) .filter(key => options.params[key] !== null && options.params[key] !== undefined && options.params[key] !== '') .map(key => { const value = options.params[key] const encodedValue = (value !== null && typeof value === 'object') ? encodeURIComponent(JSON.stringify(value)) : encodeURIComponent(value) return `${encodeURIComponent(key)}=${encodedValue}` }) .join('&') if (queryString) { url += (url.includes('?') ? '&' : '?') + queryString } console.log('[Request] GET URL:', url) console.log('[Request] GET Params:', options.params) } // 发送请求 uni.request({ url: url, method: options.method || 'GET', data: options.method === 'GET' ? undefined : (options.data || options.params), header: header, success: (res) => { console.log('[Request] Response:', res) // 处理401未登录 if (res.statusCode === 401) { if (!shouldHandleAuthExpired(options.url)) { reject({ code: 401, message: '未登录', data: res }) return } const latestToken = uni.getStorageSync('token') if (latestToken && normalizeToken(requestToken) !== normalizeToken(latestToken)) { reject({ code: 401, message: '未登录', data: res }) return } if (isHandlingAuthExpired) { reject({ code: 401, message: '未登录', data: res }) return } isHandlingAuthExpired = true console.log('[Request] 401 未登录,清除token') uni.removeStorageSync('token') uni.removeStorageSync('userInfo') uni.showModal({ title: '登录已过期', content: '请重新登录', showCancel: false, success: () => { uni.reLaunch({ url: '/pages/login/index' }) }, complete: () => { setTimeout(() => { isHandlingAuthExpired = false }, 500) } }) reject({ code: 401, message: '未登录', data: res }) return } if (res.statusCode === 200) { if (res.data && typeof res.data === 'object' && res.data.code === 401) { if (!shouldHandleAuthExpired(options.url)) { reject({ code: 401, message: '未登录', data: res.data }) return } const latestToken = uni.getStorageSync('token') if (latestToken && normalizeToken(requestToken) !== normalizeToken(latestToken)) { reject({ code: 401, message: '未登录', data: res.data }) return } if (!isHandlingAuthExpired) { isHandlingAuthExpired = true console.log('[Request] 业务码401 未登录,清除所有登录信息') uni.removeStorageSync('token') uni.removeStorageSync('userInfo') uni.removeStorageSync('currentRole') // 🔥 关键修复:也清除角色信息 uni.showModal({ title: '登录已过期', content: '请重新登录', showCancel: false, success: () => { uni.reLaunch({ url: '/pages/login/index' }) }, complete: () => { setTimeout(() => { isHandlingAuthExpired = false }, 500) } }) } reject({ code: 401, message: '未登录', data: res.data }) return } // 返回完整的响应对象,让调用方自己处理 resolve(res.data) } else if (res.statusCode === 404) { console.error('[Request] 404 Not Found:', url) uni.showToast({ title: '接口不存在', icon: 'none' }) reject({ code: 404, message: '接口不存在', data: res }) } else if (res.statusCode === 500) { console.error('[Request] 500 Server Error:', res) uni.showToast({ title: '服务器错误', icon: 'none' }) reject({ code: 500, message: '服务器错误', data: res }) } else { console.error('[Request] Error:', res.statusCode, res) uni.showToast({ title: `网络错误(${res.statusCode})`, icon: 'none' }) reject(res) } }, fail: (err) => { console.error('[Request] Fail:', err) uni.showToast({ title: '网络连接失败', icon: 'none' }) reject(err) } }) }) } // 导出便捷方法 request.get = function(url, params) { return request({ url: url, method: 'GET', params: params }) } request.post = function(url, data, params) { // 如果有 params,拼接到 URL if (params && typeof params === 'object') { const queryString = Object.keys(params) .filter(key => params[key] !== null && params[key] !== undefined && params[key] !== '') .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&') if (queryString) { url += (url.includes('?') ? '&' : '?') + queryString } } return request({ url: url, method: 'POST', data: data, params: params }) } request.put = function(url, data, params) { // 如果有 params,拼接到 URL if (params && typeof params === 'object') { const queryString = Object.keys(params) .filter(key => params[key] !== null && params[key] !== undefined && params[key] !== '') .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&') if (queryString) { url += (url.includes('?') ? '&' : '?') + queryString } } return request({ url: url, method: 'PUT', data: data, params: params }) } request.delete = function(url, params) { return request({ url: url, method: 'DELETE', params: params }) } request.upload = function(url, filePath) { return new Promise((resolve, reject) => { const token = uni.getStorageSync('token') const tenantId = uni.getStorageSync('currentTenantId') const header = { 'Authorization': token ? `Bearer ${token}` : '' } if (tenantId) { header['X-Tenant-Id'] = tenantId } const uploadTask = uni.uploadFile({ url: BASE_URL + url, filePath: filePath, name: 'file', header: header, timeout: 60000, // 60秒超时 success: (res) => { try { const data = JSON.parse(res.data) if (data.code === 200) { resolve(data.data) } else { uni.showToast({ title: data.message || '上传失败', icon: 'none' }) reject(data) } } catch (e) { console.error('解析上传响应失败:', res.data) reject({ message: '服务器响应异常' }) } }, fail: (err) => { console.error('上传失败:', err) uni.showToast({ title: err.errMsg || '上传失败', icon: 'none' }) reject(err) } }) // 监听上传进度 uploadTask.onProgressUpdate((res) => { console.log('上传进度:', res.progress + '%') }) }) } export default request