Объяснимая разбивка
Журнал разворачивается до девяти вкладок, а строка, по которой товар не поехал, несёт причину. Разговор переходит от «система посчитала странно» к обсуждению конкретного правила.
NO_STOCK_WH · NEED_MET · TURNOVER_SENDER
Ритейл · движок распределения
Было: решение о том, сколько товара и в какой магазин отправить, собиралось в таблицах, и объяснить его задним числом было почти невозможно. Сделали: вычислительный движок с тремя пайплайнами разбивки — прямой логистикой, обратной и перераспределением между магазинами — и операторской панелью, где у каждой строки видно основание и причина отказа. Стало: расчёт запускается как задание, привязан к снимку метрик и воспроизводится, а любой срез журнала выгружается в Excel.
Контекст и задача
Конечного заказчика мы не называем — проект под NDA, и прямая оговорка здесь честнее умолчания. Речь о розничной сети: склады, магазины и ежедневное решение, куда что отправить.
Распределение товара — задача, у которой нет одного правильного ответа. Можно везти туда, где быстрее продаётся; можно — туда, где полка пустая; можно оставить запас на складе. Любой выбор кто-то оспорит, поэтому от системы требуется не только результат, но и основание: почему в этот магазин отправили две единицы, а в соседний ни одной.
Второе требование — воспроизводимость. Расчёт опирается на скорость продаж, остатки и потребность, а они меняются каждый день. Если пересчитать вчерашнее распределение сегодня, цифры не совпадут, и разбор превращается в спор о том, какие данные были «на самом деле».
Третье — сами данные. Справочники, правила и настройки живут не только в базе: часть приходит файлами, часть операторы меняют руками. Требовать разработчика на каждую правку справочника — значит остановить работу сети.
Отсюда рамки проекта:
Решение
Алгоритмы здесь — только половина работы. Вторая половина в том, чтобы результат можно было прочитать, оспорить и повторить.
shop_article_metrics привязан к JobId, поэтому расчёт всегда можно повторить на тех же данных. Сами расчёты выполняются как асинхронные задания со статусами PENDING → RUNNING → DONE/FAILED.NO_STOCK_WH, NEED_MET, TURNOVER_SENDER проставляются на строке. Любой отфильтрованный срез выгружается в Excel через excelize, а сама панель встроена в бинарь через go:embed — отдельного фронтенд-деплоя нет.ADR · снимок метрик вместо расчёта «по живым данным»
Контекст. Исходные метрики меняются ежедневно, а разбор распределения происходит позже — иногда сильно позже. Решение. Каждый запуск фиксирует снимок shop_article_metrics и привязывает его к JobId; все шаги пайплайна читают снимок, а не текущее состояние. Следствие. Результат воспроизводится и защищается цифрами. Цена — хранение снимков и дисциплина: ни один шаг не имеет права ходить в живые данные напрямую.
Архитектура
Две службы вместо россыпи сервисов: шлюз с GraphQL, REST-загрузкой xlsx и встроенной панелью — и вычислительное ядро с тремя пайплайнами. Между ними gRPC, под ними MS SQL Server.
Результат
Движок распределения ценен ровно настолько, насколько его решениям доверяют. Поэтому основная часть результата — про объяснимость и самостоятельность.
Журнал разворачивается до девяти вкладок, а строка, по которой товар не поехал, несёт причину. Разговор переходит от «система посчитала странно» к обсуждению конкретного правила.
NO_STOCK_WH · NEED_MET · TURNOVER_SENDER
Задание фиксирует снимок метрик, поэтому вчерашнее распределение пересчитывается сегодня с тем же результатом. Статус задания виден, интерфейс не ждёт окончания расчёта.
JobId · PENDING → RUNNING → DONE/FAILED
Загрузка xlsx и csv с одиннадцатью типами настраиваемых правил разбора и live-CRUD справочников через whitelist управляемых таблиц: оператор правит то, что ему разрешено, и не может задеть остальное.
11 типов правил · whitelist таблиц
Любой отфильтрованный список уходит в Excel — данные не приходится копировать руками, чтобы показать их коллеге. Отдельно поддержан режим РКН для комиссионного товара.
excelize · режим РКН
Роль maincode
Проект под NDA, поэтому цифры сети и коммерческий эффект здесь не приводятся. Инженерная часть описана как есть.
go:embed.Что применили
Услуга
Вычислительные службы, асинхронные задания, gRPC внутри и GraphQL наружу — с предсказуемым поведением на длинных расчётах.
Услуга
Схемы под аналитические расчёты, снимки метрик, индексы и запросы, которые успевают к началу рабочего дня.
Услуга
Операторские панели, справочники, правила загрузки и права доступа как часть учётной системы, а не как набор надстроек.
Если ваша задача другая — посмотрите разбор мультиарендной ERP или логистическую платформу про микросервисы. Полный список — на странице проектов.
Похожая задача?
Смотрим на исходные данные, правила и то, кому потом придётся объяснять результат. Возвращаемся с архитектурой расчёта и планом. maincode — инженерная студия ИП Диденко М. С.