import Taro from '@tarojs/taro' import { hc } from 'hono/client' import ResponsePolyfill from './response-polyfill' // 刷新token的函数 let isRefreshing = false let refreshSubscribers: ((token: string) => void)[] = [] // 执行token刷新 const refreshToken = async (): Promise => { if (isRefreshing) { // 如果已经在刷新,等待结果 return new Promise((resolve) => { refreshSubscribers.push((token) => { resolve(token) }) }) } isRefreshing = true try { const refreshToken = Taro.getStorageSync('enterprise_refresh_token') if (!refreshToken) { throw new Error('未找到刷新token') } // 调用刷新token接口 const response = await Taro.request({ url: `${process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'}/api/v1/yongren/auth/refresh-token`, method: 'POST', header: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${refreshToken}` } }) if (response.statusCode === 200) { const { token, refresh_token: newRefreshToken } = response.data Taro.setStorageSync('enterprise_token', token) if (newRefreshToken) { Taro.setStorageSync('enterprise_refresh_token', newRefreshToken) } // 通知所有等待的请求 refreshSubscribers.forEach(callback => callback(token)) refreshSubscribers = [] return token } else { throw new Error('刷新token失败') } } catch (error) { console.error('刷新token失败:', error) // 清除token,跳转到登录页 Taro.removeStorageSync('enterprise_token') Taro.removeStorageSync('enterprise_refresh_token') Taro.removeStorageSync('enterpriseUserInfo') // 跳转到登录页 Taro.showToast({ title: '登录已过期,请重新登录', icon: 'none' }) setTimeout(() => { Taro.redirectTo({ url: '/pages/login/index' }) }, 1500) return null } finally { isRefreshing = false } } // API配置 const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000' // 完整的API地址 // const BASE_URL = `${API_BASE_URL}/api/${API_VERSION}` // 创建自定义fetch函数,适配Taro.request,支持token自动刷新 const taroFetch: any = async (input, init) => { const url = typeof input === 'string' ? input : input.url const method = init.method || 'GET' const requestHeaders: Record = init.headers; const keyOfContentType = Object.keys(requestHeaders).find(item => item.toLowerCase() === 'content-type') if (!keyOfContentType) { requestHeaders['content-type'] = 'application/json' } // 构建Taro请求选项 const options: Taro.request.Option = { url, method: method as any, data: init.body, header: requestHeaders } // 添加token - 优先使用企业token,兼容mini_token let token = Taro.getStorageSync('enterprise_token') if (!token) { token = Taro.getStorageSync('mini_token') } if (token) { options.header = { ...options.header, 'Authorization': `Bearer ${token}` } } // 发送请求 const sendRequest = async (): Promise => { try { console.log('API请求:', options.url) const response = await Taro.request(options) const responseHeaders = response.header; // 处理204 No Content响应,不设置body const body = response.statusCode === 204 ? null : responseHeaders['content-type']!.includes('application/json') ? JSON.stringify(response.data) : response.data; return new ResponsePolyfill( body, { status: response.statusCode, statusText: response.errMsg || 'OK', headers: responseHeaders } ) } catch (error) { console.error('API Error:', error) throw error } } try { let response = await sendRequest() // 检查是否为401错误,尝试刷新token if (response.status === 401 && token) { console.log('检测到401错误,尝试刷新token...') const newToken = await refreshToken() if (newToken) { // 更新请求header中的token options.header = { ...options.header, 'Authorization': `Bearer ${newToken}` } // 重试原始请求 response = await sendRequest() } else { // 刷新失败,返回原始401响应 return response } } return response } catch (error) { console.error('API请求失败:', error) Taro.showToast({ title: error.message || '网络错误', icon: 'none' }) throw error } } // 创建Hono RPC客户端 export const rpcClient = () => { // @ts-ignore return hc(`${API_BASE_URL}`, { fetch: taroFetch }) }