Files
lucky_shop/common/js/http.js
ZF sun 30cda71749 refactor(http,lang): 重构请求逻辑并新增多语言错误提示
1.  新增网络相关错误的多语言翻译配置
2.  重写http请求层,添加自动重试机制与错误分类提示
3.  优化鸿蒙平台下的组件渲染兼容性与数据更新逻辑
4.  调整部分页面的模板结构与请求配置
2026-07-13 15:45:15 +08:00

274 lines
7.4 KiB
JavaScript

import Config from './config.js'
import Util from './util.js'
import store from '@/store/index.js'
import Platform from './platform.js'
const { appType, appTypeName } = Platform.getPlatformInfo();
let _Lang = null;
function getLang() {
if (!_Lang) {
_Lang = require('./lang.js').default;
}
return _Lang;
}
const DEFAULT_TIMEOUT = 15000;
const DEFAULT_MAX_RETRIES = 2;
const RETRY_DELAY_BASE = 2000;
function _shouldRetry(errMsg) {
if (!errMsg) return false;
if (errMsg === 'request:fail url not in domain list') return false;
if (errMsg === 'request:fail abort') return false;
const retryableErrors = [
'request:fail timeout',
'request:fail connect',
'request:fail network'
];
return retryableErrors.some(err => errMsg.startsWith(err));
}
function _classifyError(errMsg, statusCode) {
if (!errMsg && !statusCode) return 'unknown';
if (errMsg === 'request:fail abort') return 'aborted';
if (errMsg && errMsg.indexOf('timeout') !== -1) return 'timeout';
if (errMsg && (errMsg.indexOf('connect') !== -1 || errMsg.indexOf('network') !== -1)) return 'networkError';
if (statusCode && statusCode >= 500) return 'serverError';
if (errMsg && errMsg.indexOf('url not in domain list') !== -1) return 'domainNotInList';
return 'unknown';
}
function _sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function _makeRequest(url, method, data, header, dataType, responseType, timeout) {
return new Promise((resolve, reject) => {
uni.request({
url: url,
method: method,
data: data,
header: header,
dataType: dataType,
responseType: responseType,
timeout: timeout,
success: (res) => {
if (res.statusCode && [200, 500].indexOf(res.statusCode) == -1) {
reject({ errMsg: `request:fail statusCode=${res.statusCode}`, statusCode: res.statusCode });
return;
}
if (res.data.code == -3 && store.state.siteState > 0) {
store.commit('setSiteState', -3);
Util.redirectTo('/pages_tool/storeclose/storeclose', {}, 'reLaunch');
return;
}
if (res.data.refreshtoken) {
store.commit('setToken', res.data.refreshtoken);
}
if (res.data.code == -10009 || res.data.code == -10010) {
store.commit('setToken', '');
store.commit('setMemberInfo', '');
}
resolve(res.data);
},
fail: (res) => {
reject(res);
}
});
});
}
async function _requestWithRetry(params, url, method, data, header, dataType, responseType) {
const timeout = params.timeout || DEFAULT_TIMEOUT;
const maxRetries = params.noRetry ? 0 : (params.maxRetries || DEFAULT_MAX_RETRIES);
let lastError = null;
let lastStatusCode = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const result = await _makeRequest(url, method, data, header, dataType, responseType, timeout);
return result;
} catch (err) {
lastError = err;
lastStatusCode = err && err.statusCode;
if (attempt >= maxRetries || !_shouldRetry(err.errMsg)) {
break;
}
const delay = RETRY_DELAY_BASE * Math.pow(2, attempt);
await _sleep(delay);
}
}
if (params.silentError) {
throw lastError;
}
const errorType = _classifyError(lastError && lastError.errMsg, lastStatusCode);
const lang = getLang();
if (errorType === 'domainNotInList') {
uni.showToast({
title: lang.lang('http.domainNotInList', { url: Config.baseUrl }),
'icon': 'none',
duration: 10000
});
} else {
const i18nKey = 'http.' + errorType;
uni.showToast({
title: lang.lang(i18nKey),
'icon': 'none',
duration: 10000
});
}
throw lastError;
}
export default {
sendRequest(params) {
if (!Config.baseUrl) {
uni.showToast({
title: getLang().lang('http.baseUrlNotConfigured'),
'icon': 'none',
duration: 10000
});
return;
}
const _isRemoteOnly = getLang().isRemoteOnly && getLang().isRemoteOnly();
const _bootDone = getLang().isBootCompleted && getLang().isBootCompleted();
if (_isRemoteOnly && !_bootDone) {
let bootPromise = null;
try {
const app = getApp();
if (app && app.$langReady) bootPromise = app.$langReady;
} catch (e) {}
if (!bootPromise) bootPromise = getLang().boot();
if (params.async === false) {
return bootPromise.then(() => this._sendRequestNow(params));
} else {
bootPromise.then(() => this._sendRequestNow(params));
return;
}
}
return this._sendRequestNow(params);
},
_sendRequestNow(params) {
var method = params.data != undefined ? 'POST' : 'GET',
url = (Config.baseUrl + params.url).replace(/(?<!:)\/+/g, '/'),
data = {
app_type: appType,
app_type_name: appTypeName
};
data.token = store.state.token || '';
data.uniacid = uni.getStorageSync('uniacid') || Config.uniacid;
var default_store_info = store.state.defaultStoreInfo;
if (default_store_info) {
data.store_id = default_store_info.store_id;
}
var store_info = store.state.globalStoreInfo;
if (store_info) data.store_id = store_info.store_id;
if (params.data != undefined) Object.assign(data, params.data);
const header = params.header || {
'content-type': 'application/x-www-form-urlencoded;application/json'
};
const dataType = params.dataType || 'json';
const responseType = params.responseType || 'text';
if (params.async === false) {
return _requestWithRetry(params, url, method, data, header, dataType, responseType)
.then(res => {
typeof params.complete == 'function' && params.complete(res);
return res;
})
.catch(err => {
typeof params.complete == 'function' && params.complete();
throw err;
});
} else {
_requestWithRetry(params, url, method, data, header, dataType, responseType)
.then(res => {
typeof params.success == 'function' && params.success(res);
typeof params.complete == 'function' && params.complete(res);
})
.catch(err => {
typeof params.fail == 'function' && params.fail(err);
typeof params.complete == 'function' && params.complete();
});
}
},
uploadBase64(params) {
uni.request({
url: Config.baseUrl + '/api/upload/headimgBase64',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded;application/json'
},
data: {
app_type: appType,
app_type_name: appTypeName,
images: params.base64
},
dataType: 'json',
responseType: 'text',
success: res => {
typeof params.success == 'function' && params.success(res.data);
},
fail: () => {
typeof params.fail == 'function' && params.fail(res);
}
});
},
pullImage(params) {
uni.request({
url: Config.baseUrl + '/api/upload/headimgPull',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded;application/json'
},
data: {
app_type: appType,
app_type_name: appTypeName,
path: params.path
},
dataType: 'json',
responseType: 'text',
success: res => {
typeof params.success == 'function' && params.success(res.data);
},
fail: () => {
typeof params.fail == 'function' && params.fail(res);
}
});
},
upload(params) {
uni.uploadFile({
url: Config.baseUrl + params.url,
filePath: params.filePath,
name: params.name || 'file',
fileType: params.fileType || 'image',
formData: {
app_type: appType,
app_type_name: appTypeName,
},
header: {
'content-type': 'application/x-www-form-urlencoded;application/json'
},
success: (res) => {
typeof params.success == 'function' && params.success(JSON.parse(res.data));
},
fail: (res) => {
typeof params.fail == 'function' && params.fail(res);
}
});
}
}