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

图片

关历史文章(阅读本文前,您可能需要先看下之前的系列👇

国内最全的Spring Boot系列之三

2020上半年发文汇总「值得收藏」

「工作流Activiti」介绍-新手上路,注意安全 - 第320篇

「Activiti精品 悟纤出品」activiti介绍-十万个为什么 - 第321篇

「Activiti精品 悟纤出品」Activiti6 Getting Started- 稳扎稳打 - 第322篇

「Activiti精品 悟纤出品」Activiti7 Getting Started-摸石头过河 - 第323篇

「Activiti精品 悟纤出品」流程模型搭建-小试牛刀 - 第324篇

「Activiti精品 悟纤出品」基于mysql初始化「图图为啥消失了」- 第325篇

「Activiti精品 悟纤出品」Activiti插件来助你一臂之力 - 第327篇

「Activiti精品 悟纤出品」核心类以及如何在SpringBoot集成说明 - 第328篇

SpringBoot的controller为什么不能并行执行?同一个浏览器连续多次访问同一个url竟然是串行的?- 第329篇

 

师傅:徒儿,赶紧起来了,要开干了。

图片

悟纤:什么,什么要干谁?

悟纤:谁把师傅得罪了。

师傅:…. 你这脑洞怎么就怎么….

悟纤:怎么不是要干谁呀,那这是要干嘛,大早上的,我还没睡饱呐。

图片

师傅:你看太阳都晒到屁股了。

悟纤:睡得这么久了嘛,哎,梦里的美女… 只能晚上在陪你了。

图片

师傅:活到老,还有美女没睡好,口误是还有好多没学了。

图片

 

前言

       有志者事竟成,破釜沉舟,百二秦关终属楚;

       苦心人天不负,卧薪尝胆,三千越甲可吞吴。

       前面做了这么的铺垫,就是为了今天这一天的胜利而准备的,至此我们要在Spring Boot中集成Activiiti,这一站胜利了,对于activiti也算入门了。

 

一、开发说明

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文件如下:

<?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="m1598520463370" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
    <process id="leaveProcess" isClosed="false" isExecutable="true" name="请假流程" processType="None">
        <startEvent id="startEvent" name="开始事件"/>
        <userTask activiti:assignee="${jobNumber}" activiti:exclusive="true" id="applyTask" name="员工申请"/>
        <userTask activiti:assignee="${deptJobNumber}" activiti:exclusive="true" id="deptApproveTask" name="部门领导审批"/>
        <endEvent id="endEvent" name="结束事件"/>
        <sequenceFlow id="_5" sourceRef="deptApproveTask" targetRef="endEvent"/>
        <sequenceFlow id="_11" sourceRef="startEvent" targetRef="applyTask"/>
        <sequenceFlow id="_2" sourceRef="applyTask" targetRef="deptApproveTask"/>
    </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="leaveProcess">
            <bpmndi:BPMNShape bpmnElement="startEvent" id="Shape-startEvent">
                <omgdc:Bounds height="32.0" width="32.0" x="70.0" y="230.0"/>
                <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="applyTask" id="Shape-applyTask">
                <omgdc:Bounds height="55.0" width="85.0" x="165.0" y="220.0"/>
                <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="deptApproveTask" id="Shape-deptApproveTask">
                <omgdc:Bounds height="55.0" width="85.0" x="350.0" y="220.0"/>
                <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="endEvent" id="Shape-endEvent">
                <omgdc:Bounds height="32.0" width="32.0" x="495.0" y="230.0"/>
                <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="_2" id="BPMNEdge__2" sourceElement="applyTask" targetElement="deptApproveTask">
                <omgdi:waypoint x="250.0" y="247.5"/>
                <omgdi:waypoint x="350.0" y="247.5"/>
                <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="deptApproveTask" targetElement="endEvent">
                <omgdi:waypoint x="435.0" y="247.5"/>
                <omgdi:waypoint x="495.0" y="246.0"/>
                <bpmndi:BPMNLabel>
                    <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
                </bpmndi:BPMNLabel>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="startEvent" targetElement="applyTask">
                <omgdi:waypoint x="102.0" y="246.0"/>
                <omgdi:waypoint x="165.0" y="247.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>

2.3 添加依赖

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

H2:内存数据库;

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

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

       具体的pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kfit</groupId>
    <artifactId>spring-boot-activiti7-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-activiti7-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.1.0.M6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

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

@SpringBootApplication(
        exclude = {SecurityAutoConfiguration.class,
                ManagementWebSecurityAutoConfiguration.class}
        )

 

2.4 启动测试

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

1)The 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.sql(4)ProcessEngine default created(5)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和数据源依赖:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.23</version>
</dependency>

 

2.5.2 添加配置信息

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

### 数据源的配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/activiti7_demo?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf-8&useSSL=false&nullCatalogMeansCurrent=true
spring.datasource.username=root
spring.datasource.password=root

### activiti数据库的配置
#表示启动时检查数据库表,不存在则创建
spring.activiti.database-schema-update=true
#表示哪种情况下使用历史表,这里配置为full表示全部记录历史
spring.activiti.history-level=full
#为true表示使用历史表,如果不配置,则工程启动后可以检查数据库
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用来进行编写整个流程的操作。

@RestController
@RequestMapping("/leave")
public class LeaveController {

    //org.activiti.engine.RuntimeService
    @Autowired
    private RuntimeService runtimeService;//流程相关

    //org.activiti.engine.TaskService
    @Autowired
    private TaskService taskService;//任务相关

    //org.activiti.engine.HistoryService
    @Autowired
    private HistoryService historyService;//历史记录相关
}

 

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

 

2.7 开发 – A1001提交申请

2.7.1 代码

 

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

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

/**
 * 开始流程
 * @param jobNumber
 * @author 悟纤
 * @return
 */
@RequestMapping(value="/start")
public String start(String jobNumber) {
    //设置流程的发起者
    Authentication.setAuthenticatedUserId(jobNumber);

    // bpmn中定义process的id。
    String instanceKey = "leaveProcess";
    System.out.println("开启请假流程...");

    // 设置流程参数,开启流程
    Map<String,Object> variables = new HashMap<String,Object>();
    //设置参数,这里的key就是上面配置的assignee的${jobNumber},会进行赋值。
    variables.put("jobNumber",jobNumber);

    //使用流程定义的key启动流程实例,key对应helloworld.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动
    // 流程开启成功之后,获取到ProcessInstance信息。
    ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceKey, variables);

    System.out.println("流程实例ID:"+instance.getId());
    System.out.println("流程定义ID:"+instance.getProcessDefinitionId());


    //验证是否启动成功
    //通过查询正在运行的流程实例来判断
    ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
    //根据流程实例ID来查询
    List<ProcessInstance> runningList = processInstanceQuery.processInstanceId(instance.getProcessInstanceId()).list();
    System.out.println("根据流程ID查询条数:"+runningList.size());

    // 返回流程ID
    return instance.getId();
}

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

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

 

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

//设置流程的发起者
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的任务:

/**
 * <p>查看任务</p>
 * @author 悟纤
 */
@RequestMapping(value="/showTask")
public List<Map<String, String>> showTask(String jobNumber) {
    /*
     * 获取请求参数
     */
    TaskQuery taskQuery = taskService.createTaskQuery();

    List<Task> taskList = null;
    if(jobNumber == null){
        //获取所有人的所有任务.
        taskList = taskQuery.list();
    }else{
        //获取分配人的任务.
        taskList = taskQuery.taskAssignee(jobNumber).list();
    }

    if(taskList == null || taskList.size() == 0) {
        System.out.println("查询任务列表为空!");
        return null;
    }


    /*
     * 查询所有任务,并封装
     */
    List<Map<String, String>> resultList = new ArrayList<>();
    for(Task task : taskList) {
        Map<String, String> map = new HashMap<>();
        map.put("taskId", task.getId());
        map.put("name", task.getName());
        map.put("createTime", task.getCreateTime().toString());
        map.put("assignee", task.getAssignee());
        map.put("instanceId", task.getProcessInstanceId());
        map.put("executionId", task.getExecutionId());
        map.put("definitionId", task.getProcessDefinitionId());
        resultList.add(map);
    }


    /*
     * 返回结果
     */
    return resultList;
}

 

 

 

   测试访问地址:

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

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

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

 

 

 

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

2.9 开发 – A1001员工提交申请

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

/**
 * 员工提交申请
 * @author 悟纤
 * @return String 申请受理结果
 */
@RequestMapping(value="/employeeApply")
public String employeeApply(HttpServletRequest request){
    System.out.println("--> 提交申请单信息");
    /*
     * 获取请求参数
     */
    String taskId = request.getParameter("taskId"); // 任务ID
    //String jobNumber = request.getParameter("jobNumber"); // 工号
    String deptJobNumber = request.getParameter("deptJobNumber"); //上级
    String leaveDays = request.getParameter("leaveDays"); // 请假天数
    String leaveReason = request.getParameter("leaveReason"); // 请假原因


    /*
     *  查询任务
     */
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    if(task == null) {
        System.out.println("任务ID:"+taskId+"查询到任务为空!");
        return "fail";
    }


    /*
     * 参数传递并提交申请
     */
    Map<String, Object> variables = new HashMap<String, Object>();
    variables.put("days", leaveDays);
    variables.put("date", new Date());
    variables.put("reason", leaveReason);
    variables.put("deptJobNumber", deptJobNumber);
    taskService.complete(task.getId(), variables);
    System.out.println("执行【员工申请】环节,流程推动到【部门审核】环节");

    /*
     * 返回成功
     */
    return "success";
}

 

 

 

    这里有一个参数很重要,在这里特别说明下,就是任务的下一个节点: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

       返回的数据如下:


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

 

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

 

2.11 开发 – A1002审批操作

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

/**
 * <p>部门经理审核</p>
 * @return String 受理结果
 * @author  悟纤
 */
@ResponseBody
@RequestMapping(value="/deptManagerAudit")
public String deptManagerAudit(HttpServletRequest request) {
    /*
     * 获取请求参数
     */
    //任务id
    String taskId = request.getParameter("taskId");
    //审批意见
    String auditOpinion = request.getParameter("auditOpinion");
    //审批结果:同意1;不同意0
    String audit = request.getParameter("audit");

    if(StringUtils.isBlank(taskId)) return "fail";



    /*
     * 查找任务
     */
    Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
    if(task == null) {
        System.out.println("审核任务ID:"+taskId+"查询到任务为空!");
        return "fail";
    }


    /*
     * 设置局部变量参数,完成任务
     */
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("audit", audit);
    map.put("auditOpinion", auditOpinion);
    taskService.complete(taskId, map);
    
    return "success";
}

     访问测试下:

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

       至此整个流程完毕。

 

2.12 开发 – A1001查看请假记录

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

@RequestMapping("/historyList")
public List<Map<String,Object>> historyList(String jobNumber){

    List<HistoricProcessInstance> historicProcessInstances =historyService // 历史任务Service
            .createHistoricProcessInstanceQuery() // 创建历史活动实例查询
            .processDefinitionKey("leaveProcess")//processDefinitionKey
            .finished().startedBy(jobNumber)
            .orderByProcessInstanceEndTime().desc()
            .list();

    List<Map<String,Object>> list = new ArrayList<>();
    for(HistoricProcessInstance hpi:historicProcessInstances){

        Map<String, Object> map = new HashMap<>();
        map.put("startUserId", hpi.getStartUserId());
        map.put("startTime", hpi.getStartTime());
        map.put("endTime", hpi.getEndTime());
        list.add(map);

        //查询审批结果:
        Map<String, Object> variableMap = new HashMap<>();
        List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceId(hpi.getId()).list();
        for(HistoricVariableInstance hvi:varInstanceList){
            variableMap.put(hvi.getVariableName(),hvi.getValue());
        }
        map.put("variables", variableMap);
        list.add(map);
    }
    return list;
}

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

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

请求结果:

[
    {
        startUserId: "A1001",
        variables: {
            date: "2020-08-30T09:22:52.019+00:00",
            reason: "家里有事",
            auditOpinion: "同意",
            deptJobNumber: "A1002",
            audit: "1",
            days: "2",
            jobNumber: "A1001",
        },
        startTime: "2020-08-30T09:22:27.076+00:00",
        endTime: "2020-08-30T09:23:10.892+00:00"
    }
]

悟纤小结

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

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

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

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

 

(1)核心步骤:

① 创建一个bpmn文件;

② 引人activiti的依赖;

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

 

我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。

à悟空学院:https://t.cn/Rg3fKJD

学院中有Spring Boot相关的课程!点击「阅读原文」进行查看!

SpringBoot视频:http://t.cn/A6ZagYTi

SpringBoot交流平台:https://t.cn/R3QDhU0

SpringSecurity5.0视频:http://t.cn/A6ZadMBe

ShardingJDBC分库分表:http://t.cn/A6ZarrqS

分布式事务解决方案:http://t.cn/A6ZaBnIr

JVM内存模型调优实战:http://t.cn/A6wWMVqG

Spring入门到精通:https://t.cn/A6bFcDh4

大话设计模式之爱你:https://dwz.cn/wqO0MAy7

悟纤 CSDN认证博客专家 知远公司创始人 架构师 访问1000万+
「公众号SpringBoot」:
①阿里巴巴前高级研发工程师;
②估值20亿美金的Blued架构师;
③北京知远公司创始人;
④浙江甄才公司架构师;
⑤云课堂学员10000+;
⑥博客访问量1000万+;
⑦10年互联网行业从业;
⑧360万的访问《从零开始学SprngBoot》作者;
⑨技术加盟多个独立项目。
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页
实付 19.89元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值