{"id":14291,"url":"\/distributions\/14291\/click?bit=1&hash=257d5375fbb462be671b713a7a4184bd5d4f9c6ce46e0d204104db0e88eadadd","hash":"257d5375fbb462be671b713a7a4184bd5d4f9c6ce46e0d204104db0e88eadadd","title":"\u0420\u0435\u043a\u043b\u0430\u043c\u0430 \u043d\u0430 Ozon \u0434\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u0442\u0430\u043c \u043d\u0435 \u043f\u0440\u043e\u0434\u0430\u0451\u0442","buttonText":"","imageUuid":""}

Как запустить бизнес-процесс на Camunda BPM, оркестратор между REST Django и Flutter Web (часть №1)

Привет, меня зовут Евгений, я руководитель студии разработки ПО и активно внедряю бизнес-процессы на Camunda BPM для новых проектов компании. В этой статье я расскажу, как мы запустили бизнес-процесс по разбору ИППСУ на web-портале, где Flutter Web и REST Django обмениваются данными посредством подписки на External Task бизнес-процесса Camunda.

Какую проблему решаем?

История такова, наш клиент заказал разработку мобильного приложения, которое позволяет сотрудникам отмечать оказанные клиентам услуги в формате TODO list. Схема взаимодействия стандартная: менеджер вносит задания в портале, а сотрудники отмечают выполнение в приложении по итогам смены. Такое планирование заданий занимает много времени у менеджера, необходимо вручную ввести данные клиентов, плановый состав услуг меняется, каждый месяц история повторяется.

Предистория, как всё устроено

Схема взаимодействия между сотрудниками и менеджером

Frontend

  • Flutter + своя библиотека по работе с элементами интерфейса
  • Для посылки запросов используется 2 веб-клиента: sensitiveApi, mainApi
  • dart-dio генерирует пакет: дата-модель и API из OpenAPI yaml
  • Логика переходов внутри экрана реализуется посредством BLOC
  • Injectable генератор упрощает передачу данных между модулями

пример

@singleton class CachedServicesProvider { final Map<String, Service> id2service = {}; final API api; Service? operator [](String id) => id2service[id]; CachedServicesProvider(this.api); Future<bool> load() async { id2service.clear(); var servicesResponse = await api.mainApi .mainRestV1ServicesList(pageLimit: Constants.PAGE_LIMIT); servicesResponse.data!.results.forEach((service) => id2service[service.id] = service); return id2service.isNotEmpty; } }

Backend

  • Nginx, Django, Postgres (впоследствии добавили Redis и Flask)
  • Nginx выступает в роли reverse-proxy для входящего SSL трафика
  • Организовано два микро-сервиса на базе Django:
  • Sensitive-django для управления перс. данными: ФИО, контакты, адреса
  • Public-django для управления бизнес данными: задания, каталог услуг
  • Каждый сервис снаружи предоставляет OpenAPI спецификацию
  • Спецификация генерируется из кода с помощью drf-spectacular
  • Django предоставляет Admin портал с базовым интерфейсом из коробки
  • Менеджер вводит вручную задания по каждому сотрудникам отделения
  • База данных Postgres - выполняет запросы со стороны Django
  • Боевое окружение изолирует работу с перс. данными в отдельном облаке
  • Sensitive сервис принимает трафик со стороны Public (JWT для обмена)
  • Public БД содержит только ID персональных данных (покрываем 152 ФЗ)
  • Общий список Docker контейнеров для backend разработки представлен ниже (аналогичная структура POD будет и для Prod кластера Kubernetes)

пример

PORTS NAMES 80/tcp public-nginx-dev 8000/tcp public-django-dev 5432/tcp public-database-dev 80/tcp sensitive-nginx-dev 8000/tcp sensitive-django-dev 5432/tcp sensitive-database-dev 80->80/tcp nginx-web 443->443/tcp nginx-letsencrypt nginx-gen

Изменение спецификации OpenAPI не несёт существенных сложностей

  • Django вызывает генерацию спецификации по итогам изменения модели
  • Flutter API обновляется каждый раз по итогам генерации из спецификации
пример сгенерированной OpenAPI спецификации по заданиям

Что здесь автоматизировать?

Для того, чтобы запланировать задания на год по организациям, необходимо вручную создать более 1800 заданий, принимая во внимание, что каждое задание содержит свой набор услуг.

Заданий = O x D x E x 12

  • Организаций: 15-20
  • Отделений в организации: 2-3
  • Сотрудников в отделении: 5-7

Варианты решения, которые мы рассматривали:

  • Расширить админ-портал Django, добавив конструктор заданий
  • Написать user-friendly портал также с генерацией заданий
  • Получить план работ из файла или 1С и автоматически создать задания

Первый и второй варианты не устроили клиента, предполагался ручной ввод данных по каждому заданию, нужно проводить обучение сотрудников - всё это долго и затратно. После серии встреч мы выяснили, что департамент присылает ИППСУ в виде Word файла, разобрались со структурой и сопоставили модель данных БД с имеющимся текстом.

Причины перехода на бизнес-процесс

  • Разбор файла происходит поэтапно, включает шаги подтверждения от пользователя
  • Процесс утверждения плана работ проходит с участием разных групп пользователей
  • Необходима возможность возврата на предыдущий шаг, откатив изменения данных

Почему Camunda BPM?

черновой вариант BPMN диаграммы в начале работы
  • Нужен оркестратор с обменом через REST, желательно бесплатный
  • Кроме файла, обновления плана заданий приходит из системы типа 1С
  • Процесс будет усложняться и порядок шагов меняться местами (спринт-3)

Трудности, с которыми столкнулись

Схема взаимодействия с бизнес-процессом
  • Camunda BPM не поддерживает работу по спецификации OpenAPI, из коробки есть только REST
  • разработали отдельный микро-сервис, который выступает в роли посредника и закрывает нужные функции для работы с процессами и Activity, а именно старт / стоп процесса и подход с External task:
  • Fetch & lock - подписываем сервис на получение данных из контекста
    Complete - завершаем и передаём результаты в контекст
    Handle failure - error handling механизм, чтобы повторить попытку (интеграцию)
  • использовали pycamunda для реализации REST вызовов в Camunda
    некоторые функции не работали для версии 7.15.0, дописывали библиотеку
  • Если бизнес-логика не реализуется средствами Camunda из коробки: Java и Spring, то для реализации шагов процесса остаётся использовать модель подписки на topic. Это повлекло серьёзные архитектурные изменения и переделку части решения; backend использует очередь Celery, чтобы вести подписку на разные topic
  • Контекст процесса представлен переменными и для обмена данными со сложной структурой остаётся использовать java.util.LinkedHashMap. Нужна проверка содержимого после получения (fetch) и перед посылкой (complete), пример ниже.
  • Отправка файла происходит переменной в виде строки из base64 encode

пример

... "value_info": { "objectTypeName": "java.util.LinkedHashMap", "serializationDataFormat": "application/x-java-serialized-object" } } ], "version": "0.1", "method": "GET", "host": "cloud.xxx", "returned": "2021-04-30T09:47:39.930887+0000", "status": 200, "title": "Success" }

Продолжение

Это моя первая статья VC, хотите продолжения - дайте обратную связь.

Спасибо.

0
Комментарии
-3 комментариев
Раскрывать всегда