import { api, loadPersistedToken, setAuthToken, STORAGE_KEYS } from '@/services/api'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; export type Gender = 'male' | 'female' | ''; export type UserProfile = { fullName?: string; email?: string; gender?: Gender; age?: string; // 个人中心是字符串展示 weightKg?: number; heightCm?: number; avatarUri?: string | null; }; export type UserState = { token: string | null; profile: UserProfile; loading: boolean; error: string | null; }; const initialState: UserState = { token: null, profile: { }, loading: false, error: null, }; export type LoginPayload = Record & { // 可扩展:用户名密码、Apple 身份、短信验证码等 username?: string; password?: string; appleIdentityToken?: string; }; export const login = createAsyncThunk( 'user/login', async (payload: LoginPayload, { rejectWithValue }) => { try { // 后端路径允许传入 '/api/login' 或 'login' const data = await api.post<{ token?: string; profile?: UserProfile } | (UserProfile & { token?: string })>( '/api/login', payload, ); // 兼容两种返回结构 const token = (data as any).token ?? (data as any)?.profile?.token ?? null; const profile: UserProfile | null = (data as any).profile ?? (data as any); if (!token) throw new Error('登录响应缺少 token'); await AsyncStorage.setItem(STORAGE_KEYS.authToken, token); await AsyncStorage.setItem(STORAGE_KEYS.userProfile, JSON.stringify(profile ?? {})); await setAuthToken(token); return { token, profile } as { token: string; profile: UserProfile }; } catch (err: any) { const message = err?.message ?? '登录失败'; return rejectWithValue(message); } } ); export const rehydrateUser = createAsyncThunk('user/rehydrate', async () => { const [token, profileStr] = await Promise.all([ loadPersistedToken(), AsyncStorage.getItem(STORAGE_KEYS.userProfile), ]); await setAuthToken(token); let profile: UserProfile = {}; if (profileStr) { try { profile = JSON.parse(profileStr) as UserProfile; } catch { profile = {}; } } return { token, profile } as { token: string | null; profile: UserProfile }; }); export const logout = createAsyncThunk('user/logout', async () => { await Promise.all([ AsyncStorage.removeItem(STORAGE_KEYS.authToken), AsyncStorage.removeItem(STORAGE_KEYS.userProfile), ]); await setAuthToken(null); return true; }); const userSlice = createSlice({ name: 'user', initialState, reducers: { updateProfile(state, action: PayloadAction>) { state.profile = { ...(state.profile ?? {}), ...action.payload }; }, }, extraReducers: (builder) => { builder .addCase(login.pending, (state) => { state.loading = true; state.error = null; }) .addCase(login.fulfilled, (state, action) => { state.loading = false; state.token = action.payload.token; state.profile = action.payload.profile; }) .addCase(login.rejected, (state, action) => { state.loading = false; state.error = (action.payload as string) ?? '登录失败'; }) .addCase(rehydrateUser.fulfilled, (state, action) => { state.token = action.payload.token; state.profile = action.payload.profile; }) .addCase(logout.fulfilled, (state) => { state.token = null; state.profile = {}; }); }, }); export const { updateProfile } = userSlice.actions; export default userSlice.reducer;