Краткий ответ
Онлайн-запись требует единого правила доступности ресурса и атомарного подтверждения брони. Свободный слот на экране ещё не гарантирует возможность записи: между просмотром и подтверждением его может занять другой человек. Для удержания, оплаты, отмены и повторов нужны отдельные состояния.
Определите ресурс и вместимость
Бронировать можно время специалиста, помещение, оборудование или место в группе. У каждого ресурса свои ограничения. Один специалист не может вести два приёма одновременно, а группа допускает несколько участников до установленной вместимости. Модель должна отражать реальную единицу ограничения.
Уточните длительность, перерывы, подготовку и зависимые ресурсы. Условной консультации может быть нужен только специалист, а обследованию — ещё кабинет и оборудование. Проверка одного календаря не гарантирует доступность всей услуги.
Разделите отображение и подтверждение
Список свободных слотов показывает состояние на момент получения данных. Другой человек может выбрать тот же слот до завершения первой записи. Поэтому окончательная проверка выполняется при подтверждении операции, с защитой от конкурирующих изменений на уровне системы хранения или эквивалентного механизма.
Не пытайтесь решить проблему только отключением кнопки в браузере. У двух посетителей разные сессии, а запрос может прийти напрямую. Если используется внешний API расписания, выясните, какую гарантию предоставляет именно операция бронирования.
Карта состояний
| Состояние | Значение | Что требуется определить |
|---|---|---|
| Доступно | Ресурс можно запросить | Источник и актуальность |
| Временно удержано | Идёт завершение записи | Срок и освобождение |
| Подтверждено | Запись принята | Идентификатор и уведомление |
| Ожидает оплаты | Условия ещё не выполнены | Дедлайн и связь с бронью |
| Отменено | Запись прекращена | Возврат доступности |
Не всем системам нужно временное удержание. Оно полезно, когда между выбором и подтверждением есть значимый этап, например оплата. Если удержание вводится, его истечение должно обрабатываться даже при закрытом браузере.
Продумайте время и календарь
Укажите, в каком часовом поясе отображается запись, особенно если клиент и исполнитель находятся в разных регионах. Хранение и отображение должны сохранять однозначный момент времени. Не полагайтесь на случайные настройки устройства посетителя без пояснения.
Опишите исключения расписания: выходной, замена специалиста, перенос рабочего времени и уже подтверждённые записи. Изменение шаблона доступности не должно молча отменять действующие обязательства. Такие случаи требуют отдельного процесса уведомления и согласования.
Обработайте повтор и потерянный ответ
Если сервер принял запись, а ответ не дошёл, повторное действие должно позволить узнать исходный результат или безопасно повторить запрос. Идемпотентность помогает не создавать вторую операцию из-за сетевой неопределённости. Ключ должен относиться к конкретному намерению, а не блокировать все будущие записи пользователя.
Документация Stripe показывает один технический вариант такого контракта. Это не универсальный рецепт для расписания: срок ключа, повтор с изменёнными параметрами и поиск результата проектируют для выбранной системы.
Согласуйте оплату и отмену
Платёж может подтвердиться после истечения удержания. Заранее решите, как обрабатывается такой конфликт: повторная проверка, ручное решение или другой согласованный процесс. Нельзя автоматически выдавать подтверждение записи, если ресурс уже занят.
Условия отмены и возврата должны быть согласованы владельцем услуги и отражены в интерфейсе. Техническая статья не задаёт универсальных коммерческих или правовых правил. Система реализует конкретные утверждённые условия и сохраняет понятную историю изменений.
Проверки до пилота
- Два пользователя одновременно выбирают последний доступный слот.
- Удержание истекает при открытой и закрытой странице.
- Повторяется запрос после потери ответа.
- Уведомление приходит повторно или с задержкой.
- Изменяется расписание с уже существующими бронями.
- Пользователь отменяет запись и проверяет итоговый статус.
Наблюдайте не только экран, но и итоговые записи ресурса и операции. Для первой версии приложения эти сценарии важнее дополнительных декоративных функций: они определяют, можно ли доверять главному обещанию сервиса.
Сценарий одновременного бронирования
Создайте в тестовом расписании один доступный ресурс на определённое время и откройте его двумя пользователями. Оба видят свободный слот. Затем отправьте подтверждения так, чтобы запросы конкурировали. Ожидаемый результат зависит от вместимости: для единичного ресурса подтверждается только допустимая запись, второму пользователю объясняется изменение доступности.
Проверьте итоговые данные, а не только сообщения браузеров. В системе не должно оставаться двух подтверждённых обязательств при одном ресурсе. Если используется удержание, посмотрите, кто им владеет, когда оно истекает и что происходит после закрытия страницы.
Повторите упражнение с потерей ответа победившему пользователю. Повторный запрос должен позволить безопасно узнать результат. Затем проверьте отмену и появление доступности для следующей записи. Так проверяется весь цикл ресурса, а не отдельная операция создания.
Для сервиса с оплатой добавьте задержанное подтверждение после истечения удержания. Это отдельный конфликт, требующий принятого правила. Журнал должен связывать бронь и платёжный результат, чтобы ответственному не приходилось угадывать последовательность по письмам. Число успешных кликов на календаре не является доказательством корректности такой системы.
Почему одинакового времени начала недостаточно
Проверка уникальности пары «ресурс + начало» не выявит все пересечения. В учебном расписании кабинет занят с 10:00 до 11:00, а следующая заявка начинается в 10:30. Время начала разное, но обязательства конфликтуют. Для услуг разной длительности проверяют весь занятый интервал, включая согласованный перерыв на подготовку.
| Запрос при занятом кабинете А с 10:00 до 11:00 | Ожидаемое решение |
|---|---|
| Кабинет А, 10:30–11:30 | Отказ из-за пересечения |
| Кабинет А, 11:00–12:00 | Допустимо, если между приёмами нет обязательного перерыва |
| Кабинет Б, 10:30–11:30 | Допустимо, если нет других общих ограничивающих ресурсов |
Все интервалы примера относятся к одной дате и одному явно указанному часовому поясу. Конец интервала считается свободной границей. Если после приёма нужен перерыв 15 минут, первый кабинет фактически занят до 11:15, и начало в 11:00 становится недопустимым. Такое правило должно одинаково применяться в календаре и при подтверждении.
В документации PostgreSQL 17 показано ограничение EXCLUDE для непересекающихся диапазонов, в том числе по одному помещению. Это пример механизма для единичного ресурса. Групповая вместимость, несколько связанных ресурсов, удержания и отмены требуют собственной модели; одного ограничения из документации недостаточно для готовой системы записи.
Идемпотентность и защита от пересечений решают разные задачи. Повтор одного намерения с тем же ключом должен возвращать согласованный результат. Два разных клиента с разными ключами всё равно конкурируют за один интервал. В приёмке проверьте обе ситуации и потерянное подтверждение, а отправку уведомления свяжите с результатом сохранённой брони. Сообщение «вы записаны» должно соответствовать принятому обязательству.
Термины из материала
Источники и документация
У каждого документа указана дата последней проверки. Состав функций и интерфейсы сервисов могут меняться. Ссылки открываются в новой вкладке.
- Stripe: идемпотентные запросы Проверено
- PostgreSQL 17: диапазоны и ограничения пересечений Проверено
Применить к вашему проекту
Поможем описать состояния записи и проверить поведение при одновременных запросах.
Обсудить задачу