diff --git a/_prj_blueprint/DSL.md b/_prj_blueprint/DSL.md new file mode 100644 index 0000000..e2d06c9 --- /dev/null +++ b/_prj_blueprint/DSL.md @@ -0,0 +1,114 @@ +# Спецификация: LPON Store Hub DSL (Компактный конструктор интерфейсов на кортежах) + +драфт + +## 1. Обзор и цели +Мы реализуем гибкий, управленный данными (Data-Driven) конструктор страниц внутри бэкенда Django для **LPON.RU**. +Вместо написания отдельных вьюх (Views) и шаблонов под каждый формат или раздел (например, `/catalog/cassettes/`, `/catalog/vinyl/`), мы формируем динамические **Страницы-Хабы (Hub Pages)** с помощью компактного JSON Tuple DSL, который хранится прямо в базе данных. + +Система завязана на модель `TbArticle` с типом `l_article_type = 'HUB'`. Конфигурация хранится в JSON-поле метаданных статьи (`j_article_metadata['hub']`). + +--- + +## 2. Синтаксис и структура JSON DSL + +Массив `hub` представляет собой список позиционных кортежей (массивов). Движок определяет тип блока на основе длины массива и типов переданных значений. + +### Правила синтаксиса: + +```json +{ + "hub": [ + ["source-slug", "view_mode"], + ["source-slug", limit, "sort_rule", "view_mode"], + ["module-or-article-slug"] + ] +} +``` + +## 3. Спецификация блоков + +### Тип A: Блок предложений товаров (по умолчанию Длина = 4) + +Выводит подборку товаров (TbOffer), полученную на основе категории, тега формата или слаг-коллекции. + +Формат: `[ "slug", limit, "sort", "view_mode" ]` + +Параметры: + +1. `slug` (str): Уникальный слаг TbMusicStyle, TbArticle (Категория/Тег) или идентификатор формата (например, cassettes-studio, cassettes-used-blank). +2. `limit` (int): Количество выводимых товаров (например, 5, 20). Фолбэк при ошибке: 10. +3. `sort` (str): Правило сортировки: + - `views` → -i_offer_views (самые просматриваемые) + - `favorites` → -i_offer_favorites (самые популярные / в избранном) + - `newest` → -t_offer_created (новинки) + - `price_asc` → f_offer_price (сначала дешевые) + - `price_desc` → -f_offer_price (сначала дорогие) +4. `view_mode` (str): Компонент рендеринга (шаблон): + - `carousel` → Горизонтальная карусель / слайдер. + - `grid` → Стандартная сетка карточек. + - `list` → Компактный вертикальный список. + +Пример: ["cassettes-studio", 5, "views", "carousel"] + +Замечания: `view_mode` -- это название зарегистрированного шаблона в системе. Если передан неизвестный `view_mode` или он отсутствует, используется дефолтный `list`. + +### Тип B: Баннер / Вложенная статья / Вложенный хаб (Длина = 2) + +Встраивает существующую статью, рекламный баннер или массив вложенного хаба. + +Формат: [ "slug", "view_mode" ] + +Параметры: + +1. `slug` (str): Слаг целевой TbArticle. +2. `view_mode` (str): Режим отображения: + - `slider` / `hero` → Главный баннер / промо-слайдер. + - `teaser` → Тизер статьи с кнопкой «Читать далее». + - `full` → Полный HTML-контент статьи, встроенный inline. + +Пример: ["hero-banner-cassettes", "slider"] + +### Тип C: Динамический модуль / Плагин (Длина = 1) + +Запускает кастомный Python-модуль, зарегистрированный в системе (например, расчет скидок, интерактивные виджеты, реклама). + +Формат: [ "module-slug" ] + +Параметры: + +1. `slug` (str): Уникальный идентификатор, зарегистрированный в PluginRegistry (например, module-promo-mandarin, module-ad-banner-1). + +Пример: ["module-promo-mandarin"] + +## 4. Полный пример конфига (j_article_metadata) + +{ + "hub": [ + ["hero-cassettes-2026", "slider"], + ["cassettes-studio", 6, "views", "carousel"], + ["cassettes-studio", 20, "newest", "grid"], + ["module-mandarin-promo"], + ["cassettes-maxell-used-ud2-c90-ver2", 6, "popular", "carousel"], + ["cassettes-tdk-ned-cding2-c60", 20, "price_desc", "list"], + ["guide-how-to-clean-heads", "teaser"] + ] +} + +## 5. Защита и логика бэкенда (Требования к Python / Django) + +При разработке парсера DSL: + +1. Защита от зацикливания и бесконечной рекурсии: + - Передавать множество `visited_slugs` при рендере страницы. + - Если вложенный блок пытается загрузить `slug`, который уже есть в `visited_slugs`, пропускать блок или выводить отладочный HTML-комментарий (``). +2. Ограничение глубины вложенности: + - Жестко ограничить максимальную глубину рекурсии: `MAX_RECURSION_DEPTH = 2` (устанавливается в `settings.py`). +3. Отказоустойчивость (Graceful Degradation): + - Если limit передан не числом или sort содержит неизвестную строку, парсер должен тихо применить безопасные дефолты (`limit=10`, `sort='newest'`, `view_mode='grid'`). Ошибка синтаксиса в JSON никогда не должна приводить к `500 Server Error`. + - Если `slug` не найден в базе, блок пропускается, а в HTML выводится комментарий: ``. +4. Изоляция данных: + - Внутри DSL запрещено использовать сырые имена полей БД (например, k_offer_to_item__s_item). Все выборки данных маппятся строго через `slug` и внутренние обработчики запросов (Query Handlers). + - Все ключи из json['hub'] должны быть объявлены заранее. +5. Валидация slug + - Нужно добавить в валидатор админки статей (TbArticle) проверку на "не совпадение" slug с существующими роутами сайта из `'urls.py'` (чтобы не было конфликта URL-ов). \ No newline at end of file