在之前的文章已经介绍过Activiti7的基本使用,现在就使用springboot整合Activiti7,完成一个完整的请假流程。
Activiti7 发布正式版之后,它与 SpringBoot2.x 已经完全支持整合开发。
Activti7 新支持ProcessRuntime 接口和TaskRuntime 接口,我没有尝试用这两个接口,想用的朋友可以自己研究。
首先创建一个springboot项目,添加Activiti依赖,如下:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.Beta2</version>
</dependency>
因为 Activiti7 与 SpringBoot 整合后,默认情况下,集成了 SpringSecurity 安全框架,所以我们就要去准备 SpringSecurity 整合进来的相关用户权限配置信息(该配置我就不贴出来了)。
项目搭建好后,就要画流程图:
整个流程是这样:请假人填写请假信息提交后,就开启一个流程实例,并完成“填写请假信息”任务,然后到“部门主管审批”,审批不通过就返回“填写请假信息”,审批通过并且请假天数小于等于5天,就转到“人事存档”,审批通过并且请假天数大于5天,转到“总经理审批”,经理审批不通过就跳回“填写请假信息”,经理审批通过就转到“人事存档”,“人事存档”完成任务就转到“行政考勤”和“财务统计”,当“行政考勤”和“财务统计”都完成任务,整个流程结束。
下边是流程图的详细信息:
<?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="m1574936357289" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="qingjia_1" isClosed="false" isExecutable="true" name="请假申请流程" processType="None">
<startEvent id="_2" name="请假申请开始"/>
<userTask activiti:exclusive="true" id="_3" name="填写请假信息"/>
<userTask activiti:exclusive="true" id="_4" name="部门主管审批">
<extensionElements>
<activiti:taskListener class="com.lxm.sbactiviti.listener.ZhuGuanListener" event="create"/>
</extensionElements>
</userTask>
<exclusiveGateway gatewayDirection="Unspecified" id="_5" name="ExclusiveGateway"/>
<userTask activiti:exclusive="true" id="_6" name="总经理审批">
<extensionElements>
<activiti:taskListener class="com.lxm.sbactiviti.listener.ZongJingLiListener" event="create"/>
</extensionElements>
</userTask>
<exclusiveGateway gatewayDirection="Unspecified" id="_7" name="ExclusiveGateway"/>
<userTask activiti:exclusive="true" id="_8" name="人事存档">
<extensionElements>
<activiti:taskListener class="com.lxm.sbactiviti.listener.RenShiListener" event="create"/>
</extensionElements>
</userTask>
<parallelGateway gatewayDirection="Unspecified" id="_9" name="ParallelGateway"/>
<userTask activiti:exclusive="true" id="_10" name="行政考勤">
<extensionElements>
<activiti:taskListener class="com.lxm.sbactiviti.listener.XingZhengListener" event="create"/>
</extensionElements>
</userTask>
<userTask activiti:exclusive="true" id="_11" name="财务统计">
<extensionElements>
<activiti:taskListener class="com.lxm.sbactiviti.listener.CaiWuListener" event="create"/>
</extensionElements>
</userTask>
<parallelGateway gatewayDirection="Unspecified" id="_12" name="ParallelGateway"/>
<endEvent id="_13" name="请假申请结束"/>
<sequenceFlow id="_14" sourceRef="_2" targetRef="_3"/>
<sequenceFlow id="_15" sourceRef="_3" targetRef="_4"/>
<sequenceFlow id="_16" sourceRef="_4" targetRef="_5"/>
<sequenceFlow id="_17" sourceRef="_5" targetRef="_8">
<conditionExpression xsi:type="tFormalExpression"></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_18" sourceRef="_5" targetRef="_6">
<conditionExpression xsi:type="tFormalExpression">5}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_19" sourceRef="_6" targetRef="_7"/>
<sequenceFlow id="_20" sourceRef="_7" targetRef="_8">
<conditionExpression xsi:type="tFormalExpression"></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_21" sourceRef="_8" targetRef="_9"/>
<sequenceFlow id="_22" sourceRef="_9" targetRef="_10"/>
<sequenceFlow id="_23" sourceRef="_9" targetRef="_11"/>
<sequenceFlow id="_24" sourceRef="_10" targetRef="_12"/>
<sequenceFlow id="_25" sourceRef="_11" targetRef="_12"/>
<sequenceFlow id="_26" sourceRef="_12" targetRef="_13"/>
<sequenceFlow id="_27" sourceRef="_7" targetRef="_3">
<conditionExpression xsi:type="tFormalExpression"></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_28" sourceRef="_5" targetRef="_3">
<conditionExpression xsi:type="tFormalExpression"></conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram documentation="background=#FFFFFF;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="qingjia_1">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="175.0" y="5.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="55.0" width="85.0" x="150.0" y="95.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
<omgdc:Bounds height="55.0" width="85.0" x="150.0" y="195.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="175.0" y="305.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.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="295.0" y="295.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="320.0" y="430.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="55.0" width="85.0" x="150.0" y="420.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9">
<omgdc:Bounds height="32.0" width="32.0" x="250.0" y="520.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="85.0" x="155.0" y="585.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_11" id="Shape-_11">
<omgdc:Bounds height="55.0" width="85.0" x="300.0" y="585.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_12" id="Shape-_12">
<omgdc:Bounds height="32.0" width="32.0" x="255.0" y="680.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_13" id="Shape-_13">
<omgdc:Bounds height="32.0" width="32.0" x="255.0" y="760.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_15" id="BPMNEdge__15" sourceElement="_3" targetElement="_4">
<omgdi:waypoint x="192.5" y="150.0"/>
<omgdi:waypoint x="192.5" y="195.0"/>
<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="_2" targetElement="_3">
<omgdi:waypoint x="191.0" y="37.0"/>
<omgdi:waypoint x="191.0" y="95.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_17" id="BPMNEdge__17" sourceElement="_5" targetElement="_8">
<omgdi:waypoint x="191.0" y="337.0"/>
<omgdi:waypoint x="191.0" y="420.0"/>
<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="_4" targetElement="_5">
<omgdi:waypoint x="191.0" y="250.0"/>
<omgdi:waypoint x="191.0" y="305.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="_6" targetElement="_7">
<omgdi:waypoint x="336.0" y="350.0"/>
<omgdi:waypoint x="336.0" y="430.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_18" id="BPMNEdge__18" sourceElement="_5" targetElement="_6">
<omgdi:waypoint x="207.0" y="321.0"/>
<omgdi:waypoint x="295.0" y="322.5"/>
<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="_7" targetElement="_8">
<omgdi:waypoint x="320.0" y="446.0"/>
<omgdi:waypoint x="235.0" y="447.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_22" id="BPMNEdge__22" sourceElement="_9" targetElement="_10">
<omgdi:waypoint x="265.0" y="551.0"/>
<omgdi:waypoint x="265.0" y="575.0"/>
<omgdi:waypoint x="240.0" y="612.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_21" id="BPMNEdge__21" sourceElement="_8" targetElement="_9">
<omgdi:waypoint x="190.0" y="475.0"/>
<omgdi:waypoint x="190.0" y="505.0"/>
<omgdi:waypoint x="250.0" y="536.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_24" id="BPMNEdge__24" sourceElement="_10" targetElement="_12">
<omgdi:waypoint x="195.0" y="640.0"/>
<omgdi:waypoint x="195.0" y="670.0"/>
<omgdi:waypoint x="255.0" y="696.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_23" id="BPMNEdge__23" sourceElement="_9" targetElement="_11">
<omgdi:waypoint x="282.0" y="536.0"/>
<omgdi:waypoint x="340.0" y="575.0"/>
<omgdi:waypoint x="340.0" y="585.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_26" id="BPMNEdge__26" sourceElement="_12" targetElement="_13">
<omgdi:waypoint x="271.0" y="712.0"/>
<omgdi:waypoint x="271.0" y="760.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_25" id="BPMNEdge__25" sourceElement="_11" targetElement="_12">
<omgdi:waypoint x="340.0" y="640.0"/>
<omgdi:waypoint x="340.0" y="660.0"/>
<omgdi:waypoint x="287.0" y="696.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_28" id="BPMNEdge__28" sourceElement="_5" targetElement="_3">
<omgdi:waypoint x="175.0" y="321.0"/>
<omgdi:waypoint x="100.0" y="220.0"/>
<omgdi:waypoint x="150.0" y="122.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_27" id="BPMNEdge__27" sourceElement="_7" targetElement="_3">
<omgdi:waypoint x="352.0" y="446.0"/>
<omgdi:waypoint x="420.0" y="300.0"/>
<omgdi:waypoint x="235.0" y="122.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
在除了“填写请假信息”外的所有任务,都添加了监听器,作用是在这些任务创建时,添加任务的候选人,然后这些候选人都可以查看到这些任务,然后可以领取任务并完成任务。在排他网关的出口都添加判断条件,这些条件在上面xml中都有。
然后我们启动springboot项目,发现数据库表只有17张,但不影响操作;
首先部署流程:
//部署流程定义
@RequestMapping(value= "deploy/{zipName}")
@ResponseBody
public Respon deploy(@PathVariable(name="zipName")String zipName){
Respon respon=new Respon();
//转化出ZipInputStream流对象
InputStream is = TestController.class.getClassLoader().getResourceAsStream("diagram/"+zipName+".zip");
//将 inputstream流转化为ZipInputStream流
ZipInputStream zipInputStream = new ZipInputStream(is);
//进行部署
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream).deploy();
//输出部署的一些信息
System.out.println(deployment.getId());
respon.setCode(Constant.CODE_SUCCESS);
respon.setMsg(Constant.CODE_SUCCESS_STRING);
return respon;
}
开始请假
//开始一个请假流程,并完成填写请假申请信息
@RequestMapping(value= "startTest/{userName}/{totalDay}")
@ResponseBody
public Respon startTest(@PathVariable(name="userName")String userName, @PathVariable(name="totalDay")Float totalDay){
Respon respon=new Respon();
String key="qingjia_1";
ProcessInstance processInstance=runtimeService.startProcessInstanceByKey(key);
String processInstanceId=processInstance.getProcessInstanceId();
System.out.println(processInstanceId);
System.out.println("开启了流程实例");
List<Task> list=taskService.createTaskQuery()
.processDefinitionKey(key)//流程实例key
.processInstanceId(processInstanceId)
.list();
for(Task task:list){
System.out.println("任务id:"+list.get(0).getId());
System.out.println("任务名称:"+list.get(0).getName());
taskService.setAssignee(list.get(0).getId(),userName);
Map<String,Object> map=new HashMap<String,Object>();
map.put("totalDay",totalDay);
map.put("applyUserName",userName);
taskService.complete(task.getId(),map);
//这里完成任务后,监听器马上触发,所以是监听器代码先运行。
System.out.println(userName+"-->完成任务:"+task.getId()+"."+task.getName());
}
respon.setData(processInstanceId);
respon.setCode(Constant.CODE_SUCCESS);
respon.setMsg(Constant.CODE_SUCCESS_STRING);
return respon;
}
/**
* Created by Administrator on 2019/11/22.
* 主管监听器
*/
@Component
public class ZhuGuanListener implements TaskListener{
@Override
public void notify(DelegateTask delegateTask) {
System.out.println("进入主管监听器");
System.out.println("新产生任务id:"+delegateTask.getId());
System.out.println("新产生任务名称:"+delegateTask.getName());
List<String> list=new ArrayList<>();
list.add("主管-诸葛亮");
list.add("副主管-刘伯温");
list.add("副主管-李靖");
delegateTask.addCandidateUsers(list);
}
}
所有主管都可以查询到相关任务
//查询可选任务
@RequestMapping(value= "listTask/{userName}")
@ResponseBody
public Respon listTask(@PathVariable(name="userName")String userName){
Respon respon=new Respon();
List<Task> list=taskService.createTaskQuery().taskCandidateUser(userName).list();
if(list.size()>0){
String taskId=list.get(0).getId();
System.out.println("任务id:"+list.get(0).getId());
System.out.println("任务名称:"+list.get(0).getName());
respon.setMsg(taskId);
}else{
respon.setMsg("没有可选任务");
return respon;
}
respon.setCode(Constant.CODE_SUCCESS);
return respon;
}
主管领取并完成任务
//主管领取任务并审批
@RequestMapping(value= "claimTask/{userName}/{taskId}/{flag}/{totalDay}")
@ResponseBody
public Respon claimTask(@PathVariable(name="userName")String userName,@PathVariable(name="taskId")String taskId,@PathVariable(name="flag")String flag,@PathVariable(name="totalDay")Float totalDay){
Respon respon=new Respon();
//flag="_3"驳回;flag="1"通过
Map<String,Object> map=new HashMap<>();
map.put("zhuguanFlag",flag);
map.put("totalDay",totalDay);
taskService.claim(taskId,userName);
taskService.complete(taskId,map);
System.out.println(userName+":审批任务id:"+taskId);
respon.setCode(Constant.CODE_SUCCESS);
respon.setMsg(Constant.CODE_SUCCESS_STRING);
return respon;
}
我这里主管审批通过了,所以下一步就是总经理可以查看到任务
总经理审批任务不通过
//经理领取任务并审批
@RequestMapping(value= "claimTask2/{userName}/{taskId}/{flag}")
@ResponseBody
public Respon claimTask2(@PathVariable(name="userName")String userName,@PathVariable(name="taskId")String taskId,@PathVariable(name="flag")String flag){
Respon respon=new Respon();
//flag="2"驳回;flag="1"通过
Map<String,Object> map=new HashMap<>();
map.put("jingliFlag",flag);
taskService.claim(taskId,userName);
taskService.complete(taskId,map);
System.out.println(userName+":审批任务id:"+taskId);
respon.setCode(Constant.CODE_SUCCESS);
respon.setMsg(Constant.CODE_SUCCESS_STRING);
return respon;
}
审批没通过就返回到“填写请假信息”,我不知道是什么原因,这里驳回不用写其他代码,在网上看过很多驳回的例子都是要写很多东西,但那都是Activiti6及更低的版本,可能Activiti7就不需要。
那现在就查看是否进行到“填写请假信息”这个任务
//查询该流程实例有什么任务
@RequestMapping(value= "processTask/{processInstanceId}")
@ResponseBody
public Respon processTask(@PathVariable(name="processInstanceId")String processInstanceId){
Respon respon=new Respon();
String key="qingjia_1";
List<Task> list=taskService.createTaskQuery().processDefinitionKey(key)//流程实例key
.processInstanceId(processInstanceId).list();
if(list.size()>0){
String taskId=list.get(0).getId();
System.out.println("任务id:"+list.get(0).getId());
System.out.println("任务名称:"+list.get(0).getName());
respon.setMsg(taskId);
}else{
respon.setMsg("没有可选任务");
return respon;
}
respon.setCode(Constant.CODE_SUCCESS);
return respon;
}
到这里差不多了,剩下的步骤和上面的都类似,所有我就不一一操作,如各位有什么问题和意见都可以提出来,大家一起研究。
最后希望这篇文章能帮到各位学习到更多,谢谢。