工作流审批功能是办公OA系统核心能力如果让你设计一个工作流审批系统你会吗千万不要小瞧OA内部系统的复杂性大家可以头脑风暴思考一下实现方案。要明白工作流审批涉及多个用户的任务流转多个流程分支跳转虽然是办公内部系统但是这个系统并不简单如果没有强大的工作流引擎难以高效扩展旧流程难以增加新流程工作流审批将成为公司所有人的噩梦但是在使用 activiti开源工作流引擎后一切痛苦与噩梦均烟消云散~activiti 支持新增流程非常简单只需要两步画个流程图搭配前端页面首先画一个流程图将文章开头的需求转化为 activiti 流程图使用Idea 安装 actiBPM 插件创建该流程图文件命名apply.mpmn实现请假流程的二级审批能力一级主管审批超过3天二级主管审批。测试流程图首先创建工作流引擎部署流程图//创建工作流引擎 ProcessEngine engine ProcessEngines.getDefaultProcessEngine(); //RepositoryService用于部署流程图 RepositoryService repositoryService engine.getRepositoryService(); //部署请假流程图 repositoryService.createDeployment().addClasspathResource(processes/apply.bpmn).deploy();部署流程图这部分工作一般放在工作流的后台系统开发创建好流程图以后上传部署到系统中。无需开发修改代码repositoryService.createDeployment().addClasspathResource(processes/apply.bpmn).deploy();这行代码负责部署流程图到流程引擎。 工作流引擎会解析该流程图文件创建流程模版接下来就可以在使用该流程模版发起流程实例了。员工zhang3发起新流程设置审批人MapString, Object variableMap new HashMap(); variableMap.put(applyUser, zhang3); variableMap.put(supervisor, li4); variableMap.put(upperSupervisor, wang5);员工zhang3 提出请假申请在发起新流程时通过OA其他系统查到zhang3的一级主管是li4二级主管是wang5于是设置上审批人。 有人疑问以下变量applyUser等是系统默认的还是在哪里指定的 在创建主管审批节点时指定审批人变量${applyUser}想象一下如果请假类的流程均可能需要一二级主管审批是不是可以在发起流程时统一填充一二级主管 审批人变量。这部分代码是不是就是通用的新增流程时无需二次修改了。发起一个新流程ProcessInstance instance runtimeService.startProcessInstanceByKey(apply_processor_1, variableMap);如上代码指定了全流程审批人发起了一个新流程。 有人会疑问apply_processor_1是什么在哪里指定的这是流程模版的 Key在使用Idea插件画流程图时需要指定流程图的Key申请人设置请假天数TaskService taskService engine.getTaskService(); Task firstTask taskService.createTaskQuery().taskAssignee(zhang3).singleResult(); taskService.complete(firstTask.getId(), Maps.newHashMap(day, 4));创建并开启流程实例后工作流引擎相当于帮你执行了 流程图的 开始节点然后流程执行到 请假申请节点此时通过taskService查询 zhang3的 处理任务。TaskService是通过第一步ProcessEngine获取到的主要用于任务查询。有人会疑问为什么要区分创建流程、处理申请人审批任务两个步骤我们在提请假申请时只需要提申请一步就完成了。 实际上 提请假申请时系统会帮你创建好流程然后自动替你完成审批。为什么工作流引擎要区分为两步呢所有的流程图都需要经过 开始节点也都需要结束节点如此设计方式可以让工作流引擎的抽象层次更高。它可以在开始和结束时点建立事件通知维护流程状态的完整性。接下来zhang3 通过taskService完成该任务并且设置变量 day4即请假天数是4天。一级主管审批任务Task secondTask taskService.createTaskQuery().taskAssignee(li4).singleResult(); taskService.setVariable(secondTask.getId(), result1, true);接下来zhang3和 领导li4 说”我家里有事要请假辛苦4哥审批一下“领导在自己的审批后台查询 审批任务查到后通过了审批任务。有人会疑问怎么标识 审批通过和不通过呢 result1 是什么东西系统默认的还是在何处甚至的变量? 在流程图上配置的工作流引擎没有审批通过不通过的概念。当流程上存在 A 和 B 两个分支时流程图上可以使用排他网关进行分支判定。如请假流程图中一级审批结果就是一个排他网关。在网关的下游分支上配置如果要走A分支应该满足哪些条件走B分支要满足哪个条件而排他网关上并没有配置路由条件。例如在一级主管审批后流程上设置新的变量 result1经过排他网关时A分支 是 #{result1true} 判定通过于是走了A分支。工作流引擎负责检查网关的下级分支的条件是否满足哪个条件满足走哪个分支。也就是说上一个任务在处理时并不知道接下来走哪个分支也没有指定走哪个分支而是将自己的处理结果放到流程变量中在排他网关的下游分支条件上根据流程变量进行判断接下来走哪个分支这就是工作流引擎对于流程的抽象。工作流引擎负责驱动流程到排他网关至于走哪个分支由分支上的条件决定 这个思维一定要记住哦~继续剩余的流程一级主管审批通过后需要判断是否需要二级审批这时又有一个排他网关使用的条件就是#{day3}或者#{day3}在第2步中zhang3 申请了 4天的假期于是走到了二级主管审批页面。二级主管wang5 在审批任务列表中找到了zhang3的请假申请然后点击了通过。Task thirdTask taskService.createTaskQuery().taskAssignee(wang5).singleResult(); if (thirdTask ! null) { taskService.setVariable(thirdTask.getId(), result2, true); log.warn(用户任务完成流程ID:{}, 任务名称:{}, id:{}, assignee:{}, thirdTask.getProcessInstanceId(), thirdTask.getName(), thirdTask.getId(), thirdTask.getAssignee(), thirdTask.getDelegationState()); taskService.complete(thirdTask.getId()); } else { log.warn(没有查到二级主管审批任务); }接下来流程上没有其他审批任务但是引擎依然会继续驱动流程如下图中二级主管审批结果通过或拒绝分别进入到两个不同的终点。值得一提的是上面的代码仅仅是各个审批人在处理审批任务时必要的代码、通用的代码。如审批是否通过这一流程变量完全可以统一规范无需二次开发。例如 二级主管审批通过设置了 result2实际上可以使用 二级主管审批这个节点的id 后缀如 result_10来代表执行结果规范以后流程审批代码更为统一。新增流程模版时在审批任务节点审批通过或审批拒绝均不需要再次开发代码。惊喜的事情是我们没有开发任何一行流程驱动和分支判定相关的代码。但是我们依然需要处理前端页面。因为请假申请页面上不同的假期类型需要的表单参数不同需要新增前端页面新增发起流程的后端接口。相比整体的流程控制代码这部分开发工作量大大降低难度更是大大降低。接下来我提出一个问题如果新增一个需求要求请病假的时候需要HR审批你知道如何修改流程图支持新的处理流程吗?很简单在审批通过终节点的前面再加一个排他网关判断请假类型 ”带薪病假“如果条件通过则增加HR审批节点如果条件不通过则直接走到 审批通过的 终点。不需要开发一行代码就能支持需求哦~有了工作流引擎 activiti ,新增和修改流程 都变得非常简单它帮我们完成了所有的流程驱动和分支判定工作。 这就是工作流引擎的价值之一。如何查看完整的执行流程图通过流程引擎ProcessEngine获取HistoryService通过流程实例id拿到流程执行的所有节点。执行记录如下图可完整看到流程执行的完整过程。ListHistoricActivityInstance activityInstanceList historyService.createHistoricActivityInstanceQuery().processInstanceId(instance.getId()).list(); for (HistoricActivityInstance historicActivityInstance : activityInstanceList) { log.warn(activityName:{},activityType:{}, assignee:{}, taskId:{}, historicActivityInstance.getActivityName(), historicActivityInstance.getActivityType(), historicActivityInstance.getAssignee(), historicActivityInstance.getTaskId());activiti 项目基础配置首先引入activiti pomdependency groupIdorg.activiti/groupId artifactIdactiviti-spring-boot-starter-basic/artifactId version5.23.0/version /dependency我使用的是activiti 5.x配套的Springboot starter parent是parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.5.4/version relativePath/ /parent配置数据源和工作流引擎配置类activiti 需要使用方提供数据库配置在项目启动时activiti 会自动检查数据库是否包含activiti 相关的表如果不包含会自动帮你建表。在学习activiti 的过程中我没有安装MySQL而是使用H2 内存数据库该数据库在Java进程中随JVM同生共死无需担心重复启动数据被污染等问题非常适合学习activiti 的时候使用?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd bean iddataSource classorg.apache.commons.dbcp.BasicDataSource property namedriverClassName valueorg.h2.Driver/ !--property nameurl valuejdbc:h2:mem:testdb;DB_CLOSE_DELAY-1;modeMySQL;TRACE_LEVEL_SYSTEM_OUT2/-- property nameurl valuejdbc:h2:mem:testdb;DB_CLOSE_DELAY-1;modeMySQL;/ /bean bean idprocessEngineConfiguration classorg.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration property namedataSource refdataSource/ property namedatabaseSchemaUpdate valuetrue/ /bean context:component-scan base-packagecom.muppet.activiti/ /beansH2 需要的POM如下dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependency全部示例代码SpringBootTest(classes {ActivitiStartApplication.class}) class ActivitiStartApplicationTests { publicstaticfinal Logger log LoggerFactory.getLogger(ActivitiStartApplicationTests.class); Autowired private ApplyTaskListener applyTaskListener; Test void contextLoads() { System.out.println(启动成功); ProcessEngine engine ProcessEngines.getDefaultProcessEngine(); RepositoryService repositoryService engine.getRepositoryService(); RuntimeService runtimeService engine.getRuntimeService(); TaskService taskService engine.getTaskService(); HistoryService historyService engine.getHistoryService(); repositoryService.createDeployment().addClasspathResource(processes/apply.bpmn).deploy(); MapString, Object variableMap new HashMap(); variableMap.put(applyUser, zhang3); variableMap.put(supervisor, li4); variableMap.put(upperSupervisor, wang5); ProcessInstance instance runtimeService.startProcessInstanceByKey(apply_processor_1, variableMap); Task firstTask taskService.createTaskQuery().taskAssignee(zhang3).singleResult(); log.warn(用户任务完成流程ID:{}, 任务名称 :{}, id:{}, assignee:{}, firstTask.getProcessInstanceId(), firstTask.getName(), firstTask.getId(), firstTask.getAssignee()); taskService.complete(firstTask.getId(), Maps.newHashMap(day, 4)); Task secondTask taskService.createTaskQuery().taskAssignee(li4).singleResult(); taskService.setVariable(secondTask.getId(), result1, true); log.warn(用户任务完成流程ID:{}, 任务名称 :{}, id:{}, assignee:{}, secondTask.getProcessInstanceId(), secondTask.getName(), secondTask.getId(), secondTask.getAssignee()); taskService.complete(secondTask.getId()); Task thirdTask taskService.createTaskQuery().taskAssignee(wang5).singleResult(); if (thirdTask ! null) { taskService.setVariable(thirdTask.getId(), result2, true); log.warn(用户任务完成流程ID:{}, 任务名称:{}, id:{}, assignee:{}, thirdTask.getProcessInstanceId(), thirdTask.getName(), thirdTask.getId(), thirdTask.getAssignee(), thirdTask.getDelegationState()); taskService.complete(thirdTask.getId()); } else { log.warn(没有查到二级主管审批任务); } log.warn(流程执行过程如下); ListHistoricActivityInstance activityInstanceList historyService.createHistoricActivityInstanceQuery().processInstanceId(instance.getId()).list(); for (HistoricActivityInstance historicActivityInstance : activityInstanceList) { log.warn(activityName:{},activityType:{}, assignee:{}, taskId:{}, historicActivityInstance.getActivityName(), historicActivityInstance.getActivityType(), historicActivityInstance.getAssignee(), historicActivityInstance.getTaskId()); } } }activiti 工作流引擎数据库设计为了保证流程的可靠性和可恢复性工作流引擎通常会将流程实例的状态和数据持久化存储到中。在流程执行过程中引擎会不断地更新数据库中的状态数据。activiti 共包含了一系列用于存储流程定义、运行时数据以及历史记录的表。1. 流程定义相关表ACT_RE_*系列表主要包括流程定义Process Definitions、流程资源Resources和其他静态信息的存储。2. 运行时数据表ACT_RU_*系列表这些表存放了流程实例执行过程中的实时数据如任务Tasks、流程实例Process Instances、变量Variables、执行对象Executions等。3. 历史数据表ACT_HI_*系列表当流程实例结束或达到特定条件时相关的运行时数据会被迁移到历史表中以供后期审计、报告分析之用。4. 身份和权限表ACT_ID_*系列表主要用于存储用户、组以及相关的身份和权限信息。5. 其他辅助表包括事件日志表Event Log、作业及定时器表Job and Timer entities等它们服务于调度、异步处理等功能需求。工作流引擎API 介绍ProcessEngine:表示Activiti工作流引擎的入口用于获取各种管理API操作的对象。RepositoryService:用于管理流程定义的API包括流程的部署和删除等操作。RuntimeService:用于管理流程实例的API包括启动、暂停和删除流程实例等操作。TaskService:用于管理任务的API包括创建、完成和查询任务等操作。HistoryService:用于查询历史记录的API包括查询已完成的任务、流程实例和变量等信息。这5个Service我们已经很熟悉了现在跟大家介绍这部分API大家应该更容易理解了。其中ProcessEngine我们用来获取各类Service类RepositoryService用来部署流程图RuntimeService用来创建流程图实例、TaskService用来查询任务和完成任务HistoryService用来查看流程执行过程。什么是BPMN流程图BPMNBusiness Process Modeling Notation是一种流程建模的通用和标准语言用来绘制业务流程图以便更好地让各部门之间理解业务流程和相互关系。BPMN 1.0规范于2004年5月对外发布而BPMN 2.0标准由OMG于2011年推出对BPMN进行了重新定义。Activiti 是由 jBPM 的创建者 Tom Baeyens 离开 JBoss 之后建立的项目构建在开发 jBPM 版本 1 到 4 时积累的多年经验的基础之上旨在创建下一代的 BPM 解决方案。同时 Activiti 选择了 Apache 许可一方面是希望 Activiti 能有更长久的生命力因为它不受任何个人或是公司的控制而是属于整个社区另一方面更是希望这个宽松的许可能够让 Activiti BPM 引擎和 BPMN2.0 被更广泛的采纳、使用和商业化。Idea actiBPM 插件如何安装https://www.cnblogs.com/No2-explorer/p/11032469.htmlIdea新建的流程图是什么本质是一个XML可以通过BPMN 可视化工具解析如下XML代码是 请假流程图创建一个二级主管审批节点userTask activiti:assignee${upperSupervisor} activiti:asyncfalse activiti:exclusivetrue id_10 name二级主管审批/创建一个审批结果的分支分支上包含连接了哪两个节点以及分支的条件表达式#{result2true}sequenceFlow id_19 sourceRef_17 targetRef_8 conditionExpression xsi:typetFormalExpression![CDATA[#{result2false}]]/conditionExpression /sequenceFlow sequenceFlow id_20 sourceRef_17 targetRef_11 conditionExpression xsi:typetFormalExpression![CDATA[#{result2true}]]/conditionExpression /sequenceFlow请假流程图完整XML?xml version1.0 encodingUTF-8 standaloneyes? definitions xmlnshttp://www.omg.org/spec/BPMN/20100524/MODEL xmlns:activitihttp://activiti.org/bpmn xmlns:bpmndihttp://www.omg.org/spec/BPMN/20100524/DI xmlns:omgdchttp://www.omg.org/spec/DD/20100524/DC xmlns:omgdihttp://www.omg.org/spec/DD/20100524/DI xmlns:tnshttp://www.activiti.org/test xmlns:xsdhttp://www.w3.org/2001/XMLSchema xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance expressionLanguagehttp://www.w3.org/1999/XPath idm1723259512248 name targetNamespacehttp://www.activiti.org/test typeLanguagehttp://www.w3.org/2001/XMLSchema process idapply_processor_1 isClosedfalse isExecutabletrue processTypeNone startEvent id_4 name开始 extensionElements activiti:executionListener classcom.muppet.activiti.listener.ApplyTaskListener eventend/ /extensionElements /startEvent userTask activiti:assignee#{applyUser} activiti:asyncfalse activiti:exclusivetrue id_5 name请假申请/ userTask activiti:assignee${supervisor} activiti:asyncfalse activiti:exclusivetrue id_6 name主管审批 extensionElements activiti:executionListener classcom.muppet.activiti.listener.ApplyTaskListener eventend/ /extensionElements /userTask exclusiveGateway gatewayDirectionUnspecified id_7 name一级审批结果/ endEvent id_8 name审批不通过 extensionElements activiti:executionListener classcom.muppet.activiti.listener.ApplyTaskListener eventend/ /extensionElements /endEvent exclusiveGateway gatewayDirectionUnspecified id_9 name天数验证2/ userTask activiti:assignee${upperSupervisor} activiti:asyncfalse activiti:exclusivetrue id_10 name二级主管审批/ endEvent id_11 name审批通过 extensionElements activiti:executionListener classcom.muppet.activiti.listener.ApplyTaskListener eventend/ /extensionElements /endEvent sequenceFlow id_2 sourceRef_4 targetRef_5/ sequenceFlow id_3 sourceRef_5 targetRef_6/ sequenceFlow id_12 sourceRef_6 targetRef_7/ sequenceFlow id_13 sourceRef_7 targetRef_8 conditionExpression xsi:typetFormalExpression![CDATA[#{result1false}]]/conditionExpression /sequenceFlow sequenceFlow id_14 sourceRef_7 targetRef_9 conditionExpression xsi:typetFormalExpression![CDATA[#{result1true}]]/conditionExpression /sequenceFlow sequenceFlow id_15 sourceRef_9 targetRef_10 conditionExpression xsi:typetFormalExpression![CDATA[#{day3}]]/conditionExpression /sequenceFlow sequenceFlow id_16 sourceRef_9 targetRef_11 conditionExpression xsi:typetFormalExpression![CDATA[#{day3}]]/conditionExpression /sequenceFlow exclusiveGateway gatewayDirectionUnspecified id_17 name审批结果2/ sequenceFlow id_18 sourceRef_10 targetRef_17/ sequenceFlow id_19 sourceRef_17 targetRef_8 conditionExpression xsi:typetFormalExpression![CDATA[#{result2false}]]/conditionExpression /sequenceFlow sequenceFlow id_20 sourceRef_17 targetRef_11 conditionExpression xsi:typetFormalExpression![CDATA[#{result2true}]]/conditionExpression /sequenceFlow /process bpmndi:BPMNDiagram documentationbackground#3C3F41;count1;horizontalcount1;orientation0;width842.4;height1195.2;imageableWidth832.4;imageableHeight1185.2;imageableX5.0;imageableY5.0 idDiagram-_1 nameNew Diagram bpmndi:BPMNPlane bpmnElementapply_processor_1 bpmndi:BPMNShape bpmnElement_4 idShape-_4 omgdc:Bounds height32.0 width32.0 x525.0 y170.0/ bpmndi:BPMNLabel omgdc:Bounds height32.0 width32.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_5 idShape-_5 omgdc:Bounds height55.0 width85.0 x495.0 y285.0/ bpmndi:BPMNLabel omgdc:Bounds height55.0 width85.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_6 idShape-_6 omgdc:Bounds height55.0 width85.0 x500.0 y390.0/ bpmndi:BPMNLabel omgdc:Bounds height55.0 width85.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_7 idShape-_7 isMarkerVisiblefalse omgdc:Bounds height32.0 width32.0 x520.0 y495.0/ bpmndi:BPMNLabel omgdc:Bounds height32.0 width32.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_8 idShape-_8 omgdc:Bounds height32.0 width32.0 x295.0 y825.0/ bpmndi:BPMNLabel omgdc:Bounds height32.0 width32.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_9 idShape-_9 isMarkerVisiblefalse omgdc:Bounds height32.0 width32.0 x515.0 y600.0/ bpmndi:BPMNLabel omgdc:Bounds height32.0 width32.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_10 idShape-_10 omgdc:Bounds height55.0 width95.0 x485.0 y720.0/ bpmndi:BPMNLabel omgdc:Bounds height55.0 width95.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_11 idShape-_11 omgdc:Bounds height32.0 width32.0 x720.0 y600.0/ bpmndi:BPMNLabel omgdc:Bounds height32.0 width32.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNShape bpmnElement_17 idShape-_17 isMarkerVisiblefalse omgdc:Bounds height32.0 width32.0 x510.0 y810.0/ bpmndi:BPMNLabel omgdc:Bounds height32.0 width32.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNShape bpmndi:BPMNEdge bpmnElement_13 idBPMNEdge__13 sourceElement_7 targetElement_8 omgdi:waypoint x520.1953352769677 y511.0000000000001/ omgdi:waypoint x310.0 y700.0/ omgdi:waypoint x310.0 y825.0808012104277/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_12 idBPMNEdge__12 sourceElement_6 targetElement_7 omgdi:waypoint x535.9999999999999 y444.97894395853575/ omgdi:waypoint x535.9999999999999 y495.19533527696785/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_15 idBPMNEdge__15 sourceElement_9 targetElement_10 omgdi:waypoint x530.9999999999999 y632.3606171769437/ omgdi:waypoint x530.9999999999999 y719.6793002915451/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_14 idBPMNEdge__14 sourceElement_7 targetElement_9 omgdi:waypoint x533.4999999999999 y524.4297052154194/ omgdi:waypoint x533.4999999999999 y602.2445273261599/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_16 idBPMNEdge__16 sourceElement_9 targetElement_11 omgdi:waypoint x546.8934865508442 y616.0/ omgdi:waypoint x719.6793107624761 y616.0/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_19 idBPMNEdge__19 sourceElement_17 targetElement_8 omgdi:waypoint x526.0000000000001 y842.0919987042433/ omgdi:waypoint x420.0 y880.0/ omgdi:waypoint x311.0 y857.0256933063517/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width4.0 x0.0 y-10.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_18 idBPMNEdge__18 sourceElement_10 targetElement_17 omgdi:waypoint x526.0000000000001 y774.9649065975595/ omgdi:waypoint x526.0000000000001 y810.6344887161214/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_2 idBPMNEdge__2 sourceElement_4 targetElement_5 omgdi:waypoint x541.0 y202.23782176749842/ omgdi:waypoint x541.0 y285.1689882302127/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_3 idBPMNEdge__3 sourceElement_5 targetElement_6 omgdi:waypoint x540.0 y339.5907569376957/ omgdi:waypoint x540.0 y389.6933376525213/ bpmndi:BPMNLabel omgdc:Bounds height0.0 width0.0 x0.0 y0.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge bpmndi:BPMNEdge bpmnElement_20 idBPMNEdge__20 sourceElement_17 targetElement_11 omgdi:waypoint x541.3655112838787 y826.0/ omgdi:waypoint x659.0 y724.0/ omgdi:waypoint x719.6793107624761 y616.0/ bpmndi:BPMNLabel omgdc:Bounds height1.0 width127.0 x0.0 y76.0/ /bpmndi:BPMNLabel /bpmndi:BPMNEdge /bpmndi:BPMNPlane /bpmndi:BPMNDiagram /definitions工作流引擎同类对比比较流行的 Camunda 和 Flowable 都是基于activiti 开发的。同类框架对比3个框架的使用流程基本一致使用流程 5板斧定义BPMN流程图使用建模工具设计定义流程图。部署流程将BPMN流程图部署到工作流引擎中。启动流程实例通过工作流引擎基于流程模版启动新流程实例。执行任务流程执行中引擎会为任务节点创建任务分配给对应执行人。监听事件开发者可以注册监听器来捕获流程执行过程中的各种事件例如任务完成、流程结束等。查询和监控工作流引擎通常提供了查询和监控功能允许开发者和管理人员查看流程实例的状态、任务执行情况以及历史数继续学习方向事件类型和事件监听。任务类型接受任务、服务任务、脚本任务学习任务监听和执行监听器表单管理顺序流程和网关并行网关等性能和扩展ID生成和 分库分表总结activiti 工作流引擎适用场景涉及多用户参与的流程管理。activiti 工作流开发分两步 1设计流程图 2部署流程图 3) 发起和驱动流程实例新增流程模版无需开发代码驱动流程和分支判定。仅需要适配前端页面和简单的后端交互类接口。