This commit is contained in:
2026-02-01 10:35:09 +03:30
parent c92d5a8068
commit 8e754bcfec
13 changed files with 330 additions and 297 deletions
+6 -1
View File
@@ -1,5 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsMobilePhone, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { IsArray, IsMobilePhone, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface';
export class CreateAdminDto {
@ApiProperty({ description: 'Mobile phone number' })
@@ -22,4 +23,8 @@ export class CreateAdminDto {
@IsNotEmpty()
@IsString()
roleId!: string;
@ApiProperty({})
@IsArray()
notificationPrefrences:NotifEvent[]
}
+7 -1
View File
@@ -1,8 +1,9 @@
import { Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core';
import { Cascade, Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity';
import { normalizePhone } from '../../util/phone.util';
import { Role } from 'src/modules/roles/entities/role.entity';
import { ulid } from 'ulid';
import { NotificationPreference } from 'src/modules/notification/entities/notification-preference.entity';
@Entity({ tableName: 'admins' })
export class Admin extends BaseEntity {
@@ -29,4 +30,9 @@ export class Admin extends BaseEntity {
@ManyToOne(() => Role)
role: Role
// @OneToOne(() => NotificationPreference, pref => pref.admin, {
// })
// notificationPreference!: NotificationPreference;
}
+31 -9
View File
@@ -1,12 +1,13 @@
import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import { Admin } from '../entities/admin.entity';
import { Role } from '../../roles/entities/role.entity';
import { EntityManager, RequiredEntityData } from '@mikro-orm/postgresql';
import { EntityManager } from '@mikro-orm/postgresql';
import { UpdateAdminDto } from '../dto/update-admin.dto';
import { normalizePhone } from '../../util/phone.util';
import { CreateAdminDto } from '../dto/create-admin.dto';
import { AdminRepository } from '../repositories/admin.repository';
import { RoleRepository } from 'src/modules/roles/respository/role.repository';
import { NotificationPreference } from 'src/modules/notification/entities/notification-preference.entity';
@Injectable()
export class AdminService {
@@ -17,7 +18,7 @@ export class AdminService {
) { }
async create(dto: CreateAdminDto) {
const { phone, firstName, lastName, roleId } = dto;
const { phone, firstName, lastName, roleId, notificationPrefrences } = dto;
const normalizedPhone = normalizePhone(phone);
@@ -25,25 +26,34 @@ export class AdminService {
phone: normalizedPhone,
});
if (exist) {
throw new NotFoundException('This phone number is already used');
throw new BadRequestException('This phone number is already used');
}
const role = await this.roleRepository.findOne({ id: roleId })
if (!role) {
throw new NotFoundException('Role not found');
throw new BadRequestException('Role not found');
}
const createData: RequiredEntityData<Admin> = {
return this.em.transactional(async em => {
const admin = em.create(Admin, {
phone: normalizedPhone,
firstName,
lastName,
role
}
const admin = this.adminRepository.create(createData)
})
await this.em.persistAndFlush(admin);
const notificationPreference = em.create(NotificationPreference, {
admin,
events: notificationPrefrences,
})
await this.em.persistAndFlush([admin, notificationPreference]);
return admin
})
return admin;
}
async findByPhone(phone: string): Promise<Admin | null> {
@@ -82,6 +92,18 @@ export class AdminService {
if (rest.lastName !== undefined) {
admin.lastName = rest.lastName;
}
if (rest.notificationPrefrences) {
//
const nf = await this.em.findOne(NotificationPreference, {
admin
})
if (!nf) {
throw new BadRequestException('Notification prefrences not found!')
}
nf.events = rest.notificationPrefrences
await this.em.flush()
}
if (rest.phone !== undefined && rest.phone !== admin.phone) {
const normalizedPhone = normalizePhone(rest.phone);
const exists = await this.adminRepository.findOne({ phone: normalizedPhone });
@@ -1,17 +1,17 @@
import { IsNotEmpty, IsEnum, IsArray } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { NotifTitleEnum } from '../interfaces/notification.interface';
import { NotifEvent } from '../interfaces/notification.interface';
import { NotifChannelEnum } from '../interfaces/notification.interface';
export class CreatePreferenceDto {
@ApiProperty({
description: 'Notification title/type (e.g., order.created, review.created)',
enum: NotifTitleEnum,
example: NotifTitleEnum.ORDER_CREATED,
enum: NotifEvent,
example: NotifEvent.NEW_ORDER,
})
@IsEnum(NotifTitleEnum)
@IsEnum(NotifEvent)
@IsNotEmpty()
title!: NotifTitleEnum;
title!: NotifEvent;
@ApiProperty({
description: 'Notification type (SMS, PUSH, BOTH, or NONE)',
@@ -1,16 +1,16 @@
import { Entity, Property, ManyToOne, Unique, PrimaryKey } from '@mikro-orm/core';
import { Entity, Property, ManyToOne, Unique, PrimaryKey, OneToOne, Cascade } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity';
import { NotifTitleEnum } from '../interfaces/notification.interface';
import { NotifChannelEnum } from '../interfaces/notification.interface';
import { NotifEvent } from '../interfaces/notification.interface';
import { Admin } from 'src/modules/admin/entities/admin.entity';
@Entity({ tableName: 'notification_preferences' })
export class NotificationPreference extends BaseEntity {
@PrimaryKey({ type: 'bigint', autoincrement: true })
id: bigint
@PrimaryKey({ type: 'int', autoincrement: true })
id: number
@Property()
title!: NotifTitleEnum;
@OneToOne(() => Admin, { owner: true, unique: true, cascade: [Cascade.PERSIST, Cascade.REMOVE] })
admin!: Admin;
@Property({ type: 'json' })
channels: NotifChannelEnum[] = [];
@Property({ type: 'json', nullable: true })
events: NotifEvent[] = [];
}
@@ -1,7 +1,7 @@
import { Entity, Property, ManyToOne, Enum, PrimaryKey } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity';
import { User } from '../../user/entities/user.entity';
import { NotifTitleEnum } from '../interfaces/notification.interface';
import { NotifEvent } from '../interfaces/notification.interface';
import { Admin } from 'src/modules/admin/entities/admin.entity';
@Entity({ tableName: 'notifications' })
@@ -15,8 +15,8 @@ export class Notification extends BaseEntity {
@ManyToOne(() => Admin, { nullable: true })
admin?: Admin;
@Enum(() => NotifTitleEnum)
title!: NotifTitleEnum;
@Enum(() => NotifEvent)
title!: NotifEvent;
@Property()
content!: string;
@@ -1,4 +1,4 @@
import type { NotifTitleEnum, recipientType } from './notification.interface';
import type { NotifEvent, recipientType } from './notification.interface';
export interface SmsNotificationQueueJob {
recipient: recipientType;
@@ -18,7 +18,7 @@ export interface PushNotificationQueueJob {
}
export interface InAppNotificationQueueJob {
recipient: { adminId: string; };
subject: NotifTitleEnum;
subject: NotifEvent;
body: string;
notificationId: string;
}
@@ -8,17 +8,17 @@ export enum NotifTypeEnum {
PROMOTIONAL = 'promotional',
SYSTEM = 'system',
}
export enum NotifTitleEnum {
PAGER_CREATED = 'pager.created',
ORDER_CREATED = 'order.created',
PAYMENT_SUCCESS = 'payment.success',
REVIEW_CREATED = 'review.created',
ORDER_STATUS_CHANGED = 'order.status.changed',
export enum NotifEvent {
NEW_REQUEST = 'newRequest',
INVOICED = 'invoiced',
NEW_ORDER = 'newOrder', // for chapkhane sms bere
DESIGNER_ASSIGN = 'designerAssign',
PAYMENT_SUCCESS = 'paymentSuccess',
}
export type recipientType = { userId: string } | { adminId: string };
export interface NotifRequestMessage {
title: NotifTitleEnum;
title: NotifEvent;
content: string;
sms: {
templateId: string;
@@ -53,11 +53,11 @@ interface INotifySmsPayload {
}
interface INotifySms {
phone: string;
subject: NotifTitleEnum;
subject: NotifEvent;
}
export interface IInAppNotificationPayload {
notificationId: string;
subject: NotifTitleEnum;
subject: NotifEvent;
body: string;
}
@@ -68,7 +68,7 @@ interface IPaymentSuccessSmsNotifyPayload extends INotifySmsPayload {
}
interface IPaymentSuccessSmsNotify extends INotifySms {
subject: NotifTitleEnum.PAYMENT_SUCCESS;
subject: NotifEvent.PAYMENT_SUCCESS;
payload: IPaymentSuccessSmsNotifyPayload;
}
@@ -3,7 +3,7 @@ import { EntityManager } from '@mikro-orm/postgresql';
import { NotificationPreference } from '../entities/notification-preference.entity';
import { CreatePreferenceDto } from '../dto/create-preference.dto';
import { UpdatePreferenceDto } from '../dto/update-preference.dto';
import { NotifTitleEnum } from '../interfaces/notification.interface';
import { NotifEvent } from '../interfaces/notification.interface';
@Injectable()
export class NotificationPreferenceService {
@@ -4,7 +4,7 @@ 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';
import { NotifChannelEnum, NotifRequest, NotifEvent } from '../interfaces/notification.interface';
import { SmsLog } from '../entities/smsLogs.entity';
import { SmsLogRepository } from '../repositories/sms-log.repository';
import { PaginatedResult } from '../../../common/interfaces/pagination.interface';
@@ -3,7 +3,7 @@ import { OnEvent } from '@nestjs/event-emitter';
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events';
import { Permission } from 'src/common/enums/permission.enum';
import { NotifTitleEnum } from 'src/modules/notification/interfaces/notification.interface';
import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface';
import { NotificationService } from 'src/modules/notification/services/notification.service';
import { ConfigService } from '@nestjs/config';
import { OrderRepository } from '../repositories/order.repository';
@@ -3,7 +3,7 @@ import { OnEvent } from '@nestjs/event-emitter';
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events';
import { Permission } from 'src/common/enums/permission.enum';
import { NotifTitleEnum } from 'src/modules/notification/interfaces/notification.interface';
import { NotifEvent } from 'src/modules/notification/interfaces/notification.interface';
import { NotificationService } from 'src/modules/notification/services/notification.service';
import { ConfigService } from '@nestjs/config';
import { OrderService } from 'src/modules/order/providers/order.service';
@@ -1,29 +1,29 @@
import { NotifChannelEnum, NotifTitleEnum } from '../../modules/notification/interfaces/notification.interface';
import { NotifChannelEnum, NotifEvent } from '../../modules/notification/interfaces/notification.interface';
export interface NotificationPreferenceData {
title: NotifTitleEnum;
title: NotifEvent;
channels: NotifChannelEnum[];
}
export const notificationPreferencesData: NotificationPreferenceData[] = [
{
title: NotifTitleEnum.PAGER_CREATED,
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
},
{
title: NotifTitleEnum.ORDER_CREATED,
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
},
{
title: NotifTitleEnum.PAYMENT_SUCCESS,
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
},
{
title: NotifTitleEnum.REVIEW_CREATED,
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
},
{
title: NotifTitleEnum.ORDER_STATUS_CHANGED,
channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
},
// {
// title: NotifEvent.PAGER_CREATED,
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
// },
// {
// title: NotifEvent.ORDER_CREATED,
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
// },
// {
// title: NotifEvent.PAYMENT_SUCCESS,
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
// },
// {
// title: NotifEvent.REVIEW_CREATED,
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
// },
// {
// title: NotifEvent.ORDER_STATUS_CHANGED,
// channels: [NotifChannelEnum.SMS, NotifChannelEnum.PUSH, NotifChannelEnum.IN_APP],
// },
];