pda-uni-app-perkins/common/http.js

240 lines
7.1 KiB
JavaScript
Raw Normal View History

2025-10-31 22:49:51 +08:00
import {
ENV,
MOCK_DATA
} from './env.js';
import {
DialogUtils
} from '../utils/dialog.js';
/**
* HTTP 拦截器 - 统一处理测试模式和生产模式的请求
*
* 核心功能
* 1. 测试模式返回 MOCK_DATA 中定义的模拟数据
* 2. 生产模式发起真实的 HTTP 请求到 API 服务器
* 3. 调试模式显示请求信息和详细日志
* 4. 统一的错误处理和响应格式
*/
function createHttpInterceptor() {
return {
/**
* 请求前拦截器 - 统一处理 URL 拼接和调试信息
* @param {string} method - HTTP 方法GET/POST/DELETE
* @param {string} url - API 路径不包含域名
* @param {object} params - 请求参数
* @param {object} extra - 额外配置timeoutmockDelay
* @returns {object} 处理后的请求信息
*/
beforeRequest(method, url, params, extra) {
const fullUrl = ENV.API_URL + url;
// URL 调试功能 - 在测试和生产模式下都可用
if (ENV.DEBUG_URL) {
// 显示详细的 URL 调试信息
console.log('HTTP Request Debug:', {
method,
fullUrl,
path: url,
params,
isTestMode: ENV.TEST_MODE
});
}
return {
fullUrl,
params,
extra
};
},
/**
* 响应处理器 - 根据模式返回 Mock 数据或真实请求
* @param {string} method - HTTP 方法
* @param {string} url - API 路径
* @param {object} params - 请求参数
* @param {object} extra - 额外配置
* @returns {Promise} 统一格式的响应 Promise
*/
processResponse(method, url, params, extra) {
return new Promise((resolve, reject) => {
// 统一调用前置拦截器(测试模式和生产模式都需要)
const {
fullUrl
} = this.beforeRequest(method, url, params, extra);
// 测试模式 - 返回模拟数据
if (ENV.TEST_MODE && MOCK_DATA[url]) {
const mockResponse = JSON.parse(JSON.stringify(MOCK_DATA[url])); // 深拷贝避免数据污染
// 只在开发环境输出 Mock 日志
if (process.env.NODE_ENV !== 'production') {
console.log('测试模式 - 返回模拟数据:', {
method,
url,
response: mockResponse
});
}
// 模拟网络延迟(让 UI 更贴近真实场景)
setTimeout(() => {
resolve(mockResponse);
}, extra.mockDelay || 500);
return;
}
// 生产模式 - 执行真实请求
// 从本地存储获取认证 token
const token = uni.getStorageSync('user_token') || '';
// 对于 GET 请求,将参数拼接到 URL 查询字符串中
let requestUrl = fullUrl;
let requestData;
if (method === 'GET') {
// GET 请求:参数拼接到 URLbody 为空
if (params && Object.keys(params).length > 0) {
const queryString = Object.entries(params)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
requestUrl = `${fullUrl}?${queryString}`;
}
requestData = undefined;
} else if (method === 'DELETE') {
// DELETE 请求:通常不需要 body资源 ID 在 URL 中)
requestData = undefined;
} else {
// POST/PUT 请求:参数在 body 中
requestData = params;
}
// 构建请求头
const headers = {
'Content-Type': 'application/json',
'X-App-Version': ENV.VERSION || '1.0.0'
};
// 添加认证头(如果存在 token
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
uni.request({
url: requestUrl,
method: method.toUpperCase(),
data: requestData,
header: headers,
timeout: extra.timeout || 5000,
sslVerify: ENV.SSL_VERIFY, // SSL 证书验证
// Android: 由 res/xml/network_security_config.xml 控制
// iOS: 由 Info.plist 控制
success: (res) => {
const response = {
code: res.statusCode,
data: res.data,
message: res.errMsg
};
// 只在开发环境和调试模式下输出响应日志
if (ENV.DEBUG_URL && process.env.NODE_ENV !== 'production') {
console.log('生产模式 - 真实请求响应:', {
method,
url,
fullUrl,
response
});
}
resolve(response);
},
fail: (err) => {
const errorResponse = {
code: 500,
data: null,
message: err.errMsg || 'network error'
};
// 错误日志在所有环境下都输出(帮助排查问题)
console.error('请求失败:', {
method,
url,
fullUrl,
error: errorResponse
});
reject(errorResponse);
},
});
});
}
};
}
const httpInterceptor = createHttpInterceptor();
/**
* GET 请求 - 用于查询数据
* @param {string} url - API 路径例如'/wmsServer/wms/api/orderIn/test'
* @param {object} params - 查询参数会自动拼接到 URL
* @param {object} extra - 额外配置 { timeout, mockDelay }
* @returns {Promise<{code: number, data: any, message: string}>}
*
* @example
* httpGet('/wmsServer/wms/api/orderIn/getOrderInWithVehicleNo', { vehicleNo: 'V12345' })
*/
export async function httpGet(url, params = {}, extra = {}) {
return httpInterceptor.processResponse('GET', url, params, extra);
}
/**
* POST 请求 - 用于创建或提交数据
* @param {string} url - API 路径
* @param {object} data - 请求体数据
* @param {object} extra - 额外配置 { timeout, mockDelay }
* @returns {Promise<{code: number, data: any, message: string}>}
*
* @example
* httpPost('/wmsServer/wms/api/orderIn/loginInBad', { standId: 'EF01', vehicleNo: 'V12345' })
*/
export async function httpPost(url, data = {}, extra = {}) {
return httpInterceptor.processResponse('POST', url, data, extra);
}
/**
* DELETE 请求 - 用于删除资源
* 注意DELETE 请求应该保持完整的 URL包括资源 ID
* Mock 数据匹配时会自动处理路径参数env.js 中使用基础路径作为 key
*
* @param {string} url - 完整的 API 路径例如'/wmsServer/wms/api/orderIn/deleteOrderIn/12345'
* @param {object} extra - 额外配置 { timeout, mockDelay }
* @returns {Promise<{code: number, data: any, message: string}>}
*
* @example
* httpDelete('/wmsServer/wms/api/orderIn/deleteOrderIn/12345')
*
* 对于测试模式
* - 会先尝试匹配完整 URL
* - 如果找不到会尝试匹配基础路径去除末尾数字 ID
*/
export async function httpDelete(url, extra = {}) {
// 测试模式下,支持基础路径匹配(兼容 env.js 的 MOCK_DATA 定义)
if (ENV.TEST_MODE) {
// 先尝试完整 URL 匹配
if (MOCK_DATA[url]) {
return httpInterceptor.processResponse('DELETE', url, {}, extra);
}
// 如果找不到,尝试基础路径匹配(移除末尾的数字 ID
const cleanUrl = url.split('?')[0]; // 移除查询参数
const baseUrl = cleanUrl.replace(/\/\d+$/, ''); // 移除末尾数字 ID
if (MOCK_DATA[baseUrl]) {
// 使用基础路径进行 Mock 数据匹配
return httpInterceptor.processResponse('DELETE', baseUrl, {}, extra);
}
}
// 生产模式:保持完整的 URL包括资源 ID
return httpInterceptor.processResponse('DELETE', url, {}, extra);
}