Схема базы данных
Общие правила
- Первичные ключи: 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:
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
...