full
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
/* eslint-disable camelcase */
|
||||
const jwt = require('jsonwebtoken')
|
||||
const { check, validationResult } = require('express-validator')
|
||||
const { ProvinceModel, CityModel } = require('../../../models/StateCity')
|
||||
const { billboardPublicStatusFilter } = require('../../../utils/moderationStatus')
|
||||
const { CountryModel, ProvinceModel, CityModel } = require('../../../models/StateCity')
|
||||
const UserModel = require('../../../models/UserModel')
|
||||
const AdvertisingModel = require('../../../models/AdvertisingModel')
|
||||
const fs = require('fs-extra')
|
||||
@@ -12,13 +13,41 @@ const AdvertisingCategoryModel = require('../../../models/AdvertisingCategoryMod
|
||||
const AdvertisingFeaturesModel = require('../../../models/AdvertisingFeaturesModel')
|
||||
const AdvertisingLikeModel = require('../../../models/AdvertisingLikeModel')
|
||||
const AdvertisingComment = require('../../../models/AdvertisingCommentModel')
|
||||
const { resolveCommentModerationStatus } = require('../../../utils/contentModeration')
|
||||
const { saveCommentWithModerationStatus } = require('../../../utils/commentStatusSave')
|
||||
const { commentStatusFilter } = require('../../../utils/moderationStatus')
|
||||
const AdvertisingRatingModel = require('../../../models/AdvertisingRatingModel')
|
||||
const AdvertisingProfileModel = require('../../../models/AdvertisingProfile')
|
||||
const { createLikeNotification } = require('../../../utils/likeNotification')
|
||||
const PaymentModel = require('../../../models/PaymentModel')
|
||||
const { createLikeNotification, resolveAdvertisingOwnerId } = require('../../../utils/likeNotification')
|
||||
const {
|
||||
createCommentNotification,
|
||||
createRatingNotification
|
||||
} = require('../../../utils/commentNotification')
|
||||
const { buildUnifiedInterestScore, blendFreshIntoRanked } = require('../../../utils/exploreAlgorithm')
|
||||
|
||||
/** Billboards matching the viewer's cross-content interest profile, for blending into the default (unsorted, unfiltered) listing. */
|
||||
async function getInterestMatchedAds (userId, filter, excludeIds, limit = 5) {
|
||||
if (!userId) return []
|
||||
const interestWeights = await buildUnifiedInterestScore(userId)
|
||||
const tags = Object.keys(interestWeights).filter((tag) => interestWeights[tag] > 0)
|
||||
if (!tags.length) return []
|
||||
|
||||
const interestCategories = await AdvertisingCategoryModel.find({ relatedExpertise: { $in: tags } })
|
||||
.select('title')
|
||||
.lean()
|
||||
const categoryTitles = interestCategories.map((c) => c.title)
|
||||
if (!categoryTitles.length) return []
|
||||
|
||||
return AdvertisingModel.find({
|
||||
...filter,
|
||||
category: { $in: categoryTitles },
|
||||
_id: { $nin: excludeIds }
|
||||
})
|
||||
.sort({ createdAt: -1 })
|
||||
.limit(limit)
|
||||
.lean()
|
||||
}
|
||||
|
||||
const createAdvertisingValidationRules = () => {
|
||||
return [
|
||||
@@ -66,6 +95,7 @@ const createAdvertising = async (req, res, next) => {
|
||||
category,
|
||||
title,
|
||||
description,
|
||||
country,
|
||||
province,
|
||||
city,
|
||||
neighbourhood,
|
||||
@@ -84,6 +114,7 @@ const createAdvertising = async (req, res, next) => {
|
||||
return res.status(422).json({ error: true, message: 'اطلاعات ارسالی اشتباه است' })
|
||||
}
|
||||
|
||||
const countryFind = country ? await CountryModel.findOne({ id: country }, { _id: 0 }) : null
|
||||
const provinceFind = await ProvinceModel.findOne({ id: province }, { _id: 0 })
|
||||
const cityFind = await CityModel.findOne({ id: city })
|
||||
|
||||
@@ -131,6 +162,7 @@ const createAdvertising = async (req, res, next) => {
|
||||
title,
|
||||
category,
|
||||
description,
|
||||
country: countryFind ? { id: countryFind.id, name: countryFind.name_fa, iso2: countryFind.iso2, slug: countryFind.slug } : null,
|
||||
province: provinceFind,
|
||||
city: cityFind,
|
||||
neighbourhood,
|
||||
@@ -258,19 +290,16 @@ const createAdvertising = async (req, res, next) => {
|
||||
// }
|
||||
// }
|
||||
const getAdvertisings = async (req, res, next) => {
|
||||
let previousPage = 0 // تعریف متغیر سراسری برای نگهداری شماره صفحه قبلی
|
||||
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
|
||||
const { page = 1, limit = 10, province, city, category, search, sort } = req.query
|
||||
const currentPage = parseInt(page) // شماره صفحه جاری
|
||||
|
||||
const filter = {
|
||||
payment_status: 'done',
|
||||
status: 'accepted'
|
||||
status: billboardPublicStatusFilter()
|
||||
}
|
||||
if (province) {
|
||||
const provinceFind = await ProvinceModel.findOne({ id: province }, { _id: 0 })
|
||||
@@ -310,21 +339,13 @@ const getAdvertisings = async (req, res, next) => {
|
||||
}
|
||||
const advertisings = await AdvertisingModel.paginate(filter, options)
|
||||
|
||||
// اگر شماره صفحه تغییر کرده بود، تعداد بازدیدها را به روز رسانی کنید
|
||||
if (currentPage !== previousPage) {
|
||||
await AdvertisingModel.updateMany(
|
||||
{ _id: { $in: advertisings.docs.map(ad => ad._id) } },
|
||||
{ $inc: { viewCount: 1 } }
|
||||
)
|
||||
previousPage = currentPage // بهروزرسانی شماره صفحه قبلی
|
||||
}
|
||||
// دریافت لیست لایک هر ویترین و اضافه کردن آن به اطلاعات تبلیغات
|
||||
const advertisingIds = advertisings.docs.map(ad => ad._id)
|
||||
const advertisingLikes = await AdvertisingLikeModel.find({ advertisingId: { $in: advertisingIds } })
|
||||
|
||||
const creatorIds = advertisings.docs.map(ad => ad.creator_id)
|
||||
const creators = await AdvertisingProfileModel.find({ user: { $in: creatorIds } })
|
||||
const newAds = advertisings.docs.map(ad => {
|
||||
let newAds = advertisings.docs.map(ad => {
|
||||
const creator = creators.find(user => user.user.toString() === ad.creator_id.toString())
|
||||
const likedByUser = advertisingLikes.some(like => like.advertisingId.toString() === ad._id.toString() && like.userId.toString() === decodedToken.id)
|
||||
return {
|
||||
@@ -343,12 +364,48 @@ const getAdvertisings = async (req, res, next) => {
|
||||
commentsCount: ad._doc.commentsCount,
|
||||
showDiscount: ad._doc.showDiscount,
|
||||
viewCount: ad._doc.viewCount,
|
||||
createdAt: ad._doc.createdAt,
|
||||
rate: {
|
||||
adTotalRatings: creator?.adTotalRatings,
|
||||
adAverageRating: creator?.adAverageRating
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const isDefaultBrowsePath = !sort && !category && !search && parseInt(page) === 1
|
||||
if (isDefaultBrowsePath) {
|
||||
try {
|
||||
const interestAds = await getInterestMatchedAds(decodedToken.id, filter, newAds.map((a) => a._id))
|
||||
if (interestAds.length) {
|
||||
const interestCreators = await AdvertisingProfileModel.find({ user: { $in: interestAds.map((ad) => ad.creator_id) } })
|
||||
const mappedInterestAds = interestAds.map((ad) => {
|
||||
const creator = interestCreators.find((user) => user.user.toString() === ad.creator_id.toString())
|
||||
return {
|
||||
_id: ad._id,
|
||||
category: ad.category,
|
||||
title: ad.title,
|
||||
province: ad.province,
|
||||
city: ad.city,
|
||||
neighbourhood: ad.neighbourhood,
|
||||
type: ad.type,
|
||||
mostDiscountPercentage: ad.mostDiscountPercentage,
|
||||
images: ad.images,
|
||||
likedByUser: false,
|
||||
likesCount: ad.likesCount,
|
||||
commentsCount: ad.commentsCount,
|
||||
showDiscount: ad.showDiscount,
|
||||
viewCount: ad.viewCount,
|
||||
createdAt: ad.createdAt,
|
||||
rate: { adTotalRatings: creator?.adTotalRatings, adAverageRating: creator?.adAverageRating }
|
||||
}
|
||||
})
|
||||
newAds = blendFreshIntoRanked(newAds, [...newAds, ...mappedInterestAds], 0.25).slice(0, parseInt(limit))
|
||||
}
|
||||
} catch {
|
||||
/* non-blocking: personalization is a bonus, never break the listing */
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
advertisings: newAds,
|
||||
totalPages: advertisings.totalPages,
|
||||
@@ -360,7 +417,6 @@ const getAdvertisings = async (req, res, next) => {
|
||||
}
|
||||
|
||||
const getAdvertisingsWeb = async (req, res, next) => {
|
||||
let previousPage = 0
|
||||
let userId = null
|
||||
|
||||
try {
|
||||
@@ -375,11 +431,9 @@ const getAdvertisingsWeb = async (req, res, next) => {
|
||||
}
|
||||
|
||||
const { page = 1, limit = 10, province, city, category, search, sort } = req.query
|
||||
const currentPage = parseInt(page)
|
||||
|
||||
const filter = {
|
||||
// payment_status: 'done',
|
||||
status: 'accepted'
|
||||
status: billboardPublicStatusFilter(),
|
||||
}
|
||||
if (province) {
|
||||
const provinceFind = await ProvinceModel.findOne({ id: province }, { _id: 0 })
|
||||
@@ -418,14 +472,6 @@ const getAdvertisingsWeb = async (req, res, next) => {
|
||||
}
|
||||
const advertisings = await AdvertisingModel.paginate(filter, options)
|
||||
|
||||
if (currentPage !== previousPage) {
|
||||
await AdvertisingModel.updateMany(
|
||||
{ _id: { $in: advertisings.docs.map(ad => ad._id) } },
|
||||
{ $inc: { viewCount: 1 } }
|
||||
)
|
||||
previousPage = currentPage
|
||||
}
|
||||
|
||||
const advertisingIds = advertisings.docs.map(ad => ad._id)
|
||||
const creatorIds = advertisings.docs.map(ad => ad.creator_id)
|
||||
const creators = await AdvertisingProfileModel.find({ user: { $in: creatorIds } })
|
||||
@@ -435,7 +481,7 @@ const getAdvertisingsWeb = async (req, res, next) => {
|
||||
advertisingLikes = await AdvertisingLikeModel.find({ advertisingId: { $in: advertisingIds }, userId })
|
||||
}
|
||||
|
||||
const newAds = advertisings.docs.map(ad => {
|
||||
let newAds = advertisings.docs.map(ad => {
|
||||
const creator = creators.find(user => user.user.toString() === ad.creator_id.toString())
|
||||
const likedByUser = userId ? advertisingLikes.some(like => like.advertisingId.toString() === ad._id.toString()) : false
|
||||
|
||||
@@ -454,6 +500,7 @@ const getAdvertisingsWeb = async (req, res, next) => {
|
||||
commentsCount: ad._doc.commentsCount,
|
||||
showDiscount: ad._doc.showDiscount,
|
||||
viewCount: ad._doc.viewCount,
|
||||
createdAt: ad._doc.createdAt,
|
||||
rate: {
|
||||
adTotalRatings: creator?.adTotalRatings,
|
||||
adAverageRating: creator?.adAverageRating
|
||||
@@ -461,6 +508,40 @@ const getAdvertisingsWeb = async (req, res, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
const isDefaultBrowsePath = !sort && !category && !search && parseInt(page) === 1
|
||||
if (isDefaultBrowsePath && userId) {
|
||||
try {
|
||||
const interestAds = await getInterestMatchedAds(userId, filter, newAds.map((a) => a._id))
|
||||
if (interestAds.length) {
|
||||
const interestCreators = await AdvertisingProfileModel.find({ user: { $in: interestAds.map((ad) => ad.creator_id) } })
|
||||
const mappedInterestAds = interestAds.map((ad) => {
|
||||
const creator = interestCreators.find((user) => user.user.toString() === ad.creator_id.toString())
|
||||
return {
|
||||
_id: ad._id,
|
||||
category: ad.category,
|
||||
title: ad.title,
|
||||
province: ad.province,
|
||||
city: ad.city,
|
||||
neighbourhood: ad.neighbourhood,
|
||||
type: ad.type,
|
||||
mostDiscountPercentage: ad.mostDiscountPercentage,
|
||||
images: ad.images,
|
||||
likedByUser: false,
|
||||
likesCount: ad.likesCount,
|
||||
commentsCount: ad.commentsCount,
|
||||
showDiscount: ad.showDiscount,
|
||||
viewCount: ad.viewCount,
|
||||
createdAt: ad.createdAt,
|
||||
rate: { adTotalRatings: creator?.adTotalRatings, adAverageRating: creator?.adAverageRating }
|
||||
}
|
||||
})
|
||||
newAds = blendFreshIntoRanked(newAds, [...newAds, ...mappedInterestAds], 0.25).slice(0, parseInt(limit))
|
||||
}
|
||||
} catch {
|
||||
/* non-blocking: personalization is a bonus, never break the listing */
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
advertisings: newAds,
|
||||
totalPages: advertisings.totalPages,
|
||||
@@ -471,6 +552,29 @@ const getAdvertisingsWeb = async (req, res, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
const recordAdvertisingView = async (req, res, next) => {
|
||||
try {
|
||||
const { advertisingId } = req.params
|
||||
const advertising = await AdvertisingModel.findByIdAndUpdate(
|
||||
advertisingId,
|
||||
{ $inc: { viewCount: 1 } },
|
||||
{ new: true }
|
||||
).select('viewCount status')
|
||||
|
||||
if (!advertising) {
|
||||
return res.status(404).json({ message: 'ویترین مورد نظر یافت نشد' })
|
||||
}
|
||||
|
||||
if (advertising.status !== 'accepted') {
|
||||
return res.status(404).json({ message: 'ویترین مورد نظر یافت نشد' })
|
||||
}
|
||||
|
||||
res.status(200).json({ viewCount: advertising.viewCount ?? 0 })
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getSingleAdvertising = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
@@ -492,7 +596,7 @@ const getSingleAdvertising = async (req, res, next) => {
|
||||
const likedByUser = await AdvertisingLikeModel.findOne({ advertisingId, userId })
|
||||
|
||||
// دریافت تعداد کامنتها
|
||||
const commentsCount = await AdvertisingComment.countDocuments({ advertisingId, status: 'accepted' })
|
||||
const commentsCount = await AdvertisingComment.countDocuments({ advertisingId, status: commentStatusFilter() })
|
||||
const creator = await AdvertisingProfileModel.findOne({ user: advertising.creator_id._id })
|
||||
|
||||
// اضافه کردن تعداد لایکها، تعداد کامنتها و وضعیت لایک به تبلیغ
|
||||
@@ -546,7 +650,7 @@ const getSingleAdvertisingWeb = async (req, res, next) => {
|
||||
const likesCount = await AdvertisingLikeModel.countDocuments({ advertisingId })
|
||||
|
||||
// دریافت تعداد کامنتها
|
||||
const commentsCount = await AdvertisingComment.countDocuments({ advertisingId, status: 'accepted' })
|
||||
const commentsCount = await AdvertisingComment.countDocuments({ advertisingId, status: commentStatusFilter() })
|
||||
|
||||
// دریافت اطلاعات سازنده تبلیغ
|
||||
const creator = await AdvertisingProfileModel.findOne({ user: advertising.creator_id._id })
|
||||
@@ -705,7 +809,8 @@ const toggleLike = async (req, res, next) => {
|
||||
await advertising.save()
|
||||
|
||||
await createLikeNotification({
|
||||
ownerId: advertising.creator_id,
|
||||
io: req.app.get('io'),
|
||||
ownerId: resolveAdvertisingOwnerId(advertising),
|
||||
likerId: userId,
|
||||
entityId: advertising._id,
|
||||
type: 'billboard_like'
|
||||
@@ -733,18 +838,21 @@ const addComment = async (req, res, next) => {
|
||||
// message: 'مدارک شما تایید نشده است'
|
||||
// })
|
||||
// }
|
||||
const newComment = new AdvertisingComment({
|
||||
advertisingId,
|
||||
userId,
|
||||
text
|
||||
})
|
||||
|
||||
await newComment.save()
|
||||
const newComment = await saveCommentWithModerationStatus(
|
||||
AdvertisingComment,
|
||||
{
|
||||
advertisingId,
|
||||
userId,
|
||||
text,
|
||||
},
|
||||
'text'
|
||||
)
|
||||
|
||||
const advertising = await AdvertisingModel.findById(advertisingId)
|
||||
if (advertising) {
|
||||
await createCommentNotification({
|
||||
ownerId: advertising.creator_id,
|
||||
io: req.app.get('io'),
|
||||
ownerId: resolveAdvertisingOwnerId(advertising),
|
||||
commenterId: userId,
|
||||
entityId: advertising._id,
|
||||
type: 'billboard_comment'
|
||||
@@ -753,6 +861,10 @@ const addComment = async (req, res, next) => {
|
||||
|
||||
res.status(201).json({ message: 'کامنت با موفقیت اضافه شد', comment: newComment })
|
||||
} catch (error) {
|
||||
if (error?.name === 'ValidationError') {
|
||||
console.error('addComment validation:', error.message)
|
||||
return res.status(500).json({ message: 'خطا در ثبت کامنت. مدل کامنت روی سرور بهروز نیست.' })
|
||||
}
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
@@ -763,10 +875,10 @@ const getComments = async (req, res, next) => {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
sort: { createdAt: -1 },
|
||||
populate: [{ path: 'userId', select: '_id profile_image user_type user_name first_name last_name is_verified' }]
|
||||
populate: [{ path: 'userId', select: '_id profile_image user_type user_name first_name last_name is_verified verify_badge' }]
|
||||
}
|
||||
|
||||
const comments = await AdvertisingComment.paginate({ advertisingId, status: 'accepted' }, options)
|
||||
const comments = await AdvertisingComment.paginate({ advertisingId, status: commentStatusFilter() }, options)
|
||||
res.status(200).json({
|
||||
comments: comments.docs,
|
||||
totalPages: comments.totalPages,
|
||||
@@ -806,7 +918,8 @@ const rateAdvertising = async (req, res, next) => {
|
||||
await newRating.save()
|
||||
|
||||
await createRatingNotification({
|
||||
ownerId: advertising.creator_id,
|
||||
io: req.app.get('io'),
|
||||
ownerId: resolveAdvertisingOwnerId(advertising),
|
||||
raterId: userId,
|
||||
entityId: advertising._id,
|
||||
type: 'billboard_rating'
|
||||
@@ -939,6 +1052,91 @@ const getUserAdvertisings = async (req, res, next) => {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const ADS_ANALYTICS_RANGE_DAYS = { '7d': 7, '30d': 30, '90d': 90, all: null }
|
||||
|
||||
/** Revenue-over-time + top billboards + cumulative view/like/comment totals for "Billboards" analytics. */
|
||||
const getAdvertisingAnalytics = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decodedToken.id
|
||||
|
||||
const range = ADS_ANALYTICS_RANGE_DAYS[req.query.range] !== undefined ? req.query.range : '30d'
|
||||
const days = ADS_ANALYTICS_RANGE_DAYS[range]
|
||||
const since = days ? new Date(Date.now() - days * 24 * 60 * 60 * 1000) : new Date(0)
|
||||
|
||||
const myAds = await AdvertisingModel.find({ creator_id: userId })
|
||||
.select('title images viewCount likesCount commentsCount averageRating ratingsCount')
|
||||
const adIds = myAds.map((ad) => ad._id)
|
||||
|
||||
if (!adIds.length) {
|
||||
return res.json({
|
||||
range,
|
||||
series: [],
|
||||
totals: { totalRevenue: 0, totalCount: 0, totalViews: 0, totalLikes: 0, totalComments: 0 },
|
||||
topItems: [],
|
||||
rating: { average: 0, count: 0 }
|
||||
})
|
||||
}
|
||||
|
||||
const [series, totalsAgg] = await Promise.all([
|
||||
PaymentModel.aggregate([
|
||||
{ $match: { advertising_id: { $in: adIds }, status: 'successful', createdAt: { $gte: since } } },
|
||||
{
|
||||
$group: {
|
||||
_id: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } },
|
||||
revenue: { $sum: '$amount' },
|
||||
count: { $sum: 1 }
|
||||
}
|
||||
},
|
||||
{ $sort: { _id: 1 } }
|
||||
]),
|
||||
PaymentModel.aggregate([
|
||||
{ $match: { advertising_id: { $in: adIds }, status: 'successful', createdAt: { $gte: since } } },
|
||||
{ $group: { _id: null, totalRevenue: { $sum: '$amount' }, totalCount: { $sum: 1 } } }
|
||||
])
|
||||
])
|
||||
|
||||
const totalViews = myAds.reduce((sum, ad) => sum + (ad.viewCount || 0), 0)
|
||||
const totalLikes = myAds.reduce((sum, ad) => sum + (ad.likesCount || 0), 0)
|
||||
const totalComments = myAds.reduce((sum, ad) => sum + (ad.commentsCount || 0), 0)
|
||||
const ratedAds = myAds.filter((ad) => ad.ratingsCount > 0)
|
||||
const averageRating = ratedAds.length
|
||||
? ratedAds.reduce((sum, ad) => sum + (ad.averageRating || 0), 0) / ratedAds.length
|
||||
: 0
|
||||
const totalRatingsCount = myAds.reduce((sum, ad) => sum + (ad.ratingsCount || 0), 0)
|
||||
|
||||
const topItems = [...myAds]
|
||||
.sort((a, b) => (b.viewCount || 0) - (a.viewCount || 0))
|
||||
.slice(0, 5)
|
||||
.map((ad) => ({
|
||||
id: String(ad._id),
|
||||
title: ad.title,
|
||||
image: ad.images?.[0] || null,
|
||||
metric: ad.viewCount || 0
|
||||
}))
|
||||
|
||||
return res.json({
|
||||
range,
|
||||
series,
|
||||
totals: {
|
||||
totalRevenue: totalsAgg[0]?.totalRevenue || 0,
|
||||
totalCount: totalsAgg[0]?.totalCount || 0,
|
||||
totalViews,
|
||||
totalLikes,
|
||||
totalComments
|
||||
},
|
||||
topItems,
|
||||
rating: { average: averageRating, count: totalRatingsCount }
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
|
||||
const getUserSingleAdvertising = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization').split(' ')[1]
|
||||
@@ -966,7 +1164,7 @@ const getUserSingleAdvertising = async (req, res, next) => {
|
||||
const likedByUser = await AdvertisingLikeModel.findOne({ advertisingId, userId })
|
||||
|
||||
// دریافت تعداد کامنتها
|
||||
const commentsCount = await AdvertisingComment.countDocuments({ advertisingId, status: 'accepted' })
|
||||
const commentsCount = await AdvertisingComment.countDocuments({ advertisingId, status: commentStatusFilter() })
|
||||
const creator = await AdvertisingProfileModel.findOne({ user: advertising.creator_id._id })
|
||||
|
||||
// اضافه کردن تعداد لایکها، تعداد کامنتها و وضعیت لایک به تبلیغ
|
||||
@@ -1020,6 +1218,7 @@ const editAdvertising = async (req, res, next) => {
|
||||
category,
|
||||
title,
|
||||
description,
|
||||
country,
|
||||
province,
|
||||
city,
|
||||
neighbourhood,
|
||||
@@ -1054,6 +1253,7 @@ const editAdvertising = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
const countryFind = country ? await CountryModel.findOne({ id: country }, { _id: 0 }) : null
|
||||
const provinceFind = await ProvinceModel.findOne({ id: province }, { _id: 0 })
|
||||
const cityFind = await CityModel.findOne({ id: city })
|
||||
|
||||
@@ -1121,6 +1321,9 @@ const editAdvertising = async (req, res, next) => {
|
||||
advertising.title = title
|
||||
advertising.category = category
|
||||
advertising.description = description
|
||||
if (countryFind) {
|
||||
advertising.country = { id: countryFind.id, name: countryFind.name_fa, iso2: countryFind.iso2, slug: countryFind.slug }
|
||||
}
|
||||
advertising.province = provinceFind
|
||||
advertising.city = cityFind
|
||||
advertising.neighbourhood = neighbourhood
|
||||
@@ -1419,6 +1622,77 @@ const updateUserAdvertisingProfile = async (req, res, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
const getUserLikedAdvertisings = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization')?.split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decodedToken.id
|
||||
|
||||
const page = parseInt(req.query.page) || 1
|
||||
const limit = parseInt(req.query.limit) || 10
|
||||
const skip = (page - 1) * limit
|
||||
|
||||
const likes = await AdvertisingLikeModel.find({ userId })
|
||||
.populate('advertisingId')
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(limit)
|
||||
|
||||
const total = await AdvertisingLikeModel.countDocuments({ userId })
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: likes,
|
||||
pagination: {
|
||||
currentPage: page,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
totalItems: total,
|
||||
itemsPerPage: limit
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error in getUserLikedAdvertisings:', error)
|
||||
res.status(500).json({ success: false, message: 'خطا در دریافت بیلبوردهای لایک شده', error: error.message })
|
||||
}
|
||||
}
|
||||
|
||||
const getUserAdvertisingComments = async (req, res, next) => {
|
||||
try {
|
||||
const token = req.header('Authorization')?.split(' ')[1]
|
||||
if (!token) return res.status(401).send('Access Denied')
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decodedToken.id
|
||||
|
||||
const page = parseInt(req.query.page) || 1
|
||||
const limit = parseInt(req.query.limit) || 10
|
||||
const skip = (page - 1) * limit
|
||||
|
||||
const [comments, total] = await Promise.all([
|
||||
AdvertisingComment.find({ userId })
|
||||
.populate('advertisingId')
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(limit),
|
||||
AdvertisingComment.countDocuments({ userId })
|
||||
])
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: comments,
|
||||
pagination: {
|
||||
currentPage: page,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
totalItems: total,
|
||||
itemsPerPage: limit
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error in getUserAdvertisingComments:', error)
|
||||
res.status(500).json({ success: false, message: 'خطا در دریافت نظرات بیلبورد', error: error.message })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createAdvertisingValidationRules,
|
||||
createAdvertising,
|
||||
@@ -1432,11 +1706,15 @@ module.exports = {
|
||||
getComments,
|
||||
rateAdvertising,
|
||||
getUserAdvertisings,
|
||||
getAdvertisingAnalytics,
|
||||
editAdvertising,
|
||||
getUserSingleAdvertising,
|
||||
getUserAdvertisingProfile,
|
||||
updateUserAdvertisingProfile,
|
||||
getAdvertisingsWeb,
|
||||
getSingleAdvertisingWeb,
|
||||
getUserAdvertisingProfileWeb
|
||||
getUserAdvertisingProfileWeb,
|
||||
recordAdvertisingView,
|
||||
getUserLikedAdvertisings,
|
||||
getUserAdvertisingComments
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user