创建工作流应用

配置文件

yaml配置文件

# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8081
  servlet:
    # 应用的访问路径
    context-path: /liu
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
    # 连接数满后的排队数,默认为100
    accept-count: 1000
    threads:
      # tomcat最大线程数,默认为200
      max: 800
      # Tomcat启动初始化的线程数,默认值10
      min-spare: 100

# 日志配置
logging:
  level:
    com.demo: debug
    org.springframework: warn

# Spring配置
spring:
  # 文件上传
  servlet:
    multipart:
      # 单个文件大小
      max-file-size: 10MB
      # 设置总上传的文件大小
      max-request-size: 20MB
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/flowable67?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT&nullCatalogMeansCurrent=true
    username: flowable
    password: flowable@Dw

debug: true

flowable:
  process-definition-location-prefix: classpath*:/bpmn/

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>dachengDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <hutool.version>5.8.26</hutool.version>
        <flowable.version>6.7.2</flowable.version>
        <skipTest>true</skipTest>
    </properties>

    <dependencyManagement>
        <dependencies>

            <!-- SpringBoot的依赖配置-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.5.15</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>

            <dependency>
                <groupId>org.flowable</groupId>
                <artifactId>flowable-engine</artifactId>
                <version>${flowable.version}</version>
            </dependency>

            <dependency>
                <groupId>org.flowable</groupId>
                <artifactId>flowable-spring-boot-starter-process</artifactId>
                <version>${flowable.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <dependencies>

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

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

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

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>

        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-engine</artifactId>
        </dependency>

        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-process</artifactId>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                    <compilerArgs>
                        <compilerArg>-parameters</compilerArg>
                    </compilerArgs>
                </configuration>

            </plugin>
        </plugins>
    </build>


</project>

工作流文件

基于BPMN2.0标准的流程文件

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:flowable="http://flowable.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"
             typeLanguage="http://www.w3.org/2001/XMLSchema"
             expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef"
             exporter="Flowable Open Source Modeler" exporterVersion="6.7.2">
    <process id="RequestLeaveApply" name="请事假审批流程" isExecutable="true">
        <startEvent id="start" name="开始" flowable:formFieldValidation="true"></startEvent>
        <userTask id="initer" name="发起申请" flowable:assignee="${initRequest}" flowable:formFieldValidation="true">
            <extensionElements>
                <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
            </extensionElements>
        </userTask>
        <userTask id="deptPass" name="部门领导" flowable:candidateGroups="a" flowable:formFieldValidation="true"></userTask>
        <exclusiveGateway id="judgeDay" name="是否大于一天"></exclusiveGateway>
        <userTask id="leaderPass" name="老板" flowable:candidateGroups="b" flowable:formFieldValidation="true"></userTask>
        <endEvent id="end" name="结束"></endEvent>
        <sequenceFlow id="judgeDayLess" name="小于一天" sourceRef="judgeDay" targetRef="end">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${applyDay<=1}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="deptPassTrue" name="部门通过" sourceRef="deptPass" targetRef="judgeDay">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptPass==true}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="startLine" name="开始流程" sourceRef="start" targetRef="initer"></sequenceFlow>
        <sequenceFlow id="submit" name="申请提交" sourceRef="initer" targetRef="deptPass"></sequenceFlow>
        <sequenceFlow id="deptPassFalse" name="部门驳回" sourceRef="deptPass" targetRef="initer">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptPass==false}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="leaderPassFalse" name="老板驳回" sourceRef="leaderPass" targetRef="initer">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leaderPass==false}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="leaderPassTrue" name="老板通过" sourceRef="leaderPass" targetRef="end">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${leaderPass==true}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="judgeDayThan" name="大于一天" sourceRef="judgeDay" targetRef="leaderPass">
            <extensionElements>
                <flowable:executionListener event="start" class="com.demo.listener.MyExecutorListener"></flowable:executionListener>
            </extensionElements>
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${applyDay>1}]]></conditionExpression>
        </sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_RequestLeaveApply2">
        <bpmndi:BPMNPlane bpmnElement="RequestLeaveApply2" id="BPMNPlane_RequestLeaveApply2">
            <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
                <omgdc:Bounds height="30.0" width="30.0" x="120.0" y="163.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="initer" id="BPMNShape_initer">
                <omgdc:Bounds height="80.0" width="100.0" x="225.0" y="138.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="deptPass" id="BPMNShape_deptPass">
                <omgdc:Bounds height="80.0" width="100.0" x="435.0" y="138.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="judgeDay" id="BPMNShape_judgeDay">
                <omgdc:Bounds height="40.0" width="40.0" x="660.0" y="158.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="leaderPass" id="BPMNShape_leaderPass">
                <omgdc:Bounds height="80.0" width="100.0" x="630.0" y="300.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
                <omgdc:Bounds height="28.0" width="28.0" x="830.0" y="164.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="judgeDayLess" id="BPMNEdge_judgeDayLess" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
                <omgdi:waypoint x="699.5022838749192" y="178.441717791411"></omgdi:waypoint>
                <omgdi:waypoint x="830.0000626357853" y="178.0426605073493"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="deptPassTrue" id="BPMNEdge_deptPassTrue" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="20.5" flowable:targetDockerY="20.5">
                <omgdi:waypoint x="534.95" y="178.12774936061385"></omgdi:waypoint>
                <omgdi:waypoint x="660.448717948718" y="178.44871794871796"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="submit" id="BPMNEdge_submit" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="324.94999999995036" y="178.0"></omgdi:waypoint>
                <omgdi:waypoint x="434.99999999999704" y="178.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="judgeDayThan" id="BPMNEdge_judgeDayThan" flowable:sourceDockerX="20.5" flowable:sourceDockerY="20.5" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="680.4409937888199" y="197.50293296089387"></omgdi:waypoint>
                <omgdi:waypoint x="680.1236842105263" y="300.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="startLine" id="BPMNEdge_startLine" flowable:sourceDockerX="15.0" flowable:sourceDockerY="15.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="149.9499990675947" y="178.0"></omgdi:waypoint>
                <omgdi:waypoint x="224.99999999996822" y="178.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="leaderPassTrue" id="BPMNEdge_leaderPassTrue" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="14.0" flowable:targetDockerY="14.0">
                <omgdi:waypoint x="729.9499999999999" y="340.0"></omgdi:waypoint>
                <omgdi:waypoint x="844.0" y="340.0"></omgdi:waypoint>
                <omgdi:waypoint x="844.0" y="191.94992670393867"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="leaderPassFalse" id="BPMNEdge_leaderPassFalse" flowable:sourceDockerX="50.0" flowable:sourceDockerY="40.0" flowable:targetDockerX="50.0" flowable:targetDockerY="40.0">
                <omgdi:waypoint x="629.9999999999889" y="340.0"></omgdi:waypoint>
                <omgdi:waypoint x="275.0" y="340.0"></omgdi:waypoint>
                <omgdi:waypoint x="275.0" y="217.95000000000002"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="deptPassFalse" id="BPMNEdge_deptPassFalse" flowable:sourceDockerX="50.0" flowable:sourceDockerY="1.0" flowable:targetDockerX="50.0" flowable:targetDockerY="1.0">
                <omgdi:waypoint x="485.0" y="138.0"></omgdi:waypoint>
                <omgdi:waypoint x="485.0" y="50.0"></omgdi:waypoint>
                <omgdi:waypoint x="275.0" y="50.0"></omgdi:waypoint>
                <omgdi:waypoint x="275.0" y="138.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>

测试方法实现

1.获取所有的流程定义

    @APIDesc("获取所有的流程定义")
    @GetMapping("/all/process/definition")
    public RspBase<Map<String, String>> getAllProcessDefinition() {
        Map<String, String> respData = new HashMap<>();
        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionKey().asc().list();
        for (ProcessDefinition processDefinition : processDefinitions) {
            respData.put(processDefinition.getKey(), processDefinition.getName());
        }

        return new RspBase<Map<String, String>>().setCode(200).setMsg("获取定义成功").setData(respData);
    }

2.发起流程

    @APIDesc("发起请假流程  processKey:RequestLeaveApply")
    @GetMapping("/init")
    public RspBase<Map<String, Object>> sub(@RequestParam String name, @RequestParam String processKey, @RequestParam int applyDay) {
        // 员工提交请假申请
        Map<String, Object> map = new HashMap<>();

        map.put("applyDay", applyDay);
        map.put("initRequest", name);

        // leave为员工请假流程xml文件中的id
        ProcessInstance a1 = runtimeService.startProcessInstanceByKey(processKey, map);
        Task task = taskService.createTaskQuery().processInstanceId(a1.getId()).singleResult();
        taskService.complete(task.getId());

        return new RspBase<Map<String, Object>>().setCode(200).setMsg("发起成功").setData(task.getProcessVariables());
    }

3.获取所有进行中的任务

    @APIDesc("获取所有进行中的任务")
    @GetMapping("/task/all")
    public RspBase<List<Map<String, Object>>> queryTaskAll() {
        List<Task> list = taskService.createTaskQuery().orderByTaskCreateTime().asc().list();
        List<Map<String, Object>> taskMap = new ArrayList<>();
        for (Task task : list) {
            // 根据任务id查询当前任务参数
            Map<String, Object> variables = taskService.getVariables(task.getId());
            variables.put("_TaskName", task.getName());
            variables.put("_TaskID", task.getId());
            variables.put("_TaskAssignee", task.getAssignee());
            taskMap.add(variables);
        }
        return new RspBase<List<Map<String, Object>>>().setCode(200).setMsg("查询成功").setData(taskMap);
    }

4.获取指定候选组下的任务


    @APIDesc("获取指定候选组下的任务 a:部门 b:老板")
    @GetMapping("/candidate/group")
    public RspBase<List<Map<String, Object>>> queryCandidateGroupTask(@RequestParam String candidateGroup) {

        // 查询领导分组的任务
        List<Task> teacher = taskService.createTaskQuery().taskCandidateGroup(candidateGroup).orderByTaskCreateTime().asc().list();

        List<Map<String, Object>> taskMap = new ArrayList<>();

        for (Task task : teacher) {
            // 根据任务id查询当前任务参数
            Map<String, Object> variables = taskService.getVariables(task.getId());
            variables.put("_TaskName", task.getName());
            variables.put("_TaskID", task.getId());
            variables.put("_TaskAssignee", task.getAssignee());
            taskMap.add(variables);
        }

        return new RspBase<List<Map<String, Object>>>().setCode(200).setMsg("查询成功").setData(taskMap);

    }

5.节点审批


    @APIDesc("节点审批 candidateGroup: a部门 b老板 | completeVariableKey: deptPass部门审核时提交的变量 leaderPass老板审核时提交的变量 | value:true通过 false驳回")
    @GetMapping("/task/complete")
    public RspBase<List<Map<String, Object>>> taskComplete(@RequestParam String candidateGroup, @RequestParam String completeVariableKey, @RequestParam boolean value) {

        // 领导审批
        List<Task> teacherTaskList = taskService.createTaskQuery().taskCandidateGroup(candidateGroup).list();
        Map<String, Object> teacherMap = new HashMap<>();
        teacherMap.put(completeVariableKey, value);

        List<Map<String, Object>> taskMap = new ArrayList<>();
        for (Task teacherTask : teacherTaskList) {
            taskService.complete(teacherTask.getId(), teacherMap);

            Map<String, Object> taskInfo = new HashMap<>();
            taskInfo.put("_TaskName", teacherTask.getName());
            taskInfo.put("_TaskID", teacherTask.getId());
            taskInfo.put("_TaskAssignee", teacherTask.getAssignee());
            taskInfo.putAll(teacherTask.getProcessVariables());


            taskMap.add(taskInfo);
        }
        return new RspBase<List<Map<String, Object>>>().setCode(200).setMsg("节点审批成功").setData(taskMap);
    }

6.驳回流程重新发起


    @APIDesc("驳回流程重新发起")
    @GetMapping("/reinit")
    public RspBase<Map<String, Object>> reinit(@RequestParam String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        taskService.complete(task.getId());
        return new RspBase<Map<String, Object>>().setCode(200).setMsg("驳回流程重新发起成功").setData(task.getProcessVariables());
    }

7.获取流程历史

    @APIDesc("获取流程历史  processKey:RequestLeaveApply")
    @GetMapping("/query/history")
    public RspBase<Map<String, List<Map<String, String>>>> queryHistory(@RequestParam String processKey) {

        Map<String, List<Map<String, String>>> respData = new HashMap<>();

        List<ProcessInstance> processInstance = runtimeService.createProcessInstanceQuery()
                .processDefinitionKey(processKey)
                .orderByStartTime()
                .desc()
                .list();

        if (CollectionUtils.isEmpty(processInstance)) {
            return new RspBase<Map<String, List<Map<String, String>>>>().setCode(200).setMsg("无历史流程").setData(respData);
        }

        int i = 1;
        for (ProcessInstance instance : processInstance) {

            // 获取最近的一个流程
            List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()

                    // 只查询已经完成的活动,按照结束时间排序
                    .processInstanceId(instance.getId())
                    .finished()
                    .orderByHistoricActivityInstanceEndTime()
                    .desc()

//                    .orderByHistoricActivityInstanceStartTime()
//                    .asc()
                    .list();

            List<Map<String, String>> activitiesData = activities.stream().map(a -> {
                Map<String, String> data = new HashMap<>();
                data.put("活动名称", a.getActivityName());
                data.put("活动执行时间(毫秒)", a.getDurationInMillis() + "");
                return data;
            }).collect(Collectors.toList());

            String name = instance.getProcessDefinitionName() + "(" + instance.getProcessDefinitionKey() + ")" + i;
            respData.put(name, activitiesData);
            i++;
        }

        return new RspBase<Map<String, List<Map<String, String>>>>().setCode(200).setMsg("获取流程历史成功").setData(respData);
    }

8.动态发布流程

@APIDesc("动态发布流程")
    @PostMapping("/dynamic/deploy/process")
    public RspBase<Map<String, Object>> dynamicDeployProcess(@RequestPart MultipartFile file) throws IOException {
        Deployment deployment = repositoryService.createDeployment()
                .addInputStream(file.getOriginalFilename(), file.getInputStream())
                .deploy();
        Map<String, Object> map = new HashMap<>();
        map.put("deploymentId", deployment.getId());
        map.put("deploymentName", deployment.getName());
        map.put("deploymentKey", deployment.getKey());
        return new RspBase<Map<String, Object>>().setCode(200).setMsg("动态发布流程成功").setData(map);
    }

9.删除流程

    @APIDesc("删除流程")
    @GetMapping("/dynamic/delete/process")
    public RspBase<Map<String, Object>> dynamicDeployProcess(@RequestParam String processKey) {
        repositoryService.deleteDeployment(processKey, true);
        return new RspBase<Map<String, Object>>().setCode(200).setMsg("删除流程成功").setData(null);
    }
作者:yuanfun  创建时间:2025-01-08 16:14
最后编辑:yuanfun  更新时间:2025-01-16 13:51