Files
dmenu-api/src/modules/notifications/services/notification.service.ts
T
2025-12-14 11:57:35 +03:30

156 lines
4.9 KiB
TypeScript

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<Notification[]> {
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<Notification[]> {
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<Notification> {
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<Notification[]> {
return this.em.find(
Notification,
{ restaurant: { id: restaurantId } },
{
orderBy: { createdAt: 'DESC' },
limit,
populate: ['user'],
},
);
}
async findByUserAndRestaurant(userId: string, restaurantId: string, limit = 50): Promise<Notification[]> {
return this.em.find(
Notification,
{ user: { id: userId }, restaurant: { id: restaurantId } },
{
orderBy: { createdAt: 'DESC' },
limit,
},
);
}
async removeNotification(id: string, restaurantId: string): Promise<void> {
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<Notification[]> {
return this.em.find(
Notification,
{
restaurant: { id: restaurantId },
title,
},
{
orderBy: { createdAt: 'DESC' },
limit,
populate: ['user'],
},
);
}
async findByAdminAndRestaurant(adminId: string, restaurantId: string, limit = 50): Promise<Notification[]> {
return this.em.find(
Notification,
{ admin: { id: adminId }, restaurant: { id: restaurantId } },
{
orderBy: { createdAt: 'DESC' },
limit,
},
);
}
}