Skip to main content
Полный перечень полей — в глоссарии.

Что делает эта часть модуля

Этот раздел отвечает за «личный кабинет уведомлений» сотрудника: постраничный список полученных уведомлений с поиском и фильтрами, счётчик непрочитанных, а также действия «пометить прочитанным» и «отправить в архив». Всё это работает поверх двух сущностей: самого Уведомления (общего для компании события) и Уведомления пользователя — персональной строки состояния, которая заводится отдельно для каждого получателя и хранит признак прочтения/архивации. Ключевая идея: у одного уведомления нет глобального статуса «прочитано». Вместо этого прочтение и архивация — это персональные факты. Пока у сотрудника нет строки состояния по данному уведомлению, оно для него непрочитано. Как только строка появляется — оно прочитано. Если в строке стоит признак архива — оно в архиве.

Модель данных, задействованная здесь

А персональное состояние получателя описывается так:
Наличие строки Уведомления пользователя означает «прочитано». Признак «В архиве» внутри этой строки — отдельное, более сильное состояние: архив всегда подразумевает, что строка уже существует, то есть уведомление уже считается прочитанным.

Базовый запрос: что вообще видит сотрудник

Все три сценария (список, счётчик, пометка прочтения) стартуют с одной и той же базовой выборки:
  1. Берутся только уведомления текущей компании.
  2. Из выборки исключаются одноразовые уведомления, которые уже забрал кто-то другой. Точное правило исключения: уведомление помечено как одноразовое, поле «кем захвачено» заполнено, и захватил его не текущий сотрудник.
  3. Сортировка — по убыванию поля «Обновлено» (самые свежие сверху).
Из правила 2 следует важное поведение: одноразовое уведомление видно всем адресатам ровно до тех пор, пока его никто не забрал. После захвата оно мгновенно исчезает из списков всех остальных, но остаётся видимым тому, кто его забрал.
Обычные (не одноразовые) уведомления это правило не трогает — они остаются видимыми всем адресатам независимо от того, кто их уже прочитал.

Список уведомлений

Список отдаётся постранично с курсорной пагинацией (курсор строится по полю «Обновлено», по убыванию). Курсорная схема устойчива к вставке новых записей во время листания — в отличие от нумерации страниц, она не «съезжает», когда сверху появляются свежие уведомления. Поверх базового запроса работают:
  • Поиск — по тексту уведомления. Ищется вхождение строки в содержимое сообщения.
  • Фильтры (см. ниже) — по прочтению, архиву и типу связанного объекта.
Каждое уведомление в выдаче содержит идентификатор, текст, автора/источник (отдаётся числом-идентификатором), тип и ID связанного объекта, время создания, дополнительные данные, а также признак одноразовости и информацию о захвате (кем и когда забрано). Поля времени создания, признака одноразовости и данных о захвате доступны только для чтения.

Фильтры списка

Фильтр вычисляет состояние не по самому уведомлению, а по наличию у сотрудника соответствующей строки состояния.
Фильтры «Прочитано» и «В архиве» не взаимоисключающи и опираются на разные условия. «Прочитано = да» требует строку состояния именно без признака архива, а «В архиве = да» — строку с признаком архива. Поэтому уведомление, отправленное в архив, под фильтр «Прочитано = да» не попадёт (его строка помечена как архивная), хотя по смыслу оно тоже прочитано. Чтобы получить «активные прочитанные», используйте «Прочитано = да»; чтобы «непрочитанные и не в архиве» — «Прочитано = нет» вместе с «В архиве = нет».

Счётчик непрочитанных

Счётчик считается как разность двух чисел:
  • количество всех видимых сотруднику уведомлений (та же базовая выборка компании с исключением чужих захваченных одноразовых);
  • минус количество строк состояния этого сотрудника (то есть всё, что он уже так или иначе тронул — прочитал или заархивировал).
Иначе говоря: непрочитанные = всё видимое − всё, по чему уже есть персональная строка состояния.
Счётчик считает все строки состояния сотрудника, включая архивные. Поэтому заархивированное уведомление тоже уменьшает счётчик непрочитанных — архивация неявно снимает уведомление со счётчика, даже если сотрудник не нажимал «прочитать».
Разность вычисляется по количествам, а не по совпадению конкретных записей. В нормальном потоке строки состояния всегда относятся к видимым уведомлениям, поэтому число корректно. Но это именно арифметика двух счётчиков, а не точное множество непрочитанных.

Пометка «прочитано»

Действие принимает либо список идентификаторов уведомлений, либо флаг «прочитать все». Вся операция выполняется в одной транзакции. Порядок работы:
  1. Формируется список уведомлений компании, по которым у сотрудника ещё нет строки состояния (уже прочитанные пропускаются). Если флаг «прочитать все» не выставлен — список дополнительно сужается до переданных идентификаторов.
  2. Из этого списка исключаются одноразовые уведомления, уже захваченные другим сотрудником (то же правило, что и в базовом запросе).
  3. Для каждого оставшегося уведомления:
    • Если это обычное уведомление — просто заводится строка состояния «прочитано».
    • Если это одноразовое и ещё никем не захваченное — выполняется захват «первый выиграл» (см. ниже).
  4. Все новые строки состояния создаются одной пакетной вставкой.

Захват одноразового уведомления «первый выиграл»

Для одноразового уведомления система не полагается на прочитанное ранее значение. Вместо этого выполняется условное обновление: «проставить, кто и когда забрал» — но только при условии, что поле «кем захвачено» всё ещё пустое.
  • Если обновление затронуло запись — значит, текущий сотрудник выиграл гонку. Ему заводится строка состояния, а всем остальным по вебу отправляется команда убрать это уведомление (отзыв из веб-ленты).
  • Если обновление не затронуло ни одной записи — значит, кто-то опередил долю секунды. Тогда для текущего сотрудника это уведомление тихо пропускается: строка состояния не создаётся, ошибка не возвращается.
Условие «поле захвата ещё пустое» прямо в запросе обновления — это и есть защита от гонки. Двум одновременным запросам не удастся оба «выиграть»: базой данных проходит ровно один, второй получит ноль изменённых строк и корректно отвалится.
При «прочитать все» проигравшие гонку за одноразовое уведомление просто не попадут в результат по нему — но остальные уведомления в том же запросе будут отмечены прочитанными. Частичный успех здесь — норма, а не ошибка.

Архивация

Действие принимает либо список идентификаторов, либо флаг «архивировать все». Тоже выполняется в одной транзакции.
  • Без флага «архивировать все»: среди строк состояния сотрудника по переданным уведомлениям берутся те, что ещё не в архиве, и им проставляется признак архива. Важное следствие: архивируются только те уведомления, по которым у сотрудника уже есть строка состояния (то есть уже прочитанные). Если строки нет — архивировать нечего, уведомление останется как есть.
  • С флагом «архивировать все»: сначала для всех уведомлений компании, по которым у сотрудника ещё нет строки состояния, эти строки досоздаются пакетно, а затем все его неархивные строки переводятся в архив. Так «архивировать всё» захватывает даже те уведомления, которые сотрудник до этого не открывал.
«Архивировать все» действует как «прочитать всё и убрать в архив» одновременно: оно доводит непрочитанные уведомления до состояния прочитанных, создавая строки состояния, и только потом архивирует их.
В отличие от пометки прочтения, архивация не выполняет захват одноразовых уведомлений и не рассылает отзыв из веб-ленты. При «архивировать все» строки состояния досоздаются напрямую для всех уведомлений компании — механизм «первый выиграл» здесь не применяется. Захват одноразовых происходит только через действие «прочитать».

Как это используется

Типовой сценарий фронтенда:
  1. Показать бейдж с числом непрочитанных — берётся из счётчика.
  2. Открыть ленту — постраничный список, при необходимости с поиском по тексту и фильтрами по прочтению/архиву/типу объекта.
  3. При просмотре — отметить одно или несколько уведомлений прочитанными (или все сразу).
  4. Убрать лишнее — отправить в архив по списку или целиком.
Все действия требуют аутентификации и всегда работают в контексте текущей компании: сотрудник видит и меняет только уведомления своей компании и только своё персональное состояние по ним.

Особенности поведения

  • Архивация по списку (без «архивировать все») затрагивает только те уведомления, по которым у сотрудника уже есть строка состояния; для непрочитанных уведомлений без строки состояния операция ничего не делает.
  • «Архивировать все» неявно помечает прочитанными все непрочитанные уведомления компании, досоздавая строки состояния напрямую; при этом механизм захвата одноразовых не используется и отзыв из веб-ленты не рассылается.
  • Отзыв одноразового уведомления из веб-ленты у остальных сотрудников происходит только через действие «прочитать» (после успешного захвата), но не при архивации.
  • Список использует курсорную пагинацию и сортировку по убыванию поля «Обновлено», а поиск работает только по тексту уведомления.
  • Базовый запрос скрывает одноразовые уведомления, уже захваченные другим сотрудником, но оставляет их видимыми тому, кто захватил; обычные уведомления остаются видимыми всем адресатам независимо от прочтения другими.

Связанные страницы

Глоссарий: Чтение, архивация и список

Полный перечень полей уведомления и персональной строки состояния, фильтров и параметров действий.

Уведомления (доставка и доступ)

Обзор всего модуля уведомлений: доставка по каналам, доступы и настройки.

Модель и веер доставки

Модели уведомления и персонального состояния, сигнал сохранения и разветвление доставки по каналам с учётом доступов.

Создание и группировка

Фоновые задачи создания уведомлений: одиночные, обновление по объекту и группировка в 30-минутном окне с накоплением.

Каналы доставки

Транспортные задачи и сервис: веб-доставка, push на устройства с деактивацией мёртвых токенов и рассылка в Telegram.

Push-токены устройств

Регистрация и отписка push-токенов мобильных устройств сотрудника с уникальностью по устройству и типом платформы.

Привязка Telegram

Привязка и отвязка аккаунта сотрудника к Telegram-боту по одноразовому коду верификации.

Доступ и настройки

Матрица доступов «источник × тема» на пользователя, перечни источников и тем и массовое редактирование настроек.