4.1.3
This commit is contained in:
@@ -1583,25 +1583,27 @@ const getPostsWeb = async (req, res, next) => {
|
||||
})
|
||||
}
|
||||
|
||||
// پروفایل یک کاربر خاص بهصورت ریلز: همهی پستهای pool از یک نویسندهن، پس رتبهبندی
|
||||
// شخصیسازیشده (paginatePersonalizedExplore) بیمعنیه — diversifyByAuthor توش با
|
||||
// maxPerPage=1 فقط اولین پست رو نگه میداره و بقیهی پستهای همون یک نویسنده رو با
|
||||
// ترتیبِ امتیازدهیشده (نه زمانی) میریزه ته لیست، یعنی «پست بعدی» دیگه پست
|
||||
// بعدیِ واقعیِ همون کاربر نیست. اینجا فقط seed post میره اول، بقیه دقیقاً با همون
|
||||
// ترتیب زمانی/پینشدهی posts (که بالاتر مرتب شده) میمونن
|
||||
if (feedMode === 'reels' && _id && seedPostId && posts.length > 1) {
|
||||
const { posts: paginatedPosts, totalItems, isColdStart } =
|
||||
await paginatePersonalizedExplore({
|
||||
posts,
|
||||
usersById,
|
||||
page,
|
||||
limit,
|
||||
userId: decodedToken?.id,
|
||||
seedPostId,
|
||||
reelsMode: true,
|
||||
blendFresh: false,
|
||||
cacheScope: `reels-profile:${_id}`
|
||||
})
|
||||
const seedIndex = posts.findIndex((p) => String(p._id) === String(seedPostId))
|
||||
const orderedPosts = seedIndex > 0
|
||||
? [posts[seedIndex], ...posts.slice(0, seedIndex), ...posts.slice(seedIndex + 1)]
|
||||
: posts
|
||||
|
||||
const startIndex = (page - 1) * limit
|
||||
const endIndex = page * limit
|
||||
const totalItems = orderedPosts.length
|
||||
const paginatedPosts = orderedPosts.slice(startIndex, endIndex)
|
||||
|
||||
return res.status(200).json({
|
||||
posts: paginatedPosts,
|
||||
totalPages: Math.ceil(totalItems / limit) || 1,
|
||||
totalItems,
|
||||
feedMeta: { isColdStart: !!isColdStart }
|
||||
totalItems
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1919,7 +1921,9 @@ const getSingleUserWeb = async (req, res, next) => {
|
||||
eye_color: user.eye_color,
|
||||
hair_color: user.hair_color,
|
||||
// «مشخصات فردی» فقط وقتی خودِ کاربر با تیک «نمایش مشخصات من برای عموم» موافقت کرده
|
||||
// برای بقیه نمایش داده میشه — برای خودِ صاحب پروفایل صرفنظر از این تیک همیشه برمیگرده
|
||||
// برای بقیه نمایش داده میشه — برای خودِ صاحب پروفایل صرفنظر از این تیک همیشه برمیگرده.
|
||||
// فیلدهای ترجیحاتِ پارتنریابی (relationship_goal تا smoking_status) بعداً به فرمِ
|
||||
// ویرایشِ مشخصاتِ فردی اضافه شدن ولی اینجا جا مونده بودن — الان همراهِ بقیه برمیگردن
|
||||
...(user.personal_details_public === true || isSelf
|
||||
? {
|
||||
marital_status: user.marital_status,
|
||||
@@ -1930,7 +1934,15 @@ const getSingleUserWeb = async (req, res, next) => {
|
||||
personal_style: user.personal_style,
|
||||
job: user.job,
|
||||
income_range: user.income_range,
|
||||
moral_traits: user.moral_traits
|
||||
relationship_goal: user.relationship_goal,
|
||||
personality_traits: user.personality_traits,
|
||||
lifestyle: user.lifestyle,
|
||||
interests: user.interests,
|
||||
values: user.values,
|
||||
relationship_view: user.relationship_view,
|
||||
geo_preference: user.geo_preference,
|
||||
children_status: user.children_status,
|
||||
smoking_status: user.smoking_status
|
||||
}
|
||||
: {}),
|
||||
personal_details_public: user.personal_details_public === true,
|
||||
|
||||
@@ -83,16 +83,18 @@ const getNearbyUsers = async (req, res, next) => {
|
||||
)
|
||||
const blockedMe = new Set((blockerIds || []).map((id) => String(id)))
|
||||
|
||||
// فقط کاربرانی که «نمایش لوکیشن به دیگران» رو فعال کردن و دسترسی موقعیت مکانی
|
||||
// رو به اپ دادهن (یعنی lat/lng واقعی دارن) باید توی رادار/لیست/نقشه دیده بشن
|
||||
const candidates = await UserModel.find({
|
||||
_id: { $ne: userId },
|
||||
show_location: true,
|
||||
account_status: { $ne: 'deactivated' },
|
||||
show_location: true,
|
||||
lat: { $nin: [null, ''] },
|
||||
lng: { $nin: [null, ''] },
|
||||
user_name: { $ne: null }
|
||||
})
|
||||
.select(
|
||||
'user_name first_name last_name profile_image is_verified verify_badge is_Register lat lng blocked_users'
|
||||
'user_name first_name last_name profile_image is_verified verify_badge is_Register show_location lat lng blocked_users'
|
||||
)
|
||||
.lean()
|
||||
|
||||
@@ -120,6 +122,9 @@ const getNearbyUsers = async (req, res, next) => {
|
||||
is_verified: user.is_verified,
|
||||
verify_badge: user.verify_badge,
|
||||
is_Register: user.is_Register,
|
||||
// فقط با تیکِ «نمایش لوکیشن به دیگران» باید روی نقشهی واقعی قرار بگیرن — بدونش
|
||||
// فقط توی رادار نشون داده میشن (تصمیم روی فرانتاند، اینجا فقط پرچمش رو میفرستیم)
|
||||
show_location: user.show_location === true,
|
||||
distanceMeters: Math.round(distanceMeters)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,17 @@ function escapeRegex (value) {
|
||||
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
/** Multi-select query params (relationship_goal, personality_traits, lifestyle,
|
||||
* interests, values, children_status) are sent as a single comma-separated
|
||||
* string, e.g. `?personality_traits=kind,loyal` — this is the one convention
|
||||
* the RN/web clients must use for every array-type dating-profile filter.
|
||||
* Empty/blank items are dropped; returns [] for an empty/absent param. */
|
||||
function parseCsvParam (value) {
|
||||
if (value === undefined || value === null || value === '') return []
|
||||
const raw = Array.isArray(value) ? value.join(',') : String(value)
|
||||
return raw.split(',').map((v) => v.trim()).filter((v) => v.length > 0)
|
||||
}
|
||||
|
||||
/** $expr clauses for an age range, computed from `birthday` — mirrors
|
||||
* utils/partnerMatch.js's ageFromBirthday so live search and later alert
|
||||
* matching agree on the same age. */
|
||||
@@ -31,7 +42,10 @@ function ageRangeExprClauses (ageMin, ageMax) {
|
||||
|
||||
/** Turns raw query params into the persisted alert-criteria shape consumed
|
||||
* by utils/partnerMatch.js's matchesCriteria — used both for the immediate
|
||||
* search and when a search is saved as an alert, so both always agree. */
|
||||
* search and when a search is saved as an alert, so both always agree.
|
||||
* Multi-select dating-profile filters (relationship_goal, personality_traits,
|
||||
* lifestyle, interests, values) use a single comma-separated query param,
|
||||
* e.g. `?personality_traits=kind,loyal` — see parseCsvParam. */
|
||||
async function resolveCriteriaFromQuery (query) {
|
||||
const criteria = {}
|
||||
const {
|
||||
@@ -39,7 +53,10 @@ async function resolveCriteriaFromQuery (query) {
|
||||
heightMin, heightMax, weightMin, weightMax, sizeMin, sizeMax,
|
||||
hair_color, eye_color,
|
||||
marital_status, residence_type, vehicle_type, personal_style,
|
||||
income_range, job, country, province, city
|
||||
income_range, job, country, province, city,
|
||||
relationship_goal, seeking_gender, personality_traits,
|
||||
lifestyle, interests, values, relationship_view, geo_preference,
|
||||
children_status, smoking_status
|
||||
} = query || {}
|
||||
|
||||
if (gender) criteria.gender = gender
|
||||
@@ -60,6 +77,26 @@ async function resolveCriteriaFromQuery (query) {
|
||||
if (income_range) criteria.income_range = income_range
|
||||
if (job) criteria.job = String(job).trim()
|
||||
|
||||
// desired_age_range is a stored preference field only (echoed back via
|
||||
// PARTNER_RESULT_FIELDS) — it is NOT translated into a search filter here;
|
||||
// clients bucket it into ageMin/ageMax themselves before calling search.
|
||||
const relationshipGoalList = parseCsvParam(relationship_goal)
|
||||
if (relationshipGoalList.length) criteria.relationship_goal = relationshipGoalList
|
||||
if (seeking_gender) criteria.seeking_gender = seeking_gender
|
||||
const personalityTraitsList = parseCsvParam(personality_traits)
|
||||
if (personalityTraitsList.length) criteria.personality_traits = personalityTraitsList
|
||||
const lifestyleList = parseCsvParam(lifestyle)
|
||||
if (lifestyleList.length) criteria.lifestyle = lifestyleList
|
||||
const interestsList = parseCsvParam(interests)
|
||||
if (interestsList.length) criteria.interests = interestsList
|
||||
const valuesList = parseCsvParam(values)
|
||||
if (valuesList.length) criteria.values = valuesList
|
||||
if (relationship_view) criteria.relationship_view = relationship_view
|
||||
if (geo_preference) criteria.geo_preference = geo_preference
|
||||
const childrenStatusList = parseCsvParam(children_status)
|
||||
if (childrenStatusList.length) criteria.children_status = childrenStatusList
|
||||
if (smoking_status) criteria.smoking_status = smoking_status
|
||||
|
||||
if (country) {
|
||||
const countryFind = await CountryModel.findOne({ id: country }, { _id: 0 })
|
||||
if (countryFind) criteria.country_id = countryFind.id
|
||||
@@ -99,6 +136,22 @@ function criteriaToMongoFilter (criteria) {
|
||||
if (criteria.personal_style) filter.personal_style = criteria.personal_style
|
||||
if (criteria.income_range) filter.income_range = criteria.income_range
|
||||
if (criteria.job) filter.job = { $regex: escapeRegex(criteria.job), $options: 'i' }
|
||||
|
||||
// Multi-select dating-profile fields: candidate matches if their stored
|
||||
// array overlaps ANY of the searcher's selected values ($in semantics).
|
||||
if (criteria.relationship_goal?.length) filter.relationship_goal = { $in: criteria.relationship_goal }
|
||||
if (criteria.personality_traits?.length) filter.personality_traits = { $in: criteria.personality_traits }
|
||||
if (criteria.lifestyle?.length) filter.lifestyle = { $in: criteria.lifestyle }
|
||||
if (criteria.interests?.length) filter.interests = { $in: criteria.interests }
|
||||
if (criteria.values?.length) filter.values = { $in: criteria.values }
|
||||
|
||||
// Single-select dating-profile fields: exact match.
|
||||
if (criteria.seeking_gender) filter.seeking_gender = criteria.seeking_gender
|
||||
if (criteria.relationship_view) filter.relationship_view = criteria.relationship_view
|
||||
if (criteria.geo_preference) filter.geo_preference = criteria.geo_preference
|
||||
if (criteria.children_status?.length) filter.children_status = { $in: criteria.children_status }
|
||||
if (criteria.smoking_status) filter.smoking_status = criteria.smoking_status
|
||||
|
||||
if (criteria.country_id) filter['country.id'] = criteria.country_id
|
||||
if (criteria.province_id) filter['province.id'] = criteria.province_id
|
||||
if (criteria.city_id) filter['city.id'] = criteria.city_id
|
||||
@@ -115,7 +168,9 @@ function criteriaToMongoFilter (criteria) {
|
||||
const PARTNER_RESULT_FIELDS =
|
||||
'_id user_name first_name last_name profile_image is_verified verify_badge is_Register ' +
|
||||
'gender height weight size eye_color hair_color marital_status residence_type residence_area ' +
|
||||
'vehicle_type vehicle_details personal_style job income_range moral_traits birthday country province city blocked_users'
|
||||
'vehicle_type vehicle_details personal_style job income_range birthday country province city blocked_users ' +
|
||||
'relationship_goal seeking_gender desired_age_range personality_traits lifestyle interests values ' +
|
||||
'relationship_view geo_preference children_status smoking_status'
|
||||
|
||||
/** GET /users/partner-search — filters as query params (see resolveCriteriaFromQuery).
|
||||
* Personal-details fields are included in the response ONLY here, by design
|
||||
|
||||
Reference in New Issue
Block a user