diff --git a/src/common/enums/messages.enum.ts b/src/common/enums/messages.enum.ts index ff81172..7dc7a61 100644 --- a/src/common/enums/messages.enum.ts +++ b/src/common/enums/messages.enum.ts @@ -43,14 +43,3 @@ export enum TrackingMessages { PackageDelivered = 'بسته شما با موفقیت تحویل داده شد.', TripDelayed = 'سفر با تأخیر مواجه شده است. لطفاً برای اطلاعات بیشتر با سفیر تماس بگیرید.', } - -export enum NotificationMessages { - Welcome = 'به پلتفرم هم‌بار خوش آمدید.', - PackageCreated = 'بسته شما با موفقیت ایجاد شد.', - TripCreated = 'سفر شما با موفقیت ایجاد شد.', - TripRequestCreated = 'درخواست شما برای سفیر موردنظر با موفقیت ارسال شد.', - TripRequestCanceled = 'درخواست ارسالی شما برای سفیر موردنظر با موفقیت کنسل شد.', - TripRequestAccepted = 'درخواست ارسالی شما برای سفیر موردنظر قبول شد.', - TripRequestRejected = 'درخواست ارسالی شما برای سفیر موردنظر رد شد.', - NewTransporterNote = 'سفیر برای شما یک یادداشت ارسال کرد.' -} \ No newline at end of file diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 884b91e..ed358a5 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -5,7 +5,7 @@ import { Cache } from 'cache-manager'; import { Keyv } from '@keyv/redis'; import { CheckOtpDto } from './dto/check-otp.dto'; import { ConfigService } from '@nestjs/config'; -import { AuthMessages, NotFoundMessages, NotificationMessages } from '../../common/enums/messages.enum'; +import { AuthMessages, NotFoundMessages } from '../../common/enums/messages.enum'; import { TokenService } from '../token/token.service'; import { UserService } from '../user/user.service'; import { AuthTokens } from '../../common/enums/auth.enum'; @@ -23,6 +23,7 @@ import { UserStatesEnum } from './types/auth.enums'; import { TransporterResponseDto } from '../user/dto/transporter-response.dto'; import { SmsService } from '../sms/sms.service'; import { NotificationService } from '../notification/notification.service'; +import { NotificationMessages } from '../notification/notification-messages'; @Injectable() export class AuthService { diff --git a/src/modules/notification/notification-messages.ts b/src/modules/notification/notification-messages.ts new file mode 100644 index 0000000..17490ce --- /dev/null +++ b/src/modules/notification/notification-messages.ts @@ -0,0 +1,34 @@ +export enum NotificationMessages { + Welcome = '🎉 به پلتفرم هم‌بار خوش آمدید.', + PackageCreated = '📦 بسته شما با کد #{packageCode} با موفقیت ایجاد شد. حال می‌توانید برای سفرهای متناسب درخواست بفرستید.', + PackagePickedUp = '🚛 بسته #{packageCode} دریافت شد. در مسیر است!', + PackageDelivered = '🎊 بسته #{packageCode} با موفقیت تحویل داده شد. از اعتماد شما سپاسگزاریم!', + TripCreated = '🚚 سفر شما با کد #{tripCode} با موفقیت ایجاد شد.', + TripRequestCreated = '📬 درخواست سفر #{tripCode} برای بسته #{packageCode} ارسال شد. منتظر تأیید باشید!', + TripRequestCanceled = '❌ درخواست سفر #{tripCode} برای بسته #{packageCode} با موفقیت لغو شد.', + TripRequestAccepted = '✅ عالی! درخواست سفر #{tripCode} برای بسته #{packageCode} پذیرفته شد.', + TripRequestRejected = '⚠️ متأسفانه درخواست سفر #{tripCode} برای بسته #{packageCode} رد شد.', + NewTransporterNote = '📝 سفیر {transporterName} برای بسته #{packageCode} یک یادداشت جدید ارسال کرده: {noteContent}.', +} + +export interface NotificationContext { + packageCode?: number; + tripCode?: number; + transporterName?: string; + noteContent?: string; +} + +export function getNotificationMessage( + messageKey: NotificationMessages, + context: NotificationContext = {} +): string { + let baseMessage = messageKey as string; + + // Replace placeholders with a value + Object.keys(context).forEach(key => { + const placeholder = `{${key}}`; + baseMessage = baseMessage.replace(new RegExp(placeholder, 'g'), context[key]); + }); + + return baseMessage; +} diff --git a/src/modules/package/package.service.ts b/src/modules/package/package.service.ts index b952010..e89cb47 100644 --- a/src/modules/package/package.service.ts +++ b/src/modules/package/package.service.ts @@ -2,7 +2,7 @@ import { BadRequestException, ForbiddenException, Injectable, NotFoundException import { PrismaService } from '../prisma/prisma.service'; import { CreateRecipientDto } from './dto/create-recipient.dto'; import { CreatePackageDto } from './dto/create-package.dto'; -import { AuthMessages, BadRequestMessages, NotFoundMessages, NotificationMessages } from '../../common/enums/messages.enum'; +import { AuthMessages, BadRequestMessages, NotFoundMessages } from '../../common/enums/messages.enum'; import { formatPrismaError } from '../../common/utilities'; import { UpdatePackageDto } from './dto/update.package.dto'; import { PackageStatusEnum, RequestStatusEnum, TripStatusEnum } from '../../../generated/prisma'; @@ -18,6 +18,7 @@ import { CreateRequestDto } from '../trip/dto/create-request.dto'; import { instanceToPlain } from 'class-transformer'; import { TurfService } from '../turf/turf.service'; import { NotificationService } from '../notification/notification.service'; +import { getNotificationMessage, NotificationMessages } from '../notification/notification-messages'; @Injectable() export class PackageService { @@ -192,7 +193,7 @@ export class PackageService { userId, { packageId: packageData.id, - content: NotificationMessages.PackageCreated + content: getNotificationMessage(NotificationMessages.PackageCreated, { packageCode: packageData.code }) }, tx ); @@ -518,9 +519,9 @@ export class PackageService { ...trip, additionalPrice }; - }).filter(Boolean)); + })); - return matchingResult; + return matchingResult.filter(Boolean); }); } @@ -596,7 +597,10 @@ export class PackageService { userId, { packageId, - content: NotificationMessages.TripRequestCreated + content: getNotificationMessage(NotificationMessages.TripRequestCreated, { + packageCode: packageData.code, + tripCode: tripData.code, + }) }, tx ); diff --git a/src/modules/trip/trip.service.spec.ts b/src/modules/trip/trip.service.spec.ts index ba60a83..07fa0c7 100644 --- a/src/modules/trip/trip.service.spec.ts +++ b/src/modules/trip/trip.service.spec.ts @@ -407,7 +407,8 @@ describe('TripService', () => { prisma.$transaction.mockImplementation(async (callback) => callback(prisma)); prisma.tripRequest.update.mockResolvedValue({ ...updatedRequest, - package: { senderId: 'user-123' } + package: { code: 'PKG-123', senderId: 'user-123' }, + trip: { code: 'TRIP-123' } } as any); const result = await service.updateRequest('request-123', { @@ -421,8 +422,14 @@ describe('TripService', () => { include: { package: { select: { + code: true, senderId: true } + }, + trip: { + select: { + code: true + } } } }); diff --git a/src/modules/trip/trip.service.ts b/src/modules/trip/trip.service.ts index 0db1062..3c4b705 100644 --- a/src/modules/trip/trip.service.ts +++ b/src/modules/trip/trip.service.ts @@ -3,7 +3,7 @@ import { MapService } from '../map/map.service'; import { PrismaService } from '../prisma/prisma.service'; import { formatPrismaError, generateCode, generateUniqueCode } from '../../common/utilities'; import { CreateTripDto } from './dto/create-trip.dto'; -import { AuthMessages, BadRequestMessages, NotificationMessages, TrackingMessages } from '../../common/enums/messages.enum'; +import { AuthMessages, BadRequestMessages, TrackingMessages } from '../../common/enums/messages.enum'; import { MatchedRequest, Package, PackageStatusEnum, Prisma, RequestStatusEnum, TripStatusEnum, TripTypeEnum } from '../../../generated/prisma'; import { UpdateTripDto } from './dto/update-trip.dto'; import { UpdateRequestDto } from './dto/update-request.dto'; @@ -18,6 +18,7 @@ import { S3Service } from '../s3/s3.service'; import { TurfService } from '../turf/turf.service'; import { Location } from '../map/map.types'; import { NotificationService } from '../notification/notification.service'; +import { getNotificationMessage, NotificationMessages } from '../notification/notification-messages'; @Injectable() export class TripService { @@ -104,7 +105,7 @@ export class TripService { userId, { tripId: trip.id, - content: NotificationMessages.TripCreated + content: getNotificationMessage(NotificationMessages.TripCreated, { tripCode: trip.code }) }, tx ); @@ -327,7 +328,8 @@ export class TripService { if (status === RequestStatusEnum.rejected) { return this.prisma.$transaction(async tx => { const { - package: { senderId }, + package: packageData, + trip, ...request } = await this.prisma.tripRequest.update({ where: { id: requestId }, @@ -335,19 +337,28 @@ export class TripService { include: { package: { select: { + code: true, senderId: true } + }, + trip: { + select: { + code: true + } } } }); // Add reject request notification await this.notificationService.create( - senderId, + packageData.senderId, { packageId: request.packageId, tripId: request.tripId, - content: NotificationMessages.TripRequestRejected + content: getNotificationMessage(NotificationMessages.TripRequestRejected, { + packageCode: packageData.code, + tripCode: trip.code + }) }, tx ); @@ -403,7 +414,7 @@ export class TripService { const newTotalDeviationDistance = (totalDeviationDistanceKm ?? 0) + request.deviationDistanceKm; const newTotalDeviationDuration = (totalDeviationDurationMin ?? 0) + request.deviationDurationMin; - await tx.trip.update({ + const { code: tripCode } = await tx.trip.update({ where: { id: request.tripId }, data: { totalDeviationDistanceKm: newTotalDeviationDistance, @@ -415,6 +426,7 @@ export class TripService { const packageData = await tx.package.findFirstOrThrow({ where: { id: request.packageId }, select: { + code: true, breakdown: true, senderId: true } @@ -452,7 +464,10 @@ export class TripService { { packageId: request.packageId, tripId: request.tripId, - content: NotificationMessages.TripRequestAccepted + content: getNotificationMessage(NotificationMessages.TripRequestAccepted, { + packageCode: packageData.code, + tripCode + }) }, tx ); @@ -676,6 +691,7 @@ export class TripService { id: true, package: { select: { + code: true, senderId: true, status: true, originAddress: true @@ -734,7 +750,7 @@ export class TripService { { packageId, tripId, - content: TrackingMessages.PackagePickedUp + content: getNotificationMessage(NotificationMessages.PackagePickedUp, { packageCode: packageData.code }) }, tx ); @@ -765,6 +781,7 @@ export class TripService { id: true, package: { select: { + code: true, senderId: true, status: true, recipient: { @@ -837,7 +854,7 @@ export class TripService { { packageId, tripId, - content: TrackingMessages.PackageDelivered + content: getNotificationMessage(NotificationMessages.PackageDelivered, { packageCode: packageData.code }) }, tx ); @@ -921,10 +938,21 @@ export class TripService { include: { package: { select: { + code: true, senderId: true } } } + }, + transporter: { + select: { + user: { + select: { + firstName: true, + lastName: true, + } + } + } } } }).catch((error: Error) => { @@ -949,7 +977,11 @@ export class TripService { { packageId: m.packageId, tripId: m.tripId, - content: NotificationMessages.NewTransporterNote + content: getNotificationMessage(NotificationMessages.NewTransporterNote, { + packageCode: m.package.code, + tripCode: trip.code, + noteContent: note + }) }, tx );