Activiti——将绘制的流程图存入act数据库并进行流程推进与状态流转

文章目录

  • 前言
  • [流程图入库操作 RepositoryService](#流程图入库操作 RepositoryService)
  • [流程启动 RuntimeService](#流程启动 RuntimeService)
  • [待处理任务查看 TaskService](#待处理任务查看 TaskService)
  • 流程状态的扭转
  • [查询流程定义信息 RepositoryService](#查询流程定义信息 RepositoryService)
  • [查询正在执行的流程实例 RuntimeService](#查询正在执行的流程实例 RuntimeService)
  • 已部署流程删除
  • [查询流程的历史记录信息 HistoryService](#查询流程的历史记录信息 HistoryService)

前言

之前的博客中,重点说明了activiti表的创建,以及第一张流程图的绘制。但是绘制的第一张流程图并非存在于对应的数据库中,今天需要做的就是将绘制的流程图,使用activiti的代码自动的填充进对应的数据表中。然后做一个简单的状态流转。

流程图入库操作 RepositoryService

项目结构

依旧是基于之前的项目依赖与结构。

数据库连接配置文件

其中数据库的连接配置文件activiti.cfg.xml,配置内容如下所示:

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

    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://xxxx:3306/activiti_02?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8&amp;nullCatalogMeansCurrent=true"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="root"/>
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>

   <!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://xxxx:3306/activiti?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=GMT%2B8" />
        <property name="username" value="root" />
        <property name="password" value="root" />
        <property name="maxActive" value="3" />
        <property name="maxIdle" value="1" />
    </bean>

    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="dataSource" ref="dataSource" />
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>-->

</beans>

配置文件中的一些注意事项在前面的博客中做了一些说明,此处不进行额外的讲解,有问题的参考之前的博客内容。

入库Java测试代码

编写一个测试类,其中的入库逻辑如下所示:

java 复制代码
// 单个任务的部署
@Test
public void addTable(){
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
    RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
    Deployment demo1 = repositoryService.createDeployment()
            .addClasspathResource("bpmn/demo01.bpmn") // 添加流程图
            .addClasspathResource("bpmn/demo01.png") // 对应的流程图片 支持 png|jpg|gif|svg
            .name("第一个流程测试用例")
            .deploy();
    System.out.println("流程部署id===》"+demo1.getId());
    System.out.println("流程部署name===》"+demo1.getName());
}

虽然都是使用addClasspathResource关联xml与png 图片,但是其中的底层原理则是根据文件后缀,自动去判断识别存入对应的数据表字段中。

查看控制台输出日志与数据库,可以发现能够将对应的流程图xml与流程图进行入库处理。

查看数据库是否存在对应的数据信息。

select * from ACT_RE_DEPLOYMENT;

select * from ACT_RE_PROCDEF;


上传后的文件内容存储表,可以通过查找act_ge_bytearray看见。

【注意点】

除了流程图的最初部署,会将流程图存入act_ge_bytearray表之外。

在流程审批的各个节点中,都能将对应的文件进行存储,方便后续进行文件的回显。

zip 方式进行流程的批量部署

将对应的 流程图 png 与 xml 汇总打包成一个zip文件,此时则需要注意 两个文件除了后缀不同之外,一定要保证前面的文件名称一致。

编写代码逻辑,将zip创建数据流,并使用activiti的DeploymentaddZipInputStream,将zip数据流解析并存入数据表中。

java 复制代码
/**
 * zip 包方式批量上传流程图与流程xml文件
 */
@Test
public void zipInsertTable(){
    // 获取zip文件流
    InputStream inputStream = this.getClass()
            .getClassLoader()
            .getResourceAsStream("bpmn/activitis.zip");
    ZipInputStream zipInputStream = new ZipInputStream(inputStream);

    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
    RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();

    Deployment deploy = repositoryService.createDeployment()
            .addZipInputStream(zipInputStream)
            .deploy();

    System.out.println("流程部署id===》"+deploy.getId());
    System.out.println("流程部署name===》"+deploy.getName());
}

代码执行后,观察控制台打印日志信息与数据表的查询。

select * from ACT_RE_PROCDEF;

流程的部署操作,是将文件 xml 、png 解析并存入到activiti对应的数据表的字段中。上述两种方式,分别就单一与批量进行了验证,通过查看执行的日志信息,可以发现其中受影响的表有以下几种:

  • act_re_deployment 流程定义部署表 。每执行一次部署,则会增加一条记录;若文件相同,则version字段递增。
  • act_re_procdef 流程定义表。部署每个新的流程定义都会在该表中新增记录。
  • act_ge_bytearray 流程资源表。 主要记录对应文件的 bytearray 信息,数据字段类型为 blob。

流程启动 RuntimeService

当对应的流程图的结构信息已经进行了入库操作,此时就可以编写代码进行流程的启动测试了。

启动流程的逻辑如下所示:

java 复制代码
/**
 * 流程启动  初次启动
 * 注意: 每次执行一次  就是以当前的流程图模板  创建了一个新的 activiti 的对象
 */
@Test
public void startFlow(){
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
    // 注意此处的 key  就是流程图的 id 
    ProcessInstance demo01 = runtimeService.startProcessInstanceByKey("demo01");
    System.out.println("流程定义 id===》"+demo01.getProcessDefinitionId());
    System.out.println("流程实例 id===》"+demo01.getId());
    System.out.println("当前活动 id===》"+demo01.getActivityId());
}

代码执行后查看控制台的相关日志输出。

【注意点】startProcessInstanceByKey("demo01") 指定的 key 必须与 流程图中的 id 一致

如果不一致,则会出现org.activiti.engine.ActivitiObjectNotFoundException: no processes deployed with key 'demoxxxx'的报错问题!!

【注意点】每次执行创建流程的代码,都会以工作流流程图作为模板,构建一个新的实例

在文件部署并填充至activiti数据表中后,执行上述的代码,则会根据已部署好的信息,构建一个新的流程实例。通过执行后的控制台日志,可以发现受影响的表有以下:

  • act_hi_actinst 流程实例执行历史
  • act_hi_identitylink 流程的参与用户历史信息
  • act_hi_procinst 流程实例历史信息
  • act_hi_taskinst 流程任务历史信息
  • act_ru_execution 流程执行信息
  • act_ru_identitylink 流程的参与用户信息
  • act_ru_task 任务信息

待处理任务查看 TaskService

流程启动,在有些项目中的首页,需要展示当前登录者的待处理任务列表信息。

进行了启动工作流操作,此时的工作流中的节点流转,如下:

此时可以通过TaskService查看,下面是一个简单的例子。

java 复制代码
/**
 * 查询当前个人待执行的任务
 */
@Test
public void viewReDoTask(){
    // 工作流从启动开始  就会流转至 第一个 UserTask 节点,
    // 此时可以通过配置的 Assignee 查询指定的人的  一些待处理 任务信息
    String assignee = "worker";
    // 数据库连接配置操作
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
    // 根据对应的流程 查询当前指定人的待处理任务信息
    TaskService taskService = defaultProcessEngine.getTaskService();
    List<Task> list = taskService.createTaskQuery()
            .processDefinitionKey("demo01") // 指定哪个流程图模板
            .taskAssignee(assignee) // 指定是谁负责待处理的任务
            .list();
    if(!CollectionUtil.isEmpty(list)){
        list.forEach(x->{
            System.out.println("流程实例 id "+x.getProcessInstanceId());
            System.out.println("任务 id "+x.getId());
            System.out.println("任务负责人 "+x.getAssignee());
            System.out.println("任务名称"+x.getName());
            System.out.println("===========================================");
        });
    }
}

测试代码运行后,在控制台中可以看到以下的信息。

这里有两个流程实例,是因为为了验证启动流程是否同实例对象,执行了两次开启流程的代码逻辑。

流程状态的扭转

work节点中,上面进行了待处理任务的查看操作,如果用户进行了处理,需要将状态进行向下扭转,如何扭转任务的节点,接下来看下面的代码逻辑。

java 复制代码
/**
 * 工作流的节点与状态的扭转
 */
@Test
public void doTask(){
    // 获取数据库的连接信息
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = defaultProcessEngine.getTaskService();
    
    // 完成当前节点的任务  并向下推进
    String taskId = "7505";
    taskService.complete(taskId);
}

当任务进行推进操作后,再执行查询当前登录人员需要待处理的任务节点,那么任务id为 7505的相关任务不能查询到。查询一下。

任务编号为7505的已经成功推进!

查询流程定义信息 RepositoryService

再上面的startFlow()执行后,就会依据对应的工作流模板,创建一个工作流流程实例。查看指定的流程模板下已创建的相关流程实例信息,可以使用下面的代码逻辑实现。

java 复制代码
/**
 * 查询流程定义 的一些内容
 */
@Test
public void queryProcessDefinition(){
    // 获取数据库的连接信息
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();

    RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
    ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();

    // 指定需要查询哪个流程模板信息
    String flowId = "demo01";
    List<ProcessDefinition> list = processDefinitionQuery
            .processDefinitionKey(flowId) // 指定是哪个流程模板
            .orderByProcessDefinitionVersion() // 排序字段
            .desc()
            .list();
    if(!CollectionUtil.isEmpty(list)){
        list.forEach(process->{
            System.out.println("流程定义 id "+process.getId());
            System.out.println("流程定义 name "+process.getName());
            System.out.println("流程定义 key "+process.getKey());
            System.out.println("流程定义 version "+process.getVersion());
            System.out.println("流程部署 id "+process.getDeploymentId());
            System.out.println("=============================");
        });
    }
}

查看控制台执行后的日志信息,如下所示:

查询正在执行的流程实例 RuntimeService

如果需要查询当前指定的流程模板,存在哪些流程实例处于流程中的状态,以及对应的流程实例的信息,以达到随时跟踪任务的执行情况。

可以使用如下方式进行查看:

java 复制代码
/**
 * 查询指定工作流模板中,哪些实例正在执行流程中
 */
@Test
public void queryDoProcessInstance(){
    // 指定工作流流程模板id
    String flowId= "demo01";
    // 获取数据库的连接信息
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();

    RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
    List<ProcessInstance> list = runtimeService.createProcessInstanceQuery()
            .processDefinitionKey(flowId)
            .list();
    if(!CollectionUtil.isEmpty(list)){
        list.forEach(processInstance->{
            System.out.println("流程实例 id "+processInstance.getProcessInstanceId());
            System.out.println("所属流程定义 id "+processInstance.getProcessDefinitionId());
            System.out.println("是否执行完成 "+processInstance.isEnded());
            System.out.println("是否暂停 "+processInstance.isSuspended());
            System.out.println("当前活动标识 "+processInstance.getActivityId());
            System.out.println("业务关键字 "+processInstance.getBusinessKey());
            System.out.println("=============================");
        });
    }
}

已部署流程删除

有些已经部署后的流程,需要进行删除操作,则可以使用下列的逻辑实现。

这里说的已部署,是指已经执行了addTable()做了流程文件的上传操作。

java 复制代码
/**
 * 删除 已部署 的流程实例。
 * 注意:如果当前流程实例并未执行完成,进行删除时会出现报错。
 */
@Test
public void deleteDeployment(){
    // 流程部署id
    String deployMentId = "5001";
    // 获取数据库的连接信息
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
    RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
    // 当该已部署的流程,存在流程中的实例时,执行当前流程会报错!
    repositoryService.deleteDeployment(deployMentId);
    // 如果需要进行强制删除  则可以采取下列的方式进行
    //repositoryService.deleteDeployment(deployMentId,true);
}

如果当前已部署的流程存在流程中的实例时,执行删除操作会出现下面的报错信息。

查询流程的历史记录信息 HistoryService

在一般的审批详情列表中,通常需要展示一些已审批处理节点的记录信息,此时则可以使用 HistoryService 查询指定流程实例下的各个已处理的审批节点数据记录。

当然了,测试操作需要指定指定工作流模板的流程id,先查询可以测试的流程编号。

java 复制代码
/**
 * 查询指定工作流模板中,哪些实例正在执行流程中
 */
@Test
public void queryDoProcessInstance(){
    // 指定工作流流程模板id
    String flowId= "demo01";
    // 获取数据库的连接信息
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();

    RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
    List<ProcessInstance> list = runtimeService.createProcessInstanceQuery()
            .processDefinitionKey(flowId)
            .list();
    if(!CollectionUtil.isEmpty(list)){
        list.forEach(processInstance->{
            System.out.println("流程实例 id "+processInstance.getProcessInstanceId());
            System.out.println("所属流程定义 id "+processInstance.getProcessDefinitionId());
            System.out.println("是否执行完成 "+processInstance.isEnded());
            System.out.println("是否暂停 "+processInstance.isSuspended());
            System.out.println("当前活动标识 "+processInstance.getActivityId());
            System.out.println("业务关键字 "+processInstance.getBusinessKey());
            System.out.println("=============================");
        });
    }
}

使用对应的流程实例id,这里使用10001

使用下列代码进行历史数据的检索:

java 复制代码
/**
 * 查询 历史信息
 */
@Test
public void queryHistoryInfo(){
    String processInstanceId = "10001";
    ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
    HistoryService historyService = defaultProcessEngine.getHistoryService();
    HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
    historicActivityInstanceQuery.processInstanceId(processInstanceId);// 流程实例id
    historicActivityInstanceQuery.orderByHistoricActivityInstanceStartTime().asc();
    List<HistoricActivityInstance> list = historicActivityInstanceQuery.list();
    if(!CollectionUtil.isEmpty(list)){
        list.forEach(hi->{
            System.out.println(hi.getActivityId());
            System.out.println(hi.getActivityName());
            System.out.println(hi.getProcessDefinitionId());
            System.out.println(hi.getProcessInstanceId());
            System.out.println("=============================");
        });
    }
}

执行成功后在控制台的日志中可以看到下列信息:

相关推荐
likangbinlxa12 小时前
【Oracle11g SQL详解】UPDATE 和 DELETE 操作的正确使用
数据库·sql
HashFlag13 小时前
Typora绘制-流程图
流程图·typora
r i c k13 小时前
数据库系统学习笔记
数据库·笔记·学习
野犬寒鸦13 小时前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
IvorySQL14 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·14 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德14 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫15 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i15 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.15 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql