「Activiti精品 悟纤出品」开发一个简单的SpringBoot activiti应用 - 第330篇

一、开发说明

1.1 开发环境说明

(1)OS:Mac OS;

(2)IDEA : IntellijIDEA;

(3)MySQL:8.0.12;

(4)Spring Boot : 2.3.3.RELEASE;

(5)Activiti:activiit7的7.1.0.M6 starter;

(6)bpmn:idea的插件actiBPM;

 

1.2 开发说明

       在接下来我们要使用activiti7开发一个请假流程:

员工发起申请请假申请->填写请假表单(请假时间、请假原因)->部门领导审批意见->请假流程结束。

       根据activiti的这个任务流程,那么有几个核心的事件:

·我要请假 ·填写请假单 ·领导审批

       在接下来我们会按照之前在《「工作流Activiti」流程模型搭建-小试牛刀》的流程构建来说明从代码层面应该去进行使用activiti7。

       问:对于前面的章节,看懂了,但是没有搭建流程模型环境,影响本节学习嘛?

       答:不影响,对于activiti相关的知识点,如果以前就学习过,只是不清楚在Spring Boot中不知道如何使用,那么直接看本节就可以了。

 

1.3 集成方式说明

Spring Boot整合activiti的方案主要是两种方式:

(1)不使用starter整合(不推荐:使用activiti-spring的依赖进行使用,那么就需要有一个ActivitiConfig的配置类进行注入activiti相关的,比如数据源、流程引擎工厂类ProcessEngineFactoryBean,还有我么上面提到的Service,TaskService、RuntimeService。结论:这种方式比较不复杂,不推荐。

(2)使用starter(推荐:使用activiti-spring-boot-starter,无需自己在进行activiti的相关配置,可以直接进行开发流程相关的。结论:入门简单,推荐。

       本文主要是使用第二种方式进行展开讲解。

 

二、开发实战

2.1 创建项目

       我们使用SpringBoot的start快速构建一个项目,取名为:spring-boot-activiti7-demo。

       那么这时候@SpringBootApplication的启动类就自动生成了,无需自己进行创建。

 

2.2 流程图构建

       要开发一个请假流程,那么就需要构建一个流程图,我们使用actiBPM进行创建,

       在/resources新建一个目录processes新建一个leave-process.bpmn,也就是:

/resources/processes/leave-process.bpmn

2.2.1 使用BPMN元素创建一个请假流程图

       使用BPMN的元素进行创建一个请假流程图,结果如下:

图片

2.2.2 为元素创建ID属性

       相信使用bpmn的元素构建上面的流程图,并不是难事,那么创建完成之后,我们之后需要通过代码进行操作这个流程图的元素,那么怎么操作呢,肯定是通过元素的唯一标识嘛,所以我们设置一下id属性。

【StartEvent】节点:id=startEvent、name=开始事件

【EndEvent】节点:id=endEvent、name=结束事件

【员工申请】节点:id=applyTask、name=员工申请

【部门领导审批】节点:id=deptApproveTask、name=部门领导审批

       举个栗子员工申请节点:

图片

其实上面这些好像在代码中并不一定能使用到,不设置好像也无所谓啦,但是name的还是设置下吧,方便进行流程图的查看。

       但是有一个地方的id的设置非常重要,试问一下,你怎么获取到这个流程图呐,流程图的id吧,所以这个得设置一下,点击流程图空白的地方就可以进行设置流程图的id属性了:

【process】:id=leaveProcess、name=请假流程

 

图片

2.2.3 分配任务人:动态分配

       还记得我们在使用activiti的时候,有一个很重要的地方,就是谁发起、谁审批了,也就是Assignee这个属性的配置,在实际项目中我们不会使用activiti的用户体系,我们有自己的用户体系,那么我们就需要能够在代码层面进行动态的设置这个属性了,我们使用EL表达式进行标识出来即可:

【员工申请】节点:assignee=${jobNumber}

【部门领导审批】节点:assignee=${deptJobNumber}

注意:这两个参数不能设置为一样的,否则就无法进行动态的设置分配人了。

举例说明员工申请节点示例如下:

图片

其它注意的地方:对于Assignee这个字段,如果设置好了之后,对于actiBPM这个插件,如果重启了Idea,Assignee这个属性不能够进行回显,但是在xml文件是有这个值的,好像是actiBPM的bug,这个插件好像之后就没有在更新过了,截止到2020.08.29更新下来的就是有这样的问题。

 

2.2.4 表单说明

       对于表单部分,这里我们无需进行构建表单,我们之后会通过代码动态的进行设置表单参数,这里暂时也不用进行创建表单。

 

2.2.5 bpmn文件

       根据上面的配置之后,具体的bpmn文件如下:

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2. <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="m1598520463370" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  3. <process id="leaveProcess" isClosed="false" isExecutable="true" name="请假流程" processType="None">
  4. <startEvent id="startEvent" name="开始事件"/>
  5. <userTask activiti:assignee="${jobNumber}" activiti:exclusive="true" id="applyTask" name="员工申请"/>
  6. <userTask activiti:assignee="${deptJobNumber}" activiti:exclusive="true" id="deptApproveTask" name="部门领导审批"/>
  7. <endEvent id="endEvent" name="结束事件"/>
  8. <sequenceFlow id="_5" sourceRef="deptApproveTask" targetRef="endEvent"/>
  9. <sequenceFlow id="_11" sourceRef="startEvent" targetRef="applyTask"/>
  10. <sequenceFlow id="_2" sourceRef="applyTask" targetRef="deptApproveTask"/>
  11. </process>
  12. <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">
  13. <bpmndi:BPMNPlane bpmnElement="leaveProcess">
  14. <bpmndi:BPMNShape bpmnElement="startEvent" id="Shape-startEvent">
  15. <omgdc:Bounds height="32.0" width="32.0" x="70.0" y="230.0"/>
  16. <bpmndi:BPMNLabel>
  17. <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
  18. </bpmndi:BPMNLabel>
  19. </bpmndi:BPMNShape>
  20. <bpmndi:BPMNShape bpmnElement="applyTask" id="Shape-applyTask">
  21. <omgdc:Bounds height="55.0" width="85.0" x="165.0" y="220.0"/>
  22. <bpmndi:BPMNLabel>
  23. <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
  24. </bpmndi:BPMNLabel>
  25. </bpmndi:BPMNShape>
  26. <bpmndi:BPMNShape bpmnElement="deptApproveTask" id="Shape-deptApproveTask">
  27. <omgdc:Bounds height="55.0" width="85.0" x="350.0" y="220.0"/>
  28. <bpmndi:BPMNLabel>
  29. <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
  30. </bpmndi:BPMNLabel>
  31. </bpmndi:BPMNShape>
  32. <bpmndi:BPMNShape bpmnElement="endEvent" id="Shape-endEvent">
  33. <omgdc:Bounds height="32.0" width="32.0" x="495.0" y="230.0"/>
  34. <bpmndi:BPMNLabel>
  35. <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
  36. </bpmndi:BPMNLabel>
  37. </bpmndi:BPMNShape>
  38. <bpmndi:BPMNEdge bpmnElement="_2" id="BPMNEdge__2" sourceElement="applyTask" targetElement="deptApproveTask">
  39. <omgdi:waypoint x="250.0" y="247.5"/>
  40. <omgdi:waypoint x="350.0" y="247.5"/>
  41. <bpmndi:BPMNLabel>
  42. <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
  43. </bpmndi:BPMNLabel>
  44. </bpmndi:BPMNEdge>
  45. <bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="deptApproveTask" targetElement="endEvent">
  46. <omgdi:waypoint x="435.0" y="247.5"/>
  47. <omgdi:waypoint x="495.0" y="246.0"/>
  48. <bpmndi:BPMNLabel>
  49. <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
  50. </bpmndi:BPMNLabel>
  51. </bpmndi:BPMNEdge>
  52. <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="startEvent" targetElement="applyTask">
  53. <omgdi:waypoint x="102.0" y="246.0"/>
  54. <omgdi:waypoint x="165.0" y="247.5"/>
  55. <bpmndi:BPMNLabel>
  56. <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
  57. </bpmndi:BPMNLabel>
  58. </bpmndi:BPMNEdge>
  59. </bpmndi:BPMNPlane>
  60. </bpmndi:BPMNDiagram>
  61. </definitions>

2.3 添加依赖



       我们看看使用内存数据库的依赖,主要依赖有:

H2:内存数据库;

activiti7 starter:activiti7的starter,6版本的这个很不一样,可以参考文章《「工作流Activiti」核心类以及如何在SpringBoot集成说明

spring security:这是activiti底层使用到了userDetail,所以需要引入,不然启动会报错。

       具体的pom.xml文件如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.3.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.kfit</groupId>
  12. <artifactId>spring-boot-activiti7-demo</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>spring-boot-activiti7-demo</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.activiti</groupId>
  26. <artifactId>activiti-spring-boot-starter</artifactId>
  27. <version>7.1.0.M6</version>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-security</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>com.h2database</groupId>
  35. <artifactId>h2</artifactId>
  36. </dependency>
  37. </dependencies>
  38. <build>
  39. <plugins>
  40. <plugin>
  41. <groupId>org.springframework.boot</groupId>
  42. <artifactId>spring-boot-maven-plugin</artifactId>
  43. </plugin>
  44. </plugins>
  45. </build>
  46. </project>

      这里使用了spring security的话,那么每次访问都需要进行登录,特别不方便,可以关闭掉,修改启动类即可:

  1. @SpringBootApplication(
  2. exclude = {SecurityAutoConfiguration.class,
  3. ManagementWebSecurityAutoConfiguration.class}
  4. )

 

2.4 启动测试

       到这里正常情况下,即可以进行启动了,启动查看控制台,注意一个打印信息:

1The following process definition files will be deployed: [leave-process.bpmn]2)No process extensions were found for auto-deployment in the location 'classpath*:**/processes/'3)performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sqlperforming create on history with resource org/activiti/db/create/activiti.h2.create.history.sql4)ProcessEngine default created5)Process deployed: {id: leaveProcess:1:5a36f256-e9c9-11ea-8705-463e6370b561, key: leaveProcess, name: 请假流程 }

 这里有一个很重要的信息,就是当我们启动应用的时候,会自动的发布我们的流程。也就是说白了我们就不用create app->publish这个步骤了。

 

2.5 持久化mysql

       上面这个内存数据库不能看到内在的东西,实在是不舒服,难免会有点担忧,睡不着觉,那么我们使用mysql数据库来持久化。

2.5.1 修改pom.xml

       首先修改pom.xml文件,删除h2的依赖,添加mysql和数据源依赖:

  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>com.alibaba</groupId>
  7. <artifactId>druid</artifactId>
  8. <version>1.1.23</version>
  9. </dependency>

 

2.5.2 添加配置信息

       在application.properties文件添加数据源和activiti的配置信息:

  1. ### 数据源的配置
  2. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  3. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  4. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/activiti7_demo?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&nullCatalogMeansCurrent=true
  5. spring.datasource.username=root
  6. spring.datasource.password=root
  7. ### activiti数据库的配置
  8. #表示启动时检查数据库表,不存在则创建
  9. spring.activiti.database-schema-update=true
  10. #表示哪种情况下使用历史表,这里配置为full表示全部记录历史
  11. spring.activiti.history-level=full
  12. #为true表示使用历史表,如果不配置,则工程启动后可以检查数据库
  13. spring.activiti.db-history-used=true

2.5.3 启动测试下

       启动成功的话,可以看到数据库中有25张表,具体这些表信息我们在下节进行说明,你只需要有看到表就可以了,另外我们可以验证下我们之前说的:在启动的时候就已经部署了。

SELECT *from ACT_RE_DEPLOYMENT;

说明:ACT_RE_DEPLOYMENT部署信息表,另外可以通过ACT_RE_PROCDEF看到一个流程信息。

       截图看下效果吧:

图片

图片

       这里可以看出部署表和流程表的关系,当部署表创建之后,会和流程表进行绑定,对应的字段是DEPLOYMENT_ID_

 

2.5.4 启动报错

       如果是报一下错误:

SQLSyntaxErrorException: Table'activiti7_demo.act_ge_property' doesn't exist

       在application.properties文件中的配置spring.datasource.url添加参数:&nullCatalogMeansCurrent=true

 

2.6 开发 – 准备工作

       我们新建一个LeaveController用来进行编写整个流程的操作。

  1. @RestController
  2. @RequestMapping("/leave")
  3. public class LeaveController {
  4. //org.activiti.engine.RuntimeService
  5. @Autowired
  6. private RuntimeService runtimeService;//流程相关
  7. //org.activiti.engine.TaskService
  8. @Autowired
  9. private TaskService taskService;//任务相关
  10. //org.activiti.engine.HistoryService
  11. @Autowired
  12. private HistoryService historyService;//历史记录相关
  13. }

 

说明:RuntimeService、TaskService、HistoryService都是activiti给我们提供的。

 

2.7 开发 – A1001提交申请

2.7.1 代码

 

当我们用户点击【提交申请】的时候,我们需要调用runtimeService.startProcessInstanceByKey()方法来开始一个流程。流程开启成功之后,会创建一个流程实例对象ProcessInstance。对于用户下一步就可以进行填写请假时间和请假原因。

       我们在LeaveController创建start()方法,具体代码如下:

  1. /**
  2. * 开始流程
  3. * @param jobNumber
  4. * @author 悟纤
  5. * @return
  6. */
  7. @RequestMapping(value="/start")
  8. public String start(String jobNumber) {
  9. //设置流程的发起者
  10. Authentication.setAuthenticatedUserId(jobNumber);
  11. // bpmn中定义process的id。
  12. String instanceKey = "leaveProcess";
  13. System.out.println("开启请假流程...");
  14. // 设置流程参数,开启流程
  15. Map<String,Object> variables = new HashMap<String,Object>();
  16. //设置参数,这里的key就是上面配置的assignee的${jobNumber},会进行赋值。
  17. variables.put("jobNumber",jobNumber);
  18. //使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动
  19. // 流程开启成功之后,获取到ProcessInstance信息。
  20. ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceKey, variables);
  21. System.out.println("流程实例ID:"+instance.getId());
  22. System.out.println("流程定义ID:"+instance.getProcessDefinitionId());
  23. //验证是否启动成功
  24. //通过查询正在运行的流程实例来判断
  25. ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
  26. //根据流程实例ID来查询
  27. List<ProcessInstance> runningList = processInstanceQuery.processInstanceId(instance.getProcessInstanceId()).list();
  28. System.out.println("根据流程ID查询条数:"+runningList.size());
  29. // 返回流程ID
  30. return instance.getId();
  31. }

     代码上已经注释的很清楚了,这里就不过多说明,这里的发起核心就是调用了startProcessInstanceByKey()方法。另外有一处代码很重要就是:

  1. //设置流程的发起者
  2. Authentication.setAuthenticatedUserId(jobNumber);

 

   如果使用的是activiti6的版本使用

  1. //设置流程的发起者
  2. identityService.setAuthenticatedUserId(jobNumber);

   这一个需要尽心设置,在查询历史记录的时候,会访问ACT_HI_PROCINST这个表和START_USER_ID进行比较,这个值就是上面的这个代码进行设置的。

 

2.7.2 测试



       到这里我们就可以来测试下了,启动我们的应用,访问:

http://127.0.0.1:8080/leave/start?jobNumber=A1001

       这里我们假设员工的工号为A1001进行发起了请假申请,访问地址成功的话,返回一个流程实例ID:4518e0a4-e9d7-11ea-9e4c-9ab0362195b5。

 

2.8 开发 – A1001任务查询

       对于员工A1001可以查询到自己代办的任务了,因为他提交了申请,但是还没有填写请假缘由呐,我们先看下A1001的任务:

  1. /**
  2. * <p>查看任务</p>
  3. * @author 悟纤
  4. */
  5. @RequestMapping(value="/showTask")
  6. public List<Map<String, String>> showTask(String jobNumber) {
  7. /*
  8. * 获取请求参数
  9. */
  10. TaskQuery taskQuery = taskService.createTaskQuery();
  11. List<Task> taskList = null;
  12. if(jobNumber == null){
  13. //获取所有人的所有任务.
  14. taskList = taskQuery.list();
  15. }else{
  16. //获取分配人的任务.
  17. taskList = taskQuery.taskAssignee(jobNumber).list();
  18. }
  19. if(taskList == null || taskList.size() == 0) {
  20. System.out.println("查询任务列表为空!");
  21. return null;
  22. }
  23. /*
  24. * 查询所有任务,并封装
  25. */
  26. List<Map<String, String>> resultList = new ArrayList<>();
  27. for(Task task : taskList) {
  28. Map<String, String> map = new HashMap<>();
  29. map.put("taskId", task.getId());
  30. map.put("name", task.getName());
  31. map.put("createTime", task.getCreateTime().toString());
  32. map.put("assignee", task.getAssignee());
  33. map.put("instanceId", task.getProcessInstanceId());
  34. map.put("executionId", task.getExecutionId());
  35. map.put("definitionId", task.getProcessDefinitionId());
  36. resultList.add(map);
  37. }
  38. /*
  39. * 返回结果
  40. */
  41. return resultList;
  42. }

 

 

 

   测试访问地址:

http://127.0.0.1:8080/leave/showTask?jobNumber=A1001

       这里会返回一个很核心的参数taskId:

  1. [
  2. {
  3. executionId: "45197ce7-e9d7-11ea-9e4c-9ab0362195b5",
  4. instanceId: "4518e0a4-e9d7-11ea-9e4c-9ab0362195b5",
  5. createTime: "Sat Aug 29 17:08:57 CST 2020",
  6. name: "员工申请",
  7. assignee: "A1001",
  8. taskId: "451c14fa-e9d7-11ea-9e4c-9ab0362195b5",
  9. definitionId: "leaveProcess:1:43f2ff73-e9cc-11ea-935c-463e6370b561"
  10. }
  11. ]

 

 

 

   说明任务当前到A1001上,任务taskId在进行表单的操作需要用到,请牢记。

2.9 开发 – A1001员工提交申请

       这时候员工在前端进行表单的填写,填写完成之后,需要进行任务的完成,那么就需要调用后端服务,使用taskService.complete()进行任务的完成,任务完成会传递到下一个节点:

  1. /**
  2. * 员工提交申请
  3. * @author 悟纤
  4. * @return String 申请受理结果
  5. */
  6. @RequestMapping(value="/employeeApply")
  7. public String employeeApply(HttpServletRequest request){
  8. System.out.println("--> 提交申请单信息");
  9. /*
  10. * 获取请求参数
  11. */
  12. String taskId = request.getParameter("taskId"); // 任务ID
  13. //String jobNumber = request.getParameter("jobNumber"); // 工号
  14. String deptJobNumber = request.getParameter("deptJobNumber"); //上级
  15. String leaveDays = request.getParameter("leaveDays"); // 请假天数
  16. String leaveReason = request.getParameter("leaveReason"); // 请假原因
  17. /*
  18. * 查询任务
  19. */
  20. Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
  21. if(task == null) {
  22. System.out.println("任务ID:"+taskId+"查询到任务为空!");
  23. return "fail";
  24. }
  25. /*
  26. * 参数传递并提交申请
  27. */
  28. Map<String, Object> variables = new HashMap<String, Object>();
  29. variables.put("days", leaveDays);
  30. variables.put("date", new Date());
  31. variables.put("reason", leaveReason);
  32. variables.put("deptJobNumber", deptJobNumber);
  33. taskService.complete(task.getId(), variables);
  34. System.out.println("执行【员工申请】环节,流程推动到【部门审核】环节");
  35. /*
  36. * 返回成功
  37. */
  38. return "success";
  39. }

 

 

 

    这里有一个参数很重要,在这里特别说明下,就是任务的下一个节点:deptJobNumber的分配人,这里我们使用前端进行传递的方式,等同于前端有一个地方可以进行勾选人员进行处理,那么在实际项目中,这个参数可以后端调用一个该员工的部门经理的工号进行设置即可。

       访问测试下:

http://127.0.0.1:8080/leave/employeeApply?taskId=451c14fa-e9d7-11ea-9e4c-9ab0362195b5&deptJobNumber=A1002&leaveDays=2&leaveReason=家里有事

       这里的taskId就是上面查询出来的taskId。

 

2.10 开发 – A1002任务查询

       这时候任务由A1001流转到A1002了,进行任务查询下:

http://127.0.0.1:8080/leave/showTask?jobNumber=A1002

       返回的数据如下:

  1. [
  2. {
  3. executionId: "45197ce7-e9d7-11ea-9e4c-9ab0362195b5",
  4. instanceId: "4518e0a4-e9d7-11ea-9e4c-9ab0362195b5",
  5. createTime: "Sat Aug 29 18:09:53 CST 2020",
  6. name: "部门领导审批",
  7. assignee: "A1002",
  8. taskId: "c843d9a9-e9df-11ea-94ed-72df69b1f835",
  9. definitionId: "leaveProcess:1:43f2ff73-e9cc-11ea-935c-463e6370b561"
  10. }
  11. ]

 

   注意taskId和上面是不一样的,说明activiti对于任务的流转是删除原先的,创建一个新的任务节点,而不是使用update原先的任务。

 

2.11 开发 – A1002审批操作

       到这里就是A1002进行审批操作,审批是同意还是拒绝。

  1. /**
  2. * <p>部门经理审核</p>
  3. * @return String 受理结果
  4. * @author 悟纤
  5. */
  6. @ResponseBody
  7. @RequestMapping(value="/deptManagerAudit")
  8. public String deptManagerAudit(HttpServletRequest request) {
  9. /*
  10. * 获取请求参数
  11. */
  12. //任务id
  13. String taskId = request.getParameter("taskId");
  14. //审批意见
  15. String auditOpinion = request.getParameter("auditOpinion");
  16. //审批结果:同意1;不同意0
  17. String audit = request.getParameter("audit");
  18. if(StringUtils.isBlank(taskId)) return "fail";
  19. /*
  20. * 查找任务
  21. */
  22. Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
  23. if(task == null) {
  24. System.out.println("审核任务ID:"+taskId+"查询到任务为空!");
  25. return "fail";
  26. }
  27. /*
  28. * 设置局部变量参数,完成任务
  29. */
  30. Map<String, Object> map = new HashMap<String, Object>();
  31. map.put("audit", audit);
  32. map.put("auditOpinion", auditOpinion);
  33. taskService.complete(taskId, map);
  34. return "success";
  35. }

     访问测试下:

http://127.0.0.1:8080/leave/deptManagerAudit?taskId=c843d9a9-e9df-11ea-94ed-72df69b1f835&audit=1&auditOpinion=同意

       至此整个流程完毕。

 

2.12 开发 – A1001查看请假记录

       由于已完成的请假在数据库runtime表中查不到(runtime表只保存正在进行的流程示例信息),所以需要在history表中查询。

  1. @RequestMapping("/historyList")
  2. public List<Map<String,Object>> historyList(String jobNumber){
  3. List<HistoricProcessInstance> historicProcessInstances =historyService // 历史任务Service
  4. .createHistoricProcessInstanceQuery() // 创建历史活动实例查询
  5. .processDefinitionKey("leaveProcess")//processDefinitionKey
  6. .finished().startedBy(jobNumber)
  7. .orderByProcessInstanceEndTime().desc()
  8. .list();
  9. List<Map<String,Object>> list = new ArrayList<>();
  10. for(HistoricProcessInstance hpi:historicProcessInstances){
  11. Map<String, Object> map = new HashMap<>();
  12. map.put("startUserId", hpi.getStartUserId());
  13. map.put("startTime", hpi.getStartTime());
  14. map.put("endTime", hpi.getEndTime());
  15. list.add(map);
  16. //查询审批结果:
  17. Map<String, Object> variableMap = new HashMap<>();
  18. List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceId(hpi.getId()).list();
  19. for(HistoricVariableInstance hvi:varInstanceList){
  20. variableMap.put(hvi.getVariableName(),hvi.getValue());
  21. }
  22. map.put("variables", variableMap);
  23. list.add(map);
  24. }
  25. return list;
  26. }

访问如下地址进行测试下:

http://127.0.0.1:8080/leave/historyList?jobNumber=A1001

请求结果:

  1. [
  2. {
  3. startUserId: "A1001",
  4. variables: {
  5. date: "2020-08-30T09:22:52.019+00:00",
  6. reason: "家里有事",
  7. auditOpinion: "同意",
  8. deptJobNumber: "A1002",
  9. audit: "1",
  10. days: "2",
  11. jobNumber: "A1001",
  12. },
  13. startTime: "2020-08-30T09:22:27.076+00:00",
  14. endTime: "2020-08-30T09:23:10.892+00:00"
  15. }
  16. ]

悟纤小结

师傅:今天这个代码量有点大,大脑都快接收不了了吧。

悟纤:可不,搞的脑瓜都疼了。

师傅:第一次研究的时候,也是很头大,很难、很难,还有很多坑,都跳的爬不起来了。

悟纤:师傅也辛苦了,剩下的就交给我了吧。

 

(1)核心步骤:

① 创建一个bpmn文件;

② 引人activiti的依赖;

③ 调用activiti提供的api进行流程的相关操作。

 


购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287