This commit is contained in:
2025-12-18 12:39:25 +03:30
parent ec56e03907
commit 60c0bfb8d2
6 changed files with 108 additions and 11 deletions
@@ -16,7 +16,9 @@ import {
ApiHeader, ApiHeader,
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
import { RestId } from 'src/common/decorators'; import { RestId, UserId } from 'src/common/decorators';
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
import { OptionalAuthGuard } from 'src/modules/auth/guards/optinalAuth.guard';
@ApiTags('foods') @ApiTags('foods')
@Controller() @Controller()
@@ -39,18 +41,62 @@ export class FoodController {
return this.foodsService.findAllByRestaurant(slug); return this.foodsService.findAllByRestaurant(slug);
} }
@Get('public/foods/:foodId') @Get('public/foods/:foodId')
@UseGuards(OptionalAuthGuard)
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBearerAuth()
@ApiOperation({ summary: 'Get a food by id' }) @ApiOperation({ summary: 'Get a food by id' })
@ApiParam({ name: 'foodId', required: true }) @ApiParam({ name: 'foodId', required: true })
@ApiOkResponse({ description: 'The food', type: CreateFoodDto }) findPublicFoodById(@Param('foodId') foodId: string, @UserId() userId?: string):Promise<any> {
@ApiNotFoundResponse({ description: 'Food not found' }) const a= this.foodsService.findPublicById(foodId, userId);
findPublicFoodById(@Param('foodId') foodId: string) { return a
return this.foodsService.findPublicById(foodId); }
@Post('public/foods/favorite/:foodId')
@UseGuards(AuthGuard)
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBearerAuth()
@ApiOperation({ summary: 'Set a food as favorite' })
@ApiParam({ name: 'foodId', required: true })
setAsFavorute(@Param('foodId') foodId: string, @UserId() userId: string) {
return this.foodsService.addFavorite(userId, foodId);
}
@Delete('public/foods/favorite/:foodId')
@UseGuards(AuthGuard)
@ApiHeader({
name: 'X-Slug',
required: true,
schema: {
type: 'string',
default: 'zhivan',
},
})
@ApiBearerAuth()
@ApiOperation({ summary: 'Remove a food favorite' })
@ApiParam({ name: 'foodId', required: true })
removeFavorite(@Param('foodId') foodId: string, @UserId() userId: string) {
return this.foodsService.removeFavorite(userId, foodId);
} }
@UseGuards(AdminAuthGuard) @UseGuards(AdminAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@Post('admin/foods') @Post('admin/foods')
@ApiOperation({ summary: 'Create a new food' }) @ApiOperation({ summary: 'Create a new food' })
@ApiCreatedResponse({ description: 'The food has been successfully created.', type: CreateFoodDto }) @ApiCreatedResponse({ description: 'The food has been successfully created.', type: CreateFoodDto })
@ApiBody({ type: CreateFoodDto }) @ApiBody({ type: CreateFoodDto })
@@ -0,0 +1,14 @@
import { Entity, ManyToOne, Unique } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity';
import { User } from '../../users/entities/user.entity';
import { Food } from '../../foods/entities/food.entity';
@Entity({ tableName: 'favorites' })
@Unique({ properties: ['user', 'food'] })
export class Favorite extends BaseEntity {
@ManyToOne(() => User)
user: User;
@ManyToOne(() => Food)
food: Food;
}
@@ -5,6 +5,7 @@ import { Restaurant } from '../../../modules/restaurants/entities/restaurant.ent
import { Review } from 'src/modules/review/entities/review.entity'; import { Review } from 'src/modules/review/entities/review.entity';
import { Inventory } from 'src/modules/inventory/entities/inventory.entity'; import { Inventory } from 'src/modules/inventory/entities/inventory.entity';
import { MealType } from '../interface/food.interface'; import { MealType } from '../interface/food.interface';
import { Favorite } from './favorite.entity';
@Entity({ tableName: 'foods' }) @Entity({ tableName: 'foods' })
@Index({ properties: ['restaurant', 'isActive'] }) @Index({ properties: ['restaurant', 'isActive'] })
@@ -26,6 +27,9 @@ export class Food extends BaseEntity {
}) })
inventory?: Inventory; inventory?: Inventory;
@OneToMany(() => Favorite, favorite => favorite.food)
favorites = new Collection<Favorite>(this);
@Property({ nullable: true }) @Property({ nullable: true })
title?: string; title?: string;
+3 -2
View File
@@ -12,11 +12,12 @@ import { RestaurantsModule } from '../restaurants/restaurants.module';
import { AuthModule } from '../auth/auth.module'; import { AuthModule } from '../auth/auth.module';
import { JwtModule } from '@nestjs/jwt'; import { JwtModule } from '@nestjs/jwt';
import { UtilsModule } from '../utils/utils.module'; import { UtilsModule } from '../utils/utils.module';
import { Favorite } from './entities/favorite.entity';
@Module({ @Module({
imports: [MikroOrmModule.forFeature([Food, Category]), RestaurantsModule, AuthModule, JwtModule, UtilsModule], imports: [MikroOrmModule.forFeature([Food, Category, Favorite]), RestaurantsModule, AuthModule, JwtModule, UtilsModule],
controllers: [FoodController, CategoryController], controllers: [FoodController, CategoryController],
providers: [FoodService, CategoryService, FoodRepository, CategoryRepository], providers: [FoodService, CategoryService, FoodRepository, CategoryRepository],
exports: [FoodRepository, CategoryRepository], exports: [FoodRepository, CategoryRepository],
}) })
export class FoodModule {} export class FoodModule { }
+29 -3
View File
@@ -9,6 +9,7 @@ import { Food } from '../entities/food.entity';
import { CategoryMessage, FoodMessage, RestMessage } from 'src/common/enums/message.enum'; import { CategoryMessage, FoodMessage, RestMessage } from 'src/common/enums/message.enum';
import { RestRepository } from 'src/modules/restaurants/repositories/rest.repository'; import { RestRepository } from 'src/modules/restaurants/repositories/rest.repository';
import { CacheService } from '../../utils/cache.service'; import { CacheService } from '../../utils/cache.service';
import { Favorite } from '../entities/favorite.entity';
@Injectable() @Injectable()
export class FoodService { export class FoodService {
@@ -76,10 +77,17 @@ export class FoodService {
/** /**
* Public food detail (only active foods are visible). * Public food detail (only active foods are visible).
*/ */
async findPublicById(id: string): Promise<Food> { async findPublicById(foodId: string, userId?: string):Promise<any> {
const food = await this.foodRepository.findOne({ id, isActive: true }, { populate: ['category'] }); const food = await this.foodRepository.findOne({ id: foodId, isActive: true }, { populate: ['category'] });
if (!food) throw new NotFoundException(FoodMessage.NOT_FOUND); if (!food) throw new NotFoundException(FoodMessage.NOT_FOUND);
return food; let isFavorite = false
if (userId) {
isFavorite = await this.em.count(Favorite, { user: { id: userId }, food: { id: foodId } }) > 0
}
return {
...food,
isFavorite,
};;
} }
/** /**
@@ -87,6 +95,7 @@ export class FoodService {
*/ */
async findAdminById(restId: string, id: string): Promise<Food> { async findAdminById(restId: string, id: string): Promise<Food> {
const food = await this.foodRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['category'] }); const food = await this.foodRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['category'] });
if (!food) throw new NotFoundException(FoodMessage.NOT_FOUND); if (!food) throw new NotFoundException(FoodMessage.NOT_FOUND);
return food; return food;
} }
@@ -164,6 +173,23 @@ export class FoodService {
await this.invalidateRestaurantFoodsCache(restaurantSlug); await this.invalidateRestaurantFoodsCache(restaurantSlug);
} }
async removeFavorite(userId: string, foodId: string) {
return this.em.nativeDelete(Favorite, {
user: userId,
food: foodId,
});
}
async addFavorite(userId: string, foodId: string) {
const favorite = this.em.create(Favorite, {
user: userId,
food: foodId,
});
await this.em.persistAndFlush(favorite);
}
/** /**
* Invalidate cache for restaurant foods * Invalidate cache for restaurant foods
*/ */
+6
View File
@@ -0,0 +1,6 @@
import { Injectable } from "@nestjs/common";
@Injectable()
export class OrdersCrone {
}