# ТЕХНИЧЕСКОЕ ЗАДАНИЕ (ТЗ) НА ПЛАТФОРМУ EVENTHUB Версия: 1.5 (актуальная реализация: выполнены задачи #12–#17) ## 1. ЦЕЛИ И НАЗНАЧЕНИЕ EventHub — платформа для управления событиями с поддержкой календарей, записи участников (включая специалистов), гибкого подтверждения, рейтингов, отзывов, модерации, встроенного баг-трекера и платной подписки. Целевая аудитория: владельцы календарей (бизнес), участники (клиенты), администраторы. ## 2. ФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ ### 2.1. Календари - CRUD календаря (название, описание, теги, владелец) - Расшаривание по ссылке (публичная/приватная) - Приглашение пользователей с правами "запись" или "администрирование" - Типы календарей: personal (бесплатный, без записи), commercial (платный, запись клиентов, специалисты) - Гибкое подтверждение заявок: auto (автоматически), manual (вручную), timeout (авто через N секунд) - Теги календаря, рейтинг (средняя оценка, количество голосов) **Новые поля (задача #12):** - `short_name` — короткое уникальное имя для API и поиска - `category` — категория (enum) - `color` — цвет отображения - `image_url` — изображение календаря - `settings` — дополнительные настройки (map) ### 2.1.1. Специалисты календаря (задача #12) Реализована отдельная таблица `calendar_specialist`, связывающая пользователя (специалиста) с календарём. Специалист может иметь отображаемое имя (`name`) и список специализаций (`specialization`). Статус специалиста: `active` | `inactive`. ### 2.2. События (расширенная версия с повторяющимися событиями) #### 2.2.1. Типы событий и модель хранения События могут быть одиночными (`event_type = single`) или повторяющимися (`event_type = recurring`). Для повторяющихся событий: - Мастер-событие содержит правило повторения (`recurrence_rule`) и является шаблоном. - При создании повторяющегося события генерируются экземпляры (instances) на определённый период (например, на месяц вперёд), которые хранятся как отдельные записи с полем `is_instance = true` и ссылкой на мастер (`master_id`). - При изменении мастера можно выбрать обновление всех будущих экземпляров или только мастер-записи. - При удалении мастера удаляются все связанные экземпляры. - Для поддержки исключений (отмена отдельного вхождения) используется таблица `recurrence_exception`. #### 2.2.2. Правила генерации вхождений при поиске При поиске событий на заданный диапазон дат система должна: - Включать все одиночные события, попадающие в диапазон. - Для повторяющихся событий генерировать виртуальные вхождения на основе `recurrence_rule`, исключая те, что помечены как исключения. - Возвращать как одиночные, так и сгенерированные вхождения в едином списке. #### 2.2.3. Материализация при записи участника При записи участника на конкретное вхождение повторяющегося события: - Система материализует (создаёт) физическую запись события для этого вхождения, если оно ещё не было материализовано (например, для хранения количества записавшихся). - Материализованное событие имеет `is_instance = true` и ссылается на `master_id`. - Запись участника (`booking`) всегда привязывается к конкретному экземпляру (материализованному или одиночному событию). #### 2.2.4. Изменение и удаление серий - При редактировании мастера можно применить изменения ко всем будущим экземплярам или создать новый мастер с отдельной серией. - При удалении мастера удаляются все связанные экземпляры, если на них нет активных записей. Если есть активные записи, мастер-событие помечается как `cancelled`, а существующие записи остаются. #### 2.2.5. Структура записей (records.hrl) Актуальная структура записей включает дополнительные поля, добавленные в рамках задачи #12: - `event` — добавлены `attachments :: [binary()] | undefined`, `edit_history :: [map()] | undefined` - `booking` — добавлены `notes :: binary() | undefined`, `reminder_sent :: boolean()` - `review` — добавлены `likes :: non_neg_integer()`, `dislikes :: non_neg_integer()`, `edited_at :: calendar:datetime() | undefined` #### 2.2.6. Требования к реализации - Все операции с событиями должны быть транзакционными. - Генерация вхождений должна быть эффективной (использовать `calendar:datetime_to_gregorian_seconds` и кэширование). - При поиске событий для календаря учитывать права доступа пользователя. ### 2.3. Запись участников и подтверждение - Пользователь может отправить заявку на участие в событии. - В зависимости от `confirmation` календаря заявка либо подтверждается автоматически, либо ожидает ручного подтверждения владельцем, либо подтверждается по таймауту. - Бронирование имеет статусы: `pending`, `confirmed`, `cancelled`. - Пользователь может отменить свою запись. - Владелец календаря может подтвердить или отклонить заявку. - При подтверждении фиксируется время (`confirmed_at`). - Вместимость события (`capacity`) ограничивает количество подтверждённых записей. ### 2.4. Отзывы и рейтинги - Пользователи могут оставлять отзывы (рейтинг 1–5 и комментарий) на события или календари. - Отзыв можно редактировать (сохраняется `edited_at`). - Отзывы могут быть скрыты модератором. - При добавлении/изменении/удалении отзыва пересчитывается средний рейтинг события (`rating_avg`, `rating_count`). - Реализованы лайки/дизлайки отзывов (`likes`, `dislikes`). - Возможность пожаловаться на отзыв (создание `report`). ### 2.5. Поиск и фильтрация - Полнотекстовый поиск по названиям событий, календарей, тегам. - Фильтрация по дате, категории, местоположению, рейтингу. - Пагинация результатов. - Поиск должен учитывать права доступа (не показывать скрытые/заблокированные календари). ### 2.6. Расширенные возможности - Локация события (`location` запись с адресом, широтой, долготой). - Онлайн-ссылка (`online_link`). - Вложения к событию (`attachments`). - История изменений события (`edit_history`). - Заметки пользователя к бронированию (`notes`). - Напоминания о событии (поле `reminder_sent` в бронировании, логика отправки не реализована). ### 2.7. Модерация и безопасность - Пользователи могут отправлять жалобы (`report`) на календари, события, отзывы. - Модераторы могут просматривать жалобы и принимать меры (скрывать контент, блокировать). - Список запрещённых слов (`banned_word`) для фильтрации контента. - Аудит действий администраторов (`admin_audit`). ### 2.8. Баг-трекер (автоматический) - При возникновении ошибок сервер автоматически создаёт тикет (`ticket`). - Тикет содержит хеш ошибки, сообщение, стектрейс, контекст, количество повторений. - Администраторы могут просматривать тикеты, назначать ответственных, менять статус. ### 2.9. Платная подписка - Пользователи могут оформить подписку (`subscription`) с разными планами: monthly, quarterly, biannual, annual. - Статус подписки: `active`, `expired`, `cancelled`. - Отслеживание использования пробного периода (`trial_used`). ### 2.10. Административная панель - Управление пользователями (просмотр, блокировка, изменение ролей). - Просмотр статистики платформы. - Управление жалобами и тикетами. - Управление подписками. - Аудит действий администраторов. #### 2.10.1. Ролевая модель администраторов - `superadmin` — полный доступ, может управлять другими администраторами. - `admin` — управление пользователями, контентом, подписками. - `moderator` — модерация контента (жалобы, отзывы). - `support` — работа с тикетами. #### 2.10.2. Эндпоинты для управления ролями и аудитом - `GET /v1/admin/me` — информация о текущем администраторе. - `GET /v1/admin/admins` — список администраторов (только для superadmin). - `POST /v1/admin/admins/:id` — изменение роли администратора (superadmin). - `GET /v1/admin/audit` — просмотр аудита. #### 2.10.3. Статистика для дашборда с учётом ролей - `GET /v1/admin/stats` — базовая статистика (количество пользователей, событий, бронирований). - В будущем планируется расширенная аналитика на основе данных из таблицы `stats`. ### 2.11. Real-time уведомления (WebSocket) - Пользовательский WebSocket (порт 8081): подписка на обновления событий календаря. - Административный WebSocket (порт 8446): подписка на новые жалобы и тикеты. - Создана таблица `notification` для хранения уведомлений (тип, заголовок, тело, флаг прочитано). Полноценная логика доставки уведомлений будет реализована позже. ### 2.12. Инфраструктура развертывания (Docker Compose) - Docker Swarm с тремя репликами `eventhub-node{1..3}`. - Traefik в качестве reverse-прокси и балансировщика. - Prometheus для сбора метрик, Grafana для визуализации. - Observer Web для мониторинга Erlang-узлов. - Автоматическая ротация логов. ## 3. НЕФУНКЦИОНАЛЬНЫЕ ТРЕБОВАНИЯ ### 3.1. Производительность и масштабирование - Поддержка 100 000+ пользователей. - Горизонтальное масштабирование добавлением новых узлов. - Все персистентные таблицы хранятся в `disc_copies` на каждом узле (задача #13). - Сессионные таблицы (`session`, `admin_session`) оставлены в `ram_copies` для скорости. - Индексы созданы для часто запрашиваемых полей (calendar_id, start_time, event_type, status и др.) (задача #13). - Полная репликация горячих таблиц между всеми узлами кластера (задача #14). - Автоматическое обнаружение узлов через DNS-имя `eventhub-node` или статический список (задача #14). - Периодическая очистка «мёртвых» узлов из схемы Mnesia (каждые 30 секунд) (задача #14). - Архивирование исторических данных (старше 30 дней) в отдельные Mnesia-узлы с `disc_only_copies` (задача #15). - Пагинация всех списков. ### 3.2. Надёжность - Супервизорное дерево OTP. - Философия "let it crash" — быстрый перезапуск упавших процессов. - Автоматическое восстановление после падения ноды (Mnesia). - Валидация входных данных и единый формат ошибок. - Механизм миграций схемы данных: миграции компилируются вместе с проектом и автоматически применяются при старте, поддерживается откат (задача #17). ### 3.3. Безопасность - JWT-аутентификация с разделением ролей (user, admin, superadmin, moderator, support). - Проверка прав доступа к календарям и событиям. - Пароли хэшируются с использованием Argon2. - Защита от несанкционированного просмотра архивных данных (только владелец календаря) (задача #15). ### 3.4. Наблюдаемость - Логирование в JSON-формате. - Экспорт метрик для Prometheus (HTTP-эндпоинт `/metrics`). - Встроенный Observer Web для мониторинга Erlang-системы. - Сбор статистики использования (события, бронирования, отзывы) через триггеры Mnesia с сохранением в таблице `stats` (задача #16). ### 3.5. CI/CD - Контейнеризация (Docker). - Makefile для автоматизации задач. - Возможность развёртывания в Kubernetes (в будущем). ## 4. СТЕК ТЕХНОЛОГИЙ (С ВЕРСИЯМИ) - Erlang/OTP 28 - Mnesia (встроенная БД) - Cowboy 2.12 (HTTP-сервер) - JWT (jose 1.11.10) - Prometheus (prometheus 4.11.0, prometheus_cowboy 2.1.0) - Docker Compose v3.8 - Traefik v3.1 - Grafana 11.2 - Prometheus 2.55 - Observer Web - Logrotate ## 5. ИЕРАРХИЧЕСКАЯ СТРУКТУРА КОДА ``` src/ ├── core/ — бизнес-логика (core_user, core_event, core_booking, core_review, ...) ├── handlers/ — обработчики HTTP (handler_login, handler_calendar_view, ...) ├── infra/ — инфраструктура (infra_mnesia, infra_sup, cluster_discovery, │ archive_manager, archive_controller, stats_collector, │ migration_engine, ...) ├── archive/ — архивирование и рендеринг (archive_controller, archive_manager, │ calendar_html_renderer, archive_fetcher) ├── migrations/ — файлы миграций └── eventhub_app.erl — точка входа приложения ``` ## 6. ОСНОВНЫЕ API (КРАТКО) ### Пользовательские (порт 8080) - `POST /v1/register` — регистрация. - `POST /v1/login` — вход. - `POST /v1/refresh` — обновление токена. - `GET /v1/user/me` — профиль пользователя. - `GET /v1/user/bookings` — бронирования пользователя. - `GET /v1/user/reviews` — отзывы пользователя. - `GET /v1/search` — поиск. - `GET /v1/calendars` — список календарей. - `GET /v1/calendars/:id` — календарь. - `GET /v1/calendars/:calendar_id/events` — события календаря. - `GET /v1/events/:id` — событие. - `GET /v1/events/:id/occurrences` — вхождения повторяющегося события. - `POST /v1/events/:id/bookings` — запись на событие. - `GET /v1/bookings/:id` — статус бронирования. - `POST /v1/reviews` — создать отзыв. - `GET /v1/reviews` — список отзывов. - `PUT /v1/reviews/:id` — обновить отзыв. - `POST /v1/reports` — пожаловаться. - `GET /v1/tickets` — тикеты пользователя. - `POST /v1/tickets` — создать тикет. - `GET /v1/tickets/:id` — статус тикета. - `GET /v1/subscription` — подписка пользователя. - `GET /v1/calendars/:calendar_id/view?month=YYYY-MM` — HTML-календарь (владелец), включая архив. ### Административные (порт 8445) - `GET /v1/admin/health` — состояние сервера. - `GET /v1/admin/stats` — статистика. - `POST /v1/admin/login` — вход администратора. - `GET /v1/admin/users`, `GET /v1/admin/users/:id` — пользователи. - `GET /v1/admin/reports`, `GET /v1/admin/reports/:id` — жалобы. - `DELETE /v1/admin/reviews/:id` — удалить отзыв. - `GET /v1/admin/banned-words`, `POST /v1/admin/banned-words` — запрещённые слова. - `GET /v1/admin/tickets/stats` — статистика тикетов. - `GET /v1/admin/tickets`, `GET /v1/admin/tickets/:id` — управление тикетами. - `GET /v1/admin/subscriptions`, `POST /v1/admin/subscriptions/:id` — подписки. - `PUT /v1/admin/:target_type/:id` — модерация. - `GET /v1/admin/me` — профиль администратора. - `GET /v1/admin/admins`, `POST /v1/admin/admins/:id` — управление администраторами. - `GET /v1/admin/audit` — аудит. ## 6.1. Аутентификация и авторизация - Пользователи и администраторы используют разные эндпоинты и JWT-токены. - Access-токен имеет срок жизни 1 час, refresh-токен — 30 дней. - Все защищённые эндпоинты требуют заголовок `Authorization: Bearer `. ## 7. ВЕРСИОНИРОВАНИЕ И СТАТУС Текущая версия: 1.5 (MVP, альфа). Включает: - Гибридную модель повторяющихся событий - Раздельную JWT-аутентификацию (пользователи/администраторы) - Полноценную ролевую модель администрирования - Версионированное админ-API (/v1/admin/...) - Расширенную инфраструктуру (Traefik, WAF, мониторинг, failover) - Аудит действий администраторов - Расширенную статистику для дашборда - Улучшенную обработку ошибок и валидацию **Новое в версии 1.5:** - Расширенная структура данных (новые поля и таблицы, задача #12) - Дисковое хранение и индексы (задача #13) - Репликация между узлами кластера с автоочисткой (задача #14) - Архивирование исторических данных и серверный рендеринг календаря (задача #15) - Сбор статистики через триггеры Mnesia (задача #16) - Механизм миграций схемы данных (задача #17) ## 8. ОГРАНИЧЕНИЯ И ДОПУЩЕНИЯ - Система не поддерживает транзакционную целостность между несколькими таблицами на уровне приложения (полагаемся на Mnesia). - В текущей версии отсутствует полноценная система уведомлений (только таблица). - Загрузка файлов (вложения) пока не реализована. - Серверный рендеринг календаря работает только для владельца календаря. - Автоматическое архивирование через `archive_controller` в локальном режиме использует `slave:start`, который устарел; в production планируется `peer`. ## 9. ТРЕБОВАНИЯ К ОКРУЖЕНИЮ - Erlang/OTP 28 - Docker Engine 27.3.1+ - Docker Compose v3.8 - Linux (продакшен) или WSL2 (разработка)