chore: add notification service for all user action

This commit is contained in:
mahyargdz
2025-02-28 19:43:53 +03:30
parent 5b1b574d41
commit e65334150c
19 changed files with 709 additions and 74 deletions
@@ -1,12 +1,13 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsOptional } from "class-validator";
import { IsIn, IsOptional } from "class-validator";
import { PaginationDto } from "../../../common/DTO/pagination.dto";
export class SearchNotificationQueryDto extends PaginationDto {
@IsOptional()
@Type(() => Number)
@IsIn([0, 1])
@ApiPropertyOptional({ description: "Notification status", example: 1 })
isRead?: number;
}
@@ -1,5 +1,5 @@
export enum NotificationType {
LOGIN = "LOGIN",
PASSWORD_CAHNGED = "PASSWORD_CHANGED",
PASSWORD_CHANGED = "PASSWORD_CHANGED",
WALLET_CHARGED = "WALLER_CHARGED",
}
@@ -0,0 +1,38 @@
export interface IBaseNotificationData {
userPhone: string;
userEmail?: string;
}
export interface IInvoiceNotificationData extends IBaseNotificationData {
price: number;
items: string;
dueDate: Date;
invoiceId: string;
}
export interface IServiceNotificationData extends IBaseNotificationData {
serviceName: string;
}
export interface ITicketNotificationData extends IBaseNotificationData {
ticketId: string;
subject: string;
date: Date;
}
export interface IWalletNotificationData extends IBaseNotificationData {
amount: number;
balance: number;
date: Date;
reason: string;
}
export interface IGenericNotificationData extends IBaseNotificationData {
message: string;
}
export interface IAnnouncementNotificationData extends IBaseNotificationData {
title: string;
description: string;
date: Date;
}
@@ -10,6 +10,7 @@ import { ParamDto } from "../../common/DTO/param.dto";
@Controller("notifications")
@ApiTags("Notifications")
@AuthGuards()
export class NotificationController {
constructor(private readonly notificationService: NotificationsService) {}
@@ -17,7 +18,6 @@ export class NotificationController {
@ApiOperation({ summary: "all notifications by user" })
@Pagination()
@AuthGuards()
@Get()
getAllNotifications(@UserDec("id") userId: string, @Query() queryDto: SearchNotificationQueryDto) {
return this.notificationService.getAllNotifications(queryDto, userId);
@@ -26,7 +26,6 @@ export class NotificationController {
//********************* */
@ApiOperation({ summary: "mark user notification as read" })
@AuthGuards()
@Patch(":id/read")
markAsRead(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.notificationService.markAsRead(paramDto.id, userId);
@@ -35,7 +34,6 @@ export class NotificationController {
//********************* */
@ApiOperation({ summary: "mark user all notification as read" })
@AuthGuards()
@Patch("read-all")
markAllAsRead(@UserDec("id") userId: string) {
return this.notificationService.markAllAsRead(userId);
@@ -5,11 +5,11 @@ import { Notification } from "./entities/notification.entity";
import { NotificationController } from "./notifications.controller";
import { NotificationsService } from "./providers/notifications.service";
import { NotificationRepository } from "./repositories/notifications.repository";
import { User } from "../users/entities/user.entity";
import { UsersModule } from "../users/users.module";
import { SettingModule } from "../settings/settings.module";
import { UtilsModule } from "../utils/utils.module";
@Module({
imports: [TypeOrmModule.forFeature([Notification, User]), UsersModule],
imports: [TypeOrmModule.forFeature([Notification]), UtilsModule, SettingModule],
providers: [NotificationRepository, NotificationsService],
controllers: [NotificationController],
exports: [NotificationRepository, NotificationsService],
@@ -1,30 +1,73 @@
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
import { InjectDataSource } from "@nestjs/typeorm";
import { DataSource, QueryRunner } from "typeorm";
import { CommonMessage, NotificationMessage, UserMessage } from "../../../common/enums/message.enum";
import { UsersService } from "../../users/providers/users.service";
import { CommonMessage, NotificationMessage } from "../../../common/enums/message.enum";
import { NotifType } from "../../settings/enums/notif-settings.enum";
import { UserSettingsService } from "../../settings/providers/user-settings.service";
import { dateFormat, numberFormat } from "../../utils/providers/international.utils";
import { SmsService } from "../../utils/providers/sms.service";
import { CreateNotificationDto } from "../DTO/create-notification.dto";
import { SearchNotificationQueryDto } from "../DTO/search-notification-query.dto";
import { Notification } from "../entities/notification.entity";
import {
IAnnouncementNotificationData,
IBaseNotificationData,
IInvoiceNotificationData,
ITicketNotificationData,
IWalletNotificationData,
} from "../interfaces/ISendNotificationData";
import { NotificationRepository } from "../repositories/notifications.repository";
@Injectable()
export class NotificationsService {
private readonly logger = new Logger(NotificationsService.name);
constructor(
private readonly notificationRepository: NotificationRepository,
private readonly usersService: UsersService,
private readonly smsService: SmsService,
private readonly userSettingsService: UserSettingsService,
@InjectDataSource() private readonly dataSource: DataSource,
) {}
//************************ */
async createNotification(createNotificationDto: CreateNotificationDto) {
const { user } = await this.usersService.findOneById(createNotificationDto.recipientId);
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
const notification = this.notificationRepository.create({
...createNotificationDto,
recipient: user,
});
await this.notificationRepository.save(notification);
async createNotification(createNotificationDto: CreateNotificationDto, queryRunner?: QueryRunner) {
if (!queryRunner) {
const localQueryRunner = this.dataSource.createQueryRunner();
return { notification };
await localQueryRunner.connect();
await localQueryRunner.startTransaction();
try {
//
const notification = localQueryRunner.manager.create(Notification, {
...createNotificationDto,
recipient: { id: createNotificationDto.recipientId },
});
await localQueryRunner.manager.save(notification);
await localQueryRunner.commitTransaction();
return notification;
//
} catch (error) {
this.logger.error("error in createNotification", error);
await localQueryRunner.rollbackTransaction();
throw error;
//
} finally {
await localQueryRunner.release();
}
//
} else {
//
const notification = queryRunner.manager.create(Notification, {
...createNotificationDto,
recipient: { id: createNotificationDto.recipientId },
});
//
await queryRunner.manager.save(notification);
return notification;
}
}
//************************ */
@@ -42,20 +85,13 @@ export class NotificationsService {
//************************ */
async createLoginNotification(recipientId: string) {
const message = NotificationMessage.LOGIN_MESSAGE;
return this.createNotification({ title: NotificationMessage.LOGIN, message, recipientId });
}
//************************ */
async getAllNotifications(queryDto: SearchNotificationQueryDto, recipientId: string) {
return await this.notificationRepository.getAllNotifications(queryDto, recipientId);
}
//************************ */
async getOneNotification(id: string) {
async getNotificationById(id: string) {
const notification = await this.notificationRepository.findOneBy({ id });
if (!notification) throw new BadRequestException(NotificationMessage.NOTIFICATION_NOT_FOUND_OR_ACCESS_DENIED);
return { notification };
@@ -64,18 +100,199 @@ export class NotificationsService {
//************************ */
async markAllAsRead(userId: string) {
await this.notificationRepository.update(
{
recipient: {
id: userId,
},
},
{
isRead: true,
},
);
await this.notificationRepository.update({ recipient: { id: userId } }, { isRead: true });
return {
message: CommonMessage.UPDATE_SUCCESS,
};
}
//************************ */
async createLoginNotification(recipientId: string, sendNotifData: IBaseNotificationData) {
const loginDate = new Date().toLocaleString("fa-IR");
const message = NotificationMessage.LOGIN_MESSAGE.replace("[loginDate]", loginDate);
//sent notification based on the userSettings
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.USER_LOGIN, recipientId);
if (isActive) {
await this.smsService.sendLoginSms(sendNotifData.userPhone, loginDate);
//send email too
}
return this.createNotification({ title: NotificationMessage.LOGIN, message, recipientId });
}
//************************ */
async createInvoiceCreationNotification(recipientId: string, data: IInvoiceNotificationData, queryRunner: QueryRunner) {
const message = NotificationMessage.INVOICE_CREATION_MESSAGE.replace("[id]", data.invoiceId);
//sent notification based on the userSettings
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(
NotifType.CREATE_INVOICE,
recipientId,
queryRunner,
);
if (isActive) {
await this.smsService.sendInvoiceCreationSms(data.userPhone, data.invoiceId, data.price, data.items, dateFormat(data.dueDate));
//send email too
}
return this.createNotification({ title: NotificationMessage.INVOICE_CREATION, message, recipientId }, queryRunner);
}
//************************ */
async createAnnouncementNotification(recipientId: string, data: IAnnouncementNotificationData, queryRunner: QueryRunner) {
const message = NotificationMessage.ANNOUNCEMENT_MESSAGE;
//sent notification based on the userSettings
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(
NotifType.ANNOUNCEMENT,
recipientId,
queryRunner,
);
if (isActive) {
await this.smsService.sendAnnouncementSms(data.userPhone, data.title, data.description, dateFormat(data.date));
//send email too
}
return this.createNotification({ title: NotificationMessage.ANNOUNCEMENT, message, recipientId });
}
//************************ */
async createWalletChargeNotification(recipientId: string, data: IWalletNotificationData, queryRunner: QueryRunner) {
const message = NotificationMessage.WALLET_CHARGE_MESSAGE.replace("[amount]", numberFormat(data.amount));
//sent notification based on the userSettings
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(
NotifType.WALLET_CHARGE,
recipientId,
queryRunner,
);
if (isActive) {
await this.smsService.sendWalletChargeSms(
data.userPhone,
numberFormat(data.amount),
numberFormat(data.balance),
dateFormat(data.date),
);
//send email too
}
return this.createNotification({ title: NotificationMessage.WALLET_CHARGE, message, recipientId });
}
//************************ */
async createWalletDeductionNotification(recipientId: string, data: IWalletNotificationData, queryRunner: QueryRunner) {
const message = NotificationMessage.WALLET_DEDUCTION_MESSAGE.replace("[amount]", numberFormat(data.amount));
//sent notification based on the userSettings
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(
NotifType.WALLET_DEDUCTION,
recipientId,
queryRunner,
);
if (isActive) {
await this.smsService.sendWalletDeductionSms(
data.userPhone,
numberFormat(data.amount),
numberFormat(data.balance),
data.reason,
dateFormat(data.date),
);
//send email too
}
return this.createNotification({ title: NotificationMessage.WALLET_DEDUCTION, message, recipientId });
}
// //************************ */
async createAnswerTicketNotification(recipientId: string, data: ITicketNotificationData, queryRunner: QueryRunner) {
const message = NotificationMessage.ANSWER_TICKET_MESSAGE.replace("[ticketId]", data.ticketId);
//sent notification based on the userSettings
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(
NotifType.ANSWER_TICKET,
recipientId,
queryRunner,
);
if (isActive) {
await this.smsService.sendAnswerTicketSms(data.userPhone, data.ticketId, data.subject);
//send email too
}
return this.createNotification({ title: NotificationMessage.ANSWER_TICKET, message, recipientId });
}
// //************************ */
async createTicketNotification(recipientId: string, data: ITicketNotificationData, queryRunner: QueryRunner) {
const message = NotificationMessage.CREATE_TICKET_MESSAGE.replace("[ticketId]", data.ticketId);
//sent notification based on the userSettings
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(
NotifType.CREATE_TICKET,
recipientId,
queryRunner,
);
if (isActive) {
await this.smsService.sendCreateTicketSms(data.userPhone, data.ticketId, data.subject, dateFormat(data.date));
//send email too
}
return this.createNotification({ title: NotificationMessage.CREATE_TICKET, message, recipientId });
}
//************************ */
// async createBillInvoiceReminderNotification(recipientId: string, sendNotifData: ISendNotificationData) {
// const message = NotificationMessage.BILL_INVOICE_REMINDER_MESSAGE;
// //sent notification based on the userSettings
// const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.BILL_INVOICE_REMINDER, recipientId);
// if (isActive) {
// await this.smsService.sendBillInvoiceReminderSms(sendNotifData.userPhone, sendNotifData.dueDate, sendNotifData.invoiceId);
// //send email too
// }
// return this.createNotification({ title: NotificationMessage.BILL_INVOICE_REMINDER, message, recipientId });
// }
//************************ */
// async createBillInvoiceNotification(recipientId: string, sendNotifData: ISendNotificationData) {
// const message = NotificationMessage.BILL_INVOICE_MESSAGE;
// //sent notification based on the userSettings
// const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.BILL_INVOICE, recipientId);
// if (isActive) {
// await this.smsService.sendBillInvoiceSms(sendNotifData.userPhone, sendNotifData.amount, sendNotifData.invoiceId);
// //send email too
// }
// return this.createNotification({ title: NotificationMessage.BILL_INVOICE, message, recipientId });
// }
// //************************ */
// async createServiceNotification(recipientId: string, sendNotifData: ISendNotificationData) {
// const message = NotificationMessage.CREATE_SERVICE_MESSAGE.replace("[serviceName]", sendNotifData.serviceName);
// //sent notification based on the userSettings
// const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.CREATE_SERVICE, recipientId);
// if (isActive) {
// await this.smsService.sendCreateServiceSms(sendNotifData.userPhone, sendNotifData.serviceName);
// //send email too
// }
// return this.createNotification({ title: NotificationMessage.CREATE_SERVICE, message, recipientId });
// }
// //************************ */
// async createUnblockServiceNotification(recipientId: string, sendNotifData: ISendNotificationData) {
// const message = NotificationMessage.UNBLOCK_SERVICE_MESSAGE.replace("[serviceName]", sendNotifData.serviceName);
// //sent notification based on the userSettings
// const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.UNBLOCK_SERVICE, recipientId);
// if (isActive) {
// await this.smsService.sendUnblockServiceSms(sendNotifData.userPhone, sendNotifData.serviceName);
// //send email too
// }
// return this.createNotification({ title: NotificationMessage.UNBLOCK_SERVICE, message, recipientId });
// }
// //************************ */
// async createBlockServiceNotification(recipientId: string, sendNotifData: ISendNotificationData) {
// const message = NotificationMessage.BLOCK_SERVICE_MESSAGE.replace("[serviceName]", sendNotifData.serviceName);
// //sent notification based on the userSettings
// const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.BLOCK_SERVICE, recipientId);
// if (isActive) {
// await this.smsService.sendBlockServiceSms(sendNotifData.userPhone, sendNotifData.serviceName);
// //send email too
// }
// return this.createNotification({ title: NotificationMessage.BLOCK_SERVICE, message, recipientId });
// }
}