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

Бэкенд (Spring Boot)

Стек и зависимости​

ТехнологияВерсияНазначение
Java21Язык программирования
Spring Boot4.0.4Основной фреймворк
Spring Data JPA—Доступ к БД через репозитории
Spring Security—JWT-авторизация
PostgreSQL15Реляционная БД
Liquibase—Миграции схемы БД
MapStruct—Генерация маппер-классов
AWS SDK v2 (S3)—Работа с SeaweedFS
Caffeine—In-memory кеш
JJWT—Генерация и валидация JWT
Springdoc OpenAPI—Swagger-документация
Spring RestClient—HTTP-клиент для mphone API (с интерсепторами авторизации)
Jsoup—Парсинг HTML страниц поставщика для скрейпинга картинок
Spring Mail—Email-уведомления
Lombok—Генерация boilerplate-кода

Структура пакетов​

ru.mbc.studio.dmshop/
├── controller/ # REST-контроллеры
├── service/ # Интерфейсы сервисов
│ └── impl/ # Реализации сервисов
├── repository/ # Spring Data JPA репозитории
│ └── spec/ # JPA Specifications (динамические фильтры)
├── entity/ # JPA-сущности
├── model/ # Внутренние Java records
├── dto/ # HTTP Request/Response records
│ ├── product/
│ ├── order/
│ ├── user/
│ └── ...
├── mapper/ # MapStruct маппер-интерфейсы
├── exception/ # Доменные исключения
├── common/
│ └── exception/ # GlobalExceptionHandler, ApiErrorResponse
├── config/ # Конфигурационные классы Spring
└── sync/ # Интеграция с mphone и XML-фидом
├── client/ # MphoneApiClient (RestClient)
├── config/ # MphoneApiProperties, MphoneApiConfig
├── service/ # SyncService, XmlFeedSyncService, скрейпинг картинок
├── job/ # @Scheduled-задачи (SyncJob, CategoryVisibilityJob, GbstoreMappingJob)
├── pipeline/ # XML-пайплайн: оркестратор + этапы (stages/)
└── jackson/ # MphoneDateDeserializer

Контроллеры и эндпоинты​

Публичные (без авторизации)​

КонтроллерЭндпоинтМетодОписание
AuthControllerPOST /api/v1/auth/register—Регистрация
AuthControllerPOST /api/v1/auth/login—Вход, возвращает JWT
ProductControllerGET /api/v1/products—Список товаров (пагинация, фильтры)
ProductControllerGET /api/v1/products/{id}—Карточка товара
CategoryControllerGET /api/v1/categories—Все категории
CategoryControllerGET /api/v1/categories/tree—Дерево категорий
CategoryControllerGET /api/v1/categories/homepage—Категории для главной
BrandControllerGET /api/v1/brands—Список брендов
ReviewControllerGET /api/v1/reviews/active—Активные отзывы (для витрины)
BannerBlockControllerGET /api/v1/banner-blocks/active—Активные баннеры
FeatureItemControllerGET /api/v1/feature-items/active—Активные фичи
AppConfigPublicControllerGET /api/v1/config/public—Публичные настройки сайта
ContactControllerPOST /api/v1/contacts—Отправка обращения
ImageControllerGET /api/v1/images/{id}—Скачать изображение
CartControllerPOST /api/v1/cart/validate—Валидация корзины

Пользовательские (роль USER)​

КонтроллерЭндпоинтМетодОписание
OrderControllerPOST /api/v1/orders—Оформить заказ
OrderControllerGET /api/v1/orders/{id}—Просмотр заказа
OrderControllerGET /api/v1/orders/my—Мои заказы
AuthControllerPUT /api/v1/auth/profile—Обновить профиль
AuthControllerPUT /api/v1/auth/change-password—Сменить пароль

Административные (роль ADMIN / MANAGER)​

КонтроллерЭндпоинтОписание
AdminOrderControllerGET/PATCH /api/v1/admin/ordersУправление заказами
AdminOrderControllerPOST /api/v1/admin/orders/{id}/submitПередача в mphone
AdminUserControllerGET/POST/PUT/DELETE /api/v1/admin/usersУправление пользователями
AdminContactControllerGET/PUT/DELETE /api/v1/admin/contactsОбращения клиентов
AdminSyncControllerGET /api/v1/admin/sync/statesСтатус синхронизации
AdminSyncControllerPOST /api/v1/admin/sync/runЗапустить синхронизацию
AppConfigAdminControllerGET/POST/PUT/DELETE /api/v1/admin/configНастройки сайта
ReviewControllerGET/POST/PUT/DELETE /api/v1/reviewsУправление отзывами
ReviewControllerGET /api/v1/reviews/exportЭкспорт отзывов в JSON
ReviewControllerPOST /api/v1/reviews/importИмпорт отзывов из JSON
BannerBlockControllerGET/POST/PUT/DELETE /api/v1/banner-blocksБаннеры
FeatureItemControllerGET/POST/PUT/DELETE /api/v1/feature-itemsФичи
ImageControllerGET /api/v1/imagesСписок изображений
ImageControllerPOST /api/v1/imagesЗагрузить изображение
ImageControllerPUT /api/v1/images/{id}/replaceЗаменить файл (FK сохраняются)
ImageControllerDELETE /api/v1/images/{id}Удалить изображение
ProductControllerPOST/PUT/PATCH/DELETE /api/v1/productsРедактирование товаров
ProductControllerPUT /api/v1/products/{id}/attributesРучные атрибуты товара
CategoryControllerPUT /api/v1/categories/{id}Обновить категорию
CategoryControllerPOST /api/v1/categories/{id}/move, /reorderПорядок категорий
CategoryController.../actions/hide-unused/preview /applyСкрыть неиспользуемые категории
AdminOrderControllerPATCH .../delivery-cost, DELETE .../bulk-deleteДоставка и очистка заказов
LicenseController/api/v1/admin/license/**Баланс, прайсы, пополнения лицензии
EmailNotificationControllerPOST /api/v1/admin/notifications/email/testТестовое письмо
AttributeAliasRuleController/api/v1/admin/attribute-rulesПравила атрибутов из фида
XmlFeedAdminController/api/v1/admin/xml-feed/**Ручной синк фида, маппинг категорий
XmlFeedPipelineAdminController/api/v1/admin/xml-pipeline/**Запуски, снепшоты, переопределения картинок
GbstoreScrapeQueueAdminController/api/v1/admin/scrape-queue/**Очередь и лог скрейпинга картинок

Полный перечень эндпоинтов всегда доступен в Swagger UI (/swagger-ui.html). Точные роли по каждому пути — в разделе Безопасность.

Фильтрация товаров​

ProductRepository использует JPA Specifications (spec/ProductSpecifications):

// Пример динамического фильтра
Specification<ProductEntity> spec = ProductSpecifications
.hasCategory(categoryId)
.and(ProductSpecifications.hasBrand(brandId))
.and(ProductSpecifications.isActive(true))
.and(ProductSpecifications.priceBetween(min, max));

Параметры через GET /api/v1/products:

  • categoryId, brandId
  • minPrice, maxPrice
  • search (по названию)
  • active
  • page, size, sort

Сервисы​

Каждый сервис содержит интерфейс в service/ и реализацию в service/impl/:

ProductService → ProductServiceImpl
OrderService → OrderServiceImpl
AdminOrderService → AdminOrderServiceImpl
AuthService → AuthServiceImpl
UserService → UserServiceImpl
CategoryService → CategoryServiceImpl
ReviewService → ReviewServiceImpl
ImageService → ImageServiceImpl
BannerBlockService → BannerBlockServiceImpl
FeatureItemService → FeatureItemServiceImpl
AppConfigService → AppConfigServiceImpl
ContactRequestService → ContactRequestServiceImpl
SpamProtectionService — защита от спама (без impl)
CartService → CartServiceImpl

SpamProtectionService​

Защищает эндпоинт POST /api/v1/contacts от флуда: ограничивает количество обращений с одного IP за заданный промежуток времени.

MapStruct-маппинг​

Каждый домен имеет свой маппер. Пример для Product:

@Mapper(componentModel = "spring")
public interface ProductMapper {
ProductModel toModel(ProductEntity entity);
ProductResponse toResponse(ProductModel model);
ProductEntity toEntity(CreateProductRequest request);
}

Поля, которые не заполняются из запроса (например, externalId, productImages), помечаются:

@Mapping(target = "externalId", ignore = true)
@Mapping(target = "productImages", ignore = true)

Конфигурационные классы​

КлассНазначение
SecurityConfigJWT-фильтр, CORS, разрешения эндпоинтов
JwtServiceГенерация и валидация JWT
JwtAuthFilterServlet-фильтр: читает заголовок Authorization
JwtProperties@ConfigurationProperties("jwt"): secret, expirationMs
S3ConfigСоздаёт бин S3Client (AWS SDK) для SeaweedFS
S3Properties@ConfigurationProperties("storage.s3")
CacheConfigCaffeine: 1000 записей, TTL 30 с
WebConfigCORS (origin из cors.allowed-origins)
ApplicationConfigUserDetailsService, PasswordEncoder
SchedulingConfig@EnableScheduling