# Схема базы данных ## Общие правила - Первичные ключи: UUID (генерация на уровне БД: `gen_random_uuid()`) - Мягкое удаление: поле `deleted_at` (timestamp, null = не удалён) - Аудит: поля `created_at`, `updated_at` (автоматически через триггеры или на уровне приложения) - Все внешние ключи имеют индексы --- ## Таблицы ### 1. users — пользователи | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | email | VARCHAR(255) UNIQUE NOT NULL | Email для входа | | password_hash | VARCHAR(255) NOT NULL | bcrypt хеш | | role | VARCHAR(20) NOT NULL DEFAULT 'customer' | superadmin, moderator, landlord, executor, customer | | status | VARCHAR(20) NOT NULL DEFAULT 'active' | active, banned, pending_verification | | name | VARCHAR(255) | Отображаемое имя | | avatar_url | TEXT | Аватар | | phone | VARCHAR(20) | Телефон | | bio | TEXT | Краткая биография (для исполнителей) | | country | VARCHAR(10) | Код страны (RFC) для провайдера карт/платежей | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | updated_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | deleted_at | TIMESTAMPTZ | Дата мягкого удаления | **Индексы:** - `idx_users_email` UNIQUE on email - `idx_users_role` on role - `idx_users_status` on status ### 2. refresh_tokens — refresh токены | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | user_id | UUID FK → users(id) | | | token_hash | VARCHAR(255) NOT NULL | SHA-256 хеш токена | | expires_at | TIMESTAMPTZ NOT NULL | 30 дней | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | **Индексы:** - `idx_refresh_tokens_token_hash` UNIQUE on token_hash - `idx_refresh_tokens_user_id` on user_id ### 3. tags — теги стилей съёмки (справочник) | Поле | Тип | Описание | |------|-----|----------| | id | VARCHAR(50) PK | Ключ: portrait, fashion, nature, product, wedding, street, interior | | name | VARCHAR(255) NOT NULL | Название: Портрет, Фэшн, Природа | | category | VARCHAR(50) | Группа: style, technique, setting | | sort_order | INT DEFAULT 0 | Порядок сортировки | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | ### 4. features — характеристики мест (справочник) | Поле | Тип | Описание | |------|-----|----------| | id | VARCHAR(50) PK | Ключ: parking, flash, cyclorama, makeup_room, wardrobe | | name | VARCHAR(255) NOT NULL | Название: Парковка, Импульсный свет, Циклорама | | category | VARCHAR(50) | Группа: equipment, amenity, infrastructure | | icon | VARCHAR(50) | Иконка | | sort_order | INT DEFAULT 0 | | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | ### 5. places — места и студии | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | type | VARCHAR(10) NOT NULL | place или studio | | owner_id | UUID FK → users(id) NOT NULL | | | title | VARCHAR(255) NOT NULL | Название | | description | TEXT | Описание | | address | VARCHAR(500) | Адрес | | coordinates | GEOGRAPHY(Point, 4326) NOT NULL | PostGIS точка (lat, lng) | | cover_image | TEXT | URL обложки | | access_info | TEXT | Как добраться, парковка | | status | VARCHAR(20) NOT NULL DEFAULT 'pending_moderation' | draft, pending_moderation, published, rejected, archived | | moderation_comment | TEXT | Комментарий модератора при отклонении | | rating | DECIMAL(2,1) DEFAULT 0 | Средний рейтинг (0.0-5.0) | | reviews_count | INT DEFAULT 0 | Количество отзывов | | hourly_rate | INT | Цена за час (только studio) | | currency | VARCHAR(3) DEFAULT 'RUB' | Валюта (только studio) | | min_hours | INT DEFAULT 1 | Минимальное количество часов (только studio) | | booking_url | TEXT | Ссылка на бронирование (только studio) | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | updated_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | deleted_at | TIMESTAMPTZ | | | published_at | TIMESTAMPTZ | Дата публикации | **Индексы:** - `idx_places_coordinates` GIST on coordinates - `idx_places_type` on type - `idx_places_status` on status - `idx_places_owner_id` on owner_id - `idx_places_rating` on rating - `idx_places_published_at` on published_at ### 6. place_images — изображения мест | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | place_id | UUID FK → places(id) NOT NULL | | | url | TEXT NOT NULL | URL изображения | | alt | VARCHAR(255) | Альтернативный текст | | sort_order | INT DEFAULT 0 | Порядок | | is_cover | BOOLEAN DEFAULT false | | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | **Индексы:** - `idx_place_images_place_id` on place_id ### 7. place_tags — теги мест (M2M) | Поле | Тип | Описание | |------|-----|----------| | place_id | UUID FK → places(id) | | | tag_id | VARCHAR(50) FK → tags(id) | | | PK | (place_id, tag_id) | | ### 8. place_features — характеристики мест (M2M) | Поле | Тип | Описание | |------|-----|----------| | place_id | UUID FK → places(id) | | | feature_id | VARCHAR(50) FK → features(id) | | | PK | (place_id, feature_id) | | ### 9. services — услуги исполнителей (ServiceCard) | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | executor_id | UUID FK → users(id) NOT NULL | | | title | VARCHAR(255) NOT NULL | Название услуги | | description | TEXT | Подробное описание | | price | INT NOT NULL | Цена | | currency | VARCHAR(3) DEFAULT 'RUB' | | | duration_minutes | INT | Длительность | | status | VARCHAR(20) NOT NULL DEFAULT 'published' | draft, published, archived | | rating | DECIMAL(2,1) DEFAULT 0 | | | reviews_count | INT DEFAULT 0 | | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | updated_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | deleted_at | TIMESTAMPTZ | | **Индексы:** - `idx_services_executor_id` on executor_id - `idx_services_status` on status - `idx_services_rating` on rating ### 10. service_images — портфолио услуг | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | service_id | UUID FK → services(id) NOT NULL | | | url | TEXT NOT NULL | | | alt | VARCHAR(255) | | | sort_order | INT DEFAULT 0 | | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | ### 11. service_tags — теги услуг (M2M) | Поле | Тип | Описание | |------|-----|----------| | service_id | UUID FK → services(id) | | | tag_id | VARCHAR(50) FK → tags(id) | | | PK | (service_id, tag_id) | | ### 12. reviews — отзывы | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | user_id | UUID FK → users(id) NOT NULL | Кто оставил | | target_type | VARCHAR(10) NOT NULL | place или service | | target_id | UUID NOT NULL | ID места или услуги | | rating | SMALLINT NOT NULL CHECK (1-5) | | | text | TEXT | Текст отзыва | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | updated_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | deleted_at | TIMESTAMPTZ | | **Индексы:** - `idx_reviews_target` on (target_type, target_id) - `idx_reviews_user_id` on user_id - UNIQUE on (user_id, target_type, target_id) — один отзыв на цель ### 13. bookings — бронирования студий | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | place_id | UUID FK → places(id) NOT NULL | | | user_id | UUID FK → users(id) NOT NULL | Кто забронировал | | start_time | TIMESTAMPTZ NOT NULL | | | end_time | TIMESTAMPTZ NOT NULL | | | status | VARCHAR(20) NOT NULL DEFAULT 'pending' | pending, confirmed, cancelled, completed | | total_price | INT | Итоговая цена | | currency | VARCHAR(3) DEFAULT 'RUB' | | | comment | TEXT | Комментарий к брони | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | updated_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | **Индексы:** - `idx_bookings_place_id` on place_id - `idx_bookings_user_id` on user_id - `idx_bookings_time` on (start_time, end_time) ### 14. moderation_log — лог модерации | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | moderator_id | UUID FK → users(id) NOT NULL | | | target_type | VARCHAR(20) NOT NULL | place, service | | target_id | UUID NOT NULL | | | action | VARCHAR(20) NOT NULL | approve, reject, request_changes, publish, archive | | comment | TEXT | Комментарий | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | **Индексы:** - `idx_moderation_log_target` on (target_type, target_id) - `idx_moderation_log_moderator_id` on moderator_id ### 15. payments — платежи и подписки | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | user_id | UUID FK → users(id) NOT NULL | | | type | VARCHAR(20) NOT NULL | subscription, donation, listing_fee, booking_commission | | provider | VARCHAR(20) NOT NULL | cloudpayments, stripe | | provider_payment_id | VARCHAR(255) | ID платежа у провайдера | | amount | INT NOT NULL | Сумма в копейках | | currency | VARCHAR(3) DEFAULT 'RUB' | | | status | VARCHAR(20) NOT NULL | pending, succeeded, failed, refunded | | metadata | JSONB | Доп. данные | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | updated_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | **Индексы:** - `idx_payments_user_id` on user_id - `idx_payments_status` on status - `idx_payments_provider_id` on (provider, provider_payment_id) ### 16. subscriptions — подписки | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | user_id | UUID FK → users(id) NOT NULL | | | plan | VARCHAR(50) NOT NULL | studio_listing, executor_promo, premium_user | | status | VARCHAR(20) NOT NULL | active, cancelled, expired | | current_period_start | TIMESTAMPTZ | | | current_period_end | TIMESTAMPTZ | | | provider_subscription_id | VARCHAR(255) | | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | | updated_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | ### 17. visitor_sessions — сессии посетителей на карте | Поле | Тип | Описание | |------|-----|----------| | id | UUID PK | | | user_id | UUID FK → users(id) | NULL для гостей | | coordinates | GEOGRAPHY(Point, 4326) NOT NULL | | | last_active_at | TIMESTAMPTZ NOT NULL | | | created_at | TIMESTAMPTZ NOT NULL DEFAULT now() | | **Индексы:** - `idx_visitor_sessions_coordinates` GIST on coordinates - `idx_visitor_sessions_last_active` on last_active_at --- ## PostGIS Для работы с географическими данными необходимо расширение PostGIS: ```sql CREATE EXTENSION IF NOT EXISTS postgis; ``` ## Миграции Миграции будут выполняться через `golang-migrate/migrate`. Файлы миграций: `backend/migrations/`: ``` backend/migrations/ 000001_create_extensions.up.sql 000001_create_extensions.down.sql 000002_create_users.up.sql 000002_create_users.down.sql 000003_create_tags_features.up.sql 000003_create_tags_features.down.sql 000004_create_places.up.sql 000004_create_places.down.sql ... ```