feat: fix
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import {
|
||||
INewCriticismNotificationData,
|
||||
INewCustomerNotificationData,
|
||||
INewSubscriptionNotificationData,
|
||||
IServiceReviewNotificationData,
|
||||
} from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class ServiceReviewHandler extends BaseNotificationHandler<IServiceReviewNotificationData> {
|
||||
protected readonly logger = new Logger(ServiceReviewHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IServiceReviewNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createNewServiceReviewNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "ServiceReview";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class NewCustomerHandler extends BaseNotificationHandler<INewCustomerNotificationData> {
|
||||
protected readonly logger = new Logger(NewCustomerHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: INewCustomerNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createNewCustomerNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "NewCustomer";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class NewSubscriptionHandler extends BaseNotificationHandler<INewSubscriptionNotificationData> {
|
||||
protected readonly logger = new Logger(NewSubscriptionHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: INewSubscriptionNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createNewSubscriptionNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "NewSubscription";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class NewCriticismHandler extends BaseNotificationHandler<INewCriticismNotificationData> {
|
||||
protected readonly logger = new Logger(NewCriticismHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: INewCriticismNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createNewCriticismNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "NewCriticism";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { IAnnouncementNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class AnnouncementHandler extends BaseNotificationHandler<IAnnouncementNotificationData> {
|
||||
protected readonly logger = new Logger(AnnouncementHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IAnnouncementNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createAnnouncementNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "Announcement";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
export abstract class BaseNotificationHandler<TData = unknown> {
|
||||
protected abstract readonly logger: Logger;
|
||||
|
||||
/**
|
||||
* Process the notification with automatic transaction management
|
||||
*/
|
||||
async processWithTransaction(recipientId: string, data: TData, queryRunner: QueryRunner): Promise<void> {
|
||||
try {
|
||||
await this.handle(recipientId, data, queryRunner);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to process ${this.getHandlerName()} notification: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
error instanceof Error ? error.stack : undefined,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the specific notification logic - to be implemented by each handler
|
||||
*/
|
||||
protected abstract handle(recipientId: string, data: TData, queryRunner: QueryRunner): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get the handler name for logging purposes
|
||||
*/
|
||||
protected abstract getHandlerName(): string;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { IBlogCommentNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class BlogCommentHandler extends BaseNotificationHandler<IBlogCommentNotificationData> {
|
||||
protected readonly logger = new Logger(BlogCommentHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IBlogCommentNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createNewBlogCommentNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "BlogComment";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Base handler
|
||||
export { BaseNotificationHandler } from "./base-notification.handler";
|
||||
|
||||
// Factory
|
||||
export { NotificationHandlerFactory } from "./notification-handler.factory";
|
||||
|
||||
// Special handlers (non-transactional)
|
||||
export { OtpHandler } from "./otp.handler";
|
||||
export { UserLoginHandler } from "./user-login.handler";
|
||||
|
||||
// Wallet handlers
|
||||
export { WalletChargeHandler, WalletDeductionHandler } from "./wallet.handler";
|
||||
|
||||
// Invoice handlers
|
||||
export {
|
||||
CreateInvoiceHandler,
|
||||
BillInvoiceReminderHandler,
|
||||
BillInvoiceHandler,
|
||||
ApproveInvoiceHandler,
|
||||
InvoiceOverdueHandler,
|
||||
RecurringInvoiceHandler,
|
||||
} from "./invoice.handler";
|
||||
|
||||
// Admin notification handlers
|
||||
export { BlogCommentHandler } from "./blog-comment.handler";
|
||||
export { ServiceReviewHandler, NewCustomerHandler, NewSubscriptionHandler, NewCriticismHandler } from "./admin-notifications.handler";
|
||||
|
||||
// User notification handlers
|
||||
export { AnnouncementHandler } from "./announcement.handler";
|
||||
|
||||
// Ticket handlers
|
||||
export { NewTicketHandler, AnswerTicketHandler, CreateTicketHandler, AssignTicketHandler } from "./ticket.handler";
|
||||
|
||||
// Service handlers
|
||||
export { BlockServiceHandler } from "./service.handler";
|
||||
|
||||
// Payment handlers
|
||||
export { PaymentReminderHandler, PaymentCancellationHandler } from "./payment.handler";
|
||||
@@ -0,0 +1,108 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { IInvoiceNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class CreateInvoiceHandler extends BaseNotificationHandler<IInvoiceNotificationData> {
|
||||
protected readonly logger = new Logger(CreateInvoiceHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IInvoiceNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createInvoiceCreationNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "CreateInvoice";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class BillInvoiceReminderHandler extends BaseNotificationHandler<IInvoiceNotificationData> {
|
||||
protected readonly logger = new Logger(BillInvoiceReminderHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IInvoiceNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createBillInvoiceReminderNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "BillInvoiceReminder";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class BillInvoiceHandler extends BaseNotificationHandler<IInvoiceNotificationData> {
|
||||
protected readonly logger = new Logger(BillInvoiceHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IInvoiceNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createBillInvoiceNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "BillInvoice";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ApproveInvoiceHandler extends BaseNotificationHandler<IInvoiceNotificationData> {
|
||||
protected readonly logger = new Logger(ApproveInvoiceHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IInvoiceNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createApprovedInvoiceNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "ApproveInvoice";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class InvoiceOverdueHandler extends BaseNotificationHandler<IInvoiceNotificationData> {
|
||||
protected readonly logger = new Logger(InvoiceOverdueHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IInvoiceNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createInvoiceOverdueNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "InvoiceOverdue";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RecurringInvoiceHandler extends BaseNotificationHandler<IInvoiceNotificationData> {
|
||||
protected readonly logger = new Logger(RecurringInvoiceHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IInvoiceNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createRecurringInvoiceNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "RecurringInvoice";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { NewCriticismHandler, NewCustomerHandler, NewSubscriptionHandler, ServiceReviewHandler } from "./admin-notifications.handler";
|
||||
import { AnnouncementHandler } from "./announcement.handler";
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { BlogCommentHandler } from "./blog-comment.handler";
|
||||
import {
|
||||
ApproveInvoiceHandler,
|
||||
BillInvoiceHandler,
|
||||
BillInvoiceReminderHandler,
|
||||
CreateInvoiceHandler,
|
||||
InvoiceOverdueHandler,
|
||||
RecurringInvoiceHandler,
|
||||
} from "./invoice.handler";
|
||||
import { PaymentCancellationHandler, PaymentReminderHandler } from "./payment.handler";
|
||||
import { BlockServiceHandler } from "./service.handler";
|
||||
import { AnswerTicketHandler, AssignTicketHandler, CreateTicketHandler, NewTicketHandler } from "./ticket.handler";
|
||||
import { WalletChargeHandler, WalletDeductionHandler } from "./wallet.handler";
|
||||
import { NotifType } from "../../settings/enums/notif-settings.enum";
|
||||
|
||||
@Injectable()
|
||||
export class NotificationHandlerFactory {
|
||||
private readonly logger = new Logger(NotificationHandlerFactory.name);
|
||||
private readonly handlers: Map<NotifType, BaseNotificationHandler> = new Map();
|
||||
|
||||
constructor(
|
||||
// Wallet handlers
|
||||
private readonly walletChargeHandler: WalletChargeHandler,
|
||||
private readonly walletDeductionHandler: WalletDeductionHandler,
|
||||
|
||||
// Invoice handlers
|
||||
private readonly createInvoiceHandler: CreateInvoiceHandler,
|
||||
private readonly billInvoiceReminderHandler: BillInvoiceReminderHandler,
|
||||
private readonly billInvoiceHandler: BillInvoiceHandler,
|
||||
private readonly approveInvoiceHandler: ApproveInvoiceHandler,
|
||||
private readonly invoiceOverdueHandler: InvoiceOverdueHandler,
|
||||
private readonly recurringInvoiceHandler: RecurringInvoiceHandler,
|
||||
|
||||
// Admin handlers
|
||||
private readonly blogCommentHandler: BlogCommentHandler,
|
||||
private readonly serviceReviewHandler: ServiceReviewHandler,
|
||||
private readonly newCustomerHandler: NewCustomerHandler,
|
||||
private readonly newSubscriptionHandler: NewSubscriptionHandler,
|
||||
private readonly newCriticismHandler: NewCriticismHandler,
|
||||
private readonly newTicketHandler: NewTicketHandler,
|
||||
|
||||
// User handlers
|
||||
private readonly announcementHandler: AnnouncementHandler,
|
||||
|
||||
// Ticket handlers
|
||||
private readonly answerTicketHandler: AnswerTicketHandler,
|
||||
private readonly createTicketHandler: CreateTicketHandler,
|
||||
private readonly assignTicketHandler: AssignTicketHandler,
|
||||
|
||||
// Service handlers
|
||||
private readonly blockServiceHandler: BlockServiceHandler,
|
||||
|
||||
// Payment handlers
|
||||
private readonly paymentReminderHandler: PaymentReminderHandler,
|
||||
private readonly paymentCancellationHandler: PaymentCancellationHandler,
|
||||
) {
|
||||
this.registerHandlers();
|
||||
}
|
||||
|
||||
private registerHandlers(): void {
|
||||
// Wallet notifications
|
||||
this.handlers.set(NotifType.WALLET_CHARGE, this.walletChargeHandler);
|
||||
this.handlers.set(NotifType.WALLET_DEDUCTION, this.walletDeductionHandler);
|
||||
|
||||
// Invoice notifications
|
||||
this.handlers.set(NotifType.CREATE_INVOICE, this.createInvoiceHandler);
|
||||
this.handlers.set(NotifType.BILL_INVOICE_REMINDER, this.billInvoiceReminderHandler);
|
||||
this.handlers.set(NotifType.BILL_INVOICE, this.billInvoiceHandler);
|
||||
this.handlers.set(NotifType.APPROVE_INVOICE, this.approveInvoiceHandler);
|
||||
this.handlers.set(NotifType.INVOICE_OVERDUE, this.invoiceOverdueHandler);
|
||||
this.handlers.set(NotifType.RECURRING_INVOICE, this.recurringInvoiceHandler);
|
||||
|
||||
// Admin notifications
|
||||
this.handlers.set(NotifType.NEW_BLOG_COMMENT, this.blogCommentHandler);
|
||||
this.handlers.set(NotifType.NEW_SERVICE_REVIEW, this.serviceReviewHandler);
|
||||
this.handlers.set(NotifType.NEW_CUSTOMER, this.newCustomerHandler);
|
||||
this.handlers.set(NotifType.NEW_SUBSCRIPTION, this.newSubscriptionHandler);
|
||||
this.handlers.set(NotifType.NEW_CRITICISM, this.newCriticismHandler);
|
||||
this.handlers.set(NotifType.NEW_TICKET, this.newTicketHandler);
|
||||
|
||||
// User notifications
|
||||
this.handlers.set(NotifType.ANNOUNCEMENT, this.announcementHandler);
|
||||
|
||||
// Ticket notifications
|
||||
this.handlers.set(NotifType.ANSWER_TICKET, this.answerTicketHandler);
|
||||
this.handlers.set(NotifType.CREATE_TICKET, this.createTicketHandler);
|
||||
this.handlers.set(NotifType.ASSIGN_TICKET, this.assignTicketHandler);
|
||||
|
||||
// Service notifications
|
||||
this.handlers.set(NotifType.BLOCK_SERVICE, this.blockServiceHandler);
|
||||
|
||||
// Payment notifications
|
||||
this.handlers.set(NotifType.PAYMENT_REMINDER, this.paymentReminderHandler);
|
||||
this.handlers.set(NotifType.PAYMENT_CANCELLATION, this.paymentCancellationHandler);
|
||||
|
||||
this.logger.log(`Registered ${this.handlers.size} notification handlers`);
|
||||
}
|
||||
|
||||
async processNotification(type: NotifType, recipientId: string, data: unknown, queryRunner: QueryRunner): Promise<void> {
|
||||
const handler = this.handlers.get(type);
|
||||
|
||||
if (!handler) {
|
||||
this.logger.warn(`No handler found for notification type: ${type}`);
|
||||
throw new Error(`No handler registered for notification type: ${type}`);
|
||||
}
|
||||
|
||||
await handler.processWithTransaction(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
getRegisteredTypes(): NotifType[] {
|
||||
return Array.from(this.handlers.keys());
|
||||
}
|
||||
|
||||
isTypeSupported(type: NotifType): boolean {
|
||||
return this.handlers.has(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
|
||||
import { SmsService } from "../../utils/providers/sms.service";
|
||||
import { OtpJobData } from "../interfaces/INotification-job-data";
|
||||
|
||||
@Injectable()
|
||||
export class OtpHandler {
|
||||
private readonly logger = new Logger(OtpHandler.name);
|
||||
|
||||
constructor(private readonly smsService: SmsService) {}
|
||||
|
||||
async processOtp(otpData: OtpJobData): Promise<void> {
|
||||
try {
|
||||
const { phone, code } = otpData;
|
||||
await this.smsService.sendSmsVerifyCode(phone, code);
|
||||
this.logger.log(`OTP sent successfully to ${phone}`);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to send OTP: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
error instanceof Error ? error.stack : undefined,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { IPaymentNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class PaymentReminderHandler extends BaseNotificationHandler<IPaymentNotificationData> {
|
||||
protected readonly logger = new Logger(PaymentReminderHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IPaymentNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createPaymentReminderNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "PaymentReminder";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PaymentCancellationHandler extends BaseNotificationHandler<IPaymentNotificationData> {
|
||||
protected readonly logger = new Logger(PaymentCancellationHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IPaymentNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createPaymentCancellationNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "PaymentCancellation";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { ISubscriptionNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class BlockServiceHandler extends BaseNotificationHandler<ISubscriptionNotificationData> {
|
||||
protected readonly logger = new Logger(BlockServiceHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: ISubscriptionNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createBlockServiceNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "BlockService";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { INewTicketNotificationData, ITicketNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class NewTicketHandler extends BaseNotificationHandler<INewTicketNotificationData> {
|
||||
protected readonly logger = new Logger(NewTicketHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: INewTicketNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createNewTicketGlobalNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "NewTicket";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AnswerTicketHandler extends BaseNotificationHandler<ITicketNotificationData> {
|
||||
protected readonly logger = new Logger(AnswerTicketHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: ITicketNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createAnswerTicketNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "AnswerTicket";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateTicketHandler extends BaseNotificationHandler<ITicketNotificationData> {
|
||||
protected readonly logger = new Logger(CreateTicketHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: ITicketNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createTicketNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "CreateTicket";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AssignTicketHandler extends BaseNotificationHandler<ITicketNotificationData> {
|
||||
protected readonly logger = new Logger(AssignTicketHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: ITicketNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createAssignTicketNotificationForAdmin(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "AssignTicket";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
|
||||
import { IBaseNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class UserLoginHandler {
|
||||
private readonly logger = new Logger(UserLoginHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {}
|
||||
|
||||
async processUserLogin(recipientId: string, data: IBaseNotificationData): Promise<void> {
|
||||
try {
|
||||
await this.notificationsService.createLoginNotification(recipientId, data);
|
||||
this.logger.log(`User login notification sent successfully to ${recipientId}`);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to send user login notification: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
error instanceof Error ? error.stack : undefined,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { BaseNotificationHandler } from "./base-notification.handler";
|
||||
import { IWalletNotificationData } from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
@Injectable()
|
||||
export class WalletChargeHandler extends BaseNotificationHandler<IWalletNotificationData> {
|
||||
protected readonly logger = new Logger(WalletChargeHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IWalletNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createWalletChargeNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "WalletCharge";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class WalletDeductionHandler extends BaseNotificationHandler<IWalletNotificationData> {
|
||||
protected readonly logger = new Logger(WalletDeductionHandler.name);
|
||||
|
||||
constructor(private readonly notificationsService: NotificationsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async handle(recipientId: string, data: IWalletNotificationData, queryRunner: QueryRunner): Promise<void> {
|
||||
await this.notificationsService.createWalletDeductionNotification(recipientId, data, queryRunner);
|
||||
}
|
||||
|
||||
protected getHandlerName(): string {
|
||||
return "WalletDeduction";
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,42 @@ import { ConfigModule } from "@nestjs/config";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
import { NOTIFICATION } from "./constants";
|
||||
import { LoggerModule } from "../logger/logger.module";
|
||||
import { SettingModule } from "../settings/settings.module";
|
||||
import { UtilsModule } from "../utils/utils.module";
|
||||
import { Notification } from "./entities/notification.entity";
|
||||
import {
|
||||
AnnouncementHandler,
|
||||
AnswerTicketHandler,
|
||||
ApproveInvoiceHandler,
|
||||
AssignTicketHandler,
|
||||
BillInvoiceHandler,
|
||||
BillInvoiceReminderHandler,
|
||||
BlockServiceHandler,
|
||||
BlogCommentHandler,
|
||||
CreateInvoiceHandler,
|
||||
CreateTicketHandler,
|
||||
InvoiceOverdueHandler,
|
||||
NewCriticismHandler,
|
||||
NewCustomerHandler,
|
||||
NewSubscriptionHandler,
|
||||
NewTicketHandler,
|
||||
NotificationHandlerFactory,
|
||||
OtpHandler,
|
||||
PaymentCancellationHandler,
|
||||
PaymentReminderHandler,
|
||||
RecurringInvoiceHandler,
|
||||
ServiceReviewHandler,
|
||||
UserLoginHandler,
|
||||
WalletChargeHandler,
|
||||
WalletDeductionHandler,
|
||||
} from "./handlers";
|
||||
import { NotificationController } from "./notifications.controller";
|
||||
import { LoggerModule } from "../logger/logger.module";
|
||||
import { NotificationsService } from "./providers/notifications.service";
|
||||
import { NotificationProcessor } from "./queue/notification.processor";
|
||||
import { NotificationQueue } from "./queue/notification.queue";
|
||||
import { NotificationRepository } from "./repositories/notifications.repository";
|
||||
import { NotificationSetting } from "../settings/entities/notification-setting.entity";
|
||||
import { SettingModule } from "../settings/settings.module";
|
||||
import { UtilsModule } from "../utils/utils.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -36,7 +62,37 @@ import { NotificationSetting } from "../settings/entities/notification-setting.e
|
||||
UtilsModule,
|
||||
SettingModule,
|
||||
],
|
||||
providers: [NotificationRepository, NotificationsService, NotificationQueue, NotificationProcessor],
|
||||
providers: [
|
||||
NotificationRepository,
|
||||
NotificationsService,
|
||||
NotificationQueue,
|
||||
NotificationProcessor,
|
||||
// notification handlers
|
||||
NotificationHandlerFactory,
|
||||
OtpHandler,
|
||||
UserLoginHandler,
|
||||
WalletChargeHandler,
|
||||
WalletDeductionHandler,
|
||||
CreateInvoiceHandler,
|
||||
BillInvoiceReminderHandler,
|
||||
BillInvoiceHandler,
|
||||
ApproveInvoiceHandler,
|
||||
InvoiceOverdueHandler,
|
||||
RecurringInvoiceHandler,
|
||||
BlogCommentHandler,
|
||||
ServiceReviewHandler,
|
||||
NewCustomerHandler,
|
||||
NewSubscriptionHandler,
|
||||
NewCriticismHandler,
|
||||
NewTicketHandler,
|
||||
AnnouncementHandler,
|
||||
AnswerTicketHandler,
|
||||
CreateTicketHandler,
|
||||
AssignTicketHandler,
|
||||
BlockServiceHandler,
|
||||
PaymentReminderHandler,
|
||||
PaymentCancellationHandler,
|
||||
],
|
||||
controllers: [NotificationController],
|
||||
exports: [NotificationRepository, NotificationsService, NotificationQueue],
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import { DataSource, QueryRunner } from "typeorm";
|
||||
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 { EmailService } from "../../utils/providers/email.service";
|
||||
// import { EmailService } from "../../utils/providers/email.service";
|
||||
import { dateFormat, numberFormat } from "../../utils/providers/international.utils";
|
||||
import { SmsService } from "../../utils/providers/sms.service";
|
||||
import { CreateNotificationDto } from "../DTO/create-notification.dto";
|
||||
@@ -35,7 +35,7 @@ export class NotificationsService {
|
||||
private readonly notificationRepository: NotificationRepository,
|
||||
private readonly smsService: SmsService,
|
||||
private readonly userSettingsService: UserSettingsService,
|
||||
private readonly emailService: EmailService,
|
||||
// private readonly emailService: EmailService,
|
||||
) {}
|
||||
|
||||
//************************ */
|
||||
@@ -133,7 +133,7 @@ export class NotificationsService {
|
||||
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.USER_LOGIN, recipientId);
|
||||
if (isActive) {
|
||||
await this.smsService.sendLoginSms(data.userPhone, loginDate);
|
||||
if (data.userEmail) await this.emailService.sendLoginEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendLoginEmail(data);
|
||||
}
|
||||
return this.createNotification({ title: NotificationMessage.LOGIN, type: NotifType.USER_LOGIN, message, recipientId });
|
||||
}
|
||||
@@ -156,7 +156,7 @@ export class NotificationsService {
|
||||
dateFormat(data.createDate),
|
||||
dateFormat(data.dueDate),
|
||||
);
|
||||
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.INVOICE_CREATION, type: NotifType.CREATE_INVOICE, message, recipientId },
|
||||
@@ -175,7 +175,7 @@ export class NotificationsService {
|
||||
);
|
||||
if (isActive) {
|
||||
await this.smsService.sendAnnouncementSms(data.userPhone, data.title, data.description, dateFormat(data.date));
|
||||
if (data.userEmail) await this.emailService.sendAnnouncementEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendAnnouncementEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.ANNOUNCEMENT, type: NotifType.ANNOUNCEMENT, message, recipientId },
|
||||
@@ -195,7 +195,7 @@ export class NotificationsService {
|
||||
);
|
||||
if (isActive) {
|
||||
await this.smsService.sendWalletChargeSms(data.userPhone, data.amount, data.balance, dateFormat(data.date));
|
||||
if (data.userEmail) await this.emailService.sendWalletEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendWalletEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.WALLET_CHARGE, type: NotifType.WALLET_CHARGE, message, recipientId },
|
||||
@@ -215,7 +215,7 @@ export class NotificationsService {
|
||||
);
|
||||
if (isActive) {
|
||||
await this.smsService.sendWalletDeductionSms(data.userPhone, data.amount, data.balance, data.reason, dateFormat(data.date));
|
||||
if (data.userEmail) await this.emailService.sendWalletEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendWalletEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.WALLET_DEDUCTION, type: NotifType.WALLET_DEDUCTION, message, recipientId },
|
||||
@@ -235,7 +235,7 @@ export class NotificationsService {
|
||||
);
|
||||
if (isActive) {
|
||||
await this.smsService.sendAnswerTicketSms(data.userPhone, data.ticketId, data.subject);
|
||||
if (data.userEmail) await this.emailService.sendTicketEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendTicketEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.ANSWER_TICKET, type: NotifType.ANSWER_TICKET, message, recipientId },
|
||||
@@ -255,7 +255,7 @@ export class NotificationsService {
|
||||
);
|
||||
if (isActive) {
|
||||
await this.smsService.sendCreateTicketSms(data.userPhone, data.ticketId, data.subject, dateFormat(data.date));
|
||||
if (data.userEmail) await this.emailService.sendTicketEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendTicketEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.CREATE_TICKET, type: NotifType.CREATE_TICKET, message, recipientId },
|
||||
@@ -273,7 +273,7 @@ export class NotificationsService {
|
||||
);
|
||||
if (isActive) {
|
||||
await this.smsService.sendTicketAssignedToAdminSms(data.userPhone, data.ticketId, data.subject, data.user!);
|
||||
if (data.userEmail) await this.emailService.sendTicketEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendTicketEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.ASSIGN_TICKET, type: NotifType.ASSIGN_TICKET, message, recipientId },
|
||||
@@ -289,7 +289,7 @@ export class NotificationsService {
|
||||
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.BILL_INVOICE_REMINDER, recipientId);
|
||||
if (isActive) {
|
||||
await this.smsService.sendInvoicePaymentReminderSms(data.userPhone, data.invoiceId, data.price, dateFormat(data.dueDate));
|
||||
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.BILL_INVOICE_REMINDER, type: NotifType.BILL_INVOICE_REMINDER, message, recipientId },
|
||||
@@ -305,7 +305,7 @@ export class NotificationsService {
|
||||
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.BILL_INVOICE, recipientId);
|
||||
if (isActive) {
|
||||
await this.smsService.sendInvoicePaidSms(data.userPhone, data.invoiceId, data.price, dateFormat(data.paidAt!));
|
||||
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.BILL_INVOICE, type: NotifType.BILL_INVOICE, message, recipientId },
|
||||
@@ -320,7 +320,7 @@ export class NotificationsService {
|
||||
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.APPROVE_INVOICE, recipientId);
|
||||
if (isActive) {
|
||||
await this.smsService.sendInvoiceApprovedSms(data.userPhone, data.invoiceId, data.price, dateFormat(data.dueDate));
|
||||
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.APPROVED_INVOICE, type: NotifType.BILL_INVOICE, message, recipientId },
|
||||
@@ -340,7 +340,7 @@ export class NotificationsService {
|
||||
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.INVOICE_OVERDUE, recipientId);
|
||||
if (isActive) {
|
||||
await this.smsService.sendInvoiceOverdueSms(data.userPhone, data.invoiceId, data.price, data.lateFee || 0, dateFormat(data.dueDate));
|
||||
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.INVOICE_OVERDUE, type: NotifType.INVOICE_OVERDUE, message, recipientId },
|
||||
@@ -356,7 +356,7 @@ export class NotificationsService {
|
||||
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.RECURRING_INVOICE, recipientId);
|
||||
if (isActive) {
|
||||
await this.smsService.sendRecurringInvoiceDraftSms(data.userPhone, data.invoiceId, data.price);
|
||||
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.RECURRING_INVOICE, type: NotifType.RECURRING_INVOICE, message, recipientId },
|
||||
@@ -371,14 +371,14 @@ export class NotificationsService {
|
||||
const { isActive } = await this.userSettingsService.getUserNotificationSettingWithType(NotifType.BLOCK_SERVICE, recipientId);
|
||||
if (isActive) {
|
||||
await this.smsService.sendBlockServiceSms(data.userPhone, data.invoiceId, data.planName);
|
||||
if (data.userEmail)
|
||||
await this.emailService.sendAnnouncementEmail({
|
||||
userEmail: data.userEmail,
|
||||
userPhone: data.userPhone,
|
||||
title: NotificationMessage.BLOCK_SERVICE,
|
||||
description: message,
|
||||
date: new Date(),
|
||||
});
|
||||
// if (data.userEmail)
|
||||
// await this.emailService.sendAnnouncementEmail({
|
||||
// userEmail: data.userEmail,
|
||||
// userPhone: data.userPhone,
|
||||
// title: NotificationMessage.BLOCK_SERVICE,
|
||||
// description: message,
|
||||
// date: new Date(),
|
||||
// });
|
||||
}
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.BLOCK_SERVICE, type: NotifType.BLOCK_SERVICE, message, recipientId },
|
||||
@@ -399,7 +399,7 @@ export class NotificationsService {
|
||||
|
||||
if (isActive) {
|
||||
await this.smsService.sendPaymentReminderSms(data.userPhone, data.amount);
|
||||
if (data.userEmail) await this.emailService.sendPaymentReminderEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendPaymentReminderEmail(data);
|
||||
}
|
||||
|
||||
return this.createNotification(
|
||||
@@ -425,7 +425,7 @@ export class NotificationsService {
|
||||
|
||||
if (isActive) {
|
||||
await this.smsService.sendPaymentCancellationSms(data.userPhone, data.amount);
|
||||
if (data.userEmail) await this.emailService.sendPaymentCancellationEmail(data);
|
||||
// if (data.userEmail) await this.emailService.sendPaymentCancellationEmail(data);
|
||||
}
|
||||
|
||||
return this.createNotification(
|
||||
@@ -448,13 +448,13 @@ export class NotificationsService {
|
||||
const message = NotificationMessage.NEW_BLOG_COMMENT_MESSAGE.replace("[blogTitle]", data.blogTitle);
|
||||
|
||||
await this.smsService.sendBlogNewCommentSms(data.userPhone, data.blogTitle, data.fullName);
|
||||
if (data.userEmail) {
|
||||
await this.emailService.sendAdminNotificationEmail({
|
||||
userEmail: data.userEmail,
|
||||
title: NotificationMessage.NEW_BLOG_COMMENT,
|
||||
message,
|
||||
});
|
||||
}
|
||||
// if (data.userEmail) {
|
||||
// await this.emailService.sendAdminNotificationEmail({
|
||||
// userEmail: data.userEmail,
|
||||
// title: NotificationMessage.NEW_BLOG_COMMENT,
|
||||
// message,
|
||||
// });
|
||||
// }
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.NEW_BLOG_COMMENT, type: NotifType.NEW_BLOG_COMMENT, message, recipientId },
|
||||
queryRunner,
|
||||
@@ -465,13 +465,13 @@ export class NotificationsService {
|
||||
const message = NotificationMessage.NEW_SERVICE_REVIEW_MESSAGE.replace("[serviceName]", data.serviceName);
|
||||
|
||||
await this.smsService.sendNewServiceReviewSms(data.userPhone, data.serviceName, data.fullName);
|
||||
if (data.userEmail) {
|
||||
await this.emailService.sendAdminNotificationEmail({
|
||||
userEmail: data.userEmail,
|
||||
title: NotificationMessage.NEW_SERVICE_REVIEW,
|
||||
message,
|
||||
});
|
||||
}
|
||||
// if (data.userEmail) {
|
||||
// await this.emailService.sendAdminNotificationEmail({
|
||||
// userEmail: data.userEmail,
|
||||
// title: NotificationMessage.NEW_SERVICE_REVIEW,
|
||||
// message,
|
||||
// });
|
||||
// }
|
||||
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.NEW_SERVICE_REVIEW, type: NotifType.NEW_SERVICE_REVIEW, message, recipientId },
|
||||
@@ -484,13 +484,13 @@ export class NotificationsService {
|
||||
const message = NotificationMessage.NEW_CUSTOMER_MESSAGE.replace("[fullName]", data.fullName);
|
||||
|
||||
await this.smsService.sendNewCustomerSms(data.userPhone, data.mobile, data.fullName);
|
||||
if (data.userEmail) {
|
||||
await this.emailService.sendAdminNotificationEmail({
|
||||
userEmail: data.userEmail,
|
||||
title: NotificationMessage.NEW_CUSTOMER,
|
||||
message,
|
||||
});
|
||||
}
|
||||
// if (data.userEmail) {
|
||||
// await this.emailService.sendAdminNotificationEmail({
|
||||
// userEmail: data.userEmail,
|
||||
// title: NotificationMessage.NEW_CUSTOMER,
|
||||
// message,
|
||||
// });
|
||||
// }
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.NEW_CUSTOMER, type: NotifType.NEW_CUSTOMER, message, recipientId },
|
||||
queryRunner,
|
||||
@@ -502,14 +502,14 @@ export class NotificationsService {
|
||||
const message = NotificationMessage.NEW_SUBSCRIPTION_MESSAGE.replace("[serviceName]", data.serviceName);
|
||||
|
||||
await this.smsService.sendNewSubscriptionSms(data.userPhone, data.serviceName, data.fullName, dateFormat(data.date));
|
||||
if (data.userEmail) {
|
||||
await this.emailService.sendAdminNotificationEmail({
|
||||
userEmail: data.userEmail,
|
||||
title: NotificationMessage.NEW_SUBSCRIPTION,
|
||||
fullName: data.fullName,
|
||||
message,
|
||||
});
|
||||
}
|
||||
// if (data.userEmail) {
|
||||
// await this.emailService.sendAdminNotificationEmail({
|
||||
// userEmail: data.userEmail,
|
||||
// title: NotificationMessage.NEW_SUBSCRIPTION,
|
||||
// fullName: data.fullName,
|
||||
// message,
|
||||
// });
|
||||
// }
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.NEW_SUBSCRIPTION, type: NotifType.NEW_SUBSCRIPTION, message, recipientId },
|
||||
queryRunner,
|
||||
@@ -521,13 +521,13 @@ export class NotificationsService {
|
||||
const message = NotificationMessage.NEW_TICKET_MESSAGE.replace("[ticketSubject]", data.ticketSubject);
|
||||
|
||||
await this.smsService.sendGlobalNewTicketSms(data.userPhone, data.ticketId, data.ticketSubject, data.fullName);
|
||||
if (data.userEmail) {
|
||||
await this.emailService.sendAdminNotificationEmail({
|
||||
userEmail: data.userEmail,
|
||||
title: NotificationMessage.NEW_TICKET,
|
||||
message,
|
||||
});
|
||||
}
|
||||
// if (data.userEmail) {
|
||||
// await this.emailService.sendAdminNotificationEmail({
|
||||
// userEmail: data.userEmail,
|
||||
// title: NotificationMessage.NEW_TICKET,
|
||||
// message,
|
||||
// });
|
||||
// }
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.NEW_TICKET, type: NotifType.NEW_TICKET, message, recipientId },
|
||||
queryRunner,
|
||||
@@ -539,13 +539,13 @@ export class NotificationsService {
|
||||
const message = NotificationMessage.NEW_CRITICISM_MESSAGE.replace("[title]", data.title);
|
||||
|
||||
await this.smsService.sendNewCriticismSms(data.userPhone, data.title, data.fullName);
|
||||
if (data.userEmail) {
|
||||
await this.emailService.sendAdminNotificationEmail({
|
||||
userEmail: data.userEmail,
|
||||
title: NotificationMessage.NEW_CRITICISM,
|
||||
message,
|
||||
});
|
||||
}
|
||||
// if (data.userEmail) {
|
||||
// await this.emailService.sendAdminNotificationEmail({
|
||||
// userEmail: data.userEmail,
|
||||
// title: NotificationMessage.NEW_CRITICISM,
|
||||
// message,
|
||||
// });
|
||||
// }
|
||||
return this.createNotification(
|
||||
{ title: NotificationMessage.NEW_CRITICISM, type: NotifType.NEW_CRITICISM, message, recipientId },
|
||||
queryRunner,
|
||||
|
||||
@@ -6,24 +6,12 @@ import { DataSource } from "typeorm";
|
||||
import { WorkerProcessor } from "../../../common/queues/worker.processor";
|
||||
import { LoggerService } from "../../logger/logger.service";
|
||||
import { NotifType } from "../../settings/enums/notif-settings.enum";
|
||||
import { SmsService } from "../../utils/providers/sms.service";
|
||||
import { NOTIFICATION } from "../constants";
|
||||
import { NotificationHandlerFactory } from "../handlers/notification-handler.factory";
|
||||
import { OtpHandler } from "../handlers/otp.handler";
|
||||
import { UserLoginHandler } from "../handlers/user-login.handler";
|
||||
import { NotificationJobData, OtpJobData } from "../interfaces/INotification-job-data";
|
||||
import {
|
||||
IAnnouncementNotificationData,
|
||||
IBlogCommentNotificationData,
|
||||
IInvoiceNotificationData,
|
||||
INewCriticismNotificationData,
|
||||
INewCustomerNotificationData,
|
||||
INewSubscriptionNotificationData,
|
||||
INewTicketNotificationData,
|
||||
IPaymentNotificationData,
|
||||
IServiceReviewNotificationData,
|
||||
ISubscriptionNotificationData,
|
||||
ITicketNotificationData,
|
||||
IWalletNotificationData,
|
||||
} from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
import { IBaseNotificationData } from "../interfaces/ISendNotificationData";
|
||||
|
||||
@Processor(NOTIFICATION.QUEUE_NAME, {
|
||||
concurrency: NOTIFICATION.SEND_NOTIFICATION_JOB_CONCURRENCY,
|
||||
@@ -35,145 +23,59 @@ export class NotificationProcessor extends WorkerProcessor {
|
||||
protected readonly logger = new Logger(NotificationProcessor.name);
|
||||
|
||||
constructor(
|
||||
private readonly notificationsService: NotificationsService,
|
||||
private readonly dataSource: DataSource,
|
||||
protected readonly loggerService: LoggerService,
|
||||
private readonly smsService: SmsService,
|
||||
private readonly notificationHandlerFactory: NotificationHandlerFactory,
|
||||
private readonly otpHandler: OtpHandler,
|
||||
private readonly userLoginHandler: UserLoginHandler,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async process(job: Job<NotificationJobData | OtpJobData>, token?: string) {
|
||||
this.logger.log(`Processing notification job: ${job.name} ${token ? `with token: ${token}` : ""}`);
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
let heartbeat: NodeJS.Timeout | undefined;
|
||||
|
||||
try {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
heartbeat = setInterval(() => job.updateProgress(50), 10000);
|
||||
|
||||
if (job.name === NOTIFICATION.SEND_LOGIN_OTP_JOB_NAME) {
|
||||
const { phone, code } = job.data as OtpJobData;
|
||||
await this.smsService.sendSmsVerifyCode(phone, code);
|
||||
//
|
||||
await this.otpHandler.processOtp(job.data as OtpJobData);
|
||||
} else if (job.name === NOTIFICATION.SEND_NOTIFICATION_JOB_NAME) {
|
||||
const { type, recipientId, data } = job.data as NotificationJobData;
|
||||
switch (type) {
|
||||
// Admin Notifications
|
||||
case NotifType.NEW_BLOG_COMMENT:
|
||||
await this.notificationsService.createNewBlogCommentNotification(
|
||||
recipientId,
|
||||
data as IBlogCommentNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
case NotifType.NEW_SERVICE_REVIEW:
|
||||
await this.notificationsService.createNewServiceReviewNotification(
|
||||
recipientId,
|
||||
data as IServiceReviewNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
case NotifType.NEW_CUSTOMER:
|
||||
await this.notificationsService.createNewCustomerNotification(recipientId, data as INewCustomerNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.NEW_SUBSCRIPTION:
|
||||
await this.notificationsService.createNewSubscriptionNotification(
|
||||
recipientId,
|
||||
data as INewSubscriptionNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
case NotifType.NEW_CRITICISM:
|
||||
await this.notificationsService.createNewCriticismNotification(recipientId, data as INewCriticismNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.NEW_TICKET:
|
||||
await this.notificationsService.createNewTicketGlobalNotification(recipientId, data as INewTicketNotificationData, queryRunner);
|
||||
break;
|
||||
// User Notifications
|
||||
case NotifType.USER_LOGIN:
|
||||
await this.notificationsService.createLoginNotification(recipientId, data);
|
||||
break;
|
||||
case NotifType.ANNOUNCEMENT:
|
||||
await this.notificationsService.createAnnouncementNotification(recipientId, data as IAnnouncementNotificationData, queryRunner);
|
||||
break;
|
||||
// Wallet Notifications
|
||||
case NotifType.WALLET_CHARGE:
|
||||
await this.notificationsService.createWalletChargeNotification(recipientId, data as IWalletNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.WALLET_DEDUCTION:
|
||||
await this.notificationsService.createWalletDeductionNotification(recipientId, data as IWalletNotificationData, queryRunner);
|
||||
break;
|
||||
// Ticket Notifications
|
||||
case NotifType.ANSWER_TICKET:
|
||||
await this.notificationsService.createAnswerTicketNotification(recipientId, data as ITicketNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.CREATE_TICKET:
|
||||
await this.notificationsService.createTicketNotification(recipientId, data as ITicketNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.ASSIGN_TICKET:
|
||||
await this.notificationsService.createAssignTicketNotificationForAdmin(
|
||||
recipientId,
|
||||
data as ITicketNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
// Invoice Notifications
|
||||
case NotifType.CREATE_INVOICE:
|
||||
await this.notificationsService.createInvoiceCreationNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.BILL_INVOICE_REMINDER:
|
||||
await this.notificationsService.createBillInvoiceReminderNotification(
|
||||
recipientId,
|
||||
data as IInvoiceNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
case NotifType.BILL_INVOICE:
|
||||
await this.notificationsService.createBillInvoiceNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.APPROVE_INVOICE:
|
||||
await this.notificationsService.createApprovedInvoiceNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.INVOICE_OVERDUE:
|
||||
await this.notificationsService.createInvoiceOverdueNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.RECURRING_INVOICE:
|
||||
await this.notificationsService.createRecurringInvoiceNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
// Service Notifications
|
||||
case NotifType.BLOCK_SERVICE:
|
||||
await this.notificationsService.createBlockServiceNotification(recipientId, data as ISubscriptionNotificationData, queryRunner);
|
||||
break;
|
||||
// Payment Notifications
|
||||
case NotifType.PAYMENT_REMINDER:
|
||||
await this.notificationsService.createPaymentReminderNotification(recipientId, data as IPaymentNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.PAYMENT_CANCELLATION:
|
||||
await this.notificationsService.createPaymentCancellationNotification(
|
||||
recipientId,
|
||||
data as IPaymentNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
this.logger.warn(`Unknown notification type: ${type}`);
|
||||
|
||||
// Handle special cases that don't use transactions
|
||||
if (type === NotifType.USER_LOGIN) {
|
||||
await this.userLoginHandler.processUserLogin(recipientId, data as IBaseNotificationData);
|
||||
} else {
|
||||
// Handle notifications that require transactions
|
||||
await this.processWithTransaction(type, recipientId, data);
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(`Unknown job name: ${job.name}`);
|
||||
}
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to process notification: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
error instanceof Error ? error.stack : undefined,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async processWithTransaction(type: NotifType, recipientId: string, data: any): Promise<void> {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
try {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
await this.notificationHandlerFactory.processNotification(type, recipientId, data, queryRunner);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
if (heartbeat) clearInterval(heartbeat);
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const SUBSCRIPTIONS = Object.freeze({
|
||||
PROVISIONING_QUEUE_PREFIX: "provisioning",
|
||||
PROVISIONING_QUEUE_PREFIX: "subs",
|
||||
PROVISIONING_QUEUE_NAME: "provisioning",
|
||||
PROVISIONING_JOB_NAME: "business.created",
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { BadRequestException, ForbiddenException, Injectable } from "@nestjs/common";
|
||||
import { BadRequestException, ForbiddenException, Injectable, Logger } from "@nestjs/common";
|
||||
import { Queue } from "bullmq";
|
||||
import dayjs from "dayjs";
|
||||
import Decimal from "decimal.js";
|
||||
@@ -28,6 +28,8 @@ import { UserSubscriptionsRepository } from "../repositories/user-subscriptions.
|
||||
|
||||
@Injectable()
|
||||
export class SubscriptionsService {
|
||||
private readonly logger: Logger = new Logger(SubscriptionsService.name);
|
||||
|
||||
constructor(
|
||||
@InjectQueue(SUBSCRIPTIONS.PROVISIONING_QUEUE_NAME) private readonly provisioningQueue: Queue,
|
||||
private readonly subscriptionsPlanRepository: SubscriptionsPlanRepository,
|
||||
@@ -337,6 +339,7 @@ export class SubscriptionsService {
|
||||
|
||||
//************************************ */
|
||||
async addProvisioningJob(userSubscription: UserSubscription, plan: SubscriptionPlan, user: User) {
|
||||
this.logger.debug(`Adding provisioning job for user ${user.id} and subscription ${userSubscription.id}`);
|
||||
await this.provisioningQueue.add(
|
||||
SUBSCRIPTIONS.PROVISIONING_JOB_NAME,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user