探索BPMN—工作流技术的理论与实践|得物技术

news2024/9/21 0:34:04

一、前言

  • 19世纪70年代,流程管理思想萌芽阶段。

    怎样提高工作效率?

    泰勒:标准化个人操作流程

    亨利·福特:规定标准时间定额

    标准化、精简化、通用化、专业化。

  • 20世纪70年代,工作流技术起源于办公自动化领域的研究。由于当时计算机尚未普及,网络技术水平还很低以及理论基础匮乏,这项新技术并未取得成功,许多公司采用纸张表单,手工传递的方式,一级一级审批签字,工作效率非常低下。

  • 20世纪90年代,信息技术驱动流程自动化阶段。信息技术开始引入管理领域,对传统的业务进行自动化改造。

    产品:Sabre订票系统,MRP(物料需求计划),MIS(管理信息系统),CIMS(计算机集成制造系统)。

  • 21世纪初,BPM管理与治理结合。

    BPR/BPI:以信息技术为核心重新设计业务流程,失败率高达50%-70%;

    BPM:以业务流程为主导的管理思想;

    BPM治理思想:对BPM实施的成功率以及ROI的高要求,强调BPM与企业战略相结合。

二、BPMN介绍

BPMN(Business Process Modeling Notation,业务流程建模符号)是一种用于描述业务流程的标准化建模语言。它通过图形符号及规则,帮助企业建模、分析和优化各种业务流程。BPMN有两个主要版本:BPMN 1.0和BPMN 2.0。BPMN 1.0规范由标准组织BPMI(后并入到OMG)于2004年5月发布;BPMN 2.0标准由OMG于2011年推出。

2.0相对于1.0规范了流程引擎的语义和格式,利用标准的图元描述真实的业务发生过程,保证相同的流程在不同的流程引擎中得到一致的执行结果。

01.jpg

BPMN的理论基础

  • 流程建模:BPMN使用不同的图形符号来表示业务流程中的各项活动、任务、决策和事件等。这些符号包括任务、网关、事件等,每个符号都有特定的含义和用法。

  • 流程分析:通过BPMN模型,企业可以分析业务流程的效率、资源利用率和风险等,以便进行优化和改进。

  • 流程执行:BPMN不仅是一种建模语言,还可以将建模的业务流程实际执行起来,并进行监控和控制。

BPMN的优势

02.jpg

传统使用场景

  • 采购流程:通过BPMN建模,企业可以规范采购流程,包括需求确认、供应商选择、合同签订、物品收货等环节,提高采购效率。

  • 请假审批流程:BPMN模型可以帮助企业规范请假审批流程,包括员工请假申请、直属主管审批、人力资源部门审批等环节,减少误差和纠纷。

  • 客户投诉处理流程:利用BPMN,企业可以清晰地展示客户投诉处理流程,包括客户投诉登记、处理人员分配、处理过程跟踪、客户反馈等环节,提高客户满意度。

三、实践举例:XX平台XXX商家合规治理任务优化项目(非真实场景)

背景目标

  • 商家合规治理手段的单一性和对线下人工的过度依赖方面需要优化,通过整合商家合规治理策略,并实现这些策略的可在线化配置。这一举措将打破传统的手工操作模式,允许业务方直接在系统中配置和更新治理策略,减少人工干预,提高治理效率。同时,在线化配置也便于策略的快速部署和灵活调整,以应对不同业务场景下的合规要求。

  • 各商家的合规治理进度和状态的透明度方面需要优化,为改变这一现状,我们需要搭建一套高效的工作流引擎。该引擎将支持商家合规治理流程的编排和自动化流转,从任务触发到完成审核、记录反馈等各个环节都将实现自动化处理。同时,内置的进度跟踪机制将确保业务团队能够实时查看各商家的治理进度,及时发现问题并采取相应措施。这将大大提高治理的透明度和可控性。

  • 评估治理成果和手段的有效性分析方面需要优化,将合规治理成果的数据可视化,通过强大的数据分析与可视化系统,我们将关键指标(如治理效率、违规率、改进情况等)以图表、报告等形式直观展现。同时,通过持续的数据分析与改进循环,我们将逐步优化治理策略和方式,最终形成商家合规治理的闭环机制。

方案

业务流程

03.jpg

治理流程1

04.jpg

治理流程2

系统架构图

05.jpg

流程编排

06.jpg

工作流技术选型

07.jpg

SmartEngine详细介绍

E-R图

08.jpg

CREATE TABLE `se_deployment_instance` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'PK'  ,
  `gmt_create` datetime(6) NOT NULL   COMMENT 'create time' ,
  `gmt_modified` datetime(6) NOT NULL  COMMENT 'modification time'  ,
  `process_definition_id` varchar(255) NOT NULL  COMMENT 'process definition id'  ,
  `process_definition_version` varchar(255) DEFAULT NULL  COMMENT 'process definition version'  ,
  `process_definition_type` varchar(255) DEFAULT NULL  COMMENT 'process definition type'  ,
  `process_definition_code` varchar(255) DEFAULT NULL  COMMENT 'process definition code'  ,
  `process_definition_name` varchar(255) DEFAULT NULL  COMMENT 'process definition name'  ,
  `process_definition_desc` varchar(255) DEFAULT NULL  COMMENT 'process definition desc'  ,
  `process_definition_content` mediumtext NOT NULL  COMMENT 'process definition content'  ,
  `deployment_user_id` varchar(128) NOT NULL  COMMENT 'deployment user id' ,
  `deployment_status` varchar(64) NOT NULL   COMMENT 'deployment status' ,
  `logic_status` varchar(64) NOT NULL  COMMENT 'logic status' ,

  PRIMARY KEY (`id`)
) COMMENT='流程定义表' ;

CREATE TABLE `se_process_instance` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT  COMMENT 'PK'  ,
  `gmt_create` datetime(6) NOT NULL  COMMENT 'create time' ,
  `gmt_modified` datetime(6) NOT NULL  COMMENT 'modification time' ,
  `process_definition_id_and_version` varchar(128) NOT NULL  COMMENT 'process definition id and version'  ,
  `process_definition_type` varchar(255) DEFAULT NULL COMMENT 'process definition type'  ,
  `status` varchar(64) NOT NULL COMMENT ' 1.running 2.completed 3.aborted',
  `parent_process_instance_id` bigint(20) unsigned DEFAULT NULL   COMMENT 'parent process instance id' ,
  `parent_execution_instance_id` bigint(20) unsigned DEFAULT NULL   COMMENT 'parent execution instance id' ,
  `start_user_id` varchar(128) DEFAULT NULL  COMMENT 'start user id' ,
  `biz_unique_id` varchar(255) DEFAULT NULL  COMMENT 'biz unique id' ,
  `reason` varchar(255) DEFAULT NULL   COMMENT 'reason' ,
  `comment` varchar(255) DEFAULT NULL   COMMENT 'comment' ,
  `title` varchar(255) DEFAULT NULL  COMMENT 'title' ,
  `tag` varchar(255) DEFAULT NULL  COMMENT 'tag' ,

  PRIMARY KEY (`id`)
) COMMENT='流程实例表' ;

CREATE TABLE `se_activity_instance` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT  COMMENT 'PK'  ,
  `gmt_create` datetime(6) NOT NULL   COMMENT 'create time' ,
  `gmt_modified` datetime(6) NOT NULL  COMMENT 'modification time'  ,
  `process_instance_id` bigint(20) unsigned DEFAULT NULL  COMMENT 'process instance id'  ,
  `process_definition_id_and_version` varchar(255) NOT NULL  COMMENT 'process definition id and version'  ,
  `process_definition_activity_id` varchar(64) NOT NULL COMMENT 'process definition activity id'   ,
  PRIMARY KEY (`id`)
) COMMENT='活动节点实例表' ;

CREATE TABLE `se_task_instance` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT  COMMENT 'PK'  ,
  `gmt_create` datetime(6) NOT NULL   COMMENT 'create time' ,
  `gmt_modified` datetime(6) NOT NULL  COMMENT 'modification time'  ,
  `process_instance_id` bigint(20) unsigned NOT NULL  COMMENT 'process instance id'  ,
  `process_definition_id_and_version` varchar(128) DEFAULT NULL  COMMENT 'process definition id and version'  ,
  `process_definition_type` varchar(255) DEFAULT NULL  COMMENT 'process definition type'  ,
  `activity_instance_id` bigint(20) unsigned NOT NULL   COMMENT 'activity instance id' ,
  `process_definition_activity_id` varchar(255) NOT NULL  COMMENT 'process definition activity id' ,
  `execution_instance_id` bigint(20) unsigned NOT NULL  COMMENT 'execution instance id'  ,
  `claim_user_id` varchar(255) DEFAULT NULL   COMMENT 'claim user id' ,
  `title` varchar(255) DEFAULT NULL COMMENT 'title'   ,
  `priority` int(11) DEFAULT 500 COMMENT 'priority' ,
  `tag` varchar(255) DEFAULT NULL  COMMENT 'tag'  ,
  `claim_time` datetime(6) DEFAULT NULL COMMENT 'claim time'   ,
  `complete_time` datetime(6) DEFAULT NULL COMMENT 'complete time'   ,
  `status` varchar(255) NOT NULL COMMENT 'status'     ,
  `comment` varchar(255) DEFAULT NULL  COMMENT 'comment'  ,
  `extension` varchar(255) DEFAULT NULL COMMENT 'extension'  ,

  PRIMARY KEY (`id`)
) COMMENT='人工任务节点实例表'  ;

CREATE TABLE `se_task_assignee_instance` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT  COMMENT 'PK'  ,
  `gmt_create` datetime(6) NOT NULL  COMMENT 'create time'  ,
  `gmt_modified` datetime(6) NOT NULL   COMMENT 'modification time' ,
  `process_instance_id` bigint(20) unsigned NOT NULL  COMMENT 'process instance id'  ,
  `task_instance_id` bigint(20) unsigned NOT NULL  COMMENT 'task instance id'  ,
  `assignee_id` varchar(255) NOT NULL  COMMENT 'assignee id'  ,
  `assignee_type` varchar(128) NOT NULL  COMMENT 'assignee type'  ,
  PRIMARY KEY (`id`)
) COMMENT='人工任务节点代理人实例表'  ;

CREATE TABLE `se_execution_instance` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'PK'   ,
  `gmt_create` datetime(6) NOT NULL   COMMENT 'create time' ,
  `gmt_modified` datetime(6) NOT NULL  COMMENT 'modification time'  ,
  `process_instance_id` bigint(20) unsigned NOT NULL  COMMENT 'process instance id'  ,
  `process_definition_id_and_version` varchar(255) NOT NULL  COMMENT 'process definition id and version'  ,
  `process_definition_activity_id` varchar(255) NOT NULL COMMENT 'process definition activity id'   ,
  `activity_instance_id` bigint(20) unsigned NOT NULL COMMENT 'activity instance id'   ,
  `active` tinyint(4) NOT NULL COMMENT '1:active 0:inactive',
  PRIMARY KEY (`id`)
) COMMENT='执行节点实例表-最细粒度的实例'   ;


CREATE TABLE `se_variable_instance` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT  COMMENT 'PK'  ,
  `gmt_create` datetime(6) NOT NULL  COMMENT 'create time'  ,
  `gmt_modified` datetime(6) NOT NULL  COMMENT 'modification time'  ,
  `process_instance_id` bigint(20) unsigned NOT NULL   COMMENT 'process instance id' ,
  `execution_instance_id` bigint(20) unsigned DEFAULT NULL   COMMENT 'execution instance id' ,
  `field_key` varchar(128) NOT NULL   COMMENT 'field key' ,
  `field_type` varchar(128) NOT NULL   COMMENT 'field type' ,
  `field_double_value` decimal(65,30) DEFAULT NULL   COMMENT 'field double value' ,
  `field_long_value` bigint(20) DEFAULT NULL  COMMENT 'field long value'  ,
  `field_string_value` varchar(4000) DEFAULT NULL  COMMENT 'field string value' ,

  PRIMARY KEY (`id`)
) COMMENT='执行节点变量量表' ;
代码实操

第一步,要选择正确的SmartEngine版本,将其添加到pom依赖中。

com.alibaba.smart.framework

smart-engine-extension-storage-custom

3.0.0

第二步,完成SmartEngine初始化。在初始化时,一般要加载流程定义到应用中。集群情况下,要注意流程定义的一致性(如果纯静态记载则无此类问题)。在初始化时,可以根据需要定义Bean的加载优先级。

import com.alibaba.smart.framework.engine.SmartEngine;
import com.alibaba.smart.framework.engine.configuration.InstanceAccessor;
import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
import com.alibaba.smart.framework.engine.configuration.impl.DefaultProcessEngineConfiguration;
import com.alibaba.smart.framework.engine.configuration.impl.DefaultSmartEngine;
import com.alibaba.smart.framework.engine.exception.EngineException;
import com.alibaba.smart.framework.engine.service.command.RepositoryCommandService;
import com.alibaba.smart.framework.engine.util.IOUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.InputStream;

import static org.springframework.core.Ordered.LOWEST_PRECEDENCE;


@Order(LOWEST_PRECEDENCE)
@Configuration
@ConditionalOnClass(SmartEngine.class)
public class SmartEngineAutoConfiguration implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Bean
    @ConditionalOnMissingBean
    public SmartEngine constructSmartEngine() {
        ProcessEngineConfiguration processEngineConfiguration = new DefaultProcessEngineConfiguration();
        // 实现InstanceAccessor接口
        processEngineConfiguration.setInstanceAccessor(new CustomInstanceAccessService());

        SmartEngine smartEngine = new DefaultSmartEngine();
        smartEngine.init(processEngineConfiguration);

        // 加载流程定义
        deployProcessDefinition(smartEngine);

        return smartEngine;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private class CustomInstanceAccessService implements InstanceAccessor {
        @Override
        public Object access(String name) {
            return applicationContext.getBean(name);
        }
    }

    private void deployProcessDefinition(SmartEngine smartEngine) {
        RepositoryCommandService repositoryCommandService = smartEngine
                .getRepositoryCommandService();

        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            Resource[] resources = resolver.getResources("classpath*:/smart-engine/*.xml");
            for (Resource resource : resources) {
                InputStream inputStream = resource.getInputStream();
                repositoryCommandService.deploy(inputStream);
                IOUtil.closeQuietly(inputStream);
            }
        } catch (Exception e) {
            throw new EngineException(e);
        }

    }
}
BMPN2.0 xml举例

09.jpg

附言:排他网关的条件表达式是跟着线条绑定的,要自己确保条件的是和否加在一起的总概率是100%,如果不是100%,小概率执行节点会卡在排他网关走不下去。例如:线条“否”的条件表达式为:条件1为假且条件2为假,线条“是”的条件表达式为条件1为真且条件2为真,则其他条件会卡死在排他网关流程走不下去。

test1.bpmn

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Web Modeler" exporterVersion="b1a091a" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.5.0" camunda:diagramRelationId="2a2c997b-ff2f-49b0-9ba8-3c4860d223e7">
  <bpmn:process id="Process_1qijgvk" name="test1" isExecutable="true">
    <bpmn:startEvent id="StartEvent_1">
      <bpmn:outgoing>Flow_0soou9n</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:sequenceFlow id="Flow_0soou9n" sourceRef="StartEvent_1" targetRef="Activity_02gdgyk" />
    <bpmn:endEvent id="Event_1i2y4ym">
      <bpmn:incoming>Flow_07mf0sc</bpmn:incoming>
      <bpmn:incoming>Flow_1nd3369</bpmn:incoming>
      <bpmn:incoming>Flow_160ji3a</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:exclusiveGateway id="Gateway_1kykneu" name="检查是否5天内完成治理">
      <bpmn:extensionElements />
      <bpmn:incoming>Flow_0exjsc9</bpmn:incoming>
      <bpmn:outgoing>Flow_0hj860q</bpmn:outgoing>
      <bpmn:outgoing>Flow_1oqjt08</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sequenceFlow id="Flow_0hj860q" name="否" sourceRef="Gateway_1kykneu" targetRef="Activity_1ms8vu6" />
    <bpmn:serviceTask id="Activity_02gdgyk" name="站内通知商户去治理">
      <bpmn:extensionElements />
      <bpmn:incoming>Flow_0soou9n</bpmn:incoming>
      <bpmn:outgoing>Flow_0exjsc9</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="Flow_0exjsc9" sourceRef="Activity_02gdgyk" targetRef="Gateway_1kykneu" />
    <bpmn:serviceTask id="Activity_1ms8vu6" name="发送sms通知商户">
      <bpmn:incoming>Flow_0hj860q</bpmn:incoming>
      <bpmn:outgoing>Flow_0bemgki</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="Flow_0bemgki" sourceRef="Activity_1ms8vu6" targetRef="Gateway_1dyp6hh" />
    <bpmn:sequenceFlow id="Flow_1oqjt08" name="是" sourceRef="Gateway_1kykneu" targetRef="Activity_1c9i1el" />
    <bpmn:exclusiveGateway id="Gateway_1dyp6hh" name="是否10天内完成治理">
      <bpmn:incoming>Flow_0bemgki</bpmn:incoming>
      <bpmn:outgoing>Flow_07xq405</bpmn:outgoing>
      <bpmn:outgoing>Flow_1w4qeti</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sequenceFlow id="Flow_07xq405" name="是" sourceRef="Gateway_1dyp6hh" targetRef="Activity_0m5dsna" />
    <bpmn:serviceTask id="Activity_1c9i1el" name="发放奖励给到商户">
      <bpmn:incoming>Flow_1oqjt08</bpmn:incoming>
      <bpmn:outgoing>Flow_1t7ectj</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="Flow_1t7ectj" sourceRef="Activity_1c9i1el" targetRef="Activity_1p6mkdy" />
    <bpmn:task id="Activity_0m5dsna" name="任务完成">
      <bpmn:incoming>Flow_07xq405</bpmn:incoming>
      <bpmn:outgoing>Flow_07mf0sc</bpmn:outgoing>
    </bpmn:task>
    <bpmn:sequenceFlow id="Flow_07mf0sc" sourceRef="Activity_0m5dsna" targetRef="Event_1i2y4ym" />
    <bpmn:sequenceFlow id="Flow_1w4qeti" name="否" sourceRef="Gateway_1dyp6hh" targetRef="Activity_1gud1rw" />
    <bpmn:task id="Activity_1p6mkdy" name="任务完成">
      <bpmn:incoming>Flow_1t7ectj</bpmn:incoming>
      <bpmn:outgoing>Flow_1nd3369</bpmn:outgoing>
    </bpmn:task>
    <bpmn:sequenceFlow id="Flow_1nd3369" sourceRef="Activity_1p6mkdy" targetRef="Event_1i2y4ym" />
    <bpmn:serviceTask id="Activity_1gud1rw" name="通知运营下线商户店铺">
      <bpmn:incoming>Flow_1w4qeti</bpmn:incoming>
      <bpmn:outgoing>Flow_0vr5zs3</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:sequenceFlow id="Flow_0vr5zs3" sourceRef="Activity_1gud1rw" targetRef="Activity_1pfbnmo" />
    <bpmn:task id="Activity_1pfbnmo" name="任务结束">
      <bpmn:incoming>Flow_0vr5zs3</bpmn:incoming>
      <bpmn:outgoing>Flow_160ji3a</bpmn:outgoing>
    </bpmn:task>
    <bpmn:sequenceFlow id="Flow_160ji3a" sourceRef="Activity_1pfbnmo" targetRef="Event_1i2y4ym" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1qijgvk">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="152" y="232" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_1kykneu_di" bpmnElement="Gateway_1kykneu" isMarkerVisible="true">
        <dc:Bounds x="465" y="225" width="50" height="50" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="448" y="188" width="84" height="27" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_02gdgyk_di" bpmnElement="Activity_02gdgyk">
        <dc:Bounds x="270" y="210" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1ms8vu6_di" bpmnElement="Activity_1ms8vu6">
        <dc:Bounds x="630" y="210" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1i2y4ym_di" bpmnElement="Event_1i2y4ym">
        <dc:Bounds x="1182" y="232" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_1dyp6hh_di" bpmnElement="Gateway_1dyp6hh" isMarkerVisible="true">
        <dc:Bounds x="825" y="225" width="50" height="50" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="810" y="285" width="79" height="27" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1c9i1el_di" bpmnElement="Activity_1c9i1el">
        <dc:Bounds x="630" y="320" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0m5dsna_di" bpmnElement="Activity_0m5dsna">
        <dc:Bounds x="950" y="210" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="BPMNShape_0ab1d2g" bpmnElement="Activity_1p6mkdy">
        <dc:Bounds x="840" y="320" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1gud1rw_di" bpmnElement="Activity_1gud1rw">
        <dc:Bounds x="800" y="80" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="BPMNShape_02s3tua" bpmnElement="Activity_1pfbnmo">
        <dc:Bounds x="950" y="80" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_0soou9n_di" bpmnElement="Flow_0soou9n">
        <di:waypoint x="188" y="250" />
        <di:waypoint x="270" y="250" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0exjsc9_di" bpmnElement="Flow_0exjsc9">
        <di:waypoint x="370" y="250" />
        <di:waypoint x="465" y="250" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0hj860q_di" bpmnElement="Flow_0hj860q">
        <di:waypoint x="515" y="250" />
        <di:waypoint x="630" y="250" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="567" y="232" width="11" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1oqjt08_di" bpmnElement="Flow_1oqjt08">
        <di:waypoint x="490" y="275" />
        <di:waypoint x="490" y="360" />
        <di:waypoint x="630" y="360" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="544" y="373" width="11" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0bemgki_di" bpmnElement="Flow_0bemgki">
        <di:waypoint x="730" y="250" />
        <di:waypoint x="825" y="250" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1t7ectj_di" bpmnElement="Flow_1t7ectj">
        <di:waypoint x="730" y="360" />
        <di:waypoint x="840" y="360" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_07mf0sc_di" bpmnElement="Flow_07mf0sc">
        <di:waypoint x="1050" y="250" />
        <di:waypoint x="1182" y="250" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_07xq405_di" bpmnElement="Flow_07xq405">
        <di:waypoint x="875" y="250" />
        <di:waypoint x="950" y="250" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="899" y="232" width="11" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1w4qeti_di" bpmnElement="Flow_1w4qeti">
        <di:waypoint x="850" y="225" />
        <di:waypoint x="850" y="160" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="853" y="186" width="11" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_1nd3369_di" bpmnElement="Flow_1nd3369">
        <di:waypoint x="940" y="360" />
        <di:waypoint x="1120" y="360" />
        <di:waypoint x="1120" y="250" />
        <di:waypoint x="1182" y="250" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0vr5zs3_di" bpmnElement="Flow_0vr5zs3">
        <di:waypoint x="900" y="120" />
        <di:waypoint x="950" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_160ji3a_di" bpmnElement="Flow_160ji3a">
        <di:waypoint x="1050" y="120" />
        <di:waypoint x="1120" y="120" />
        <di:waypoint x="1120" y="250" />
        <di:waypoint x="1182" y="250" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>
  • process,表示一个流程。

  • id=“exclusiveTest” version=“1.0.0”,分别表示流程定义的id和版本。这两个字段唯一区分一个流程定义。

  • startEvent,表示流程开始节点。只允许有一个开始节点。

  • endEvent,表示流程结束节点。可以有多个结束节点。

  • sequenceFlow,表示环节流转关系。sourceRef=“theStart” targetRef=“submitTask” 分别表示起始节点和目标节点。该节点有个子节点, approve = = ‘agree’,这个片段很重要,用来描述流程流转的条件。approve = = 'upgrade’使用的是MVEL表达式语法。另外,还值得注意的是,在驱动流程运转时,需要传入正确的参数。比如说,在后面介绍的api中,通常会需要在Map中传递业务请求参数。那么需要将Map中的key和Mvel的运算因子关联起来。以这个例子来说,request.put(“approve”, “agree”);里面的approve和approve == 'agree’命名要一致。

  • exclusiveGateway,表示互斥网关。该节点非常重要。用来区分流程节点的不同转向。互斥网关在引擎执行conditionExpression后,有且只能选择一条匹配的sequenceFlow继续执行。

  • serviceTask,服务任务,用来表示执行一个服务,所以他会有引擎默认的扩展:smart:class=“com.alibaba.smart.framework.example.AuditProcessServiceTaskDelegation”. Client Developer使用时,需要自定义对应的业务实现类。在该节点执行时,它会自动执行服务调用,执行smart:class 这个 delegation。该节点不暂停,会自动往下一个流转。

  • receiveTask,接收任务。在引擎遇到此类型的节点时,引擎执行会自动暂停,等待外部调用signal方法。当调用signal方法时,会驱动流程当前节点离开。在离开该节点时,引擎会自动执行smart:class 这个delegation。在一般业务场景中,我们通常使用receiveTask来表示等需要等待外部回调的节点。

  • userTask,表示用户任务节点,仅用于DataBase模式。该节点需要人工参与处理,并且通常需要在待办列表中展示。在Custom模式下,建议使用receiveTask来代替。

  • parallelGateway,这个节点并未在上述流程定义中体现,这里详细说一下。parallelGateway 首先必须成对出现,分别承担fork和join职责。其次,在join时需要实现分布式锁接口:LockStrategy。第三,fork默认是顺序遍历多个sequeceFlow,但是你如果需要使用并发fork功能的话,则需要实现该接口:ExecutorService。

重要领域对象
  • 部署实例: DeploymentInstance,描述这个流程定义是谁发布的,当前处于什么状态。

  • 流程定义: ProcessDefinition,描述一个流程有几个环节,之间的流转关系是什么样子的。

  • 流程实例: ProcessInstance,可以简单理解为我们常见的一个工单。

  • 活动实例: ActivityInstance,主要是描述流程实例(工单)的流转轨迹。

  • 执行实例: ExecutionInstance,主要根据该实例的状态,来判断当前流程处在哪个节点上。

  • 任务实例: TaskInstance,用来表示人工任务处理的,可以理解为一个需要人工参与处理的环节。

  • 任务处理:TaskAssigneeInstance,用来表示当前任务共有几个处理者。通常在代办列表中用到此实体。

  • 变量实例:VariableInstance,用来存储流程实例上下文。

SmartEngine引擎源码地址:https://github.com/alibaba/SmartEngine/tree/master
SmartEngine UserGuide: https://github.com/alibaba/SmartEngine/wiki/SmartEngine-UserGuide–Chinese-Version-%28%E4%B8%AD%E6%96%87%E7%89%88%29
Camunda开源流程设计器(支持在线和本地node.js部署两种方式):https://camunda.com/download/modeler/
Camunda设计器学习文档:https://docs.camunda.io/docs/components/modeler/bpmn/bpmn-primer/

四、总结与建议

优点

  • 业务流程可视化与实际系统流程可视化高度一致,所见即所得。

  • 调整效率高(业务平均每个月会升级一次治理流程),如果业务流程1.0要升级到1.1,只需要重新复制一份bpmn.xml流程模板重新编排为1.1,并下发流程实例即可,不影响原有的流程模板和流程实例执行(调整效率由原来的一周缩短到1小时)。

  • 流程实例和流程节点实例可视,方便监控各个节点的执行和数据报表的产出。

缺点

  • 异常处理的支持度不够友好(SmartEngine是异常丢弃),如果在某一个节点上执行失败(一般情况是业务接口执行失败导致),默认当前流程进度是卡在该节点的,需要设计张异常表,把当前流程实例,节点实例以及变量都保存下载,通过job重新拉起重试去驱动流程继续执行,并需要做好告警监控,以及任务实例和流程实例的核对。

  • 高并发场景的支持度并不是太友好,要通过异步消息的方式来控制创建流程实例的速度,目前得到的创建流程实例的TPS是100/s单台,只是相对于activiti来说并发支持度要高,超过这个上限的场景建议谨慎使用。

  • 可扩展性不足,例如:ProcessQueryService只支持findById,findList,count;ExecutionQueryService只支持findActiveExecutionList,findAll这些基本查询 ,复杂查询需要新写SmartEngine核心包,升级jar包版本后才可使用。

  • 无历史记录表,每隔一段时间要清理表中流程实例已经完结的相关数据,否则历史数据堆积影响查询效率。

建议

如果业务流程的复杂度一般,且经常会调整,并发量并不高的情况下,建议使用;如果业务复杂度过高,或并发量TPS超过单台100/s,不建议使用。

*文 / 冬冬

本文属得物技术原创,更多精彩文章请看:得物技术

未经得物技术许可严禁转载,否则依法追究法律责任!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1943841.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

minio 服务docker配置

用minio docker配置了一个服务&#xff0c;分享链接始终是127.0.01开始的&#xff0c; 改成docker的host的ip则提示签名不匹配&#xff0c; 好在这个文件主要是用来下载的&#xff0c;所以可以通过设置bucket的匿名访问权限来实现下载&#xff1b; 这样不需要后面的地址参数就…

GeoHash原理介绍以及在redis中的应用

GeoHash将二维信息编码成了一个一维信息。降维后有三个好处&#xff1a; 编码后数据长度变短&#xff0c;利于节省存储。利于使用前缀检索当分割的足够细致,能够快速的对双方距离进行快速查询 GeoHash是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串。 1…

网站漏洞扫描软件Burp suite和Xray安装应用及联合使用

目录 1、网站漏洞扫描软件应用-Burp suite 01 burp 扫描工具使用介绍&#xff1a; 02 burp 扫描工具安装过程&#xff1a; 1&#xff09;获取扫描工具程序包 2&#xff09;安装部署扫描工具 3&#xff09;bp安装完毕的基础设置&#xff1a; 3.1&#xff09;抓取浏览器访…

CSS怎么实现镜像效果?

实现镜像效果&#xff08;包含动画&#xff09; 需求分析 创建一个可以接收任意内容的 Vue 组件&#xff0c;并在其下方显示该内容的镜像。镜像效果应包括垂直翻转和渐变透明效果&#xff0c;以模拟真实的倒影。支持动画效果&#xff0c;使内容和镜像同步运动。组件应具有高可…

Redis从入门到超神-(四)Redis实现分布式锁原理

引言 什么是分布式锁&#xff1f; 分布式锁是分布式系统中用于控制多个进程或线程对共享资源的访问的一种机制。在分布式系统中&#xff0c;由于存在多个服务实例或节点&#xff0c;它们可能会同时尝试访问或修改同一份数据或资源。如果没有适当的同步机制&#xff0c;就可能导…

装机基础知识,不被坑,纯小白级别

装机基础知识&#xff0c;不被坑&#xff0c;纯小白级别 CPU主要是英特尔和AMD1&#xff0c;AMDyes2 &#xff0c;英特尔老大哥牙膏厂3&#xff0c;CPU参数 显卡主要是NVidia和AMD1&#xff0c;gtx系列2&#xff0c;rtx系列3&#xff0c;AMD的rx系列显卡4&#xff0c;显卡参数问…

PLC通过IGT-SER系列智能网关快速实现WebService接口调用案例

IGT-SER系列智能网关支持PLC设备数据对接到各种系统平台&#xff0c;包括SQL数据库&#xff0c;以及MQTT、HTTP协议的数据服务端&#xff1b;通过其边缘计算功能和脚本生成的工具软件&#xff0c;非常方便快速实现PLC、智能仪表与WebService服务端通信。 本文是通过智能网关读取…

SpringSecurity如何正确的设置白名单

在SpringSecurity中,往往需要对部分接口白名单访问,而大部分在使用Security中就有一个误区,那就是免鉴权访问和白名单的区别。 大部分的Security文章包括官方文档给出免鉴权访问都是使用.permitAll()去对相应路径进行免鉴权访问,但实际上这仅仅只表示该资源不需要相应的权限访问…

交易积累-BIAS

BIAS&#xff08;乖离率&#xff09;是股票交易中常用的一种技术分析指标&#xff0c;用于衡量股票当前价格与其某一移动平均线之间的偏离程度。它的计算简单&#xff0c;易于理解和应用&#xff0c;因此在投资者和交易者中相当流行。 计算公式&#xff1a; BIAS的计算公式是&…

高效部署Modbus转MQTT网关:Modbus RTU、Modbus TCP转MQTT

钡铼Modbus转MQTT网关&#xff0c;简而言之&#xff0c;就是通过将Modbus协议&#xff08;包括Modbus RTU和Modbus TCP&#xff09;的数据转换为MQTT协议的数据格式&#xff0c;从而实现设备数据的上传和云端控制指令的下发。这一转换过程使得设备能够与基于MQTT协议的云平台进…

Linux_实现UDP网络通信

目录 1、实现服务器的逻辑 1.1 socket 1.2 bind 1.3 recvfrom 1.4 sendto 1.5 服务器代码 2、实现客户端的逻辑 2.1 客户端代码 3、实现通信 结语 前言&#xff1a; 在Linux下&#xff0c;实现传输层协议为UDP的套接字进行网络通信&#xff0c;网络层协议为IPv4&am…

Spring Boot集成Spire.doc实现对word的操作

1.什么是spire.doc? Spire.Doc for Java 是一款专业的 Java Word 组件&#xff0c;开发人员使用它可以轻松地将 Word 文档创建、读取、编辑、转换和打印等功能集成到自己的 Java 应用程序中。作为一款完全独立的组件&#xff0c;Spire.Doc for Java 的运行环境无需安装 Micro…

JMeter:BeanShell到JSR223迁移中的注意事项

前言 在之前的文章JMeter&#xff1a;BeanShell向JSR223迁移过程遭遇的java标准库不可用问题-如何切换JDK版本中引用了一段使用BeanShell对入参进行加密的脚本&#xff0c;迁移到JSR223&#xff0c;虽然更换JDK后编译通过&#xff0c;看似也可以执行了&#xff0c;但是其实那段…

AI绘画入门实践 | Midjourney:画面权重控制

在 Midjourney 中&#xff0c;使用两个连续的英文冒号::来进行分割与权重控制。 作为分隔符使用 在提示词中添加双冒号::表示让 MJ 将部分提示词单独考虑 2d illustration, french fries, hot dog --v 6 2d illustration, french fries, hot:: dog --v 6 作为权重控制使用 在双…

google 浏览器插件开发简单学习案例:TodoList;打包成crx离线包

参考&#xff1a; google插件支持&#xff1a; https://blog.csdn.net/weixin_42357472/article/details/140412993 这里是把前面做的TodoList做成google插件&#xff0c;具体网页可以参考下面链接 TodoList网页&#xff1a; https://blog.csdn.net/weixin_42357472/article/de…

docker基础镜像

一、配置 docker 本地源 [docker-ce-stable] nameDocker CE Stable baseurlhttp://10.35.186.181/docker-ce-stable/ enabled1 gpgcheck0 配置阿里云Docker Yum源 yum install -y yum-utils device-mapper-persistent-data lvm2 git yum-config-manager --add-repo http://mirr…

简单修改,让UE4/5着色器编译速度变快

简单修改&#xff0c;让UE4/5着色器编译速度变快 目录 简单修改&#xff0c;让UE4/5着色器编译速度变快 一、问题描述 二、解决方法 &#xff08;一&#xff09;硬件升级 &#xff08;二&#xff09;调整相关设置和提升优先级 1.调整相关设置 &#xff08;1&#xff09…

【Android】碎片的初识

之前我们学习的是一个活动作为一个页面&#xff0c;有了平板之后&#xff0c;页面如果像手机一样设计就会浪费很多的空间&#xff0c;会有很多的空白区域&#xff0c;为了使屏幕充分利用&#xff0c;引入了碎片这样一个概念。 碎片&#xff08;Fragment&#xff09;&#xff1…

pikachu之sql lnjet 字符型注入

先测试一下闭合 注释符号&#xff1a;-- 注释符号可以忽略其后的内容&#xff0c;使得后续的原始查询内容不会影响我们注入的SQL代码。 条件测试&#xff1a;通过and 11和and 12分别测试真假条件&#xff0c;可以判断输入是否成功闭合&#xff0c;并且可以检测注入是否成功。 …

构造+位运算,CF 1901C - Add, Divide and Floor

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1901C - Add, Divide and Floor 二、解题报告 1、思路分析 我们假设将原数组排序&#xff0c;那么每次操作不会改变数组单调性 当 最大值 调整等于 最小值时 所有数都相等&#xff0c;因为单调性不变&…