This commit is contained in:
2025-12-22 23:27:53 +03:30
parent ce2987d67b
commit 4dafba955e
14 changed files with 126 additions and 25 deletions
+3
View File
@@ -12,6 +12,7 @@ import { MikroOrmModule } from '@mikro-orm/nestjs';
import { User } from '../users/entities/user.entity';
import { AdminAuthGuard } from './guards/adminAuth.guard';
import { RefreshToken } from './entities/refresh-token.entity';
import { NotificationsModule } from '../notifications/notifications.module';
@Module({
imports: [
@@ -34,6 +35,8 @@ import { RefreshToken } from './entities/refresh-token.entity';
forwardRef(() => AdminModule),
forwardRef(() => RestaurantsModule),
MikroOrmModule.forFeature([User, RefreshToken]),
forwardRef(() => NotificationsModule),
],
controllers: [AuthController],
providers: [AuthService, TokensService, AdminAuthGuard],
+1 -1
View File
@@ -1,7 +1,7 @@
import { Injectable, BadRequestException } from '@nestjs/common';
import { RequestOtpDto } from '../dto/request-otp.dto';
import { CacheService } from '../../utils/cache.service';
import { SmsService } from '../../utils/sms.service';
import { SmsService } from '../../notifications/services/sms.service';
import { UserService } from '../../users/providers/user.service';
import { randomInt } from 'crypto';
import { TokensService } from './tokens.service';
@@ -0,0 +1,17 @@
import { Entity, Property, ManyToOne, PrimaryKey } from '@mikro-orm/core';
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
@Entity({ tableName: 'sms_logs' })
export class SmsLog {
@PrimaryKey()
id!: number;
@ManyToOne(() => Restaurant)
restaurant!: Restaurant;
@Property()
phone!: string;
@Property()
createdAt: Date = new Date();
}
@@ -0,0 +1,7 @@
export class SmsSentEvent {
constructor(
public readonly phoneNumber: string,
public readonly restaurantId: string,
) {}
}
@@ -3,6 +3,7 @@ import type { NotifTitleEnum, recipientType } from './notification.interface';
export interface SmsNotificationQueueJob {
recipient: recipientType;
templateId: string;
restaurantId: string;
parameters?: Record<string, string>;
}
export interface PushNotificationQueueJob {
@@ -0,0 +1,55 @@
import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { SmsSentEvent } from '../events/sms.events';
import { RestRepository } from '../../restaurants/repositories/rest.repository';
import { EntityManager } from '@mikro-orm/postgresql';
import { SmsLog } from '../entities/smsLogs.entity';
@Injectable()
export class SmsListeners {
private readonly logger = new Logger(SmsListeners.name);
constructor(
private readonly restRepository: RestRepository,
private readonly em: EntityManager,
) {
}
@OnEvent(SmsSentEvent.name)
async handleSmsSent(event: SmsSentEvent) {
try {
this.logger.log(
`SMS sent event received: phone ${event.phoneNumber} for restaurant: ${event.restaurantId}`,
);
// Get the restaurant entity
const restaurant = await this.restRepository.findOne({ id: event.restaurantId });
if (!restaurant) {
this.logger.warn(
`Restaurant not found for SMS log: ${event.restaurantId}`,
);
return;
}
// Create SMS log record
const smsLog = this.em.create(SmsLog, {
restaurant: restaurant,
phone: event.phoneNumber,
createdAt: new Date(),
});
await this.em.flush();
this.logger.log(
`SMS log created successfully: ${smsLog.id} for phone ${event.phoneNumber}`,
);
} catch (error) {
this.logger.error(
`Failed to create SMS log for event: ${event.restaurantId}`,
error instanceof Error ? error.stack : String(error),
);
}
}
}
@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common';
import { Module, forwardRef } from '@nestjs/common';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { BullModule } from '@nestjs/bullmq';
import { JwtModule } from '@nestjs/jwt';
@@ -23,13 +23,17 @@ import { InAppProcessor } from './processors/in-app.processor';
import { AdminModule } from '../admin/admin.module';
import { UserModule } from '../users/user.module';
import { NotificationCrone } from './crone/notification.crone';
import { SmsService } from './services/sms.service';
import { SmsLog } from './entities/smsLogs.entity';
import { SmsLogRepository } from './repositories/sms-log.repository';
import { RestaurantsModule } from '../restaurants/restaurants.module';
import { SmsListeners } from './listeners/sms.listeners';
@Module({
imports: [
AuthModule,
UtilsModule,
forwardRef(() => AuthModule),
JwtModule,
MikroOrmModule.forFeature([Notification, NotificationPreference, User, Restaurant]),
MikroOrmModule.forFeature([Notification, NotificationPreference, User, Restaurant, SmsLog]),
BullModule.registerQueue(
{ name: NotificationQueueNameEnum.SMS },
{ name: NotificationQueueNameEnum.PUSH },
@@ -46,7 +50,9 @@ import { NotificationCrone } from './crone/notification.crone';
}),
}),
AdminModule,
UserModule,
forwardRef(() => UserModule),
UtilsModule,
forwardRef(() => RestaurantsModule),
],
controllers: [NotificationsController],
providers: [
@@ -60,7 +66,11 @@ import { NotificationCrone } from './crone/notification.crone';
NotificationsGateway,
WsAdminAuthGuard,
NotificationCrone,
SmsService,
SmsLogRepository,
SmsListeners,
],
exports: [NotificationService, NotificationPreferenceService, NotificationQueueService, PushNotificationService],
exports: [NotificationService, NotificationPreferenceService,
NotificationQueueService, PushNotificationService, SmsService],
})
export class NotificationsModule {}
export class NotificationsModule { }
@@ -5,7 +5,9 @@ import { UserRepository } from 'src/modules/users/repositories/user.repository';
import { SmsNotificationQueueJob } from '../interfaces/jobs-queue.interface';
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
import { NotificationQueueNameEnum } from '../constants/queue';
import { SmsService } from 'src/modules/utils/sms.service';
import { SmsService } from '../services/sms.service';
import { SmsSentEvent } from '../events/sms.events';
import { EventEmitter2 } from '@nestjs/event-emitter';
@Processor(NotificationQueueNameEnum.SMS)
export class SmsProcessor extends WorkerHost {
@@ -15,12 +17,13 @@ export class SmsProcessor extends WorkerHost {
private readonly adminRepository: AdminRepository,
private readonly userRepository: UserRepository,
private readonly smsService: SmsService,
private readonly eventEmitter: EventEmitter2,
) {
super();
}
async process(job: Job<SmsNotificationQueueJob>) {
const { recipient, templateId, parameters } = job.data;
const { recipient, templateId, parameters, restaurantId } = job.data;
this.logger.log(`Processing SMS notification - Recipient: ${JSON.stringify(recipient)}, templateID: ${templateId}`);
@@ -55,6 +58,8 @@ export class SmsProcessor extends WorkerHost {
this.logger.log(`SMS notification sent successfully to ${phone}`);
this.eventEmitter.emit(SmsSentEvent.name, new SmsSentEvent(phone, restaurantId));
return {
success: true,
};
@@ -0,0 +1,11 @@
import { Injectable } from '@nestjs/common';
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
import { SmsLog } from '../entities/smsLogs.entity';
@Injectable()
export class SmsLogRepository extends EntityRepository<SmsLog> {
constructor(readonly em: EntityManager) {
super(em, SmsLog);
}
}
@@ -58,6 +58,7 @@ export class NotificationService {
recipient,
templateId: message.sms.templateId,
parameters: message.sms.parameters,
restaurantId,
})),
);
}
@@ -1,7 +1,7 @@
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';
import { ISmsParams, ISmsResponse, ISmsBodyParameters } from './interface/sms';
import { ISmsParams, ISmsResponse, ISmsBodyParameters } from '../interfaces/sms';
@Injectable()
export class SmsService {
@@ -58,6 +58,7 @@ export class SmsService {
throw new HttpException('The SMS was not sent. Please try again later.', HttpStatus.BAD_GATEWAY);
}
}
formatPhone(phone: string): string {
// remove spaces, dashes, parentheses
phone = phone.replace(/[^\d+]/g, '');
@@ -28,7 +28,7 @@ export class OrderListeners {
const statusMap: Record<OrderStatus, string> = {
[OrderStatus.PENDING_PAYMENT]: 'در انتظار پرداخت',
[OrderStatus.PAID]: 'پرداخت شده',
[OrderStatus.PREPARING]: 'در حال آماده‌سازی',
[OrderStatus.PREPARING]: 'در حال آماده‌سازی',
[OrderStatus.DELIVERED_TO_RECEPTIONIST]: 'تحویل به پذیرش',
[OrderStatus.DELIVERED_TO_WAITER]: 'تحویل به گارسون',
[OrderStatus.SHIPPED]: 'ارسال شده',
@@ -91,15 +91,6 @@ export class OrderListeners {
`Order status changed event received: ${event.orderId} for restaurant: ${event.restaurantId} and order number: ${event.orderNumber}`,
);
// const order = await this.OrderRepository.findOneById(event.orderId, { populate: ['user'] });
// if (!order) {
// this.logger.warn(`Order not found: ${event.orderId}`);
// return;
// }
// if (order.user && order.user.phoneNumber) {
// this.logger.warn(`Order not found: ${event.orderId}`);
// }
const recipients = [
{
userId: event.userId,
+3 -4
View File
@@ -1,12 +1,11 @@
import { Module } from '@nestjs/common';
import { CacheService } from './cache.service';
import { SmsService } from './sms.service';
@Module({
imports: [],
controllers: [],
providers: [CacheService, SmsService],
exports: [CacheService, SmsService],
providers: [CacheService, ],
exports: [CacheService],
})
export class UtilsModule {}