Offers
Сущность Offers (Офферы) представляет собой механизм управления персонализированными предложениями для клиентов через различные каналы распределения. Система поддерживает два типа распределения: на основе весов (weights) и на основе правил (rules) с возможностью персонализации через динамические профили (будет добавлена ссылка на раздел Динамических профилей).

Создание оффера
Каждый оффер можно создавать/редактировать/удалять в реальном времени через интерфейс менеджера.
Для этого выполните следующие шаги:
- Авторизуйтесь в Accelera Loyalty.
- Перейдите в раздел Global Dictionaries.
- Выберите сущность Offers из списка.
- Нажмите на кнопку
Add Offerв правом верхнем углу. - В открывшемся окне нажмите
Create new. - Заполните поля в форме создания оффера.
- После заполнения полей нажмите кнопку
Save. Если оффер успешно создан, соответствующая строка появится в общем списке офферов.

После открытия формы создания оффера заполните необходимые поля. Описания всех полей приведены ниже в разделе Поля сущности.
Возможные ошибки при создании оффера
- Не заполнены обязательные поля. Убедитесь, что все поля, помеченные как обязательные, заполнены.
- Оффер с указанным идентификатором уже существует. Поле
idдолжно быть уникальным. Если оффер с таким ID уже существует, выберите другой.
Поля сущности
При создании или редактировании оффера необходимо заполнить следующие поля:
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
**id** | Строка (50) | Да | Уникальный идентификатор оффера. |
**title** | Строка (50) | Да | Название оффера, отображаемое в интерфейсе. |
**description** | Строка (250) | Нет | Подробное описание оффера. |
**partner** | Строка (250) | Нет | Название партнера, от которого исходит оффер. |
**active_from** | Дата | Да | Дата начала действия оффера. |
**active_to** | Дата | Да | Дата окончания действия оффера. |
**is_specific** | Boolean | Нет | Флаг, указывающий, является ли оффер специфичным (привязанным к счетчику). |
**specific_counter** | Строка (50) | Нет* | Название счетчика. Обязательно, если is_specific = true. |
**from** / **to** | Integer | Нет* | Диапазон значений счетчика, для которого действует оффер. Обязательно, если is_specific = true. |
**additional** | JSONB | Нет | Дополнительные параметры оффера в свободной форме. |
**rule** | JSONB | Нет | Правило принятия решений для персонализации. Используется для распределения типа rules. |
Примечания:
- Для работы персонализированного распределения необходимо настроить Каналы (Channels) и Правила (Rules).
- Подробнее о типах распределения читайте ниже в разделе Типы распределения офферов.
- Технические детали работы с API описаны ниже в Технической спецификации по системе офферов.
Особенности и использование
Связь с каналами распределения
Офферы сами по себе не доставляются клиентам. Для этого их необходимо привязать к Каналу (Channel), который определяет способ доставки (например, "Мобильное приложение", "Email-рассылка") и тип логики распределения (weights или rules).
Типы распределения офферов
- Весовое распределение (Weights): Оффер выбирается случайным образом на основе заданных вероятностей (весов). Подходит для массовых, неперсонализированных акций.
- Распределение по правилам (Rules): Оффер выбирается на основе правил, учитывающих данные клиента (профиль, поведение, переданные параметры). Позволяет реализовать сложную персонализацию.
Персонализация
Для тонкой настройки условий показа оффера конкретному клиенту используется объект rule, где задаются условия на основе данных из профиля клиента, его счетчиков или внешних параметров, переданных в запросе.
Подробнее о настройке персонализации с помощью правил см. в разделе Правила (Rules).
Пример запроса персонализированного оффера
GET /v1/internal/offers/clients/user123/channel456?city=Saransk&age=25
Authorization: Basic <credentials>Система учтет параметры city=Saransk&age=25 при применении правил и вернет подходящий оффер.
История и аналитика
Система автоматически записывает в историю каждый факт выдачи оффера клиенту. Эти данные доступны для построения отчетов и анализа эффективности офферов.
Техническая спецификация по системе офферов
Архитектура
Основные компоненты
- Офферы (Offers) - базовые предложения
- Каналы (Channels) - способы доставки офферов
- Распределение (Distribution) - логика выбора офферов для клиентов при весовом распределении
- Персонализация - правила фильтрации на основе профилей клиентов
- История (History) - отслеживание выданных офферов
Схемы базы данных
global.offers- основные офферыoffers.channels- каналы распределенияoffers.{distribution_id}- динамические таблицы распределенияhistory.offers- история выдачи офферов
API Endpoints
Internal API (/v1/internal/offers)
Получение офферов:
GET /- получить все офферы (с кешированием)GET /clients/{client_id}/{channel_id}- получить персонализированный оффер для клиентаGET /records/{environment_id}- получить записи с фильтрацией (для истории)
Модель данных
Оффер (global.offers)
{
id: string(50), // Уникальный ID оффера
title: string(50), // Название оффера
description: string(250), // Описание оффера
partner: string(250), // Партнер оффера
active_from: DATE, // Дата начала действия
active_to: DATE, // Дата окончания действия
is_specific: boolean, // Специфичный ли оффер
specific_counter: string(50), // Название счетчика (для специфичных)
from: integer, // От какого значения (для специфичных)
to: integer, // До какого значения (для специфичных)
additional: JSONB, // Дополнительные параметры
rule: JSONB, // Правило принятия решений
creator: string(250), // Создатель
date: DATE, // Дата создания
time: TIME, // Время создания
datetime: DATETIME, // Дата и время создания
timestamp: BIGINT // Unix timestamp
}Канал (offers.channels)
{
id: string(50), // Уникальный ID канала
title: string(50), // Название канала
description: string(250), // Описание канала
distribution_id: string(50), // ID таблицы распределения
distribution_type: enum, // Тип распределения: "weights" | "rules"
creator: string(250), // Создатель
date: DATE, // Дата создания
time: TIME, // Время создания
datetime: DATETIME, // Дата и время создания
timestamp: BIGINT // Unix timestamp
}Распределение (offers.{distribution_id})
{
distribution_id: string(250), // ID распределения
channel_id: string(50), // ID канала
offer_id: string(50), // ID оффера
weight: integer, // Вес (для типа weights)
from: integer, // От (для типа weights)
to: integer, // До (для типа weights)
message: TEXT, // Сообщение клиенту
content: TEXT, // Контент оффера
url: TEXT, // Ссылка оффера
image: TEXT, // Изображение оффера
creator: string(250), // Создатель
timestamp: BIGINT, // Unix timestamp
date: DATE, // Дата
time: TIME, // Время
datetime: DATETIME // Дата и время
}История (history.offers)
{
num: BIGINT, // Автоинкремент ID
distribution_id: string(50), // ID распределения
channel_id: string(50), // ID канала
client_id: string(50), // ID клиента
offer_id: string(50), // ID оффера
probability: integer, // Вероятность/вес
date: DATE, // Дата выдачи
time: TIME, // Время выдачи
datetime: DATETIME // Дата и время выдачи
}Кеширование
Уровни кеширования
- Офферы:
REDIS_CACHE_OFFERS- кеширование списка офферов - Профили: кеш на 60 секунд -
platform:cache:lookup:profiles - Распределения: кеш по URL -
/v1/internal/offers/distributions/{distribution_id} - Записи: опциональный кеш через заголовки:
loyalty-use-cache: trueloyalty-cache-lifetime: {minutes}(по умолчанию 60)
События и аналитика
Отправка событий
При каждой выдаче оффера отправляется событие в систему аналитики:
let api_event = 'offers';
let api_context = {
'api': 'internal',
'method': 'get',
'code': 200,
'path': req.path,
'type': 'clients',
'params': req.query,
'body': {},
'response': {
status: 'Ok',
error: null,
type: "rules|weights",
offer: offerData
}
};
flow.publishLoyaltyEvent(client_id, api_event, api_context, null);Запись в историю
await models.HISTORY.Offers.create({
"distribution_id": distribution_id,
"channel_id": channel_id,
"client_id": client_id,
"offer_id": offer_id,
"probability": probability_or_weight
});Безопасность
Аутентификация
- Management API: сессионная авторизация через cookies
- Internal API: Basic Authentication
- Проверка клиентов в blacklist
Валидация
- Проверка существования каналов
- Валидация дат (active_from <= active_to)
- Ограничения на длину строк
- Автоматическое создание таблиц распределения
Производительность
Оптимизации
- Индексы:
global_offers_id_indexнаglobal.offers.idoffers_channels_id_indexнаoffers.channels.id- Составные индексы на таблицах распределения
- Индексы истории по всем ключевым полям
- Запросы:
- Raw SQL для сложных выборок распределения
- Ограничение LIMIT 1 для случайного выбора
- Кеширование частых запросов
- Память:
- Принудительная сборка мусора каждые 3 секунды
- Опциональные heap dumps для мониторинга
Мониторинг и логирование
Логирование
// Детальное логирование выполнения правил
log.info('Executing decision rule:', JSON.stringify(rule),
'with input:', JSON.stringify(req.query));
// Измерение производительности
let start = now();
result = await Decisions.asyncRun(req.query, rule, filtered_profiles);
let end = now();
log.info(`Got decision in ${(end-start).toFixed(3)} ms,
decision ${isPassed ? 'passed' : 'NOT passed'}`);Метрики
- Время выполнения правил
- Количество прохождений/отклонений правил
- Статистика кеширования
- Ошибки при выполнении правил