一、引子
在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是怎么设计它的以及我们要怎么用。