From 707047a7af941172b7f460349dce22180d3c8b03 Mon Sep 17 00:00:00 2001 From: "Nik (Claude)" Date: Thu, 18 Jun 2026 12:25:09 +0300 Subject: [PATCH] fix: scheduleForArticle picks unique slots, no collisions --- src/services/articleAutoPublish.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/services/articleAutoPublish.js b/src/services/articleAutoPublish.js index 90415a2..f8c0513 100644 --- a/src/services/articleAutoPublish.js +++ b/src/services/articleAutoPublish.js @@ -32,15 +32,27 @@ async function pickScheduleTime(channel) { return now; // публикуем сразу } - // Сегодня — ближайший слот с временем > now - const todayMinutes = now.getHours() * 60 + now.getMinutes(); - const futureToday = slots.find(s => s.slot_hour * 60 + s.slot_minute > todayMinutes); - if (futureToday) { - const t = new Date(now); - t.setHours(futureToday.slot_hour, futureToday.slot_minute, 0, 0); - return t; + // Получаем уже занятые слоты (pending) + const { rows: taken } = await query( + `SELECT scheduled_at FROM scheduled_posts WHERE channel_id=$1 AND status='pending'`, + [channel.id] + ); + const takenTimes = new Set(taken.map(r => new Date(r.scheduled_at).getTime())); + + // Ищем ближайший незанятый слот на ближайшие 7 дней + for (let dayOffset = 0; dayOffset <= 7; dayOffset++) { + const base = new Date(now); + base.setDate(base.getDate() + dayOffset); + for (const s of slots) { + const t = new Date(base); + t.setHours(s.slot_hour, s.slot_minute, 0, 0); + if (t > now && !takenTimes.has(t.getTime())) { + return t; + } + } } - // Все слоты на сегодня прошли — берём первый завтрашний + + // Fallback — завтра первый слот const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setHours(slots[0].slot_hour, slots[0].slot_minute, 0, 0);