/** * API客户端模块 - 多后端支持 * * 功能概述: * 为前端提供统一的HTTP客户端,支持连接多个后端服务。 * 当前支持两个应用的版本管理: * - mobile (榴莲 App): 连接 admin-service 后端 * - mining (股行 App): 连接 mining-admin-service 后端 * * 配置方式: * 通过环境变量配置后端地址: * - NEXT_PUBLIC_API_URL: 榴莲App后端地址 * - NEXT_PUBLIC_MINING_API_URL: 股行App后端地址 * * 使用方式: * ```typescript * // 直接使用预创建的客户端 * import { apiClient, miningApiClient } from '@/infrastructure' * * // 或通过工厂函数创建 * const client = createApiClient('mining') * ``` */ import axios, { AxiosInstance, AxiosError } from 'axios' export class ApiError extends Error { constructor( public statusCode: number, message: string, public data?: unknown ) { super(message) this.name = 'ApiError' } } /** * 应用类型 * - mobile: 榴莲 App (使用 admin-service 后端) * - mining: 股行 App (使用 mining-admin-service 后端) */ export type AppType = 'mobile' | 'mining' export interface ApiConfig { baseURL: string apiPrefix: string } /** * 各应用的API配置 * * 注意: * - mobile 使用 /api/v1/versions 前缀 (admin-service) * - mining 使用 /api/v2/upgrade-versions 前缀 (mining-admin-service 公开接口) * - API前缀不同是因为两个后端是独立的服务 */ const APP_CONFIGS: Record = { // 榴莲 App - 连接原有的 admin-service 后端 mobile: { baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3010', apiPrefix: '/api/v1/versions', }, // 股行 App - 连接新的 mining-admin-service 后端 mining: { baseURL: process.env.NEXT_PUBLIC_MINING_API_URL || 'http://localhost:3023', apiPrefix: '/api/v2/upgrade-versions', }, } export function createApiClient(appType: AppType = 'mobile'): AxiosInstance { const config = APP_CONFIGS[appType] const client = axios.create({ baseURL: config.baseURL, timeout: 30000, headers: { 'Content-Type': 'application/json', }, }) client.interceptors.response.use( (response) => { // mining-admin-service 的 TransformInterceptor 会将响应包装为 // { success: true, data: , timestamp: "..." } // 这里自动解包,使前端代码无需感知包装格式 const body = response.data if (body && typeof body === 'object' && !Array.isArray(body) && 'success' in body && 'data' in body) { response.data = body.data } return response }, (error: AxiosError) => { if (error.response) { const data = error.response.data as { message?: string } throw new ApiError( error.response.status, data?.message || error.message, data ) } throw new ApiError(0, error.message) } ) return client } export function getApiPrefix(appType: AppType): string { return APP_CONFIGS[appType].apiPrefix } export const apiClient = createApiClient('mobile') export const miningApiClient = createApiClient('mining')