Перейти к содержанию

Бэкенд (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 Список расшаренных образов

Медиа (/api/media)

Метод Путь Описание
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.