import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' import axios from 'axios' import { AxiosCanceler } from './cancel' import router from '@/router' import { toast } from 'vue-sonner' import { HiAesUtil } from './HiAesUtil.ts' const encryptionKey = 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx' function redirectLogin() { localStorage.removeItem('Authorization') router.push({ path: '/', query: { login: 'true' } }) } export interface ExtraConfig { /** * 默认值 1 错误等级 0-忽略,1-warning,2-error */ errorLevel?: 0 | 1 | 2 /** * 默认true 是否携带token */ withToken?: boolean /** * 默认true 是否处理响应 */ handleResponse?: boolean /** * 默认false 是否取消重复请求 */ cancelRepetition?: boolean /** * 默认false 是否返回axios完整响应 */ originResponseData?: boolean /** * 默认token 存储key */ tokenKey?: string /** * 获取token的方法 */ getToken?: () => string /** * 自定义header方法,此方法返回的header会覆盖默认的header */ formatHeader?: (header: Record) => Record } export interface RequestConfig extends AxiosRequestConfig { extraConfig?: ExtraConfig } interface ResponseType extends AxiosResponse { config: RequestConfig } const ERROR_MESSAGES: Record = { '200': '成功', '500': '内部服务器错误', '10001': '数据库查询错误', '10002': '数据库更新错误', '10003': '数据库插入错误', '10004': '数据库删除错误', '20001': '用户已存在', '20002': '用户不存在', '20003': '用户密码错误', '20004': '用户已禁用', '20005': '余额不足', '20006': '停止注册', '20007': '未绑定Telegram', '20008': '用户未绑定OAuth方式', '20009': '邀请码错误', '30001': '节点已存在', '30002': '节点不存在', '30003': '节点组已存在', '30004': '节点组不存在', '30005': '节点组不为空', '400': '参数错误', '40002': '用户令牌为空', '40003': '用户令牌无效', '40004': '用户令牌已过期', '40005': '您还没有登录', '401': '请求过多', '50001': '优惠券不存在', '50002': '优惠券已被使用', '50003': '优惠券不匹配', '60001': '订阅已过期', '60002': '订阅不可用', '60003': '用户已有订阅', '60004': '订阅已被使用', '60005': '单一订阅模式超出限制', '60006': '订阅配额限制', '70001': '验证码错误', '80001': '队列入队错误', '90001': '调试模式已启用', '90002': '发送短信错误', '90003': '短信功能未启用', '90004': '电子邮件功能未启用', '90005': '不支持的登录方式', '90006': '身份验证器不支持此方式', '90007': '电话区号为空', '90008': '密码为空', '90009': '区号为空', '90010': '需要密码或验证码', '90011': '电子邮件已存在', '90012': '电话号码已存在', '90013': '设备已存在', '90014': '电话号码错误', '90015': '此账户今日已达到发送次数限制', '90017': '设备不存在', '90018': '用户 ID 不匹配', '61001': '订单不存在', '61002': '支付方式未找到', '61003': '订单状态错误', '61004': '重置周期不足', '61005': '存在没用完的流量', } export default class Request { public axiosInstance: AxiosInstance private config: RequestConfig constructor(config: RequestConfig) { this.config = config this.axiosInstance = axios.create(config) this.init() } static defaultConfig: Required = { errorLevel: 2, withToken: true, handleResponse: true, cancelRepetition: false, originResponseData: false, tokenKey: 'token', formatHeader: (headers) => { return headers }, getToken: () => '', } private errorReport(lv: number, message: string) { toast(message) } private init() { const axiosCanceler = new AxiosCanceler() this.axiosInstance.interceptors.request.use( (config: RequestConfig) => { const mergeExtraConfig = { ...Request.defaultConfig, ...this.config.extraConfig, ...config.extraConfig, } if (config.data && !(config.data instanceof FormData)) { const plainText = JSON.stringify(config.data) config.data = HiAesUtil.encryptData(plainText, encryptionKey) } if (config.method?.toLowerCase() === 'get' || config.params) { const paramsToEncrypt = config.params || {} const plainParamsText = JSON.stringify(paramsToEncrypt) const encryptedParams = HiAesUtil.encryptData(plainParamsText, encryptionKey) config.params = { data: encryptedParams.data, time: encryptedParams.time, } } config.headers = mergeExtraConfig.formatHeader({ ...this.config.headers, ...config.headers, lang: 'zh_CN', 'login-type': 'device', 'user-agent': 'android', ...(mergeExtraConfig.withToken && { [mergeExtraConfig.tokenKey]: mergeExtraConfig.getToken(), }), } as any) config.extraConfig = mergeExtraConfig if (mergeExtraConfig.cancelRepetition) { axiosCanceler.addPending(config) } return config }, (error) => { this.errorReport(error?.config.extraConfig.errorLevel, error) return Promise.reject(error) }, ) this.axiosInstance.interceptors.response.use( (response: ResponseType) => { const { data, config } = response let responseData = response.data.data if (responseData && responseData.data && responseData.time) { try { const decryptedStr = HiAesUtil.decryptData( responseData.data, responseData.time, encryptionKey, ) responseData = JSON.parse(decryptedStr) } catch (e) { console.error('解密失败:', e) return Promise.reject({ message: '数据解密异常' }) } } axiosCanceler.removePending(config) if (data.code !== 200) { const msg = ERROR_MESSAGES[data.code] || response.data?.msg || data?.error || '未知错误' if (data.code == 40004 || data.code == 40003 || data.code == 40005) { toast.error(msg) redirectLogin() return } if (config.extraConfig?.handleResponse) { this.errorReport(config.extraConfig.errorLevel ?? 2, msg) return Promise.reject({ ...data, message: msg, }) } } return config.extraConfig?.originResponseData ? response : responseData }, (error) => { const status = error?.response?.status const code = error?.code let message = error?.message if (status === 401) { redirectLogin() return } if (code === 'ECONNABORTED') { message = '网络环境太差,请求超时' } else if (code === 'Network Error' || message === 'Network Error') { if (error.response) { message = `${error.response.status}:network连接失败,请求中断` } else { message = '网络好像出现问题了' } } if (error.__CANCEL__) { console.warn('request canceled', error?.message) } else { this.errorReport(error?.config?.extraConfig?.errorLevel, message) } return Promise.reject(error) }, ) } public get(url: string, params?: Record, config?: RequestConfig): Promise { return this.axiosInstance.get(url, { ...config, params }) } public post(url: string, data?: D, config?: RequestConfig): Promise { return this.axiosInstance.post(url, data, { ...config }) } public put(url: string, data?: D, config?: RequestConfig): Promise { return this.axiosInstance.put(url, data, { ...config }) } public patch(url: string, data?: D, config?: RequestConfig): Promise { return this.axiosInstance.patch(url, data, { ...config }) } public delete(url: string, params?: D, config?: RequestConfig): Promise { return this.axiosInstance.delete(url, { ...config, params }) } }