Перейти к основному содержимому

Модель данных ArtHunt

СУБД: PostgreSQL 15
Схема: arthunt

Диаграмма схемы доступна на dbdiagram.io.

Обзор сущностей

ГруппаТаблицы
Пользователиusers, email_tokens
Специалистыspecialists, portfolio_items, portfolio_media
Тегиtags, specialist_tags, portfolio_item_tags, project_tags
Проектыprojects, project_deliverables
Матчингresponses, response_portfolio_items, invitations
Отзывыreviews
Поддержкаsupport_tickets, support_messages

Пользователи

users

Центральная таблица всех пользователей системы.

CREATE TABLE users (
user_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
email varchar(255) UNIQUE NOT NULL,
password_hash varchar(255) NOT NULL,
role varchar(20) NOT NULL, -- 'specialist' | 'client' | 'moderator'
is_active boolean NOT NULL DEFAULT true,
is_blocked boolean NOT NULL DEFAULT false,
email_confirmed_at timestamp,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- Индексы
CREATE INDEX ON users (role);
CREATE INDEX idx_users_flags ON users (is_active, is_blocked);

email_tokens

Токены для подтверждения email и сброса пароля.

CREATE TABLE email_tokens (
token_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
user_id int NOT NULL REFERENCES users(user_id),
token_hash varchar(255) UNIQUE NOT NULL,
purpose varchar(20) NOT NULL, -- 'email_confirm' | 'password_reset'
expires_at timestamp NOT NULL,
used_at timestamp,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Специалисты и портфолио

specialists

Профиль специалиста (1:1 с users).

CREATE TABLE specialists (
specialist_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
user_id int UNIQUE NOT NULL REFERENCES users(user_id),
name varchar(100) NOT NULL,
specialization varchar(50) NOT NULL,
-- 'photographer','videographer','graphic_designer','ui_ux_designer',
-- 'motion_designer','illustrator','copywriter','screenwriter','musician','other'
city varchar(100),
bio text,
min_price numeric(10,2),
max_price numeric(10,2),
avatar_url varchar(500),
avg_rating numeric(3,2) DEFAULT 0,
portfolio_count int NOT NULL DEFAULT 0,
is_moderated boolean NOT NULL DEFAULT false,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

portfolio_items

Работы в портфолио. Каждый специалист должен иметь ≥3 одобренных работ чтобы появиться в поиске.

CREATE TABLE portfolio_items (
item_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
specialist_id int NOT NULL REFERENCES specialists(specialist_id),
title varchar(200) NOT NULL,
description text,
moderation_status varchar(20) NOT NULL DEFAULT 'pending',
-- 'pending' | 'approved' | 'rejected'
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

portfolio_media

Медиафайлы работ (изображения, видео). Хранятся в Object Storage (S3/MinIO), в БД — только URL.

CREATE TABLE portfolio_media (
media_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
item_id int NOT NULL REFERENCES portfolio_items(item_id),
storage_url varchar(500) NOT NULL,
mime_type varchar(100) NOT NULL,
size_bytes bigint NOT NULL,
order_index int NOT NULL DEFAULT 0,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Справочник тегов

tags

Справочник тегов — нормализованная таблица, используется для поиска и фильтрации.

CREATE TABLE tags (
tag_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name varchar(50) NOT NULL,
category varchar(20) NOT NULL, -- 'style','genre','technique','mood','format','color'
slug varchar(60) UNIQUE NOT NULL
);

Связующие таблицы (M2M)

-- Теги специалиста
CREATE TABLE specialist_tags (
specialist_id int NOT NULL REFERENCES specialists(specialist_id),
tag_id int NOT NULL REFERENCES tags(tag_id),
PRIMARY KEY (specialist_id, tag_id)
);

-- Теги работы портфолио
CREATE TABLE portfolio_item_tags (
item_id int NOT NULL REFERENCES portfolio_items(item_id),
tag_id int NOT NULL REFERENCES tags(tag_id),
PRIMARY KEY (item_id, tag_id)
);

-- Теги проекта
CREATE TABLE project_tags (
project_id int NOT NULL REFERENCES projects(project_id),
tag_id int NOT NULL REFERENCES tags(tag_id),
PRIMARY KEY (project_id, tag_id)
);

Проекты

projects

Проекты заказчиков. Проходят модерацию перед публикацией.

CREATE TABLE projects (
project_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
client_id int NOT NULL REFERENCES users(user_id),
title varchar(200) NOT NULL,
description text NOT NULL,
status varchar(20) NOT NULL DEFAULT 'draft',
-- 'draft','open','in_progress','closed','cancelled'
min_budget numeric(12,2),
max_budget numeric(12,2),
deadline_days int,
max_responses int NOT NULL DEFAULT 20,
responses_count int NOT NULL DEFAULT 0,
moderation_status varchar(20) NOT NULL DEFAULT 'pending',
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
published_at timestamp
);

Матчинг

responses

Отклики специалистов на проекты. Ограничение: 1 отклик на пару (проект, специалист).

CREATE TABLE responses (
response_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
project_id int NOT NULL REFERENCES projects(project_id),
specialist_id int NOT NULL REFERENCES specialists(specialist_id),
cover_letter text NOT NULL,
proposed_price numeric(12,2),
proposed_deadline_days int,
status varchar(20) NOT NULL DEFAULT 'pending',
-- 'pending','accepted','rejected'
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT uq_response_per_pair UNIQUE (project_id, specialist_id)
);

invitations

Приглашения заказчиков к специалистам. Ограничение: 1 приглашение на пару (проект, специалист).

CREATE TABLE invitations (
invitation_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
project_id int NOT NULL REFERENCES projects(project_id),
client_id int NOT NULL REFERENCES users(user_id),
specialist_id int NOT NULL REFERENCES specialists(specialist_id),
message text,
status varchar(20) NOT NULL DEFAULT 'pending',
rejection_reason varchar(255),
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT uq_invitation_per_pair UNIQUE (project_id, specialist_id)
);

Отзывы

reviews

Отзывы после завершения проектов. Ограничение: 1 отзыв на (проект, автор).

CREATE TABLE reviews (
review_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
project_id int NOT NULL REFERENCES projects(project_id),
reviewer_id int NOT NULL REFERENCES users(user_id),
target_id int NOT NULL REFERENCES users(user_id),
rating int NOT NULL CHECK (rating BETWEEN 1 AND 5),
text text,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT uq_review_per_author UNIQUE (project_id, reviewer_id)
);

Поддержка

support_tickets и support_messages

CREATE TABLE support_tickets (
ticket_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
user_id int NOT NULL REFERENCES users(user_id),
topic varchar(200) NOT NULL,
status varchar(20) NOT NULL DEFAULT 'open', -- 'open','pending','closed'
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE support_messages (
message_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
ticket_id int NOT NULL REFERENCES support_tickets(ticket_id),
author_id int NOT NULL REFERENCES users(user_id),
text text NOT NULL,
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);

ER-диаграмма

users ──────────────────────────────────────────────┐
│ │
├──► specialists ──► portfolio_items ──► portfolio_media
│ │ │
│ ├──► specialist_tags portfolio_item_tags
│ │ │ │
│ │ tags ◄────────────────────┘
│ │ │
│ │ project_tags
│ │ │
├──► projects ───────┘
│ │
│ ├──► project_deliverables
│ ├──► responses ──► response_portfolio_items
│ ├──► invitations
│ └──► reviews

└──► support_tickets ──► support_messages

Ключевые индексы

ТаблицаИндексНазначение
users(is_active, is_blocked)Фильтрация активных пользователей
specialists(is_moderated, avg_rating)Каталог специалистов
specialists(city, specialization)Фильтрация по городу и специализации
portfolio_items(specialist_id, moderation_status)Портфолио специалиста
projects(status, published_at)Лента проектов
responsesUNIQUE(project_id, specialist_id)Один отклик на пару
invitationsUNIQUE(project_id, specialist_id)Одно приглашение на пару