From 4d0344c37632685919b3330f45f685ef1c525eed Mon Sep 17 00:00:00 2001 From: mahyargdz Date: Sun, 2 Mar 2025 15:19:55 +0330 Subject: [PATCH] update: change the danak services fetch to another route and accept category id " query for that --- src/common/DTO/param.dto.ts | 4 ++- src/common/enums/message.enum.ts | 5 +++ .../DTO/danak-services-search-query.dto.ts | 8 +++-- .../DTO/update-review-status.dto.ts | 13 ++++++++ .../danak-services.controller.ts | 32 ++++++++++++++----- .../providers/danak-services.service.ts | 18 +++++++++-- .../repositories/danak-services.repository.ts | 29 +++++++++++++++-- .../invoices/providers/invoices.service.ts | 2 +- src/modules/utils/providers/sms.service.ts | 13 +++----- 9 files changed, 99 insertions(+), 25 deletions(-) create mode 100644 src/modules/danak-services/DTO/update-review-status.dto.ts diff --git a/src/common/DTO/param.dto.ts b/src/common/DTO/param.dto.ts index 5201875..8a84660 100755 --- a/src/common/DTO/param.dto.ts +++ b/src/common/DTO/param.dto.ts @@ -1,4 +1,4 @@ -import { ApiProperty } from "@nestjs/swagger"; +import { ApiProperty, PartialType } from "@nestjs/swagger"; import { IsNotEmpty, IsUUID } from "class-validator"; import { CommonMessage } from "../enums/message.enum"; @@ -10,6 +10,8 @@ export class ParamDto { id: string; } +export class OptionalParamDto extends PartialType(ParamDto) {} + export class ParamNumberIdDto { @IsNotEmpty({ message: CommonMessage.ID_REQUIRED }) @ApiProperty({ description: "Id of the entity", example: "20" }) diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 492ef3b..5fc6de2 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -165,6 +165,7 @@ export const enum ServiceMessage { REVIEW_REJECTED = "نظر با موفقیت رد شد", IS_ACTIVE_SHOULD_BE_1_0 = "وضعیت سرویس باید یکی از مقادیر ۰ و ۱ باشد", IS_SUGGEST_SHOULD_BE_1_0 = "وضعیت پیشنهادی بودن سرویس باید یکی از مقادیر ۰ و ۱ باشد", + REVIEW_STATUS_REQUIRED = "وضعیت نظر سرویس مورد نیاز است", } export const enum AnnouncementMessage { @@ -560,3 +561,7 @@ export const enum UserQuickAccessMessage { QUICK_ACCESS_LIMIT_REACHED = "حداکثر تعداد دسترسی سریع برای این کاربر ایجاد شده است", QUICK_ACCESS_EXIST = "این دسترسی سریع قبلا ایجاد شده است", } + +export const enum SmsMessage { + SEND_SMS_ERROR = "خطا در ارسال پیامک", +} diff --git a/src/modules/danak-services/DTO/danak-services-search-query.dto.ts b/src/modules/danak-services/DTO/danak-services-search-query.dto.ts index 8a21676..f9fe0e5 100755 --- a/src/modules/danak-services/DTO/danak-services-search-query.dto.ts +++ b/src/modules/danak-services/DTO/danak-services-search-query.dto.ts @@ -1,13 +1,13 @@ -import { ApiPropertyOptional } from "@nestjs/swagger"; +import { ApiPropertyOptional, PickType } from "@nestjs/swagger"; import { Type } from "class-transformer"; -import { IsIn, IsOptional, IsString } from "class-validator"; +import { IsIn, IsOptional, IsString, IsUUID } from "class-validator"; import { PaginationDto } from "../../../common/DTO/pagination.dto"; import { CategoryMessage, ServiceMessage } from "../../../common/enums/message.enum"; export class DanakServicesSearchQueryDto extends PaginationDto { @IsOptional() - @IsString({ message: CategoryMessage.CAT_ID_SHOULD_BE_UUID }) + @IsUUID(4, { message: CategoryMessage.CAT_ID_SHOULD_BE_UUID }) @ApiPropertyOptional({ description: "Category id", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" }) categoryId?: string; @@ -28,3 +28,5 @@ export class DanakServicesSearchQueryDto extends PaginationDto { @ApiPropertyOptional({ description: "is danak suggest this service", example: 1 }) isDanakSuggest?: number; } + +export class DanakServicesQueryDto extends PickType(DanakServicesSearchQueryDto, ["limit", "page", "categoryId"] as const) {} diff --git a/src/modules/danak-services/DTO/update-review-status.dto.ts b/src/modules/danak-services/DTO/update-review-status.dto.ts new file mode 100644 index 0000000..3a20f18 --- /dev/null +++ b/src/modules/danak-services/DTO/update-review-status.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsEnum, IsNotEmpty } from "class-validator"; + +import { ParamDto } from "../../../common/DTO/param.dto"; +import { ServiceMessage } from "../../../common/enums/message.enum"; +import { ReviewStatus } from "../enums/review-status.enum"; + +export class UpdateReviewStatusDto extends ParamDto { + @IsNotEmpty({ message: ServiceMessage.REVIEW_STATUS_REQUIRED }) + @IsEnum(ReviewStatus) + @ApiProperty({ enum: ReviewStatus, example: ReviewStatus.APPROVED, description: "Review status" }) + status: ReviewStatus; +} diff --git a/src/modules/danak-services/danak-services.controller.ts b/src/modules/danak-services/danak-services.controller.ts index 3eebc20..01f933f 100755 --- a/src/modules/danak-services/danak-services.controller.ts +++ b/src/modules/danak-services/danak-services.controller.ts @@ -1,4 +1,4 @@ -import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Query } from "@nestjs/common"; +import { Body, Controller, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from "@nestjs/common"; import { ApiOperation, ApiTags } from "@nestjs/swagger"; import { AddReviewDto } from "./DTO/add-review.dto"; @@ -6,7 +6,8 @@ import { CategoryListSearchQueryDto, CategorySearchQueryDto } from "./DTO/catego import { CreateCategoryDto } from "./DTO/create-category.dto"; import { CreateServiceDto } from "./DTO/create-service.dto"; import { DanakServiceReviewQueryDto } from "./DTO/danak-service-review-query.dto"; -import { DanakServicesSearchQueryDto } from "./DTO/danak-services-search-query.dto"; +import { DanakServicesQueryDto, DanakServicesSearchQueryDto } from "./DTO/danak-services-search-query.dto"; +import { UpdateReviewStatusDto } from "./DTO/update-review-status.dto"; import { DanakServicesService } from "./providers/danak-services.service"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { Pagination } from "../../common/decorators/pagination.decorator"; @@ -53,12 +54,12 @@ export class DanakServicesController { return this.danakServicesService.getCategoriesUserSide(); } - @AuthGuards() - @ApiOperation({ summary: "get category services with category id" }) - @Get("categories/:id/services") - getCategoryServices(@Param() paramDto: ParamDto) { - return this.danakServicesService.getCategoryServices(paramDto.id); - } + // @AuthGuards() + // @ApiOperation({ summary: "get category services with category id" }) + // @Get("categories/:id/services") + // getCategoryServices(@Param() paramDto: ParamDto) { + // return this.danakServicesService.getCategoryServices(paramDto.id); + // } @AuthGuards() @PermissionsDec(PermissionEnum.SERVICES) @@ -85,6 +86,13 @@ export class DanakServicesController { return this.danakServicesService.getServicesList(queryDto); } + @ApiOperation({ summary: "get all danak services for user side" }) + @AuthGuards() + @Get("public") + getServicesForUser(@Query() queryDto: DanakServicesQueryDto) { + return this.danakServicesService.getServicesListForUser(queryDto); + } + @AuthGuards() @PermissionsDec(PermissionEnum.SERVICES) @ApiOperation({ summary: "toggle status of danak service => admin route" }) @@ -124,6 +132,14 @@ export class DanakServicesController { return this.danakServicesService.getDanakServiceReviews(queryDto); } + @ApiOperation({ summary: "update danak service review status ==> admin route" }) + @AuthGuards() + @PermissionsDec(PermissionEnum.SERVICES) + @Patch("reviews/:id/:status") + updateDanakServiceReviewStatus(@Param() updateParamDto: UpdateReviewStatusDto) { + return this.danakServicesService.updateDanakServiceReviewStatus(updateParamDto); + } + // @AuthGuards() // // @ApiOperation({ summary: "get all user purchased danak services" }) diff --git a/src/modules/danak-services/providers/danak-services.service.ts b/src/modules/danak-services/providers/danak-services.service.ts index cd8c340..a5a43b0 100755 --- a/src/modules/danak-services/providers/danak-services.service.ts +++ b/src/modules/danak-services/providers/danak-services.service.ts @@ -10,7 +10,8 @@ import { CategoryListSearchQueryDto, CategorySearchQueryDto } from "../DTO/categ import { CreateCategoryDto } from "../DTO/create-category.dto"; import { CreateServiceDto } from "../DTO/create-service.dto"; import { DanakServiceReviewQueryDto } from "../DTO/danak-service-review-query.dto"; -import { DanakServicesSearchQueryDto } from "../DTO/danak-services-search-query.dto"; +import { DanakServicesQueryDto, DanakServicesSearchQueryDto } from "../DTO/danak-services-search-query.dto"; +import { UpdateReviewStatusDto } from "../DTO/update-review-status.dto"; import { DanakServiceCategory } from "../entities/danak-service-category.entity"; import { ReviewStatus } from "../enums/review-status.enum"; import { DanakServicesCategoryRepository } from "../repositories/danak-services-category.repository"; @@ -96,7 +97,7 @@ export class DanakServicesService { } /******************************************** */ - async getCategoryServices(categoryId: string) { + async getCategoryServices(categoryId?: string) { const category = await this.danakServicesCategoryRepository.findOne({ where: { id: categoryId, isActive: true, danakServices: { isActive: true } }, relations: { @@ -164,6 +165,11 @@ export class DanakServicesService { async getServicesList(queryDto: DanakServicesSearchQueryDto) { return this.danakServicesRepository.getServicesForAdmin(queryDto); } + /******************************************** */ + + async getServicesListForUser(queryDto: DanakServicesQueryDto) { + return this.danakServicesRepository.getServicesForUser(queryDto); + } /******************************************** */ @@ -322,6 +328,14 @@ export class DanakServicesService { /******************************************** */ + async updateDanakServiceReviewStatus(updateParamDto: UpdateReviewStatusDto) { + if (updateParamDto.status === ReviewStatus.APPROVED) { + return await this.approveReview(updateParamDto.id); + } else { + return await this.rejectReview(updateParamDto.id); + } + } + async approveReview(reviewId: string) { const review = await this.danakServiceReviewRepository.findOneBy({ id: reviewId }); if (!review) throw new BadRequestException(ServiceMessage.REVIEW_NOT_EXIST); diff --git a/src/modules/danak-services/repositories/danak-services.repository.ts b/src/modules/danak-services/repositories/danak-services.repository.ts index 9bdd951..eaf32b8 100755 --- a/src/modules/danak-services/repositories/danak-services.repository.ts +++ b/src/modules/danak-services/repositories/danak-services.repository.ts @@ -3,7 +3,7 @@ import { InjectRepository } from "@nestjs/typeorm"; import { Brackets, Repository } from "typeorm"; import { PaginationUtils } from "../../utils/providers/pagination.utils"; -import { DanakServicesSearchQueryDto } from "../DTO/danak-services-search-query.dto"; +import { DanakServicesQueryDto, DanakServicesSearchQueryDto } from "../DTO/danak-services-search-query.dto"; import { DanakService } from "../entities/danak-service.entity"; @Injectable() @@ -19,7 +19,7 @@ export class DanakServicesRepository extends Repository { async findServiceById(id: string): Promise { return this.findOneBy({ id }); } - + //+******************** async getServicesForAdmin(queryDto: DanakServicesSearchQueryDto) { const { limit, skip } = PaginationUtils(queryDto); @@ -54,6 +54,31 @@ export class DanakServicesRepository extends Repository { } const [services, count] = await queryBuilder.getManyAndCount(); + return { + services, + count, + }; + } + //+******************** + async getServicesForUser(queryDto: DanakServicesQueryDto) { + const { limit, skip } = PaginationUtils(queryDto); + + const queryBuilder = this.createQueryBuilder("service"); + queryBuilder + .innerJoin("service.subscriptionPlans", "subscriptionPlans", "subscriptionPlans.isActive = :isActive", { isActive: true }) + .leftJoinAndSelect("service.category", "category") + .where("service.isActive = :isActive", { isActive: true }) + .andWhere("service.isDanakSuggest = :isDanakSuggest", { isDanakSuggest: true }) + .orderBy("service.createdAt", "DESC") + .skip(skip) + .take(limit); + + if (queryDto.categoryId) { + queryBuilder.andWhere("service.categoryId = :categoryId", { categoryId: queryDto.categoryId }); + } + + const [services, count] = await queryBuilder.getManyAndCount(); + return { services, count, diff --git a/src/modules/invoices/providers/invoices.service.ts b/src/modules/invoices/providers/invoices.service.ts index f60608e..33b1a96 100755 --- a/src/modules/invoices/providers/invoices.service.ts +++ b/src/modules/invoices/providers/invoices.service.ts @@ -199,6 +199,7 @@ export class InvoicesService { dueDate, items: invoiceItems, }); + await queryRunner.manager.save(Invoice, invoice); await this.notificationsService.createInvoiceCreationNotification( user.id, @@ -214,7 +215,6 @@ export class InvoicesService { queryRunner, ); // - await queryRunner.manager.save(Invoice, invoice); return invoice; } diff --git a/src/modules/utils/providers/sms.service.ts b/src/modules/utils/providers/sms.service.ts index 8090987..25bb3ca 100755 --- a/src/modules/utils/providers/sms.service.ts +++ b/src/modules/utils/providers/sms.service.ts @@ -3,6 +3,7 @@ import { Inject, Injectable, InternalServerErrorException, Logger } from "@nestj import { AxiosError } from "axios"; import { catchError, firstValueFrom } from "rxjs"; +import { SmsMessage } from "../../../common/enums/message.enum"; import { ISmsConfigs } from "../../../configs/sms.config"; import { SMS_CONFIG } from "../constants"; import { ISmsGetLineResponse, ISmsVerifyBody, ISmsVerifyResponse } from "../interfaces/ISms"; @@ -46,6 +47,7 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); + throw new InternalServerErrorException(SmsMessage.SEND_SMS_ERROR); } } //************************************************* */ @@ -81,7 +83,7 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - throw new InternalServerErrorException("error in sending sms"); + throw new InternalServerErrorException(SmsMessage.SEND_SMS_ERROR); } } //************************************************* */ @@ -109,7 +111,6 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - // throw new InternalServerErrorException("error in sending sms"); } } //************************************************* */ @@ -135,6 +136,8 @@ export class SmsService { }) .pipe( catchError((err: AxiosError) => { + console.error(err); + this.logger.error("error in sending invoice created sms", err); throw new InternalServerErrorException("error in sending invoice created sms"); }), @@ -143,7 +146,6 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - throw new InternalServerErrorException("error in sending sms"); } } //************************************************* */ @@ -174,7 +176,6 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - throw new InternalServerErrorException("error in sending sms"); } } //************************************************* */ @@ -206,7 +207,6 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - throw new InternalServerErrorException("error in sending sms"); } } //************************************************* */ @@ -239,7 +239,6 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - throw new InternalServerErrorException("error in sending sms"); } } @@ -272,7 +271,6 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - throw new InternalServerErrorException("error in sending sms"); } } //************************************************* */ @@ -303,7 +301,6 @@ export class SmsService { return data; } catch (error) { this.logger.error("error in sending sms", error); - throw new InternalServerErrorException("error in sending sms"); } }