From 9d50578cc21c664cbb2206e96b4a8c6f938f6044 Mon Sep 17 00:00:00 2001 From: mahyargdz Date: Sun, 4 May 2025 12:59:55 +0330 Subject: [PATCH] chore: add notif service for new blog and service comment --- src/modules/blogs/blogs.module.ts | 4 +- src/modules/blogs/providers/blogs.service.ts | 63 +++++++++++---- .../danak-services/danak-services.module.ts | 8 +- .../providers/danak-services.service.ts | 76 ++++++++++++++----- src/modules/users/providers/admins.service.ts | 5 ++ 5 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/modules/blogs/blogs.module.ts b/src/modules/blogs/blogs.module.ts index c2e603c..2c98b5a 100755 --- a/src/modules/blogs/blogs.module.ts +++ b/src/modules/blogs/blogs.module.ts @@ -9,10 +9,10 @@ import { BlogsService } from "./providers/blogs.service"; import { BlogCategoriesRepository } from "./repositories/blog-categories.repository"; import { BlogCommentsRepository } from "./repositories/blog-comments.repository"; import { BlogsRepository } from "./repositories/blogs.repository"; +import { NotificationModule } from "../notifications/notifications.module"; import { UsersModule } from "../users/users.module"; - @Module({ - imports: [TypeOrmModule.forFeature([Blog, BlogComment, BlogCategory]), UsersModule], + imports: [TypeOrmModule.forFeature([Blog, BlogComment, BlogCategory]), UsersModule, NotificationModule], controllers: [BlogsController], providers: [BlogsService, BlogsRepository, BlogCommentsRepository, BlogCategoriesRepository], exports: [BlogsService], diff --git a/src/modules/blogs/providers/blogs.service.ts b/src/modules/blogs/providers/blogs.service.ts index 61715da..720de55 100755 --- a/src/modules/blogs/providers/blogs.service.ts +++ b/src/modules/blogs/providers/blogs.service.ts @@ -1,8 +1,10 @@ import { BadRequestException, Injectable } from "@nestjs/common"; -import { Not } from "typeorm"; +import { DataSource, IsNull, Not } from "typeorm"; import { AdminMessage, BlogMessage, CommonMessage } from "../../../common/enums/message.enum"; import { CategoryListSearchQueryDto } from "../../danak-services/DTO/category-search-query.dto"; +import { NotificationsService } from "../../notifications/providers/notifications.service"; +import { AdminsService } from "../../users/providers/admins.service"; import { UsersService } from "../../users/providers/users.service"; import { BlogCommentQueryDto } from "../DTO/blog-comment-query.dto"; import { CreateBlogDto } from "../DTO/create-blog.dto"; @@ -23,6 +25,9 @@ export class BlogsService { private readonly blogCommentsRepository: BlogCommentsRepository, private readonly blogCategoriesRepository: BlogCategoriesRepository, private readonly usersService: UsersService, + private readonly adminsService: AdminsService, + private readonly dataSource: DataSource, + private readonly notificationsService: NotificationsService, ) {} //*********************************** */ @@ -233,23 +238,53 @@ export class BlogsService { //*********************************** */ async createComment(blogId: string, createCommentDto: CreateCommentDto, userId: string) { - const blog = await this.findBlogById(blogId); + const queryRunner = this.dataSource.createQueryRunner(); - const { user } = await this.usersService.findOneById(userId); + try { + await queryRunner.connect(); + await queryRunner.startTransaction(); - const comment = this.blogCommentsRepository.create({ - ...createCommentDto, - blog, - user, - status: CommentStatus.PENDING, - }); + const blog = await queryRunner.manager.findOne(this.blogsRepository.target, { where: { id: blogId, deletedAt: IsNull() } }); + if (!blog) throw new BadRequestException(BlogMessage.BLOG_NOT_FOUND); - await this.blogCommentsRepository.save(comment); + const user = await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner); - return { - message: CommonMessage.CREATED, - comment, - }; + const comment = queryRunner.manager.create(this.blogCommentsRepository.target, { + ...createCommentDto, + blog, + user, + status: CommentStatus.PENDING, + }); + + await queryRunner.manager.save(this.blogCommentsRepository.target, comment); + + const superAdmin = await this.adminsService.getSuperAdmins(); + + for (const admin of superAdmin) { + await this.notificationsService.createNewBlogCommentNotification( + admin.id, + { + userPhone: user.phone, + userEmail: user.email, + blogTitle: blog.title, + fullName: `${user.firstName} ${user.lastName}`, + }, + queryRunner, + ); + } + + await queryRunner.commitTransaction(); + + return { + message: CommonMessage.CREATED, + comment, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } } //*********************************** */ diff --git a/src/modules/danak-services/danak-services.module.ts b/src/modules/danak-services/danak-services.module.ts index 483322e..c004fde 100755 --- a/src/modules/danak-services/danak-services.module.ts +++ b/src/modules/danak-services/danak-services.module.ts @@ -11,9 +11,15 @@ import { DanakServicesCategoryRepository } from "./repositories/danak-services-c import { DanakServicesImageRepository } from "./repositories/danak-services-image.repository"; import { DanakServiceReviewRepository } from "./repositories/danak-services-review.repository"; import { DanakServicesRepository } from "./repositories/danak-services.repository"; +import { NotificationModule } from "../notifications/notifications.module"; +import { UsersModule } from "../users/users.module"; @Module({ - imports: [TypeOrmModule.forFeature([DanakService, DanakServiceImage, DanakServiceCategory, DanakServiceReview])], + imports: [ + TypeOrmModule.forFeature([DanakService, DanakServiceImage, DanakServiceCategory, DanakServiceReview]), + UsersModule, + NotificationModule, + ], providers: [ DanakServicesService, DanakServicesRepository, diff --git a/src/modules/danak-services/providers/danak-services.service.ts b/src/modules/danak-services/providers/danak-services.service.ts index 6cb75a3..fd6fa90 100755 --- a/src/modules/danak-services/providers/danak-services.service.ts +++ b/src/modules/danak-services/providers/danak-services.service.ts @@ -3,7 +3,10 @@ import { DataSource, FindOptionsWhere, In, IsNull, Not, QueryRunner } from "type import { ParamDto } from "../../../common/DTO/param.dto"; import { CategoryMessage, CommonMessage, ServiceMessage, SubscriptionMessage } from "../../../common/enums/message.enum"; +import { NotificationsService } from "../../notifications/providers/notifications.service"; import { SubscriptionStatus } from "../../subscriptions/enums/subscription-status.enum"; +import { AdminsService } from "../../users/providers/admins.service"; +import { UsersService } from "../../users/providers/users.service"; import { PaginationUtils } from "../../utils/providers/pagination.utils"; import { AddReviewDto } from "../DTO/add-review.dto"; import { CategoryListSearchQueryDto, CategorySearchQueryDto } from "../DTO/category-search-query.dto"; @@ -21,7 +24,6 @@ import { DanakServicesCategoryRepository } from "../repositories/danak-services- import { DanakServicesImageRepository } from "../repositories/danak-services-image.repository"; import { DanakServiceReviewRepository } from "../repositories/danak-services-review.repository"; import { DanakServicesRepository } from "../repositories/danak-services.repository"; - @Injectable() export class DanakServicesService { private readonly logger = new Logger(DanakServicesService.name); @@ -31,6 +33,9 @@ export class DanakServicesService { private readonly danakServiceReviewRepository: DanakServiceReviewRepository, private readonly danakServicesImageRepository: DanakServicesImageRepository, private readonly dataSource: DataSource, + private readonly adminsService: AdminsService, + private readonly notificationsService: NotificationsService, + private readonly usersService: UsersService, ) {} /******************************************** */ @@ -492,26 +497,58 @@ export class DanakServicesService { /******************************************** */ async addReview(userId: string, serviceId: string, addReviewDto: AddReviewDto) { - const service = await this.findServiceById(serviceId); + const queryRunner = this.dataSource.createQueryRunner(); - const hasSubscription = await this.checkUserPurchasedService(userId, serviceId); - if (!hasSubscription) throw new BadRequestException(SubscriptionMessage.NOT_PURCHASED_CANNOT_REVIEW); + try { + await queryRunner.connect(); + await queryRunner.startTransaction(); - // // Check if user already reviewed this service - // const existingReview = await this.danakServiceReviewRepository.findOne({ - // where: { user: { id: userId }, service: { id: serviceId } }, - // }); + const user = await this.usersService.findOneByIdWithQueryRunner(userId, queryRunner); - // if (existingReview) { - // throw new BadRequestException(ServiceMessage.ALREADY_REVIEWED); - // } + const service = await queryRunner.manager.findOne(this.danakServicesRepository.target, { + where: { id: serviceId, deletedAt: IsNull() }, + }); - const review = this.danakServiceReviewRepository.create({ user: { id: userId }, ...addReviewDto, service }); - await this.danakServiceReviewRepository.save(review); - return { - message: ServiceMessage.REVIEW_ADDED, - review, - }; + if (!service) throw new BadRequestException(ServiceMessage.SERVICE_NOT_EXIST); + + const hasSubscription = await this.checkUserPurchasedService(userId, serviceId); + if (!hasSubscription) throw new BadRequestException(SubscriptionMessage.NOT_PURCHASED_CANNOT_REVIEW); + + const review = queryRunner.manager.create(this.danakServiceReviewRepository.target, { + user: { id: userId }, + ...addReviewDto, + service, + }); + + await queryRunner.manager.save(this.danakServiceReviewRepository.target, review); + + const superAdmins = await this.adminsService.getSuperAdmins(); + + for (const admin of superAdmins) { + await this.notificationsService.createNewServiceReviewNotification( + admin.id, + { + serviceName: service.name, + userPhone: user.phone, + userEmail: user.email, + fullName: `${user.firstName} ${user.lastName}`, + }, + queryRunner, + ); + } + + await queryRunner.commitTransaction(); + return { + message: ServiceMessage.REVIEW_ADDED, + review, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error("Error in add review", error); + throw error; + } finally { + await queryRunner.release(); + } } /******************************************** */ @@ -578,9 +615,10 @@ export class DanakServicesService { .replace(/-+/g, "-") // Replace multiple hyphens with single hyphen .trim(); } + /******************************************** */ - private async checkUserPurchasedService(userId: string, serviceId: string) { - const subscription = await this.danakServicesRepository + private async checkUserPurchasedService(userId: string, serviceId: string, queryRunner?: QueryRunner) { + const subscription = await (queryRunner ? queryRunner.manager : this.danakServicesRepository) .createQueryBuilder() .select("1") .from("user_subscription", "us") diff --git a/src/modules/users/providers/admins.service.ts b/src/modules/users/providers/admins.service.ts index 85ce489..f749a07 100755 --- a/src/modules/users/providers/admins.service.ts +++ b/src/modules/users/providers/admins.service.ts @@ -10,6 +10,7 @@ import { CreateRoleDto } from "../DTO/create-role.dto"; import { SearchAdminQueryDto } from "../DTO/search-admins-query.dto"; import { SearchRolesQueryDto } from "../DTO/search-roles.dto"; import { UpdateAdminDto } from "../DTO/update-admin.dto"; +import { RoleEnum } from "../enums/role.enum"; import { PermissionsRepository } from "../repositories/permissions.repository"; import { RoleRepository } from "../repositories/roles.repository"; import { UserRepository } from "../repositories/users.repository"; @@ -221,4 +222,8 @@ export class AdminsService { paginate: true, }; } + /************************************************************ */ + async getSuperAdmins() { + return this.userRepository.find({ where: { roles: { isAdmin: true, name: RoleEnum.SUPER_ADMIN } } }); + } }