request.ts 6.62 KB
import axios, {CancelTokenSource} from "axios";
import Storage from './composables/storage-helper';
import CryptoHelper from './composables/cryptoJs-helper';
import { ElMessage } from 'element-plus'
import useUserStore from '@/store/modules/user'

const CANCELTTYPE = {
  CACHE: 1,
  REPEAT: 2,
};
interface Request {
  md5Key: string;
  source: CancelTokenSource;
}
const pendingRequests: Request[] = [];
const storage = new Storage();
const cryptoHelper = new CryptoHelper('cacheKey');

const service = axios.create({
  baseURL: (import.meta.env.VITE_OPEN_PROXY === 'true') ? `/api/` : `${import.meta.env.VITE_API_BASEURL}`,
  timeout: 10 * 60 * 1000,// request timeout
  headers: { 'Content-Type': 'application/json;charset=UTF-8' }
  // withCredentials:true
})
// 请求拦截
service.interceptors.request.use(
  async (config: any) => {
    //TODO,可能正在刷新token..等刷新完成再调用接口。
    const process = () => {
      /**
       * 为每一次请求生成一个cancleToken
       */
      const source = axios.CancelToken.source();
      config.cancelToken = source.token;
      if (config.method === "postfile") {
        config.method = "post";
        config.headers = Object.assign({}, config.headers, {
          "Content-Type": "multipart/form-data",
        });
        config.headers.Authorization = localStorage.getItem('token');
        return config;
      }
      if (config.method == "postjsond") {
        config.method = "post";
        config.headers = Object.assign({}, config.headers, {
          "Content-Type": "application/json;charset=UTF-8",
        });
        config.data = JSON.stringify(config.data);
        config.headers.Authorization = localStorage.getItem('token');
        return config;
      }
      if (config.responseType == "blob") {
        // 文件流,文件名称相同时会判定同一个请求。
        const userStore = useUserStore();
        config.headers.Authorization = localStorage.getItem('token');
        return config;
      }
      // /**
      //  * 缓存命中判断
      //  * 成功则取消当次请求
      //  */
      // const data = storage.get(cryptoHelper.encrypt(
      //   config.url + JSON.stringify(config.data) + (config.method || ''),
      // ));
      // if (data && (Date.now() <= data.exppries)) {
      //   // console.log(`接口:${config.url} 缓存命中 -- ${Date.now()} -- ${data.exppries}`);
      //   source.cancel(JSON.stringify({
      //     type: CANCELTTYPE.CACHE,
      //     data: data.data,
      //   }));
      // }
      /** 若是门户的url,则不做重复请求处理,会出现不同标签页都需要查同一个字典参数列表的 */
      if (config.url.indexOf('/portal/portal')) {
        config.headers.Authorization = localStorage.getItem('token');
        return config;
      }
      /**
       * 重复请求判断
       * 同url,同请求类型判定为重复请求
       * 以最新的请求为准
       */
      const md5Key = cryptoHelper.encrypt(
        config.url +
          JSON.stringify(config.data) +
          JSON.stringify(config.params) +
          (config.method || "")
      );
      /**
       * 将之前的重复且未完成的请求全部取消
       */
      const hits = pendingRequests.filter((item) => item.md5Key === md5Key);
      if (hits.length > 0) {
        hits.forEach((item) =>
          item.source.cancel(
            JSON.stringify({
              type: CANCELTTYPE.REPEAT,
              data: "重复请求,以取消",
            })
          )
        );
      }
      /**
       * 将当前请求添加进请求对列中
       */
      pendingRequests.push({
        md5Key,
        source,
      });

      config.headers.Authorization = localStorage.getItem('token');
      return config;
    };
    /** 若是刷新token的请求,则不需要等refreshToken完成。 */
    if (config.url.includes('/csbr-zuul/user/refreshToken')) {
      return process();
    }
    let expiresIn = localStorage.getItem('expiresIn');
    let refresh_token = localStorage.getItem('refresh_token');
    const refreshing = localStorage.getItem('refreshing');
    if (expiresIn && refresh_token && localStorage.getItem('token') && refreshing) {
      /** 等refreshToken刷新成功后再继续调用接口 */
      await new Promise(resolve => setTimeout(resolve, 1000));
      return process();
    } else {
     return process();
    }
  },
  (error: any) => {
    console.log(error)
    return Promise.reject(error)
  }
)

// 响应拦截
service.interceptors.response.use(
  (response: any) => {
    const res = response.data
    if (res.code !== 1 || res.code !== 200 || res.code !== '00000') {
      return response.data
    } else {
      return response.data
    }
  },
  (error: any) => {
    if (error.response) {
      switch (error.response.status) {
        case 400:
          let status = error.response.data.status;
          if (status == '402' || status == '403') {
            ElMessage({
              message: error.response.data.msg,
              type: "error",
              duration: 3000
            })
            useUserStore().logout(true)
            return;
          }
          if (error.response.data.msg.indexOf('token无效') > -1) {
            ElMessage({
              message: "token已过期,请重新登录",
              type: "error",
              duration: 3000
            })
            useUserStore().logout(true)
          } else if (error.response.data.msg.indexOf('token已过期') > -1) {
            ElMessage({
              message: "token已过期,请重新登录",
              type: "error",
              duration: 3000
            })
            useUserStore().logout(true)
          } else {
            ElMessage({
              message: error.response.data.msg,
              type: "error",
              duration: 3000
            })
          }
          break
        case 403:
        case 402:
          ElMessage({
            message: "token已过期,请重新登录",
            type: "error",
            duration: 3000
          })
          useUserStore().logout(true)
          break
        case 500:
          if (error.response.data?.msg) {
            ElMessage.error(error.response.data?.msg);
          } else {
            ElMessage({
              message: "服务器错误,请稍后重试",
              type: "error",
              duration: 3000
            })
          }
          break
        default:
          if (error.response.data.error == "invalid_grant") {
            ElMessage({
              message: error.response.data.error_description,
              type: "error"
            })
          }
          return Promise.reject(error)
      }
    }
  }
)

export default service