Групповые действия ====================== .. _group_actions: .. note:: Доступно только в Enterprise версии. .. contents:: :depth: 3 Групповые действия позволяют обработать большое количество элементов на сервере. Примеры групповых действий: 1. Выгрузка в Excel 2. Скачивание Zip архива со всеми выбранными элементами Лицензия --------- Функционал групповых действий доступен если в разделе **features** содержимого лицензии есть запись **group-actions**: .. code-block:: features: group-actions: {} Описание новых групповых действий в микросервисе ------------------------------------------------- Для добавления возможности описывать групповые действия в микросервисе нужно добавить в **pom.xml** зависимость: .. code-block:: ru.citeck.ecos.ent.groupactions ecos-group-actions После этого можно описывать свою реализацию следующего интерфейса как spring-компонент (он зарегистрируется автоматически): .. code-block:: interface GroupActionExecutionFactory { fun createExecution(config: C): GroupActionExecution fun getType(): String } Метод **getType** должен вернуть тип, по которому можно будет вызвать реализуемое действие. Метод **createExecution** вызывается каждый раз когда запускается новое действие. Интерфейс **GroupActionExecution** выглядит следующим образом: .. code-block:: interface GroupActionExecution { fun getRequiredAttributes(): Map { return emptyMap() } @Throws(Exception::class) fun execute(context: GroupActionContext): ActionResult @Throws(Exception::class) fun dispose() } Метод **getRequiredAttributes** возвращает мапу с атрибутами, которые нужны для выполнения действия. Атрибуты можно запрашивать и в ходе выполнения действия, но эффективнее вернуть их список заранее, чтобы не делать лишних запросов. Метод **dispose** вызывается когда действие завершилось с любым результатом. Т.е. в случае ошибки метод **dispose** будет так же вызван. Метод **execute** вызывается в момент запуска действия. Все основные манипуляции с данными выполняется именно в этом методе. В метод **execute** передается контекст действия с методами **getValues(): Iterable** и **getBatchedValues(batchSize: Int): Iterable>**. Можно вызвать любой из этих методов и перебрать все значения через цикл или вручную. Если предполагается, что при возникновении ошибки обработку не нужно прерывать, то исполнитель действия должен сам это учитывать. В случае выбрасывания исключения из метода **execute** выполнение действия сразу завершается. Варианты результата групповой операции: .. list-table:: :widths: 5 5 10 20 :header-rows: 1 :align: center :class: tight-table * - Класс - ID - Данные - Описание * - **ActionResultLink** - LINK - url: String - | Вернуть ссылку на что-то для скачивания. | Ссылка должна быть относительной (без протокола, хоста, порта). | Например: .. code-block:: /gateway/emodel/api/ecos/webapp/content?ref=temp-file@1c1bf32d-07ad-422c-85e4-4789058e0fb1 | Для получения ссылки на скачивание контента записи можно пользоваться методом **getDownloadUrl** в сервисе **EcosContentApi**. | Там же есть API для создания временного файла. * - **ActionResultMessage** - MESSAGE - message: String - Вернуть сообщение * - **ActionResultOk** - OK - - Вернуть простой результат о том что действие успешно выполнено * - **ActionResultResults** - RESULTS - .. code-block:: results: List Типы: Result: message: String, status: ResultStatus, recordRef: EntityRef enum ResultStatus { OK, ERROR, PERMISSION_DENIED, SKIPPED } - Вернуть список результатов по каждой обрабатываемой записи Потоки исполнения ------------------ Все групповые действия выполняются в рамках преднастроенного тред пула. Настройка количества потоков исполнения через spring свойства: .. code-block:: ecos: webapp: task: executors: group-actions: corePoolSize: 5 # по умолчанию действия выполнают пять потоков Выполнение действия происходит в контексте пользователя, который его инициировал (права доступа, часовой пояс, локаль и т.д.) Запуск группового действия ---------------------------- Для запуска группового действия необходимо подготовить следующие параметры: .. code-block:: GroupActionParams( values: GroupActionValuesParams, # параметры формирования списка значений для обработки execution: GroupActionExecutionParams # параметры обработки ) GroupActionValuesParams( type: String, # тип источника значений для обработки config: ObjectData, # конфигурация для формирования списка значений для обработки limit: Long = -1 # ограничение на количество элементов в списке значений. -1 - без ограничений ) GroupActionExecutionParams( type: String, # тип действия config: ObjectData = ObjectData.create(), # конфигурация действия timeout: Duration = Duration.ofHours(5) # ограничение на время выполнения ) Источники значений: .. list-table:: :widths: 10 10 20 :header-rows: 1 :align: center :class: tight-table * - Тип - Конфигурация - Описание * - records-list - .. code-block:: records: List - Список конкретных сущностей * - records-query - .. code-block:: query: RecordsQuery pageSize: Int - Список конкретных сущностей Архитектура решения -------------------- Общий принцип работы групповых действий следующий: При добавлении зависимости **ecos-group-actions** Spring Boot автоконфигурация регистрирует: 1. Сервис групповых действий 2. Регистратор групповых действий 3. RecordsDAO для групповых действий с ID "group-action" Для запуска группового действия выполняется мутация записи: .. code-block:: {appName_микросервиса}/group-action@ В атрибутах заполняются поля для **GroupActionParams** (т.е. values и execution) RecordsDao через сервис запускает групповую операцию и сразу же (не дожидаясь её завершения) возвращает ссылку на созданное действие в следующем виде: .. code-block:: {appName_микросервиса}:{appInstance_микросервиса}/group-action@{actionId} Например: .. code-block:: transformations:nrfdsvbocapo/group-action@7c269f9c-262b-4426-8865-7309dec07f2c Далее инициатор действия может загрузить по вернувшемуся рефу атрибуты для получения информации о состоянии действия: .. code-block:: status: GroupActionStatus initiator: EntityRef processedCount: Long totalCount: Long result: GroupActionResult? # результат выполнения. В статусах WAITING и RUNNING всегда возвращает null. В остальных случаях всегда возвращается не-null значение. Типы: enum GroupActionStatus = { WAITING, // действие ожидает пока освободится поток для его выполнения RUNNING, // действие выполняется COMPLETED, // действие завершено успешно ERROR // ошибка при выполнении действия } GroupActionResult( type: String, # Тип результата. Может быть одним из штатных типов результата (LINK, OK, и т.д.) или ошибочным - "ERROR" data: ObjectData # Данные по результату. Например, для LINK здесь будет ссылка, для OK пустой объект. ) Инициатор действия периодически может проверять состояние действия через загрузку нужных атрибутов и может выполнить какие-либо действия как только получит статус отличный от WAITING/RUNNING или ненулевой результат. UI действие ------------- .. _ui_group_actions: Тип действия - **server-group-action-v2** Конфиг действия: .. code-block:: yaml targetApp: String # целевое приложение где описана реализация групповой операции valuesParams: limit: Number # Лимит обрабатываемых элементов executionParams: type: String # Тип действия timeout: Duration # Максимальное время, которое действие может выполняться config: Map # Конфигурация действия. Содержимое зависит от типа действия outputParams: type: String # Тип способа возвращения результата действия config: Map # Конфигурация, описывающая дополнительные свойства для возвращения результата действия **outputParams** не является обязательным параметром и прописывается в том случае, если необходимо исправить дефолтный способ ответа действия. Пример конфигурации: .. code-block:: yaml id: group-action-export-csv type: server-group-action-v2 name: ru: Скачать CSV-файл en: Download Excel-file config: targetApp: transformations valuesParams: limit: 1000000 executionParams: type: export-xlsx timeout: T1H config: fileName: "report" columns: [{name: Column, attribute: "?disp"}] features: execForRecords: true execForQuery: true execForRecord: false При выполнении групповой операции отображается окно с прогрессом (% выполнения): .. image:: _static/group_actions/01.png :width: 500 :align: center Пример конфигурации с outputParams: .. code-block:: yaml id: group-action-export-xlsx type: server-group-action-v2 name: ru: Скачать Excel-файл en: Download Excel-file config: targetApp: transformations valuesParams: limit: 1000000 executionParams: type: export-xlsx timeout: T1H config: fileName: "${$context.journalName}_${$now|fmt('yyyy-MM-dd_HH-mm-ss')}" columns: "${$context.reportColumns[]?json}" outputParams: type: EMAIL config: notificationRef: notifications/template@default-link-for-export-file-notification features: execForQuery: true execForRecord: false execForRecords: true В результае выгрузки выдается сообщение: .. image:: _static/group_actions/02.png :width: 600 :align: center