Перейти к содержанию

Мониторинг

Модуль лояльности ведёт единый журнал бонусных операций и предоставляет операторам набор сводных представлений поверх него: агрегированные метрики начислений и списаний, сигналы об аномалиях, перечень баллов с приближающимся сроком сгорания, аудит ручных начислений и оповещения по техническим и бизнес-правилам. Управление операционным (бизнес-) днём — открытие, закрытие и дневной отчёт — вынесено в отдельный раздел этой же области.

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

Все эндпоинты мониторинга и отчётности доступны только для чтения (за исключением закрытия бизнес-дня) и защищены ролевой моделью OAuth2/Keycloak.

Денежные величины

Суммы во всех ответах передаются в минорных единицах баллов (поля с суффиксом Minor, целочисленные). Это исключает ошибки округления при агрегации.

Экран «Мониторинг»

Заголовок экрана — «Мониторинг бонусов», подзаголовок — «Начисления, списания, аномалии, сгорание — за период».

Период. Переключатель ширины окна — «7 дней», «30 дней», «90 дней» (по умолчанию 30) — и рядом выбор диапазона дат с полями «Начало» и «Конец». Любую из границ можно оставить пустой: без дат выдача строится за последние N дней, с одной датой вторая граница достраивается по правилам произвольного периода, с обеими — ширина не участвует. Даты берутся целыми сутками в часовом поясе программы. Очистка диапазона возвращает выдачу к выбранной ширине. Под подзаголовком выводится подпись «Показано за …» с фактическим окном, которое вернул сервер.

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

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

Роли и доступ

Роль Назначение
LOYALTY_MONITORING_READ Чтение сводок, аномалий, истекающих баллов, аудита ручных начислений, журнала аудита (кто/что/когда, обращения к персональным данным); раздел «Отчёты»; чтение состояния и отчёта по бизнес-дню.
LOYALTY_BUSINESS_DAY_WRITE Закрытие операционного дня.

Действует принцип deny-by-default: при отсутствии требуемой роли запрос отклоняется.

Журнал бонусных операций

Все представления мониторинга строятся поверх единого журнала бонусных операций (начисления, списания, переводы, обмены, сгорания и т. п.). У записи журнала есть тип движения — ACCRUAL (начисление), REDEMPTION (списание), REVERSAL (сторно начисления), CORRECTION (корректировка) — и источник. Сгорание записывается движением REDEMPTION с источником EXPIRY (плановое, по сроку) или MANUAL_EXPIRY (запущенное оператором) — поэтому сводки отделяют его от трат клиента по источнику. Ручное сгорание, записанное до появления источника MANUAL_EXPIRY, осталось в журнале с источником MANUAL и от ручного списания не отличается. Каждая операция журнала несёт операционный день, к которому она относится (см. раздел «Операционный (бизнес-) день» ниже).

Период выдачи

Все четыре выдачи /v1/monitoring принимают произвольный период: границы from и to (ISO-8601, обе включительно) и ширину окна days. Ширина задаёт размер окна, границы — его положение.

Что задано Журнальные выдачи (summary, anomalies, manual-accruals) Предстоящее сгорание (expiring)
Ничего Последние days дней до текущего момента (по умолчанию 30) Ближайшие days дней от текущего момента (по умолчанию 14)
Только from От from до текущего момента От from на days дней вперёд
Только to days дней, заканчивающиеся в to От текущего момента до to
from и to Ровно [from, to], days не участвует Ровно [from, to], days не участвует

Открытой границы не бывает намеренно: запрос по всей истории операций ничем не ограничен по объёму. Начало позже конца отклоняется 422 invalid_period; так же отклоняется неположительная ширина, если она участвует в расчёте окна.

Сводка по начислениям и списаниям

GET /v1/monitoring/summary?days=30 или GET /v1/monitoring/summary?from=…&to=…

Возвращает агрегированные метрики за период: суммарно начислено, списано клиентами и сгорело (количество и сумма), разбивку по источникам и типам операций, а также дневную динамику.

Параметр Тип По умолчанию Описание
days int 30 Ширина окна в днях — участвует, только если не задан from
from date-time — Начало окна включительно
to date-time — Конец окна включительно
Поле Описание
from, to Фактическое окно выдачи
days Ширина фактического окна в полных сутках — считается по окну, а не повторяет параметр запроса
accruedMinor, accruedCount Сумма и число начислений за период
redeemedMinor, redeemedCount Сумма и число списаний клиентами — без сгорания
expiredMinor, expiredCount Сумма и число сгораний — планового и запущенного оператором
bySource[] Разбивка по источнику и типу операции: source, type, totalMinor, count
dynamics[] Динамика по дням: date, accruedMinor, redeemedMinor, expiredMinor

Итоги считаются из того же разреза по источникам, который уходит в ответ, поэтому сводка всегда сходится со своей разбивкой. Сторно начислений и корректировки не входят ни в одну из трёх величин — они видны только в разрезе bySource[].

{
  "days": 30,
  "from": "2026-08-31T00:00:00+03:00",
  "to": "2026-09-30T00:00:00+03:00",
  "accruedMinor": 1542300,
  "accruedCount": 4821,
  "redeemedMinor": 689100,
  "redeemedCount": 1903,
  "expiredMinor": 120000,
  "expiredCount": 48,
  "bySource": [
    { "source": "PURCHASE", "type": "ACCRUAL", "totalMinor": 1200000, "count": 3900 },
    { "source": "EXPIRY", "type": "REDEMPTION", "totalMinor": 120000, "count": 48 }
  ],
  "dynamics": [
    { "date": "2026-09-24", "accruedMinor": 51000, "redeemedMinor": 22000, "expiredMinor": 4000 }
  ]
}

Выявление аномалий

GET /v1/monitoring/anomalies?days=30

Возвращает операции, помеченные эвристиками контроля как потенциально аномальные: например, круглые суммы, нетипично крупные начисления/списания, операции без привязки к покупке. Каждая запись несёт набор меток (flags), поясняющих сработавшие признаки.

Параметр Тип По умолчанию Описание
days int 30 Ширина окна в днях — участвует, только если не задан from
from, to date-time — Границы окна включительно (см. «Период выдачи»)
Поле Описание
operationId Идентификатор операции в журнале
externalId Внешний идентификатор клиента
type, source Тип и источник операции
amountMinor Сумма операции
reason, operator Основание и оператор (если применимо)
createdAt Время операции
flags[] Сработавшие признаки: NON_POSITIVE_AMOUNT, ROUND_AMOUNT, LARGE_AMOUNT, ACCRUAL_WITHOUT_PURCHASE

Назначение

Список аномалий — инструмент сигнализации для последующего ручного разбора, а не автоматической блокировки. Решение по каждой записи принимает оператор.

Баллы с приближающимся сгоранием

GET /v1/monitoring/expiring?days=14

Возвращает лоты баллов, срок действия которых истекает в пределах указанного окна.

Параметр Тип По умолчанию Описание
days int 14 Окно до сгорания в днях — участвует, только если не задан to
from, to date-time — Границы окна сгорания включительно (см. «Период выдачи»)

Поля ответа: accountNumber, externalId, lotId, currentBalanceMinor, expiresAt.

Начало окна в прошлом допустимо: лот с наступившим сроком остаётся открытым до ближайшего прогона сгорания, и вопрос «что должно было сгореть, но ещё не сгорело» законен.

Аудит ручных начислений

GET /v1/monitoring/manual-accruals?days=30

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

Поля ответа: operationId, externalId, amountMinor, reason, operator, createdAt.

Настоящий total в списках

BREAKING (26.3.0). Списки аномалий, истекающих баллов и ручных начислений раньше отдавались голым массивом, обрезанным потолком в 500 строк, — клиент не мог отличить обрезанную выдачу от полной, потому что общее число не передавалось. Экран вводил в заблуждение ровно тогда, когда данных много, — то есть когда на него и смотрят.

Все три ручки отдают конверт {items, page, size, total}, где total — настоящее число строк за период, а size — потолок выдачи (500). Если строк больше, чем показано, под таблицей на экране мониторинга выводится подпись «Показаны первые N из M — уточните период, чтобы увидеть остальные.»

Аномалии считаются по всему периоду, а не по первым 500 строкам

Раньше выдача сначала отбирала последние 500 операций и уже среди них искала аномалии — при потоке операций больше пятисот за период экран показывал считанные аномалии не потому, что их мало, а потому что выборка до них не доходила. Признак аномалии теперь один предикат в базе, а ограничение по числу строк применяется уже после фильтра.

Оповещения по техническим и бизнес-правилам

Модуль отдаёт метрики Prometheus и (опционально) правила оповещений PrometheusRule, чтобы о проблеме узнавали не только те, кто догадался открыть экран мониторинга.

Бизнес-метрики:

Метрика Значение
loyalty.monitoring.anomalies Число операций с признаками аномалии за сутки.
loyalty.monitoring.manual.operations, loyalty.monitoring.manual.minor Число и сумма ручных операций за сутки — включая сгорание, запущенное оператором.
loyalty.monitoring.expiring.minor Объём баллов к сгоранию на ближайшую неделю (информационный показатель, оповещения по нему нет).

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

Правила оповещений (выключены по умолчанию, включаются на уровне развёртывания; пороги настраиваются под контур):

Правило Условие Важность
Сервис недоступен Цель пропала из сбора метрик дольше 5 минут. critical
Очередь приёма не разбирается Возраст самого старого необработанного сообщения приёма превышает порог дольше 5 минут. warning
Команды приёма отложены в разбор Число команд в статусе «В разборе» превышает порог дольше 15 минут. warning
Сервис отвечает ошибками Доля ответов 5xx выше порога 10 минут подряд. critical
Много операций с признаками аномалии Число аномалий за сутки выше порога дольше 30 минут. warning
Всплеск ручных операций с баллами Число ручных операций за сутки выше порога дольше 30 минут. warning
Уведомления клиентам не уходят Самое старое созревшее сообщение очереди исходящих уведомлений ждёт отправки дольше порога 10 минут подряд. warning
Прогон сгорания баллов давно не отрабатывал На контуре задано расписание сгорания, а последний успешный прогон был дольше порога назад (15 минут подряд). warning

Оповещения о предстоящем сгорании больше нет

Прежнее правило «Крупный объём баллов сгорает в ближайшую неделю» срабатывало на большой пачке предстоящих сгораний и молчало, когда прогон сгорания не запускался неделями. Его заменило правило «Прогон сгорания баллов давно не отрабатывал» — по факту работы прогона. Объём предстоящего сгорания остался информационным показателем.

Подробности о развёртывании и метриках — в руководстве администратора, «Наблюдаемость».

Операционный (бизнес-) день

Операции лояльности привязаны к операционному дню. Раздел позволяет посмотреть текущий день, закрыть его с открытием следующего и получить дневной отчёт.

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

Статусы дня (status) отражаются в полях представления DayView: businessDate, status, openedAt, closedAt.

Текущий и недавние дни

GET /v1/business-day

Возвращает текущий операционный день и список недавних дней (за последние 14 дней).

{
  "current": {
    "businessDate": "2026-06-25",
    "status": "OPEN",
    "openedAt": "2026-06-25T00:00:00Z",
    "closedAt": null
  },
  "recent": [
    {
      "businessDate": "2026-06-24",
      "status": "CLOSED",
      "openedAt": "2026-06-24T00:00:00Z",
      "closedAt": "2026-06-25T00:01:12Z"
    }
  ]
}

Закрытие дня

POST /v1/business-day/close

Закрывает текущий операционный день и открывает следующий. Требует роль LOYALTY_BUSINESS_DAY_WRITE.

stateDiagram-v2
    [*] --> OPEN: открытие
    OPEN --> CLOSED: close
    CLOSED --> [*]

Переоткрытие дня не предусмотрено

В любой момент времени открыт ровно один операционный день — это ограничение целостности учёта. Закрытый день переоткрыть нельзя; корректировки вносятся текущим (открытым) днём.

Дневной отчёт

GET /v1/business-day/{date}/summary

Возвращает агрегированный отчёт по операциям за конкретную дату: суммарно начислено, списано и общее число операций.

Поле Описание
businessDate Дата операционного дня
accruedMinor Сумма начислений за день
redeemedMinor Сумма списаний за день
operations Общее число операций за день

«Списано» в дневном отчёте считается иначе, чем в сводке

Дневной отчёт относит к списаниям все движения типа REDEMPTION — включая сгорание — и сравнивается с итогами внешней системы при сверке. Сводка мониторинга и раздел «Отчёты» показывают сгорание отдельно от трат клиента. Сторно начислений (REVERSAL) и корректировки (CORRECTION) ни в «начислено», ни в «списано» дневного отчёта не входят, но учитываются в общем числе операций.

Сводка эндпоинтов

Метод и путь Назначение Роль
GET /v1/monitoring/summary Сводка по начислениям/списаниям и динамика LOYALTY_MONITORING_READ
GET /v1/monitoring/anomalies Аномалии операций (конверт с total) LOYALTY_MONITORING_READ
GET /v1/monitoring/expiring Баллы с приближающимся сгоранием (конверт с total) LOYALTY_MONITORING_READ
GET /v1/monitoring/manual-accruals Аудит ручных начислений (конверт с total) LOYALTY_MONITORING_READ
GET /v1/business-day Текущий и недавние операционные дни LOYALTY_MONITORING_READ
POST /v1/business-day/close Закрыть текущий и открыть следующий день LOYALTY_BUSINESS_DAY_WRITE
GET /v1/business-day/{date}/summary Дневной отчёт по операциям LOYALTY_MONITORING_READ

Эндпоинты отчётов за период (/v1/reports/**) описаны на странице «Отчёты».

Раздел «Администрирование» дополняет мониторинг двумя экранами разбора: «Очередь приёма» — застрявшие асинхронные команды, и «Журнал аудита» — кто, что и когда делал, включая обращения к персональным данным.

В дорожной карте

Выгрузка событий мониторинга во внешний поток (outbox → Kafka → SDE/RT-Flow) и витрины DWH на стороне аналитики в текущей версии модуля не реализованы и запланированы к развитию.