rwadurian/frontend/mobile-upgrade/src/infrastructure/http/api-client.ts

114 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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')