diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js
index 38d6134..63c83e2 100644
--- a/backend/controllers/userController.js
+++ b/backend/controllers/userController.js
@@ -54,6 +54,15 @@ const updateUserProfile = async (req, res, next) => {
user.preferences.ageRange.min = req.body.preferences.ageRange.min || user.preferences.ageRange.min;
user.preferences.ageRange.max = req.body.preferences.ageRange.max || user.preferences.ageRange.max;
}
+ // Обновление предпочтений по городу
+ if (req.body.preferences.cityPreferences) {
+ if (typeof req.body.preferences.cityPreferences.sameCity === 'boolean') {
+ user.preferences.cityPreferences.sameCity = req.body.preferences.cityPreferences.sameCity;
+ }
+ if (Array.isArray(req.body.preferences.cityPreferences.allowedCities)) {
+ user.preferences.cityPreferences.allowedCities = req.body.preferences.cityPreferences.allowedCities;
+ }
+ }
}
console.log('Данные пользователя перед сохранением:', user);
@@ -99,16 +108,124 @@ const updateUserProfile = async (req, res, next) => {
const getUsersForSwiping = async (req, res, next) => {
try {
const currentUserId = req.user._id;
+
+ // Получаем данные текущего пользователя для фильтрации рекомендаций
+ const currentUser = await User.findById(currentUserId)
+ .select('preferences location dateOfBirth liked passed');
- // TODO: Более сложная логика фильтрации и сортировки
- const users = await User.find({
- _id: { $ne: currentUserId },
- // Можно добавить условие, чтобы у пользователя было хотя бы одно фото
- // 'photos.0': { $exists: true } // Если нужно показывать только тех, у кого есть фото
- })
- .select('name dateOfBirth gender bio photos preferences.ageRange'); // photos все еще нужны для выбора главной
+ if (!currentUser) {
+ const error = new Error('Пользователь не найден.');
+ error.statusCode = 404;
+ return next(error);
+ }
- const usersWithAgeAndPhoto = users.map(user => {
+ // Вычисляем возраст текущего пользователя
+ let currentUserAge = null;
+ if (currentUser.dateOfBirth) {
+ const birthDate = new Date(currentUser.dateOfBirth);
+ const today = new Date();
+ currentUserAge = today.getFullYear() - birthDate.getFullYear();
+ const m = today.getMonth() - birthDate.getMonth();
+ if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
+ currentUserAge--;
+ }
+ }
+
+ // Автоматически устанавливаем возрастные предпочтения ±2 года, если они не заданы
+ let ageRangeMin = 18;
+ let ageRangeMax = 99;
+
+ if (currentUser.preferences?.ageRange?.min && currentUser.preferences?.ageRange?.max) {
+ // Используем пользовательские настройки
+ ageRangeMin = currentUser.preferences.ageRange.min;
+ ageRangeMax = currentUser.preferences.ageRange.max;
+ } else if (currentUserAge) {
+ // Устанавливаем ±2 года от возраста пользователя по умолчанию
+ ageRangeMin = Math.max(18, currentUserAge - 2);
+ ageRangeMax = Math.min(99, currentUserAge + 2);
+ console.log(`[USER_CTRL] Автоматически установлен возрастной диапазон: ${ageRangeMin}-${ageRangeMax} для пользователя ${currentUserAge} лет`);
+ }
+
+ // Строим базовый фильтр
+ let matchFilter = {
+ _id: {
+ $ne: currentUserId,
+ $nin: [...(currentUser.liked || []), ...(currentUser.passed || [])] // Исключаем уже просмотренных
+ },
+ isActive: true // Только активные пользователи
+ };
+
+ // Фильтрация по полу согласно предпочтениям
+ if (currentUser.preferences?.gender && currentUser.preferences.gender !== 'any') {
+ matchFilter.gender = currentUser.preferences.gender;
+ }
+
+ // Улучшенная фильтрация по городу
+ const cityFilter = [];
+
+ // Если включена настройка "только мой город" и город указан
+ if (currentUser.preferences?.cityPreferences?.sameCity !== false && currentUser.location?.city) {
+ cityFilter.push(currentUser.location.city);
+ }
+
+ // Добавляем дополнительные разрешенные города
+ if (currentUser.preferences?.cityPreferences?.allowedCities?.length > 0) {
+ cityFilter.push(...currentUser.preferences.cityPreferences.allowedCities);
+ }
+
+ // Применяем фильтр по городу только если есть что фильтровать
+ if (cityFilter.length > 0) {
+ // Убираем дубликаты
+ const uniqueCities = [...new Set(cityFilter)];
+ matchFilter['location.city'] = { $in: uniqueCities };
+ console.log(`[USER_CTRL] Фильтрация по городам: ${uniqueCities.join(', ')}`);
+ }
+
+ // Получаем пользователей с базовой фильтрацией
+ const users = await User.find(matchFilter)
+ .select('name dateOfBirth gender bio photos location preferences.ageRange preferences.gender');
+
+ // Дополнительная фильтрация по возрасту и взаимным предпочтениям
+ const filteredUsers = users.filter(user => {
+ // Вычисляем возраст пользователя
+ let userAge = null;
+ if (user.dateOfBirth) {
+ const birthDate = new Date(user.dateOfBirth);
+ const today = new Date();
+ userAge = today.getFullYear() - birthDate.getFullYear();
+ const m = today.getMonth() - birthDate.getMonth();
+ if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
+ userAge--;
+ }
+ }
+
+ // Проверяем возрастные предпочтения текущего пользователя
+ if (userAge) {
+ if (userAge < ageRangeMin || userAge > ageRangeMax) {
+ return false;
+ }
+ }
+
+ // Проверяем взаимные предпочтения по полу
+ if (user.preferences?.gender && user.preferences.gender !== 'any' && currentUser.gender) {
+ if (user.preferences.gender !== currentUser.gender) {
+ return false;
+ }
+ }
+
+ // Проверяем взаимные возрастные предпочтения
+ if (currentUserAge && user.preferences?.ageRange) {
+ const { min = 18, max = 99 } = user.preferences.ageRange;
+ if (currentUserAge < min || currentUserAge > max) {
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ // Формируем ответ с дополнительными данными
+ const usersWithAgeAndPhoto = filteredUsers.map(user => {
let age = null;
if (user.dateOfBirth) {
const birthDate = new Date(user.dateOfBirth);
@@ -120,19 +237,12 @@ const getUsersForSwiping = async (req, res, next) => {
}
}
- // --- Логика выбора главной фотографии ---
+ // Логика выбора главной фотографии
let mainPhotoUrl = null;
if (user.photos && user.photos.length > 0) {
- // Ищем фото, помеченное как главное
const profilePic = user.photos.find(photo => photo.isProfilePhoto === true);
- if (profilePic) {
- mainPhotoUrl = profilePic.url;
- } else {
- // Если нет явно помеченного главного фото, берем первое из массива
- mainPhotoUrl = user.photos[0].url;
- }
+ mainPhotoUrl = profilePic ? profilePic.url : user.photos[0].url;
}
- // -----------------------------------------
return {
_id: user._id,
@@ -140,12 +250,49 @@ const getUsersForSwiping = async (req, res, next) => {
age: age,
gender: user.gender,
bio: user.bio,
- mainPhotoUrl: mainPhotoUrl, // <--- Добавляем URL главной фотографии
- photos: user.photos, // Возвращаем все фото для карусели
+ mainPhotoUrl: mainPhotoUrl,
+ photos: user.photos,
+ location: user.location
};
});
+
+ // Улучшенная сортировка пользователей
+ const sortedUsers = usersWithAgeAndPhoto.sort((a, b) => {
+ // 1. Приоритет пользователям из того же города
+ const aInSameCity = a.location?.city === currentUser.location?.city;
+ const bInSameCity = b.location?.city === currentUser.location?.city;
+
+ if (aInSameCity && !bInSameCity) return -1;
+ if (!aInSameCity && bInSameCity) return 1;
+
+ // 2. Если оба из одного города или оба из разных, сортируем по близости возраста
+ if (currentUserAge && a.age && b.age) {
+ const aDiff = Math.abs(a.age - currentUserAge);
+ const bDiff = Math.abs(b.age - currentUserAge);
+ if (aDiff !== bDiff) {
+ return aDiff - bDiff;
+ }
+ }
+
+ // 3. Приоритет пользователям с фотографиями
+ const aHasPhotos = a.photos && a.photos.length > 0;
+ const bHasPhotos = b.photos && b.photos.length > 0;
+
+ if (aHasPhotos && !bHasPhotos) return -1;
+ if (!aHasPhotos && bHasPhotos) return 1;
+
+ // 4. Приоритет пользователям с био
+ const aHasBio = a.bio && a.bio.trim().length > 0;
+ const bHasBio = b.bio && b.bio.trim().length > 0;
+
+ if (aHasBio && !bHasBio) return -1;
+ if (!aHasBio && bHasBio) return 1;
+
+ return 0;
+ });
- res.status(200).json(usersWithAgeAndPhoto);
+ console.log(`[USER_CTRL] Найдено ${sortedUsers.length} пользователей для рекомендаций (возраст: ${ageRangeMin}-${ageRangeMax})`);
+ res.status(200).json(sortedUsers);
} catch (error) {
console.error('Ошибка при получении пользователей для свайпа:', error.message);
diff --git a/backend/models/User.js b/backend/models/User.js
index 21b3ecc..a5f43f3 100644
--- a/backend/models/User.js
+++ b/backend/models/User.js
@@ -54,6 +54,11 @@ const userSchema = new mongoose.Schema(
min: { type: Number, default: 18 },
max: { type: Number, default: 99 },
},
+ // Добавляем предпочтения по городу
+ cityPreferences: {
+ sameCity: { type: Boolean, default: true }, // Показывать только из того же города
+ allowedCities: [{ type: String }] // Дополнительные разрешенные города
+ }
},
isActive: {
type: Boolean,
diff --git a/src/router/index.js b/src/router/index.js
index bcbd0ff..b2b8248 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -50,6 +50,12 @@ const routes = [
component: () => import('../views/UserProfileView.vue'),
meta: { requiresAuth: true },
props: true // Позволяет передавать :userId как пропс в компонент
+ },
+ {
+ path: '/preferences', // Настройки предпочтений поиска
+ name: 'Preferences',
+ component: () => import('../views/PreferencesView.vue'),
+ meta: { requiresAuth: true }
}
// ... здесь будут другие маршруты: /profile, /swipe, /chat/:id и т.д.
];
diff --git a/src/views/PreferencesView.vue b/src/views/PreferencesView.vue
new file mode 100644
index 0000000..8f3560c
--- /dev/null
+++ b/src/views/PreferencesView.vue
@@ -0,0 +1,708 @@
+
+
+ Ваш возраст: {{ currentUserAge }} лет
+ Настройки поиска
+ Пол партнера
+ Возраст
+ Местоположение
+
+ Дополнительные города
+
Здесь вы можете управлять настройками уведомлений.
+ +Настройте предпочтения поиска для получения более релевантных рекомендаций
+