SpringBoot整合Flowable【06】- 查询历史数据

一、引子

在05篇中我们学习了如何使用流程变量,如果业务需求为固定的内容 ,其实流程变量就可以当"表单"使用的,K-V的结构几乎天然支持了这部分。但是,如果你的业务需求需要用户灵活定制流程变量,这里用文字说明可能有的同学不太理解,具体案例大家可以去参考钉钉的工作流 ,用户可以在每个节点自己定义和管理变量,这种场景流程变量就无法很好地支持了,就需要用到Flowable提供的表单来操作了。

上述的内容其实是对流程变量的一个小结,可以让大家对流程变量 有更好的理解,也是我们后续内容的一个预告。说回正题,前面我们多次提到了历史这个词,历史流程、历史变量等等,在实际的业务场景中,我们当然会有查询历史记录的需求,所以在06篇中我们来学习下怎么查询历史数据。

二、认识"历史"

在前面的文章中,提到了历史流程历史变量这两个概念。"历史"这一设计可以很好地实现我们实际业务场景中的一些需求,比如我在上篇中提到的想要查看某个人的历史绩效数据。另外,以流程变量为例,它在整个流程以及各节点中都是随时可变的,不是"最终结果",而一旦随着流程或任务结束进入历史表的变量将无法再改变,也是一种"最终结果"。

三、实操

这里我们依然使用前面的绩效流程,明确一下节点为自评->上级评->隔级评,包含两个全局流程变量,分别为分数和绩效所属人。完成任务接口发生了一点小变化,就是查询变成了根据流程定义ID查询当前流程所处的任务节点,具体看代码:

java 复制代码
/**
 * 查询流程的待办任务.
 *
 * @param procDefId 流程定义ID
 * @return 待办任务id
 */
private String findUserAgentTask(String procDefId) {
    //1.查询流程的待办任务
    Task agentTask = taskService.createTaskQuery()
            .processDefinitionId(procDefId)
            .singleResult();
    //2.返回待办任务的id
    return agentTask.getId();
}

除了修改这里的查询逻辑,我们还需要做一步处理,还要额外声明个全局流程变量来表示这个绩效流程的所属人,毕竟在实际场景中,一个绩效肯定会属于一个人嘛,也是让我们在查询历史流程时有查询依据,这里我为了方便,就直接写死个值,实际场景中前端传也好,后端获取也好,设置成你们自己系统里的用户唯一标识即可(Ps:这应该大多数都是userId吧!),代码如下:

java 复制代码
@PostMapping("/start/{processId}")
public ResponseEntity<Object> startProcessDef(@PathVariable String processId) {
    //1.启动流程
    ProcessInstance processInstance = runtimeService.startProcessInstanceById(processId);
    //2.维护该流程的分数变量
    runtimeService.setVariable(processInstance.getId(), "score", 0);
    //3.维护该流程的所属用户
    runtimeService.setVariable(processInstance.getId(),"ownerId", 88);
    //4.返回流程实例对象id
    return new ResponseEntity<>(new BaseResponse<>(processInstance.getId()), HttpStatus.OK);
}

1.启动流程

完成了上述改造之后,我们启动三个属于用户id为88的流程,这时在act_re_procdef 表中就可以看到这三条流程定义: act_ru_task 表中也可以看到三个流程的三个节点都开始自评了: 以及在act_ru_variable 表中可以看到六个流程变量:

2.执行流程

既然三个流程都正常启动了,我们现在把它们全部都完成,接下来就可以到act_hi_procinst历史流程表 里看到这三条记录: act_hi_varinst历史变量表 就可以查到那三个历史流程关联的六条历史变量:

3.查询历史流程

到目前为止,准备工作就做好了,我们再来回忆下场景:查询某个人的历史绩效,以我们这里的数据为例,就是查询用户id为88的历史流程,代码如下:

java 复制代码
/**
 * 查询指定用户的历史绩效.
 *
 * @param userId 用户id
 * @return 历史流程集合
 */
@PostMapping("/history-process-def/userId/{userId}")
public ResponseEntity<Object> getHistoryProcessDef(@PathVariable Long userId) {
    //1.根据用户id查询历史流程
    List<HistoricVariableInstance> historicVariableInstanceList = historyService
            .createHistoricVariableInstanceQuery()
            .variableValueEquals("ownerId", userId)
            .list();
    //2.抽出流程插入ID
    Set<String> processInstanceIds = historicVariableInstanceList.stream()
            .map(HistoricVariableInstance::getProcessInstanceId)
            .collect(Collectors.toSet());
    //3.根据流程插入ID查询历史流程
    List<HistoricProcessInstance> historicProcessInstanceList = historyService
            .createHistoricProcessInstanceQuery()
            .processInstanceIds(processInstanceIds)
            .list();
    //4.抽出流程定义ID集合
    Set<String> processDefinitionIds = historicProcessInstanceList.stream()
            .map(HistoricProcessInstance::getProcessDefinitionId)
            .collect(Collectors.toSet());
    //5.根据流程定义ID获取流程定义
    List<ProcessDefinition> processDefinitionList = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionIds(processDefinitionIds)
            .list();
    //6.抽出流程部署id
    Set<String> deploymentIds = processDefinitionList.stream()
            .map(ProcessDefinition::getDeploymentId)
            .collect(Collectors.toSet());
    //7.返回流程部署id
    return new ResponseEntity<>(new BaseResponse<>(deploymentIds), HttpStatus.OK);
}

通过调用接口,返回三条流程部署id: 这里只是为了给大家展示Flowable提供的丰富的查询API,可以让我们从历史变量一路向上查询到流程部署,可能有的同学还没想到这里的用处,所以我需要再额外封装个对象给大家演示下,代码如下:

java 复制代码
/**
 * 历史绩效VO.
 */
@Data
public class HistoryPerformanceVo implements Serializable {

    /**
     * 绩效名称.
     */
    private String performanceName;
    
    /**
     * 绩效分数.
     */
    private String score;
}

然后在部署模型时更改名称,这里我们模拟实际业务中每月进行绩效考核,代码如下:

java 复制代码
/**
 * 部署流程模型.
 *
 * @return 流程key
 */
@PostMapping("/deploy")
public ResponseEntity<Object> createProcessDef() {
    //1.创建部署对象
    Deployment deployment = repositoryService.createDeployment()
            //2.添加流程定义文件
            .addClasspathResource("process/performance.bpmn20.xml")
            //3.设置流程名称
            .name("2024年4月绩效")
            //4.部署
            .deploy();
    //5.通过流程部署id查询流程定义id
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
            .deploymentId(deployment.getId()).singleResult();
    //6.返回流程定义id
    return new ResponseEntity<>(new BaseResponse<>(processDefinition.getId()), HttpStatus.OK);
}

设置流程名称来模拟实际业务场景中的绩效名称,结果如下: 然后我们执行前面的步骤,开启流程和完成任务,最终又回到了历史表,这时候我们对查询历史的接口再次进行迭代,代码如下:

java 复制代码
/**
 * 查询指定用户的历史绩效.
 *
 * @param userId 用户id
 * @return 历史流程集合
 */
@PostMapping("/history-process-def/userId/{userId}")
public ResponseEntity<Object> getHistoryProcessDef(@PathVariable Long userId) {
    //1.根据用户id查询历史流程
    List<HistoricVariableInstance> historicVariableInstanceList = historyService
            .createHistoricVariableInstanceQuery()
            .variableValueEquals("ownerId", userId.intValue())
            .list();
    //2.抽出流程插入ID
    Set<String> processInstanceIds = historicVariableInstanceList.stream()
            .map(HistoricVariableInstance::getProcessInstanceId)
            .collect(Collectors.toSet());
    //3.根据流程插入ID查询历史流程
    List<HistoricProcessInstance> historicProcessInstanceList = historyService
            .createHistoricProcessInstanceQuery()
            .processInstanceIds(processInstanceIds)
            .list();
    //4.转为map,键为流程定义id,值为流程插入id
    Map<String, String> processInstanceIdMap = historicProcessInstanceList
            .stream()
            .collect(Collectors.toMap(HistoricProcessInstance::getProcessDefinitionId,
                    HistoricProcessInstance::getId));
    //5.抽出流程定义ID集合
    Set<String> processDefinitionIds = historicProcessInstanceList.stream()
            .map(HistoricProcessInstance::getProcessDefinitionId)
            .collect(Collectors.toSet());
    //6.根据流程定义ID获取流程定义
    List<ProcessDefinition> processDefinitionList = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionIds(processDefinitionIds)
            .list();
    //7.抽出流程部署id
    List<String> deploymentIds = processDefinitionList.stream()
            .map(ProcessDefinition::getDeploymentId)
            .collect(Collectors.toList());
    //8.转为map,键为流程部署id,值为流程定义id
    Map<String, String> deploymentIdMap = processDefinitionList
            .stream()
            .collect(Collectors.toMap(ProcessDefinition::getDeploymentId,
                    ProcessDefinition::getId));
    //9.查询流程部署信息
    List<Deployment> deploymentList = repositoryService
            .createDeploymentQuery()
            .deploymentIds(deploymentIds)
            .list();
    //10.声明结果集
    List<HistoryPerformanceVo> historyPerformanceVos = new ArrayList<>();
    //11.遍历流程部署集合,为返回对象赋值
    deploymentList.forEach(deployment -> {
        //11-1.声明历史流程对象
        HistoryPerformanceVo historyPerformanceVo = new HistoryPerformanceVo();
        //11-2.设置绩效名称
        historyPerformanceVo.setPerformanceName(deployment.getName());
        //11-3.设置绩效分数
        String processDefinitionId = deploymentIdMap.get(deployment.getId());
        String processInstanceId = processInstanceIdMap.get(processDefinitionId);
        List<HistoricVariableInstance> historicVariableInstances = historyService
                .createHistoricVariableInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();
        List<HistoricVariableInstance> score = historicVariableInstances.stream()
                .filter(historicVariableInstance -> historicVariableInstance
                        .getVariableName().equals("score"))
                .toList();
        historyPerformanceVo.setScore((String) score.get(0).getValue());
        //11-4.添加进集合
        historyPerformanceVos.add(historyPerformanceVo);
    });
    //12.返回流程绩效对象
    return new ResponseEntity<>(new BaseResponse<>(historyPerformanceVos), HttpStatus.OK);
}

这时候我们调用接口的测试结果如下: 看到这里想必大家已经能想到应用场景了,我为了演示API查了一条链路,其实这里这个需求有更简单的方案,既然只需要返回指定用户的历史绩效和分数,那就多加一个全局流程变量记录绩效名称就好了,所以Flowable为我们提供的API非常灵活,同一个需求的实现方案也有很多,大家在实际使用中多想想怎么做是最优解。

四、小结

不知不觉已经写到了06篇,下一篇将会讲讲在流程中常见的功能-驳回,学习一下Flowable是怎么设计它的以及我们要怎么用。

相关推荐
自由的疯20 小时前
java 怎么判断事务有无提交成功
java·后端·架构
bcbnb21 小时前
Socket 抓包工具与实战,从抓取到定位(Socket 的命令、分析)
后端
用户83562907805121 小时前
用Python轻松转换Excel表格为HTML格式
后端·python
用户0840598129021 小时前
高版本的jdk在使用maven时,如何编译成低版本的class
后端
摇滚侠21 小时前
Spring Boot 3零基础教程,整合Redis,笔记12
spring boot·redis·笔记
荣淘淘21 小时前
互联网大厂Java求职面试全景实战解析(涵盖Spring Boot、微服务及云原生技术)
java·spring boot·redis·jwt·cloud native·microservices·interview
吃饭最爱21 小时前
spring高级知识概览
spring boot
这里有鱼汤21 小时前
炒股的尽头真的是玄学?我用八字+AI做了个实验,结果震惊
后端
hrrrrb21 小时前
【Spring Security】认证(二)
java·后端·spring