Состояния мест и типы удержания
Seatmap.pro отслеживает каждое место на каждом событии через двухслойную модель состояний. Первый слой – небольшой закрытый набор состояний жизненного цикла, которые движок бронирования использует для управления конкурентным доступом и доступностью. Второй слой – открытый, определяемый клиентом holdType, который несёт бизнес-контекст: почему это место удерживается, кто может его освободить и как оно должно выглядеть на схеме.
Два слоя
| Слой | Колонка | Значения | Что определяет |
|---|---|---|---|
| Жизненный цикл | state |
закрытый enum | конкурентный доступ, доступность |
| Тип удержания | holdType |
открытая строка | политика освобождения, оплата, отображение |
Движок бронирования всегда проверяет лишь state = 'ACTIVE', чтобы определить, можно ли заблокировать место. holdType – это метаданные, управляющие бизнес-процессом вокруг этого места.
Состояния жизненного цикла
Четыре состояния, фиксированные платформой.
| Состояние | Значение | Доступно для брони |
|---|---|---|
ACTIVE |
Доступно для выбора | Да |
LOCKED |
Удержано, ожидает действия | Нет |
SOLD |
Подтверждено и оформлено | Нет |
BLOCKED |
Операционно недоступно | Нет |
Типичный поток корзины переводит место ACTIVE → LOCKED → SOLD. Брошенная корзина возвращает его LOCKED → ACTIVE. Неисправное место остаётся в BLOCKED, пока операционная служба его не очистит.
Типы удержания
holdType – это строка, которую вы прикрепляете к месту при его переходе из ACTIVE. Она существует рядом с состоянием жизненного цикла и описывает причину удержания места.
Значения платформы по умолчанию
Два типа удержания поставляются из коробки:
| Тип удержания | Допустимое состояние | Освобождается | Оплата |
|---|---|---|---|
CART |
LOCKED |
клиент, админ, система | требуется |
RESERVED |
LOCKED |
админ | не требуется |
CART – значение по умолчанию для эндпоинта /lock бронирования v2: отсутствие holdType в теле запроса разрешается в CART.
Примечание об автоистечении: поле
ttlSecondsниже принимается конфигурационным API и сохраняется вHoldTypeDefinition, но автоматическое освобождение блокировки по TTL пока не применяется сервисом бронирования. Удержания сохраняются до явного вызова/unlock,/saleили/revertsale. Применение TTL (записьseat_on_event.locked_untilпри блокировке и фоновый сборщик) запланировано в roadmap.
Определение собственных типов удержания
Каждая организация может объявлять дополнительные типы удержания через JSONB-колонку config в organization (и при желании наследовать от родительского tenant). Конфигурация читается через стандартный config API организации/арендатора, используемый для других функций, например webhooks.
Пример payload, отправляемого в POST /api/organizations/config/:
{
"holdTypes": {
"PARTNER_HOLD": {
"validStates": ["LOCKED"],
"ttlSeconds": 3600,
"releaseApi": "ADMIN,API",
"paymentRequired": true,
"displayName": "Partner Hold",
"displayColor": "#8B008B"
},
"COMP": {
"validStates": ["LOCKED", "SOLD"],
"ttlSeconds": null,
"releaseApi": "ADMIN",
"paymentRequired": false,
"displayName": "Complimentary",
"displayColor": "#FFD700"
}
}
}
Справочник полей:
| Поле | Тип | Описание |
|---|---|---|
validStates |
массив строк | Для каких состояний жизненного цикла допустим этот тип удержания. Сервис бронирования отклоняет запросы с недопустимым сочетанием. |
ttlSeconds |
целое число или null | Предполагаемое окно автоосвобождения в секундах. Хранится как конфигурационные метаданные; автоосвобождение пока не применяется (см. примечание выше). null означает, что удержание сохраняется до явного освобождения. |
releaseApi |
строка | Список акторов через запятую, которым разрешено освобождать: CUSTOMER, ADMIN, SYSTEM, API. |
paymentRequired |
boolean | Должно ли за этим удержанием следовать оплата для перехода в SOLD. |
skipLockedState |
boolean | При true переход выполняется напрямую ACTIVE → SOLD (используется для процессов кассы). |
displayName |
строка | Человекочитаемая подпись, отображаемая в админ-интерфейсах. |
displayColor |
строка | Hex-цвет для оформления в рендерере. |
displayIcon |
строка | Необязательная ссылка на изображение или иконку для рендерера. |
Наследование на уровне арендатора
Если ваша организация принадлежит арендатору, вы можете определить типы удержания на уровне арендатора один раз, и они применятся к каждой организации арендатора. Переопределения на уровне организации имеют приоритет поле за полем, поэтому организация может, например, переопределить displayColor у CART, не переписывая полное определение.
Порядок разрешения при любом поиске типа удержания:
config.holdTypes[name]на уровне организацииconfig.holdTypes[name]на уровне арендатора- Значения платформы по умолчанию
Резолвер объединяет эти три слоя. Если вы переопределяете только displayColor на уровне организации, остальная часть определения наследуется от арендатора или значения платформы по умолчанию.
Использование типов удержания в booking API
Payload StateSelection бронирования v2 принимает необязательное поле holdType:
POST /api/private/v2.0/booking/lock?eventId=123e4567-e89b-12d3-a456-426614174000
Content-Type: application/json
{
"sessionId": "abc123",
"seats": [{ "id": 42 }],
"holdType": "PARTNER_HOLD"
}
Сервис бронирования проверяет:
- Тип удержания определён (как значение платформы по умолчанию, запись на уровне арендатора или на уровне организации) для вызывающей организации.
validStatesвключает целевое состояние жизненного цикла.
Неизвестные или несовпадающие типы удержания возвращают 400 Bad Request. Эндпоинты /unlock и /revertsale не принимают holdType – возврат места в ACTIVE очищает любое прикреплённое удержание.
Конкурентный доступ и условная блокировка
Каждый переход состояния – это условное SQL-обновление, которое утверждает ожидаемое текущее состояние в своём предложении WHERE. Два конкурентных запроса, блокирующих одно и то же место, не могут оба завершиться успешно – обновление второго запроса затрагивает ноль строк, и API возвращает false. Это делает двойное бронирование невозможным на уровне базы данных, независимо от таймингов потока корзины.
Журнал аудита
Каждый успешный переход состояния добавляет неизменяемую строку в seat_state_events, записывая:
- Старое и новое состояние жизненного цикла
- Тип удержания на момент перехода
- Идентификатор сессии (когда доступен)
- Затронутое место или группу мест
- Временную метку
Этот журнал, работающий только на добавление, является источником истины для аналитики, заменяя приблизительную телеметрию, существовавшую ранее. Последующая отчётность может отвечать на вопросы вроде «какой процент удержаний CART конвертировался в SOLD за последнюю неделю», не затрагивая горячий путь бронирования.
Интеграция с рендерером
Booking Renderer стилизует места по их состоянию жизненного цикла (ACTIVE, LOCKED, SOLD) из коробки. Чтобы визуализировать пользовательские значения holdType – или любое другое различие – собственными цветами, анимациями или иконками, SDK рендерера предоставляет setSeatsState(seats, stateKey) и clearSeatsState(seats), управляемые декларативной конфигурацией theme.seatStyles. Считайте payload мест (включая их holdType) и примените соответствующее состояние рендерера к нужным ID мест. См. Пользовательские состояния мест (Renderer SDK) для полного API, справочника типов и проработанных примеров.
Связанное
- Состояния и стилизация секций – эквивалентная концепция, применяемая к обводкам секций (выделенные, выбранные, недоступные, отфильтрованные).