EDI

Основные принципы

Интеграция с ЭДО провайдерами, как уже писалось в других статьях на эту тему, построена на стандартном для микросервиса интеграций механизме - синхронизациях ECOS Synchronization. Это для нас означает, что у нас есть простой способ управлять состоянием интеграции с конкретным ЭДО провайдером в рамках настроенного ящика в ECOS посредством уже готового функционала (включение/выключение чекбоксами, сброс состояния при необходимости для старта интеграции с самого начала и тд).

Перейдем к описанию самого функционала с указанием классов.

ECOS синхронизации работают на механизме объявления реализации SyncExecutionFactory, которые поставляют реализации SyncExecution, в котором уже происходит шедулинг интеграции.

В нашем случае, за это ответственны классы EdiSyncExecutionFactory и EdiSyncExecution. Работу EdiSyncExecutionFactory рассматривать не будем, так как кроме регистрации в SyncService и создания EdiSyncExecution - никакой работы он и не делает.

Рассмотрим работу EdiSyncExecution:

  • Зашедулить джобу по настройкам.

  • В джобе - повесить лок с помощью ShedLock. Если смог повесить лок - работа продолжается.

  • Собирает данные для интеграции, ищет зарегистрированную для ЭДО-провайдера из настроек ящика реализацию интерфейса EdiEventsSyncService. Если не находит - сыпет исключения. Важно отметить, что реализаций EdiEventsSyncService в самом микросервисе integrations на самом деле нет, как они подключаются будет описано в разделе ниже.

  • Делегирует работу найденному EdiEventsSyncService, передавая ему собранные ранее данные + метод-колбек для обновления состояния синхронизации.

То есть, по факту, особой работы EdiSyncExecution не делает, полезная работа выполняется за счет делегирования.

Связь сервисов-реализации с EdiSyncExecution для интеграции

Ответ довольно прост, существует общая либа ecos-edi-commons, в которую вынесены основные интерфейсы интеграции и структуры данных. Таким образом, удается связать решение на уровне интерфейсов (то есть, связь такая, что интерфейс используется в микросервисе integrations, а реализация идет в сторонних либах).

Ок, с этим разобрались, что-то реализуется, а как микросервис то получит это, если он этих зависимостей даже не имеет? Тут ответ уже сложнее. Решение основано на загрузке кода библиотек в микросервис в виде OSGi бандлов. Для более подробного изучения этого - можно почитать статью Функционал загрузки OSGI пакетов (и рекомендованные статьи), а так же пример загрузки либы контур для этих целей - Настройка получения событий с ящиком Контур_Диадок

Рассмотрим что именно за интерфейсы и сервисы определены в ecos-edi-commons либе:

  • EdiEventsSyncService - интерфейс, в который делегируется вся интеграция с ЭДО-провайдером.

  • EdiEventsSyncServiceResolver - класс, в котором регистрируются EdiEventsSyncService по связке “ЭДО-провайдер - Сервис” при загрузке бандла, предназначенного для интеграции с каким-то ЭДО-провайдером. Содержится в микросервисе в виде бина.

  • EdiBoxService - интерфейс для получения информации о ящике. Расширяется в основном коде микросервиса в виде бина.

  • EdiApiService - интерфейс для общения с ЭДО провайдером. Содержит методы для всех операций с ЭДО (или почти все).

  • EdiApiServiceResolver - класс, в котором регистрируются EdiApiService по связке “ЭДО-провайдер - Сервис” при загрузке бандла, предназначенного для интеграции с каким-то ЭДО-провайдером. Содержится в микросервисе в виде бина.

  • EdiService - Класс, который содержит те же методы, что и в EdiApiService, за исключением того, что в каждый метод дополнительно передается параметр EdiProviderType. По сути, является композитным EdiApiService. Делегирует логику в конкретный EdiApiService, полученный по связке с EdiProviderType из EdiApiServiceResolver.

  • EdiGenerator + дочерние интерфейсы - интерфейсы для расширения возможностей генерации контента в ЭДО провайдерах. К примеру, может быть 2 реализации генерации печатной формы: через вызов API генерации ПФ в сервисах диадока (удаленная генерация) или генерация силами ECOS (локальная). Регистрируется в EdiGeneratorResolver.

  • EdiGeneratorResolver - класс для регистрации EdiGenerator реализаций по составному ключу “Эдо-провайдер+Тип-генератора+вид-генератора”, где Эдо-провайдер - Контур или Корус, Тип генератора - Генерация печатной формы или Генерация титула покупателя, Вид генератора - Произвольная строка с символизирующая вид. Обычно, что то вроде “локальная генерация” или “генерация по API”.

  • EdiStateService - Сервис для отправки событий на обработку после получения и первичной обработки их в либе (будет рассмотрено ниже).

Примерное взаимодействие сервисов можно изучить подробнее на drawIO диаграмме:

edi_services

Что происходит в либах обработки и что за зверь - EdiStateService

Либы не обрабатывают события. Они их получают исходя последнего обработанного события (хранящегося в состоянии синхронизации), составляют универсальную структуру Event, хранящуюся в либе ecos-edi-commons и после этого отправляет эту структуру в EdiStateService.

Структуры в ecos-edi-commons можно посмотреть в следующей диаграмме drawIO :

edi_structures

EdiStateService - это класс в ecos-edi-commons, который отправляет события по определенному endpoint внутри camel контекста. CamelContext отправителя и CamelContext получателя события - это, как правило, разные контексты. Подобная передача осуществляется использованием эндпоинта direct-vm типа, а не простого direct.

Таким образом, происходит следующее взаимодействие:

Integration EDI

, где указано по шагам:

1 - Отправляется запрос выполнить интеграцию через Контур (или иной другой вариант ЭДО-провайдера), для какого-то ящика, от такого то события (вызов EdiEventsSyncService).

2 - В случае найденных событий - трансформирует их в структуру Event и через EdiStateService отправляет сообщение в Camel из контекста Camel микросервиса интеграций.

3 - Camel контекст микросервиса интеграций отправляет сообщение с Event в CamelContext внутри VM, в котором зарегистрирован указанный эндпоинт. На скриншоте указано, что мы используем еще какой-то customer-lib со своим camel контекстом, но такая кастомизация нужна не всегда, в общем случае - будет использоваться сразу переход в ecos-lib.

4 - После выполнения каких-то работа в customer-lib роутах - они отправляют сообщение в CamelContext либы ecos-lib. Там происходит обновление документов, подписей и тд в альфреско путем вызовов Records API.

Стоит уточнить еще раз, customer-lib и ecos-lib - отдельные OSGi бандлы, включенные в микросервис после его старта, которые создают и стартуют Camel контекст, на роутах которого возложена связь между ними. Обязательно нужно соблюдать контракт по именованию endpoint.

Структура Event

Не совсем так. Нет, я не имею ввиду, что Event - это какая-то хитрая структура. Это обычный POJO, с сеттерами, геттерами, equals и тд. Однако, заполнение Event носит определенный характер. Внутри него ДОЛЖНЫ содержаться все документы, состояния которых изменены в рамках данного события. Это означает, что если обрабатываем событие “Документ подписан”, то это означает, что должна прийти не просто подпись, а еще и информация о документе с АКТУАЛЬНЫМ статусом. Другая ситуация, если по документу типа УПД пришла корректировка УКД - это означает, что в рамках этого события придет не только УКД, но и информация о текущем новом состоянии УПД (только статус). Неизменившиеся поля, вроде контента или основанных на контенте формализованных атрибутах, которые могут быть тяжелыми при транспортировании - опускаются.