BPMN (англ. Business Process Modeling Notation, «нотация и модель бизнес-процессов») — это язык визуального моделирования бизнес-процессов, использующий графические блок-схемы. Это открытый стандарт, созданный консорциумом Object Management Group (OMG).
Основными целями BPMN являются:
-
Достижение большей гибкости бизнеса.
-
Достижение более высоких уровней эффективности и результативности.
-
Повышение производительности с точки зрения качества, затрат и сроков.
Object Management Group определила ряд стандартов и нотаций, известных как BPM (англ. “Business Process Management”, «управление бизнес-процессами»).
Процессный движок Flowable позволяет разворачивать процессы в соответствии с международным отраслевым стандартом BPMN 2.0. Каждый процесс BPM представляет собой последовательность объектов, связанных с действиями и имеющих стартовое и конечное события.
BPMN используется для автоматизации бизнеса — например, в управлении пользовательским/клиентским опытом или управлении мероприятиями. Он упрощает и ускоряет разработку, уменьшая количество ошибок.
Стартовое событие (Start Event) — объект BPMN-потока, который определяет начальную точку процесса.
Поток операций (Sequence Flow) — объекты потока операций используются для соединения объектов потока. Они играют роль транспортировщика данных между событиями потока.
Шлюз «ИЛИ/ИЛИ» (исключающий, Exclusive Gateway) — основан на условиях и разбивает поток на один или несколько.
Пользовательская задача (User Task) — используется для моделирования задачи, которая должна выполняться человеком. Например, Admin Maker — это пользовательская задача, которая находится под контролем любого пользователя.
Конечное событие (End Event) — это объект BPMN-потока, который определяет точку окончания процесса.
Теперь рассмотрим работу приложения на Spring Boot, которое использует внутренние сервисы с движками бизнес-процессов.
Прежде чем погрузиться в детали Flowable, давайте обсудим, какие сценарии могут использоваться для оптимизации процесса разработки и повышения его эффективности, надёжности и точности.
Приложения для управления бизнес-процессами могут охватывать различные области, такие как системы управления аэропортом или банковские приложения.
Процесс на диаграмме
Давайте рассмотрим процесс на диаграмме. В нём участвуют два участника: создатель / maker (инициатор запроса) и проверяющий / checker (тот, кто подтверждает запрос). Создатель инициирует запрос, а проверяющий должен подтвердить его для завершения процесса.

Начнём с кода:
POM:
Файл pom.xml, представленный ниже, содержит зависимости, используемые для реализации BPMN.
<dependency> <groupId>org.flowable</groupId> <artifactId>flowable5-spring-compatibility</artifactId> <version>${flowable.version}</version> </dependency>
В процессном движке Flowable:
-
Элемент
<definations>является корневым элементом файла определения процесса. Он используется для определения набора процессов, диаграмм взаимодействия и других артефактов, которые являются частью бизнес-процесса. -
Элемент
<process>определяет сам бизнес-процесс и содержит начальные и конечные события, а также другие элементы, такие как<userTask>,<serviceTask>,<sequenceFlow>и<exclusiveGateway>. -
Элемент
<startEvent>обозначает начало процесса или подпроцесса. -
<userTask>представляет работу, выполняемую пользователем. -
<sequenceFlow>обозначает поток управления между действиями в процессе или подпроцессе. -
exclusiveGateway— тип шлюза, который используется для обеспечения условного разветвления в процессе или подпроцессе. Он имеет один вход и несколько выходов, и принимает решение о том, какой исходящий поток выбрать на основе заданного условия. Каждый исходящий поток обычно ассоциируется с выражением условия, и шлюз выбирает первый поток, для которого условие оценивается как истинное (true). Если ни одно из условий не является истинным, выбирается поток по умолчанию. -
endEvent— это элемент, обозначающий завершение конец процесса или подпроцесса. Его цель — указать конец выполнения процесса и определить действия, которые должны быть выполнены при завершении процесса или подпроцесса. -
conditionExpression— это булево выражение, которое используется для определения того, следует ли выполнять последовательность действий или нет. Обычно оно используется в сочетании с исключающим (exclusive) шлюзом, который позволяет процессу разветвляться в разных направлениях на основе оценки условия.
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <process id="FlowableProcess" name="FlowableProcess" isExecutable="true"> <startEvent id="startevent1" name="Start"></startEvent> <userTask id="maker" name="maker" activiti:owner="maker"></userTask> <userTask id="checker" name="checker" activiti:owner="checker"></userTask> <sequenceFlow id="Flow_StartToMaker" sourceRef="startevent1" targetRef="maker"></sequenceFlow> <exclusiveGateway id="MakerExclusivegateway" name="Exclusive Gateway Maer"></exclusiveGateway> <sequenceFlow id="Flow_MakerToGateway" sourceRef="maker" targetRef="MakerExclusivegateway"></sequenceFlow> <sequenceFlow id="Flow_MakerGatewayToChecker" sourceRef="MakerExclusivegateway" targetRef="checker"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${makerApproved}]]></conditionExpression> </sequenceFlow> <exclusiveGateway id="CheckerExclusivegateway" name="Exclusive Gateway"></exclusiveGateway> <sequenceFlow id="Flow_CheckerToGateway" sourceRef="checker" targetRef="CheckerExclusivegateway"></sequenceFlow> <endEvent id="endevent" name="End"></endEvent> <sequenceFlow id="Flow_CheckerGatewayToEnd" sourceRef="CheckerExclusivegateway" targetRef="endevent"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${checkerApproved}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="Flow_MakerGatewayToEnd" sourceRef="MakerExclusivegateway" targetRef="endevent"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!makerApproved}]]></conditionExpression> </sequenceFlow> <sequenceFlow id="Flow_CheckerGatewayToMaker" sourceRef="CheckerExclusivegateway" targetRef="maker"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!checkerApproved}]]></conditionExpression> </sequenceFlow> </process> </definitions>

Давайте реализуем BPMN flowable процесс для диаграммы процесса, показанной выше.
Теперь давайте также реализуем код, который использует вышеприведённое определение процесса для построения системы жалоб.
Контроллер:
Этот класс содержит 5 методов, каждый из которых имеет определённую аннотацию отображения HTTP-запроса:
-
@PostMapping("maker/request/{complain}")— используется для обработки HTTP POST-запросов с URI «/api/maker/request/{complain}». Этот метод будет приниматьcomplainкак переменную пути, передавать её методуflowableService.makerRequestи возвращать ответ от него. -
@GetMapping("checker/pending/tasks")— используется для обработки HTTP GET-запросов с URI «/api/checker/pending/tasks». Этот метод получит отложенные задачи checker-а и вернёт их в качестве ответа. -
@PostMapping("/checker/review/task/{processId}/{approve}")— используется для обработки HTTP POST-запросов с URI «/api/checker/review/task/{processId}/{approve}». Этот метод приметprocessIdиapproveкак переменные пути, передаст их методуflowableService.checkerReviewи вернёт ответ от него. -
@GetMapping("maker/return/pending/tasks")— используется для обработки HTTP GET-запросов с URI «/api/maker/return/pending/tasks». Этот метод получит отложенные задачи инициатора и вернёт их в качестве ответа. -
@PostMapping("/makerReview/{complain}/{processId}/{approve}")— используется для обработки HTTP POST-запросов с URI «/api/makerReview/{complain}/{processId}/{approve}». Этот метод приметcomplain,processIdиapproveв качестве переменных пути и передаст их во flowable-сервис.
Все эти методы будут взаимодействовать с flowableService для выполнения определённого действия и возврата ответа.
package com.mediumBlog.Flowabledemo.controller; import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.flowable.engine.ProcessEngine; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.repository.Deployment; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.mediumBlog.Flowabledemo.service.FlowableService; @RestController @RequestMapping("/api/") public class Controller { @Autowired private FlowableService flowableService; /* * This api is to initiate a complain request and assign to checker */ @PostMapping("maker/request/{complain}") public String makerRequest(@PathVariable("complain") String complaint) { String respons = flowableService.makerRequest(complaint); return respons; } /* * This api is to get checker pending tasks which are assigned by maker */ @GetMapping("checker/pending/tasks") public Map<String, Object> checkerPending() { Map<String, Object> response = new HashMap<String, Object>(); response = flowableService.getCheckerPendings(); return response; } /* * This api is to review the task which are pending at checker */ @PostMapping("/checker/review/task/{processId}/{approve}") public String checkerReviewReview(@PathVariable("processId") String processId, @PathVariable("approve") Boolean approve) { String respons = flowableService.checkerReview(processId, approve); return respons; } /* * This api is to get maker return pending tasks which are assigned and rejected by checker level */ @GetMapping("maker/return/pending/tasks") public Map<String, Object> getMakerReturnPendings() { Map<String, Object> response = new HashMap<String, Object>(); response = flowableService.getMakerReturnPendings(); return response; } /* * This api is to review the maker return pending tasks which are pending at maker level */ @PostMapping("/makerReview/{complain}/{processId}/{approve}") public String makerReviewReturn(@PathVariable("complain") String complain, @PathVariable("processId") String processId, @PathVariable("approve") Boolean approve) { String respons = flowableService.makerReviewReturn(complain, processId, approve); return respons; } }
Сервис:
package com.mediumBlog.Flowabledemo.service; import java.util.Map; public interface FlowableService { public String makerRequest(String complain); public Map<String,Object> getCheckerPendings(); public String checkerReview(String processId,Boolean review); public Map<String,Object> getMakerReturnPendings(); public String makerReviewReturn(String complaincomplain,String processId, Boolean review); }
Реализация сервиса
Этот класс реализует интерфейс FlowableService и реализует методы, объявленные в этом интерфейсе.
-
Deploymentпредставляет собой коллекцию определений процессов, форм и других ресурсов, которые развёртываются на движке. -
RepositoryServiceиспользуется для управления определениями процессов, развёртываниями и другими артефактами в движке Flowable. Создание и управление развёртыванием осуществляется через API RepositoryService, который предоставляет методы для работы с определениями процессов, формами и другими ресурсами. -
Метод
createDeployment()вызывается на объектеrepositoryServiceдля создания нового развёртывания. МетодaddClasspathResource()добавляет определение процесса BPMN 2.0 в развертывание из файла «flowableProcess.bpmn20.xml» вclasspath. -
Метод
startProcessInstanceByKey()запускает новый экземпляр процесса по ключу, передаваемому в качестве аргумента. -
Taskпредставляет одну задачу для пользователя. -
Затем создается запрос задачи с помощью метода
createTaskQuery()на объектеTaskService. МетодprocessDefinitionKey()указывает ключ определения процесса для задачи, а методprocessInstanceId(processInstance.getId())связывает задачу с идентификатором экземпляра процесса. -
Метод
singleResult()возвращает одну задачу, соответствующую критериям запроса. -
Метод
setVariables(task.getId(), variables)устанавливает несколько переменных для задачи, гдеtask.getId()— это идентификатор задачи, аvariables— карта переменных. -
Метод
setVariable(task.getId(), "makerApproved", true)устанавливает переменную «makerApproved» в true для задачи. -
Метод
complete(task.getId())завершает задачу, позволяя экземпляру процесса перейти к следующему шагу. -
Метод
getVariables(task.getId())используется для получения переменных, которые были заданы для задачи, гдеtask.getId()— это id задачи. Этот метод вернёт карту с переменными, которые были заданы для задачи, где ключ — это имя переменной, а значение — это значение переменной.
package com.mediumBlog.Flowabledemo.serviceImpl; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.flowable.task.api.Task; import org.flowable.engine.ProcessEngine; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.repository.Deployment; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.mediumBlog.Flowabledemo.dao.TaskDetails; import com.mediumBlog.Flowabledemo.entity.Complaint; import com.mediumBlog.Flowabledemo.repo.FlowableRepo; import com.mediumBlog.Flowabledemo.service.FlowableService; @Service public class FlowableServiceImpl implements FlowableService { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @PersistenceContext private EntityManager entityManager; @Autowired private TaskService taskService; @Autowired private FlowableRepo flowableRepo; ProcessEngine processEngine; @Override public String makerRequest(String complain) { Map<String, Object> variables = new HashMap<String, Object>(); String response = ""; try { Deployment deployment = repositoryService.createDeployment() .addClasspathResource("flowableProcess.bpmn20.xml").deploy(); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("FlowableProcess", variables); Task task = taskService.createTaskQuery().processDefinitionKey("FlowableProcess") .processInstanceId(processInstance.getId()).singleResult(); variables.put("RequestBy", "maker"); variables.put("processId", processInstance.getProcessInstanceId() + ""); variables.put("complain", complain); taskService.setVariables(task.getId(), variables); taskService.setVariable(task.getId(), "makerApproved", true); taskService.complete(task.getId()); response = "Succesfully Done with processId :" + processInstance.getProcessInstanceId() + ""; } catch (Exception e) { // TODO: handle exception response = "Error Occured While Maker Requesting :" + e.getMessage(); } return response; } @Override public Map<String, Object> getCheckerPendings() { Map<String, Object> var = new HashMap<String, Object>(); try { List<Task> tasks = new ArrayList<Task>(); tasks = taskService.createTaskQuery().processDefinitionKey("FlowableProcess").taskOwner("checker") .orderByTaskCreateTime().desc().list(); List<TaskDetails> taskList = getTaskDetails(tasks); var.put("data", taskList); var.put("00", "Process Ok"); } catch (Exception e) { var.put("01", "process failed" + e.getMessage()); } return var; } @Override public String checkerReview(String processId, Boolean approve) { String response = ""; try { Task task = taskService.createTaskQuery().processDefinitionKey("FlowableProcess").taskOwner("checker").processInstanceId(processId) .singleResult(); if (task == null) { return response = "There is no task available with processId :" + processId; } // getting variable data which came from maker Map<String, Object> variables = taskService.getVariables(task.getId()); String complain = (String) variables.get("complain"); variables.put("reviewBy", "checker"); taskService.setVariables(task.getId(), variables); taskService.setVariable(task.getId(), "checkerApproved", approve); taskService.complete(task.getId()); response = "Checker Successfully reviewed"; if(approve==true){ Complaint complaint = new Complaint(); complaint.setComplaint(variables.get("complain")+""); complaint.setComplaintInitiator(variables.get("RequestBy")+""); complaint.setComplaintApprover(variables.get("reviewBy")+""); complaint.setDate(new Date()); flowableRepo.save(complaint); } } catch (Exception e) { response = "Error Occured While Maker Requesting :" + e.getMessage(); } return response; } @Override public Map<String, Object> getMakerReturnPendings() { Map<String, Object> var = new HashMap<String, Object>(); try { List<Task> tasks = new ArrayList<Task>(); tasks = taskService.createTaskQuery().processDefinitionKey("FlowableProcess").taskOwner("maker") .orderByTaskCreateTime().desc().list(); List<TaskDetails> taskList = getTaskDetails(tasks); var.put("data", taskList); var.put("00", "Process Ok"); } catch (Exception e) { var.put("01", "process failed" + e.getMessage()); } return var; } @Override public String makerReviewReturn(String complain,String processId, Boolean approve) { String response = ""; try { Task task = taskService.createTaskQuery().processDefinitionKey("FlowableProcess").taskOwner("maker").processInstanceId(processId) .singleResult(); if (task == null) { return response = "There is no task available with processId :" + processId; } // getting variable data which came from maker Map<String, Object> variables = taskService.getVariables(task.getId()); variables.put("complain", complain.equals("") ? variables.get("complain") : complain); taskService.setVariables(task.getId(), variables); taskService.setVariable(task.getId(), "makerApproved", approve); taskService.complete(task.getId()); response = "Maker Successfully reviewed"; } catch (Exception e) { response = "Error Occured While Maker Requesting :" + e.getMessage(); } return response; } public List<TaskDetails> getTaskDetails(List<Task> tasks) { List<TaskDetails> taskDetail = new ArrayList<>(); for (Task task : tasks) { Map<String, Object> variables = new HashMap<String, Object>(); Map<String, Object> processVariable = taskService.getVariables(task.getId()); taskDetail.add(new TaskDetails(task.getId(), task.getName(), task.getCreateTime(), processVariable)); } return taskDetail; } }
Entity класс
Этот класс будет использоваться интерфейсом FlowableRepo для выполнения CRUD-операций с базой данных.
package com.mediumBlog.Flowabledemo.entity; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import lombok.Data; @Entity @Data public class Complaint { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String complaint; private String complaintInitiator; private String complaintApprover; private Date date; }
Детали задачи:
Класс «TaskDetails» в пакете используется для хранения информации о текущей задаче и её данных.
Он имеет четыре поля:
-
«taskId» типа «String»: id задачи.
-
«taskName» типа «String»: имя задачи.
-
«updatedDate» типа «Date», дата обновления задачи.
-
«taskData» типа «Map<String, Object>»: данные задачи с парой ключ-значение.
Этот класс используется в классе FlowableServiceImpl для хранения данных о задаче, и, скорее всего, он будет возвращен контроллеру как часть ответа, который будет передан на фронтенд.
package com.mediumBlog.Flowabledemo.dao; import java.util.Date; import java.util.Map; import lombok.Data; @Data public class TaskDetails { String taskId; String taskName; Date updatedDate; public Map<String, Object> taskData; public TaskDetails(String taskId, String taskName, Date updatedDate, Map<String, Object> taskData) { super(); this.taskId = taskId; this.taskName = taskName; this.updatedDate = updatedDate; this.taskData = taskData; } }
Таблицы данных времени выполнения в Activiti
Данные времени выполнения в Activiti хранятся только во время выполнения экземпляра процесса, и когда процесс завершается, записи удаляются.
Таблицы ACT_RU_TASK и ACT_RU_VARIABLE являются частью данных времени выполнения в Activiti. В таблице ACT_RU_TASK хранится информация о текущих задачах, такие как исполнитель, сроки и статус выполнения. ACT_RU_VARIABLE содержит переменные, связанные с задачами или процессами, и обе таблицы связаны внешним ключом PROC_INST_ID_ и TASK_ID_.
Для проверки завершенных процессов мы можем получить подробности из ACT_HI_TASKINST и ACT_HI_VARINST. ACT_HI_TASKINST содержит данные о завершенных заданиях, включая исполнителя, время начала и окончания. Таблица ACT_HI_VARINST содержит данные о переменных процесса, их значения и типы. Эти таблицы используются для аудита и отчётности, например, для отслеживания хода процесса или анализа производительности.
Заключение
В этой статье мы рассмотрели реализацию модели бизнес-процессов и узнали, как создать мощное и гибкое решение по управлению рабочими процессами и BPM-приложениями, объединив Flowable и Spring Boot.
Весь код, упомянутый в этой статье, доступен на GitHub.
Как правильно использовать шлюзы при моделировании процессов в нотации BPMN? Обсудим это на открытом уроке 15 апреля. Записаться можно на странице онлайн-курса.
ссылка на оригинал статьи https://habr.com/ru/articles/806357/
Добавить комментарий