Бэкенд (Kotlin / Spring Boot)
Технологический стек
| Компонент |
Технология |
Назначение |
| Язык |
Kotlin 1.9.24 |
Основной язык разработки |
| Фреймворк |
Spring Boot 3.2.5 |
REST API, DI, конфигурация |
| ORM |
Spring Data JPA |
Доступ к данным |
| Безопасность |
Spring Security + JJWT 0.12.5 |
JWT-авторизация |
| Миграции |
Liquibase |
Версионирование схемы БД |
| БД |
PostgreSQL 15 |
Реляционное хранилище |
| Объектное хранилище |
MinIO SDK 8.5.9 |
S3-совместимый доступ к медиа |
| Документация |
SpringDoc OpenAPI 2.5.0 |
Swagger UI |
| JDK |
21 |
Среда выполнения |
Роль бэкенда
Бэкенд выполняет функции:
- CRUD-операции — управление пользователями, вещами, образами, медиафайлами
- Авторизация — регистрация, вход, JWT access/refresh токены
- Медиа-проксирование — загрузка файлов в MinIO, отдача URL
- Интеграция с ML — отправка фото на классификацию при загрузке, получение предсказаний
- Фид и рекомендации — формирование ленты образов с фильтрацией и пагинацией
- Шеринг — генерация токенов, публичный доступ к образам, счётчики просмотров/забираний
- Справочники — предоставление каталогов цветов, категорий, слотов, стилей, коллекций
Эндпоинты API
Аутентификация (/api/auth)
| Метод |
Путь |
Описание |
| POST |
/auth/register |
Регистрация нового пользователя |
| POST |
/auth/login |
Вход (email/телефон + пароль) |
| POST |
/auth/refresh |
Обновление access-токена |
Пользователи (/api/users)
| Метод |
Путь |
Описание |
| GET |
/users/me |
Профиль текущего пользователя |
| PATCH |
/users/me |
Обновление профиля (имя, аватар) |
| POST |
/users/me/password |
Смена пароля |
| GET |
/users/me/shared-outfits |
Список расшаренных образов |
| Метод |
Путь |
Описание |
| POST |
/media |
Загрузка медиафайла (+ ML-предсказания) |
| GET |
/media/{media_id} |
Метаданные медиа |
Вещи (/api/items)
| Метод |
Путь |
Описание |
| GET |
/items |
Список вещей (фильтры: категория, цвет, сезон, стиль, слот, статус) |
| POST |
/items |
Добавление вещи |
| GET |
/items/{item_id} |
Просмотр вещи |
| PATCH |
/items/{item_id} |
Редактирование вещи |
| DELETE |
/items/{item_id} |
Удаление вещи |
Образы (/api/outfits)
| Метод |
Путь |
Описание |
| GET |
/outfits |
Список образов пользователя |
| POST |
/outfits |
Создание образа |
| GET |
/outfits/{outfit_id} |
Просмотр образа |
| PATCH |
/outfits/{outfit_id} |
Обновление образа |
| DELETE |
/outfits/{outfit_id} |
Удаление образа |
| GET |
/outfits/saved |
Сохранённые (избранные) образы |
| POST |
/outfits/{outfit_id}/save |
Добавить в избранное |
| DELETE |
/outfits/{outfit_id}/save |
Убрать из избранного |
| POST |
/outfits/{outfit_id}/share |
Создать ссылку для шеринга |
| GET |
/outfits/{outfit_id}/similar |
Похожие образы |
| POST |
/outfits/suggest-title |
Генерация названия образа |
Лента (/api/feed)
| Метод |
Путь |
Описание |
| GET |
/feed |
Лента образов (фильтры: occasion, style, weather, season, collection; курсорная пагинация) |
Шеринг (/api/share)
| Метод |
Путь |
Описание |
| GET |
/share/{token} |
Просмотр образа по ссылке (публичный, +view_count) |
| POST |
/share/{token} |
Забрать образ себе (+claim_count) |
| DELETE |
/share/{token} |
Закрыть (отозвать) ссылку |
Справочники (/api/reference)
| Метод |
Путь |
Описание |
| GET |
/reference/colors |
Справочник цветов (11 базовых) |
| GET |
/reference/categories |
Категории вещей (древовидные) |
| GET |
/reference/slots |
Слоты одежды (top, bottom, shoes, outerwear, accessory, bag, dress) |
| GET |
/reference/styles |
Стили (pattern, aesthetic, fit, season, other) |
| GET |
/reference/collections |
Подборки для ленты (daily_mix, university, date, office, sport) |
Итого: 28 эндпоинтов (3 auth + 4 users + 2 media + 5 items + 11 outfits + 1 feed + 3 share + 5 reference).
Модель данных
erDiagram
users {
UUID id PK
VARCHAR name
VARCHAR email
VARCHAR phone
VARCHAR password_hash
UUID avatar_media_id FK
TIMESTAMPTZ created_at
}
media {
UUID id PK
UUID owner_user_id FK
TEXT url
VARCHAR mime_type
VARCHAR kind "avatar | item_photo | outfit_photo | collage | other"
INT width
INT height
TIMESTAMPTZ created_at
}
items {
UUID id PK
UUID owner_user_id FK
VARCHAR title
INT category_id FK
INT primary_color_id FK
UUID primary_media_id FK
VARCHAR status "active | archived | deleted"
BOOLEAN excluded_from_recommendations
TIMESTAMPTZ created_at
TIMESTAMPTZ updated_at
}
outfits {
UUID id PK
UUID owner_user_id FK
VARCHAR title
VARCHAR visibility "private | public | unlisted"
UUID cover_media_id FK
VARCHAR origin_type "manual | imported | shared | generated"
UUID origin_outfit_id FK
TIMESTAMPTZ created_at
TIMESTAMPTZ updated_at
}
outfit_items {
UUID id PK
UUID outfit_id FK
UUID item_id FK
INT slot_id FK
INT layer_index
}
outfit_item_snapshots {
UUID id PK
UUID outfit_id FK
INT slot_id FK
INT layer_index
INT category_id FK
INT color_id FK
UUID media_id FK
VARCHAR title
}
outfit_share_links {
UUID id PK
UUID outfit_id FK
VARCHAR token "unique"
INT view_count
INT claim_count
TIMESTAMPTZ expires_at
}
user_saved_outfits {
UUID user_id FK
UUID outfit_id FK
TIMESTAMPTZ created_at
}
slot_defs {
SERIAL id PK
VARCHAR code "top | bottom | shoes | outerwear | accessory | bag | dress"
VARCHAR name
INT order_index
INT min_layers
INT max_layers
}
item_categories {
SERIAL id PK
VARCHAR name
INT parent_id FK
INT default_slot_id FK
}
colors {
SERIAL id PK
VARCHAR name
}
style_nodes {
SERIAL id PK
VARCHAR name
VARCHAR kind "pattern | aesthetic | fit | season | other"
INT parent_id FK
}
item_style_nodes {
UUID item_id FK
INT style_node_id FK
}
users ||--o{ items : "владеет"
users ||--o{ outfits : "создаёт"
users ||--o{ media : "загружает"
users ||--o{ user_saved_outfits : "сохраняет"
items ||--o{ outfit_items : "входит в образ"
items }o--|| item_categories : "категория"
items }o--o| colors : "основной цвет"
items }o--o| media : "фото"
items ||--o{ item_style_nodes : "стили"
style_nodes ||--o{ item_style_nodes : "стиль"
outfits ||--o{ outfit_items : "содержит"
outfits ||--o{ outfit_item_snapshots : "снимки (внешние)"
outfits ||--o{ outfit_share_links : "ссылки шеринга"
outfits ||--o{ user_saved_outfits : "избранное"
outfit_items }o--|| slot_defs : "слот"
item_categories }o--o| slot_defs : "слот по умолчанию"

Структура проекта
backend-kotlin/src/main/kotlin/ru/plechiki/
├── PlechikiApplication.kt # Точка входа
├── auth/ # Аутентификация (контроллер, сервис, JWT)
├── controller/ # UserController, AdminController, HealthController
├── domain/ # JPA-сущность User
├── items/ # CRUD вещей, эмбеддинги вещей
├── media/ # Загрузка/отдача медиа через MinIO
├── outfits/ # Образы, обложки, генерация названий
├── feed/ # Лента: формирование, кэширование, виртуальные образы
├── share/ # Шеринг: токены, счётчики, публичный доступ
├── reference/ # Справочники: цвета, категории, слоты, стили
├── repository/ # UserRepository
└── config/ # Security, CORS, MinIO, ML-клиент, исключения
Особенности реализации
- JWT-аутентификация: access-токен (короткоживущий) + refresh-токен. Фильтр
JwtAuthenticationFilter проверяет Bearer-токен на каждом защищённом эндпоинте.
- ML-интеграция: при загрузке медиа (
POST /media) бэкенд асинхронно отправляет изображение в ML-сервис, получает предсказания (articleType, baseColour, season с confidence) и возвращает их фронтенду в ответе.
- Фид с кэшированием:
FeedCacheRefreshService периодически обновляет кэш ленты. VirtualOutfitComposer генерирует виртуальные образы из вещей пользователя.
- Слотовая модель образов: каждая вещь в образе привязана к слоту (top, bottom, shoes, outerwear, accessory, bag, dress) с поддержкой слоёв (layer_index) для многослойных комбинаций.
- Шеринг с аналитикой: генерация уникальных токенов, подсчёт просмотров (view_count) и забираний (claim_count). Поддержка форматов: link, story, square.