FLOWABLE打印底层sql

一.简介

建议在 Spring Boot 的 application.properties 中添加如下配置,开启 flowable 日志,一定要是debug 模式:

logging.level.org.flowable=debug

yml文件写法:

logging:
 	# config: classpath:logconfig/logback.xml
  level:
    root: info
    com.alibaba.nacos.client.naming: WARN 
    com.alibaba.nacos.client.config.impl: WARN
    # 增加flowable 底层打印sql
    org.flowable: debug 
    
设置完这个控制台会一直打印 -- 待解决
2023-11-24 13:28:05,076 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.common.engine.impl.interceptor.LogInterceptor - --- starting AcquireTimerJobsCmd --------------------------------------------------------
2023-11-24 13:28:05,076 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.common.spring.SpringTransactionInterceptor - Running command with propagation REQUIRED
2023-11-24 13:28:05,083 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.common.engine.impl.agenda.AbstractAgenda - Operation class org.flowable.engine.impl.interceptor.CommandInvoker$1 added to agenda
2023-11-24 13:28:05,083 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.engine.impl.interceptor.CommandInvoker - Executing operation class org.flowable.engine.impl.interceptor.CommandInvoker$1
2023-11-24 13:28:05,083 [flowable-bpmn-acquire-timer-jobs] DEBUG o.f.j.s.impl.persistence.entity.TimerJobEntityImpl.selectTimerJobsToExecute - ==>  Preparing: SELECT RES.* from ACT_RU_TIMER_JOB RES where SCOPE_TYPE_ is null and DUEDATE_ <= ? and LOCK_OWNER_ is null order by RES.ID_ asc LIMIT ? OFFSET ?
2023-11-24 13:28:05,084 [flowable-bpmn-acquire-timer-jobs] DEBUG o.f.j.s.impl.persistence.entity.TimerJobEntityImpl.selectTimerJobsToExecute - ==> Parameters: 2023-11-24 13:28:05.083(Timestamp), 512(Integer), 0(Integer)
2023-11-24 13:28:05,093 [flowable-bpmn-acquire-timer-jobs] DEBUG o.f.j.s.impl.persistence.entity.TimerJobEntityImpl.selectTimerJobsToExecute - <==      Total: 0
2023-11-24 13:28:05,093 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.common.engine.impl.db.DbSqlSession - Flushing dbSqlSession
2023-11-24 13:28:05,093 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.common.engine.impl.db.DbSqlSession - flush summary: 0 insert, 0 update, 0 delete.
2023-11-24 13:28:05,093 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.common.engine.impl.db.DbSqlSession - now executing flush...
2023-11-24 13:28:05,105 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.common.engine.impl.interceptor.LogInterceptor - --- AcquireTimerJobsCmd finished --------------------------------------------------------
2023-11-24 13:28:05,105 [flowable-bpmn-acquire-timer-jobs] DEBUG org.flowable.job.service.impl.asyncexecutor.AcquireTimerJobsRunnable - timer job acquisition thread for engine bpmn sleeping for 10000 millis
FindExpiredJobsCmd finished --------------------------------------------------------

在这里插入图片描述

flowable一直查询定时任务act_ru_timer_job问题 ,解决方式:
async-executor-activate: false # 设置这个就不打印


flowable:
  # 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
  # 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
  # 3. create_drop: 启动时自动创建表,关闭时自动删除表
  # 4. drop_create: 启动时,删除旧表,再创建新表
  database-schema-update: false # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化
  db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置
  check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程
  history-level: full # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
  async-executor-activate: false # 设置这个就不打印

在这里插入图片描述

flowable:
  async-executor-activate: false
  dmn:
    enabled: false
  idm:
    enabled: false
    ldap:
      enabled: false
  form:
    enabled: false
  database-schema-update: true
  history-level: full
management:
  endpoint:
    flowable:
      enabled: false

参考链接:https://www.cnblogs.com/zhaodefu/p/17717921.html

这个配置表示开启 flowable 的日志,开启日志的好处是可以看到底层的 SQL语句。

二.查询部署信息

例如查询流程的部署信息,代码如下:

@Test
void test01() throws IOException {
    List<Deployment> list = repositoryService.createDeploymentQuery().list();
    for (Deployment deployment : list) {
        logger.info("id:{};key:{}", deployment.getId(), deployment.getKey());
    }
}

创建一个查询器,然后返回所有的流程部署信息并打印出来,此时看下 IDEA 控制台打印出来的 SQL 信息:
在这里插入图片描述
可以看到,底层执行的 SQL 其实就是去查询 ACT_RE_DEPLOYMENT 表。
在这里插入图片描述
从上图中可以看到,利用流程部署的名字、分类、ID 等各种信息去查询,可以精确匹配也可以模糊匹配

例如:查询 key 为 javaboy的工作流key 的流程部署文件,但是这个流程我之前部署过多次(版本升级),现在我想查询最近一次的流程部署信息,代码如下:

@Test
void test01() throws IOException {
    Deployment deployment = repositoryService.createDeploymentQuery().deploymentKey("javaboy的工作流key").latest().singleResult();
    logger.info("id:{};key:{}", deployment.getId(), deployment.getKey());
}

看下控制台打印出来的 SQL:

--- [           main] i.p.e.D.selectDeploymentsByQueryCriteria : ==>  Preparing: SELECT RES.* from ACT_RE_DEPLOYMENT RES WHERE RES.KEY_ = ? and RES.DEPLOY_TIME_ = (select max(DEPLOY_TIME_) from ACT_RE_DEPLOYMENT where KEY_ = RES.KEY_ and DERIVED_FROM_ is null and ( (TENANT_ID_ IS NOT NULL and TENANT_ID_ = RES.TENANT_ID_) or (TENANT_ID_ IS NULL and RES.TENANT_ID_ IS NULL) ) ) order by RES.ID_ asc
--- [           main] i.p.e.D.selectDeploymentsByQueryCriteria : ==> Parameters: javaboy的工作流key(String)
--- [           main] i.p.e.D.selectDeploymentsByQueryCriteria : <==      Total: 1

这个 SQL 写的有点复杂,但是仔细看就一个意思,给定查询的 key 是 javaboy的工作流key,查询时间是一个最大的时间,这就很好懂了。

三.查询流程定义信息

查询所有的流程定义信息,代码如下:

@Test
void test02() {
    List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
    for (ProcessDefinition pd : list) {
        logger.info("id:{};key:{};version:{};",pd.getId(),pd.getKey(),pd.getVersion());
    }
}

看下控制台打印的 SQL:
在这里插入图片描述
可以看到,就是去流程定义表 ACT_RE_PROCDEF 去查看所有。

基于此,其他的查询 API 就都好理解了,例如:根据流程定义的 KEY 去查询所有的流程定义,这个 KEY 其实就是流程定义 XML 文件中的 id:

@Test
void test02() {
    List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("javaboy_submit_an_expense_account").list();
    for (ProcessDefinition pd : list) {
        logger.info("id:{};key:{};version:{};",pd.getId(),pd.getKey(),pd.getVersion());
    }
}

看下控制台打印的 SQL:
在这里插入图片描述
其他的查询 API 也是类似的。

四.原生查询

我们也可以自己写 SQL 查询。

跟前面一样,例如我想查询 key 为 javaboy的工作流key 的流程部署文件,但是这个流程我之前部署过多次(版本升级),现在我想查询最近一次的流程部署信息,查询代码如下:

@Test
void test03() {
    Deployment deployment = repositoryService.createNativeDeploymentQuery().sql("SELECT RES.* from ACT_RE_DEPLOYMENT RES WHERE RES.KEY_ = #{key} and RES.DEPLOY_TIME_ = (select max(DEPLOY_TIME_) from ACT_RE_DEPLOYMENT where KEY_ = RES.KEY_) order by RES.ID_ asc").parameter("key", "javaboy的工作流key").singleResult();
    logger.info("id:{};key:{}", deployment.getId(), deployment.getKey());
}
``
自己写 SQL 即可,参数的占位符用 #,因为这里底层的 SQL 操作实际上是 MyBatis。

相同的道理,我想根据流程定义的 KEY 去查询流程定义信息,使用原生查询方式,代码如下:

@Test
void test04() {
List list = repositoryService.createNativeProcessDefinitionQuery()
.sql(“SELECT RES.* from ACT_RE_PROCDEF RES WHERE RES.KEY_ = #{key} order by RES.ID_ asc”)
.parameter(“key”, “javaboy_submit_an_expense_account”).list();
for (ProcessDefinition pd : list) {
logger.info(“id:{};key:{};version:{};”, pd.getId(), pd.getKey(), pd.getVersion());
}
}

以上转载:https://blog.youkuaiyun.com/weixin_40991408/article/details/130375294
第二篇参考文章

一.简介

有一个常见的场景是,查询执行过的流程信息。对于正在执行的流程,会在 ACT_RU_EXECUTION 表中保存一条对应的记录,不过流程执行结束之后,ACT_RU_EXECUTION 表中的记录会被删除掉,此时要是想查询已经执行过的流程信息,去哪里查询呢?这就要了解下 HistoryService 的使用方法。

流程图如下:
在这里插入图片描述

二.查询历史流程

查询历史流程的代码如下:

@SpringBootTest
public class HiTest {

    @Autowired
    HistoryService historyService;
    private static final Logger logger = LoggerFactory.getLogger(HiTest.class);

    @Test
    void test01() {
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().list();
        for (HistoricProcessInstance hi : list) {
            logger.info("==={},{},{},{},{},{}",hi.getId(),hi.getName(),hi.getStartActivityId(),hi.getStartTime(),hi.getEndActivityId(),hi.getEndTime());
        }
    }
}

由于项目使用的框架是 Spring Boot ,所以基本上不需要额外的配置,直接注入 HistoryService 实例即可。

test01() 方法中的代码就是查询出来目前所有的流程实例,包括正在执行的和已经执行完毕的都可以查询到。查询之后,控制台打印结果如下:

==>  Preparing: SELECT RES.* , DEF.KEY_ as PROC_DEF_KEY_, DEF.NAME_ as PROC_DEF_NAME_, DEF.VERSION_ as PROC_DEF_VERSION_, DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_ from ACT_HI_PROCINST RES left outer join ACT_RE_PROCDEF DEF on RES.PROC_DEF_ID_ = DEF.ID_ order by RES.ID_ asc
==> Parameters: 
<==      Total: 1
Flushing dbSqlSession
flush summary: 0 insert, 0 update, 0 delete.
now executing flush...
--- HistoricProcessInstanceQueryImpl finished --------------------------------------------------------
===a3786614-38eb-11ed-afc8-acde48001122,null,startEvent1,Tue Sep 20 21:53:42 CST 2022,null,null

首先可以看到,这里查询的 SQL,查询的表是 ACT_HI_PROCINST,截图如下:
在这里插入图片描述
这张表中记录了流程实例 ID,流程定义 ID,流程开始的时间,流程结束的时间,流程执行耗时,流程开始的节点,流程发起人,流程结束的节点等等。

同时大家也可以看到,在打印出来的查询结果中,getEndActivityId 和 getEndTime 这两个字段的值都为 null,这就说明这个流程目前还在执行中,还没执行完毕,当一个流程执行完毕的时候,这两个字段就不会为 null 了,我们可以根据这个特点去判断一个流程是否执行完毕。

现将流程这个流程中的任务都执行完毕,执行完毕之后,ACT_RU_EXECUTION 表中关于流程的记录就会被删除掉,也就是执行如下代码现在是查询不到上面这个流程了:

@Autowired
RuntimeService runtimeService;
@Test
void test02() {
    ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId("a3786614-38eb-11ed-afc8-acde48001122").singleResult();
    logger.info("pi:{}",pi);
}

这个方法执行返回的 pi 对象现在为 null。

现在我们想查询刚刚执行的流程,得去查询历史流程,也就是去 ACT_HI_PROCINST 表中去查询,执行我们最上面那个方法:

@Test
void test01() {
    List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().list();
    for (HistoricProcessInstance hi : list) {
        logger.info("==={},{},{},{},{},{}",hi.getId(),hi.getName(),hi.getStartActivityId(),hi.getStartTime(),hi.getEndActivityId(),hi.getEndTime());
    }
}

现在去查询,返回的结果中就可以看到 EndActivityId 以及 EndTime 这两个字段了,因为流程已经执行结束了。

在流程执行的过程中,ACT_HI_PROCINST 表中的流程记录和 ACT_RU_EXECUTION 表中的流程记录是一一对应的。

三.查询历史活动

一个流程中的每一个节点都是一个活动,当一个流程执行结束的时候,如果我们还想查看每一个活动执行的细节,就得通过查询历史活动来实现了。
在查询之前,先来了解下跟历史活动相关的两张表。

1.ACT_HI_ACTINST

这张表中保存**一个流程中的所有活动细节。包括流程的启动节点、结束节点、各种 Task 甚至节点之间的连线,**可以看下这张表中的信息:在这里插入图片描述

这刚好是一个流程的完整记录,从上往下,分别是:
①启动流程。
②连线
③提交请假申请这个 UserTask
④连线
⑤主管审批这个 UserTask
⑥连线
⑦经理审批这个 UserTask
⑧连线
⑨结束

2.ACT_HI_TASKINST

这个表只记录下来了 Task 的信息,截图如下:
在这里插入图片描述
这张表记录下来了每一个 Task 的具体信息包括这个 Task 是由谁处理的,Task 的名字,开始时间、结束时间、耗时等信息。

针对这两张表,也有不同的查询方式。

首先来看查询历史活动:

@Test
void test03() {
    List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().orderByHistoricActivityInstanceStartTime().asc().list();
    for (HistoricActivityInstance hai : list) {
        logger.info("流程ID:{},活动名称:{},活动ID:{},活动处理人:{}",hai.getProcessInstanceId(),hai.getActivityName(),hai.getActivityId(),hai.getAssignee());
    }
}

查询结果如下图:
在这里插入图片描述
由于只执行了一个流程,所以这里就只显示了一个流程的所有活动,大家看打印出来的流程 ID 都是一样的。

再来看查询历史 Task:

@Test
void test04() {
    List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().orderByHistoricTaskInstanceStartTime().asc().list();
    for (HistoricTaskInstance hti : list) {
        logger.info("流程ID:{},Task 开始时间:{},Task 结束时间:{},Task 处理人:{}",hti.getProcessInstanceId(),hti.getCreateTime(),hti.getEndTime(),hti.getAssignee());
    }
}

在这里插入图片描述

以上转载:https://blog.youkuaiyun.com/weixin_40991408/article/details/130415623

第三篇文章

事情是这个样子的,在学习flowable的时候,粗略的看了一遍用户手册,是的还蛮清晰的,但是总觉得少了点什么,仔细想了想,方法调用是很简单,但总觉得不了解内部机制,而了解内部运行机制最简单的方法就是数据库表,的确无论是官网还是博客资料均对flowable数据库表进行了详细的说明,细化到了每个表的字段,但是呢?还是不够,我想要了解的是运行机制,比如我部署的时候,内部到底执行了什么,执行的sql语句是什么,操作了哪些表哪些字段等等,带着这种疑问,尝试了起来。
我的运行环境是springboot+flowable,这里我把代码都一起贴上来。

核心

logging.level.org.flowable.engine.impl.persistence.entity.*=debug
logging.level.org.flowable.task.service.impl.persistence.entity.*=debug

pom文件

<?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.6.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>flowable</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>flowable</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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.flowable/flowable-spring-boot-starter -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.4.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>

    </dependencies>

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

</project>

配置文件 application.properties

注意最后两句就是可以让flowable相关的执行可以带引出sql语句的配置

spring.datasource.url=jdbc:mysql://??????/flowable-spring-boot?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456

logging.level.org.flowable.engine.impl.persistence.entity.*=debug
logging.level.org.flowable.task.service.impl.persistence.entity.*=debug

如果是.yaml文件,是这样的:

logging:
  level:
    org.flowable.engine.impl.persistence.entity.*: debug
    org.flowable.task.service.impl.persistence.entity.*: debug

FlowableApplication

package com.example.flowable;

import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class FlowableApplication {

    public static void main(String[] args) {
        SpringApplication.run(FlowableApplication.class, args);
    }
    
//    启动时自动执行
    @Bean
    public CommandLineRunner init(final RepositoryService repositoryService,
                                  final RuntimeService runtimeService,
                                  final TaskService taskService) {

        return new CommandLineRunner() {
            @Override
            public void run(String... strings) throws Exception {
                System.out.println("Number of process definitions : "
                        + repositoryService.createProcessDefinitionQuery().count());
                System.out.println("Number of tasks : " + taskService.createTaskQuery().count());
                runtimeService.startProcessInstanceByKey("oneTaskProcess"); //启动自动部署,这里是开启实例
                System.out.println("Number of tasks after process start: "
                        + taskService.createTaskQuery().count());
            }
        };
    }
}

MyRestController

package com.example.flowable.controller;

import com.example.flowable.service.MyService;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

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

@RestController
public class MyRestController {

    @Autowired
    private MyService myService;

    @RequestMapping(value="/process", method= RequestMethod.POST)
    public void startProcessInstance() {
        myService.startProcess();
    }

    @RequestMapping(value="/tasks", method= RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE)
    public List<TaskRepresentation> getTasks(@RequestParam String assignee) {
        List<Task> tasks = myService.getTasks(assignee);
        List<TaskRepresentation> dtos = new ArrayList<TaskRepresentation>();
        for (Task task : tasks) {
            dtos.add(new TaskRepresentation(task.getId(), task.getName()));
        }
        return dtos;
    }

    @GetMapping(value="/getDeploment")
    public void getDeploment() {
        myService.getDeploment();
        System.out.println();
    }

    static class TaskRepresentation {

        private String id;
        private String name;

        public TaskRepresentation(String id, String name) {
            this.id = id;
            this.name = name;
        }

        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }

    }

}

MyService

package com.example.flowable.service;

import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class MyService {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private TaskService taskService;

    @Transactional
    public void startProcess() {
        runtimeService.startProcessInstanceByKey("oneTaskProcess");
    }

    @Transactional
    public List<Task> getTasks(String assignee) {
        return taskService.createTaskQuery().taskAssignee(assignee).list();
    }

    @Transactional
    public void getDeploment() {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId("f8046347-ab16-11ec-a6c0-3c9c0f202230").singleResult();

        System.out.println(processDefinition.getId());
    }

}

然后启动后就会打印一堆日志

在这里插入图片描述
当我调用某个关于flowable的某个方法时,也会有sql语句输出,这样就知道内部机制了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
那么之后,你想知道什么,就可以用这种方式去调试看内部sql机制了。
以上转载至https://blog.youkuaiyun.com/weixin_44663675/article/details/123705018

第四篇sql 如何使用

背景

流程任务流转过程中,各环节的处理,会填写处理意见。
Camunda自带了相关的功能,但功能过于简陋,问题较多,今天来说说这一块。

自带功能的问题

如使用Camunda官方自身的办理意见相关功能,会遇到两个问题:
1.因为指定环节跳转,使用了cancelActivityInstance,所以存在部分历史任务状态为deleted,需要过滤掉该部分数据
2.任务转办,需要查询并显示状态为未结束的任务环节相关审批意见(任务转办只重新设置审批人,不会重新生成1条新的任务)

使用官方自带的api,如下,始终无法拼接生成有效的sql语句

    HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery();
    query.processInstanceId(id).taskDeleteReason(WorkFlowConstant.TASK_DELETE_REASON_COMPLETE)
    .or().taskDeleteReason(null).endOr().orderByHistoricActivityInstanceStartTime().asc();

生成的sql语句如下:

==>  Preparing: select distinct RES.* from ACT_HI_TASKINST RES WHERE ( 1 = 1 and RES.PROC_INST_ID_ = ? and RES.DELETE_REASON_ = ? ) and ( 1 = 1 ) order by RES.START_TIME_ asc LIMIT ? OFFSET ? 
==> Parameters: a1624965-e050-11ea-a4ec-8c1645df7f9d(String), completed(String), 2147483647(Integer), 0(Integer)

即api无法支持查询null值,推测碰到null值会自动忽略

转而采用native查询来实现

  NativeHistoricTaskInstanceQuery query =historyService.createNativeHistoricTaskInstanceQuery()
            .sql("select * from act_hi_taskinst where PROC_INST_ID_=#{processInstanceId} " +
                    "and  (delete_reason_=#{deleteReason} or delete_reason_ is null)  order by start_time_ ")
            .parameter("processInstanceId",id)
            .parameter("deleteReason",WorkFlowConstant.TASK_DELETE_REASON_COMPLETE);

以上代码能获取到期望的审批历史,但是……仍然存在问题:
1.任务转办后,查出任务历史直接是被转办人,例如张三转办给李四,在李四处理前,显示该环节为李四,但审批意见是张三写的,看上去很诡异
2.任务转办处理后,显示任务是李四处理的,张三和李四的审批意见做了拼接,依旧很诡异,看上去张三仍然在流程处理过程中但什么也没做,实际原本流程处理人就是他,并由他进行了任务转办。

任务委派也出现复用任务的情况,导致流程处理过程不清晰,不显示某些实际参与流程处理的人,以及合并显示审批意见的情况出现。

基于以上几点,还是自己实现审批意见表比较合适。

自行设计与实现

实体配置

image.png
实体属性
image.png

常用处理意见

通过平台数据字典功能,配置常用的处理意见,用于快速填充
image.png

数据写入

在任务处理等环节,弹出对话框,由用户录入办理意见或通过常用处理意见下拉列表快速填充
image.png
任务处理时,调用办理意见的服务接口,写入数据,以任务提交为例:

 /**
    * 新增处理意见
    * @param processInstanceId 流程实例标识
    * @param nodeId 环节标识
    * @param nodeName 环节名称
    * @param comment 处理意见
    * @param commitType 提交类型
    */
   void addComment(String processInstanceId,String nodeId,String nodeName, String comment, CommitTypeEnum commitType);


  @Override
    public void addComment(String processInstanceId,String nodeId,String nodeName, String comment, CommitTypeEnum commitType) {
        WorkflowComment entity=init();
        entity.setProcessInstanceId(processInstanceId);
        entity.setNodeId(nodeId);
        entity.setNodeName(nodeName);
        entity.setComment(comment);
        entity.setCommitType(commitType.name());
        add(entity);
    }

流转历史

在流程流转期间以及流程结束的查看页面,通过办理意见表,可以直观地看到该流程实例的流转和处理情况,如下图所示:
image.png
前端封装了一个通用组件

<template>
  <el-card>
    <template #header>
      <span>历史</span>
    </template>
    <el-timeline>
      <el-timeline-item v-for="(step, index) in steps" :key="index" type="success" size="large">
        <div style="font-weight: bold">{{ step.nodeName }}</div>
        <div>{{ step.assigneeName }}</div>
        <div>{{ step.commitTypeName }}</div>
        <div :title="step.comment">{{ abbreviate(step.comment, 20) }}</div>
        <div>{{ $dateFormatter.formatUTCTime(step.commitTime) }}</div>
      </el-timeline-item>
    </el-timeline>
  </el-card>
</template>
<script>
import { abbreviate } from '@/utils'
export default {
  data() {
    return {
      // 历史信息
      steps: [],
      queryParams: {
        processInstanceId: '',
        sortInfo: {
          sort_field: 'commitTime',
          sort_sortType: 'ascending'
        }
      }
    }
  },
  methods: {
    abbreviate,
    view(processInstanceId) {
      this.queryParams.processInstanceId = processInstanceId
      this.$api.workflow.workflowComment.list(this.queryParams).then((res) => {
        this.steps = res.data
      })
    }
  }
}
</script>

<style scoped></style>

调用的后端方法就是标准的获取办理意见的列表,按提交时间升序排列即可。

此外,流程中可能存在上传附件的需求,跟办理意见类似,Camunda也有这方面功能支持,同样支持的不好,我们平台的表单自身就能处理附件,不使用Camunda的附件相关功能。

开发平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
开源不易,欢迎收藏、点赞、评论。

转载至 https://blog.youkuaiyun.com/seawaving/article/details/133981217

如何转载别人的csdn
https://blog.youkuaiyun.com/weixin_45190065/article/details/129622677
https://blog.youkuaiyun.com/qq_44999561/article/details/129602541?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-129602541-blog-109611915.235v38pc_relevant_default_base3&spm=1001.2101.3001.4242.1&utm_relevant_index=3
https://blog.youkuaiyun.com/weixin_52322842/article/details/129652955
工具网址
http://www.atoolbox.net/Tool.php?Id=715

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值