Store / Catalog / Warehouse / Order — BR 1.4.4

Репозитории: erp-store-service, erp-catalog-service, erp-warehouse-service, erp-order-service Один и тот же паттерн изменений во всех четырёх сервисах. Может делать агент параллельно.

Зависимости

Требуется задеплоенный Auth Service с обновлённым /internal/auth/validate (возвращает scope вместо role/store_ids/legal_entity_id).


Фаза 1 — Обновить DTO auth-validate-response

  • Найти клиентский DTO у каждого сервиса (обычно AuthValidateResponse или ValidateTokenResponse)
  • Убрать поля: role, store_ids, legal_entity_id
  • Добавить поля: role_ids, permissions, scope { type, legal_entity_ids?, store_ids? }

Файлы (примерно):

  • src/main/java/com/erp/{svc}/client/AuthServiceClient.java или dto/AuthValidateResponse.java

Фаза 2 — JWT filter / SecurityContext

  • JwtAuthenticationFilter (или аналог — ищи по SecurityFilterChain) при валидации:
    • Получать scope и permissions из ответа Auth
    • Складывать в JwtUser / UserPrincipal вместо старых полей
  • Обновить JwtUser класс:
    • Убрать role, storeIds, legalEntityId
    • Добавить Scope scope, List<String> permissions, List<UUID> roleIds

Фаза 3 — Service-level авторизация

Паттерн замены switch(user.getRole()):

// Было:
switch (user.getRole()) {
    case "admin_franchise" -> findAll(user.getFranchiseId());
    case "admin_franchisee" -> findByLegalEntityIds(user.getLegalEntityIds());
    case "manager" -> findByStoreIds(user.getStoreIds());
    default -> throw new ForbiddenException();
}
 
// Стало:
Scope scope = user.getScope();
return switch (scope.type()) {
    case "all_franchise" -> findAll(user.getFranchiseId());
    case "legal_entity_ids" -> findByLegalEntityIds(scope.legalEntityIds());
    case "store_ids" -> findByStoreIds(scope.storeIds());
};

Плюс permission-check:

if (!user.getPermissions().contains("stores.edit")) {
    throw new ApiException("FORBIDDEN", HttpStatus.FORBIDDEN);
}

Per-service задачи

Store Service

  • StoreService.list/get — фильтрация по scope (all_franchise | legal_entity_ids → join через stores.legal_entity_id | store_ids)
  • StoreService.create/update/delete/publish — проверка permissions.contains("stores.edit")
  • Удалить все switch("franchise"|"franchisee"|...) строковые проверки (BR 1.4.2 коммит сделал частично — теперь убираем совсем)

Catalog Service

  • CategoryService / ProductService / ModifierService — фильтр по franchise_id (для каталога per-store фильтрации нет)
  • Permissions: menu.read / menu.edit для товаров/категорий; price_list.* для прейскурантов; recipes.* для техкарт; ingredients.*; stoplists.*
  • Убрать role-checks

Warehouse Service

  • Stock-levels / acceptance-acts / write-off-acts — фильтр по store_ids из scope (у warehouse всё привязано к конкретной ТТ)
  • Permissions: warehouse.*
  • Убрать role-checks

Order Service

  • OrderService — фильтр по store_ids из scope
  • Permissions: orders.*
  • Убрать role-checks

Фаза 4 — Удалить hardcode role-значений

  • Поиск по кодовой базе каждого сервиса: grep -r "admin_franchise\|admin_franchisee\|\"manager\"\|\"cashier\""
  • Любое совпадение — проверить и удалить/заменить на permission или scope check

Фаза 5 — Сборка и деплой в правильном порядке

Синхронный деплой

Все 4 сервиса деплоятся одновременно с Auth Service (т.к. формат /auth/validate меняется). Если один сервис стартует со старым клиентом — упадёт с NPE на полях role etc.

  • Собрать 4 образа
  • Стопнуть старые контейнеры
  • Запустить новые одновременно (или по цепочке, убеждаясь что Auth уже новый)

Выходные критерии

  • grep "switch.*role\|\"admin_franchise\"\|\"admin_franchisee\"\|\"manager\"\|\"cashier\"" — пусто во всех 4 репо
  • Docker build OK
  • Health checks проходят после деплоя
  • Smoke test: список магазинов/товаров/складских операций/заказов возвращается корректно для admin@erp.local

Риски

  • Store Service — ранее в BR 1.4.2 делал ручной role-check patch. После BR 1.4.4 — полная перепись на scope
  • Warehouse/Order — в этих сервисах могут быть места где legal_entity_id использовался (например, фильтр документов по ЮЛ). Нужно будет маппить scope.legal_entity_idsstore_ids (через Store Service internal endpoint), если в сервисе нет прямой привязки к LE