0.前言
流程實例是與業務相關聯的,先介紹一下業務:用戶申請物品,領導進行審批(同意/拒絕),同意:流程結束,申請狀態為通過;拒絕:流程結束,申請狀態為拒絕。
下圖為流程圖,key為material_apply
可以看到,"銷售支持審批"設置的是變量${sale_support_member}
,邏輯是只能角色是"銷售支持"的用戶才可以審批。
在saleSupportVerify
這個用戶任何節點設置了兩個表單屬性
FormProperty_regionAdvice--__!!radio--__!!審批意見--__!!i--__!!同意--__--不同意
FormProperty_regionText--__!!textarea--__!!批注--__!!f__!!null
一個單選按鈕:同意/駁回;一個輸入框:可以寫駁回理由等等。
在走向結束時,設置一個監聽器,用來修改實體類的狀態
@Slf4j
@Controller
public class MaterialListener implements ExecutionListener{
//獲取流程圖設置的狀態值
private Expression state;
//一旦是走拒絕和最后審批的同意的都會觸發這個監聽器
//拒絕 state=2
//最后一個審批的同意 state=3
@Override
public void notify(DelegateExecution delegateExecution) {
Material material = new Material();
String processInstanceId = delegateExecution.getProcessInstanceId();
String status = (String)state.getValue(delegateExecution);
ProcessInstance processInstance = SpringUtils.getBean(RuntimeService.class).createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
material.setId(Long.parseLong(processInstance.getBusinessKey()));
if(status.equals("3")){
material.setApplyStatus('3');
}else if(status.equals("2")){
material.setApplyStatus('2');
}
material.setUpdateBy(SecurityUtils.getUsername());
material.setUpdateTime(new Date());
SpringUtils.getBean(IMaterialService.class).updateById(material);
}
}
1.創建流程實例
注:businessKey
是關鍵點,是實體類的id,從而與流程定義進行關聯,並設置候選人(在此有一個問題,如果后期將某個人設置角色為銷售支持,但他在自己的待辦任務里面是看不到之前銷售提交的審批任務的)
public AjaxResult submitApply(@RequestBody Material material){
//目前只設計了一級審批 銷售支持部審批
try{
//將角色與每個審批用戶做關聯
List<String> saleSupportList = materialService.listByRoleName("sale_support_member");
String saleSupportJoin = StringUtils.join(saleSupportList,",");
// regionManager regionDirector president
ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
.processDefinitionKey("material_apply")
.name(material.getApplyTitle())
//將流程實例與物料做關聯
.businessKey(material.getId()+"")
.variable("sale_support_member",saleSupportJoin)
.start();
log.info("processInstanceId: "+processInstance.getProcessInstanceId());;
log.info("id: "+processInstance.getId());
material.setInstanceId(processInstance.getId());
//申請中
material.setApplyStatus('1');
materialService.updateById(material);
return AjaxResult.success("提交申請成功");
}catch (Exception e){
log.error("submitApply error: "+e.getMessage());
return AjaxResult.error("提交申請失敗");
}
}
2.撤銷申請(未實現)
邏輯:刪除流程實例或者修改流程實例的狀態為掛起
問題: 根據流程實例id沒有獲取到流程實例?
如果流程實例已結束,根據流程實例id獲取不到流程實例;但目前只是剛創建了流程實例
public AjaxResult cancelApply(@RequestBody String instanceId) {
String msg = processDefinitionService.cancelApply(instanceId, "用戶撤銷");
return success(msg);
}
public String cancelApply(String instanceId, String reason) {
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId)
.singleResult();
if(instance != null){
//說明流程實例還在進行中,可以撤銷
runtimeService.deleteProcessInstance(instanceId,reason);
return "撤銷申請成功";
}else{
return "流程已結束,不允許撤銷申請";
}
}
// 執行此方法后未審批的任務 act_ru_task 會被刪除,流程歷史 act_hi_taskinst 不會被刪除,並且流程歷史的狀態為finished完成
public String cancelApply(String instanceId, String reason) {
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId("e7361785-ec2c-11eb-89e3-0068ebbadc17")
.singleResult();
//true-暫停 false-激活
boolean suspend = instance.isSuspended();
String processInstanceId = instance.getId();
if(suspend){
//如果已暫停,就激活
runtimeService.activateProcessInstanceById(processInstanceId);
log.info("流程實例已激活");
runtimeService.deleteProcessInstance(instanceId, reason);
return "流程實例已激活";
}else{
//如果已激活,就暫停
runtimeService.suspendProcessInstanceById(processInstanceId);
log.info("流程實例已暫停");
runtimeService.deleteProcessInstance(instanceId, reason);
return "流程實例已暫停";
}
}
}
3.查看審批歷史(流程實例)
有兩種:
- 只獲取審批節點(即只獲取userTask)
- 獲取全部節點,比如startEvent、userTask、gateWay、endEvent
public AjaxResult historyProcess(@PathVariable("instanceId") String instanceId) {
List<HistoricTaskInstance> list=historyService // 歷史相關Service
.createHistoricTaskInstanceQuery() // 創建歷史任務實例查詢
.processInstanceId(instanceId) // 用流程實例id查詢
.finished() // 查詢已經完成的任務
.list();
list.stream().sorted(Comparator.comparing(HistoricTaskInstance::getStartTime));
List<ApplyHis> applyHisList = new ArrayList<>();
list.stream().forEach(
ht -> {
ApplyHis applyHis = new ApplyHis();
applyHis.setTaskName(ht.getName());
applyHis.setAssignee(ht.getAssignee());
applyHis.setStartTime(ht.getStartTime());
if(ht.getEndTime() != null){
applyHis.setEndTime(ht.getEndTime());
}
applyHisList.add(applyHis);
}
);
return AjaxResult.success(applyHisList);
}
4.查看審批高亮圖
該功能設計三個請求:
- 根據流程實例id獲取流程定義id、部署id、資源名
public AjaxResult getDefinitionsByInstanceId(@PathVariable("instanceId") String instanceId){
ProcessInstance pi = runtimeService // 獲取運行時Service
.createProcessInstanceQuery() // 創建流程實例查詢
.processInstanceId(instanceId) // 用流程實例id查詢
.singleResult();
if(pi != null){
log.info("流程正在執行!");
log.info("流程定義id: "+pi.getProcessDefinitionId());
return AjaxResult.success(processDefinitionService.getDefinitionsByInstanceId(instanceId));
}else{
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
String processDefinitionId = historicProcessInstance.getProcessDefinitionId();
ProcessDefinitionQuery pdq = repositoryService.createProcessDefinitionQuery();
ProcessDefinition pd = pdq.processDefinitionId(processDefinitionId).singleResult();
log.info("********************************************************************************");
log.info("deploymentId: "+pd.getDeploymentId());
String resourceName = pd.getResourceName();
log.info("resourceName: "+resourceName);
log.info("流程定義id: "+pd.getId());
log.info("********************************************************************************");
return AjaxResult.success(new DefinitionIdDTO(pd.getDeploymentId(),resourceName,pd.getId()));
}
}
- 根據流程實例id和流程定義id獲取json形式的高亮數據
public AjaxResult gethighLine(@RequestParam("instanceId") String instanceId,@RequestParam("processDefinitionId") String processDefinitionId) {
ActivitiHighLineDTO activitiHighLineDTO = activitiHistoryService.getHighLine(instanceId,processDefinitionId);
return AjaxResult.success(activitiHighLineDTO);
}
public ActivitiHighLineDTO getHighLine(String instanceId,String processDefinitionId) {
//只有流程沒走完的才能查找到流程實例
//ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
//獲取bpmnModel對象
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//因為我們這里只定義了一個Process 所以獲取集合中的第一個即可
Process process = bpmnModel.getProcesses().get(0);
//獲取所有的FlowElement信息
Collection<FlowElement> flowElements = process.getFlowElements();
Map<String, String> map = new HashMap<>();
for (FlowElement flowElement : flowElements) {
//判斷是否是連線
if (flowElement instanceof SequenceFlow) {
SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
String ref = sequenceFlow.getSourceRef();
String targetRef = sequenceFlow.getTargetRef();
map.put(ref + targetRef, sequenceFlow.getId());
}
}
//獲取流程實例 歷史節點(全部)
List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId)
.list();
//各個歷史節點 兩兩組合 key
Set<String> keyList = new HashSet<>();
for (HistoricActivityInstance i : list) {
for (HistoricActivityInstance j : list) {
if (i != j) {
keyList.add(i.getActivityId() + j.getActivityId());
}
}
}
//高亮連線ID
Set<String> highLine = new HashSet<>();
keyList.forEach(s -> highLine.add(map.get(s)));
//獲取流程實例 歷史節點(已完成)
List<HistoricActivityInstance> listFinished = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId)
.finished()
.list();
//高亮節點ID
Set<String> highPoint = new HashSet<>();
listFinished.forEach(s -> highPoint.add(s.getActivityId()));
//獲取流程實例 歷史節點(待辦節點)
List<HistoricActivityInstance> listUnFinished = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId)
.unfinished()
.list();
//需要移除的高亮連線
Set<String> set = new HashSet<>();
//待辦高亮節點
Set<String> waitingToDo = new HashSet<>();
listUnFinished.forEach(s -> {
waitingToDo.add(s.getActivityId());
for (FlowElement flowElement : flowElements) {
//判斷是否是 用戶節點
if (flowElement instanceof UserTask) {
UserTask userTask = (UserTask) flowElement;
if (userTask.getId().equals(s.getActivityId())) {
List<SequenceFlow> outgoingFlows = userTask.getOutgoingFlows();
//因為 高亮連線查詢的是所有節點 兩兩組合 把待辦 之后 往外發出的連線 也包含進去了 所以要把高亮待辦節點 之后 即出的連線去掉
if (outgoingFlows != null && outgoingFlows.size() > 0) {
outgoingFlows.forEach(a -> {
if (a.getSourceRef().equals(s.getActivityId())) {
set.add(a.getId());
}
});
}
}
}
}
});
highLine.removeAll(set);
Set<String> iDo = new HashSet<>(); //存放 高亮 我的辦理節點
//當前用戶已完成的任務
List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery()
// .taskAssignee(SecurityUtils.getUsername())
.finished()
.processInstanceId(instanceId).list();
taskInstanceList.forEach(a -> iDo.add(a.getTaskDefinitionKey()));
ActivitiHighLineDTO activitiHighLineDTO =new ActivitiHighLineDTO();
activitiHighLineDTO.setHighPoint(highPoint);
activitiHighLineDTO.setHighLine(highLine);
activitiHighLineDTO.setWaitingToDo(waitingToDo);
activitiHighLineDTO.setiDo(iDo);
return activitiHighLineDTO;
}
返回結果
{
"highPoint" : ["StartEvent_1"],
"highLine" : [null,"Flow_0w19svd"],
"waitingToDo" : ["saleSupportVerify"],
"iDo" : []
}
- 根據第一個請求返回的部署id和資源名稱獲取xml
public void getProcessDefineXML(HttpServletResponse response,
@RequestParam("deploymentId") String deploymentId,
@RequestParam("resourceName") String resourceName) throws IOException {
processDefinitionService.getProcessDefineXML(response, deploymentId, resourceName);
}
- 將xml和第二個請求的響應數據結合,最后顯現高亮進度圖