From 9cdd7ef28e035e2d915efd60c67ff32fb5293717 Mon Sep 17 00:00:00 2001 From: Matin Date: Mon, 17 Feb 2025 11:27:40 +0330 Subject: [PATCH] chore: get discount data with all relation --- src/common/enums/message.enum.ts | 8 +++ .../address/DTO/cities-search-query.dto.ts | 12 ++++ .../address/DTO/provinces-search-query.dto.ts | 12 ++++ src/modules/address/address.controller.ts | 21 ++++++- .../address/providers/address.service.ts | 50 +++++++++++++++- .../DTO/discount-search-query.dto.ts | 12 ++++ src/modules/discounts/discounts.controller.ts | 11 ++-- .../discounts/providers/discounts.service.ts | 60 +++++++++++++++---- 8 files changed, 168 insertions(+), 18 deletions(-) create mode 100644 src/modules/address/DTO/cities-search-query.dto.ts create mode 100644 src/modules/address/DTO/provinces-search-query.dto.ts create mode 100644 src/modules/discounts/DTO/discount-search-query.dto.ts diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 339a183..1d895d1 100644 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -414,3 +414,11 @@ export const enum AdsMessage { ADS_ACTIVATED = "تبلیغ با موفقیت فعال شد", ADS_DEACTIVATED = "تبلیغ با موفقیت غیرفعال شد", } + +export const enum ProvinceMessage { + SEARCH_QUERY_MUST_BE_A_STRING = "رشته جستجو باید یک رشته باشد", +} + +export const enum CityMessage { + SEARCH_QUERY_MUST_BE_A_STRING = "رشته جستجو باید یک رشته باشد", +} diff --git a/src/modules/address/DTO/cities-search-query.dto.ts b/src/modules/address/DTO/cities-search-query.dto.ts new file mode 100644 index 0000000..a7505f4 --- /dev/null +++ b/src/modules/address/DTO/cities-search-query.dto.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { IsOptional, IsString } from "class-validator"; + +import { PaginationDto } from "../../../common/DTO/pagination.dto"; +import { CityMessage } from "../../../common/enums/message.enum"; + +export class SearchCitiesDto extends PaginationDto { + @IsOptional() + @IsString({ message: CityMessage.SEARCH_QUERY_MUST_BE_A_STRING }) + @ApiPropertyOptional({ description: "search query", example: "search query" }) + q: string; +} diff --git a/src/modules/address/DTO/provinces-search-query.dto.ts b/src/modules/address/DTO/provinces-search-query.dto.ts new file mode 100644 index 0000000..69d3260 --- /dev/null +++ b/src/modules/address/DTO/provinces-search-query.dto.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { IsOptional, IsString } from "class-validator"; + +import { PaginationDto } from "../../../common/DTO/pagination.dto"; +import { ProvinceMessage } from "../../../common/enums/message.enum"; + +export class SearchProvincesDto extends PaginationDto { + @IsOptional() + @IsString({ message: ProvinceMessage.SEARCH_QUERY_MUST_BE_A_STRING }) + @ApiPropertyOptional({ description: "search query", example: "search query" }) + q: string; +} diff --git a/src/modules/address/address.controller.ts b/src/modules/address/address.controller.ts index 6bc8d2f..9b1b79c 100644 --- a/src/modules/address/address.controller.ts +++ b/src/modules/address/address.controller.ts @@ -1,6 +1,23 @@ -import { Controller } from "@nestjs/common"; +import { Controller, Get, Query } from "@nestjs/common"; +import { ApiOperation } from "@nestjs/swagger"; + +import { SearchCitiesDto } from "./DTO/cities-search-query.dto"; +import { SearchProvincesDto } from "./DTO/provinces-search-query.dto"; +import { AddressService } from "./providers/address.service"; @Controller("address") export class AddressController { - constructor() {} + constructor(private readonly addressService: AddressService) {} + + @ApiOperation({ summary: "Get all cities" }) + @Get("cities/list") + getCitiesList(@Query() queryDto: SearchCitiesDto) { + return this.addressService.getAllCities(queryDto); + } + + @ApiOperation({ summary: "Get all provinces" }) + @Get("provinces/list") + getProvincesList(@Query() queryDto: SearchProvincesDto) { + return this.addressService.getAllProvinces(queryDto); + } } diff --git a/src/modules/address/providers/address.service.ts b/src/modules/address/providers/address.service.ts index 5fd58c2..eda58a0 100644 --- a/src/modules/address/providers/address.service.ts +++ b/src/modules/address/providers/address.service.ts @@ -1,6 +1,54 @@ import { Injectable } from "@nestjs/common"; +import { PaginationUtils } from "../../utils/providers/pagination.utils"; +import { SearchCitiesDto } from "../DTO/cities-search-query.dto"; +import { CityRepository } from "../repositories/city.repository"; +import { ProvinceRepository } from "../repositories/province.repository"; + @Injectable() export class AddressService { - constructor() {} + constructor( + private readonly cityRepository: CityRepository, + private readonly provinceRepository: ProvinceRepository, + ) {} + + async getAllCities(queryDto: SearchCitiesDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.cityRepository.createQueryBuilder("city"); + + if (queryDto.q) { + queryBuilder.andWhere("city.name ILIKE :q", { q: `%${queryDto.q}%` }); + } + + queryBuilder.skip(skip).take(limit); + + const [cities, count] = await queryBuilder.getManyAndCount(); + + return { + cities, + count, + paginate: true, + }; + } + + async getAllProvinces(queryDto: SearchCitiesDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.provinceRepository.createQueryBuilder("province"); + + if (queryDto.q) { + queryBuilder.andWhere("province.name ILIKE :q", { q: `%${queryDto.q}%` }); + } + + queryBuilder.skip(skip).take(limit); + + const [provinces, count] = await queryBuilder.getManyAndCount(); + + return { + provinces, + count, + paginate: true, + }; + } } diff --git a/src/modules/discounts/DTO/discount-search-query.dto.ts b/src/modules/discounts/DTO/discount-search-query.dto.ts new file mode 100644 index 0000000..f1a1b50 --- /dev/null +++ b/src/modules/discounts/DTO/discount-search-query.dto.ts @@ -0,0 +1,12 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { IsOptional, IsString } from "class-validator"; + +import { PaginationDto } from "../../../common/DTO/pagination.dto"; +import { FinancialMessage } from "../../../common/enums/message.enum"; + +export class SearchDiscountsDto extends PaginationDto { + @IsOptional() + @IsString({ message: FinancialMessage.SEARCH_QUERY_MUST_BE_A_STRING }) + @ApiPropertyOptional({ description: "search query", example: "search query" }) + q: string; +} diff --git a/src/modules/discounts/discounts.controller.ts b/src/modules/discounts/discounts.controller.ts index d6ceca0..fbf6e7b 100644 --- a/src/modules/discounts/discounts.controller.ts +++ b/src/modules/discounts/discounts.controller.ts @@ -1,7 +1,8 @@ -import { Body, Controller, Get, Param, Patch, Post } from "@nestjs/common"; +import { Body, Controller, Get, Param, Patch, Post, Query } from "@nestjs/common"; import { ApiOperation, ApiTags } from "@nestjs/swagger"; import { CreateDiscountDto } from "./DTO/create-discount.dto"; +import { SearchDiscountsDto } from "./DTO/discount-search-query.dto"; import { DiscountService } from "./providers/discounts.service"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { Roles } from "../../common/decorators/roles.decorator"; @@ -29,8 +30,8 @@ export class DiscountController { @ApiOperation({ summary: "Retrieve all discounts" }) @Roles(RoleEnum.ADMIN) @Get() - async findAll() { - return this.discountService.findAll(); + async findAll(@Query() queryDto: SearchDiscountsDto) { + return this.discountService.findAll(queryDto); } //************************************ */ @@ -46,8 +47,8 @@ export class DiscountController { @ApiOperation({ summary: "Retrieve a discounts by userID" }) @Get("/user-discounts") - async findDiscountsByUsers(@UserDec("id") userId: string) { - return this.discountService.findDiscountsByUser(userId); + async findDiscountsByUsers(@Query() queryDto: SearchDiscountsDto, @UserDec("id") userId: string) { + return this.discountService.findDiscountsByUser(queryDto, userId); } //************************************ */ diff --git a/src/modules/discounts/providers/discounts.service.ts b/src/modules/discounts/providers/discounts.service.ts index e3cbda0..1b002df 100644 --- a/src/modules/discounts/providers/discounts.service.ts +++ b/src/modules/discounts/providers/discounts.service.ts @@ -6,7 +6,9 @@ import { In } from "typeorm"; import { CommonMessage, DiscountMessage, SubscriptionMessage, UserMessage } from "../../../common/enums/message.enum"; import { SubscriptionsPlanRepository } from "../../subscriptions/repositories/subscriptions.repository"; import { UserRepository } from "../../users/repositories/users.repository"; +import { PaginationUtils } from "../../utils/providers/pagination.utils"; import { CreateDiscountDto } from "../DTO/create-discount.dto"; +import { SearchDiscountsDto } from "../DTO/discount-search-query.dto"; import { DiscountRepository } from "../repositories/discount.repository"; @Injectable() @@ -74,26 +76,64 @@ export class DiscountService { /******************************************** */ - async findDiscountsByUser(userId: string) { + async findDiscountsByUser(queryDto: SearchDiscountsDto, userId: string) { const user = await this.userRepository.findOneBy({ id: userId, }); if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); - const discounts = await this.discountRepository.find({ - where: { - users: { id: user.id }, - }, - }); + const { limit, skip } = PaginationUtils(queryDto); - return { discounts }; + const queryBuilder = this.discountRepository.createQueryBuilder("discount"); + + queryBuilder + .leftJoin("discount.users", "user") + .where("user.id = :userId", { userId: user.id }) + .leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans") + .leftJoin("subscriptionPlans.service", "service") + .addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"]); + + if (queryDto.q) { + queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` }); + } + + queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit); + + const [discounts, count] = await queryBuilder.getManyAndCount(); + + return { + discounts, + count, + paginate: true, + }; } /******************************************** */ - async findAll() { - const discounts = await this.discountRepository.find({ relations: ["subscriptionPlans"] }); - return { discounts }; + async findAll(queryDto: SearchDiscountsDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.discountRepository.createQueryBuilder("discount"); + + queryBuilder + .leftJoinAndSelect("discount.subscriptionPlans", "subscriptionPlans") + .leftJoin("subscriptionPlans.service", "service") + .addSelect(["service.id", "service.name", "service.title", "service.link", "service.icon", "service.description"]) + .leftJoinAndSelect("discount.users", "users"); + + if (queryDto.q) { + queryBuilder.andWhere("discount.code ILIKE :q", { q: `%${queryDto.q}%` }); + } + + queryBuilder.orderBy("discount.createdAt", "DESC").skip(skip).take(limit); + + const [discounts, count] = await queryBuilder.getManyAndCount(); + + return { + discounts, + count, + paginate: true, + }; } /******************************************** */