299 lines
9.1 KiB
JavaScript
299 lines
9.1 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) {
|
|
const reqId = 'req_' + Date.now() + '_' + Math.floor(Math.random() * 10000);
|
|
const reqStart = Date.now();
|
|
console.log('[HTTP][REQUEST]', reqId, method, url, JSON.stringify({
|
|
data: data,
|
|
header: header,
|
|
dataType: dataType,
|
|
responseType: responseType,
|
|
timeout: timeout
|
|
}));
|
|
return new Promise((resolve, reject) => {
|
|
uni.request({
|
|
url: url,
|
|
method: method,
|
|
data: data,
|
|
header: header,
|
|
dataType: dataType,
|
|
responseType: responseType,
|
|
timeout: timeout,
|
|
success: (res) => {
|
|
console.log('[HTTP][RESPONSE]', reqId, method, url, 'statusCode=', res.statusCode, 'cost=', (Date.now() - reqStart) + 'ms', JSON.stringify(res.data));
|
|
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) => {
|
|
console.error('[HTTP][FAIL]', reqId, method, url, 'cost=', (Date.now() - reqStart) + 'ms', JSON.stringify(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 {
|
|
if (attempt > 0) {
|
|
console.log('[HTTP][RETRY]', method, url, 'attempt=', attempt, 'maxRetries=', maxRetries);
|
|
}
|
|
const result = await _makeRequest(url, method, data, header, dataType, responseType, timeout);
|
|
return result;
|
|
} catch (err) {
|
|
lastError = err;
|
|
lastStatusCode = err && err.statusCode;
|
|
console.warn('[HTTP][RETRY-FAIL]', method, url, 'attempt=', attempt, 'errMsg=', err && err.errMsg, 'statusCode=', lastStatusCode);
|
|
if (attempt >= maxRetries || !_shouldRetry(err.errMsg)) {
|
|
break;
|
|
}
|
|
const delay = RETRY_DELAY_BASE * Math.pow(2, attempt);
|
|
console.log('[HTTP][RETRY-WAIT]', method, url, 'delay=', delay + 'ms');
|
|
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) {
|
|
console.log('[HTTP][REQUEST] uploadBase64', Config.baseUrl + '/api/upload/headimgBase64');
|
|
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 => {
|
|
console.log('[HTTP][RESPONSE] uploadBase64', 'statusCode=', res.statusCode, JSON.stringify(res.data));
|
|
typeof params.success == 'function' && params.success(res.data);
|
|
},
|
|
fail: () => {
|
|
console.error('[HTTP][FAIL] uploadBase64', JSON.stringify(res));
|
|
typeof params.fail == 'function' && params.fail(res);
|
|
}
|
|
});
|
|
},
|
|
pullImage(params) {
|
|
console.log('[HTTP][REQUEST] pullImage', Config.baseUrl + '/api/upload/headimgPull', 'path=', params.path);
|
|
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 => {
|
|
console.log('[HTTP][RESPONSE] pullImage', 'statusCode=', res.statusCode, JSON.stringify(res.data));
|
|
typeof params.success == 'function' && params.success(res.data);
|
|
},
|
|
fail: () => {
|
|
console.error('[HTTP][FAIL] pullImage', JSON.stringify(res));
|
|
typeof params.fail == 'function' && params.fail(res);
|
|
}
|
|
});
|
|
},
|
|
upload(params) {
|
|
console.log('[HTTP][REQUEST] upload', Config.baseUrl + params.url, 'name=', params.name || 'file');
|
|
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) => {
|
|
console.log('[HTTP][RESPONSE] upload', Config.baseUrl + params.url, 'statusCode=', res.statusCode, res.data);
|
|
typeof params.success == 'function' && params.success(JSON.parse(res.data));
|
|
},
|
|
fail: (res) => {
|
|
console.error('[HTTP][FAIL] upload', Config.baseUrl + params.url, JSON.stringify(res));
|
|
typeof params.fail == 'function' && params.fail(res);
|
|
}
|
|
});
|
|
}
|
|
} |