114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
/**
|
||
* 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<AppType, ApiConfig> = {
|
||
// 榴莲 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: <actual>, 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')
|