Техническая справка: AI-генерация меты
Документ для разработчиков. Описывает устройство фичи генерации меты и её применения к
товарам (apply-meta).
Идея
Генерация — это сохранённый переиспользуемый payload тех же четырёх групп, что и
копирование между товарами: описание, SEO, теги, характеристики. Поэтому генерация
применяется к товарам через тот же пайплайн apply-meta, что и «копирование из
другого товара». Внешний AI изолирован интерфейсом и пока подменён заглушкой.
Модель данных
Таблица meta_generations — миграция
back/.../db/changelog/changes/084-create-meta-generations.xml
(зарегистрирована в db.changelog-master.xml).
Сущность entity/MetaGenerationEntity.java:
| Поле | Тип | Назначение |
|---|---|---|
title | varchar | Заголовок для поиска/переиспользования |
status | varchar(20) | PENDING|DONE|ERROR; заглушка всегда DONE (задел под async) |
model | varchar(100) | Выбранная модель (значение с фронта; логики маршрутизации нет) |
comment | text | Доп. инструкция для AI |
sourceProductId | bigint FK | Товар-основа; FK ON DELETE SET NULL |
genDescription/genSeo/genTags/genAttributes | boolean | Что было запрошено |
inputSnapshot | jsonb (Map<String,String>) | Резолвленные значения товара, переданные AI (метка → значение) |
output | jsonb (GenerationOutput) | Результат |
model/generation/GenerationOutput.java (JSONB): descriptionDoc (DescriptionDoc),
metaTitle, metaDescription, tagNames: List<String> (теги — имена, не id),
attributes: List<GenerationAttribute(name,value)>.
JSONB маппится через @JdbcTypeCode(SqlTypes.JSON) (как descriptionDoc у товара).
Граница внешнего AI
generation/MetaGenerationClient.java— интерфейс:GenerationOutput generate(GenerationCommand).generation/GenerationCommand.java— вход:model, comment, description, seo, tags, attributes, inputs: Map<String,String>.generation/StubMetaGenerationClient.java—@Component, синхронная заглушка: детерминированный плейсхолдер, заполняет только запрошенные группы, вплетает комментарий.
Точка подмены: реальный HTTP-клиент реализует MetaGenerationClient и заменяет
StubMetaGenerationClient — сущность, сервис и UI не меняются. Под асинхронный контракт
переключить status на PENDING + опрос/вебхук.
Сервис и эндпоинты
service/MetaGenerationService + service/impl/MetaGenerationServiceImpl:
preview(GeneratePreviewRequest)— грузит товар,buildInputs()собирает выбранные поля вMap<метка,значение>, вызывает клиент, возвращаетoutput + inputSnapshot(без сохранения).save / list / get / update / delete / regenerate.regenerateперезапускает клиент по сохранённомуinputSnapshot.buildInputs()— маппинг ключей полей (name, category, brand, country, price, description, attributes, tags) в русские метки (Название, Категория, Бренд, Страна, Цена, Текущее описание, Характеристики, Теги); эти метки читает заглушка.
Контроллер controller/MetaGenerationController — /api/v1/generations:
| Метод | Путь | Назначение |
|---|---|---|
| POST | /preview | Прогон без сохранения |
| POST | / | Сохранить (201) |
| GET | / | Список (по createdAt desc) |
| GET | /{id} | Одна генерация |
| PUT | /{id} | Правка title + output |
| DELETE | /{id} | Удалить (204) |
| POST | /{id}/regenerate | Перепрогон заглушки |
Доступ: /api/v1/generations/** → ADMIN, SUPER_ADMIN (в config/SecurityConfig).
Исключение GenerationNotFoundException зарегистрировано в GlobalExceptionHandler.
Применение: apply-meta
POST /api/v1/products/{id}/apply-meta (controller/ProductController), DTO
dto/product/ApplyMetaRequest:
applyDescription + descriptionDoc
applySeo + metaTitle + metaDescription
applyTags + tagIds
applyAttributes + attributes
Каждый флаг applyX включает свою группу (false = не трогать, true = применить, в т.ч.
пусто). ProductServiceImpl.applyMeta(...) — один @Transactional: описание/SEO
выставляются, теги и MANUAL-характеристики полностью замещаются. Общая логика вынесена в
приватные replaceTags(...) / replaceManualAttributes(...), которые переиспользуют
setTags и setManualAttributes (валидация неизвестного тега откатывает транзакцию).
apply-meta не знает о генерациях — фронт резолвит генерацию в тот же черновик.
Фронтенд
admin/products/[id]/generate/page.tsx— экран создания (входы/выходы/модель/ комментарий →preview→ редактируемое превью →save/ «применить сейчас»).admin/generations/page.tsx— список + «Применить к товару» (пикер цели →/copy-from?generationId=).admin/products/[id]/copy-from/page.tsx— мастер. Источник (товар или генерация) приводится к общемуResolvedSource; вкладки источника; резолвtagNames → tagIds(несовпавшие → блок «создать/пропустить» черезPOST /api/v1/tags); поддержка?generationId=(обёрнут вSuspenseиз-заuseSearchParamsв Next 16).components/Admin/DescriptionEditor.tsx— добавлены необязательныеonChange(живой поток значения,onChangeдержится вrefво избежание цикла ре-рендера) иhideSave.- Типы:
types/generation.ts,types/product.ts(ApplyMetaRequest,ProductAttribute).
Тесты
controller/ProductApplyMetaIT— apply-meta (все группы,applyX=false, замена MANUAL с сохранением AUTO, неизвестный тег → 404, 401).controller/MetaGenerationControllerIT— preview/save/get/list/update/delete/regenerate, 401, 404.generation/StubMetaGenerationClientTest— unit на заглушку.
Запуск: cd back && ./gradlew test --tests "*MetaGeneration*" --tests "*ApplyMeta*"
(нужен Docker для Testcontainers).