import Taro, { UploadTask } from '@tarojs/taro'; import { HttpError } from '@/http/error'; import { safeJsonParse } from '@/utils/common'; import { API, BASE_URL } from './api'; import { HTTP_STATUS, RESPONSE_ERROR_CODE } from './constant'; import interceptors from './interceptor'; import { IRequestResponse } from './type'; import { isTokenExpired, refreshToken } from './utils'; interface IRequestOption extends Taro.request.Option { contentType?: 'application/json' | 'application/x-www-form-urlencoded'; blRetryTime?: number; } interface IUploadOption extends Taro.uploadFile.Option { blRetryTime?: number; onProgress?: UploadTask.OnProgressUpdateCallback; } interceptors.forEach(interceptor => Taro.addInterceptor(interceptor)); class Http { private throwHttpError = (resp: Taro.request.SuccessCallbackResult) => { const { statusCode, data } = resp; const errorCode = data?.code || 'null'; const errorMsg = data?.msg || 'unknown'; const traceId = data?.traceId; console.error( `http request fail, httpCode: ${statusCode}, errorCode: ${errorCode}, errorMsg: ${errorMsg}, traceId: ${traceId}` ); throw new HttpError('request fail', errorCode, errorMsg, traceId); }; private isNeedLoginError = ( options: { blRetryTime?: number }, resp: Taro.request.SuccessCallbackResult ) => { if (options.blRetryTime && options.blRetryTime >= 3) { return false; } const { statusCode, data } = resp; return statusCode !== HTTP_STATUS.SUCCESS && data?.code === RESPONSE_ERROR_CODE.NEED_LOGIN; }; private uploadFile = (options: IUploadOption) => { const { onProgress, ...option } = options; let offProgressUpdate: (() => void) | null = null; return new Promise((resolve, reject) => { option.success = resolve; option.fail = reject; const task = Taro.uploadFile(option); onProgress && task.onProgressUpdate(onProgress); onProgress && (offProgressUpdate = () => task.offProgressUpdate(onProgress)); }) .then((resp: Taro.uploadFile.SuccessCallbackResult) => { if (resp.statusCode === 200) { return safeJsonParse(resp.data.replace(/\uFEFF/g, '')); } else if (this.isNeedLoginError(option, resp as Taro.request.SuccessCallbackResult)) { option.blRetryTime = (option.blRetryTime || 0) + 1; return refreshToken().then(() => this.uploadFile(option)); } else { this.throwHttpError(resp as Taro.request.SuccessCallbackResult); } }) .finally(() => offProgressUpdate?.()); }; private request = (option: IRequestOption) => { return new Promise((resolve, reject) => { option.success = resolve; option.fail = reject; Taro.request(option); }).then((resp: Taro.request.SuccessCallbackResult) => { if (resp.statusCode === HTTP_STATUS.SUCCESS) { return resp.data; } else if (this.isNeedLoginError(option, resp)) { option.blRetryTime = (option.blRetryTime || 0) + 1; return refreshToken().then(() => this.request(option)); } else { this.throwHttpError(resp); } }); }; private baseUpload = (params: IUploadOption) => { const { url, ...otherParams } = params; const option: IUploadOption = { // 默认超时时间三分钟 timeout: 1000 * 60 * 3, ...otherParams, blRetryTime: 0, url: url.startsWith('http') ? url : BASE_URL + url, header: { 'content-type': 'multipart/form-data' }, }; return this.uploadFile(option); }; private baseRequest = (params: IRequestOption, method: Taro.request.Option['method'] = 'GET') => { const { url, data, ...otherParams } = params; const contentType = params.contentType || 'application/json'; const option: IRequestOption = { ...otherParams, blRetryTime: 0, url: BASE_URL + url, data, method: method, header: { 'content-type': contentType /*'user-Id': '588002047871053824' */ }, }; return this.request(option); }; async init(inviteCode?: string) { if (isTokenExpired()) { await refreshToken(inviteCode); } } get = (url: API, params?: Omit, 'url'>): Promise => { const option = { url, ...params }; return this.baseRequest(option); }; post = (url: API, params?: Omit, 'url'>): Promise => { const option = { url, ...params }; return this.baseRequest(option, 'POST'); }; upload = (url: API | string, params?: Omit): Promise => { const option = { url, ...params } as IUploadOption; return this.baseUpload(option); }; } export default new Http();