full app
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable eqeqeq */
|
||||
/* eslint-disable camelcase */
|
||||
const mongoose = require('mongoose')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const { check, validationResult } = require('express-validator')
|
||||
const { billboardPublicStatusFilter } = require('../../../utils/moderationStatus')
|
||||
@@ -25,6 +26,8 @@ const {
|
||||
createRatingNotification
|
||||
} = require('../../../utils/commentNotification')
|
||||
const { buildUnifiedInterestScore, blendFreshIntoRanked } = require('../../../utils/exploreAlgorithm')
|
||||
const { resolveStoryFlags } = require('../../../utils/storyRingFlags')
|
||||
const { findActiveAd, buildAdBillboardItem, trackAdEvent } = require('../../../utils/adDelivery')
|
||||
|
||||
/** 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) {
|
||||
@@ -540,6 +543,24 @@ const getAdvertisingsWeb = async (req, res, next) => {
|
||||
} catch {
|
||||
/* non-blocking: personalization is a bonus, never break the listing */
|
||||
}
|
||||
|
||||
// تبلیغِ نوع billboard — عینِ الگوی first_post (adDelivery.js): فقط صفحهی اول،
|
||||
// فقط برای کاربرِ واردشده، خطاش هیچوقت نباید لیستِ بیلبوردها رو بشکنه
|
||||
try {
|
||||
const viewer = await UserModel.findById(userId)
|
||||
.select('country province city gender expertise sub_expertise')
|
||||
.lean()
|
||||
const sponsoredAd = await findActiveAd('billboard', { ...viewer, _id: userId })
|
||||
if (sponsoredAd) {
|
||||
const adBillboard = await buildAdBillboardItem(sponsoredAd)
|
||||
if (adBillboard) {
|
||||
newAds = [adBillboard, ...newAds]
|
||||
trackAdEvent(sponsoredAd._id, 'view').catch(() => {})
|
||||
}
|
||||
}
|
||||
} catch (adError) {
|
||||
console.error('billboard ad injection error:', adError.message)
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
@@ -831,12 +852,26 @@ const addComment = async (req, res, next) => {
|
||||
const decodedToken = jwt.verify(token, process.env.APP_SECRET)
|
||||
const userId = decodedToken.id
|
||||
const user = await UserModel.findById(decodedToken.id)
|
||||
|
||||
// مثل شاپپروداکت/آموزشگاه: امتیاز فقط روی اولین کامنتِ هر کاربر روی این آگهی مجازه
|
||||
let rateValue = null
|
||||
const requestedRate = Number(req.body.rate)
|
||||
if (requestedRate >= 1 && requestedRate <= 5) {
|
||||
const existingRating = await AdvertisingComment.findOne({
|
||||
advertisingId,
|
||||
userId,
|
||||
rate: { $ne: null, $gt: 0 }
|
||||
}).select('_id')
|
||||
if (!existingRating) rateValue = requestedRate
|
||||
}
|
||||
|
||||
const newComment = await saveCommentWithModerationStatus(
|
||||
AdvertisingComment,
|
||||
{
|
||||
advertisingId,
|
||||
userId,
|
||||
text
|
||||
text,
|
||||
rate: rateValue
|
||||
},
|
||||
'text',
|
||||
user?.verify_badge
|
||||
@@ -872,11 +907,62 @@ const getComments = async (req, res, next) => {
|
||||
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: commentStatusFilter() }, options)
|
||||
const [comments, ratingAgg] = await Promise.all([
|
||||
AdvertisingComment.paginate({ advertisingId, status: commentStatusFilter() }, options),
|
||||
AdvertisingComment.aggregate([
|
||||
{ $match: { advertisingId: new mongoose.Types.ObjectId(advertisingId), rate: { $ne: null, $gt: 0 } } },
|
||||
{ $group: { _id: null, average: { $avg: '$rate' }, count: { $sum: 1 } } }
|
||||
])
|
||||
])
|
||||
|
||||
// بررسی وجود توکن (اختیاری — لیست کامنتها بدون لاگین هم قابل مشاهدهست)
|
||||
let currentUserId = null
|
||||
const authHeader = req.header('Authorization')
|
||||
if (authHeader) {
|
||||
const tokenParts = authHeader.split(' ')
|
||||
if (tokenParts.length === 2) {
|
||||
try {
|
||||
currentUserId = jwt.verify(tokenParts[1], process.env.APP_SECRET).id
|
||||
} catch (err) {
|
||||
console.error('Invalid Token:', err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let hasRated = false
|
||||
if (currentUserId) {
|
||||
const existingRating = await AdvertisingComment.findOne({
|
||||
advertisingId,
|
||||
userId: currentUserId,
|
||||
rate: { $ne: null, $gt: 0 }
|
||||
}).select('_id')
|
||||
hasRated = Boolean(existingRating)
|
||||
}
|
||||
|
||||
const commenterIds = comments.docs.map((c) => c.userId?._id).filter(Boolean)
|
||||
const storyFlags = await resolveStoryFlags(commenterIds, currentUserId)
|
||||
const commentsWithStoryFlags = comments.docs.map((c) => {
|
||||
const obj = c.toObject ? c.toObject() : c
|
||||
if (obj.userId) {
|
||||
const flags = storyFlags[String(obj.userId._id)] || {}
|
||||
obj.userId = {
|
||||
...obj.userId,
|
||||
has_story: flags.has_story || false,
|
||||
has_unviewed_story: flags.has_unviewed_story || false
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
res.status(200).json({
|
||||
comments: comments.docs,
|
||||
comments: commentsWithStoryFlags,
|
||||
totalPages: comments.totalPages,
|
||||
totalItems: comments.totalDocs
|
||||
totalItems: comments.totalDocs,
|
||||
has_rated: hasRated,
|
||||
rating: {
|
||||
average: ratingAgg[0]?.average || 0,
|
||||
count: ratingAgg[0]?.count || 0
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
|
||||
Reference in New Issue
Block a user