Разработка сайта фитнес-клуба на 1С-Битрикс
Сайт фитнес-клуба — это не витрина с фотографиями залов. Это рабочий инструмент, через который клиент записывается на тренировку, покупает абонемент, отслеживает остаток занятий и взаимодействует с тренером. Всё это должно работать без ручного вмешательства администратора, синхронизироваться с внутренними системами клуба и выдерживать пиковые нагрузки в понедельник вечером, когда полгорода решает начать новую жизнь.
Разберём, как это реализуется на 1С-Битрикс — от структуры данных до логики бронирования мест.
Расписание занятий: Highload-блоки и фильтрация
Расписание — центральный элемент сайта. Клиент приходит сюда чаще всего, и если расписание тормозит или неудобно фильтруется — он уходит в Telegram-бот конкурента.
Почему Highload-блок, а не обычный инфоблок. Обычный инфоблок (b_iblock_element) хранит данные в EAV-модели: каждое свойство — отдельная строка в b_iblock_element_property. При 300 занятиях в неделю, 15 залах и 40 тренерах таблица свойств разрастается на десятки тысяч строк. Фильтрация по комбинации «зал + день + тренер + направление» превращается в серию JOIN-ов, которые на боевом сервере дают 800-1200 мс.
Highload-блок (b_hlblock_entity) — это плоская таблица в MySQL/PostgreSQL. Одна строка = одно занятие, все поля — колонки. Фильтрация работает через обычные индексы.
Структура Highload-блока FitnessSchedule:
| Поле | Тип | Назначение |
|---|---|---|
| UF_DATE | date | Дата занятия |
| UF_TIME_START | string | Начало (формат HH:MM) |
| UF_TIME_END | string | Окончание |
| UF_HALL_ID | integer | ID зала (связь с HL FitnessHalls) |
| UF_TRAINER_ID | integer | ID тренера (связь с инфоблоком тренеров) |
| UF_DIRECTION_ID | integer | Направление: йога, кроссфит, бассейн... |
| UF_CAPACITY | integer | Максимум участников |
| UF_BOOKED | integer | Текущее количество записавшихся |
| UF_STATUS | enumeration | active / cancelled / full |
| UF_IS_RECURRING | boolean | Повторяющееся по шаблону |
| UF_TEMPLATE_ID | integer | Ссылка на шаблон расписания |
Для повторяющихся занятий создаётся отдельный Highload-блок ScheduleTemplate с полями дня недели и времени. Cron-задача (agents Битрикса или системный cron) раз в неделю генерирует конкретные занятия на следующую неделю по шаблонам. Это позволяет тренеру отменить конкретное занятие 15 марта, не ломая всё расписание.
Фильтрация на фронте. Используем Bitrix\Highloadblock\HighloadBlockTable::compileEntity() для получения ORM-сущности, далее стандартный DataManager::getList() с фильтром:
$result = $entityClass::getList([
'filter' => [
'UF_DATE' => $selectedDate,
'UF_HALL_ID' => $hallId,
'UF_STATUS' => 'active',
],
'order' => ['UF_TIME_START' => 'ASC'],
]);
На фронте расписание рендерится как сетка: по горизонтали — залы, по вертикали — временные слоты. Клиент переключает день, фильтрует по направлению или тренеру. AJAX-запросы через компонент bitrix:highloadblock.list или кастомный REST-эндпоинт.
Онлайн-запись с лимитом мест и waitlist
Запись на занятие — это не просто «нажал кнопку, попал в список». Это транзакционная операция с проверкой лимита, конкурентным доступом и механизмом ожидания.
Основной сценарий:
- Клиент нажимает «Записаться» на конкретном занятии
- Система проверяет:
UF_BOOKED < UF_CAPACITY - Если да — создаёт запись в Highload-блоке
FitnessBooking, инкрементируетUF_BOOKED - Если нет — предлагает встать в лист ожидания
Проблема конкурентного доступа. Два клиента одновременно нажимают «Записаться» на занятие, где осталось одно место. Без блокировки оба получат подтверждение, а на тренировке окажется лишний человек.
Решение — использование \Bitrix\Main\Application::getConnection() с транзакцией и блокировкой строки:
$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();
$row = $entityClass::getList([
'filter' => ['ID' => $scheduleId],
'select' => ['UF_BOOKED', 'UF_CAPACITY'],
'runtime' => [/* FOR UPDATE через raw SQL */],
])->fetch();
if ($row['UF_BOOKED'] < $row['UF_CAPACITY']) {
// создаём бронирование, инкрементируем UF_BOOKED
$connection->commitTransaction();
} else {
$connection->rollbackTransaction();
// предлагаем waitlist
}
На практике чистый ORM Битрикса не поддерживает SELECT ... FOR UPDATE, поэтому критическую секцию оборачиваем в raw SQL через $connection->query().
Лист ожидания (waitlist). Отдельный Highload-блок FitnessWaitlist с полями: UF_SCHEDULE_ID, UF_USER_ID, UF_POSITION, UF_CREATED_AT. Когда кто-то отменяет запись, агент Битрикса проверяет waitlist и автоматически переносит первого в очереди в основной список, отправляя SMS/push через модуль messageservice.
Отмена записи. Клуб обычно разрешает отмену за 2-4 часа до начала. Логика проверки времени — в обработчике события, который сравнивает UF_TIME_START с текущим временем и блокирует отмену, если лимит прошёл.
Продажа абонементов через модуль sale
Абонементы фитнес-клуба — это не товары из каталога. У них своя логика: срок действия, количество посещений, заморозка.
Типы абонементов реализуются как элементы инфоблока «Абонементы» со свойствами:
-
DURATION_DAYS— срок действия в днях -
VISIT_LIMIT— лимит посещений (0 = безлимит) -
TYPE— разовый / месячный / годовой -
FREEZE_ALLOWED— можно ли замораживать -
FREEZE_MAX_DAYS— максимальный срок заморозки
При покупке через \Bitrix\Sale\Order::create() абонемент добавляется в корзину как обычный товар, но после оплаты срабатывает обработчик события OnSaleOrderPaid. Он создаёт запись в Highload-блоке UserSubscription с полями: UF_USER_ID, UF_START_DATE, UF_END_DATE, UF_VISITS_LEFT, UF_IS_FROZEN, UF_FREEZE_START.
Заморозка абонемента. Клиент в личном кабинете нажимает «Заморозить». Система проверяет FREEZE_ALLOWED и FREEZE_MAX_DAYS, устанавливает UF_IS_FROZEN = true, сохраняет UF_FREEZE_START. При разморозке — пересчитывает UF_END_DATE, добавляя количество замороженных дней.
Интеграция с CRM-системами клуба
Фитнес-клубы редко работают только через сайт. Основные системы учёта — 1С:Фитнес клуб и Mobifitness.
1С:Фитнес клуб. Обмен через стандартный протокол CommerceML или через REST API самой 1С (HTTP-сервисы). Синхронизируются: справочник услуг, расписание, клиентская база, продажи абонементов. Обмен запускается по cron каждые 15-30 минут.
Mobifitness API. REST API с авторизацией по токену. Основные эндпоинты: /api/v1/schedule, /api/v1/bookings, /api/v1/clients. Битрикс выступает фронтендом, а Mobifitness — мастер-системой расписания. В этом случае Highload-блок FitnessSchedule заполняется не вручную, а через синхронизацию с API.
Выбор архитектуры зависит от того, какая система является мастером данных. Если клуб уже работает в Mobifitness — сайт подтягивает расписание оттуда. Если клуб переходит на цифру с нуля — Битрикс может быть основной системой.
Личный кабинет клиента
Личный кабинет строится на стандартном модуле main (авторизация, профиль) с расширениями:
-
История посещений — выборка из
FitnessBookingпоUF_USER_IDс JOIN на расписание -
Остаток занятий — поле
UF_VISITS_LEFTизUserSubscription -
Продление абонемента — кнопка, которая создаёт заказ в
saleс привязкой к текущей подписке - Заморозка/разморозка — интерфейс управления статусом подписки
Авторизация — через телефон с SMS-кодом (модуль messageservice + кастомный компонент). Фитнес-аудитория не любит пароли.
Тренерские профили
Тренеры — отдельный инфоблок с привязкой к направлениям через множественное свойство типа «Привязка к элементам». Каждый тренер имеет: фото, сертификаты (файловые свойства), опыт, описание, ссылку на расписание (фильтр по UF_TRAINER_ID).
На детальной странице тренера автоматически выводится его расписание на текущую неделю — один AJAX-запрос к FitnessSchedule с фильтром по тренеру.
Сроки реализации
| Масштаб проекта | Состав | Срок |
|---|---|---|
| Небольшой клуб (1 зал, 5-7 направлений) | Расписание, запись, абонементы, личный кабинет | 8-10 недель |
| Сеть из 3-5 клубов | + мультисайтовость, единая база, интеграция с Mobifitness | 14-18 недель |
| Крупная сеть (10+ клубов) | + B2B-портал для корпклиентов, мобильное приложение через REST API Битрикса, сложная тарификация | 20-28 недель |
Что учесть до старта
Перед разработкой нужно определить мастер-систему расписания: будет ли расписание вестись в Битриксе или в сторонней CRM. Это определяет направление синхронизации и архитектуру Highload-блоков. Второй вопрос — платёжный шлюз: для абонементов с автосписанием нужен эквайринг с поддержкой рекуррентных платежей, а это отдельная интеграция через sale.paysystem.







