By Андрей -
andreydust@gmail.com
September 28, 2026
Онлайн расписание тренировок
Онлайн-расписание тренировок — это не картинка и не PDF, который администратор перезаливает по понедельникам. Это интерфейс к текущему состоянию клуба: сколько мест осталось на сайкл в 19:30, кто сегодня ведёт занятие вместо заболевшего тренера, перенесли ли групповую тренировку из-за ремонта в зале. Разница между «страницей с расписанием» и «расписанием как сервисом» ровно одна — есть ли за страницей живой источник данных. Если его нет, автоматизация фитнеса заканчивается на красивой вёрстке: человек всё равно звонит администратору, чтобы уточнить, актуальна ли строка в таблице. Реализации вроде той, что разобрана по ссылке https://sportpriority.com/schedule/, показывают главное: пользователь должен видеть не «программу занятий», а конкретный слот, который прямо сейчас можно занять.
Что должно быть под капотом
Техническая часть расписания сводится к трём сущностям, которые нельзя смешивать. Класс — шаблон занятия: название, длительность, описание методики, требуемый уровень. Занятие — экземпляр класса в конкретную дату и время с конкретным тренером и залом. Бронь — связь занятия и клиента. Ошибка, из-за которой потом приходится переписывать интеграцию, — попытка хранить всё это одной таблицей: как только тренер заменяется на одном занятии из десяти, шаблон и экземпляр приходится разделять задним числом, а брони уже расставлены.
Источник истины должен быть один — учётная система клуба (CRM). Сайт её не дублирует, а отображает. Любое ручное ведение второй копии расписания в админке сайта заканчивается расхождением в первую же неделю, и доверие к онлайн-записи теряется быстрее, чем набирается.
Интеграция: где всё ломается
Модуль бронирования связывается с базой клуба через API — либо готовым, от поставщика CRM, либо через встраиваемый виджет. Сам вызов написать несложно, сложно пережить ситуации, когда что-то идёт не так. Ниже — узлы, которые ломаются практически у всех, и то, что с ними делают.
| Узел | Как ломается | Рабочее решение |
|---|---|---|
| Свободные места | Число берётся из кеша часовой давности, клиент приезжает на занятый слот | Кеш 15–60 секунд плюс отметка «обновлено N секунд назад»; финальная проверка мест в момент подтверждения брони, а не при отрисовке страницы |
| Повторная отправка формы | Двойной клик создаёт две брони на одного человека | Ключ идемпотентности в запросе: повтор с тем же ключом возвращает ту же бронь, а не создаёт новую |
| Последнее место | Два посетителя одновременно занимают один слот | Гонку решает транзакция в учётной системе, а не проверка в браузере; сайт показывает понятный отказ и сразу предлагает ближайшее время |
| API недоступен | Пустая страница или «Ошибка 500» вместо расписания | Показать последнее известное расписание с честной пометкой о времени обновления, бронирование временно отключить, телефон вывести на видное место |
| Отмена занятия | Записавшиеся узнают об отмене на входе в клуб | Вебхук об изменении расписания запускает уведомления записавшимся и автоматический перевод людей из листа ожидания |
| Время | «19:30» без указания зоны и клуба | Хранить локальное время клуба вместе с часовой зоной и явно показывать, к какой площадке относится занятие |
| Рендеринг | Расписание рисуется только скриптом, поисковый робот видит пустой блок | Отдавать каркас расписания с сервера и иметь отдельный адрес у каждого занятия |
Отдельно про обновления. Опрашивать API по таймеру каждые несколько секунд — дорого и для вас, и для поставщика. Надёжнее связка: вебхук на изменение расписания как основной канал плюс редкий фоновой опрос как страховка, потому что вебхуки теряются. И ключ доступа к API живёт на сервере: если он ушёл в JavaScript на странице, его заберут в первый же день.
Наш опыт: чужой API как часть публичного результата
Мы столкнулись с той же инженерной задачей с неожиданной стороны — в сервисе розыгрышей с протоколом на pro-chislo.ru. Задача там другая, а требования к внешним вызовам оказались один в один как у бронирования: результат виден посторонним людям, и «извините, сервис недоступен» их не устраивает.
Устроено это так. Случайность мы принципиально не генерируем у себя — её отдаёт публичный маяк drand сети League of Entropy, новый раунд выходит раз в три секунды. Сначала фиксируется состав участников: список превращается в хеш-отпечаток, и тут же назначается будущий раунд маяка, значение которого на момент анонса не знает никто, включая нас. Когда раунд выходит, из отпечатка и случайности собирается зерно, а победителей считает чистая функция — её результат любой желающий повторяет у себя. Дальше запись встаёт в цепочку хешей, а метку времени на неё ставит независимая служба по стандарту RFC 3161.
Четыре вывода из этой интеграции переносятся на запись в фитнес-клуб без изменений.
- Своё состояние фиксируется до обращения наружу. Сначала отпечаток списка, потом запрос к маяку. В бронировании порядок тот же: сначала транзакция, которая занимает место, и только потом письма, пуши и синхронизации.
- Таймаут и повторы — обязательная часть вызова. У нас это четыре секунды и два повтора. Всё, что дольше, посетитель читает как «сайт сломался», и никакие логи его не переубедят.
- Отказ внешнего сервиса не должен ронять сценарий. Не ответил маяк — розыгрыш не падает, а ждёт следующего раунда. Метка времени вообще живёт отдельным полем и дозапрашивается позже, ограниченное число попыток и не чаще раза в десять минут. Аналог для расписания — показать сохранённые данные с честной пометкой, а не белый экран.
- Результат должен быть проверяемым. У нас для этого есть машиночитаемая выдача протокола и открытый журнал. У клуба роль протокола играет бронь: номер, статус, время, занятие, тренер — всё, что человек может предъявить на стойке.
Общий принцип простой: внешний API — это не «функция, которая всегда возвращает ответ», а сетевой вызов, который однажды обязательно не вернётся. Проектировать надо второй случай, первый работает сам.
Что автоматизация даёт клубу
Когда человек видит детали занятия, описание методики и свободные места и занимает слот в несколько касаний, из цепочки «узнать — выбрать — записаться» исчезает администратор. Это снимает рутину с персонала: звонки о расписании и ручная перезапись отменённых занятий перестают занимать рабочий день. Одновременно закрывается частая дыра в деньгах — освободившееся после отмены место уходит человеку из листа ожидания автоматически, а не остаётся пустым, потому что перезвонить было некому и некогда.
Клуб при этом перестаёт быть местом, куда надо дозвониться, и становится сервисом, доступным в любое время суток. Прозрачность и скорость ответа работают на лояльность не абстрактно: посетитель видит, что его время ценят, а его выбор учитывают на каждом шаге. Из этого и вырастает переход от разового визита к постоянным занятиям — но начинается он не с маркетинга, а с того, что цифра свободных мест на странице соответствует действительности.
Отзывы и комментарии
Пока нет комментариев. Будьте первым!