diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 6131a39..bbed418 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -134,6 +134,10 @@ export const enum CategoryMessage { IS_ACTIVE_SHOULD_BE_1_0 = "وضعیت دسته بندی باید یکی از مقادیر ۰ و ۱ باشد", IS_SUGGEST_SHOULD_BE_1_0 = "وضعیت پیشنهادی بودن دسته بندی باید یکی از مقادیر ۰ و ۱ باشد", CATEGORY_NOT_EXIST_OR_HAS_NO_SERVICE = "دسته بندی مورد نظر یافت نشد یا هیچ سرویسی در این دسته بندی وجود ندارد", + ORDER_REQUIRED = "ترتیب دسته بندی مورد نیاز است", + ORDER_SHOULD_BE_INT = "ترتیب دسته بندی باید یک عدد صحیح باشد", + CREATION_FAILED = "ایجاد دسته بندی موفیقیت آمیز نبود", + UPDATE_FAILED = "به روز رسانی دسته بندی موفیقیت آمیز نبود", } export const enum ServiceMessage { diff --git a/src/modules/danak-services/DTO/create-category.dto.ts b/src/modules/danak-services/DTO/create-category.dto.ts index 1bea5fe..6f0f39b 100755 --- a/src/modules/danak-services/DTO/create-category.dto.ts +++ b/src/modules/danak-services/DTO/create-category.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; -import { IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator"; +import { IsBoolean, IsInt, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator"; import { CategoryMessage } from "../../../common/enums/message.enum"; @@ -25,4 +25,9 @@ export class CreateCategoryDto { @IsUUID("4", { message: CategoryMessage.PARENT_ID_SHOULD_BE_UUID }) @ApiPropertyOptional({ description: "Parent category id", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" }) parentId?: string; + + @IsNotEmpty({ message: CategoryMessage.ORDER_REQUIRED }) + @IsInt({ message: CategoryMessage.ORDER_SHOULD_BE_INT }) + @ApiProperty({ description: "Category order", example: 1 }) + order: number; } diff --git a/src/modules/danak-services/entities/danak-service-category.entity.ts b/src/modules/danak-services/entities/danak-service-category.entity.ts index de0a853..e1e1aa9 100755 --- a/src/modules/danak-services/entities/danak-service-category.entity.ts +++ b/src/modules/danak-services/entities/danak-service-category.entity.ts @@ -14,6 +14,9 @@ export class DanakServiceCategory extends BaseEntity { @Column({ type: "boolean", default: true }) isActive: boolean; + @Column({ type: "int", default: 1 }) + order: number; + @ManyToOne(() => DanakServiceCategory, (category) => category.children) @JoinColumn({ name: "parentId" }) parent: DanakServiceCategory; diff --git a/src/modules/danak-services/providers/danak-services.service.ts b/src/modules/danak-services/providers/danak-services.service.ts index 471fe55..3e09e32 100755 --- a/src/modules/danak-services/providers/danak-services.service.ts +++ b/src/modules/danak-services/providers/danak-services.service.ts @@ -1,5 +1,5 @@ -import { BadRequestException, Injectable } from "@nestjs/common"; -import { FindOptionsWhere, In, IsNull } from "typeorm"; +import { BadRequestException, Injectable, Logger } from "@nestjs/common"; +import { DataSource, FindOptionsWhere, In, IsNull, Not, QueryRunner } from "typeorm"; import { ParamDto } from "../../../common/DTO/param.dto"; import { CategoryMessage, CommonMessage, ServiceMessage, SubscriptionMessage } from "../../../common/enums/message.enum"; @@ -24,24 +24,67 @@ import { DanakServicesRepository } from "../repositories/danak-services.reposito @Injectable() export class DanakServicesService { - // private readonly logger = new Logger(DanakServicesService.name); + private readonly logger = new Logger(DanakServicesService.name); constructor( private readonly danakServicesRepository: DanakServicesRepository, private readonly danakServicesCategoryRepository: DanakServicesCategoryRepository, private readonly danakServiceReviewRepository: DanakServiceReviewRepository, private readonly danakServicesImageRepository: DanakServicesImageRepository, + private readonly dataSource: DataSource, ) {} /******************************************** */ async createCategory(createDto: CreateCategoryDto) { - const existCategory = await this.danakServicesCategoryRepository.findOneByTitle(createDto.title); - if (existCategory) throw new BadRequestException(CategoryMessage.TITLE_EXIST); - const category = this.danakServicesCategoryRepository.create(createDto); - await this.danakServicesCategoryRepository.save(category); - return { - message: CommonMessage.CREATED, - category, - }; + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + const existCategory = await queryRunner.manager.findOne(this.danakServicesCategoryRepository.target, { + where: { title: createDto.title }, + }); + + if (existCategory) { + throw new BadRequestException(CategoryMessage.TITLE_EXIST); + } + + const category = queryRunner.manager.create(this.danakServicesCategoryRepository.target, createDto); + await queryRunner.manager.save(this.danakServicesCategoryRepository.target, category); + + await this.handleOrderCollision(queryRunner, category); + + await queryRunner.commitTransaction(); + return { + message: CommonMessage.CREATED, + category, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error("Error in create category", error); + throw new BadRequestException(CategoryMessage.CREATION_FAILED); + } finally { + await queryRunner.release(); + } + } + //*********************************** */ + private async handleOrderCollision(queryRunner: QueryRunner, newCategory: DanakServiceCategory): Promise { + const conflictingCategory = await queryRunner.manager.findOne(this.danakServicesCategoryRepository.target, { + where: { + order: newCategory.order, + id: Not(newCategory.id), + deletedAt: IsNull(), + }, + }); + + if (conflictingCategory) { + const highestOrderCategory = await queryRunner.manager.findOne(this.danakServicesCategoryRepository.target, { + order: { order: "DESC" }, + where: { deletedAt: IsNull() }, + }); + + const newOrder = highestOrderCategory ? highestOrderCategory.order + 1 : newCategory.order + 1; + await queryRunner.manager.update(this.danakServicesCategoryRepository.target, conflictingCategory.id, { order: newOrder }); + } } /******************************************** */ @@ -56,20 +99,42 @@ export class DanakServicesService { /******************************************** */ async updateCategory(categoryId: string, updateDto: UpdateCategoryDto) { - const category = await this.danakServicesCategoryRepository.findOneById(categoryId); - if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); - if (updateDto.title) { - const existCategory = await this.danakServicesCategoryRepository.findOneByTitle(updateDto.title, categoryId); - if (existCategory) throw new BadRequestException(CategoryMessage.TITLE_EXIST); + try { + const category = await queryRunner.manager.findOne(DanakServiceCategory, { where: { id: categoryId } }); + if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST); + + if (updateDto.title) { + const existCategory = await queryRunner.manager.findOne(DanakServiceCategory, { + where: { title: updateDto.title, id: Not(categoryId) }, + }); + if (existCategory) throw new BadRequestException(CategoryMessage.TITLE_EXIST); + } + + await queryRunner.manager.update(DanakServiceCategory, categoryId, updateDto); + + if (updateDto.order !== undefined && updateDto.order !== category.order) { + await this.handleOrderCollision(queryRunner, { ...category, ...updateDto }); + } + + await queryRunner.commitTransaction(); + return { + message: CommonMessage.UPDATE_SUCCESS, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error("Error in update category", error); + throw new BadRequestException(CategoryMessage.UPDATE_FAILED); + } finally { + await queryRunner.release(); } - - await this.danakServicesCategoryRepository.save({ ...category, ...updateDto }); - return { - message: CommonMessage.UPDATE_SUCCESS, - }; } + /******************************************** */ + async deleteCategoryById(categoryId: string) { const category = await this.danakServicesCategoryRepository.findOneById(categoryId); if (!category) throw new BadRequestException(CategoryMessage.CATEGORY_NOT_EXIST); @@ -131,7 +196,7 @@ export class DanakServicesService { async getCategoriesUserSide() { const categories = await this.danakServicesCategoryRepository.find({ where: { isActive: true, deletedAt: IsNull() }, - order: { createdAt: "DESC" }, + order: { order: "DESC" }, }); return { categories,