一、flowable 使用代码动态修改监听器
1、配置类
@Configuration public class FlowableGlobListenerConfig { @Lazy @Autowired private TaskStartListener taskStartListener; @Lazy @Autowired private TaskCompleteListener taskCompletedListener; @Lazy @Autowired private ActivityCompletedListener activityCompletedListener; @Bean @Primary public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> globalListenerConfigurer() { return engineConfiguration -> engineConfiguration.setTypedEventListeners(this.customFlowableListeners()); } /** * 监听类配置 flowable监听器级别 * * @return java.util.Map<java.lang.String, java.util.List < org.flowable.common.engine.api.delegate.event.FlowableEventListener>> */ private Map<String, List<FlowableEventListener>> customFlowableListeners() { Map<String, List<FlowableEventListener>> listenerMap = Maps.newHashMap(); listenerMap.put(FlowableEngineEventType.TASK_CREATED.name(), new ArrayList<>(Collections.singletonList(taskStartListener))); listenerMap.put(FlowableEngineEventType.TASK_COMPLETED.name(), new ArrayList<>(Collections.singletonList(taskCompletedListener))); listenerMap.put(FlowableEngineEventType.ACTIVITY_COMPLETED.name(), new ArrayList<>(Collections.singletonList(activityCompletedListener))); return listenerMap; } }
说明:通过注册关注的事件,实现对应的监听逻辑实现相应业务逻辑
2、流程节点创建时的监听逻辑
import cn.hutool.core.collection.CollUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.landray.kmss.sys.notify.webservice.NotifyTodoSendContextVo; import cn.togeek.geekbidder.crm.contract.entity.ContractEntity; import cn.togeek.geekbidder.crm.contract.mapper.ContractMapper; import cn.togeek.geekbidder.crm.contract.service.ContractNewServiceImpl; import cn.togeek.geekbidder.crm.contract.util.TitleUtil; import cn.togeek.maple.message.api.enums.MessageType; import cn.togeek.maple.message.api.enums.ReadState; import cn.togeek.maple.message.api.model.Message; import cn.togeek.maple.message.api.model.Recipient; import cn.togeek.maple.message.api.service.MessageService; import cn.togeek.maple.platform.api.model.User; import cn.togeek.maple.platform.api.service.UserService; import cn.togeek.maple.process.TaskStartInvoker; import cn.togeek.maple.process.api.model.ProcessMessage; import cn.togeek.maple.process.api.util.SpringContextUtil; import cn.togeek.oa.TaskQueryService; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.flowable.common.engine.api.delegate.event.FlowableEvent; import org.flowable.common.engine.api.delegate.event.FlowableEventListener; import org.flowable.common.engine.impl.event.FlowableEntityEventImpl; import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; import org.flowable.engine.TaskService; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.identitylink.api.IdentityLink; import org.flowable.task.service.impl.persistence.entity.TaskEntity; import org.flowable.variable.api.history.HistoricVariableInstance; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component("taskStartListener") public class TaskStartListener implements FlowableEventListener { private static final Logger log = LoggerFactory.getLogger(TaskStartListener.class); @Autowired private TaskService taskService; @Autowired private MessageService messageService; @Autowired private RepositoryService repositoryService; @Autowired private UserService userService; @Autowired private ObjectMapper objectMapper; @Autowired private TaskQueryService taskQueryService; @Autowired private HistoryService historyService; @Value("${system.url}") private String url; @Autowired private ContractMapper contractMapper; @Autowired private ContractNewServiceImpl contractNewService; /** * 流程发起时统一通知逻辑 * process-task/processVariables/33a9feee-65a6-11ef-8ac0-024204500072 */ public TaskStartListener() { } public void onEvent(FlowableEvent event) { TaskEntity taskEntity = (TaskEntity)((FlowableEntityEventImpl)event).getEntity(); String taskId = taskEntity.getId(); List<IdentityLink> idList = this.taskService.getIdentityLinksForTask(taskId); if (!CollectionUtils.isEmpty(idList)) { List<Recipient> recipientList = new ArrayList(); idList.stream().distinct().forEach((identityLink) -> { Recipient recipient; if (StringUtils.isNotBlank(identityLink.getUserId()) && "assignee".equals(identityLink.getType())) { recipient = new Recipient(); recipient.setId(identityLink.getUserId()); recipientList.add(recipient); } if (StringUtils.isNotBlank(identityLink.getUserId()) && "candidate".equals(identityLink.getType())) { recipient = new Recipient(); recipient.setId(identityLink.getUserId()); recipientList.add(recipient); } else if (StringUtils.isNotBlank(identityLink.getGroupId()) && "candidate".equals(identityLink.getType())) { List<User> userList = this.userService.getUserByRole(identityLink.getGroupId()); userList.forEach((obj) -> { Recipient r = new Recipient(); r.setId(obj.getId()); recipientList.add(r); }); } this.sendMessage(taskEntity, recipientList); }); /** * 流程通知oa系统相关人逻辑 * process-task/processVariables/33a9feee-65a6-11ef-8ac0-024204500072 * //todo 需确认与oa系统对应用户的关系、及连接点击时token信息的有效性 */ List<String> ids = recipientList.stream().map(Recipient::getId).collect(Collectors.toList()); List<String> taskUsers = this.userService.getUsersByIds(ids).stream().map(User::getName).collect(Collectors.toList()); String instId = taskEntity.getProcessInstanceId(); HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instId).singleResult(); List<HistoricVariableInstance> list = historyService.createHistoricVariableInstanceQuery().processInstanceId(instId).list(); String definitionKey = instance.getProcessDefinitionKey(); ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionKey(definitionKey) .active() .singleResult(); String deploymentId = processDefinition.getDeploymentId(); Map<String, Object> map = list.stream().filter(e->Objects.nonNull(e.getValue())).collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue, (o1, o2)->o2)); String processName = processDefinition.getName(); String activeTab = processDefinition.getCategory(); String businessKey = instance.getBusinessKey(); // String activeTab = OAConstant.PROCESS_MAP.get(definitionKey).get(1); ContractEntity contractEntity; if((contractEntity=contractMapper.selectById(businessKey))!=null) { contractEntity.setCurrentApprovalUser(String.join(",", taskUsers)); contractMapper.updateById(contractEntity); } //根据规则创建subject CompletableFuture.runAsync(()-> { if(CollUtil.isEmpty(map)) { return; } log.info("==================BEGIN异步同步oa代办BEGIN================="); NotifyTodoSendContextVo vo = new NotifyTodoSendContextVo(); vo.setBusinessKey(instId); vo.setModelId(instId); vo.setType(2); vo.setSubject(TitleUtil.buildSubject(map, processName)); // vo.setLink(url + "/process-task/processVariables/"+ instId); vo.setLink(buildLink(deploymentId, instId, taskId, activeTab)); taskQueryService.syncAgency(vo, taskUsers); log.info("==================END异步同步oa代办END================="); }); } Map<String, TaskStartInvoker> type = SpringContextUtil.getBeansOfType(TaskStartInvoker.class); type.values().parallelStream().forEach( (invoker) -> { invoker.consume(event); } ); } private String buildLink(String deployId, String procInsId, String taskId, String activeTab) { return url+ "/process/record?deployId="+deployId+"&procInsId=="+procInsId+"&taskId="+taskId+"&activeTab="+activeTab; } public boolean isFailOnException() { return false; } public boolean isFireOnTransactionLifecycleEvent() { return false; } public String getOnTransaction() { return null; } public void sendMessage(TaskEntity taskEntity, List<Recipient> recipientList) { ProcessMessage processMessage = new ProcessMessage(); ProcessDefinition pd = this.repositoryService.createProcessDefinitionQuery().processDefinitionId(taskEntity.getProcessDefinitionId()).singleResult(); processMessage.setDeployId(pd.getDeploymentId()); processMessage.setProcInsId(taskEntity.getProcessInstanceId()); processMessage.setRouterName("审批详情路由"); processMessage.setTaskId(taskEntity.getId()); processMessage.setCheckUrl("/process-task/runing/" + taskEntity.getId()); Message message = new Message(); message.setCreateTime(LocalDateTime.now()); message.setMessage("您有新的流程待办消息,请及时处理!"); message.setRecipients(recipientList); message.setTitle("流程待办提醒"); message.setReadState(ReadState.NOT_READ); message.setType(MessageType.MESSAGE); message.setMessageType("PROCESS_TODO_MESSAGE"); String transmissionText = null; try { transmissionText = this.objectMapper.writeValueAsString(processMessage); } catch (JsonProcessingException var8) { log.error(var8.getMessage(), var8); } message.setTransmissionText(transmissionText); this.messageService.sendMessage(message); } }
3、流程节点结束时逻辑
import cn.togeek.maple.process.TaskComplateInvoker; import cn.togeek.maple.process.api.util.SpringContextUtil; import java.util.Map; import org.flowable.common.engine.api.delegate.event.FlowableEvent; import org.flowable.common.engine.api.delegate.event.FlowableEventListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component("taskCompleteListener") public class TaskCompleteListener implements FlowableEventListener { private static final Logger log = LoggerFactory.getLogger(TaskCompleteListener.class); public TaskCompleteListener() { } public void onEvent(FlowableEvent event) { Map<String, TaskComplateInvoker> type = SpringContextUtil.getBeansOfType(TaskComplateInvoker.class); type.values().parallelStream().forEach( (invoker) -> { invoker.consume(event); } ); } public boolean isFailOnException() { return false; } public boolean isFireOnTransactionLifecycleEvent() { return false; } public String getOnTransaction() { return null; } }
4、流程任意节点结束时
@Slf4j @Component("activityCompletedListener") public class ActivityCompletedListener implements FlowableEventListener { @Autowired private RepositoryService repositoryService; @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Autowired private HistoryService historyService; @Autowired private ApplicationEventPublisher applicationEventPublisher; @Override public void onEvent(FlowableEvent event) { if(event instanceof FlowableActivityEventImpl) { FlowableActivityEventImpl activityEvent = (FlowableActivityEventImpl) event; String activityType = activityEvent.getActivityType(); if(!"userTask".equals(activityType)) { return; } String processDefinitionId = activityEvent.getProcessDefinitionId(); String processInstanceId = activityEvent.getProcessInstanceId(); ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId); List<HistoricActivityInstance> allProcessNodes = historyService.createHistoricActivityInstanceQuery() .activityId(processInstance.getActivityId()) .processInstanceId(processInstanceId) .list(); List<HistoricActivityInstance> userTaskList = allProcessNodes.stream().filter(e -> e.getActivityType().equals("userTask")).collect(Collectors.toList()); //判断节点列表中是否大于两个节点、第一个必为填报节点、第二个必为审批节点 String businessKey = processInstance.getBusinessKey(); if(userTaskList.size() >= 2) { ListenerEvent listenerEvent = new ListenerEvent(); listenerEvent.setApprovalResults("PROGRESS_IN"); listenerEvent.setBusinessKey(businessKey); listenerEvent.setDefinitionId(processDefinitionId); listenerEvent.setProcessInstanceId(processInstanceId); listenerEvent.setDefinitionKey(processDefinition.getKey()); listenerEvent.setDefinitionName(processDefinition.getName()); this.applicationEventPublisher.publishEvent(new ListenerEventSource(listenerEvent)); } } } @Override public boolean isFailOnException() { return false; } @Override public boolean isFireOnTransactionLifecycleEvent() { return false; } @Override public String getOnTransaction() { return null; } }
二、流程跳过逻辑
/** * 对已驳回的流程发起时选择从驳回节点开始审批 * @param oldInstId * @param newInstId */ public void skipPreAgreeNodes(String oldInstId, String newInstId){ HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(oldInstId).singleResult(); String deployId = Optional.ofNullable(processInstance) .map(HistoricProcessInstance::getProcessDefinitionId) .map(item -> item.substring(item.lastIndexOf(":") + 1)) .orElse(null); Map<String, Object> record = this.flowTaskService.flowRecord(oldInstId, deployId); List<FlowTask> flowList = (List<FlowTask>) record.getOrDefault("flowList", Lists.newArrayList()); if(flowList.size()<2) { return; } FlowTask lastRejectNode = flowList.get(0); List<Task> list; int count = 0; int times = 0; int excludeFillAndRejectCount = flowList.size() - 2; while((list=taskService.createTaskQuery().processInstanceId(newInstId).list()).stream() .noneMatch(e->e.getTaskDefinitionKey().equals(lastRejectNode.getTaskDefKey())) || count< excludeFillAndRejectCount || times< excludeFillAndRejectCount ) { //正常情况下list中只有一个元素(非多实例)、多实例节点时此处有多个元素 for(Task task : list) { //筛选办理记录中节点定义id与当前id一致的记录 List<FlowTask> allTasks = flowList.stream() .filter(e -> e.getTaskDefKey().equals(task.getTaskDefinitionKey())) .collect(Collectors.toList()); String assignee = task.getAssignee(); for(FlowTask allTask : allTasks) { //多实例节点时assignee不可能为空,需根据审批记录中的审批人id 判断需不需要跳过该用户 if(assignee !=null && !assignee.equals(allTask.getAssigneeId())) { continue; } FlowTaskVo flowTaskVo = new FlowTaskVo(); BeanUtils.copyProperties(task, flowTaskVo); flowTaskVo.setInstanceId(newInstId); flowTaskVo.setComment(allTask.getComment().getComment()); flowTaskVo.setAssignee(allTask.getAssigneeId()); flowTaskVo.setUserId(allTask.getAssigneeId()); flowTaskVo.setTaskId(task.getId()); List<Attachment> attachmentList = allTask.getAttachmentList(); if(CollUtil.isNotEmpty(attachmentList)) { List<MultipartFile> files = attachmentList.stream().map(f -> { InputStream attachmentContent = taskService.getAttachmentContent(f.getId()); try { return new MockMultipartFile(f.getName(), attachmentContent); } catch(Exception e) { e.printStackTrace(); return null; } }).filter(Objects::nonNull).collect(Collectors.toList()); flowTaskVo.setFiles(files); } this.flowTaskService.complete(flowTaskVo); count++; } } times ++; } }
三、流程结束时遇到的问题(结束了但业务表状态未修改成功)
import cn.hutool.core.util.StrUtil; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import cn.togeek.foundation.web.api.BusinessCode; import cn.togeek.foundation.web.exception.BusinessException; import cn.togeek.geekbidder.crm.contract.entity.ContractEntity; import cn.togeek.geekbidder.crm.contract.enums.ApprovalState; import cn.togeek.geekbidder.crm.contract.mapper.ContractNewMapper; import cn.togeek.maple.process.api.enums.FlowCommentEnum; import cn.togeek.maple.process.api.model.ListenerEvent; import cn.togeek.maple.process.api.model.ListenerEventSource; import java.util.Arrays; import java.util.Map; import java.util.Objects; import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @Component @AllArgsConstructor @Slf4j public class ContractFlowEventListener implements ApplicationListener<ListenerEventSource> { @Autowired private RuntimeService runtimeService; @Autowired private RepositoryService repositoryService; @Autowired private ContractNewMapper contractNewMapper; @Autowired private HistoryService historyService; /** * 流程结束监听 * * @param event */ @Override public void onApplicationEvent(ListenerEventSource event) { callBackWithStateCal(event); } public synchronized void callBackWithStateCal(ListenerEventSource event){ log.info("------------------流程结束回调--------------------"); HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(event.getListenerEvent().getProcessInstanceId()) .singleResult(); if(Objects.isNull(processInstance)) { return; } String deploymentId = processInstance.getDeploymentId(); ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult(); if(Objects.isNull(processDefinition)) { return; } String defKey = processDefinition.getKey(); //只对发布的事件中关注的事件进行处理 boolean needDeal = Arrays.stream(ContractApproveGlobal.values()) .map(ContractApproveGlobal::getDefKey) .anyMatch(e -> e.equals(defKey)); log.info("------------------needDeal{}--------------------"+needDeal); if(needDeal) { String id = processInstance.getBusinessKey(); ListenerEvent listenerEvent = event.getListenerEvent(); String approvalResults = listenerEvent.getApprovalResults(); log.info("------------------approvalResults{}--------------------"+approvalResults); ContractEntity contractEntity = contractNewMapper.selectById(id); Assert.notNull(contractEntity, () -> { throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), String.format( "合同id为"+ id + "的合同不存在,可能已被删除,请检查!!!")); }); if(FlowCommentEnum.NORMAL.getType().equals(approvalResults)) { ContractApproveGlobal dbNode = ContractApproveGlobal.getByCode(contractEntity.getApprovalNode()); ContractApproveGlobal nextNode = dbNode.getNearNode()[1]; contractEntity.setCurrentApprovalUser("无"); //上一个流程节点结束时将全局流程节点改为下一个节点 if(nextNode!=null && Integer.parseInt(nextNode.getCode()) <=3) { // 审批通过 contractEntity.setApprovalState("0"); contractEntity.setApprovalNode(nextNode.getCode()); } else { contractEntity.setApprovalState(ApprovalState.CHANGE.getCode()); } } else if(FlowCommentEnum.REJECT.getType().equals(approvalResults)) { // 审批驳回 Integer rejectCnt = contractEntity.getRejectCnt(); contractEntity.setCurrentApprovalUser("无"); rejectCnt = rejectCnt == null ? 0 : rejectCnt; contractEntity.setRejectCnt(rejectCnt+1); contractEntity.setApprovalState(ApprovalState.TERMINATION.getCode()); } else if("PROGRESS_IN".equals(approvalResults)) { contractEntity.setApprovalState(ApprovalState.EXECUTE.getCode()); } else if(FlowCommentEnum.REBACK.getType().equals(approvalResults)) { // 审批退回 contractEntity.setApprovalState(ApprovalState.UN_SUBMIT.getCode()); } contractNewMapper.updateById(contractEntity); } } }
原因总结:由于流程结束时使用了 runtimeService 取流程实例已经取不到了,导致后续的操作都失败了,使用 historyService 则可以避免此问题
标签:xml,String,flowable,private,监听器,import,org,togeek,cn From: https://www.cnblogs.com/g177w/p/18420999