Пользовательская задача
|
Этот раздел перенесён из документации Camunda 7 и в дальнейшем будет доработан с учётом особенностей OpenBPM Engine |
Пользовательская задача используется для моделирования работы, которую должен выполнить человек. Когда выполнение процесса достигает такой задачи, в списке задач пользователя или группы, назначенных для её выполнения, создаётся новая задача.
Пользовательская задача определяется в XML следующим образом. Атрибут id является обязательным, атрибут name — необязательным.
<userTask id="theTask" name="Important task" />
Описание
Пользовательская задача также может иметь описание. Фактически, любой элемент BPMN 2.0 может иметь описание. Описание задаётся путём добавления элемента documentation.
<userTask id="theTask" name="Schedule meeting" >
<documentation>
Schedule an engineering meeting for next week with the new hire.
</documentation>
Текст описания можно получить из задачи стандартным способом через Java API:
task.getDescription();
Свойства
Срок выполнения (Due Date)
Каждая задача имеет поле, указывающее срок её выполнения. С помощью Query API можно запрашивать задачи, у которых срок выполнения совпадает с определённой датой, наступает до или после неё.
Существует расширение активности, позволяющее задать выражение в определении задачи для установки начального срока выполнения в момент её создания. Выражение должно всегда разрешаться в java.util.Date, java.util.String (в формате ISO8601 http://en.wikipedia.org/wiki/ISO_8601) или null. При использовании строк в формате ISO8601 можно указать либо конкретный момент времени, либо период относительно момента создания задачи. Например, можно использовать дату, введённую в предыдущей форме процесса, или вычисленную в предыдущей сервисной задаче.
<userTask id="theTask" name="Important task" camunda:dueDate="${dateVariable}"/>
Срок выполнения задачи также можно изменить через TaskService или в TaskListener с помощью переданного DelegateTask.
Дата отслеживания (Follow Up Date)
Каждая задача имеет поле, указывающее дату отслеживания. С помощью Query API можно запрашивать задачи, требующие отслеживания до или после определённой даты.
Существует расширение активности, позволяющее задать выражение в определении задачи для установки начальной даты отслеживания в момент её создания. Выражение должно всегда разрешаться в java.util.Date, java.util.String (в формате ISO8601 http://en.wikipedia.org/wiki/ISO_8601) или null. При использовании строк в формате ISO8601 можно указать либо конкретный момент времени, либо период относительно момента создания задачи. Например, можно использовать дату, введённую в предыдущей форме процесса, или вычисленную в предыдущей сервисной задаче.
<userTask id="theTask" name="Important task" camunda:followUpDate="${dateVariable}"/>
Назначение исполнителей
Пользовательская задача может быть напрямую назначена одному пользователю, списку пользователей или списку групп.
Назначение с использованием BPMN Resource Assignments
В BPMN определены нативные концепции назначения, которые можно использовать в OpenBPM Engine. В качестве более мощной альтернативы OpenBPM Engine также предоставляет набор собственных элементов расширений (см. ниже).
Human Performer
Назначение выполняется путём определения дочернего элемента humanPerformer. Такое определение требует наличия resourceAssignmentExpression, который задаёт конкретного пользователя. В настоящее время поддерживаются только formalExpression.
<process ... >
...
<userTask id='theTask' name='important task' >
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>kermit</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
Задаче может быть назначен только один пользователь в качестве исполнителя (humanPerformer). В терминологии движка такой пользователь называется исполнителем (assignee). Задачи, назначенные конкретному исполнителю, не отображаются в списках задач других пользователей и доступны только в личном списке задач этого исполнителя.
Задачи, назначенные непосредственно пользователям, можно получить через TaskService следующим образом:
List<Task> tasks = taskService.createTaskQuery().taskAssignee("kermit").list();
Potential Owner
Задачи также можно помещать в так называемый список кандидатов. В этом случае необходимо использовать конструкцию potentialOwner. Использование аналогично конструкции humanPerformer. Обратите внимание, что для каждого элемента в формальном выражении требуется явно указать, является ли он пользователем или группой (движок не может определить это самостоятельно).
<process ... >
...
<userTask id='theTask' name='important task' >
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>user(kermit), group(management)</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
Задачи, определённые с использованием конструкции potentialOwner, можно получить следующим образом (или аналогичным запросом TaskQuery, например для задач с назначенным исполнителем):
List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit");
Этот запрос вернёт все задачи, в которых kermit является кандидатом, то есть формальное выражение содержит пользователя kermit. Также будут возвращены все задачи, назначенные группе, членом которой является kermit (например, group(management), если kermit является членом этой группы и используется компонент identity). Группы пользователя определяются во время выполнения и управляются через IdentityService.
Если не указано явно, является ли заданная строка пользователем или группой, движок по умолчанию считает её группой. Поэтому следующие два варианта дают одинаковый результат:
<formalExpression>accountancy</formalExpression>
<formalExpression>group(accountancy)</formalExpression>
Назначение с использованием расширений OpenBPM Engine
Очевидно, что назначение пользователей и групп через стандартные конструкции BPMN может быть неудобным в случаях, когда логика назначения более сложная. Чтобы избежать этих сложностей, для пользовательской задачи доступны кастомные расширения.
Assignee (Исполнитель)
Атрибут assignee: это кастомное расширение позволяет напрямую назначить пользовательскую задачу конкретному пользователю.
<userTask id="theTask" name="my task" camunda:assignee="kermit" />
Это полностью эквивалентно использованию конструкции humanPerformer, описанной выше.
Candidate Users (Пользователи-кандидаты)
Атрибут candidateUsers: это кастомное расширение позволяет сделать пользователя кандидатом для выполнения задачи.
<userTask id="theTask" name="my task" camunda:candidateUsers="kermit, gonzo" />
Это полностью эквивалентно использованию конструкции potentialOwner, описанной выше. Обратите внимание, что нет необходимости использовать объявление user(kermit), как в случае с конструкцией potentialOwner, поскольку данный атрибут предназначен только для пользователей.
Candidate Groups (Группы-кандидаты)
Атрибут candidateGroups: это кастомное расширение позволяет сделать группу кандидатом для выполнения задачи.
<userTask id="theTask" name="my task" camunda:candidateGroups="management, accountancy" />
Это полностью эквивалентно использованию конструкции potentialOwner, описанной выше. Обратите внимание, что нет необходимости использовать объявление group(management), как в случае с конструкцией potentialOwner, поскольку данный атрибут предназначен только для групп.
Назначение на основе данных и сервисной логики
В приведённых выше примерах используются константные значения, такие как kermit или management. Но что если точное имя исполнителя или группы-кандидата неизвестно на этапе проектирования? И что если исполнитель — не константа, а зависит от данных, например: «Пользователь, запустивший процесс»? Возможно, логика назначения более сложная и требует обращения к внешнему источнику данных, такому как LDAP, для реализации поиска, например: «Руководитель сотрудника, запустившего процесс».
Такие сценарии можно реализовать с помощью выражений назначения или слушателей задач.
Выражения назначения
Выражения назначения позволяют обращаться к переменным процесса или вызывать бины и сервисы.
Использование переменных процесса
Переменные процесса удобны для назначения на основе данных, которые были собраны или вычислены заранее.
Следующий пример показывает, как назначить пользовательскую задачу пользователю, запустившему процесс:
<startEvent id="startEvent" camunda:initiator="starter" />
...
<userTask id="task" name="Clarify Invoice" camunda:assignee="${ starter }"/>
...
Сначала расширение camunda:initiator используется для сохранения идентификатора пользователя, запустившего («инициировавшего») процесс, в переменную starter. Затем выражение ${starter} извлекает это значение и использует его в качестве исполнителя задачи.
В выражении можно использовать все переменные процесса, видимые из пользовательской задачи.
Вызов сервиса / бина
При использовании Spring или CDI можно делегировать вызов бину или реализации сервиса. Это позволяет реализовать сложную логику назначения без моделирования её в виде отдельной сервисной задачи в процессе, результатом которой была бы переменная, используемая в назначении.
В следующем примере исполнитель будет установлен путём вызова метода findManagerOfEmployee() на бине Spring/CDI ldapService. Параметр emp — это переменная процесса.
<userTask id="task" name="My Task" camunda:assignee="${ldapService.findManagerForEmployee(emp)}"/>
Аналогичным образом это работает для пользователей-кандидатов и групп-кандидатов:
<userTask id="task" name="My Task" camunda:candidateUsers="${ldapService.findAllSales()}"/>
Обратите внимание, что это работает только если тип возвращаемого значения вызываемых методов — String или Collection<String> (для пользователей-кандидатов и групп-кандидатов):
public class FakeLdapService {
public String findManagerForEmployee(String employee) {
return "Kermit The Frog";
}
public List<String> findAllSales() {
return Arrays.asList("kermit", "gonzo", "fozzie");
}
}
Назначение через слушателей
Также можно использовать слушателей задач для обработки назначений. Следующий пример демонстрирует слушателя задачи на событии create:
<userTask id="task1" name="My task" >
<extensionElements>
<camunda:taskListener event="create" class="io.openbpm.bpm.MyAssignmentHandler" />
</extensionElements>
</userTask>
Объект DelegateTask, передаваемый в реализацию TaskListener, позволяет задать исполнителя и пользователей/группы-кандидатов:
public class MyAssignmentHandler implements TaskListener {
public void notify(DelegateTask delegateTask) {
// Здесь выполняется кастомный поиск пользователей,
// а затем, например, вызываются следующие методы:
delegateTask.setAssignee("kermit");
delegateTask.addCandidateUser("fozzie");
delegateTask.addCandidateGroup("management");
...
}
}
Назначение задачи или изменение любого другого её свойства через TaskListener не приведёт к возникновению события assignment или update, если только для этого не используется метод TaskService. Это сделано намеренно, чтобы избежать создания циклов событий.
|
Назначения и Identity Service
Несмотря на то что движок OpenBPM Engine предоставляет компонент управления идентификацией, доступный через IdentityService, он не проверяет, известен ли указанный пользователь этому компоненту. Это позволяет интегрировать движок с существующими системами управления идентификацией при встраивании в приложение.
Тем не менее обратите внимание, что можно использовать Identity Service в сервисе / бине или слушателе для запроса репозитория пользователей, если это необходимо.
Запрос пользователей с помощью Identity Service можно выполнить следующим образом:
ProcessEngine processEngine = delegateTask.getProcessEngine();
IdentityService identityService = processEngine.getIdentityService();
List<User> managementUsers = identityService.createUserQuery()
.memberOfGroup("management")
.list();
User kermit = identityService.createUserQuery()
.userFirstName("kermit")
.singleResult();
Сообщение об ошибке BPMN
Смотрите документацию по граничным событиям ошибки.
Чтобы сообщить о бизнес-ошибке в ходе выполнения пользовательской задачи, используйте метод TaskService#handleBpmnError. Его можно вызвать только когда задача активна.
Метод #handleBpmnError принимает обязательный аргумент: errorCode.
Код ошибки идентифицирует заранее определённую ошибку. Если указанный errorCode не существует или для него не определено граничное событие, текущий экземпляр активности просто завершается, а ошибка не обрабатывается.
Пример:
Task task = taskService.createTaskQuery().taskName("Perform check").singleResult();
// ... возникает бизнес-ошибка
taskService.handleBpmnError(
task.getId(),
"bpmn-error-543", // errorCode
"Thrown BPMN Error during...", // errorMessage
variables);
Ошибка BPMN с кодом bpmn-error-543 будет передана. Если существует граничное событие с таким кодом ошибки, ошибка BPMN будет перехвачена и обработана.
Сообщение об ошибке и переменные являются необязательными. Они могут предоставить дополнительную информацию об ошибке. Переменные будут переданы в выполнение, если ошибка BPMN будет перехвачена.
Сообщение об эскалации BPMN
Смотрите документацию по перехвату событий эскалации.
Сообщение об эскалации в ходе выполнения пользовательской задачи можно отправить через метод TaskService#handleEscalation. Для этого задача должна быть активной. Параметр escalationCode является обязательным для инициирования эскалации — этот код идентифицирует заранее определённую эскалацию. Если указанный escalationCode не существует, будет выброшено исключение движка процессов. Пример:
taskService.handleEscalation(
taskId,
"escalation-432", // escalationCode
variables);
Здесь инициируется эскалация с кодом escalation-432. Если существует граничное событие с таким кодом эскалации, эскалация будет перехвачена и обработана.
Переменные являются необязательными. Они будут переданы в выполнение, если эскалация будет перехвачена.
Завершение задачи
Завершение является частью жизненного цикла задачи наряду с созданием, назначением кандидатов, назначением исполнителя и т. д. (доступно через Java API). Задачу можно завершить, передав переменные; при необходимости переменные процесса можно получить в ответе:
taskService.complete(taskId, variables);
// или завершить задачу и получить переменные процесса в ответе
VariableMap processVariables = taskService
.completeWithVariablesInReturn(taskId, variables, shouldDeserializeValues);
Формы
Для передачи информации, необходимой для отображения формы пользовательской задачи, используется атрибут camunda:formKey:
<userTask id="someTask" camunda:formKey="someForm.html">
...
</userTask>
Ключ формы — это символьное значение, которое можно задать в XML-файле BPMN с помощью атрибута расширения formKey и получить во время выполнения через API движка процессов.
Если форма пользовательской задачи отображается в OpenBPM Engine Tasklist, формат formKey должен соответствовать специальным правилам. Подробности см. в соответствующем разделе руководства пользователя.
В пользовательских приложениях значение атрибута ключа формы может интерпретироваться произвольно. В зависимости от используемой UI-технологии оно может указывать на имя HTML-файла, шаблон JSF/Facelets, представление Vaadin/GWT и т. д.
Получение ключа формы через Form Service
String formKey = formService.getTaskFormData(someTaskId).getFormKey();
Получение формы через Task Service
При выполнении запроса задач можно также получить ключ формы. Это особенно полезно, когда ключи форм нужно получить для полного списка задач:
List<Task> tasks = TaskService.createTaskQuery()
.assignee("jonny")
.initializeFormKeys() // must be invoked
.list();
for(Task task : tasks) {
String formKey = task.getFormKey();
}
Обратите внимание, что для инициализации ключей форм необходимо вызвать метод .initializeFormKeys() на объекте TaskQuery.
Расширения OpenBPM Engine
Атрибуты |
|
Элементы расширений |
|
Ограничения |
Атрибут |
Допускается только один элемент расширения |
|
Атрибут |
Лицензия и атрибуция
Эта документация была создана на базе материала "Camunda 7 Docs" от Camunda, находится под лицензией Creative Commons Attribution-ShareAlike 3.0 Unported License .
Оригинал документации: https://docs.camunda.org