Перейти к содержимому

Фоновые задачи и уведомления

Единственная точка входа — NotificationsService.notify(userIds, kind, payload, push?). Она всегда пишет строки Notification (это журнал), и, если передан push, шлёт на все токены адресатов и штампует sentAt.

Журнал пишется даже когда пуш не ушёл — иначе нельзя было бы ни показать ленту, ни дедуплицировать повторы.

Метод и путь Что делает
POST /me/push-tokens регистрирует токен устройства
DELETE /me/push-tokens/{token} снимает токен
GET /me/notifications лента, последние 50

Регистрация токена — upsert по самому токену, а не по паре с пользователем: устройство может сменить владельца, и тогда старая привязка должна уйти.

Виды, которые система шлёт сейчас: tournament.reminder, waitlist.promoted, followee.registered, tournament.results, admin_custom.

pg-boss на том же PostgreSQL, без Redis. Стартует по DI-токену BOSS, в тестах подменён FakeBoss — поэтому в тестах нет ни очереди, ни таймеров.

Джоба Расписание Что делает
tournament-reminders */5 * * * * пуш за 24 ч до старта и пуш в момент дедлайна отмены
season-rollover 5 0 * * * UTC архивирует истёкший сезон, заводит текущий квартал

Окно поиска равно периоду запуска — пять минут.

Дедупликация — по журналу Notification. Ключ складывается из kind, payload.tournamentId и payload.window, поэтому повторный тик, ручной перезапуск джобы или наложение двух воркеров не задваивают напоминание. Гости без аккаунта пропускаются.

Дедлайн отмены берётся у организатора: cancelDeadlineHours, по умолчанию 3.

Второе напоминание привязано к дедлайну отмены, а не к «за три часа до старта». Если клуб поставит дедлайн в 24 часа, оба напоминания придут почти одновременно.

admin_custom шлётся вручную из админки с выбором аудитории: все, участники турнира, город, поимённый список. В ответе — сколько адресатов и скольким реально ушёл пуш; расхождение нормально, у части людей нет активных токенов.

Отправка требует права push.send — оно есть у super_admin, moderator и marketing.