[DRAFT] Асинхронная обработка B2B-чеков в billing-service

Документация контура финансовых транзакций: Расчет аналитики расходов на основе вебхука торговой сети Магазин

Published

July 1, 2026

1 Функциональное назначение

Метод представляет собой асинхронный обработчик событий (Kafka Consumer) внутри финансового микросервиса billing-service. Он предназначен для ведения автоматического учета B2B-расходов пользователей и формирования аналитики трат на основе данных, поступающих из фискального вебхука торговой сети.

Метод решает следующие архитектурные задачи:

  1. Асинхронный финансовый аудит: Вычитывает доверенное событие успешного импорта чека из брокера сообщений, итерируется по массиву товаров и рассчитывает точную суммарную стоимость всей корзины с учетом дробного веса позиций.
  2. Ведение фискальной истории (Multi-Tenancy): Сохраняет агрегированные данные транзакции и сырой лог чека в изолированную базу данных PostgreSQL Billing DB. Это позволяет пользователям видеть прозрачную аналитику расходов в разрезе магазинов, категорий и дат, не нагружая транзакционные таблицы инвентаря физической еды.
  3. Обеспечение идемпотентности фин-контура: Защищает баланс аккаунта и исторические метрики от повторного начисления сумм при дублировании сообщений в брокере Kafka за счет жесткой проверки фискального номера чека на уникальность.

2 Схема обработки запроса (Диаграмма последовательности Mermaid)

На диаграмме представлена логика асинхронной обработки фискальных данных после закрытия синхронного периметра вебхука. Финансовый сервис изолирован от контура холодильника и гарантирует фиксацию транзакции расходов в ACID-режиме.

sequenceDiagram
    autonumber
    
    participant K as Apache Kafka Cluster
    participant Bill as billing-service (FINANCE)
    participant DB as DB_BILLING (PostgreSQL)

    K->>Bill: Шаг 1: Вычитка события из топика bpds.inventory.in.receipt.import
    activate Bill
    
    Bill->>DB: Шаг 2: SQL SELECT id FROM b2b_transactions WHERE receipt_number = ...
    activate DB
    DB-->>Bill: Шаг 3: Проверка уникальности (Записей нет / Токен чист)
    deactivate DB
    
    note over Bill: Шаг 4: Внутренний метод:<br/>CalculateReceiptTotal()<br/>Расчет суммы: ∑(quantity * price)
    
    Bill->>DB: Шаг 5: SQL INSERT INTO b2b_transactions (UUID, total_sum...)
    activate DB
    DB-->>Bill: Шаг 6: Транзакция успешно зафиксирована (COMMIT)
    deactivate DB
    
    deactivate Bill

Процесс асинхронного расчета финансовой аналитики чека (Billing B2B Sync)

3 Расшифровка шагов

Шаг Действие Параметры / Запросы Ошибки (Исключения / Статусы)
Шаг 1 (KAFKA -> Bill) Фоновый воркер финансового сервиса вычитывает доверенное сообщение об импорте B2B-чека из системного топика пополнения с префиксом bpds. Топик: bpds.inventory.in.receipt.import
Handler: ProcessB2BFinancials()
Kafka: CommitFailedException (сбой фиксации офсета сдвига в кластере Kafka)
Шаг 2 (Bill -> DB_Billing) Идемпотентность финансов: Сервис проверяет, не обрабатывался ли данный фискальный документ финансовым контуром ранее, чтобы избежать двойного учета сумм. SQL-запрос:
SELECT id FROM b2b_transactions WHERE store_id = 'shop-almaty-05' AND receipt_number = 'CH-20260701-09' LIMIT 1;
PostgreSQL Exception: ConnectionPoolTimeout (отказ пула соединений финансовой СУБД)
Шаг 3 (DB_Billing -> Bill) База данных возвращает пустой результат, подтверждая, что транзакция уникальна и обрабатывается впервые. Вилка исключений (Дубликат): Если строка найдена, воркер прерывает транзакцию с логом BILLING_DUPLICATE_IGNORE. metric: billing_idempotency_blocks_total (счетчик предотвращенных начислений-дубликатов)
Шаг 4 (Bill -> Bill) Расчет аналитики: Сервис итерируется по массиву позиций чека (items) и калькулирует общую сумму расходов с учетом дробного веса. Внутренний метод: CalculateReceiptTotal()
Формула: total = sum(item.quantity * item.price)
NumericOverflowException (ошибка переполнения типа данных при некорректных ценах вендора)
Шаг 5 (Bill -> DB_Billing) Запись транзакции: Сервис открывает атомарную транзакцию и сохраняет фискальную запись, связывая её с пространством пользователя и суммой трат. SQL-запрос:
INSERT INTO b2b_transactions (home_group_id, receipt_number, store_id, total_amount, currency) VALUES ('uuid-77', 'CH-20260701-09', 'shop-almaty-05', 1191.03, 'KZT');
PostgreSQL Exception: DeadlockDetected
PostgreSQL Exception: ForeignKeyViolation
Шаг 6 (DB_Billing -> Bill) Реляционная СУБД успешно фиксирует изменения в финансовой таблице и закрывает ACID-транзакцию. Ответ СУБД: Статус COMMIT (успешная запись финансового лога). PostgreSQL Exception: DatabaseIsShuttingDown