Первый проект на Camunda: добавляем формы и автоматизацию
В прошлой статье остановились на том, что научились делать простые формы для ввода данных в бизнес-процессы через Camunda Modeler. Формочки были примитивные, данные были размазаны по XML-файлу и вообще было плохо. Исправляем в этой статье!
Видеоверсия
Выделяем явную структуру объекта заявки
Для отображения каких-то данных на форме мы использовали простые типы данных — строки, булевы переменные, дробные числа и так далее. Из этого формировалась форма для ввода этих данных.
С таким подходом есть несколько существенных проблем:
- Читатель диаграммы не понимает, о чем речь — это нам понятно, что есть заявка в контексте, а вот нашим коллегам без когнитивной нагрузки не справиться.
- Чтобы добавить новое пол, надо пробежаться по всем формам и убедиться, что ничего не сломалось.
- При передаче каких-то данных в соседние процессы, нам надо не забыть прокинуть новые поля.
Словом, мы очень быстро устанем держать эту информацию в голове и нам будет сложно и лениво развивать наше приложение.
Избавиться от этого можно, явно описав нашу сущность заявки. Сущность назовём BusinessTrip, а хранить её будем в контексте под названием application.
Чтобы это сделать, создадим в нашем приложении класс BusinessTrip.kt:
Класс довольно наивный, в следующих статьях будем это исправлять, а пока это нам не помешает учиться. Надо обратить внимание на приставку data перед словом class — компилятор Kotlin сделает некоторые полезные вещи за нас, если мы будем указывать это слово.
Настраиваем сереализацию для Camunda в Kotlin
Так же Kotlin потребует дополнительной настройки, потому что Kotlin не требуются getter\setter, как в Java. Настройка нужна будет, потому что Camunda написана на java и использует библиотеку Jackson, которая опирается на эти геттеры и сеттеры, а их нет.
- Включим зависимость Jackson-kotlin, которая будет автоматически под капотом обрабатывать Kotlin классы, как будто там есть геттеры и сеттеры:
- Переопределим функции получения формата данных. Просто скопируйте код из гита.
- Последнее — создайте папку services в папке META-INF, создайте там файл с указанным именем и впишите туда название класса из пред.пункта.
Заменяем формы на Angular
Теперь мы будем создавать свои собственные формы для пользовательских задач, которые будут работать с нашей заявкой. Вместе с Camunda идет библиотека на JavaScript, которой мы и воспользуемся в этом уроке.
- Создадим первую, стартовую форму. Создайте пустой файл:
- Откройте свою BPMN-схему и замените атрибуты формы:
- Нам нужно написать код самой формы. Допустим, мы хотим получить такой результат:
Фишки, которые невозможны в формах в Camunda Modeler — это обязательность заполнения полей, автоподстановка текущего логина в поле Employee.
- Давайте посмотрим на содержимое файла startForm.html
Встроенная библиотека от Camunda требует, чтобы вся форма была завернута в html-тег <form>.
- 1 кусок кода получает текущий UserName пользователя и сохраняет его в объект $scope.
- 2 кусок кода используется для того, чтобы вываливать календари в полях с датой.
- 3 кусок кода после загрузки формы создает переменную нужного типа BusinessTrip, которая в объекте $scope называется dataObject. Через этот объект мы будем взаимодействовать непосредственно с HTML-элементами формы за счёт магии ангуляра.
Развернём div:
В целом это обычный HTML и Angular 1- надо обратить внимание на директивы ng-model — за счёт этого мы ссылаемся на конкретный атрибут сущности. А с помощью директив cam-variable-name и cam-variable-type указываем типы данных, которые понимает Camunda. Angular позволяет осуществить связывание данных из HTML input с объектом dataObject и его атрибутами, которые после выполнения формы будут отправлены на сервер.
Эта форма стартовая, поэтому мы создаём переменную. В других формах нам переменную нужно получить, потому что она уже лежит в контексте:
Таким образом мы создаем формы для каждой из задач.
Добавим автоматческую отправку e-mail нотификации
В пред.версии приложения уведомление сотрудника было ручное, а теперь мы его заменим на e-mail уведомление.
Откройте вашу XML и впишите в квадратик такую информацию:
Camunda ничего не знает про е-мейлы и вообще любые другие автоматизации. Поэтому она делегирует нам выполнение какого-то кода, который мы решили вызвать в процессе.
- 1 Квадратик — Есть несколько способов указать, какой именно код вызывать — пока воспользуемся Delegate Expression.
- 2 Квадратик — Camunda будет пытаться выполнить наш код сразу же при получении команды на исполнение, синхронно. Если е-мейл сервер не ответит, то процесс даже не встанет на этот квадратик. Такие отметки, как в квадратике 2, решат этот вопрос. Подробнее про них поговорим в других уроках.
Посмотрим на класс, который будет реализовывать отправку е-мейла:
Над классом стоит текст @Component — это аннотация, которая позволит Camunda найти этот кусок кода. Во втором квадратике стоят поля с аннотацией @Value — она позволяет взять настройки из файла application.properties, который лежит в папке resources. Вот так, например, раздел с этими настройками в файле выглядит у меня:
А для отправки непосредственно почты будем использовать библиотеку Simple Java Mail. Включите её в pom.xml по инструкции с сайта.
Для того, чтобы Camunda смогла вызвать наш класс, он должен наследоваться от класса JavaDelegate и реализовывать абстрактный метод execute:
В метод execute приходит объект execution, который даёт нам доступ ко всем внутренностям системы и конкретного инстанса, в котором вызывается наш квадратик. Например, вот так я получаю заявку из контекста:
Fall application = execution. getVariable ("application") as Business Trip
А вот так я пытаюсь получить из Camunda email пользователя, который был указан в поле employee заявки:
val emailTo = execution.processEngineServices.identityService.createUserQuery().userId(application.employee)?.singleResult()?.email
Ну а дальше, просто отправляю e-mail с настройками из application.properties и данными из объекта application типа BusinessTrip по инструкции от библиотеки:
val subject = "Your trip from ${application.from} to ${application.to} is ready!" var body = "Here you trip details \n" + "------ \n" + "From: ${application.from} \n" + "To: ${application.to} \n" + "Fly description: ${application.flyDescription} \n" + "Hotel description: ${application.hotelDescription} \n" + "------ \n" + "Bye!" if (emailTo != null) { val email = EmailBuilder.startingBlank() .from(smtpFromName, smtpFromEmail!!) .to(emailTo) .withSubject(subject) .withPlainText(body) .buildEmail() val mailer = MailerBuilder .withSMTPServer(smtpHost, smtpPort, smtpUserName, smtpPassword) .withTransportStrategy(TransportStrategy.SMTPS) .clearEmailAddressCriteria() // turns off email validation .buildMailer() mailer.sendMail(email) }
В итоге
Наше решение стало лучше, теперь мы:
- Имеем явновыраженную сущность, с которой хотим работать.
- Интерактивные( +-) формы, которые умеют делать автоподстановки
- Заменили ручную задачу на автоматическую, сэкономили ресурсы людей.
Но мы по прежнему далеки от идеала:
- Формы надо копипастить для каждой задачи отдельно.
- Стили на формах не очень красивые.
- Старый этот Angular уже не поддерживают.
- Храним данные в базе Camunda, что плохо (узнаете почему в след. уроке).
Мои исходники доступны тут:. https://github.com/KotskinKotskin/camunda-first-bpmn-process
Всё получилось? Завелось? Пишите в комментарии и поделитесь статьей.
Комментарии