PostgreSQL 16+. Все primary keys — UUID. Все временны́е метки хранятся в UTC.
Users & Auth
users
| Колонка | Тип | Описание |
|---|
id | UUID (PK) | |
email | string | Уникальный |
phone | string | Уникальный |
role | enum | client · professional · admin |
encrypted_password | string | bcrypt |
confirmed_at | timestamp | Email-подтверждение |
discarded_at | timestamp | Soft delete |
Владелец салона определяется через salons.owner_id, не через role.
profiles
| Колонка | Тип |
|---|
user_id | UUID (FK → users) |
first_name | string |
last_name | string |
avatar_url | string |
bio | text |
locale | string |
oauth_identities
| Колонка | Тип | Описание |
|---|
id | UUID (PK) | |
user_id | UUID (FK) | → users.id |
provider | string | "google" и другие в будущем |
uid | string | Уникальный ID у провайдера (sub) |
created_at | timestamp | |
updated_at | timestamp | |
Уникальный индекс: provider + uid.
Salons & Services
salons
| Колонка | Тип | Описание |
|---|
id | UUID (PK) | |
owner_id | UUID (FK) | → users.id |
name | string | |
slug | string | URL-friendly, уникальный |
address | string | |
lat, lng | decimal | Координаты |
phone | string | |
status | enum | pending · active · suspended |
photo_url | string | |
reviews_count | integer | Counter cache |
rating | decimal | Средний рейтинг |
currency | string | ISO 4217, по умолчанию MDL |
timezone | string | IANA, по умолчанию Europe/Chisinau |
cancellation_hours_before | integer | По умолчанию 24 |
cancellation_fee_percent | decimal | По умолчанию 0 |
auto_cancel_after_min | integer | nullable |
salon_services (AR model: Service)
| Колонка | Тип |
|---|
id | UUID (PK) |
salon_id | UUID (FK) |
name | string |
description | text |
duration_min | integer |
category_id | integer (FK → categories) |
Уникальный индекс: salon_id + name.
service_master_prices (AR model: ServiceMasterPrice)
| Колонка | Тип |
|---|
id | UUID (PK) |
salon_id | UUID (FK) |
service_id | UUID (FK → salon_services) |
master_id | UUID (FK → users) |
price | decimal |
Уникальный индекс: service_id + master_id.
categories
| Колонка | Тип |
|---|
id | integer |
name | string |
slug | string |
parent_id | integer (self-join, nullable) |
position | integer |
Memberships & Master Profiles
salon_memberships
| Колонка | Тип | Описание |
|---|
id | UUID (PK) | |
salon_id | UUID (FK) | |
user_id | UUID (FK) | |
role | enum | master · receptionist |
status | enum | pending · active · deactivated |
invite_token | string | Для принятия приглашения |
invited_at | timestamp | |
accepted_at | timestamp | nullable |
Уникальный индекс: salon_id + user_id.
master_profiles
| Колонка | Тип |
|---|
id | UUID (PK) |
user_id | UUID (FK) |
specialization | string |
experience_years | integer |
instagram_url | string |
portfolio_url | string |
reviews_count | integer |
rating | decimal |
Scheduling & Bookings
working_hours
| Колонка | Тип | Описание |
|---|
salon_id | UUID (FK) | |
membership_id | UUID (FK → salon_memberships, nullable) | null = весь салон |
day_of_week | integer | 0=вс, 1=пн ... 6=сб |
start_time | time | |
end_time | time | |
appointments
| Колонка | Тип | Описание |
|---|
id | UUID (PK) | |
client_id | UUID (FK) | → users.id |
salon_id | UUID (FK) | |
master_id | UUID (FK) | → users.id |
service_id | UUID (FK) | → salon_services.id |
starts_at | timestamp | |
ends_at | timestamp | |
status | string | AASM: confirmed/in_progress/completed/reviewed/cancelled/no_show |
price_snapshot | decimal | Цена на момент бронирования (заморожена) |
duration_min_snapshot | integer | Длительность на момент бронирования |
total_amount | decimal | |
Reviews & Favorites
reviews
| Колонка | Тип |
|---|
id | UUID (PK) |
appointment_id | UUID (FK) |
client_id | UUID (FK) |
salon_id | UUID (FK) |
master_id | UUID (FK) |
rating | integer |
body | text |
favorites
| Колонка | Тип |
|---|
id | UUID (PK) |
user_id | UUID (FK) |
salon_id | UUID (FK) |
Уникальный индекс: user_id + salon_id.