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