import { foodLibraryApi } from '@/services/foodLibraryApi'; import type { FoodCategory, FoodCategoryDto, FoodItem, FoodItemDto, FoodLibraryState } from '@/types/food'; import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; // 数据转换工具函数 const transformFoodItemDto = (dto: FoodItemDto): FoodItem => ({ id: dto.id.toString(), name: dto.name, emoji: '🍽️', // 默认 emoji,可以根据分类或其他逻辑设置 calories: dto.caloriesPer100g || 0, unit: '100克', description: dto.description, protein: dto.proteinPer100g, carbohydrate: dto.carbohydratePer100g, fat: dto.fatPer100g, fiber: dto.fiberPer100g, sugar: dto.sugarPer100g, sodium: dto.sodiumPer100g, additionalNutrition: dto.additionalNutrition, imageUrl: dto.imageUrl, }); const transformFoodCategoryDto = (dto: FoodCategoryDto): FoodCategory => ({ id: dto.key, name: dto.name, foods: dto.foods.map(transformFoodItemDto), icon: dto.icon, sortOrder: dto.sortOrder, isSystem: dto.isSystem, }); // 异步 thunks export const fetchFoodLibrary = createAsyncThunk( 'foodLibrary/fetchFoodLibrary', async (_, { rejectWithValue }) => { try { const response = await foodLibraryApi.getFoodLibrary(); return response; } catch (error: any) { return rejectWithValue(error.message || '获取食物库失败'); } } ); export const searchFoods = createAsyncThunk( 'foodLibrary/searchFoods', async (keyword: string, { rejectWithValue }) => { try { const response = await foodLibraryApi.searchFoods(keyword); return response; } catch (error: any) { return rejectWithValue(error.message || '搜索食物失败'); } } ); export const getFoodById = createAsyncThunk( 'foodLibrary/getFoodById', async (id: number, { rejectWithValue }) => { try { const response = await foodLibraryApi.getFoodById(id); return response; } catch (error: any) { return rejectWithValue(error.message || '获取食物详情失败'); } } ); // 初始状态 const initialState: FoodLibraryState = { categories: [], loading: false, error: null, searchResults: [], searchLoading: false, lastUpdated: null, }; // 创建 slice const foodLibrarySlice = createSlice({ name: 'foodLibrary', initialState, reducers: { // 清除错误 clearError: (state) => { state.error = null; }, // 清除搜索结果 clearSearchResults: (state) => { state.searchResults = []; }, // 添加自定义食物到指定分类 addCustomFood: (state, action: PayloadAction<{ categoryId: string; food: FoodItem }>) => { const { categoryId, food } = action.payload; const category = state.categories.find(cat => cat.id === categoryId); if (category) { category.foods.push(food); } }, // 从指定分类移除食物 removeFoodFromCategory: (state, action: PayloadAction<{ categoryId: string; foodId: string }>) => { const { categoryId, foodId } = action.payload; const category = state.categories.find(cat => cat.id === categoryId); if (category) { category.foods = category.foods.filter(food => food.id !== foodId); } }, // 添加食物到收藏 addToFavorites: (state, action: PayloadAction) => { const favoriteCategory = state.categories.find(cat => cat.id === 'favorite'); if (favoriteCategory) { // 检查是否已存在 const exists = favoriteCategory.foods.some(food => food.id === action.payload.id); if (!exists) { favoriteCategory.foods.push(action.payload); } } }, // 从收藏移除食物 removeFromFavorites: (state, action: PayloadAction) => { const favoriteCategory = state.categories.find(cat => cat.id === 'favorite'); if (favoriteCategory) { favoriteCategory.foods = favoriteCategory.foods.filter(food => food.id !== action.payload); } }, }, extraReducers: (builder) => { // 获取食物库 builder .addCase(fetchFoodLibrary.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchFoodLibrary.fulfilled, (state, action) => { state.loading = false; state.categories = action.payload.categories.map(transformFoodCategoryDto); state.lastUpdated = Date.now(); state.error = null; }) .addCase(fetchFoodLibrary.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }); // 搜索食物 builder .addCase(searchFoods.pending, (state) => { state.searchLoading = true; state.error = null; }) .addCase(searchFoods.fulfilled, (state, action) => { state.searchLoading = false; state.searchResults = action.payload.map(transformFoodItemDto); state.error = null; }) .addCase(searchFoods.rejected, (state, action) => { state.searchLoading = false; state.error = action.payload as string; state.searchResults = []; }); // 获取食物详情 builder .addCase(getFoodById.pending, (state) => { state.loading = true; state.error = null; }) .addCase(getFoodById.fulfilled, (state, action) => { state.loading = false; state.error = null; // 可以在这里处理获取到的食物详情,比如更新缓存等 }) .addCase(getFoodById.rejected, (state, action) => { state.loading = false; state.error = action.payload as string; }); }, }); // 导出 actions export const { clearError, clearSearchResults, addCustomFood, removeFoodFromCategory, addToFavorites, removeFromFavorites, } = foodLibrarySlice.actions; // 选择器 export const selectFoodLibrary = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary; export const selectFoodCategories = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.categories; export const selectFoodLibraryLoading = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.loading; export const selectFoodLibraryError = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.error; export const selectSearchResults = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.searchResults; export const selectSearchLoading = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.searchLoading; // 复合选择器 export const selectFoodCategoryById = (categoryId: string) => (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.categories.find(cat => cat.id === categoryId); export const selectFoodById = (foodId: string) => (state: { foodLibrary: FoodLibraryState }) => { for (const category of state.foodLibrary.categories) { const food = category.foods.find(f => f.id === foodId); if (food) return food; } return null; }; export const selectFavoritesFoods = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.categories.find(cat => cat.id === 'favorite')?.foods || []; export const selectCommonFoods = (state: { foodLibrary: FoodLibraryState }) => state.foodLibrary.categories.find(cat => cat.id === 'common')?.foods || []; // 导出 reducer export default foodLibrarySlice.reducer;