request.js 6.32 KB
import host from '@/utils/ip-config.js';
import {
  t
} from '@/plugins/index.js'
import {
  getStageObj
} from '@/utils/index.js'

let requestFlag = true
let isRefreshing = false // 是否正在刷新token
let refreshSubscribers = [] // 重试队列

// 添加订阅
function addSubscriber(callback) {
  refreshSubscribers.push(callback)
}

// 执行订阅的回调
function executeSubscribers(newToken) {
  refreshSubscribers.forEach(callback => callback(newToken))
  refreshSubscribers = []
}

// 刷新token的函数
async function refreshToken(hostUrlIp) {
  if (isRefreshing) {
    // 如果已经在刷新,返回一个Promise等待刷新完成
    return new Promise((resolve) => {
      addSubscriber(resolve)
    })
  }

  isRefreshing = true

  try {
    const rtoken = uni.getStorageSync('rtoken')
    if (!rtoken) {
      throw new Error('No refresh token')
    }
    const userInfoStage = getStageObj('user')
    // 使用原生uni.request,避免使用封装的request导致循环调用
    const refreshResult = await new Promise((resolve, reject) => {
      uni.request({
        url: hostUrlIp + `/report/auth/api/v1/auth/users/${userInfoStage.unid}/atoken`, // 根据你的实际接口调整
        method: 'GET',
        header: {
          'Authorization': rtoken
        },
        success: (res) => {
          if (res.data.atoken) {
            resolve(res.data.atoken)
          } else {
            handleAuthFailure('登录已失效,请重新登录')
            reject(new Error(res.data.msg || '刷新token失败'))
          }
        },
        fail: (err) => {
          reject(err)
        }
      })
    })

    // 存储新的Authorization
    uni.setStorageSync('Authorization', refreshResult)
    isRefreshing = false

    // 执行所有等待的回调
    executeSubscribers(refreshResult)

    return refreshResult
  } catch (error) {
    console.error('刷新token失败:', error)
    isRefreshing = false
    refreshSubscribers = [] // 清空队列
    throw error
  }
}

// 请求封装
export default function request(options, ipType = 'ip') {
  // 根据不同的类型选择不同的ip
  const ipTypeMap = {
    ip: host.ip,
    assetsIp: host.assetsIp
  }
  const url = ipTypeMap[ipType] + options.url
  console.log(url,'=s=s');
  return new Promise((resolve, reject) => {
    const systemInfo = uni.getSystemInfoSync()
    const method = options.method?.toUpperCase() || 'GET';

    const headerContentType = method === 'GET' ? 'application/x-www-form-urlencoded' : 'application/json';

    const _data = {
      ...(options.data || {}),
    }
    const languageObj = {
      'mall_CN': 'zh-CN,zh;q=0.9',
      'zh_CN': 'zh-CN,zh;q=0.9',
      'en_US': 'en-US,en;q=0.5',
      'zh_TW': 'zh-TW,zh;q=0.3',
    }
    const lang = uni.getStorageSync('lang') || host.defaultLang

    const createRequestConfig = (authorization = null) => {
      const authToken = authorization || uni.getStorageSync('Authorization') || ''

      return {
        url,
        data: _data,
        method,
        timeout: 60 * 1000,
        header: {
          'content-type': headerContentType,
          'Device-OS': systemInfo.platform || 'unknown',
          'Device-Model': systemInfo.model || 'unknown',
          'Authorization': authToken,
          'Accept-Language': languageObj[lang] || 'zh-CN,zh;q=0.9',
        },
      }
    }

    // 执行请求
    const executeRequest = (authorization = null) => {
      let setting = createRequestConfig(authorization)

      if (options.header) {
        setting.header = Object.assign(setting.header, options.header);
      }

      uni.request({
        ...setting,
        success: async (res) => {
          // 如果是静态资源,直接返回
          if (ipType === 'assetsIp' || ipType === 'StoreAssetsIp') {
            resolve(res.data);
            return;
          }

          // 处理401状态码
          if (res.data.ecode === 401 && ipType !== 'store.keliuyun') {
            // 如果是刷新token的接口本身返回401,直接跳转登录
            if (options.url === '/report/auth/api') {
              handleAuthFailure(res?.data?.enote || t('Message.authLoginError'));
              return;
            }

            try {
              // 尝试刷新token
              const newToken = await refreshToken(host.ip)
              
              console.log(newToken);
              // 使用新token重试当前请求
              executeRequest(newToken)
            } catch (error) {
              // 刷新token失败,跳转登录
              handleAuthFailure(t('Message.authLoginError'));
            }
            return;
          } else if (res.data.code !== 200) {
            handleRequestError(res, options, reject);
          } else {
            resolve(res.data);
          }
        },
        fail(err) {
          console.log('err', err);
          uni.showToast({
            icon: 'none',
            title: t(err.errMsg || 'app.login.networkError')
          })
          reject(err);
        },
      });
    }


    // 开始第一次请求
    executeRequest();
  })
}

// 处理认证失败
function handleAuthFailure(message) {
  if (requestFlag) {
    requestFlag = false
    uni.showModal({
      title: t('message.prompt'),
      showCancel: false,
      content: message,
      confirmText: t('app.login.reLogin'),
      success: function(resModal) {
        if (resModal.confirm) {
          requestFlag = true
          uni.removeStorageSync('Authorization')
          uni.removeStorageSync('rtoken')
          // uni.clearStorageSync()
          uni.reLaunch({
            url: '/pages/login/index'
          })
        } else if (resModal.cancel) {
          console.log('用户点击取消');
        }
      }
    })
  }
}


// 处理请求错误
function handleRequestError(res, options, reject) {
  const errorMsg = res?.data?.msg || 'Error'
  if (!options.hiddenMsg) {
    uni.showToast({
      icon: 'none',
      title: errorMsg
    })
  }
  // 针对登录接口 单独修改报错返回信息
  if (options.url === '/report/users/login') {
    if (['用户名/密码错误', 'Username/Password Incorrect', '用戶名/密碼錯誤'].includes(errorMsg)) {
      reject('U/P Error')
    }
    // 如果是502 提示默认密码 需要修改 同时传递atoken过去
    if (res.data.code === 502) {
      reject(`P Reset` + '*$*' + `${res?.data?.data?.atoken || ''}`)
    }
  }
  reject(errorMsg)
}