315 lines
23 KiB
Markdown
315 lines
23 KiB
Markdown
# ТЕХНИЧЕСКОЕ ЗАДАНИЕ (ТЗ) НА ПЛАТФОРМУ 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 <token>`.
|
||
|
||
## 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 (разработка) |