389 lines
13 KiB
JavaScript
389 lines
13 KiB
JavaScript
// /utils/senders/smsMessages.js
|
|
'use strict';
|
|
|
|
const { sendSingleSms } = require('./sms.base');
|
|
const { recordAndSend } = require('./notificationRecorder');
|
|
const { getSmsTemplate } = require('../../components/settings/settingService');
|
|
const { buildSmsParameters } = require('../../components/settings/smsTemplates');
|
|
const User = require('../../components/users/userModel');
|
|
const logger = require('../logger');
|
|
|
|
const resolveUserIdByPhone = async (phoneNumber) => {
|
|
if (!phoneNumber) return null;
|
|
const user = await User.findOne({ phoneNumber: String(phoneNumber) }).select('_id').lean();
|
|
return user?._id || null;
|
|
};
|
|
|
|
const resolveUserName = async (userId, fallback = '') => {
|
|
if (!userId) return fallback;
|
|
const user = await User.findById(userId).select('name uniqueCode').lean();
|
|
return {
|
|
fullName: user?.name || fallback,
|
|
userCode: user?.uniqueCode || ''
|
|
};
|
|
};
|
|
|
|
const sendTemplateSms = async ({
|
|
templateKey,
|
|
receiver,
|
|
userId = null,
|
|
subject,
|
|
body,
|
|
relatedEvent,
|
|
slotValues = {}
|
|
}) => {
|
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
|
const { templateId, variables, enabled } = await getSmsTemplate(templateKey);
|
|
if (enabled === false) {
|
|
logger.info(`[SMS] Template ${templateKey} is disabled. Skipping SMS send to ${receiver}.`);
|
|
return { success: true, skipped: true, reason: 'template_disabled' };
|
|
}
|
|
return recordAndSend({
|
|
userId: resolvedUserId,
|
|
channel: 'sms',
|
|
subject,
|
|
body,
|
|
relatedEvent,
|
|
sendFn: () => sendSingleSms(receiver, templateId, buildSmsParameters(variables, {
|
|
phoneNumber: receiver,
|
|
mobile: receiver,
|
|
...slotValues
|
|
}))
|
|
});
|
|
};
|
|
|
|
const sendAccountCredentialsSms = async ({
|
|
receiver,
|
|
username,
|
|
password,
|
|
userId = null,
|
|
subject,
|
|
body,
|
|
relatedEvent,
|
|
templateKey = 'accountCreated',
|
|
userCode = ''
|
|
}) => {
|
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
|
let fullName = '';
|
|
let code = userCode;
|
|
if (resolvedUserId) {
|
|
const profile = await resolveUserName(resolvedUserId, username);
|
|
fullName = profile.fullName;
|
|
code = code || profile.userCode;
|
|
}
|
|
return sendTemplateSms({
|
|
templateKey,
|
|
receiver,
|
|
userId: resolvedUserId,
|
|
subject,
|
|
body,
|
|
relatedEvent,
|
|
slotValues: {
|
|
username,
|
|
password,
|
|
fullName: fullName || username,
|
|
name: fullName || username,
|
|
userCode: code
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendAccountCreatedSms = async (receiver, username, password, userId = null, userCode = '') => {
|
|
return sendAccountCredentialsSms({
|
|
receiver,
|
|
username,
|
|
password,
|
|
userId,
|
|
userCode,
|
|
subject: 'ایجاد حساب کاربری',
|
|
body: `حساب کاربری شما ایجاد شد. نام کاربری: ${username}`,
|
|
relatedEvent: 'user.created',
|
|
templateKey: 'accountCreated'
|
|
});
|
|
};
|
|
|
|
const sendPasswordResetSms = async (receiver, username, password, userId = null, userCode = '') => {
|
|
return sendAccountCredentialsSms({
|
|
receiver,
|
|
username,
|
|
password,
|
|
userId,
|
|
userCode,
|
|
subject: 'بازنشانی رمز عبور',
|
|
body: `رمز عبور شما بازنشانی شد. نام کاربری: ${username}`,
|
|
relatedEvent: 'user.password_reset',
|
|
templateKey: 'passwordReset'
|
|
});
|
|
};
|
|
|
|
const sendClassRegisteredSms = async (receiver, className, userId = null, extraContext = {}) => {
|
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
|
let fullName = extraContext.fullName || '';
|
|
if (!fullName && resolvedUserId) {
|
|
const profile = await resolveUserName(resolvedUserId);
|
|
fullName = profile.fullName;
|
|
}
|
|
return sendTemplateSms({
|
|
templateKey: 'classRegistered',
|
|
receiver,
|
|
userId: resolvedUserId,
|
|
subject: 'ثبتنام در کلاس',
|
|
body: `ثبتنام شما در کلاس «${className}» انجام شد.`,
|
|
relatedEvent: 'user.enrolled',
|
|
slotValues: {
|
|
className,
|
|
courseName: extraContext.courseName || className,
|
|
fullName: fullName || 'کارآموز',
|
|
classStartDate: extraContext.classStartDate || '',
|
|
classDays: extraContext.classDays || '',
|
|
courseTime: extraContext.courseTime || '',
|
|
classCode: extraContext.classCode || '',
|
|
...extraContext
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendClassReminderSms = async (receiver, className, time, place = '', userId = null, extraContext = {}) => {
|
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
|
let fullName = extraContext.fullName || '';
|
|
if (!fullName && resolvedUserId) {
|
|
const profile = await resolveUserName(resolvedUserId);
|
|
fullName = profile.fullName;
|
|
}
|
|
return sendTemplateSms({
|
|
templateKey: 'classReminder',
|
|
receiver,
|
|
userId: resolvedUserId,
|
|
subject: 'یادآوری کلاس',
|
|
body: `یادآوری کلاس «${className}» ساعت ${time} — مکان: ${place || '-'}`,
|
|
relatedEvent: 'session.reminder',
|
|
slotValues: {
|
|
className,
|
|
topic: extraContext.topic || className,
|
|
courseName: extraContext.courseName || className,
|
|
time,
|
|
classTime: extraContext.classTime || extraContext.courseTime || extraContext.time || time || '',
|
|
sessionDate: extraContext.sessionDate || '',
|
|
place: place || '-',
|
|
fullName: fullName || 'کارآموز',
|
|
classStartDate: extraContext.classStartDate || '',
|
|
classDays: extraContext.classDays || '',
|
|
courseTime: extraContext.courseTime || extraContext.classTime || extraContext.time || time || '',
|
|
classCode: extraContext.classCode || '',
|
|
...extraContext
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendInvoiceCreatedSms = async (receiver, payload, userId = null) => {
|
|
const resolvedUserId = userId || await resolveUserIdByPhone(receiver);
|
|
const data = typeof payload === 'object' && payload !== null ? payload : { amount: payload };
|
|
const fullNameLabel = data.fullName || 'کارآموز';
|
|
const amountLabel = data.amount != null ? String(data.amount) : '';
|
|
const courseLabel = data.course || '-';
|
|
|
|
return sendTemplateSms({
|
|
templateKey: 'invoiceCreated',
|
|
receiver,
|
|
userId: resolvedUserId,
|
|
subject: 'ایجاد صورتحساب',
|
|
body: `کارآموز عزیز، ${fullNameLabel}، یک صورتحساب به مبلغ ${amountLabel} تومان بابت دوره «${courseLabel}» برای شما ایجاد شد.`,
|
|
relatedEvent: 'payment.created',
|
|
slotValues: {
|
|
fullName: fullNameLabel,
|
|
amount: amountLabel,
|
|
course: courseLabel,
|
|
classStartDate: data.classStartDate || '',
|
|
classDays: data.classDays || '',
|
|
courseTime: data.courseTime || '',
|
|
invoiceCode: data.invoiceCode || ''
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendPaymentStatusChangedSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'paymentStatusChanged',
|
|
receiver,
|
|
userId,
|
|
subject: 'تغییر وضعیت پرداخت',
|
|
body: `وضعیت صورتحساب شما به «${data.statusLabel || data.status || '-'}» تغییر کرد.`,
|
|
relatedEvent: 'payment.status_changed',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
status: data.statusLabel || data.status || '-',
|
|
amount: data.amount != null ? String(data.amount) : '',
|
|
invoiceCode: data.invoiceCode || '',
|
|
course: data.course || '-'
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendPaymentReminderSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'paymentReminder',
|
|
receiver,
|
|
userId,
|
|
subject: 'یادآوری سررسید پرداخت',
|
|
body: `یادآوری: مبلغ ${data.amount || '-'} تومان تا ${data.dueDate || '-'} سررسید دارد.`,
|
|
relatedEvent: 'payment.reminder_due',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
amount: data.amount != null ? String(data.amount) : '',
|
|
dueDate: data.dueDate || '',
|
|
invoiceCode: data.invoiceCode || '',
|
|
course: data.course || '-'
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendTransactionRecordedSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'transactionRecorded',
|
|
receiver,
|
|
userId,
|
|
subject: 'ثبت تراکنش',
|
|
body: `تراکنش به مبلغ ${data.amount || '-'} تومان ثبت شد.`,
|
|
relatedEvent: 'payment.transaction_added',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
amount: data.amount != null ? String(data.amount) : '',
|
|
invoiceCode: data.invoiceCode || '',
|
|
transactionCode: data.transactionCode || '',
|
|
receiptNumber: data.receiptNumber || ''
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendSessionCancelledSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'sessionCancelled',
|
|
receiver,
|
|
userId,
|
|
subject: 'لغو جلسه',
|
|
body: `جلسه «${data.topic || data.className || 'کلاس'}» لغو شد.`,
|
|
relatedEvent: 'session.cancelled',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
className: data.className || '-',
|
|
topic: data.topic || '-',
|
|
sessionDate: data.sessionDate || '',
|
|
reason: data.reason || '-',
|
|
classCode: data.classCode || ''
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendSessionHoldingSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'sessionHolding',
|
|
receiver,
|
|
userId,
|
|
subject: 'برگزاری جلسه طبق برنامه',
|
|
body: `جلسه «${data.topic || data.className || 'کلاس'}» کلاس ${data.className || ''} در تاریخ ${data.sessionDate || '-'} و ساعت ${data.classTime || data.time || '-'} طبق برنامه برگزار خواهد شد.`,
|
|
relatedEvent: 'session.holding',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
topic: data.topic || '-',
|
|
className: data.className || '-',
|
|
sessionDate: data.sessionDate || '',
|
|
classTime: data.classTime || data.time || '',
|
|
courseName: data.courseName || data.className || '-',
|
|
classCode: data.classCode || '',
|
|
place: data.place || '-'
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendClassRequestApprovedSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'classRequestApproved',
|
|
receiver,
|
|
userId,
|
|
subject: 'تأیید درخواست تشکیل کلاس',
|
|
body: `درخواست تشکیل کلاس «${data.courseName || '-'}» تأیید شد.`,
|
|
relatedEvent: 'class_request.approved',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
courseName: data.courseName || '-',
|
|
classCode: data.classCode || ''
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendClassRequestRejectedSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'classRequestRejected',
|
|
receiver,
|
|
userId,
|
|
subject: 'رد درخواست ثبتنام',
|
|
body: `درخواست شما برای «${data.courseName || '-'}» رد شد.`,
|
|
relatedEvent: 'class_request.rejected',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
courseName: data.courseName || '-',
|
|
reason: data.reason || '-',
|
|
registrationCode: data.registrationCode || ''
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendPendingRegistrationSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'pendingRegistration',
|
|
receiver,
|
|
userId,
|
|
subject: 'دریافت درخواست ثبتنام',
|
|
body: `درخواست ثبتنام شما در «${data.className || '-'}» دریافت شد.`,
|
|
relatedEvent: 'pending.registration_received',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
className: data.className || '-',
|
|
registrationCode: data.registrationCode || ''
|
|
}
|
|
});
|
|
};
|
|
|
|
const sendCertificateIssuedSms = async (receiver, payload, userId = null) => {
|
|
const data = payload || {};
|
|
return sendTemplateSms({
|
|
templateKey: 'certificateIssued',
|
|
receiver,
|
|
userId,
|
|
subject: 'صدور گواهینامه',
|
|
body: `گواهینامه «${data.certificateTitle || '-'}» برای شما صادر شد.`,
|
|
relatedEvent: 'certificate.issued',
|
|
slotValues: {
|
|
fullName: data.fullName || 'کارآموز',
|
|
certificateTitle: data.certificateTitle || '-',
|
|
certificateCode: data.certificateCode || '',
|
|
courseName: data.courseName || '-'
|
|
}
|
|
});
|
|
};
|
|
|
|
module.exports = {
|
|
sendAccountCreatedSms,
|
|
sendPasswordResetSms,
|
|
sendClassRegisteredSms,
|
|
sendClassReminderSms,
|
|
sendInvoiceCreatedSms,
|
|
sendPaymentStatusChangedSms,
|
|
sendPaymentReminderSms,
|
|
sendTransactionRecordedSms,
|
|
sendSessionCancelledSms,
|
|
sendSessionHoldingSms,
|
|
sendClassRequestApprovedSms,
|
|
sendClassRequestRejectedSms,
|
|
sendPendingRegistrationSms,
|
|
sendCertificateIssuedSms
|
|
};
|