From 4c274c31184c0c1357d1e90bed86d06322907cce Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Mon, 10 Nov 2025 23:23:44 +0330 Subject: [PATCH] up --- src/app.module.ts | 2 + .../restaurants/dto/create-restaurant.dto.ts | 97 +++++++++++++++++++ .../restaurants/dto/update-restaurant.dto.ts | 4 + .../restaurants/entities/restaurant.entity.ts | 69 +++++++++++++ .../restaurants/restaurants.controller.ts | 34 +++++++ src/modules/restaurants/restaurants.module.ts | 9 ++ .../restaurants/restaurants.service.ts | 42 ++++++++ 7 files changed, 257 insertions(+) create mode 100644 src/modules/restaurants/dto/create-restaurant.dto.ts create mode 100644 src/modules/restaurants/dto/update-restaurant.dto.ts create mode 100644 src/modules/restaurants/entities/restaurant.entity.ts create mode 100644 src/modules/restaurants/restaurants.controller.ts create mode 100644 src/modules/restaurants/restaurants.module.ts create mode 100644 src/modules/restaurants/restaurants.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index c860a17..6791489 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,6 +14,7 @@ import { UploaderModule } from './modules/uploader/uploader.module'; import { AdminModule } from './modules/admin/admin.module'; // import { CacheService } from './modules/utils/cache.service'; import { ThrottlerModule } from '@nestjs/throttler'; +import { RestaurantsModule } from './modules/restaurants/restaurants.module'; @Module({ imports: [ @@ -41,6 +42,7 @@ import { ThrottlerModule } from '@nestjs/throttler'; limit: 5, // max requests per window }, ]), + RestaurantsModule, ], controllers: [], // providers: [CacheService], diff --git a/src/modules/restaurants/dto/create-restaurant.dto.ts b/src/modules/restaurants/dto/create-restaurant.dto.ts new file mode 100644 index 0000000..2cf8b56 --- /dev/null +++ b/src/modules/restaurants/dto/create-restaurant.dto.ts @@ -0,0 +1,97 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsString, IsOptional, IsNumber, IsBoolean, IsArray, IsUrl } from 'class-validator'; + +export class CreateRestaurantDto { + @ApiProperty({ example: 'Cafe Morteza' }) + @IsString() + name!: string; + + @ApiProperty({ example: 'cafe-morteza' }) + @IsString() + slug!: string; + + @ApiPropertyOptional({ example: 'https://example.com/logo.png' }) + @IsOptional() + @IsUrl() + logo?: string; + + @ApiPropertyOptional({ example: 'Tehran, Valiasr St. No. 123' }) + @IsOptional() + @IsString() + address?: string; + + @ApiPropertyOptional({ example: '#ff6600' }) + @IsOptional() + @IsString() + menuColor?: string; + + @ApiPropertyOptional({ example: 35.6892 }) + @IsOptional() + @IsNumber() + latitude?: number; + + @ApiPropertyOptional({ example: 51.389 }) + @IsOptional() + @IsNumber() + longitude?: number; + + @ApiPropertyOptional({ example: 5000, description: 'Service area in meters' }) + @IsOptional() + @IsNumber() + serviceArea?: number; + + @ApiPropertyOptional({ example: 2020 }) + @IsOptional() + @IsNumber() + establishedYear?: number; + + @ApiPropertyOptional({ example: '+989123456789' }) + @IsOptional() + @IsString() + phoneNumber?: string; + + @ApiPropertyOptional({ example: 'https://instagram.com/cafemorteza' }) + @IsOptional() + @IsUrl() + instagram?: string; + + @ApiPropertyOptional({ example: 'https://t.me/cafemorteza' }) + @IsOptional() + @IsUrl() + telegram?: string; + + @ApiPropertyOptional({ example: 'https://wa.me/989123456789' }) + @IsOptional() + @IsUrl() + whatsapp?: string; + + @ApiPropertyOptional({ example: 'Cozy place with best coffee in town.' }) + @IsOptional() + @IsString() + description?: string; + + @ApiPropertyOptional({ example: 'Best Cafe in Tehran' }) + @IsOptional() + @IsString() + seoTitle?: string; + + @ApiPropertyOptional({ example: 'Cafe Morteza serves high-quality coffee.' }) + @IsOptional() + @IsString() + seoDescription?: string; + + @ApiPropertyOptional({ example: ['coffee', 'breakfast', 'cafe'] }) + @IsOptional() + @IsArray() + tagNames?: string[]; + + @ApiPropertyOptional({ example: true }) + @IsOptional() + @IsBoolean() + isOpenNow?: boolean; + + @ApiPropertyOptional({ example: 0.09 }) + @IsOptional() + @IsNumber() + vat?: number; +} diff --git a/src/modules/restaurants/dto/update-restaurant.dto.ts b/src/modules/restaurants/dto/update-restaurant.dto.ts new file mode 100644 index 0000000..d029d5f --- /dev/null +++ b/src/modules/restaurants/dto/update-restaurant.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateRestaurantDto } from './create-restaurant.dto'; + +export class UpdateRestaurantDto extends PartialType(CreateRestaurantDto) {} diff --git a/src/modules/restaurants/entities/restaurant.entity.ts b/src/modules/restaurants/entities/restaurant.entity.ts new file mode 100644 index 0000000..56c1b7f --- /dev/null +++ b/src/modules/restaurants/entities/restaurant.entity.ts @@ -0,0 +1,69 @@ +import { Entity, Property } from '@mikro-orm/core'; +import { BaseEntity } from '../../../common/entities/base.entity'; + +@Entity({ tableName: 'restaurants' }) +export class Restaurant extends BaseEntity { + // --- اطلاعات پایه --- + @Property() + name!: string; + + @Property({ unique: true }) + slug!: string; + + @Property({ nullable: true }) + logo?: string; + + @Property({ nullable: true }) + address?: string; + + @Property({ nullable: true }) + menuColor?: string; + + // --- مختصات جغرافیایی --- + @Property({ nullable: true }) + latitude?: number; + + @Property({ nullable: true }) + longitude?: number; + + // --- شعاع خدمات (مثلاً بر حسب متر یا کیلومتر) --- + @Property({ nullable: true }) + serviceArea?: number; + + // --- وضعیت‌ها --- + @Property({ default: true }) + isActive: boolean = true; + + @Property({ nullable: true }) + establishedYear?: number; + + @Property({ nullable: true }) + phoneNumber?: string; + + @Property({ nullable: true }) + instagram?: string; + + @Property({ nullable: true }) + telegram?: string; + + @Property({ nullable: true }) + whatsapp?: string; + + // --- توضیحات --- + @Property({ type: 'text', nullable: true }) + description?: string; + + // --- سئو --- + @Property({ nullable: true }) + seoTitle?: string; + + @Property({ nullable: true, type: 'text' }) + seoDescription?: string; + + @Property({ nullable: true, type: 'json' }) + tagNames?: string[]; + + // --- مالیات یا VAT --- + @Property({ type: 'decimal', default: 0 }) + vat?: number = 0; +} diff --git a/src/modules/restaurants/restaurants.controller.ts b/src/modules/restaurants/restaurants.controller.ts new file mode 100644 index 0000000..f9c6a25 --- /dev/null +++ b/src/modules/restaurants/restaurants.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; +import { RestaurantsService } from './restaurants.service'; +import { CreateRestaurantDto } from './dto/create-restaurant.dto'; +import { UpdateRestaurantDto } from './dto/update-restaurant.dto'; + +@Controller('restaurants') +export class RestaurantsController { + constructor(private readonly restaurantsService: RestaurantsService) {} + + @Post() + create(@Body() createRestaurantDto: CreateRestaurantDto) { + return this.restaurantsService.create(createRestaurantDto); + } + + @Get() + findAll() { + return this.restaurantsService.findAll(); + } + + @Get(':slug') + findOne(@Param('slug') slug: string) { + return this.restaurantsService.findBySlug(slug); + } + + @Patch(':id') + update(@Param('id') id: string, @Body() updateRestaurantDto: UpdateRestaurantDto) { + return this.restaurantsService.update(+id, updateRestaurantDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.restaurantsService.remove(+id); + } +} diff --git a/src/modules/restaurants/restaurants.module.ts b/src/modules/restaurants/restaurants.module.ts new file mode 100644 index 0000000..9e2b088 --- /dev/null +++ b/src/modules/restaurants/restaurants.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { RestaurantsService } from './restaurants.service'; +import { RestaurantsController } from './restaurants.controller'; + +@Module({ + controllers: [RestaurantsController], + providers: [RestaurantsService], +}) +export class RestaurantsModule {} diff --git a/src/modules/restaurants/restaurants.service.ts b/src/modules/restaurants/restaurants.service.ts new file mode 100644 index 0000000..6fed433 --- /dev/null +++ b/src/modules/restaurants/restaurants.service.ts @@ -0,0 +1,42 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { CreateRestaurantDto } from './dto/create-restaurant.dto'; +import { UpdateRestaurantDto } from './dto/update-restaurant.dto'; +import { Restaurant } from './entities/restaurant.entity'; +import { EntityManager } from '@mikro-orm/postgresql'; + +@Injectable() +export class RestaurantsService { + constructor(private readonly em: EntityManager) {} + + async create(dto: CreateRestaurantDto): Promise { + const restaurant = this.em.create(Restaurant, { + ...dto, + isActive: true, + }); + + await this.em.persistAndFlush(restaurant); + return restaurant; + } + + findAll() { + return `This action returns all restaurants`; + } + + async findBySlug(slug: string): Promise { + const restaurant = await this.em.findOne(Restaurant, { slug }); + + if (!restaurant) { + throw new NotFoundException(`Restaurant with slug "${slug}" not found`); + } + + return restaurant; + } + + update(id: number, updateRestaurantDto: UpdateRestaurantDto) { + return `This action updates a #${id} restaurant`; + } + + remove(id: number) { + return `This action removes a #${id} restaurant`; + } +}