Ver Fonte

♻️ refactor(rpc-client): 重构 RPC 客户端实现,简化配置和 API 调用

- 将 `createRpcClient` 函数重命名为 `rpcClient`,并简化其参数和内部实现
- 移除 `RpcClientConfig` 接口和 `TokenRefreshManager` 类,将 token 刷新逻辑内联到 `taroFetch` 函数中
- 将 `createTaroFetch` 函数内联为 `taroFetch`,并移除配置依赖,直接使用硬编码的存储键名和 API 路径
- 简化客户端创建过程,直接返回 `hc` 调用结果,不再需要配置对象和 token 管理器实例

📝 docs(api-clients): 更新各模块的 API 客户端导入和使用方式

- 在 `mini-enterprise-auth-ui` 中,将 `enterpriseAuthClient` 的创建方式从 `createRpcClient` 改为 `rpcClient`
- 在 `yongren-dashboard-ui` 中,移除 `EnterpriseCompanyClientManager` 单例类,直接导出 `enterpriseCompanyClient` 常量
- 在 `yongren-talent-management-ui` 中,移除 `EnterpriseDisabilityClientManager` 单例类,直接导出 `enterpriseDisabilityClient` 常量
- 更新各模块的 `index.ts` 文件,仅导出客户端实例,移除类型导出和单例管理器导出
yourname há 1 mês atrás
pai
commit
00aa0546c0

+ 2 - 2
mini-ui-packages/mini-enterprise-auth-ui/src/api/enterpriseAuthClient.ts

@@ -1,4 +1,4 @@
 import type { enterpriseAuthRoutes } from '@d8d/auth-module';
-import { createRpcClient } from '@d8d/mini-shared-ui-components/utils/rpc/rpc-client';
+import { rpcClient } from '@d8d/mini-shared-ui-components/utils/rpc/rpc-client';
 
-export const enterpriseAuthClient = createRpcClient<typeof enterpriseAuthRoutes>({ apiBaseUrl: '/api/v1/yongren/auth' }); 
+export const enterpriseAuthClient = rpcClient<typeof enterpriseAuthRoutes>('/api/v1/yongren/auth'); 

+ 150 - 190
mini-ui-packages/mini-shared-ui-components/src/utils/rpc/rpc-client.ts

@@ -2,221 +2,181 @@ import Taro from '@tarojs/taro'
 import { hc } from 'hono/client'
 import { ResponsePolyfill } from './response-polyfill'
 
-export interface RpcClientConfig {
-  /** API基础URL,例如: http://localhost:3000 */
-  apiBaseUrl: string
-  /** token存储键名,默认: 'token' */
-  tokenStorageKey?: string
-  /** 刷新token存储键名,默认: 'refresh_token' */
-  refreshTokenStorageKey?: string
-  /** 刷新token的API路径,默认: '/api/v1/auth/refresh-token' */
-  refreshTokenPath?: string
-  /** 登录页面路径,token刷新失败时跳转,默认: '/pages/login/index' */
-  loginPagePath?: string
-  /** 用户信息存储键名,默认: 'userInfo' */
-  userInfoStorageKey?: string
-  /** 认证类型前缀,默认: 'Bearer ' */
-  authPrefix?: string
-}
+// 刷新token的函数
+let isRefreshing = false
+let refreshSubscribers: ((token: string) => void)[] = []
+
+// 执行token刷新
+const refreshToken = async (): Promise<string | null> => {
+  if (isRefreshing) {
+    // 如果已经在刷新,等待结果
+    return new Promise((resolve) => {
+      refreshSubscribers.push((token) => {
+        resolve(token)
+      })
+    })
+  }
 
-// 默认配置
-const defaultConfig: RpcClientConfig = {
-  apiBaseUrl: process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000',
-  tokenStorageKey: 'token',
-  refreshTokenStorageKey: 'refresh_token',
-  refreshTokenPath: '/api/v1/auth/refresh-token',
-  loginPagePath: '/pages/login/index',
-  userInfoStorageKey: 'userInfo',
-  authPrefix: 'Bearer '
-}
+  isRefreshing = true
+  try {
+    const refreshToken = Taro.getStorageSync('enterprise_refresh_token')
+    if (!refreshToken) {
+      throw new Error('未找到刷新token')
+    }
 
-// 刷新token的管理器
-class TokenRefreshManager {
-  private isRefreshing = false
-  private refreshSubscribers: ((token: string | null) => void)[] = []
+    // 调用刷新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}`
+      }
+    })
 
-  constructor(private config: RpcClientConfig) {}
+    if (response.statusCode === 200) {
+      const { token, refresh_token: newRefreshToken } = response.data
+      Taro.setStorageSync('enterprise_token', token)
+      if (newRefreshToken) {
+        Taro.setStorageSync('enterprise_refresh_token', newRefreshToken)
+      }
 
-  async refreshToken(): Promise<string | null> {
-    if (this.isRefreshing) {
-      // 如果已经在刷新,等待结果
-      return new Promise((resolve) => {
-        this.refreshSubscribers.push((token) => {
-          resolve(token)
-        })
-      })
+      // 通知所有等待的请求
+      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)
 
-    this.isRefreshing = true
-    try {
-      const refreshToken = Taro.getStorageSync(this.config.refreshTokenStorageKey!)
-      if (!refreshToken) {
-        throw new Error('未找到刷新token')
-      }
+    return null
+  } finally {
+    isRefreshing = false
+  }
+}
 
-      // 调用刷新token接口
-      const response = await Taro.request({
-        url: `${this.config.apiBaseUrl}${this.config.refreshTokenPath}`,
-        method: 'POST',
-        header: {
-          'Content-Type': 'application/json',
-          'Authorization': `${this.config.authPrefix}${refreshToken}`
-        }
-      })
+// API配置
+const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'
 
-      if (response.statusCode === 200) {
-        const { token, refresh_token: newRefreshToken } = response.data
-        Taro.setStorageSync(this.config.tokenStorageKey!, token)
-        if (newRefreshToken) {
-          Taro.setStorageSync(this.config.refreshTokenStorageKey!, newRefreshToken)
-        }
+// 完整的API地址
+// const BASE_URL = `${API_BASE_URL}/api/${API_VERSION}`
 
-        // 通知所有等待的请求
-        this.refreshSubscribers.forEach(callback => callback(token))
-        this.refreshSubscribers = []
-        return token
-      } else {
-        throw new Error('刷新token失败')
-      }
-    } catch (error) {
-      console.error('刷新token失败:', error)
-      // 清除token,跳转到登录页
-      this.clearAuthData()
-
-      // 跳转到登录页
-      Taro.showToast({
-        title: '登录已过期,请重新登录',
-        icon: 'none'
-      })
-      setTimeout(() => {
-        Taro.redirectTo({
-          url: this.config.loginPagePath!
-        })
-      }, 1500)
-
-      // 通知所有等待的请求刷新失败
-      this.refreshSubscribers.forEach(callback => callback(null))
-      this.refreshSubscribers = []
-      return null
-    } finally {
-      this.isRefreshing = false
-    }
-  }
+// 创建自定义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<string, string> = init.headers;
 
-  clearAuthData(): void {
-    Taro.removeStorageSync(this.config.tokenStorageKey!)
-    Taro.removeStorageSync(this.config.refreshTokenStorageKey!)
-    Taro.removeStorageSync(this.config.userInfoStorageKey!)
+  const keyOfContentType = Object.keys(requestHeaders).find(item => item.toLowerCase() === 'content-type')
+  if (!keyOfContentType) {
+    requestHeaders['content-type'] = 'application/json'
   }
 
-  getToken(): string | null {
-    return Taro.getStorageSync(this.config.tokenStorageKey!)
+  // 构建Taro请求选项
+  const options: Taro.request.Option = {
+    url,
+    method: method as any,
+    data: init.body,
+    header: requestHeaders
   }
-}
 
-// 创建自定义fetch函数,适配Taro.request,支持token自动刷新
-const createTaroFetch = (config: RpcClientConfig, tokenManager: TokenRefreshManager) => {
-  return async (input: RequestInfo | URL, init?: RequestInit): Promise<any> => {
-    const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url
-    const method = init?.method || 'GET'
-    const requestHeaders: Record<string, string> = { ...init?.headers } as Record<string, string>
-
-    // 确保有Content-Type
-    const keyOfContentType = Object.keys(requestHeaders).find(item => item.toLowerCase() === 'content-type')
-    if (!keyOfContentType) {
-      requestHeaders['content-type'] = 'application/json'
+  // 添加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}`
     }
+  }
 
-    // 构建Taro请求选项
-    const options: Taro.request.Option = {
-      url,
-      method: method as any,
-      data: init?.body,
-      header: requestHeaders
+  // 发送请求
+  const sendRequest = async (): Promise<any> => {
+    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
     }
+  }
 
-    // 添加token
-    let token = tokenManager.getToken()
-    if (token) {
-      options.header = {
-        ...options.header,
-        'Authorization': `${config.authPrefix}${token}`
-      }
-    }
+  try {
+    let response = await sendRequest()
 
-    // 发送请求
-    const sendRequest = async (): Promise<any> => {
-      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
-      }
-    }
+    // 检查是否为401错误,尝试刷新token
+    if (response.status === 401 && token) {
+      console.log('检测到401错误,尝试刷新token...')
+      const newToken = await refreshToken()
 
-    try {
-      let response = await sendRequest()
-
-      // 检查是否为401错误,尝试刷新token
-      if (response.status === 401 && token) {
-        console.log('检测到401错误,尝试刷新token...')
-        const newToken = await tokenManager.refreshToken()
-
-        if (newToken) {
-          // 更新请求header中的token
-          options.header = {
-            ...options.header,
-            'Authorization': `${config.authPrefix}${newToken}`
-          }
-
-          // 重试原始请求
-          response = await sendRequest()
-        } else {
-          // 刷新失败,返回原始401响应
-          return response
+      if (newToken) {
+        // 更新请求header中的token
+        options.header = {
+          ...options.header,
+          'Authorization': `Bearer ${newToken}`
         }
-      }
 
-      return response
-    } catch (error: any) {
-      console.error('API请求失败:', error)
-      Taro.showToast({
-        title: error.message || '网络错误',
-        icon: 'none'
-      })
-      throw error
+        // 重试原始请求
+        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 createRpcClient = <T extends any>(customConfig?: Partial<RpcClientConfig>) => {
-  const config = { ...defaultConfig, ...customConfig }
-  const tokenManager = new TokenRefreshManager(config)
-  const fetch = createTaroFetch(config, tokenManager)
-
+export const rpcClient = <T extends any>(apiBasePath?: string) => {
   // @ts-ignore
-  return hc<T>(`${config.apiBaseUrl}`, { fetch })
-}
-
-// 导出类型
-export type RpcClient<T> = ReturnType<typeof createRpcClient<T>>
+  return hc<T>(`${API_BASE_URL}${apiBasePath}`, {
+    fetch: taroFetch
+  })
+}

+ 2 - 42
mini-ui-packages/yongren-dashboard-ui/src/api/enterpriseCompanyClient.ts

@@ -1,44 +1,4 @@
 import { companyEnterpriseRoutes } from '@d8d/allin-company-module';
-import { createRpcClient } from '@d8d/mini-shared-ui-components/utils/rpc/rpc-client';
+import { rpcClient } from '@d8d/mini-shared-ui-components/utils/rpc/rpc-client';
 
-export class EnterpriseCompanyClientManager {
-  private static instance: EnterpriseCompanyClientManager;
-  private client: ReturnType<typeof createRpcClient<typeof companyEnterpriseRoutes>> | null = null;
-
-  private constructor() {}
-
-  public static getInstance(): EnterpriseCompanyClientManager {
-    if (!EnterpriseCompanyClientManager.instance) {
-      EnterpriseCompanyClientManager.instance = new EnterpriseCompanyClientManager();
-    }
-    return EnterpriseCompanyClientManager.instance;
-  }
-
-  // 初始化客户端
-  public init(baseUrl: string = '/'): ReturnType<typeof createRpcClient<typeof companyEnterpriseRoutes>> {
-    return this.client = createRpcClient<typeof companyEnterpriseRoutes>({ apiBaseUrl: baseUrl });
-  }
-
-  // 获取客户端实例
-  public get(): ReturnType<typeof createRpcClient<typeof companyEnterpriseRoutes>> {
-    if (!this.client) {
-      return this.init()
-    }
-    return this.client;
-  }
-
-  // 重置客户端(用于测试或重新初始化)
-  public reset(): void {
-    this.client = null;
-  }
-}
-
-// 导出单例实例
-const enterpriseCompanyClientManager = EnterpriseCompanyClientManager.getInstance();
-
-// 导出默认客户端实例(延迟初始化)
-export const enterpriseCompanyClient = enterpriseCompanyClientManager.get()
-
-export {
-  enterpriseCompanyClientManager
-}
+export const enterpriseCompanyClient = rpcClient<typeof companyEnterpriseRoutes>('/api/v1/yongren/company'); 

+ 1 - 3
mini-ui-packages/yongren-dashboard-ui/src/api/index.ts

@@ -1,3 +1 @@
-export { enterpriseCompanyClient, enterpriseCompanyClientManager } from './enterpriseCompanyClient';
-import { companyEnterpriseRoutes } from '@d8d/allin-company-module';
-export type EnterpriseCompanyRoutes = typeof companyEnterpriseRoutes;
+export { enterpriseCompanyClient } from './enterpriseCompanyClient';

+ 2 - 42
mini-ui-packages/yongren-talent-management-ui/src/api/enterpriseDisabilityClient.ts

@@ -1,44 +1,4 @@
 import { personExtensionRoutes } from '@d8d/allin-disability-module';
-import { createRpcClient } from '@d8d/mini-shared-ui-components/utils/rpc/rpc-client';
+import { rpcClient } from '@d8d/mini-shared-ui-components/utils/rpc/rpc-client';
 
-export class EnterpriseDisabilityClientManager {
-  private static instance: EnterpriseDisabilityClientManager;
-  private client: ReturnType<typeof createRpcClient<typeof personExtensionRoutes>> | null = null;
-
-  private constructor() {}
-
-  public static getInstance(): EnterpriseDisabilityClientManager {
-    if (!EnterpriseDisabilityClientManager.instance) {
-      EnterpriseDisabilityClientManager.instance = new EnterpriseDisabilityClientManager();
-    }
-    return EnterpriseDisabilityClientManager.instance;
-  }
-
-  // 初始化客户端
-  public init(baseUrl: string = '/'): ReturnType<typeof createRpcClient<typeof personExtensionRoutes>> {
-    return this.client = createRpcClient<typeof personExtensionRoutes>({ apiBaseUrl: baseUrl });
-  }
-
-  // 获取客户端实例
-  public get(): ReturnType<typeof createRpcClient<typeof personExtensionRoutes>> {
-    if (!this.client) {
-      return this.init()
-    }
-    return this.client;
-  }
-
-  // 重置客户端(用于测试或重新初始化)
-  public reset(): void {
-    this.client = null;
-  }
-}
-
-// 导出单例实例
-const enterpriseDisabilityClientManager = EnterpriseDisabilityClientManager.getInstance();
-
-// 导出默认客户端实例(延迟初始化)
-export const enterpriseDisabilityClient = enterpriseDisabilityClientManager.get()
-
-export {
-  enterpriseDisabilityClientManager
-}
+export const enterpriseDisabilityClient = rpcClient<typeof personExtensionRoutes>('/api/v1/yongren/disability-person'); 

+ 1 - 3
mini-ui-packages/yongren-talent-management-ui/src/api/index.ts

@@ -1,3 +1 @@
-export { enterpriseDisabilityClient, enterpriseDisabilityClientManager } from './enterpriseDisabilityClient';
-import { personExtensionRoutes } from '@d8d/allin-disability-module';
-export type EnterpriseDisabilityRoutes = typeof personExtensionRoutes;
+export { enterpriseDisabilityClient } from './enterpriseDisabilityClient';