如何利用SpringBoot+Activiti工作流引擎实现请假审批流程?

Activiti是由Alfresco发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、工作流、服务协作等领域的一个开源的、灵活的、易扩展的可执行流程语言框架。Activiti基于Apache许可的开源BPM平台,创始人Tom Baeyens是JBoss jBPM的项目架构师,它特色是提供了eclipse插件,开发人员可以通过插件直接绘画出业务流程图。bliblibli...

概念啥的就不扯了,网上大把资料.话不多说,直接切入正题,预完成一个员工请假流程,1天部门经理审核,3天以内(含3天)副总审核,3天到7天(含7天)总经理审核,7天以上其他渠道审核.

demo结构图如上图所示,springboot+maven项目,整合Activiti6.0,pom依赖如下图所示:

org.springframework.boot spring-boot-starter-parent 2.1.5.RELEASE cn.dale activiti-demo 1.0-SNAPSHOT 1.8 UTF-8 org.springframework.boot spring-boot-starter-jdbc org.mybatis.spring.boot mybatis-spring-boot-starter 2.0.1 org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime org.springframework.boot spring-boot-starter-test test com.alibaba druid 1.1.10 org.activiti activiti-spring-boot-starter-basic 6.0.0 org.slf4j slf4j-api 1.7.21 org.slf4j slf4j-log4j12 1.7.21 org.springframework.boot spring-boot-maven-plugin

Activiti6.0已经整合到springboot了,所以上手起来顺畅的多.

在application.properties文件中做些许配置:

##spring database configuration spring.datasource.url=jdbc:mysql://172.10.3.150:3306/activiti?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=Mysql_108 spring.datasource.driver-class-name=com.mysql.jdbc.Driver #activiti #自动检查、部署流程定义文件 spring.activiti.check-process-definitions=true #自动更新数据库结构 spring.activiti.database-schema-update=true #流程定义文件存放目录 spring.activiti.process-definition-location-prefix=classpath:/processes/ 接下来运行一段代码,生成工作流所需的表,你得先在数据库里建好数据库activiti:

package cn.dale.activitidemo; import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngineConfiguration; import org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration; /** * @Author: dale * @Date: 2019/11/5 17:26 */ public class ActivitiTable { public static void main(String[] args) { // 引擎配置 ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration() .setJdbcUrl("jdbc:mysql://172.10.3.150:3306/activiti?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull") .setJdbcUsername("root") .setJdbcPassword("Mysql_108") .setJdbcDriver("com.mysql.jdbc.Driver") .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); // 获取流程引擎对象 ProcessEngine processEngine = cfg.buildProcessEngine(); } } run main()方法,表就建好了,接下来在idea添加一个叫actiBPM的插件,直接下载安装就好了,重启idea;

在resources目录下新建process目录,右键新建bpmn文件—>leave_process.bpmn,通过拖拽右边的图形元素绘图,将鼠标停留在元素中部,出现黑点后可以画连线.

画图时要注意id不能重复,Name要想好怎么命名,一个项目会有很多图形元素需要命名,一定要起名有规律,后面要用到的.哪个红色中间带个×的叫排他网关,达到某种条件才能进入这个网关,需要人为设定,如下图所示:

这个也不好描述,直接上这个图对应的xml文件:

7}]]>

要用的使用,把后缀名改成bpmn就行了.

跑一个测试用例,看能不能按照预先设定的逻辑执行:

package cn.dale.activitidemo; import cn.dale.activitidemo.entity.Employee; import cn.dale.activitidemo.task.ActivitiTask; import org.activiti.engine.*; import org.activiti.engine.impl.persistence.entity.ExecutionEntity; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author: dale * @Date: 2019/11/5 17:59 */ @RunWith(SpringRunner.class) @WebAppConfiguration @SpringBootTest @Transactional @Rollback(false) //@Ignore public class ActiTest { @Autowired private RuntimeService runtimeService; @Autowired private IdentityService identityService; @Autowired private TaskService taskService; @Autowired private RepositoryService repositoryService; //初始化一个要请假的员工 private static final Employee emp = new Employee("zs", 8); @Test public void testInitProcess() { System.out.println("method startActivityDemo begin...."); System.out.println("调用流程存储服务,已部署流程数量:" + repositoryService.createDeploymentQuery().count()); Map map = new HashMap(); // 流程图里写的${user} ,这里传进去user map.put("Applier", emp.getEmployeeName()); map.put("DeptMng", "ls"); map.put("ViceMng", "ww"); map.put("GnlMng", "zl"); map.put("special", "sq"); // 指定流程的发起者 不指定发起者的字段就为空,注意跟审核人分开 identityService.setAuthenticatedUserId(emp.getEmployeeName()); //流程启动 ExecutionEntity pi = (ExecutionEntity) runtimeService.startProcessInstanceByKey("start", map); String processInstanceId = pi.getId(); System.out.println("流程开启成功,流程实例id:" + processInstanceId); ActivitiTask at = new ActivitiTask(taskService); //开始进行流程 while (runtimeService .createProcessInstanceQuery()//获取查询对象 .processInstanceId(processInstanceId)//根据id查询流程实例 .singleResult()//获取查询结果,如果为空,说明这个流程已经执行完毕,否则,获取任务并执行 != null) { Task task = taskService.createTaskQuery()//创建查询对象 .processInstanceId(processInstanceId)//通过流程实例id来查询当前任务 .singleResult();//获取单个查询结果 String taskName = task.getName(); System.out.println("任务ID: " + task.getId()); System.out.println("任务的办理人: " + task.getAssignee()); System.out.println("任务名称: " + taskName); System.out.println("任务的创建时间: " + task.getCreateTime()); System.out.println("流程实例ID: " + task.getProcessInstanceId()); switch (taskName) { case "Applier": //职员节点 at.completeEmployeeTask(task, emp); break; case "DeptMng": //部门经理节点 at.completeLeaderTask(task, emp); break; default: //经理节点 at.completeJingliTask(task, emp); break; } System.out.println(emp.getApplyStatus().getName()); } System.out.println("method startActivityDemo end...."); } }

ActivitiTask代码:

package cn.dale.activitidemo.task; import cn.dale.activitidemo.entity.Employee; import cn.dale.activitidemo.enums.ApplyStatusEnum; import org.activiti.engine.TaskService; import org.activiti.engine.task.Task; import java.util.HashMap; import java.util.Map; /** * @Author: dale * @Date: 2019/11/6 15:38 */ public class ActivitiTask { private TaskService taskService; public ActivitiTask(TaskService taskService) { this.taskService = taskService; } //职员提交申请 public void completeEmployeeTask(Task task, Employee emp){ //获取任务id String taskId = task.getId(); //完成任务 taskService.complete(taskId); emp.setApplyStatus(ApplyStatusEnum.AUDITING); System.out.println("职员已经提交申请......."); } //领导审批 public void completeLeaderTask(Task task, Employee emp){ //获取任务id String taskId = task.getId(); //领导意见 Map variables = new HashMap(); variables.put("day",emp.getDay()); variables.put("deptMngResult", 1); emp.setApplyStatus(ApplyStatusEnum.AUDITING); //完成任务 taskService.complete(taskId, variables); System.out.println("领导审核完毕........"); } //经理审批 public void completeJingliTask(Task task, Employee emp){ //获取任务id String taskId = task.getId(); String name = task.getName(); //经理意见 Map variables = new HashMap(); variables.put("day",emp.getDay()); // variables.put("result", 0); // emp.setApplyStatus(ApplyStatusEnum.NO); variables.put("result", 1); emp.setApplyStatus(ApplyStatusEnum.YES); //完成任务 taskService.complete(taskId, variables); System.out.println("经理审核完毕........,审核经理:"+name); } }

请假被sq拒绝后的运行结果:

可以看到流程一直循环执行,因为到最后一环被拒绝了,现在我让sq同意请假申请,结果如下图:

这样,一个8天的请假流程就顺利走完了.简单的一个案例,当然Activiti还有满足不同场景需求的更多的Api,这里就不一一列举,大家根据自己项目的实际需要去学习

演示案例GitHub仓库地址:https://github.com/DaleZl/ActivitiDemo.git