Документация Accelera

Offers

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

Справочник офферов

Создание оффера

Каждый оффер можно создавать/редактировать/удалять в реальном времени через интерфейс менеджера.

Для этого выполните следующие шаги:

  1. Авторизуйтесь в Accelera Loyalty.
  2. Перейдите в раздел Global Dictionaries.
  3. Выберите сущность Offers из списка.
  4. Нажмите на кнопку Add Offer в правом верхнем углу.
  5. В открывшемся окне нажмите Create new.
  6. Заполните поля в форме создания оффера.
  7. После заполнения полей нажмите кнопку Save. Если оффер успешно создан, соответствующая строка появится в общем списке офферов.

Форма создания оффера

После открытия формы создания оффера заполните необходимые поля. Описания всех полей приведены ниже в разделе Поля сущности.

Возможные ошибки при создании оффера

  1. Не заполнены обязательные поля. Убедитесь, что все поля, помеченные как обязательные, заполнены.
  2. Оффер с указанным идентификатором уже существует. Поле 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 при применении правил и вернет подходящий оффер.

История и аналитика

Система автоматически записывает в историю каждый факт выдачи оффера клиенту. Эти данные доступны для построения отчетов и анализа эффективности офферов.

Техническая спецификация по системе офферов

Архитектура

Основные компоненты

  1. Офферы (Offers) - базовые предложения
  2. Каналы (Channels) - способы доставки офферов
  3. Распределение (Distribution) - логика выбора офферов для клиентов при весовом распределении
  4. Персонализация - правила фильтрации на основе профилей клиентов
  5. История (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                 // Дата и время выдачи
}

Кеширование

Уровни кеширования

  1. Офферы: REDIS_CACHE_OFFERS - кеширование списка офферов
  2. Профили: кеш на 60 секунд - platform:cache:lookup:profiles
  3. Распределения: кеш по URL - /v1/internal/offers/distributions/{distribution_id}
  4. Записи: опциональный кеш через заголовки:
    • loyalty-use-cache: true
    • loyalty-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)
  • Ограничения на длину строк
  • Автоматическое создание таблиц распределения

Производительность

Оптимизации

  1. Индексы:
    • global_offers_id_index на global.offers.id
    • offers_channels_id_index на offers.channels.id
    • Составные индексы на таблицах распределения
    • Индексы истории по всем ключевым полям
  2. Запросы:
    • Raw SQL для сложных выборок распределения
    • Ограничение LIMIT 1 для случайного выбора
    • Кеширование частых запросов
  3. Память:
    • Принудительная сборка мусора каждые 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'}`);

Метрики

  • Время выполнения правил
  • Количество прохождений/отклонений правил
  • Статистика кеширования
  • Ошибки при выполнении правил

На этой странице