Springboot整合Activiti/Flowable(最详细版)

写在最前:flowable和activiti本是一家,所以有很多api和设计是一样的,这里用的api是flowable的,流程设计器war包也可以共用,建议搜不到activiti某些资料的搜flowable的试试。

1.导包

<?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>activiti_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <mysql.version>5.1.29</mysql.version>
    </properties>
    <dependencies>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        
        <!--两种使用坐标任选其一-->
        <!--activiti坐标始-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.1.0.M3.1</version>
        </dependency>
        <!--解决activiti坐标导致的security注入问题-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>
        <!--activiti坐标末-->
        
		<!--flowable坐标始-->
		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-spring-boot-starter-process</artifactId>
			<version>6.6.0</version>
		</dependency>
		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-ui-modeler-logic</artifactId>
			<version>6.6.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.mybatis</groupId>
					<artifactId>mybatis</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-log4j2</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.google.guava</groupId>
					<artifactId>guava</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!--flowable坐标末-->
		
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.新建启动类
(1)activiti方式:启动类上的exclude = {}一定要有,排除这两个配置类,不然会与activiti的权限管理冲突报错

(2)flowable方式则不需要启动类上的exclude = {}

package com.wanglj;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Author 955
 * @Date 2022-06-21 13:47
 * @Description
 */
@SpringBootApplication(exclude = {
        org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
        org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class
})
public class ActivitiApplication {
    public static void main(String[] args) {
        SpringApplication.run(ActivitiApplication.class, args);
    }
}

3.idea下载bpmn画图插件Activiti BPMN visualizer,安装重启idea(actiBPM插件早就不适配2021的idea了,想用可以降级idea版本或者用eclipse画,我相信没人想用这两个方法,推荐使用下文官方画图工具,Activiti BPMN visualizer这个插件只能有一些简单的功能)
在这里插入图片描述
4.在resources目录下新建processes文件夹,并点击右键选择bpmn文件新建(注意:流程名字随便取,但文件必须是xx.bpmn20.xml格式)
在这里插入图片描述
5.填写相应参数
在这里插入图片描述
6.打开新建的xml文件,点击右键打开流程图工具
在这里插入图片描述
7.画流程图(以user task为例,其他功能自行参考官网),点击右键新建开始–>Activities–>User task,注意每个步骤写好流程名称
在这里插入图片描述

在这里插入图片描述

注:流转下一步人名称得写上(这里提交请假申请-->worker,部门经理审批-->leader,财务审批-->finance)

在这里插入图片描述

拖动箭头使流程连接

在这里插入图片描述

最终效果

在这里插入图片描述

8.保存流程图到项目,右键Save to PNG,选择路径为xx.bpmn20.xml同路径
在这里插入图片描述

完整结构如图

在这里插入图片描述
9.新建配置
(1)activitiXML方式配置(注:必须叫activiti.cfg.xml且必须放resources根目录下,否则需要在代码配置放置路径,内容如下)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

        <property name="jdbcUrl" value="jdbc:mysql://xxxxxxx:3306/xxxx" />
        <property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver" />
        <property name="jdbcUsername" value="xxxx" />
        <property name="jdbcPassword" value="xxxx" />

        <property name="databaseSchemaUpdate" value="true" />

    </bean>

</beans>

(2)flowable配置文件方式(activiti也能使用此方式配置,只是少许方法参数不一样):

import liquibase.Liquibase;
import lombok.AllArgsConstructor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.ui.modeler.service.FlowableModelQueryService;
import org.flowable.ui.modeler.service.ModelImageService;
import org.flowable.ui.modeler.service.ModelServiceImpl;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * @Author 955
 * @Date 2022-06-21 15:01
 * @Description
 */
@Configuration
@AllArgsConstructor
public class ActivitiConfig {


    private final DataSource dataSource;

    private final PlatformTransactionManager transactionManager;


    @Bean
    public SpringProcessEngineConfiguration getProcessEngineConfiguration() {
        SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
        // 流程图字体设置
        config.setActivityFontName("宋体");
        config.setAnnotationFontName("宋体");
        config.setLabelFontName("黑体");
        config.setDataSource(dataSource);
        config.setTransactionManager(transactionManager);
        config.setAsyncExecutorActivate(true);
        //数据库类型
        config.setDatabaseType("mysql");
        //是否使用idm
        config.setDisableIdmEngine(true);
        //是否开启检查表结构
        config.setDatabaseSchemaUpdate("false");
        return config;
    }


    @Bean
    public ModelImageService modelImageService() {
        return new ModelImageService();
    }

    @Bean
    public ModelService modelService() {
        return new ModelServiceImpl();
    }

//    @Bean
//    public RepositoryService repositoryService(){
//        return new RepositoryServiceImpl();
//    }
//
//
//    @Bean
//    public RuntimeService runtimeService(){
//        return new RuntimeServiceImpl();
//    }
//    
//    @Bean
//    public TaskService taskService(){
//        return new TaskServiceImpl();
//    }
//    
//    @Bean
//    public HistoryService historyService(){
//        return new HistoryServiceImpl();
//    }
//    @Bean
//    public ProcessEngineFactoryBean processEngineFactoryBean(){
//        return new ProcessEngineFactoryBean();
//    }


    @Bean(destroyMethod = "clearCache")
    @Qualifier("flowableModeler")
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    public FlowableModelQueryService flowableModelQueryService() {
        return new FlowableModelQueryService();
    }

    @Bean
    @Primary
    public TaskExecutor primaryTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }


}

注:若使用flowable集成时,需在yml文件配置如下内容,否则启动报错

mybatis-plus:  
  configuration-properties:
    blobType: BLOB
    prefix: 

10.编写测试代码

注:其中开始流程的key值为新建流程文件时的名称,如果不清楚可打开流程图面板,左键点击空白处,可显示id,对应key值

在这里插入图片描述

import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;

/**
 * @Author 955
 * @Date 2022-06-21 15:01
 * @Description
 */
@Slf4j
public class test {
    /**
     * 初始化流程部署
     */
    @Test
    public void testDeployment() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/process_955.bpmn20.xml").addClasspathResource("processes/process_955.png").name("请假申请流程").deploy();
        System.out.println("流程部署id:" + deployment.getId());
        System.out.println("流程部署名称:" + deployment.getName());
    }

    /**
     * 开始流程
     */
    @Test
    public void testStartProcess() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("process_955");

        System.out.println("流程部署id:" + processInstance.getId());
        System.out.println("流程部署名称:" + processInstance.getName());
        System.out.println("processInstanceId:" + processInstance.getProcessInstanceId());
    }

    /**
     * 查询流转到该所属角色的任务
     */
    @Test
    public void testFindPersonalTaskList() {
    	//对应各流程节点流转下一步人名称,这里第一步从worker开始
    	//调用下方completTask方法可通过审批,再查询下一个名称leader,以此类推直到结束,因为流程图没有不通过的情况所以暂不考虑
        String assignee = "worker";
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> list = taskService.createTaskQuery().processDefinitionKey("process_955").taskAssignee(assignee).list();
        for (Task task : list) {
            System.out.println("流程实例id:" + task.getProcessInstanceId());
            System.out.println("任务id:" + task.getId());
            System.out.println("任务负责人:" + task.getAssignee());
            System.out.println("任务名称:" + task.getName());
        }
    }

    /**
     * 完成任务
     */
    @Test
    public void completTask() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        //根据流程key和任务的负责人 查询任务
        //返回一个任务对象
        //对应各流程节点流转下一步人名称,这里第一步从worker开始,分别为worker-->leader-->finance
        Task task = taskService.createTaskQuery().processDefinitionKey("process_955").taskAssignee("worker").singleResult();
        //完成任务,参数:任务id
        taskService.complete(task.getId());
    }

    /**
     * 查询出当前所有的流程定义
     */
    @Test
    public void queryProcessDefinition() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        //查询出当前所有的流程定义
        List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("process_955").orderByProcessDefinitionVersion().desc().list();
        //输出流程定义信息
        for (ProcessDefinition processDefinition : list) {
            System.out.println("流程定义 id=" + processDefinition.getId());
            System.out.println("流程定义 name=" + processDefinition.getName());
            System.out.println("流程定义 key=" + processDefinition.getKey());
            System.out.println("流程部署id =" + processDefinition.getDeploymentId());
            System.out.println("<=========================================>");
        }
    }

    /**
     * 删除流程
     */
    @Test
    public void deleteDeployment(){
        String deploymentId = "1";
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //通过流程引擎获取repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //删除流程定义,如果该流程定义已有流程实例启动则删除报错
        repositoryService.deleteDeployment(deploymentId);
        //设置为true,则有流程实例在启动也可以强制删除
//        repositoryService.deleteDeployment(deploymentId,true);
    }

    /**
     * 输出流程文件和流程图到文件夹
     * @throws Exception
     */
    @Test
    public void queryBpmnFile()throws Exception{
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("process_955").singleResult();
        //通过流程定义信息,得到部署id
        String deploymentId = processDefinition.getDeploymentId();
        //得到png图片流
        InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
        //得到bpmn文件流
        InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
        File file_png = new File("C:\\Users\\HYGK\\Desktop\\bpmn\\process_955.png");
        File file_bpmn = new File("C:\\Users\\HYGK\\Desktop\\bpmn\\process_955.bpmn");
        FileOutputStream pngOut = new FileOutputStream(file_png);
        FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
        IOUtils.copy(pngInput,pngOut);
        IOUtils.copy(bpmnInput,bpmnOut);
        pngOut.close();
        bpmnOut.close();
    }

    /**
     * 根据instanceId查询整个流程
     */
    @Test
    public void findHistoryInfo(){
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        //获取actinst表的查询对象
        HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
        //根据instanceId查询整个流程
        instanceQuery.processInstanceId("5001").orderByHistoricActivityInstanceStartTime().asc();
        List<HistoricActivityInstance> list = instanceQuery.list();
        for (HistoricActivityInstance historicActivityInstance : list) {
            System.out.println(historicActivityInstance.getActivityId());
            System.out.println(historicActivityInstance.getActivityName());
            System.out.println(historicActivityInstance.getProcessDefinitionId());
            System.out.println(historicActivityInstance.getCalledProcessInstanceId());
            System.out.println("<=========================================>");
        }
    }

}

==============================================================================

进阶使用:

写在前面:如下使用flowable-ui画流程图,初始操作如下,但第一次启动后所有画好的流程文件都是一次性的,关闭服务就没有的,若想存数据库,请先参考下列第5条。

要使用监听器最好使用其他的画图工具,idea的画图工具支持不是很好,这里推荐官方的工具
1.flowable下载地址(从flowable7版本开始flowable-ui被移除,若想使用建议下载6.X的版本或者前往官网使用最新的设计器):https://github.com/flowable/flowable-engine/releases
2.下载解压后复制这两个war包到tomcat的wepapps目录下
3.启动tomcat,访问localhost:8080/flowable-ui,初始账号:admin,初始密码:test
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.画流程图
在这里插入图片描述
在这里插入图片描述

5.流程数据保存到数据库
(1)关闭tomcat服务,进入webapps,发现生成了两个文件夹(注:需启动一次后才会生成)
在这里插入图片描述
(2)分别点击进入两个文件夹,位置如下

C:\apache-tomcat-9.0.71\webapps\flowable-ui\WEB-INF\classes\flowable-default.properties
C:\apache-tomcat-9.0.71\webapps\flowable-rest\WEB-INF\classes\flowable-default.properties

(3)修改配置文件数据库连接(注释初始连接,选择数据源放开修改,这里选择mysql,若是oracle选择对应数据源配置即可,需注意的是,配置连接信息后会出现一系列的问题,如数据库版本问题、选择其他数据源启动报错等问题,只需更换或新增X:\apache-tomcat-9.0.71\webapps\flowable-ui\WEB-INF\lib目录下对应的jar包即可,如oracle数据库需要引入oracle的驱动jar包才行,这里不过多赘述)

提供一个mysql的连接信息:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://xxx.xxx.xxx.x:3306/flowable_biz?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true

在这里插入图片描述
例如这里就缺少mysql的驱动,maven仓库下一个即可
在这里插入图片描述

6.启动tomcat,等待数据库建立这些表,只要能启动和在建表中间有报错不用管,启动完成后按上述1、2、3、4步骤即可
在这里插入图片描述

注:
Execution listeners为执行监听器,可以查看执行中的一些流程信息等。
Task listeners为任务监听器,可以当任务流转到某一步骤时,如动态设置受理人等。
Assignments为指定用户/用户组,可以指定当前步骤由谁执行。

流程指向箭头:
在流程指向箭头中,可以设置el表达式来设定满足某些条件时指定流程走向,具体用法为${逻辑},此外流程箭头可以点击工具类上方拆分按钮使连线拆分,满足强迫症患者。
在这里插入图片描述
在这里插入图片描述
执行/任务监听器设定:
点击:Execution listeners/Task listeners,添加监听器,监听器配置为项目相对路径

在这里插入图片描述
画好流程图后点击保存,会直接保存到act_de_model表中,前提是上面配置了数据源连接
在这里插入图片描述

<5>编写监听类(以经理审批任务监听类为例,执行监听器和任务监听器的代码区别就是分别实现ExecutionListener 和TaskListener,重写notify方法的参数DelegateTaskDelegateExecution不同):
注:想了解执行/任务监听器的设计区别,请参考这一文章:https://blog.csdn.net/yabushandaxue/article/details/119297080

package com.wanglj.listener;

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

/**
 * @Author 955
 * @Date 2022-10-14 14:34
 * @Description 经理审批任务监听器
 */
public class ManagerTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
    //动态配置用户组
        delegateTask.addCandidateGroup("jl1,jl2,jl3");
    //动态配置执行人
        delegateTask.setAssignee("s");
    //这里也可配置满足某些条件自动跳过下一步,简单来说就是走一下执行流程方法
    //需要注意的是监听器没有被spring管理(我自己的理解),所以不能注入service进来用,得用上下文去获取bean来用
    }
}


/**
 * @Author 955
 * @Date 2022-10-14 10:10
 * @Description 经理审批执行监听器
 */
public class ManagerExecutionListener implements ExecutionListener {
    @Override
    public void notify(DelegateExecution delegateExecution) {
        System.out.println("经理审批监听");
    }
}


<6>提供一些service方法:
注:如果是新建项目未建表的,在springboot-test类执行此方法,配置好流程文件路径即可

    @Test
    public void testDeployment1() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/leave.bpmn20.xml").addClasspathResource("processes/leave.png").name("借款申请流程").deploy();
        System.out.println("流程部署id:" + deployment.getId());
        System.out.println("流程部署名称:" + deployment.getName());
    }

(1)act业务父类

FinshBpVo
package com.wanglj.vo;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.util.Map;

/**
 * @Author 955
 * @Date 2022-10-14 11:15
 * @Description
 */
@Data
public class FinshBpVo implements Serializable {
    @ApiModelProperty(value = "任务id")
    private String taskId;

    @ApiModelProperty(value = "下一步需要的参数,可为空")
    private Map<String, Object> variables;

    @ApiModelProperty(value = "用户")
    private String userId;

    @ApiModelProperty(value = "完成时的批注内容")
    private String comment;
}

package com.wanglj.service.impl;

import com.wanglj.util.R;
import com.wanglj.vo.EndProcessVo;
import com.wanglj.vo.StartProcessInstanceVo;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * act业务父类
 * @Author 955
 * @Date 2022-07-04 10:21
 * @Description
 */
@Service
public class BaseActService {
    @Autowired
    protected ManagementService managementService;
    @Autowired
    protected TaskService taskService;
    @Autowired
    protected RuntimeService runtimeService;
    @Autowired
    protected RepositoryService repositoryService;
    @Autowired
    protected HistoryService historyService;

    /**
     * 初始化流程部署
     */
    public void testDeployment() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/leave.bpmn20.xml").addClasspathResource("processes/leave.png").name("借款申请流程").deploy();
        System.out.println("流程部署id:" + deployment.getId());
        System.out.println("流程部署名称:" + deployment.getName());
    }


    public R<String> startProcessInstance(StartProcessInstanceVo vo) {
        testDeployment();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(vo.getProcessDefinitionKey())
                .latestVersion().singleResult();
        if (processDefinition != null && processDefinition.isSuspended()) {
            return R.fail("该流程已存在");
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .processDefinitionKey(vo.getProcessDefinitionKey().trim())
                .businessKey(vo.getBusinessKey().trim())
                .variables(vo.getVariables())
                .start();

        String processInstanceId = processInstance.getProcessInstanceId();
        return R.ok(processInstanceId);
    }


    public R<String> stopProcessInstanceById(EndProcessVo endVo) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(endVo.getProcessInstanceId()).singleResult();
        if (processInstance != null) {
            //1、添加审批记录
//            flowableCommentService.addComment(null,endVo.getUserId(),endVo.getUserName(),endVo.getDeptId(), endVo.getProcessInstanceId(), TaskStatusEnum.QZOVER.getStatus(),
//                    endVo.getMessage());
//            List<EndEvent> endNodes = flowableBpmnModelService.findEndFlowElement(processInstance.getProcessDefinitionId());
//            String endId = endNodes.get(0).getId();
            String processInstanceId = endVo.getProcessInstanceId();
            //2、执行终止
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
            List<String> executionIds = new ArrayList<>();
            executions.forEach(execution -> executionIds.add(execution.getId()));
//            this.moveExecutionsToSingleActivityId(executionIds, endId);
            return R.ok("终止成功!");
        } else {
            return R.fail("不存在运行的流程实例,请确认!");
        }

    }


    public List<FlowNode> findFlowNodes(String processDefId) {
        List<FlowNode> flowNodes = new ArrayList<>();
        BpmnModel bpmnModel = this.getBpmnModelByProcessDefId(processDefId);
        org.activiti.bpmn.model.Process process = bpmnModel.getMainProcess();
        Collection<FlowElement> list = process.getFlowElements();
        list.forEach(flowElement -> {
            if (flowElement instanceof FlowNode) {
                flowNodes.add((FlowNode) flowElement);
            }
        });
        return flowNodes;
    }

    public BpmnModel getBpmnModelByProcessDefId(String processDefId) {
        return repositoryService.getBpmnModel(processDefId);
    }


    public void testFindPersonalTaskList() {
        String assignee = "xmfzr1";
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
//        List<Task> list = taskService.createTaskQuery().processDefinitionKey("order_ex_flow").taskAssignee(assignee).list();
        List<Task> list = taskService.createTaskQuery().processDefinitionKey("order_ex_flow").taskCandidateUser(assignee).list();
        for (Task task : list) {
            System.out.println("流程实例id:" + task.getProcessInstanceId());
            System.out.println("ProcessDefinitionId:" + task.getProcessDefinitionId());
            System.out.println("任务id:" + task.getId());
            System.out.println("任务负责人:" + task.getAssignee());
            System.out.println("任务名称:" + task.getName());
            System.out.println("任务名称:" + task.getCreateTime());
        }
    }

}

(2)具体业务实现类(只是一些很杂乱的方法,最下方有可直接用于生产的接口设计-原生):

package com.wanglj.service.impl;

import com.wanglj.service.ActivitiService;
import com.wanglj.util.Kit;
import com.wanglj.util.R;
import com.wanglj.vo.FinshBpVo;
import com.wanglj.vo.StartProcessInstanceVo;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ActivitiTaskAlreadyClaimedException;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Author 955
 * @Date 2022-10-09 15:38
 * @Description
 */
@Service
@Slf4j
public class ActivitiServiceImpl extends BaseActService implements ActivitiService {

    @Override
    public R<String> startApplyProcess(StartProcessInstanceVo vo) {
        return startProcessInstance(vo);
    }

    /**
     * 获取最新版本的所有的流程定义列表
     *
     * @return
     */
    @Override
    public List<ProcessDefinition> getLastVersionProcessDefinitions() {
        //我们需要的是最新的版本
        return repositoryService.createProcessDefinitionQuery().latestVersion().list();
    }

    /**
     * 根据流程定义id 获取到对应的模板节点信息 节点名称,节点id,监听事件也可以获取
     *
     * @param processDefinitionId
     * @return
     */
    @Override
    public List<FlowElement> getFlowElementByProcessDefId(String processDefinitionId) {

        log.info("根据流程定义获取节点信息入参:{}", processDefinitionId);
        //流程定义中的模板信息
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        //模板中的所有流程
        List<Process> processes = bpmnModel.getProcesses();
        log.info("流程定义id:{}的流程条数大小:{}", processDefinitionId, processes.size());

        //取得流程的节点包括 连线实体
        Process process = processes.get(0);

        //把所有节点 用map装好。
        Map<String, FlowElement> flowElements = process.getFlowElementMap();
        List<FlowElement> flowElementList = new ArrayList<>();
        if (CollectionUtils.isEmpty(flowElements)) {
            return flowElementList;
        }
        //转换一下 成为list
        for (Map.Entry<String, FlowElement> flowElement : flowElements.entrySet()) {
            flowElementList.add(flowElement.getValue());
        }

        return flowElementList;
    }


    /**
     * 根据用户id 或者该用户的所有 有权限审核的流程task
     *
     * @param userId
     * @return
     */
    @Override
    public List<Task> getAuthorityTaskByUserId(String userId) {
        return taskService
                .createTaskQuery()
                .taskCandidateUser(userId)
                .taskUnassigned()
                .list();
    }

    /**
     * 用户认领task,如果该用户有权限并且该task未被认领
     *
     * @param taskId
     * @param userId
     */
    @Override
    public R<Boolean> claimTaskByUserId(String taskId, String userId) {
        try {
            taskService.claim(taskId, userId);
        } catch (ActivitiTaskAlreadyClaimedException e) {
            log.info("该任务已经被认领taskId:{},userId:{}", taskId, userId);
            R.fail("该任务已经被其他的人认领");
        } catch (Exception e) {
            log.info("无权限认领该任务:{},userId:{}", taskId, userId);
        }
        return R.ok(true);
    }

    /**
     * 根据用户id 获取需要立即审核的流程task
     *
     * @param userId
     * @return
     */
    @Override
    public List<Task> getAssignedTaskByUserId(String userId) {
        return taskService.createTaskQuery().taskAssignee(userId).list();
    }

    /**
     * 完成当前节点 进入到下一步。
     *
     * @param taskId finshBpVo
     * @throws Exception
     */
    @Override
    public R<Boolean> completeTask(FinshBpVo finshBpVo) {
        if (Kit.isNotEmpty(finshBpVo)) {
            String taskId = finshBpVo.getTaskId();
            String comment = finshBpVo.getComment();
            String userId = finshBpVo.getUserId();
            Map<String, Object> variables = finshBpVo.getVariables();
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            if (Objects.isNull(task)) {
                log.info("该任务taskId:{},已被完成或者不存在", taskId);
                return R.fail("任务已被完成或者不存在");
            }
            if (Objects.nonNull(task) && !userId.equals(task.getAssignee())) {
                log.info("该用户没有权限完成处理该任务taskId:{},userId:{},yUserId:{}", taskId, userId, task.getAssignee());
                return R.fail("该用户没有权限完成处理该任务");
            }
            //如果批注信息不为空,新增批注信息内容
            if (StringUtils.isNotBlank(comment)) {
                taskService.addComment(taskId, task.getProcessInstanceId(), comment);
            }
            taskService.complete(taskId, variables);
            return R.ok(true);
        }
        return R.ok(false);
    }

    /**
     * 根据实例id 获取已经完成的节点
     *
     * @param processInstanceId
     * @return
     */
    @Override
    public List<HistoricTaskInstance> getHistoryTaskByProcessInstanceId(String processInstanceId) {
        return historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished().orderByTaskCreateTime().desc().list();
    }

    /**
     * 根据用户id获取到 用户处理过的所有任务信息
     *
     * @param userId
     * @return
     */
    @Override
    public List<HistoricTaskInstance> getHistoryTaskByUserId(String userId) {
        return historyService
                .createHistoricTaskInstanceQuery()
                .taskAssignee(userId).finished()
                .orderByTaskCreateTime().desc().list();
    }

    /**
     * 根据流程实例id 结束任务 任何时候都可以结束
     *
     * @param processId
     * @return
     */
    @Override
    public boolean finishProcess(String processId) {
        runtimeService.deleteProcessInstance(processId, "结束");
        Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
        if (null == task) {
            return true;
        }
        return false;
    }

    /**
     * 根据taskId 获取批注消息
     *
     * @param taskId 任务id
     * @return
     */
    @Override
    public Comment getComment(@RequestParam("taskId") String taskId) {
        return taskService.getComment(taskId);
    }

    /**
     * 根据instanceId查询整个流程
     */
    @Override
    public List<HistoricActivityInstance> findHistoryInfo(String processInstanceId) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        //获取actinst表的查询对象
        HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
        //根据instanceId查询整个流程
        instanceQuery.processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc();
        List<HistoricActivityInstance> list = instanceQuery.list();
        return list;
    }

    /**
     * 撤回或者驳回任务, 撤回是当前处理人 处理任务后后悔了,在下一个处理人还未处理的情况可以撤回
     * 驳回:当前处理人 查看任务后发现上一步处理人未处理到位,可以驳回到上一个人。
     *
     * @param processInstanceId 流程实例id
     * @param userId            当前用户id
     * @param setUserId         撤回或者驳回后的 设置用户处理人id
     * @param recallOrRebutFlag 是撤回还是驳回标识  1代表撤回 2代表驳回
     * @param comment           撤回或者驳回时的批注内容
     * @throws Exception
     */
    @Override
    public R<Boolean> cancel(String processInstanceId, String userId, String setUserId, Integer recallOrRebutFlag, String comment) {
        log.info("撤回或者驳回的任务的参数processInstanceId:{}, userId:{},serUserId:{}, recallOrRebutFlag:{}",
                processInstanceId, userId, setUserId, recallOrRebutFlag);


        //参数验证
        if (StringUtils.isBlank(processInstanceId)) {
            return R.fail("流程实例id不能为空");
        }
        if (StringUtils.isBlank(userId)) {
            return R.fail("当前用户id不能为空");
        }
        if (StringUtils.isBlank(setUserId)) {
            return R.fail("撤回或者驳回后的处理人id不能为空");
        }
        if (Objects.isNull(recallOrRebutFlag)) {
            return R.fail("撤回或这驳回类型不能为空");
        }

        //获取当前的task
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

        if (task == null) {
            log.info("任务节点已完成或者未启动,无法撤回processInstanceId:{}", processInstanceId);
            return R.fail("任务节点已完成或者未启动");
        }


        //根据时间的正序 输出,最后的一个就是正在进行中的节点任务。
        List<HistoricTaskInstance> historicTaskInstances = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime().asc().list();
        //撤回的应该是 正在进行中的上一个节点
        HistoricTaskInstance myTask = null;
        for (int i = 0; i < historicTaskInstances.size(); i++) {
            if (historicTaskInstances.get(i).getId().equals(task.getId()) && i > 0) {
                myTask = historicTaskInstances.get(i - 1);
                break;
            }
        }
        if (myTask == null) {
            log.info("流程实例id,{},上一步任务节点为空");
            return R.fail("上一步任务节点为空,无法撤回或驳回");
        }
        log.info("流程实例id,{}的上一个节点的id:{},节点处理人:{}", myTask.getId(), myTask.getAssignee());
        //权限校验
        if (recallOrRebutFlag.equals(1)) {

            if (!userId.equals(myTask.getAssignee())) {
                log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理撤回", processInstanceId, userId, myTask.getAssignee());
                return R.fail("无权限撤回");
            }
        } else if (recallOrRebutFlag.equals(2)) {
            if (!userId.equals(task.getAssignee())) {
                log.info("流程实例id:{},用户id:{}无权限,需要用户id为:{}处理驳回", processInstanceId, userId, myTask.getAssignee());
                return R.fail("无权限驳回");
            }
        } else {
            log.info("流程实例id:{},类型标识:{},不是撤回也不是驳回类型", processInstanceId, recallOrRebutFlag);
            return R.fail("类型标识无法识别");
        }

        //获取到当前节点的上一个节点的id
        String myTaskId = myTask.getId();

        String processDefinitionId = myTask.getProcessDefinitionId();

        //获取上一个节点流程定义
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService
                .createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        //获取到 历史的activity 节点
        List<HistoricActivityInstance> haiList = historyService
                .createHistoricActivityInstanceQuery()
                .executionId(myTask.getExecutionId())
                .finished().list();

        String myActivityId = null;
        for (HistoricActivityInstance hai : haiList) {
            if (myTaskId.equals(hai.getTaskId())) {
                myActivityId = hai.getActivityId();
                break;
            }
        }

        FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);


        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        //获取当前节点的 活动节点
        String activityId = execution.getActivityId();
        log.info("流程实例id:{},需要撤回的活动id:{},当前活动id:{}", processInstanceId, myActivityId, activityId);
        //获取当前的 node
        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);

        //记录当前task的节点活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<>();
        oriSequenceFlows.addAll(flowNode.getOutgoingFlows());

        //清理当前的活动方向
        flowNode.getOutgoingFlows().clear();
        //建立新的方向
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(flowNode);
        newSequenceFlow.setTargetFlowElement(myFlowNode);
        newSequenceFlowList.add(newSequenceFlow);
        flowNode.setOutgoingFlows(newSequenceFlowList);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), comment);
        taskService.complete(task.getId());

        //设置回原来的 方向
        flowNode.setOutgoingFlows(oriSequenceFlows);
        //再次获取到当前的 活动节点就已经是 撤销后的节点了
        Task cuTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        //设置受理人
        taskService.setAssignee(cuTask.getId(), setUserId);
        return R.ok(true);
    }

}

<7>swagger测试:
1.启动流程:
注:其他几个参数都不重要,processDefinitionKey参数一定得填对,值为流程图创建时输入的model_key,执行成功会返回processInstanceId

在这里插入图片描述
2.根据processInstanceId查询任务进度
在这里插入图片描述
3.完成当前节点,进入下一步
注:comment参数为批注,可不填,taskId为第2步任务进度最后一步的taskid,userId为流程图设置的userid,variables参数为流程指向线的条件等

在这里插入图片描述
4.其他业务方法自行研究即可

项目整体结构
![在这里插入图片描述](https://img-blog.csdnimg.cn/6759d6c4267d47fe89b1df48cbb42a49.png
一些工具类和返回实体:

返回实体:


import java.io.Serializable;
import java.util.List;

import com.xxxx.common.core.constant.CommonConstants;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * 响应信息体
* @author:955 
* @date:2022年5月9日 下午9:53:56    
*/
@Builder
@ToString
@Accessors(chain = true)
@ApiModel(description = "响应信息主体")
@AllArgsConstructor
public class R<T> implements Serializable
{
	private static final long serialVersionUID = 1L;

	//@Builder.Default
	@Getter
	@Setter
	@ApiModelProperty(value = "返回标记:成功标记=0,失败标记=1")
	private int code = CommonConstants.SUCCESS;

	//@Builder.Default
	@Getter
	@Setter
	@ApiModelProperty(value = "返回信息")
	private String msg = "success";
	
	
	
	@Getter
	@Setter
	@ApiModelProperty(value = "返回错误集合信息")
	private List<ErrorMessage> errorMessage;

	@Getter
	@Setter
	@ApiModelProperty(value = "数据")
	private T data;
	
	@Getter
	@Setter
	@ApiModelProperty(value = "是否成功")
	private boolean success;

	public R()
	{
		super();
	}

	public R(T data)
	{
		super();
		this.data = data;
	}

	public R(T data, String msg)
	{
		super();
		this.data = data;
		this.msg = msg;
	}

	public R(Throwable e)
	{
		super();
		this.msg = e.getMessage();
		this.code = CommonConstants.FAIL;
	}

	public static <T> R<T> ok()
	{
		return R.restResult(null, CommonConstants.SUCCESS, null);
	}

	public static <T> R<T> ok(T data)
	{
		return R.restResult(data, CommonConstants.SUCCESS, null);
	}

	public static <T> R<T> ok(T data, String msg)
	{
		return R.restResult(data, CommonConstants.SUCCESS, msg);
	}

	public static <T> R<T> fail()
	{
		return R.restResult(null, CommonConstants.FAIL, null);
	}

	public static <T> R<T> fail(int code)
	{
		return R.restResult(null, code, null);
	}

	public static <T> R<T> fail(String msg)
	{
		return R.restResult(null, CommonConstants.FAIL, msg);
	}

	public static <T> R<T> fail(int code, String msg)
	{
		return R.restResult(null, code, msg);
	}
	
	public static <T> R<T> fail(List<ErrorMessage> errorMessages)
	{
		return R.restErrorMessageResult(null, CommonConstants.FAIL, errorMessages);
	}

	public static <T> R<T> fail(T data)
	{
		return R.restResult(data, CommonConstants.FAIL, null);
	}

	public static <T> R<T> fail(T data, String msg)
	{
		return R.restResult(data, CommonConstants.FAIL, msg);
	}

	private static <T> R<T> restResult(T data, int code, String msg)
	{
		R<T> apiResult = new R<>();
		apiResult.setCode(code);
		if(code==CommonConstants.SUCCESS) {
			apiResult.setSuccess(true);
		}else if(code==CommonConstants.FAIL) {
			apiResult.setSuccess(false);
		}
		apiResult.setData(data);
		apiResult.setMsg(msg);
		return apiResult;
	}
	
	
	private static <T> R<T> restErrorMessageResult(T data, int code, List<ErrorMessage> errorMessages)
	{
		R<T> apiResult = new R<>();
		apiResult.setCode(code);
		if(code==CommonConstants.SUCCESS) {
			apiResult.setSuccess(true);
		}else if(code==CommonConstants.FAIL) {
			apiResult.setSuccess(false);
		}
		apiResult.setData(data);
		apiResult.setErrorMessage(errorMessages);
		return apiResult;
	}

	/**
	 * @param localizedMessage
	 * @return
	 */
	public static R<?> failed(String localizedMessage) {
		return R.restResult(null, CommonConstants.FAIL, localizedMessage);
	}
}


错误实体:

@Builder
@Data
@ApiModel(description = "响应错误列表")
@AllArgsConstructor
public class ErrorMessage implements Serializable {


    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private String field;

    private String message;

}

公共属性抽象类:

public interface CommonConstants
{
	/**
	 * 编码
	 */
	String UTF8 = "UTF-8";


	/**
	 * 成功标记
	 */
	Integer SUCCESS = 0;

	/**
	 * 失败标记
	 */
	Integer FAIL = 1;
	
	String AC_TASK_MSG ="msg";

	
	String AC_TASK_USER= "taskUser";
	
	
	String AC_TASK_USERNAME= "taskUserName";
	

	String AC_TASK_STATUS= "taskStatus";

	String ACT_TASK_FLAG = "flag";

	String AC_TASK_DEPT = "taskDept";
	
	
	String AC_AGENT = "agent";
	
	
	String AC_TASK_ID = "taskUnId";
	
}

可用于生产的接口设计(基于flowable,activiti类似。每个人设计思路不一, 不喜勿喷,原生写法永不过时)

设计思路:这里分为两步管理,使用模型管理和流程管理两个模块,模型管理model的增删改查部署,流程管理控制该流程的生效失效导出流程图等。顺序为先部署流程模型,再控制流程状态。

1.模型管理

import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.ui.modeler.domain.AbstractModel;
import org.flowable.ui.modeler.repository.ModelRepository;
import org.flowable.ui.modeler.repository.ModelSort;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Slf4j
@Service
@AllArgsConstructor
public class MyModelServiceImpl{

	private static final String BPMN20_XML = ".bpmn20.xml";

    private final RepositoryService repositoryService;

    private final ModelService modelService;

    private final ModelRepository modelRepository;

    private final ObjectMapper objectMapper;
    
       /**
     * 创建流程-在生成流程文件之后会自动有数据,一般不用此方法新建
     * @param name 模型名称-随便填
     * @param key 模型key-对应创建模型时填的key
     * @param desc 备注
     * @param modelType 模型分类字典,默认为0-bpmn
     * @return
     */
    @Override
    public org.flowable.ui.modeler.domain.Model create(String name, String key, String desc, Integer modelType) {
        try {
            ObjectNode editorNode = objectMapper.createObjectNode();
            editorNode.put("id", "canvas");
            editorNode.put("resourceId", "canvas");
            ObjectNode properties = objectMapper.createObjectNode();
            properties.put("process_author", "模型归属-随便填");
            properties.put("process_id", key);
            properties.put("name", name);
            editorNode.set("properties", properties);
            ObjectNode stencilset = objectMapper.createObjectNode();
            stencilset.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
            editorNode.set("stencilset", stencilset);


            org.flowable.ui.modeler.domain.Model model = new org.flowable.ui.modeler.domain.Model();
            model.setKey(key);
            model.setName(name);
            model.setCreatedBy("anonymousUser");
            model.setModelType(modelType);
            model.setVersion(Integer.parseInt(
            String.valueOf(modelRepository.findByKeyAndType(model.getKey(), modelType).size() + 1)));
            //model.setTenantId(String.valueOf(TenantContextHolder.getTenantId()));
            modelRepository.save(model);
            return model;
        } catch (Exception e) {
            MyModelServiceImpl.log.error("UnsupportedEncodingException", e);
        }
        return null;
    }
    
    /**
     * 分页获取流程
     *
     * @param params
     * @return
     */
    @Override
    public IPage<org.flowable.ui.modeler.domain.Model> getModelPage(Integer pageNumber,Integer pageSize) {

        List<org.flowable.ui.modeler.domain.Model> models = modelRepository.findByModelType(0, "nameAsc");
        IPage<org.flowable.ui.modeler.domain.Model> result = new Page<org.flowable.ui.modeler.domain.Model>(pageNumber, pageSize);
        result.setTotal(models.size());
        result.setRecords(models);
        return result;
    }
    
    /**
     * 删除流程
     *
     * @param id
     * @return
     */
    @Override
    public Boolean removeModelById(String id) {
        modelService.deleteModel(id);

        return Boolean.TRUE;
    }

    /**
     * 部署流程
     *
     * @param id
     * @return
     */
    @Override
    public Boolean deploy(String id) {
        try {
            // 获取模型
            org.flowable.ui.modeler.domain.Model modelData = modelService.getModel(id);
            BpmnModel model = modelService.getBpmnModel(modelData);
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
            String processName = modelData.getName() + BPMN20_XML;
            Deployment deploy = repositoryService.createDeployment().category(modelData.getDescription()).name(modelData.getName()).addString(processName, new String(bpmnBytes, "UTF-8")).deploy();
//			// 设置流程分类
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
                    .deploymentId(deploy.getId()).list();
            list.stream().forEach(processDefinition -> repositoryService
                    .setProcessDefinitionCategory(processDefinition.getId(), modelData.getName()));
        } catch (Exception e) {
            MyModelServiceImpl.log.error("部署失败,异常", e);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }
}

页面格式:
在这里插入图片描述

2.流程管理

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@AllArgsCons
tructor
public class ProcessServiceImpl implements ProcessService {

    private final RepositoryService repositoryService;

    private final RuntimeService runtimeService;

    /**
     * 分页流程列表
     *
     * @param params
     * @return
     */
    @Override
    public IPage<ProcessDefDTO> getProcessByPage(Integer pageNumber,Integer pageSize) {
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().latestVersion();
        IPage<ProcessDefDTO> result = new Page<ProcessDefDTO>(pageNumber, pageSize);
        result.setTotal(query.count());
        List<ProcessDefDTO> deploymentList = query.listPage((page - 1) * limit, limit).stream().map(processDefinition -> {
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(processDefinition.getDeploymentId()).singleResult();
            return ProcessDefDTO.toProcessDefDTO(processDefinition, deployment);
        }).collect(Collectors.toList());
        result.setRecords(deploymentList);
        return result;
    }

    /**
     * 读取xml/image资源-返回前端流程图文件
     *
     * @param procDefId
     * @param proInsId
     * @param resType
     * @return
     */
    @Override
    public InputStream readResource(String procDefId, String proInsId, String resType) {

        if (StrUtil.isBlank(procDefId)) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(proInsId).singleResult();
            procDefId = processInstance.getProcessDefinitionId();
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId).singleResult();

        String resourceName = "";
        if ("image".equals(resType)) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if ("xml".equals(resType)) {
            resourceName = processDefinition.getResourceName();
        }

        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        return resourceAsStream;
    }

    /**
     * 更新状态-控制流程生效或失效(默认status为active)
     *
     * @param status
     * @param procDefId
     * @return
     */
    @Override
    public Boolean updateStatus(String status, String procDefId) {
    //图片资源
        if ("active".equals(status)) {
            repositoryService.activateProcessDefinitionById(procDefId, true, null);
        } else if ("suspend".equals(status)) {
        //xml资源
            repositoryService.suspendProcessDefinitionById(procDefId, true, null);
        }
        return Boolean.TRUE;
    }

    /**
     * 删除部署的流程,级联删除流程实例
     *
     * @param deploymentId 流程id
     * @return
     */
    @Override
    public Boolean removeProcIns(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);
        return Boolean.TRUE;
    }
}

页面格式:
在这里插入图片描述
3.一些重要接口方法(如审核、发起、结办流程等)可以自行修改上面的方法,正式接口方法以后空了会放上来

  • 39
    点赞
  • 192
    收藏
    觉得还不错? 一键收藏
  • 23
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值