Skip to content

Doc How Tour Publishing

PlutoGr edited this page Oct 17, 2026 · 5 revisions

Как работает конструктор и публикация тура

Менеджер собирает тур в мастере из 7 шагов. Пока тур черновик, его видно только владельцу и админу. Публикация проверяет, что тур заполнен, делает его фото публичными и открывает тур в каталоге.

Простой пример

POST /api/v1/catalog/manage/tours
→ 201  ETag: "1"   { "id": "3f2a…", "status": "Draft", … }

PUT /api/v1/catalog/manage/tours/3f2a…/basics
If-Match: "1"
{ "title": "Неделя в Стамбуле", "country": "Турция", "durationDays": 7, … }
→ 200  ETag: "2"

POST /api/v1/catalog/manage/tours/3f2a…/publish
If-Match: "2"
→ 422  { "type": "https://travelagency/errors/tour-not-publishable",
         "missing": ["description", "program.dayCount", "images.minCount", …] }

Менеджер дозаполняет шаги, каждый раз отправляя последнюю версию, и следующий publish проходит: тур появляется в GET /api/v1/catalog/tours.

Шаги мастера

Фронтенд TourWizardPage (/manager/tours/new, /manager/tours/:tourId), компонент TourWizard:

№ Шаг интерфейса Запрос
1 Основное PUT …/basics и PUT …/description
2 Программа PUT …/program
3 Включено и не включено PUT …/conditions
4 Проживание и питание PUT …/conditions (шаги 3 и 4 сохраняются одним запросом)
5 Даты и цены PUT …/prices
6 Фото загрузка в Media, затем PUT …/images
7 Проверка и публикация POST …/publish

Номер шага хранится в ?step= адресной строки.

Версия тура: ETag и If-Match

Задача: два окна одного менеджера (или менеджер и админ) не должны молча затирать изменения друг друга.

  1. Каждый ответ с туром несёт ETag: "<Version>".
  2. Любая запись, кроме создания черновика, требует If-Match с этой версией (W/ и кавычки допускаются).
  3. Версия не совпала: 409. Заголовка нет: 428.
  4. Любое изменение агрегата, даже только фото или дней программы, вызывает доменный Touch() и увеличивает Version. EF Core использует Version как токен конкуренции: если между чтением и сохранением версия сменилась, UPDATE … WHERE Version = <старая> не найдёт строку, и ответ тоже 409.

Почему не системный столбец PostgreSQL xmin: он меняется, только когда переписывается строка Tours. Шаг, который меняет одни фото, строку тура не трогает, и устаревший If-Match прошёл бы проверку.

Проверка публикации

Коды лежат в TourPublishRequirementCodes (Catalog.Domain), это единственный источник. Тексты для пользователя только на фронтенде (src/domain/tourManageErrors.ts, тест проверяет, что у каждого кода есть текст).

Код Условие
title, shortDescription, description тексты заполнены
program.dayCount дней в программе ровно DurationDays
inclusions.included хотя бы один пункт «включено»
mealPlan, accommodation питание и проживание заданы
offers.future хотя бы одно предложение с будущей датой
images.cover есть обложка
images.minCount не меньше 3 фото (максимум 20)
images.coverMinWidth ширина оригинала обложки не меньше 1280 px; ширина 0 (неизвестна) не проходит

Опубликованный тур проверяется и при правке: изменение, которое ломает проверку, получает 422, а тур сам с публикации не снимается.

Что происходит при publish

  1. Загрузить тур, сверить If-Match.
  2. Через gRPC GetMediaFiles получить у Media все фото тура: каждое должно принадлежать владельцу тура; взять ширину обложки.
  3. Проверить требования. Есть пропуски: 422 с missing.
  4. Вызвать MarkMediaFilesPublic для всех фото тура. Media недоступен: 503, статус тура не меняется, повтор безопасен.
  5. Только после успешного вызова поставить Published и PublishedAt, сохранить.

Порядок «сначала фото, потом статус» гарантирует, что в момент публикации все фото тура уже открыты.

PUT images тоже проверяет через GetMediaFiles владельца каждого фото, назначение tour-image и ширину обложки. Чужой файл, неизвестный id и другое назначение дают один 422 images.notFound. Обложка меньше 1280 px: 422 сразу при сохранении. Если тур уже Published, перед заменой фото и сохранением вызывается MarkMediaFilesPublic для всех id из запроса. Media недоступен: 503, список фото тура не меняется, повтор безопасен. В мастере при этом остаётся текст «Не удалось обновить фото тура. Изменения не сохранены, попробуйте ещё раз через минуту», выбранные фото не сбрасываются. У черновика и снятого тура флаг не ставится: его поставит следующий publish.

Снятие и удаление

  • unpublish: Published → Unpublished. Тур пропадает из каталога и со страницы (404), в избранном остаётся карточкой с available: false. Фото остаются публичными, чтобы карточка избранного не сломалась.
  • DELETE: только черновик, иначе 409.

Кто что может

Manager Admin
список свои туры все
создать черновик да, становится владельцем да
править, публиковать, снимать, удалять только свои (иначе 403) любые

Предварительный просмотр фото черновика идёт через /api/v1/media/manage/files/{id}/{size}, публичные адреса до публикации отвечают 404. См. Картинки.

Clone this wiki locally