Files
digital-pilates/services/api.ts
richarjiang 00ddec25c5 feat: 集成 Redux 状态管理和用户目标管理功能
- 添加 Redux 状态管理,支持用户登录和个人信息的持久化
- 新增目标管理页面,允许用户设置每日卡路里和步数目标
- 更新首页,移除旧的活动展示,改为固定的热点功能卡片
- 修改布局以适应新功能的展示和交互
- 更新依赖,添加 @reduxjs/toolkit 和 react-redux 库以支持状态管理
- 新增 API 服务模块,处理与后端的交互
2025-08-12 22:22:30 +08:00

89 lines
2.5 KiB
TypeScript

import { buildApiUrl } from '@/constants/Api';
import AsyncStorage from '@react-native-async-storage/async-storage';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
let inMemoryToken: string | null = null;
export async function setAuthToken(token: string | null): Promise<void> {
inMemoryToken = token;
}
export function getAuthToken(): string | null {
return inMemoryToken;
}
export type ApiRequestOptions = {
method?: HttpMethod;
headers?: Record<string, string>;
body?: any;
signal?: AbortSignal;
};
export type ApiResponse<T> = {
data: T;
};
async function doFetch<T>(path: string, options: ApiRequestOptions = {}): Promise<T> {
const url = buildApiUrl(path);
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers || {}),
};
const token = getAuthToken();
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, {
method: options.method ?? 'GET',
headers,
body: options.body != null ? JSON.stringify(options.body) : undefined,
signal: options.signal,
});
const text = await response.text();
let json: any = null;
try {
json = text ? JSON.parse(text) : null;
} catch {
// 非 JSON 响应
}
if (!response.ok) {
const errorMessage = (json && (json.message || json.error)) || `HTTP ${response.status}`;
const error = new Error(errorMessage);
// @ts-expect-error augment
error.status = response.status;
throw error;
}
// 支持后端返回 { data: ... } 或直接返回对象
return (json && (json.data ?? json)) as T;
}
export const api = {
get: <T>(path: string, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'GET' }),
post: <T>(path: string, body?: any, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'POST', body }),
put: <T>(path: string, body?: any, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'PUT', body }),
patch: <T>(path: string, body?: any, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'PATCH', body }),
delete: <T>(path: string, options?: ApiRequestOptions) => doFetch<T>(path, { ...options, method: 'DELETE' }),
};
export const STORAGE_KEYS = {
authToken: '@auth_token',
userProfile: '@user_profile',
} as const;
export async function loadPersistedToken(): Promise<string | null> {
try {
const t = await AsyncStorage.getItem(STORAGE_KEYS.authToken);
return t || null;
} catch {
return null;
}
}