Что делает эта часть модуля
Этот раздел отвечает за «личный кабинет уведомлений» сотрудника: постраничный список полученных уведомлений с поиском и фильтрами, счётчик непрочитанных, а также действия «пометить прочитанным» и «отправить в архив». Всё это работает поверх двух сущностей: самого Уведомления (общего для компании события) и Уведомления пользователя — персональной строки состояния, которая заводится отдельно для каждого получателя и хранит признак прочтения/архивации. Ключевая идея: у одного уведомления нет глобального статуса «прочитано». Вместо этого прочтение и архивация — это персональные факты. Пока у сотрудника нет строки состояния по данному уведомлению, оно для него непрочитано. Как только строка появляется — оно прочитано. Если в строке стоит признак архива — оно в архиве.Модель данных, задействованная здесь
А персональное состояние получателя описывается так:
Наличие строки Уведомления пользователя означает «прочитано». Признак «В архиве» внутри этой строки — отдельное, более сильное состояние: архив всегда подразумевает, что строка уже существует, то есть уведомление уже считается прочитанным.
Базовый запрос: что вообще видит сотрудник
Все три сценария (список, счётчик, пометка прочтения) стартуют с одной и той же базовой выборки:- Берутся только уведомления текущей компании.
- Из выборки исключаются одноразовые уведомления, которые уже забрал кто-то другой. Точное правило исключения: уведомление помечено как одноразовое, поле «кем захвачено» заполнено, и захватил его не текущий сотрудник.
- Сортировка — по убыванию поля «Обновлено» (самые свежие сверху).
Обычные (не одноразовые) уведомления это правило не трогает — они остаются видимыми всем адресатам независимо от того, кто их уже прочитал.
Список уведомлений
Список отдаётся постранично с курсорной пагинацией (курсор строится по полю «Обновлено», по убыванию). Курсорная схема устойчива к вставке новых записей во время листания — в отличие от нумерации страниц, она не «съезжает», когда сверху появляются свежие уведомления. Поверх базового запроса работают:- Поиск — по тексту уведомления. Ищется вхождение строки в содержимое сообщения.
- Фильтры (см. ниже) — по прочтению, архиву и типу связанного объекта.
Фильтры списка
Фильтр вычисляет состояние не по самому уведомлению, а по наличию у сотрудника соответствующей строки состояния.Счётчик непрочитанных
Счётчик считается как разность двух чисел:- количество всех видимых сотруднику уведомлений (та же базовая выборка компании с исключением чужих захваченных одноразовых);
- минус количество строк состояния этого сотрудника (то есть всё, что он уже так или иначе тронул — прочитал или заархивировал).
Разность вычисляется по количествам, а не по совпадению конкретных записей. В нормальном потоке строки состояния всегда относятся к видимым уведомлениям, поэтому число корректно. Но это именно арифметика двух счётчиков, а не точное множество непрочитанных.
Пометка «прочитано»
Действие принимает либо список идентификаторов уведомлений, либо флаг «прочитать все». Вся операция выполняется в одной транзакции. Порядок работы:- Формируется список уведомлений компании, по которым у сотрудника ещё нет строки состояния (уже прочитанные пропускаются). Если флаг «прочитать все» не выставлен — список дополнительно сужается до переданных идентификаторов.
- Из этого списка исключаются одноразовые уведомления, уже захваченные другим сотрудником (то же правило, что и в базовом запросе).
- Для каждого оставшегося уведомления:
- Если это обычное уведомление — просто заводится строка состояния «прочитано».
- Если это одноразовое и ещё никем не захваченное — выполняется захват «первый выиграл» (см. ниже).
- Все новые строки состояния создаются одной пакетной вставкой.
Захват одноразового уведомления «первый выиграл»
Для одноразового уведомления система не полагается на прочитанное ранее значение. Вместо этого выполняется условное обновление: «проставить, кто и когда забрал» — но только при условии, что поле «кем захвачено» всё ещё пустое.- Если обновление затронуло запись — значит, текущий сотрудник выиграл гонку. Ему заводится строка состояния, а всем остальным по вебу отправляется команда убрать это уведомление (отзыв из веб-ленты).
- Если обновление не затронуло ни одной записи — значит, кто-то опередил долю секунды. Тогда для текущего сотрудника это уведомление тихо пропускается: строка состояния не создаётся, ошибка не возвращается.
Архивация
Действие принимает либо список идентификаторов, либо флаг «архивировать все». Тоже выполняется в одной транзакции.- Без флага «архивировать все»: среди строк состояния сотрудника по переданным уведомлениям берутся те, что ещё не в архиве, и им проставляется признак архива. Важное следствие: архивируются только те уведомления, по которым у сотрудника уже есть строка состояния (то есть уже прочитанные). Если строки нет — архивировать нечего, уведомление останется как есть.
- С флагом «архивировать все»: сначала для всех уведомлений компании, по которым у сотрудника ещё нет строки состояния, эти строки досоздаются пакетно, а затем все его неархивные строки переводятся в архив. Так «архивировать всё» захватывает даже те уведомления, которые сотрудник до этого не открывал.
«Архивировать все» действует как «прочитать всё и убрать в архив» одновременно: оно доводит непрочитанные уведомления до состояния прочитанных, создавая строки состояния, и только потом архивирует их.
Как это используется
Типовой сценарий фронтенда:- Показать бейдж с числом непрочитанных — берётся из счётчика.
- Открыть ленту — постраничный список, при необходимости с поиском по тексту и фильтрами по прочтению/архиву/типу объекта.
- При просмотре — отметить одно или несколько уведомлений прочитанными (или все сразу).
- Убрать лишнее — отправить в архив по списку или целиком.
Особенности поведения
- Архивация по списку (без «архивировать все») затрагивает только те уведомления, по которым у сотрудника уже есть строка состояния; для непрочитанных уведомлений без строки состояния операция ничего не делает.
- «Архивировать все» неявно помечает прочитанными все непрочитанные уведомления компании, досоздавая строки состояния напрямую; при этом механизм захвата одноразовых не используется и отзыв из веб-ленты не рассылается.
- Отзыв одноразового уведомления из веб-ленты у остальных сотрудников происходит только через действие «прочитать» (после успешного захвата), но не при архивации.
- Список использует курсорную пагинацию и сортировку по убыванию поля «Обновлено», а поиск работает только по тексту уведомления.
- Базовый запрос скрывает одноразовые уведомления, уже захваченные другим сотрудником, но оставляет их видимыми тому, кто захватил; обычные уведомления остаются видимыми всем адресатам независимо от прочтения другими.
Связанные страницы
Глоссарий: Чтение, архивация и список
Полный перечень полей уведомления и персональной строки состояния, фильтров и параметров действий.
Уведомления (доставка и доступ)
Обзор всего модуля уведомлений: доставка по каналам, доступы и настройки.
Модель и веер доставки
Модели уведомления и персонального состояния, сигнал сохранения и разветвление доставки по каналам с учётом доступов.
Создание и группировка
Фоновые задачи создания уведомлений: одиночные, обновление по объекту и группировка в 30-минутном окне с накоплением.
Каналы доставки
Транспортные задачи и сервис: веб-доставка, push на устройства с деактивацией мёртвых токенов и рассылка в Telegram.
Push-токены устройств
Регистрация и отписка push-токенов мобильных устройств сотрудника с уникальностью по устройству и типом платформы.
Привязка Telegram
Привязка и отвязка аккаунта сотрудника к Telegram-боту по одноразовому коду верификации.
Доступ и настройки
Матрица доступов «источник × тема» на пользователя, перечни источников и тем и массовое редактирование настроек.