const { Worker } = require("bullmq") const Redis = require("ioredis") const { getRedisConnectionOptions } = require("../services/redis/connection") const HOT_DEAL_TTL_SECONDS = 12 * 60 * 60 const HOT_DEAL_LIMIT = 1000 const HOT_DEAL_WINDOW_DAYS = 3 const HOT_DAY_WINDOW_DAYS = 1 const HOT_WEEK_WINDOW_DAYS = 7 const HOT_MONTH_WINDOW_DAYS = 30 function createRedisClient() { return new Redis(getRedisConnectionOptions()) } function parseSearchResults(results = []) { const ids = []; // i=1'den başlıyoruz (results[0] toplam sayıdır), ikişer ikişer atlıyoruz for (let i = 1; i < results.length; i += 2) { const key = results[i]; // Örn: "deals:cache:20" const value = results[i + 1]; // Örn: ["$", "[{\"id\":20,...}]"] try { // Dialect 3 formatında JSON her zaman bir array string'i olarak gelir: [0] = "$", [1] = "[{...}]" // JSON.parse(value[1])[0] diyerek direkt objeye ulaşıyoruz. const [deal] = JSON.parse(value[1]); ids.push(Number(deal.id)); } catch { // Eğer JSON'da bir sorun olursa, ID'yi key'den (deals:cache:20) güvenli bir şekilde çek const idFromKey = key.split(":")[2]; if (idFromKey) ids.push(Number(idFromKey)); } } return ids; } async function buildHotDealListForRange({ windowDays, listKey, latestKey }) { const redis = createRedisClient() try { const now = Date.now() const windowMs = Math.floor(Number(windowDays) * 24 * 60 * 60 * 1000) const cutoffMs = now - windowMs console.log(`[hot-list] now=${new Date(now).toISOString()} cutoff=${new Date(cutoffMs).toISOString()}`) /** * SORGUNUN ANALİZİ: * 1. @status:{ACTIVE} -> Veritabanında 'ACTIVE' (BÜYÜK HARF) olduğundan emin ol. * 2. @createdAtTs:[${cutoffMs} +inf] -> Sayısal aralık. */ const query = `@status:{ACTIVE} @createdAtTs:[${cutoffMs} +inf]` console.log(`🔍 Redis Query: FT.SEARCH idx:deals "${query}" SORTBY score DESC DIALECT 3`) const results = await redis.call( "FT.SEARCH", "idx:deals", query, "SORTBY", "score", "DESC", "LIMIT", "0", String(HOT_DEAL_LIMIT), "DIALECT", "3", "RETURN", "1", "$" ) // Redis kaç tane döküman buldu? const totalFound = results[0] || 0 console.log(`📊 Redis Toplam Bulunan: ${totalFound}`) const dealIds = parseSearchResults(results) const runId = String(now) const payload = { id: runId, createdAt: new Date(now).toISOString(), total: dealIds.length, dealIds, } const key = `${listKey}:${runId}` await redis.call("JSON.SET", key, "$", JSON.stringify(payload)) await redis.expire(key, HOT_DEAL_TTL_SECONDS) await redis.set(latestKey, runId, "EX", HOT_DEAL_TTL_SECONDS) return { id: runId, total: dealIds.length } } catch (error) { console.error("❌ buildHotDealList Hatası:", error.message) throw error } finally { redis.disconnect() } } async function handler() { const results = {} results.hot = await buildHotDealListForRange({ windowDays: HOT_DEAL_WINDOW_DAYS, listKey: "deals:lists:hot", latestKey: "deals:lists:hot:latest", }) results.hotDay = await buildHotDealListForRange({ windowDays: HOT_DAY_WINDOW_DAYS, listKey: "deals:lists:hot_day", latestKey: "deals:lists:hot_day:latest", }) results.hotWeek = await buildHotDealListForRange({ windowDays: HOT_WEEK_WINDOW_DAYS, listKey: "deals:lists:hot_week", latestKey: "deals:lists:hot_week:latest", }) results.hotMonth = await buildHotDealListForRange({ windowDays: HOT_MONTH_WINDOW_DAYS, listKey: "deals:lists:hot_month", latestKey: "deals:lists:hot_month:latest", }) return results } function startHotDealListWorker() { const worker = new Worker("hotdeal-list", handler, { connection: getRedisConnectionOptions(), concurrency: 1, }) worker.on("completed", (job) => { console.log( `✅ Hot lists done. hot=${job.returnvalue?.hot?.total ?? 0} day=${job.returnvalue?.hotDay?.total ?? 0} week=${job.returnvalue?.hotWeek?.total ?? 0} month=${job.returnvalue?.hotMonth?.total ?? 0}` ) }) worker.on("failed", (job, err) => { console.error(`❌ Hot Deal Worker Hatası!`, err.message) }) return worker } module.exports = { startHotDealListWorker }