> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yume.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

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

> Три фоновые задачи очереди сообщений: создание, обновление-или-создание и сгруппированное уведомление — с определением компании, скользящим окном группировки, кэш-блокировкой, сбросом клейм-раз и прямым веб-репушем.

Полный перечень полей — в [глоссарии](/ru/logic/notifications/creation-grouping-glossary).

## Обзор

Эта страница описывает три фоновые задачи, с которых начинается жизнь любого уведомления в Yume. Все три выполняются в отдельной очереди сообщений и решают одну и ту же задачу — записать уведомление в базу компании, — но по-разному отвечают на вопрос «а не было ли уже такого?»:

| Задача                              | Что делает                                                                        | Дедупликация          | Группировка |
| ----------------------------------- | --------------------------------------------------------------------------------- | --------------------- | ----------- |
| Создать уведомление                 | Всегда создаёт новую запись                                                       | Нет                   | Нет         |
| Обновить или создать уведомление    | Обновляет запись по объекту-источнику либо создаёт новую                          | Да, по объекту        | Нет         |
| Создать сгруппированное уведомление | Объединяет повторяющиеся события об одном объекте в одну запись за временное окно | Да, по объекту и теме | Да          |

<Info>
  Само по себе создание записи об уведомлении не занимается доставкой по каналам. Как только уведомление сохранено, автоматически при сохранении запускается веер доставки, который и рассылает его в веб, мобильный push и Telegram. Эти задачи отвечают только за то, **какая именно запись** появится в базе.
</Info>

## Общие правила для всех трёх задач

### Пустой текст — выход без действий

Первое, что проверяет каждая из задач, — заполнен ли текст уведомления. Если текст пустой, задача завершается молча, ничего не создавая и не обновляя.

### Определение компании

Уведомление всегда принадлежит конкретной компании, и её нужно определить до любой работы с базой. Логика одинакова во всех трёх задачах и работает по приоритету:

1. Если задан **явный ID компании** (передан в задачу параметром), берётся он. Это нужно, когда задачу запускают вне контекста компании — например, из служебного кода, где текущая компания ещё не определена (контекст компании пуст).
2. Иначе берётся компания из текущего подключения — но только если подключение действительно привязано к реальной компании, а не к «пустышке» без определённой компании.
3. Если ни то, ни другое не дало результата, компания считается неопределённой и задача завершается молча.

<Warning>
  Если задачу поставить в очередь без определённой компании и без явного ID компании, уведомление просто не будет создано — без ошибки и без записи в журнал. Для вызовов вне контекста компании всегда передавайте явный ID компании.
</Warning>

## Создать уведомление

Самый простой сценарий. После проверки текста и определения компании задача безусловно создаёт одну новую запись уведомления с переданными полями: тип объекта-источника и его ID, текст, доп. данные, список явных получателей, тему и набор каналов доставки. Никакой проверки на дубликаты и никакой группировки здесь нет — сколько раз задачу вызовут, столько записей и появится.

Эта задача подходит для разовых, самостоятельных событий, у которых не бывает «повторов» (например, единичное системное сообщение).

## Обновить или создать уведомление

Задача обеспечивает **ровно одно** актуальное уведомление на объект-источник. Ключ дедупликации — тройка «компания + тип объекта + ID объекта».

Порядок работы:

1. Ищутся все уведомления компании с таким же типом и ID объекта.
2. **Если такие уже есть** — сначала удаляются все персональные записи состояния прочтения по этим уведомлениям, а затем сам текст, доп. данные и список получателей перезаписываются новыми значениями.
3. **Если их ещё нет** — создаётся новое уведомление со всеми переданными полями, включая тему и каналы доставки.

<Note>
  Тема и каналы доставки применяются **только при создании** нового уведомления. При обновлении уже существующего они не меняются — перезаписываются лишь текст, доп. данные и список получателей.
</Note>

### Почему удаляется состояние прочтения

Удаление персональных записей прочтения при обновлении — намеренный приём. Смысл уведомления изменился (обновился текст), поэтому оно снова должно выглядеть непрочитанным для всех получателей. После удаления этих записей уведомление для каждого адресата снова становится «новым».

<Warning>
  Обновление затрагивает **все** найденные уведомления по объекту разом: и текст, и доп. данные, и получатели перезаписываются у каждой такой записи, а состояние прочтения обнуляется у всех. Если по одному объекту почему-то оказалось несколько записей, они все станут одинаковыми.
</Warning>

## Создать сгруппированное уведомление

Самый сложный сценарий. Он нужен, когда об одном и том же объекте приходит поток однотипных событий, и показывать каждое отдельным уведомлением бессмысленно. Задача сворачивает их в одну запись со счётчиком и накопленным списком последних сообщений — пока события укладываются во временное окно.

### Защита от гонки: кэш-блокировка

Несколько таких событий по одному объекту легко могут прийти почти одновременно и в параллельных обработчиках. Чтобы они не создали два конкурирующих уведомления, задача берёт короткую блокировку в кэше.

* Ключ блокировки составляется из компании, типа объекта, ID объекта и темы — то есть блокировка индивидуальна для конкретной группы.
* Блокировка ставится на **10 секунд**. Если поставить её не удалось (значит, группу прямо сейчас обрабатывает другой экземпляр), задача уходит в повтор через **1 секунду**.
* Повторов допускается **до 10**. Вся основная работа выполняется под блокировкой, и в конце — независимо от исхода — блокировка обязательно снимается.

### Поиск существующей группы (временное окно)

Под блокировкой задача вычисляет начало окна: **текущий момент минус окно группировки** (по умолчанию **30 минут**). Затем ищется самое свежее уведомление компании с тем же типом объекта, ID объекта и темой, у которого **отметка обновления не старше** начала окна.

<Info>
  Окно отсчитывается от **времени последнего обновления** уведомления, а не от его создания. Каждое попадание события в группу обновляет эту отметку, поэтому окно фактически скользящее: пока события приходят не реже, чем раз в 30 минут, группа продолжает жить и расти. Как только пауза превысит окно — следующее событие заведёт новую группу.
</Info>

### Ветка «группы ещё нет»

Если подходящей группы не нашлось, создаётся новое уведомление. В его доп. данные складываются:

* **счётчик событий**, равный переданному шагу счётчика (по умолчанию 1);
* **порция сообщений** — не более последних 10 элементов.

Текстом становится базовый переданный текст. На этом задача завершается — прямого веб-репуша здесь нет, доставку берёт на себя веер доставки, запускаемый при создании.

### Ветка «группа найдена» (перегруппировка)

Если группа найдена, она обновляется на месте:

1. **Счётчик** увеличивается на шаг счётчика (прежнее значение берётся из доп. данных, при отсутствии считается равным 1).
2. **Текст** формируется одним из двух способов:
   * если задан шаблон заголовка группы — текст собирается как «шаблон (счётчик)», и, если в порции есть последнее сообщение, к нему через двоеточие добавляется текст этого последнего сообщения. Например: «Новые заявки (5): заявка №123»;
   * если шаблон не задан — используется базовый переданный текст как есть.
3. **Доп. данные** пересобираются: старые данные, поверх — новые, затем обновлённый счётчик и список сообщений. Список сообщений — это прежние накопленные сообщения плюс новая порция, обрезанные до **последних 10**.
4. **Получатели** заменяются новыми, только если новый список непустой; пустой список получателей прежних адресатов не затирает.
5. **Одноразовый захват**: если событие помечено как одноразовое, у уведомления сбрасываются отметки «кем захвачено» и «время захвата», а сам флаг ставится заново.
6. Удаляются все персональные записи состояния прочтения по этой группе.
7. Уведомление сохраняется.

<Warning>
  Сброс одноразового захвата при перегруппировке означает: если сотрудник уже «забрал» такое уведомление, а затем в группу попало новое событие, уведомление снова становится незахваченным — его снова сможет забрать один из адресатов. Это осознанное поведение для «клейм-раз» уведомлений.
</Warning>

<Note>
  Как и при обновлении по объекту, перегруппировка удаляет состояние прочтения по группе — обновлённое уведомление снова показывается непрочитанным всем получателям.
</Note>

### Прямой веб-репуш после перегруппировки

После сохранения обновлённой группы (уже вне блокировки) задача дополнительно и напрямую отправляет уведомление в веб. Это нужно потому, что автоматический веер доставки полноценно срабатывает при **создании**, а здесь произошло **обновление** существующей записи.

Условия и защита от дублей:

* Репуш выполняется, только если среди каналов доставки уведомления есть **веб**.
* Он защищён отдельным ключом кэша на **30 секунд** для конкретного уведомления: если за последние 30 секунд по этой группе веб-репуш уже был, повторный не отправляется. Это гасит всплеск быстрых перегруппировок.
* Список веб-получателей определяется по матрице доступа: берутся сотрудники компании с включённой подпиской на эту тему по веб-каналу. Если у уведомления задан явный список получателей, отбор дополнительно сужается только до них.

<Info>
  Проверка каналов при репуше опирается на каналы доставки **самого уведомления**, зафиксированные при его создании. Набор каналов, переданный в конкретный вызов задачи при перегруппировке, на уже существующую группу не влияет.
</Info>

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

* **Разовые, уникальные события** ставят «Создать уведомление» — каждый вызов даёт отдельную запись.
* **События, у которых важно только последнее актуальное состояние объекта** (например, обновляемый статус) ставят «Обновить или создать уведомление» — на объект остаётся одна запись, и она обновляется на месте.
* **Потоки однотипных событий по одному объекту** (частые повторяющиеся сигналы) ставят «Создать сгруппированное уведомление» — вместо десятков записей получатель видит одну со счётчиком и списком последних сообщений.

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

<Info>
  Прочие особенности поведения:

  * Все три задачи при пустом тексте уведомления завершаются молча, ничего не создавая и не логируя.
  * Удаление состояния прочтения при обновлении и при перегруппировке сделано намеренно — обновлённое уведомление снова становится непрочитанным для всех получателей.
  * В сгруппированном уведомлении хранятся только последние 10 сообщений — более старые вытесняются при накоплении.
  * Новые получатели заменяют прежних только если список непустой; пустой список получателей не затирает уже накопленных адресатов группы.
  * Прямой веб-репуш происходит только в ветке перегруппировки существующей группы; при создании новой группы задача выходит раньше и полагается на автоматический веер доставки.
  * Проверка наличия веб-канала при прямом репуше опирается на каналы доставки самого сохранённого уведомления (зафиксированные при его создании), а не на набор каналов, переданный в текущий вызов при перегруппировке.
  * Прямой веб-репуш защищён ключом кэша на 30 секунд на уведомление: при частых перегруппировках повторные веб-отправки в этом окне подавляются.
  * Кэш-блокировка группы ставится на 10 секунд; при конфликте задача повторяется через 1 секунду, но не более 10 раз, после чего завершится ошибкой.
  * Ключ группировки и блокировки включает тему, поэтому события об одном объекте, но с разными темами, образуют разные группы.
  * Список веб-получателей при прямом репуше берётся из матрицы доступа (включённая подписка на тему по веб-каналу) и, если у уведомления заданы явные получатели, дополнительно сужается только до них.
</Info>

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

<CardGroup cols={2}>
  <Card title="Глоссарий: Создание и группировка" icon="book" href="/ru/logic/notifications/creation-grouping-glossary">
    Полный перечень полей и терминов трёх задач создания и группировки уведомлений.
  </Card>

  <Card title="Уведомления (доставка и доступ)" icon="bell" href="/ru/logic/notifications">
    Обзорная страница модуля уведомлений и всех его подразделов.
  </Card>

  <Card title="Модель и веер доставки" icon="sitemap" href="/ru/logic/notifications/model-dispatch">
    Модели уведомления и его персонального состояния, сигнал сохранения и разветвление доставки по каналам с учётом доступов пользователей.
  </Card>

  <Card title="Каналы доставки" icon="tower-broadcast" href="/ru/logic/notifications/delivery-channels">
    Транспортные задачи и сервис доставки: веб через Ably, мобильный push через Expo с отключением мёртвых токенов и рассылка в Telegram.
  </Card>

  <Card title="Чтение, архивация и список" icon="envelope-open" href="/ru/logic/notifications/read-archive">
    Адреса списка и счётчика непрочитанных, отметка прочтения и архивация, включая логику «первый забрал» и фильтры.
  </Card>

  <Card title="Push-токены устройств" icon="mobile-screen" href="/ru/logic/notifications/push-tokens">
    Регистрация и отписка push-токенов мобильных устройств пользователя с уникальностью по устройству и типом платформы.
  </Card>

  <Card title="Привязка Telegram" icon="paper-plane" href="/ru/logic/notifications/telegram-link">
    Привязка и отвязка аккаунта сотрудника к Telegram-боту по одноразовому коду верификации.
  </Card>

  <Card title="Доступ и настройки" icon="sliders" href="/ru/logic/notifications/access-settings">
    Матрица доступа «источник × тема» на пользователя, перечни источников и тем и массовое редактирование настроек уведомлений.
  </Card>
</CardGroup>
