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

CM Ocean Loyalty — бонусный движок на двойной бухгалтерии

Версия продукта (CalVer): 26.3.0 (в разработке; текущий публичный релиз — 26.2.0)

CM Ocean Loyalty — продукт платформы CM Ocean (Data Sapience): самостоятельный вертикальный срез (React Module-Federation UI, встроенный в core-ui, поверх Spring Boot backend со своей PostgreSQL, единый auth через Keycloak). Продукт ведёт бонусные счета участников программы лояльности и в ответ на бизнес-события (прежде всего покупки) начисляет и списывает баллы. В отличие от Promo (триггер — предъявленный промокод), Loyalty event-triggered: правило срабатывает на бизнес-событие.

Ядро учёта баллов — двойная бухгалтерия (double-entry ledger): каждое движение баллов — это проводка между счетами, баланс всегда сходится. Деньги и баллы — всегда в минорных единицах (копейках/сотых балла). Вся бизнес-логика — на бэкенде (REST API /v1), UI и внешние системы — лишь потребители одного контракта.

Этот документ — обзорное погружение «без лишних деталей». Точная структура хранения вынесена в отдельный документ — Модель данных. Технический разбор слоёв — в Архитектуре (Руководство администратора).

1. Что это и для кого

Роль Что делает
Маркетолог / администратор ПЛ заводит акции и механики, настраивает конфигурации программы (уровни, акции программы, правила списания), условия акций и карточку клиента, лимиты, справочники, операционную конфигурацию; смотрит отчёты за период
Оператор кассы / контакт-центра смотрит карточку участника, баланс и историю, выполняет ручные операции (начисление/списание с причиной), резерв/списание баллов на кассе
Интегратор (POS / сайт / Promo / Check Scan) шлёт события покупки и начисления через API (hold/commit, приём чеков, integration/promo/accruals, резолюция карты на POS, безопасная проекция клиента для Check Scan), либо асинхронно через очередь приёма команд (опционально — брокер Kafka)
RT Flow / decisioning потребляет конфигурацию правил и справочников для real-time расчёта (Apache Flink / SDE), вызывает начисления (integration/rtflow/accruals) и синхронно проверяет условия аудитории (теги клиента, участие в акциях, сегменты клиента) той же логикой, что условия акции — integration/rtflow/condition-checks
Система кампаний (Batch Flow) заливает состав сегментов клиентов (integration/customer-segments/**); залитый состав — единственный источник сегментов для условий акций
Администратор / ops устанавливает и сопровождает продукт (Helm-чарт, Keycloak, PostgreSQL, мониторинг)

2. Терминология

Термин Значение
Счёт (account) бонусный счёт участника или системный счёт (источник/приёмник/сгорание); привязан к плану счетов и валюте баллов
План счетов (chart_of_accounts) типы счетов с природой сальдо (дебет/кредит)
Проводка (ledger_entry) двойная запись: дебет одного счёта, кредит другого, на сумму в минорных единицах
Лот (account_lot) партия начисленных баллов с собственным сроком жизни (expires_at); списание и сгорание идут по лотам
Начисление / списание / сгорание приход баллов (accrual), расход (redemption), аннулирование по сроку (expiry/breakage)
Hold → commit / cancel двухфазное списание: резерв баллов без движения по леджеру → захват (commit) либо возврат (cancel)
Акция (promotion) правило, авторованное поверх механики, с окном действия, приоритетом, бюджетом и параметрами
Механика (mechanic) шаблон поведения (кэшбэк, множитель, подарок, реферал, начисление за единицу, бандл)
Конфигурация программы (program_config) набор базовых правил программы: уровни, акции программы и уровней, правила списания; действует ровно одна — активная
Уровень / тир (loyalty_tier) ступень программы в составе конфигурации (Серебро/Золото/Платина) с порогом по сумме и/или количеству покупок; начисление уровню задают акции уровня
Бизнес-день (business_day) операционная дата (открытие/закрытие), которой штампуются операции
Справочник / НСИ (reference_entry) операционные словари (группы акций, каналы, причины операций, типы баллов, регионы)
Golden profile мастер-профиль участника во внешнем MDM (Data Ocean); Loyalty обращается к нему через IdentityPort
Минорные единицы все суммы — целые *_minor (копейки/сотые балла); мажорные единицы — только на отображении

3. Назначение и принципы

  • Двойная бухгалтерия как инвариант. Сумма дебетов равна сумме кредитов; баланс счёта — производная от проводок. Леджер append-only: прошлое не пересчитывается, исправление — это сторно/корректировка новой проводкой.
  • Event-triggered. Точка входа — бизнес-событие; сейчас это покупка (чек кассы или событие покупки). Акции и глобальная механика вычисляют эффект, движок фиксирует начисление.
  • Лоты и сроки. Баллы живут партиями (лотами) с индивидуальным сроком; списание и сгорание идут в заданном порядке (FIFO / LIFO / FEFO, настраивается без редеплоя).
  • Идемпотентность денег. Каждая денежная операция защищена ключом идемпотентности и ссылкой проводки на уровне БД — повтор не списывает баллы дважды.
  • Deny-by-default. Каждая операция требует явной роли LOYALTY_<RESOURCE>_<ACTION>; источник истины авторизации — backend, UI лишь прячет недоступные кнопки.
  • API-first. Любой сценарий интерфейса доступен и через REST /v1; контракт зафиксирован OpenAPI 3.1 с CI diff-gate.

4. Ключевые возможности

  • Бонусное ядро (леджер): счета (у клиента — во всех включённых валютах), субсчета (целевые счета внутри валюты со своей очередью списания, базовым субсчётом и областью списания), проводки, лоты со сроками, срок жизни баллов по цепочке (акция → субсчёт → уровень клиента → валюта), балансы (balance / held / available; баллы с наступившим сроком недоступны сразу), плановое и ручное сгорание с предупреждениями клиенту по шагам, hold/commit-списание с сроком резерва по умолчанию, единый справочник денежных и балльных валют с историей курса и парами обмена, идемпотентность, опциональный кеш баланса в Redis.
  • Акции: каталог механик (параметры механики — из справочника), акции с жизненным циклом (включая автопереход «Запланирована → Активна → Завершена» по датам) и окном действия, согласование запуска по принципу «четырёх глаз», тип события и настройки начисляемых баллов, метки акций (справочник со своим экраном), условия из реестра условий («Настройка акции»: товары, место покупки, мерчант, канал, сегменты, сумма чека, клиент, теги, участие в акциях, дополнительные условия на переменные покупки и поля профиля; доступны и интеграциям через condition-checks), ограничения и лимиты (бюджет в деньгах, лимит участий с настраиваемым окном), версионирование и арбитраж (стекинг/приоритет), статистика применения, начисление за натуральный объём (литры/штуки), глобальная механика поверх всех покупок.
  • Конфигурации программы и уровни: несколько конфигураций, одна активная; уровни в составе конфигурации с порогами по сумме и/или количеству покупок, акции программы и уровней, правила списания, активация с историей. Уровень клиента хранится и считается по журналу покупок: повышается при покупке, пересматривается в личную дату клиента; ручное назначение уровня с причиной, история переходов с источником, событие о смене уровня.
  • Клиенты: каталог с серверным поиском/фильтрами/массовыми операциями и импортом из CSV, карточка по версионируемой настройке (атрибуты, таблицы данных из источников), теги клиента (справочник + присвоение), сегменты из системы кампаний, часовой пояс клиента, блокировка с историей смены статуса, слияние (MERGED, merged_into), мягкое удаление, обезличивание, правка/создание карточки, интеграция identity (MDM/без MDM).
  • Ручные операции: начисление, корректировка, перевод уровня, добавление и снятие тегов — одиночно и пакетом (список или файл) с причиной из справочника, асинхронным выполнением и отложенным запуском.
  • Вовлечённость: реферальная программа (реферальный код, связь, дуальное вознаграждение), достижения и бейджи (правило «условие → бейдж»), карта участника в мобильном кошельке (Apple Wallet / Google Кошелёк).
  • Партнёрские предложения (Loyalty × Promo): реестр партнёров и справочник мерчантов (MID/MCC), офферы по четырём осям (доставка PROMOCODE/CALCULATED, акцепт OPT_IN/AUTO, аудитория ALL/SEGMENT, финансирование PARTNER/PROGRAM/SHARED), card-linked кэшбэк по транзакциям у партнёра (резолв мерчанта → отбор → расчёт → лимиты/бюджет), взаиморасчёты и отчёт по счёту партнёра (PRT-<code>).
  • Личный кабинет клиента (B2C): отдельный клиентский контур для показа лояльности конечному клиенту — API /v1/b2c/me/** (клиент из токена; staff-предпросмотр по previewExternalId), JS/TS-SDK и референс web-кабинет (карта, баланс, уровень, вкладки «Акции»/«Партнёры»/ «История»/«Достижения»), ограничение частоты запросов кабинета.
  • Администрирование / НСИ: справочники с ЖЦ и аудитом (перенос между контурами с предпросмотром, защита архивных и используемых записей), собственные справочники городов, торговых точек, мерчантов, каталог товаров и сегменты клиентов, реестр условий акции, настройка карточки клиента, единицы измерения, справочник валют программы с парами обмена, операционная конфигурация (системные счета, валюта баллов, порядок списания, срок резерва, часовой пояс программы, применяемый и сервером) без передеплоя, лимиты списания по разрезам (TIER/CURRENCY/SEGMENT), бизнес-день (без переоткрытия — исправления только встречной операцией), очередь приёма застрявших асинхронных команд.
  • Отчёты и мониторинг: раздел «Отчёты» — сводные величины за период (обзор, баллы, уровни, акции с выгрузкой таблицы в CSV, рефералы); мониторинг операций и аномалий с произвольным периодом и настоящими счётчиками.
  • Безопасность и аудит: гранулярные роли (54 роли LOYALTY_* заведены в Keycloak), сквозной журнал аудита изменений, доменных событий и обращений к персональным данным (просмотр/поиск/выгрузка) с защитой от подделки задним числом (печати) и опциональной передачей в SIEM заказчика, сверка итогов и контроль целостности учёта.
  • Платформа: перенос акций/механик/справочников между контурами (экспорт-импорт с предпросмотром), асинхронный приём команд через очередь и опциональный Kafka-транспорт, возврат по чеку, очередь исходящих (события смены уровня и уведомления клиентам для брокера), обмен данными с внешними системами (ленты изменений, регламентные выгрузки витрин), наблюдаемость (JSON-логи, трассировка, Prometheus, оповещения), состав ПО (SBOM), Helm-поставка.

5. Кейсы использования

  1. Начисление за покупку. POS/Promo шлёт событие покупки → применяются акции и глобальная механика → формируется лот баллов с проводкой и сроком сгорания; контекст пишется в bonus_operation, покупка — в журнал покупок, уровень клиента пересчитывается один раз на чек.
  2. Списание на кассе (hold → commit). Касса резервирует баллы (hold) против доступного баланса → при подтверждении чека commit захватывает резерв проводкой, при отмене — cancel возвращает. Лимиты списания применяются по уровню/валюте/сегменту.
  3. Сгорание по сроку. Заранее клиент получает предупреждения по шагам (например, за 7 дней и за 1 день); после наступления срока баллы сразу недоступны, а регламентное задание аннулирует остаток лота на счёт сгорания (breakage).
  4. Повышение уровня. Накопление клиента за период конфигурации (сумма и/или количество покупок) достигает порога → уровень повышается сразу при покупке, фиксируется переход (customer_tier_transition) и событие о смене уровня для коммуникаций. Понижение — на пересмотре в личную дату клиента. Менеджер может назначить уровень вручную с причиной.
  5. Акция с начислением от Promo. Клиент гасит промокод в Promo → Promo асинхронно вызывает Loyalty POST /v1/integration/promo/accruals (идемпотентно) → баллы зачисляются.
  6. Слияние клиентов. Обнаружены два профиля одного человека → данные источника переносятся на выжившего, источник переводится в MERGED с трассировкой merged_into.
  7. Перенос акций между контурами. Экспорт акции по натуральному коду из одного стенда → идемпотентный импорт в другой.
  8. Возврат покупки. Касса присылает возврат по исходному чеку → баллы, начисленные по чеку, сторнируются пропорционально сумме возврата (не больше доступного баланса), накопление для уровня уменьшается.
  9. Смена правил программы. Маркетолог готовит новую конфигурацию в черновике и активирует её → новые клиенты сразу живут по новым правилам, текущие переходят при первой покупке или списании либо в дату пересмотра.

6. Архитектура — логический вид

Бэкенд построен package-by-feature: каждый домен владеет своими таблицами, сервисами и контроллером. Кросс-срезы (security, error, observability, idempotency, integration) общие.

Домен Назначение
ledger / account план счетов, счета, проводки, лоты, балансы — система записи баллов
redemption двухфазное списание (hold/commit/cancel), лимиты, порядок списания
promotion / mechanic каталог механик, акции, версионирование/арбитраж, параметры, статистика
programconfig / tier конфигурации программы, уровни, накопление по журналу покупок, пересмотр, ручное назначение, переходы, событие смены уровня
criteria / approval реестр условий акции, согласование запуска акции
customer / identity / profileschema директория, статус, слияние, IdentityPort к MDM, настройка карточки клиента
segment сегменты клиентов из системы кампаний
dictionary / geography / merchant / productcatalog справочники (НСИ) с ЖЦ и аудитом, города и торговые точки, мерчанты, каталог товаров, единицы измерения, типы/валюты баллов
reporting отчёты за период
notification очередь исходящих уведомлений клиентам
config операционная конфигурация, бизнес-день, лимиты, глобальная механика
audit / monitoring сквозной аудит, журнал бонусных операций, аномалии
integration приём чеков и возвратов POS, начисления от Promo и RT Flow, шина событий
[Касса/Сайт/Моб.прил.] ─ события покупки, hold/commit ─┐
[CM Ocean Promo] ─ accruals (ccreds, idem) ────────────┤
                                                        ▼
[Администратор ПЛ] → core-ui → loyalty-ui ─(JWT)─▶ loyalty-api (/v1)
                                                   │    │     │
                          ┌────────────────────────┘    │     └──▶ MDM Data Ocean (IdentityPort: Get/Find; write по флагу)
                          ▼                              ▼
                   PostgreSQL (леджер,          Keycloak (OIDC, роли LOYALTY_*)
                   акции, тиры, НСИ)                     
                          │
                          └─ справочники/события ─▶ RT Flow / SDE (Apache Flink, decisioning)

7. Архитектура — solution / развёртывание

Слой Технология
Backend Spring Boot 4.x, Java 25, Maven (mvnw)
Персистентность Spring Data JDBC + JdbcClient (явный SQL), без JPA/Hibernate
БД PostgreSQL (своя), миграции Liquibase; леджер партиционирован помесячно по времени проводки (posted_at)
Auth Keycloak OIDC; backend — OAuth2 resource server (JWT, stateless)
API REST /v1; OpenAPI 3.1 (springdoc) + CI diff-gate
Frontend React 18 + Webpack Module Federation (remote под core-ui; на стенде автономно)
Наблюдаемость Logback + JSON-логи, Micrometer Tracing (OTel), Prometheus на :9090
Поставка GitLab CI + Kaniko; distroless/non-root; порты 8080/9090; Helm-чарт

Подробнее — Установка и развёртывание и Конфигурация.

8. Интеграции с продуктами CM Ocean

Продукт / система Характер интеграции
CM Ocean Promo Promo асинхронно (из своего outbox, client-credentials, Idempotency-Key = redemptionId, роль LOYALTY_INTEGRATION_WRITE) вызывает Loyalty POST /v1/integration/promo/accruals — эффект «начислить баллы». Различие триггера: Promo code-triggered, Loyalty event-triggered. У Promo интеграция off-by-default
RT Flow / SDE (Apache Flink) real-time decisioning: правила механик и справочники реплицируются в стрим-движок; Loyalty — система записи баллов и конфигурации, RT Flow — слой исполнения логики в реальном времени. Двусторонний вызов: RT Flow пишет начисления (POST /v1/integration/rtflow/accruals, реферальная и другие сценарии) и синхронно проверяет условия — теги клиента, участие в акциях/метках (POST /v1/integration/rtflow/condition-checks) — той же логикой, что условия аудитории акции
MDM Data Ocean мастер identity (golden profile). Loyalty обращается через IdentityPort (read Get/Find; запись — за фиче-флагом). Недоступность MDM отдаётся отдельным кодом (503 mdm_unavailable), отличным от «клиента нет» (404). Есть адаптер «Без MDM» — встроенный лёгкий identity-store
POS / шина интеграций приём чеков и событий покупки через адаптер (начисление по сумме и по натуральному объёму; код торговой точки обязателен, переменные покупки — для дополнительных условий акций), возврат по чеку, резолюция карты лояльности на кассе; клиенто-безопасная проекция участника для Check Scan
Асинхронный приём команд начисление/списание/резерв можно подать через очередь приёма (/v1/intake/commands) вместо синхронного API — опционально поверх брокера Kafka, с гарантией порядка исполнения в пределах одного клиента и отложенным исполнением
Обмен данными / DWH ленты изменений (клиенты, счета, операции) с курсорной пагинацией и регламентные задания выгрузки витрин в целевую БД потребителя
Система кампаний (Batch Flow) заливка состава сегментов клиентов; залитый состав — единственный источник сегментов для акций, партнёрских предложений и лимитов списания
Коммуникации / брокер сообщений очередь исходящих: уведомления клиенту (начисление, списание, предупреждения о сгорании, факт сгорания) и событие о смене уровня публикуются в брокер для внешних систем (RT Flow, система взаимодействия с клиентом); отправка включается настройкой

9. Как читать документацию

https://datasapience.ru/platforms/cmocean