Express/MongoDB backend with JWT auth, RBAC, S3 uploads, notifications, and scheduled jobs.
123 lines
4.0 KiB
JavaScript
123 lines
4.0 KiB
JavaScript
// /components/contactInquiries/contactInquiryService.js
|
|
|
|
const ContactInquiry = require('./contactInquiryModel');
|
|
const Course = require('../courses/courseModel');
|
|
const AppError = require('../../utils/AppError');
|
|
const { parsePaginationAndSort, buildFilterQuery, calculateMeta } = require('../../utils/pagination');
|
|
|
|
const normalizePhone = (value) => {
|
|
if (!value) return undefined;
|
|
return String(value).replace(/[\s\-()]/g, '').trim();
|
|
};
|
|
|
|
const createInquiry = async (data) => {
|
|
const preferredContactMethods = Array.isArray(data.preferredContactMethods)
|
|
? [...new Set(data.preferredContactMethods)]
|
|
: [];
|
|
|
|
if (!preferredContactMethods.length) {
|
|
throw new AppError('VALIDATION_FAILED', {
|
|
preferredContactMethods: 'At least one preferred contact method is required'
|
|
}, 'حداقل یک روش تماس را انتخاب کنید.');
|
|
}
|
|
|
|
const phoneNumber = normalizePhone(data.phoneNumber || data.phone);
|
|
const email = data.email ? String(data.email).trim().toLowerCase() : undefined;
|
|
|
|
if (!phoneNumber && !email) {
|
|
throw new AppError('VALIDATION_FAILED', {
|
|
contact: 'Phone number or email is required'
|
|
}, 'شماره تلفن یا ایمیل الزامی است.');
|
|
}
|
|
|
|
const courseIds = Array.isArray(data.courses)
|
|
? [...new Set(data.courses.filter(Boolean).map(String))]
|
|
: [];
|
|
|
|
if (courseIds.length) {
|
|
const foundCount = await Course.countDocuments({
|
|
_id: { $in: courseIds },
|
|
showOnFrontend: { $ne: false }
|
|
});
|
|
if (foundCount !== courseIds.length) {
|
|
throw new AppError('COURSE_NOT_FOUND', null, 'یکی از دورههای انتخابشده یافت نشد.');
|
|
}
|
|
}
|
|
|
|
const inquiry = await ContactInquiry.create({
|
|
name: data.name || data.firstName,
|
|
surname: data.surname || data.lastName,
|
|
nationalIdCode: data.nationalIdCode || data.nationalId || undefined,
|
|
phoneNumber,
|
|
email,
|
|
message: data.message || undefined,
|
|
preferredContactMethods,
|
|
courses: courseIds
|
|
});
|
|
|
|
return inquiry.populate('courses', 'title type price');
|
|
};
|
|
|
|
const getAllInquiries = async (queryParams) => {
|
|
const { page, limit, skip, sort } = parsePaginationAndSort(queryParams, 'createdAt', 'desc');
|
|
const filter = buildFilterQuery(
|
|
queryParams,
|
|
['name', 'surname', 'nationalIdCode', 'phoneNumber', 'email', 'notes', 'message'],
|
|
['page', 'limit', 'sortBy', 'sortOrder', 'q', 'lang', 'course', 'courseId', 'courses']
|
|
);
|
|
|
|
const courseFilter = queryParams.course || queryParams.courseId || queryParams.courses;
|
|
if (courseFilter) {
|
|
const ids = String(courseFilter).split(',').map((id) => id.trim()).filter(Boolean);
|
|
if (ids.length === 1) filter.courses = ids[0];
|
|
else if (ids.length > 1) filter.courses = { $in: ids };
|
|
}
|
|
|
|
const [data, totalCount] = await Promise.all([
|
|
ContactInquiry.find(filter)
|
|
.populate('courses', 'title type price')
|
|
.sort(sort)
|
|
.skip(skip)
|
|
.limit(limit),
|
|
ContactInquiry.countDocuments(filter)
|
|
]);
|
|
|
|
return { data, meta: calculateMeta(totalCount, page, limit) };
|
|
};
|
|
|
|
const getInquiryById = async (id) => {
|
|
const inquiry = await ContactInquiry.findById(id).populate('courses', 'title type price');
|
|
if (!inquiry) {
|
|
throw new AppError('NOT_FOUND', null, 'درخواست تماس یافت نشد.');
|
|
}
|
|
return inquiry;
|
|
};
|
|
|
|
const updateInquiry = async (id, updateData) => {
|
|
const inquiry = await ContactInquiry.findById(id);
|
|
if (!inquiry) {
|
|
throw new AppError('NOT_FOUND', null, 'درخواست تماس یافت نشد.');
|
|
}
|
|
|
|
if (updateData.status !== undefined) {
|
|
if (!ContactInquiry.CONTACT_STATUSES.includes(updateData.status)) {
|
|
throw new AppError('VALIDATION_FAILED', { status: 'Invalid status' }, 'وضعیت نامعتبر است.');
|
|
}
|
|
inquiry.status = updateData.status;
|
|
}
|
|
|
|
if (updateData.notes !== undefined) {
|
|
inquiry.notes = String(updateData.notes).slice(0, 5000);
|
|
}
|
|
|
|
await inquiry.save();
|
|
return getInquiryById(id);
|
|
};
|
|
|
|
module.exports = {
|
|
createInquiry,
|
|
getAllInquiries,
|
|
getInquiryById,
|
|
updateInquiry
|
|
};
|