perf
This commit is contained in:
@@ -15,109 +15,9 @@ export class FoodLibraryService {
|
||||
) { }
|
||||
|
||||
/**
|
||||
* 获取食物库列表,按分类组织
|
||||
* 常见食物会被归类到"常见"分类中
|
||||
* 将食物模型转换为DTO
|
||||
*/
|
||||
async getFoodLibrary(): Promise<FoodLibraryResponseDto> {
|
||||
// 获取所有分类,按排序顺序
|
||||
const categories = await this.foodCategoryModel.findAll({
|
||||
order: [['sortOrder', 'ASC']],
|
||||
});
|
||||
|
||||
const result: FoodCategoryDto[] = [];
|
||||
|
||||
for (const category of categories) {
|
||||
let foods: FoodLibrary[] = [];
|
||||
|
||||
if (category.key === 'common') {
|
||||
// 常见分类:获取所有标记为常见的食物
|
||||
foods = await this.foodLibraryModel.findAll({
|
||||
where: { isCommon: true },
|
||||
order: [['sortOrder', 'ASC'], ['name', 'ASC']],
|
||||
});
|
||||
} else {
|
||||
// 其他分类:获取该分类下的非常见食物
|
||||
foods = await this.foodLibraryModel.findAll({
|
||||
where: {
|
||||
categoryKey: category.key,
|
||||
isCommon: false
|
||||
},
|
||||
order: [['sortOrder', 'ASC'], ['name', 'ASC']],
|
||||
});
|
||||
}
|
||||
|
||||
const foodDtos: FoodItemDto[] = foods.map(food => ({
|
||||
id: food.id,
|
||||
name: food.name,
|
||||
description: food.description,
|
||||
caloriesPer100g: food.caloriesPer100g,
|
||||
proteinPer100g: food.proteinPer100g,
|
||||
carbohydratePer100g: food.carbohydratePer100g,
|
||||
fatPer100g: food.fatPer100g,
|
||||
fiberPer100g: food.fiberPer100g,
|
||||
sugarPer100g: food.sugarPer100g,
|
||||
sodiumPer100g: food.sodiumPer100g,
|
||||
additionalNutrition: food.additionalNutrition,
|
||||
isCommon: food.isCommon,
|
||||
imageUrl: food.imageUrl,
|
||||
sortOrder: food.sortOrder,
|
||||
}));
|
||||
|
||||
result.push({
|
||||
key: category.key,
|
||||
name: category.name,
|
||||
icon: category.icon,
|
||||
sortOrder: category.sortOrder,
|
||||
isSystem: category.isSystem,
|
||||
foods: foodDtos,
|
||||
});
|
||||
}
|
||||
|
||||
return { categories: result };
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键词搜索食物
|
||||
*/
|
||||
async searchFoods(keyword: string): Promise<FoodItemDto[]> {
|
||||
const foods = await this.foodLibraryModel.findAll({
|
||||
where: {
|
||||
name: {
|
||||
[Op.like]: `%${keyword}%`
|
||||
}
|
||||
},
|
||||
order: [['isCommon', 'DESC'], ['name', 'ASC']],
|
||||
limit: 50, // 限制搜索结果数量
|
||||
});
|
||||
|
||||
return foods.map(food => ({
|
||||
id: food.id,
|
||||
name: food.name,
|
||||
description: food.description,
|
||||
caloriesPer100g: food.caloriesPer100g,
|
||||
proteinPer100g: food.proteinPer100g,
|
||||
carbohydratePer100g: food.carbohydratePer100g,
|
||||
fatPer100g: food.fatPer100g,
|
||||
fiberPer100g: food.fiberPer100g,
|
||||
sugarPer100g: food.sugarPer100g,
|
||||
sodiumPer100g: food.sodiumPer100g,
|
||||
additionalNutrition: food.additionalNutrition,
|
||||
isCommon: food.isCommon,
|
||||
imageUrl: food.imageUrl,
|
||||
sortOrder: food.sortOrder,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取食物详情
|
||||
*/
|
||||
async getFoodById(id: number): Promise<FoodItemDto | null> {
|
||||
const food = await this.foodLibraryModel.findByPk(id);
|
||||
|
||||
if (!food) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private mapFoodToDto(food: FoodLibrary): FoodItemDto {
|
||||
return {
|
||||
id: food.id,
|
||||
name: food.name,
|
||||
@@ -135,4 +35,112 @@ export class FoodLibraryService {
|
||||
sortOrder: food.sortOrder,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取食物库列表,按分类组织
|
||||
* 常见食物会被归类到"常见"分类中
|
||||
*/
|
||||
async getFoodLibrary(): Promise<FoodLibraryResponseDto> {
|
||||
try {
|
||||
// 一次性获取所有分类和食物数据,避免N+1查询
|
||||
const [categories, allFoods, commonFoods] = await Promise.all([
|
||||
// 获取所有分类
|
||||
this.foodCategoryModel.findAll({
|
||||
order: [['sortOrder', 'ASC']],
|
||||
}),
|
||||
// 获取所有非常见食物
|
||||
this.foodLibraryModel.findAll({
|
||||
where: { isCommon: false },
|
||||
order: [['sortOrder', 'ASC'], ['name', 'ASC']],
|
||||
}),
|
||||
// 获取所有常见食物
|
||||
this.foodLibraryModel.findAll({
|
||||
where: { isCommon: true },
|
||||
order: [['sortOrder', 'ASC'], ['name', 'ASC']],
|
||||
}),
|
||||
]);
|
||||
|
||||
// 将食物按分类分组
|
||||
const foodsByCategory = new Map<string, FoodLibrary[]>();
|
||||
allFoods.forEach(food => {
|
||||
const categoryKey = food.categoryKey;
|
||||
if (!foodsByCategory.has(categoryKey)) {
|
||||
foodsByCategory.set(categoryKey, []);
|
||||
}
|
||||
foodsByCategory.get(categoryKey)!.push(food);
|
||||
});
|
||||
|
||||
// 构建结果
|
||||
const result: FoodCategoryDto[] = categories.map(category => {
|
||||
let foods: FoodLibrary[] = [];
|
||||
|
||||
if (category.key === 'common') {
|
||||
// 常见分类:使用常见食物
|
||||
foods = commonFoods;
|
||||
} else {
|
||||
// 其他分类:使用该分类下的非常见食物
|
||||
foods = foodsByCategory.get(category.key) || [];
|
||||
}
|
||||
|
||||
return {
|
||||
key: category.key,
|
||||
name: category.name,
|
||||
icon: category.icon,
|
||||
sortOrder: category.sortOrder,
|
||||
isSystem: category.isSystem,
|
||||
foods: foods.map(food => this.mapFoodToDto(food)),
|
||||
};
|
||||
});
|
||||
|
||||
return { categories: result };
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get food library: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键词搜索食物
|
||||
*/
|
||||
async searchFoods(keyword: string): Promise<FoodItemDto[]> {
|
||||
try {
|
||||
if (!keyword || keyword.trim().length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const foods = await this.foodLibraryModel.findAll({
|
||||
where: {
|
||||
name: {
|
||||
[Op.like]: `%${keyword.trim()}%`
|
||||
}
|
||||
},
|
||||
order: [['isCommon', 'DESC'], ['name', 'ASC']],
|
||||
limit: 50,
|
||||
});
|
||||
|
||||
return foods.map(food => this.mapFoodToDto(food));
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to search foods: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取食物详情
|
||||
*/
|
||||
async getFoodById(id: number): Promise<FoodItemDto | null> {
|
||||
try {
|
||||
if (!id || id <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const food = await this.foodLibraryModel.findByPk(id);
|
||||
|
||||
if (!food) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.mapFoodToDto(food);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get food by id: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user