Управление сетью клубов из одного окна
letscourt с первого дня сделан как multi-tenant платформа. Один клиент с одним балансом и абонементом действует во всех клубах сети — не «переносить руками между Excel-файлами», а архитектурно одна запись. Дашборд директора показывает пульс каждого филиала, права сотрудников разделены по клубам, аудит-лог фиксирует кто что менял.
Подать заявку →Multi-tenancy — как у нас архитектурно
Когда у вас 5 клубов, а Иванов ходит во все три ближайших — он должен быть одной записью в системе, а не пятью.
Когда Иванов покупает абонемент в Хамовниках на 10 часов в месяц — он автоматически может списывать эти часы в любом из четырёх ваших клубов. Когда переводит деньги на баланс — баланс общий. Это не «синхронизация между базами», это одна запись на уровне Postgres с привязкой к organization_id, видная во всех клубах через scope-фильтрацию.
Дашборд директора — пульс сети за 30 секунд
Открыли — сразу видно где что плохо. Не надо открывать Excel-отчёты от каждого филиала по очереди.
Выручка сети · Конверсия в оплату · Броней · Отмены — за 7 / 30 / 90 дней vs предыдущий период. Стрелочка вверх/вниз цветом.
Автоматический список клубов с просадкой выручки 30%+ относительно среднего по сети. Подсвечен красным. Кнопка «Разобрать» ведёт прямо в финансы клуба.
Тепловая карта час × клуб. Видно где Сб 19:00 пустой, а Митино 18:00 закрашено в красный. Решение «открыть второй прайм-час» принимается за минуты.
Кто принёс больше всего часов и выручки в этом месяце. Ранг по двум осям — можно сортировать. Фильтр по клубам.
Роли сотрудников — 5 уровней доступа
Настройка кто что видит занимает ровно один экран. Не нужно отдельно ходить в каждый клуб.
| Роль | Видит | Может |
|---|---|---|
| Владелец | Все клубы, финансы, команду | Всё, включая удаление организации |
| Директор сети | Все клубы, KPI, финансы | Создавать команду, менять тарифы, видеть аудит |
| Администратор клуба | Только привязанные клубы | Брони, клиенты, инвентарь своего клуба |
| Тренер | Своё расписание во всех клубах | /coach кабинет: свои занятия, выплаты |
| Только чтение | Все клубы (read-only) | Видеть, но не менять — для собственника-инвестора |
Маша — администратор только Хамовников. Открыла /admin/schedule — видит только Хамовники в селекторе клубов. Чужих клубов не видит даже в API. Это не UI-фильтр (который можно обойти F12), это scope-фильтрация на уровне сервера в каждом запросе.
Аудит и прозрачность
В сети из 5 клубов и 30 сотрудников нужно знать кто что менял — иначе споры не решить.
Каждое создание/изменение/удаление пишется в audit_log с user_id, IP, user-agent, payload. Партицирован по месяцам — не тормозит на больших объёмах.
Фильтры: по сотруднику, по клубу, по типу действия (BOOKING_CREATE / TARIFF_UPDATE / CUSTOMER_DELETE / ...), по периоду. Спор «кто отменил оплаченную бронь?» решается за 30 секунд.
Если platform-админ Tennis.app зайдёт под кем-то из вашей команды для саппорта — это записывается в audit с action=PLATFORM_IMPERSONATE. У сотрудника при этом виден жёлтый баннер «Сейчас вы видите систему как platform-админ».
Готовность к 1000+ клубам
Архитектура с первого дня заложена под рост — без переезда базы и переписывания кода.
- ✓Партицированный audit_log. По месяцам, до 50M записей в год без падения производительности. Старые партиции архивируются в S3.
- ✓Индексированный bookings. Composite index (court_id, start_at) + EXCLUDE constraint. До 100M броней — без замедлений.
- ✓Multi-tenant scope в каждом запросе. Каждый SELECT/UPDATE автоматом фильтруется по organization_id. Шардинг по этому ключу — простой апгрейд позже (Citus).
- ✓Idempotent webhooks. ЮKassa может повторить webhook 10 раз — letscourt запишет один раз. UNIQUE constraint на event_id.
- ✓Read-replica готов. Когда понадобятся аналитические отчёты на больших данных — direct PostgreSQL streaming replication. Изменений в коде минимум.
Перенести сеть на letscourt
Подключаем по одному клубу за раз — параллельно с текущей системой. Когда удостоверились что всё работает — переключаем остальные. Миграция данных, обучение сотрудников, поддержка на старте.
Обсудить миграцию →