Files
physical-expo/lib/api.ts

527 lines
12 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.
import { API_CONFIG, API_ENDPOINTS } from "@/constants/api";
import {
ApiListResponse,
ApiResponse,
AppleSignInRequest,
AppleSignInResponse,
Country,
FavoriteCheckResponse,
FavoriteListResponse,
FavoriteRequest,
H2HData,
League,
LiveScoreMatch,
Match,
MatchDetailData,
OddsData,
RefreshTokenRequest,
RefreshTokenResponse,
SearchResult,
Sport,
UpcomingMatch,
UserProfile,
} from "@/types/api";
import axios from "axios";
import { storage } from "./storage";
const apiClient = axios.create({
baseURL: API_CONFIG.BASE_URL,
timeout: API_CONFIG.TIMEOUT,
headers: {
"Content-Type": "application/json",
},
});
apiClient.interceptors.request.use(async (config) => {
const token = await storage.getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
const refreshTokenApi = async (
request: RefreshTokenRequest
): Promise<RefreshTokenResponse> => {
try {
const response = await apiClient.post<ApiResponse<RefreshTokenResponse>>(
API_ENDPOINTS.REFRESH_TOKEN,
request
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Refresh token error:", error);
throw error;
}
};
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshTokenValue = await storage.getRefreshToken();
if (refreshTokenValue) {
const res = await refreshTokenApi({
refreshToken: refreshTokenValue,
});
await storage.setAccessToken(res.accessToken);
originalRequest.headers.Authorization = `Bearer ${res.accessToken}`;
return apiClient(originalRequest);
}
} catch {
await storage.clear();
}
}
return Promise.reject(error);
}
);
export const fetchSports = async (): Promise<Sport[]> => {
try {
const response = await apiClient.get<ApiResponse<ApiListResponse<Sport>>>(
API_ENDPOINTS.SPORTS
);
if (response.data.code === 0) {
return response.data.data.list;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch sports error:", error);
// Do not return mock data here — rethrow to let caller handle the error
throw error;
}
};
export const fetchCountries = async (): Promise<Country[]> => {
try {
const response = await apiClient.get<ApiResponse<ApiListResponse<Country>>>(
API_ENDPOINTS.COUNTRIES
);
if (response.data.code === 0) {
return response.data.data.list;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch countries error:", error);
throw error;
}
};
export const fetchLeagues = async (
sportId: number,
countryKey: string
): Promise<League[]> => {
try {
const response = await apiClient.get<ApiResponse<ApiListResponse<League>>>(
API_ENDPOINTS.LEAGUES,
{
params: {
sportId,
countryKey,
},
}
);
if (response.data.code === 0) {
return response.data.data.list;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch leagues error:", error);
throw error;
}
};
export const fetchTodayMatches = async (
sportId: number,
date?: Date | string,
timezone?: string
): Promise<Match[]> => {
try {
const params: { sportId: number; date?: string; timezone?: string } = {
sportId,
};
// 如果提供了日期,格式化为 YYYY-MM-DD 格式
if (date) {
let dateStr: string;
if (date instanceof Date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
dateStr = `${year}-${month}-${day}`;
} else {
dateStr = date;
}
params.date = dateStr;
}
// 如果提供了时区,传给后端;不传则由后端使用本地时区
if (timezone) {
params.timezone = timezone;
}
const response = await apiClient.get<ApiResponse<ApiListResponse<Match>>>(
API_ENDPOINTS.MATCHES_TODAY,
{
params,
}
);
if (response.data.code === 0) {
return response.data.data.list;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch matches error:", error);
// Let the caller handle errors; rethrow the original error
throw error;
}
};
export const fetchMatchDetail = async (
id: string
): Promise<MatchDetailData> => {
try {
const response = await apiClient.get<ApiResponse<MatchDetailData>>(
API_ENDPOINTS.MATCH_DETAIL(id)
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch match detail error:", error);
throw error;
}
};
export const fetchLiveScore = async (
sportId: number,
leagueId?: number,
timezone?: string
): Promise<LiveScoreMatch[]> => {
console.log("Fetching live scores with params:", {
sportId,
leagueId,
timezone,
});
try {
const params: { sport_id: number; league_id?: number; timezone?: string } =
{
sport_id: sportId,
};
if (leagueId) {
params.league_id = leagueId;
}
if (timezone) {
params.timezone = timezone;
}
const response = await apiClient.get<ApiResponse<LiveScoreMatch[]>>(
API_ENDPOINTS.LIVESCORE,
{ params }
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch livescore error:", error);
throw error;
}
};
export const fetchUpcomingMatches = async (
sportId: number,
leagueKey: string,
limit: number = 50
): Promise<UpcomingMatch[]> => {
try {
const response = await apiClient.get<
ApiResponse<ApiListResponse<UpcomingMatch>>
>(API_ENDPOINTS.UPCOMING_MATCHES, {
params: {
sport_id: sportId,
leagueKey,
limit,
},
});
if (response.data.code === 0) {
return response.data.data.list;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch upcoming matches error:", error);
throw error;
}
};
// 获取实时赔率(足球/网球使用 LiveOdds篮球/板球使用 Odds
export const fetchOdds = async (
sportId: number,
matchId: number
): Promise<OddsData> => {
try {
const response = await apiClient.get<ApiResponse<OddsData>>(
API_ENDPOINTS.ODDS,
{
params: {
sport_id: sportId,
match_id: matchId,
},
}
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch odds error:", error);
throw error;
}
};
// 搜索联赛、球队或球员
export const fetchSearch = async (
query: string,
sportId?: number
): Promise<SearchResult> => {
try {
const params: { q: string; sportId?: number } = { q: query };
if (sportId) {
params.sportId = sportId;
}
const response = await apiClient.get<ApiResponse<SearchResult>>(
API_ENDPOINTS.SEARCH,
{ params }
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch search error:", error);
throw error;
}
};
// 获取两队/两球员之间的历史对战数据
export const fetchH2H = async (
sportId: number,
options: {
firstTeamId?: number;
secondTeamId?: number;
firstPlayerId?: number;
secondPlayerId?: number;
timezone?: string;
}
): Promise<H2HData> => {
try {
const params: {
sport_id: number;
first_team_id?: number;
second_team_id?: number;
first_player_id?: number;
second_player_id?: number;
timezone?: string;
} = {
sport_id: sportId,
};
if (options.firstTeamId) {
params.first_team_id = options.firstTeamId;
}
if (options.secondTeamId) {
params.second_team_id = options.secondTeamId;
}
if (options.firstPlayerId) {
params.first_player_id = options.firstPlayerId;
}
if (options.secondPlayerId) {
params.second_player_id = options.secondPlayerId;
}
if (options.timezone) {
params.timezone = options.timezone;
}
const response = await apiClient.get<ApiResponse<H2HData>>(
API_ENDPOINTS.H2H,
{ params }
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch H2H error:", error);
throw error;
}
};
export const appleSignIn = async (
request: AppleSignInRequest
): Promise<AppleSignInResponse> => {
try {
const response = await apiClient.post<ApiResponse<AppleSignInResponse>>(
API_ENDPOINTS.APPLE_SIGNIN,
request
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Apple sign in error:", error);
throw error;
}
};
export const logout = async (): Promise<string> => {
try {
const response = await apiClient.post<ApiResponse<string>>(
API_ENDPOINTS.LOGOUT
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Logout error:", error);
throw error;
}
};
export const refreshToken = refreshTokenApi;
export const fetchUserProfile = async (): Promise<UserProfile> => {
try {
const response = await apiClient.get<ApiResponse<UserProfile>>(
API_ENDPOINTS.USER_PROFILE
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch user profile error:", error);
throw error;
}
};
export const addFavorite = async (request: FavoriteRequest): Promise<any> => {
try {
const token = await storage.getAccessToken();
if (!token) {
// throw new Error("Please login first");
return;
}
// console.log("Adding favorite with request:", request);
const response = await apiClient.post<ApiResponse<any>>(
API_ENDPOINTS.FAVORITES,
request
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Add favorite error:", error);
throw error;
}
};
export const removeFavorite = async (request: {
type: string;
typeId: string;
}): Promise<any> => {
try {
const token = await storage.getAccessToken();
if (!token) {
// throw new Error("Please login first");
return;
}
console.log("Removing favorite with request:", request);
const response = await apiClient.delete<ApiResponse<any>>(
API_ENDPOINTS.FAVORITES,
{ data: request }
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Remove favorite error:", error);
throw error;
}
};
export const checkFavorite = async (
type: string,
typeId: string
): Promise<FavoriteCheckResponse> => {
try {
const response = await apiClient.get<ApiResponse<FavoriteCheckResponse>>(
API_ENDPOINTS.CHECK_FAVORITE,
{
params: { type, typeId },
}
);
if (response.data.code === 0) {
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Check favorite error:", error);
throw error;
}
};
export const fetchFavorites = async (
type: string,
page: number = 1,
pageSize: number = 20
): Promise<FavoriteListResponse> => {
try {
const response = await apiClient.get<ApiResponse<FavoriteListResponse>>(
API_ENDPOINTS.FAVORITES,
{
params: { type, page, pageSize },
}
);
if (response.data.code === 0) {
console.log("Fetched favorites:", JSON.stringify(response.data.data));
return response.data.data;
}
throw new Error(response.data.message);
} catch (error) {
console.error("Fetch favorites error:", error);
throw error;
}
};