-
画个流程图 -
搭配前端页面
-
一级主管审批 -
超过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,发起新流程,设置审批人
Map<String, 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);
TaskService taskService = engine.getTaskService();
Task firstTask = taskService.createTaskQuery().taskAssignee("zhang3").singleResult();
taskService.complete(firstTask.getId(), Maps.newHashMap("day", 4));
taskService 查询 zhang3的 处理任务。TaskService是通过第一步ProcessEngine 获取到的,主要用于任务查询。
taskService完成该任务,并且设置变量 day=4,即请假天数是4天。
Task secondTask = taskService.createTaskQuery().taskAssignee("li4").singleResult();
taskService.setVariable(secondTask.getId(), "result1", true);
#{day>3},或者 #{day<=3}
在第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("没有查到二级主管审批任务");
}
HistoryService通过流程引擎 ProcessEngine获取HistoryService;通过流程实例id,拿到流程执行的所有节点。执行记录如下图,可完整看到流程执行的完整过程。
List<HistoricActivityInstance> 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());
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>5.23.0</version>
</dependency>
我使用的是activiti 5.x,配套的 Springboot starter parent是
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/>
</parent>
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://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">
<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource">
<propertyname="driverClassName"value="org.h2.Driver"/>
<!--<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;mode=MySQL;TRACE_LEVEL_SYSTEM_OUT=2"/>-->
<propertyname="url"value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;mode=MySQL;"/>
</bean>
<beanid="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<propertyname="dataSource"ref="dataSource"/>
<propertyname="databaseSchemaUpdate"value="true"/>
</bean>
<context:component-scanbase-package="com.muppet.activiti"/>
</beans>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
@SpringBootTest(classes = {ActivitiStartApplication.class})
classActivitiStartApplicationTests{
publicstaticfinal Logger log = LoggerFactory.getLogger(ActivitiStartApplicationTests.class);
@Autowired
private ApplyTaskListener applyTaskListener;
@Test
voidcontextLoads(){
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();
Map<String, 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("流程执行过程如下");
List<HistoricActivityInstance> 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());
}
}
}
ACT_RE_*系列表:主要包括流程定义(Process Definitions)、流程资源(Resources)和其他静态信息的存储。
ACT_RU_*系列表:这些表存放了流程实例执行过程中的实时数据,如任务(Tasks)、流程实例(Process Instances)、变量(Variables)、执行对象(Executions)等。
ACT_HI_*系列表:当流程实例结束或达到特定条件时,相关的运行时数据会被迁移到历史表中,以供后期审计、报告分析之用。
ACT_ID_*系列表:主要用于存储用户、组以及相关的身份和权限信息。
-
ProcessEngine: 表示Activiti工作流引擎的入口,用于获取各种管理API操作的对象。 -
RepositoryService: 用于管理流程定义的API,包括流程的部署和删除等操作。 -
RuntimeService: 用于管理流程实例的API,包括启动、暂停和删除流程实例等操作。 -
TaskService: 用于管理任务的API,包括创建、完成和查询任务等操作。 -
HistoryService: 用于查询历史记录的API,包括查询已完成的任务、流程实例和变量等信息。
ProcessEngine我们用来获取各类Service类,RepositoryService 用来部署流程图,RuntimeService用来创建流程图实例、TaskService用来查询任务和完成任务;HistoryService用来查看流程执行过程。
<userTaskactiviti:assignee="${upperSupervisor}"activiti:async="false"activiti:exclusive="true"id="_10"name="二级主管审批"/>
#{result2==true}
<sequenceFlowid="_19"sourceRef="_17"targetRef="_8">
<conditionExpressionxsi:type="tFormalExpression"><![CDATA[#{result2==false}]]></conditionExpression>
</sequenceFlow>
<sequenceFlowid="_20"sourceRef="_17"targetRef="_11">
<conditionExpressionxsi:type="tFormalExpression"><![CDATA[#{result2==true}]]></conditionExpression>
</sequenceFlow>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1723259512248" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="apply_processor_1" isClosed="false" isExecutable="true" processType="None">
<startEvent id="_4" name="开始">
<extensionElements>
<activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
</extensionElements>
</startEvent>
<userTask activiti:assignee="#{applyUser}" activiti:async="false" activiti:exclusive="true" id="_5" name="请假申请"/>
<userTask activiti:assignee="${supervisor}" activiti:async="false" activiti:exclusive="true" id="_6" name="主管审批">
<extensionElements>
<activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
</extensionElements>
</userTask>
<exclusiveGateway gatewayDirection="Unspecified" id="_7" name="一级审批结果"/>
<endEvent id="_8" name="审批不通过">
<extensionElements>
<activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
</extensionElements>
</endEvent>
<exclusiveGateway gatewayDirection="Unspecified" id="_9" name="天数验证2"/>
<userTask activiti:assignee="${upperSupervisor}" activiti:async="false" activiti:exclusive="true" id="_10" name="二级主管审批"/>
<endEvent id="_11" name="审批通过">
<extensionElements>
<activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/>
</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:type="tFormalExpression"><![CDATA[#{result1==false}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_14" sourceRef="_7" targetRef="_9">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result1==true}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_15" sourceRef="_9" targetRef="_10">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[#{day>3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_16" sourceRef="_9" targetRef="_11">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[#{day<=3}]]></conditionExpression>
</sequenceFlow>
<exclusiveGateway gatewayDirection="Unspecified" id="_17" name="审批结果2"/>
<sequenceFlow id="_18" sourceRef="_10" targetRef="_17"/>
<sequenceFlow id="_19" sourceRef="_17" targetRef="_8">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==false}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_20" sourceRef="_17" targetRef="_11">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==true}]]></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="apply_processor_1">
<bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
<omgdc:Bounds height="32.0" width="32.0" x="525.0" y="170.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
<omgdc:Bounds height="55.0" width="85.0" x="495.0" y="285.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
<omgdc:Bounds height="55.0" width="85.0" x="500.0" y="390.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="520.0" y="495.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_8" id="Shape-_8">
<omgdc:Bounds height="32.0" width="32.0" x="295.0" y="825.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="515.0" y="600.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_10" id="Shape-_10">
<omgdc:Bounds height="55.0" width="95.0" x="485.0" y="720.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="95.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_11" id="Shape-_11">
<omgdc:Bounds height="32.0" width="32.0" x="720.0" y="600.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_17" id="Shape-_17" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="510.0" y="810.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13" sourceElement="_7" targetElement="_8">
<omgdi:waypoint x="520.1953352769677" y="511.0000000000001"/>
<omgdi:waypoint x="310.0" y="700.0"/>
<omgdi:waypoint x="310.0" y="825.0808012104277"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_7">
<omgdi:waypoint x="535.9999999999999" y="444.97894395853575"/>
<omgdi:waypoint x="535.9999999999999" y="495.19533527696785"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_15" id="BPMNEdge__15" sourceElement="_9" targetElement="_10">
<omgdi:waypoint x="530.9999999999999" y="632.3606171769437"/>
<omgdi:waypoint x="530.9999999999999" y="719.6793002915451"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_14" id="BPMNEdge__14" sourceElement="_7" targetElement="_9">
<omgdi:waypoint x="533.4999999999999" y="524.4297052154194"/>
<omgdi:waypoint x="533.4999999999999" y="602.2445273261599"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_16" id="BPMNEdge__16" sourceElement="_9" targetElement="_11">
<omgdi:waypoint x="546.8934865508442" y="616.0"/>
<omgdi:waypoint x="719.6793107624761" y="616.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_19" id="BPMNEdge__19" sourceElement="_17" targetElement="_8">
<omgdi:waypoint x="526.0000000000001" y="842.0919987042433"/>
<omgdi:waypoint x="420.0" y="880.0"/>
<omgdi:waypoint x="311.0" y="857.0256933063517"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="4.0" x="0.0" y="-10.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_18" id="BPMNEdge__18" sourceElement="_10" targetElement="_17">
<omgdi:waypoint x="526.0000000000001" y="774.9649065975595"/>
<omgdi:waypoint x="526.0000000000001" y="810.6344887161214"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_2" id="BPMNEdge__2" sourceElement="_4" targetElement="_5">
<omgdi:waypoint x="541.0" y="202.23782176749842"/>
<omgdi:waypoint x="541.0" y="285.1689882302127"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_3" id="BPMNEdge__3" sourceElement="_5" targetElement="_6">
<omgdi:waypoint x="540.0" y="339.5907569376957"/>
<omgdi:waypoint x="540.0" y="389.6933376525213"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_20" id="BPMNEdge__20" sourceElement="_17" targetElement="_11">
<omgdi:waypoint x="541.3655112838787" y="826.0"/>
<omgdi:waypoint x="659.0" y="724.0"/>
<omgdi:waypoint x="719.6793107624761" y="616.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="1.0" width="127.0" x="0.0" y="76.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
-
定义BPMN流程图, 使用建模工具设计定义流程图。 -
部署流程, 将BPMN流程图部署到工作流引擎中。 -
启动流程实例, 通过工作流引擎基于流程模版,启动新流程实例。 -
执行任务, 流程执行中,引擎会为任务节点创建任务,分配给对应执行人。 -
监听事件, 开发者可以注册监听器来捕获流程执行过程中的各种事件,例如任务完成、流程结束等。 -
查询和监控, 工作流引擎通常提供了查询和监控功能,允许开发者和管理人员查看流程实例的状态、任务执行情况以及历史数
-
事件类型和事件监听。 -
任务类型;接受任务、服务任务、脚本任务学习 -
任务监听和执行监听器 -
表单管理 -
顺序流程和网关(并行网关等) -
性能和扩展;ID生成和 分库分表
-
activiti 工作流引擎适用场景,涉及多用户参与的流程管理。 -
activiti 工作流开发分两步 1)设计流程图 2)部署流程图 3) 发起和驱动流程实例 -
新增流程模版,无需开发代码驱动流程和分支判定。仅需要适配前端页面和简单的后端交互类接口。
往期推荐
凌晨两点,我因为 14 寸笔记本的 9 号字体把线上事故从 P0 拖成了 P1
还在手动发包?全网最全的 Jenkins + Maven + Git 自动化部署指南!
别再只会mvn install了!深入拆解Maven插件核心原理
Java + LangChain = 王炸!
盘一盘 XXL-JOB 中那些惊艳的架构设计
我用 Java 把二维码玩出花了

