import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { EntityManager } from '@mikro-orm/postgresql'; import { Notification } from '../entities/notification.entity'; import { NotificationPreferenceService } from './notification-preference.service'; import { NotificationQueueService } from './notification-queue.service'; import { NotificationsGateway } from '../notifications.gateway'; import { NotifChannelEnum, NotifRequest, NotifTitleEnum } from '../interfaces/notification.interface'; @Injectable() export class NotificationService { private readonly logger = new Logger(NotificationService.name); constructor( private readonly em: EntityManager, private readonly preferenceService: NotificationPreferenceService, private readonly queueService: NotificationQueueService, private readonly notificationGateway: NotificationsGateway, ) {} async sendNotification(params: NotifRequest): Promise { const { recipients, message, metadata, restaurantId } = params; // create Database notifications const notifications = await this.createAdminBulkNotifications( recipients.map(recipient => ({ restaurantId, title: message.title, content: message.content, adminId: 'adminId' in recipient ? recipient.adminId : null, userId: 'userId' in recipient ? recipient.userId : null, })), ); // get admin prefrences const preference = await this.preferenceService.findByRestaurantAndType(restaurantId, message.title); if (preference?.channels?.length === 0) { this.logger.warn(`Notification type is NONE for restaurant ${restaurantId}, title ${message.title}`); return notifications; } // send in app notification if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) { await this.queueService.addBulkInAppNotifications( notifications.map(notification => ({ recipient: { adminId: notification.admin?.id || '', restaurantId }, subject: message.title, body: message.content, notificationId: notification.id, })), ); } // add sms notifications to queue if (preference?.channels?.includes(NotifChannelEnum.SMS)) { await this.queueService.addBulkSmsNotifications( recipients.map(recipient => ({ recipient, templateId: message.sms.templateId, parameters: message.sms.parameters, })), ); } this.logger.log(`Queued notification for restaurant ${restaurantId}, title ${message.title}`); return notifications; } async createAdminBulkNotifications( params: { restaurantId: string; title: NotifTitleEnum; content: string; adminId: string | null; userId: string | null; }[], ): Promise { const notifications = params.map(param => { return this.em.create(Notification, { restaurant: param.restaurantId, admin: param.adminId, title: param.title, content: param.content, }); }); await this.em.persistAndFlush(notifications); return notifications; } async findOne(id: string): Promise { const notification = await this.em.findOne(Notification, { id }, { populate: ['restaurant', 'user'] }); if (!notification) { throw new NotFoundException('Notification not found'); } return notification; } async findByRestaurant(restaurantId: string, limit = 50): Promise { return this.em.find( Notification, { restaurant: { id: restaurantId } }, { orderBy: { createdAt: 'DESC' }, limit, populate: ['user'], }, ); } async findByUserAndRestaurant(userId: string, restaurantId: string, limit = 50): Promise { return this.em.find( Notification, { user: { id: userId }, restaurant: { id: restaurantId } }, { orderBy: { createdAt: 'DESC' }, limit, }, ); } async removeNotification(id: string, restaurantId: string): Promise { const notification = await this.em.findOne(Notification, { id, restaurant: { id: restaurantId } }); if (!notification) { throw new NotFoundException('Notification not found'); } await this.em.removeAndFlush(notification as any); } async findByRestaurantAndType(restaurantId: string, title: NotifTitleEnum, limit = 50): Promise { return this.em.find( Notification, { restaurant: { id: restaurantId }, title, }, { orderBy: { createdAt: 'DESC' }, limit, populate: ['user'], }, ); } async findByAdminAndRestaurant(adminId: string, restaurantId: string, limit = 50): Promise { return this.em.find( Notification, { admin: { id: adminId }, restaurant: { id: restaurantId } }, { orderBy: { createdAt: 'DESC' }, limit, }, ); } }