feat: VIP search now shows only opposite gender - Modified VIP search filtering to always show opposite gender regardless of user's interested_in preference - Male users see only female profiles - Female users see only male profiles - Improved gender filtering logic in vipService.ts
This commit is contained in:
257
src/services/vipService.ts
Normal file
257
src/services/vipService.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { query } from '../database/connection';
|
||||
import { BotError } from '../types/index';
|
||||
|
||||
export interface VipSearchFilters {
|
||||
ageMin?: number;
|
||||
ageMax?: number;
|
||||
city?: string;
|
||||
datingGoal?: string;
|
||||
hobbies?: string[];
|
||||
lifestyle?: string[];
|
||||
distance?: number;
|
||||
hasPhotos?: boolean;
|
||||
isOnline?: boolean;
|
||||
}
|
||||
|
||||
export interface PremiumInfo {
|
||||
isPremium: boolean;
|
||||
expiresAt?: Date;
|
||||
daysLeft?: number;
|
||||
}
|
||||
|
||||
export class VipService {
|
||||
|
||||
// Проверить премиум статус пользователя
|
||||
async checkPremiumStatus(telegramId: string): Promise<PremiumInfo> {
|
||||
try {
|
||||
const result = await query(`
|
||||
SELECT premium, premium_expires_at
|
||||
FROM users
|
||||
WHERE telegram_id = $1
|
||||
`, [telegramId]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
throw new BotError('User not found', 'USER_NOT_FOUND', 404);
|
||||
}
|
||||
|
||||
const user = result.rows[0];
|
||||
const isPremium = user.premium;
|
||||
const expiresAt = user.premium_expires_at ? new Date(user.premium_expires_at) : undefined;
|
||||
|
||||
let daysLeft = undefined;
|
||||
if (isPremium && expiresAt) {
|
||||
const now = new Date();
|
||||
const timeDiff = expiresAt.getTime() - now.getTime();
|
||||
daysLeft = Math.ceil(timeDiff / (1000 * 3600 * 24));
|
||||
|
||||
// Если премиум истек
|
||||
if (daysLeft <= 0) {
|
||||
await this.removePremium(telegramId);
|
||||
return { isPremium: false };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isPremium,
|
||||
expiresAt,
|
||||
daysLeft
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error checking premium status:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Добавить премиум статус
|
||||
async addPremium(telegramId: string, durationDays: number = 30): Promise<void> {
|
||||
try {
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setDate(expiresAt.getDate() + durationDays);
|
||||
|
||||
await query(`
|
||||
UPDATE users
|
||||
SET premium = true, premium_expires_at = $2
|
||||
WHERE telegram_id = $1
|
||||
`, [telegramId, expiresAt]);
|
||||
} catch (error) {
|
||||
console.error('Error adding premium:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Удалить премиум статус
|
||||
async removePremium(telegramId: string): Promise<void> {
|
||||
try {
|
||||
await query(`
|
||||
UPDATE users
|
||||
SET premium = false, premium_expires_at = NULL
|
||||
WHERE telegram_id = $1
|
||||
`, [telegramId]);
|
||||
} catch (error) {
|
||||
console.error('Error removing premium:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// VIP поиск с фильтрами
|
||||
async vipSearch(telegramId: string, filters: VipSearchFilters): Promise<any[]> {
|
||||
try {
|
||||
// Проверяем премиум статус
|
||||
const premiumInfo = await this.checkPremiumStatus(telegramId);
|
||||
if (!premiumInfo.isPremium) {
|
||||
throw new BotError('Premium subscription required', 'PREMIUM_REQUIRED', 403);
|
||||
}
|
||||
|
||||
// Получаем профиль пользователя
|
||||
const userProfile = await query(`
|
||||
SELECT p.*, u.telegram_id
|
||||
FROM profiles p
|
||||
JOIN users u ON p.user_id = u.id
|
||||
WHERE u.telegram_id = $1
|
||||
`, [telegramId]);
|
||||
|
||||
if (userProfile.rows.length === 0) {
|
||||
throw new BotError('Profile not found', 'PROFILE_NOT_FOUND', 404);
|
||||
}
|
||||
|
||||
const currentUser = userProfile.rows[0];
|
||||
|
||||
// Строим запрос с фильтрами
|
||||
let query_text = `
|
||||
SELECT p.*, u.telegram_id,
|
||||
CASE WHEN u.updated_at > NOW() - INTERVAL '15 minutes' THEN true ELSE false END as is_online
|
||||
FROM profiles p
|
||||
JOIN users u ON p.user_id = u.id
|
||||
LEFT JOIN swipes s ON (
|
||||
s.swiper_id = $1 AND s.swiped_id = u.id
|
||||
)
|
||||
WHERE u.telegram_id != $2
|
||||
AND s.id IS NULL
|
||||
AND p.is_active = true
|
||||
`;
|
||||
|
||||
let params = [currentUser.user_id, telegramId];
|
||||
let paramIndex = 3;
|
||||
|
||||
// Фильтр по противоположному полу
|
||||
if (currentUser.gender === 'male') {
|
||||
query_text += ` AND p.gender = 'female'`;
|
||||
} else if (currentUser.gender === 'female') {
|
||||
query_text += ` AND p.gender = 'male'`;
|
||||
} else {
|
||||
// Если пол не определен или 'other', показываем всех кроме того же пола
|
||||
query_text += ` AND p.gender != $${paramIndex}`;
|
||||
params.push(currentUser.gender);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// Фильтр по возрасту
|
||||
if (filters.ageMin) {
|
||||
query_text += ` AND p.age >= $${paramIndex}`;
|
||||
params.push(filters.ageMin);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (filters.ageMax) {
|
||||
query_text += ` AND p.age <= $${paramIndex}`;
|
||||
params.push(filters.ageMax);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// Фильтр по городу
|
||||
if (filters.city) {
|
||||
query_text += ` AND LOWER(p.city) LIKE LOWER($${paramIndex})`;
|
||||
params.push(`%${filters.city}%`);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// Фильтр по цели знакомства
|
||||
if (filters.datingGoal) {
|
||||
query_text += ` AND p.dating_goal = $${paramIndex}`;
|
||||
params.push(filters.datingGoal);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// Фильтр по хобби
|
||||
if (filters.hobbies && filters.hobbies.length > 0) {
|
||||
const hobbyConditions = filters.hobbies.map((_, index) => {
|
||||
return `LOWER(p.hobbies) LIKE LOWER($${paramIndex + index})`;
|
||||
});
|
||||
query_text += ` AND (${hobbyConditions.join(' OR ')})`;
|
||||
filters.hobbies.forEach(hobby => {
|
||||
params.push(`%${hobby}%`);
|
||||
});
|
||||
paramIndex += filters.hobbies.length;
|
||||
}
|
||||
|
||||
// Фильтр по образу жизни
|
||||
if (filters.lifestyle && filters.lifestyle.length > 0) {
|
||||
const lifestyleConditions = filters.lifestyle.map((field) => {
|
||||
const condition = `p.lifestyle ? $${paramIndex}`;
|
||||
params.push(field);
|
||||
paramIndex++;
|
||||
return condition;
|
||||
});
|
||||
query_text += ` AND (${lifestyleConditions.join(' OR ')})`;
|
||||
}
|
||||
|
||||
// Фильтр по наличию фото
|
||||
if (filters.hasPhotos) {
|
||||
query_text += ` AND p.photos IS NOT NULL AND array_length(p.photos, 1) > 0`;
|
||||
}
|
||||
|
||||
// Фильтр по онлайн статусу
|
||||
if (filters.isOnline) {
|
||||
query_text += ` AND u.updated_at > NOW() - INTERVAL '15 minutes'`;
|
||||
}
|
||||
|
||||
query_text += ` ORDER BY
|
||||
CASE WHEN u.updated_at > NOW() - INTERVAL '15 minutes' THEN 0 ELSE 1 END,
|
||||
u.updated_at DESC,
|
||||
p.created_at DESC
|
||||
LIMIT 50`;
|
||||
|
||||
const result = await query(query_text, params);
|
||||
return result.rows;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in VIP search:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Получить информацию о премиум возможностях
|
||||
getPremiumFeatures(): string {
|
||||
return `💎 ПРЕМИУМ ПОДПИСКА 💎
|
||||
|
||||
🔥 Что дает VIP статус:
|
||||
|
||||
🎯 VIP Поиск с фильтрами:
|
||||
• Поиск по возрасту
|
||||
• Поиск по городу
|
||||
• Фильтр по целям знакомства
|
||||
• Поиск по хобби и интересам
|
||||
• Фильтр по образу жизни
|
||||
• Только пользователи с фото
|
||||
• Только онлайн пользователи
|
||||
|
||||
⚡ Дополнительные возможности:
|
||||
• Неограниченные супер-лайки
|
||||
• Просмотр кто лайкнул вас
|
||||
• Возможность отменить свайп
|
||||
• Приоритет в показе другим
|
||||
• Расширенная статистика
|
||||
• Скрытый режим просмотра
|
||||
|
||||
💰 Тарифы:
|
||||
• 1 месяц - 299₽
|
||||
• 3 месяца - 699₽ (экономия 25%)
|
||||
• 6 месяцев - 1199₽ (экономия 33%)
|
||||
• 1 год - 1999₽ (экономия 44%)
|
||||
|
||||
📞 Для покупки обратитесь к администратору:
|
||||
@admin_bot
|
||||
|
||||
✨ Попробуйте VIP уже сегодня!`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user