0相对于1.0规范了流程引擎的语义和格式,利用标准的图元描述真实的业务发生过程,保证相同的流程在不同的流程引擎中得到一致的执行结果。
BPMN的理论基础
- 流程建模:BPMN使用不同的图形符号来表示业务流程中的各项活动、任务、决策和事件等。这些符号包括任务、网关、事件等,每个符号都有特定的含义和用法。
- 流程分析:通过BPMN模型,企业可以分析业务流程的效率、资源利用率和风险等,以便进行优化和改进。
- 流程执行:BPMN不仅是一种建模语言,还可以将建模的业务流程实际执行起来,并进行监控和控制。
BPMN的优势
图片
传统使用场景
- 采购流程:通过BPMN建模,企业可以规范采购流程,包括需求确认、供应商选择、合同签订、物品收货等环节,提高采购效率。
- 请假审批流程:BPMN模型可以帮助企业规范请假审批流程,包括员工请假申请、直属主管审批、人力资源部门审批等环节,减少误差和纠纷。
- 客户投诉处理流程:利用BPMN,企业可以清晰地展示客户投诉处理流程,包括客户投诉登记、处理人员分配、处理过程跟踪、客户反馈等环节,提高客户满意度。
三、实践举例:XX平台XXX商家合规治理任务优化项目(非真实场景)
背景目标
- 商家合规治理手段的单一性和对线下人工的过度依赖方面需要优化,通过整合商家合规治理策略,并实现这些策略的可在线化配置。这一举措将打破传统的手工操作模式,允许业务方直接在系统中配置和更新治理策略,减少人工干预,提高治理效率。同时,在线化配置也便于策略的快速部署和灵活调整,以应对不同业务场景下的合规要求。
- 各商家的合规治理进度和状态的透明度方面需要优化,为改变这一现状,我们需要搭建一套高效的工作流引擎。该引擎将支持商家合规治理流程的编排和自动化流转,从任务触发到完成审核、记录反馈等各个环节都将实现自动化处理。同时,内置的进度跟踪机制将确保业务团队能够实时查看各商家的治理进度,及时发现问题并采取相应措施。这将大大提高治理的透明度和可控性。
- 评估治理成果和手段的有效性分析方面需要优化,将合规治理成果的数据可视化,通过强大的数据分析与可视化系统,我们将关键指标(如治理效率、违规率、改进情况等)以图表、报告等形式直观展现。同时,通过持续的数据分析与改进循环,我们将逐步优化治理策略和方式,最终形成商家合规治理的闭环机制。
方案
业务流程
治理流程1
治理流程2
系统架构图
流程编排
工作流技术选型
图片
SmartEngine详细介绍
E-R图
图片
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依赖中。
第二步,完成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举例
附言:排他网关的条件表达式是跟着线条绑定的,要自己确保条件的是和否加在一起的总概率是100%,如果不是100%,小概率执行节点会卡在排他网关走不下去。例如:线条“否”的条件表达式为:条件1为假且条件2为假,线条“是”的条件表达式为条件1为真且条件2为真,则其他条件会卡死在排他网关流程走不下去。
test1.bpmn
Flow_0soou9n
Flow_07mf0sc
Flow_1nd3369
Flow_160ji3a
Flow_0exjsc9
Flow_0hj860q
Flow_1oqjt08
Flow_0soou9n
Flow_0exjsc9
Flow_0hj860q
Flow_0bemgki
Flow_0bemgki
Flow_07xq405
Flow_1w4qeti
Flow_1oqjt08
Flow_1t7ectj
Flow_07xq405
Flow_07mf0sc
Flow_1t7ectj
Flow_1nd3369
Flow_1w4qeti
Flow_0vr5zs3
Flow_0vr5zs3
Flow_160ji3a
- process,表示一个流程。
- id="exclusiveTest" versinotallow="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,不建议使用。