diff --git a/src/modules/foods/food.module.ts b/src/modules/foods/food.module.ts index f602212..fda3599 100644 --- a/src/modules/foods/food.module.ts +++ b/src/modules/foods/food.module.ts @@ -11,9 +11,10 @@ import { Food } from './entities/food.entity'; import { RestaurantsModule } from '../restaurants/restaurants.module'; import { AuthModule } from '../auth/auth.module'; import { JwtModule } from '@nestjs/jwt'; +import { UtilsModule } from '../utils/utils.module'; @Module({ - imports: [MikroOrmModule.forFeature([Food, Category]), RestaurantsModule, AuthModule, JwtModule], + imports: [MikroOrmModule.forFeature([Food, Category]), RestaurantsModule, AuthModule, JwtModule, UtilsModule], controllers: [FoodController, CategoryController], providers: [FoodService, CategoryService, FoodRepository, CategoryRepository], exports: [FoodRepository, CategoryRepository], diff --git a/src/modules/foods/providers/food.service.ts b/src/modules/foods/providers/food.service.ts index 6e4741f..de49c77 100644 --- a/src/modules/foods/providers/food.service.ts +++ b/src/modules/foods/providers/food.service.ts @@ -8,14 +8,19 @@ import { RequiredEntityData } from '@mikro-orm/core'; import { Food } from '../entities/food.entity'; import { CategoryMessage, FoodMessage, RestMessage } from 'src/common/enums/message.enum'; import { RestRepository } from 'src/modules/restaurants/repositories/rest.repository'; +import { CacheService } from '../../utils/cache.service'; @Injectable() export class FoodService { + private readonly FOODS_BY_RESTAURANT_CACHE_KEY_PREFIX = 'foods:restaurant:'; + private readonly FOODS_CACHE_TTL = 600; // 10 minutes in seconds + constructor( private readonly foodRepository: FoodRepository, private readonly categoryRepository: CategoryRepository, private readonly restRepository: RestRepository, private readonly em: EntityManager, + private readonly cacheService: CacheService, ) {} async create(restId: string, createFoodDto: CreateFoodDto): Promise { @@ -65,6 +70,10 @@ export class FoodService { } await this.em.persistAndFlush(food); + + // Invalidate cache for the restaurant + await this.invalidateRestaurantFoodsCache(restaurant.slug); + return food; } @@ -81,15 +90,37 @@ export class FoodService { } async findAllByRestaurant(slug: string): Promise { + const cacheKey = `${this.FOODS_BY_RESTAURANT_CACHE_KEY_PREFIX}${slug}`; + + // Try to get from cache first + const cachedFoods = await this.cacheService.get(cacheKey); + if (cachedFoods) { + try { + const parsed: unknown = JSON.parse(cachedFoods); + if (Array.isArray(parsed)) { + return parsed as Food[]; + } + } catch { + // If parsing fails, continue to fetch from DB + } + } + + // If not in cache, fetch from database const restaurant = await this.restRepository.findOne({ slug }); if (!restaurant) { throw new NotFoundException(RestMessage.NOT_FOUND); } - return this.foodRepository.find({ restaurant: { slug }, isActive: true }, { populate: ['category'] }); + + const foods = await this.foodRepository.find({ restaurant: { slug }, isActive: true }, { populate: ['category'] }); + + // Cache the result + await this.cacheService.set(cacheKey, JSON.stringify(foods), this.FOODS_CACHE_TTL); + + return foods; } async update(id: string, dto: Partial): Promise { - const food = await this.foodRepository.findOne({ id }); + const food = await this.foodRepository.findOne({ id }, { populate: ['restaurant'] }); if (!food) { throw new NotFoundException(FoodMessage.NOT_FOUND); } @@ -109,15 +140,32 @@ export class FoodService { this.em.assign(food, rest); await this.em.persistAndFlush(food); + + // Invalidate cache for the restaurant + await this.invalidateRestaurantFoodsCache(food.restaurant.slug); + return food; } async remove(id: string) { - const food = await this.foodRepository.findOne({ id }); + const food = await this.foodRepository.findOne({ id }, { populate: ['restaurant'] }); if (!food) { throw new NotFoundException(FoodMessage.NOT_FOUND); } + + const restaurantSlug = food.restaurant.slug; food.deletedAt = new Date(); await this.em.persistAndFlush(food); + + // Invalidate cache for the restaurant + await this.invalidateRestaurantFoodsCache(restaurantSlug); + } + + /** + * Invalidate cache for restaurant foods + */ + private async invalidateRestaurantFoodsCache(slug: string): Promise { + const cacheKey = `${this.FOODS_BY_RESTAURANT_CACHE_KEY_PREFIX}${slug}`; + await this.cacheService.del(cacheKey); } }