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 => { try { const response = await apiClient.post>( 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 => { try { const response = await apiClient.get>>( 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 => { try { const response = await apiClient.get>>( 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 => { try { const response = await apiClient.get>>( 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 => { 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>>( 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 => { try { const response = await apiClient.get>( 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 => { 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>( 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 => { try { const response = await apiClient.get< ApiResponse> >(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 => { try { const response = await apiClient.get>( 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 => { try { const params: { q: string; sportId?: number } = { q: query }; if (sportId) { params.sportId = sportId; } const response = await apiClient.get>( 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 => { 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>( 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 => { try { const response = await apiClient.post>( 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 => { try { const response = await apiClient.post>( 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 => { try { const response = await apiClient.get>( 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 => { 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>( 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 => { 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>( 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 => { try { const response = await apiClient.get>( 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 => { try { const response = await apiClient.get>( 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; } };