问题背景
ao审批的效率是老生常谈的问题,流程图设计的部分节点是按角色或岗位配置的,最终审批的路径会出现相邻节点是重复的审批人,导致同一个审批人需要审批好几次才能推动整个流转进程,由此要设计一个功能:相邻节点是同一人,只需要一次审批便可完成相邻节点的跳过功能。
功能图例如下:
张三同学只需要一次审批操作就可以流转节点直接走到李四的节点位置。
方案有多种设计:例如有可以在流程设计器中画各种条件网关,也可以在userTask属性上配置跳过表达式等等,但是这些改造都有局限性,那就是在流程发布之前就得完全知道节点上配置的人员信息,而且对流程图入侵比较大,并不是一个通用解决方案。
推荐方法:
采用全局监听器
配置全局监听器
/**
* @description: 全局监听器
**/
@Configuration
public class FlowableGlobListenerConfing implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private SpringProcessEngineConfiguration configuration;
@Autowired
private GlobalTaskCreateListener globalTaskCreateListener;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FlowableEventDispatcher eventDispatcher = configuration.getEventDispatcher();
eventDispatcher.addEventListener(globalTaskCreateListener, FlowableEngineEventType.TASK_CREATED);//监听task任务创建
}
}
/**
*实现监听器功能
*/
@Component
public class GlobalTaskCreateListener extends AbstractFlowableEngineEventListener {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
TaskService taskService;
@Resource
HistoryService historyService;
@Resource
private RepositoryService repositoryService;
@Resource
private RuntimeService runtimeService;
/**
* 线程共享变量
*/
static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
@Override
public void onEvent(FlowableEvent flowableEvent) {
try {
logger.info("Flowable处理事件:{}", Optional.ofNullable(flowableEvent).map(FlowableEvent::getType).map(FlowableEventType::name).orElse(null));
super.onEvent(flowableEvent);
} catch (Exception e) {
logger.error("Flowable事件处理失败!", e);
} finally {
threadLocal.remove();
}
}
/**
* //相邻节点是同一审批人需要跳过执行。
* @param event
*/
@Override
protected void taskCreated(FlowableEngineEntityEvent event) {
logger.info("Flowable处理事件-taskCreated:{}", JSON.toJSONString(event));
UserTask flowElementLod = null;
//前置节点:incomingFlows
List<SequenceFlow> incomingFlows = flowElement.getIncomingFlows();
logger.info("处理前置节点开始: {}", JSON.toJSONString(incomingFlows));
if(incomingFlows.size()==1 && incomingFlows.get(0).getSourceFlowElement() instanceof ExclusiveGateway){
logger.info("前置为网关节点");
ExclusiveGateway exclusiveGateway = (ExclusiveGateway) incomingFlows.get(0).getSourceFlowElement();
List<SequenceFlow> incomingFlows1 = exclusiveGateway.getIncomingFlows();
flowElementLod = (UserTask)process.getFlowElement(incomingFlows1.get(0).getSourceRef());
}else if (incomingFlows.size()==1 && incomingFlows.get(0).getSourceFlowElement() instanceof UserTask){
logger.info("前置为单节点");
flowElementLod = (UserTask)process.getFlowElement(incomingFlows.get(0).getSourceRef());
}else if (incomingFlows.size() > 1 ){
logger.info("前置为多节点");
List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
String taskId = processInstanceComments.get(0).getTaskId();
if (entity.getAssignee().equals(taskId) && (ProcessActionEnum.CARE_OF_PASS.name().equals(processInstanceComments.get(0).getType()) || ProcessActionEnum.PASS.name().equals(processInstanceComments.get(0).getType()))){
skipFunction(entity, processInstanceId);
}
//前置节点和当前节点不一致 不需要做跳过业务逻辑
return;
}
Map<String, List<ExtensionAttribute>> attributes = flowElementLod.getAttributes();
ExtensionAttribute userType = attributes.get("userType").get(0);
ExtensionAttribute handleMode = flowElementLod.getAttributes().get("handleMode").get(0);
checkBeforeUserNode(flowElementLod, userType, handleMode, variables, entity, processInstanceId);
}
}
/**
* 校验前置节点 与 当前节点是否一致
*
*/
private void checkBeforeUserNode(UserTask flowElementLod, ExtensionAttribute userType, ExtensionAttribute handleMode, Map<String, Object> variables, TaskEntity entity, String processInstanceId ){
if (UserTypeEnums.assignee.name().equals(userType.getValue()) && ProcessConstant.MULTIPLEUNITE_ONE.equals(handleMode.getValue())){
List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
//或签节点
if (ObjectUtils.isNotEmpty(threadLocal.get()) ){
String assigneeId = (String)threadLocal.get();
if (assigneeId.equals(entity.getAssignee())){
skipFunction(entity,processInstanceId);
}
}else if (ObjectUtils.isEmpty(threadLocal.get()) && CollectionUtils.isNotEmpty(processInstanceComments)){
if (ProcessActionEnum.REJECT.name().equals(processInstanceComments.get(0).getType())){
return;
}
List<Comment> collect = processInstanceComments.stream().filter(x -> ProcessActionEnum.PASS.name().equals(x.getType()) || ProcessActionEnum.CARE_OF_PASS.name().equals(x.getType())).collect(toList());
if (collect.get(0).getUserId().equals(entity.getAssignee())){
skipFunction(entity,processInstanceId);
}
}
}else if (UserTypeEnums.assignee.name().equals(userType.getValue()) && ProcessConstant.MULTIPLEUNITE_ALL.equals(handleMode.getValue())){
//会签节点
if (checkAssigneeList(flowElementLod, variables).contains(entity.getAssignee())){
skipFunction(entity,processInstanceId);
}
}else if(UserTypeEnums.assigneeInitiator.name().equals(userType.getValue())){
if (entity.getAssignee().equals(checkAssignee(flowElementLod, variables))){
skipFunction(entity,processInstanceId);
}
}else if (UserTypeEnums.departmentLeader.name().equals(userType.getValue()) || UserTypeEnums.assigneePerDepartment.name().equals(userType.getValue())){
if (entity.getAssignee().equals(checkAssignee(flowElementLod, variables))){
skipFunction(entity,processInstanceId);
}
}else if (UserTypeEnums.assignee.name().equals(userType.getValue()) && ProcessConstant.SOLO.equals(handleMode.getValue())){
if (entity.getAssignee().equals(checkAssignee(flowElementLod, variables))){
skipFunction(entity,processInstanceId);
}
}else if (UserTypeEnums.customDefine.name().equals(userType.getValue()) && ProcessConstant.MULIIPLE_UNITE_ALL.equals(handleMode.getValue())){
Map<String, Object> variablesTemp = customExportVariableMap(variables);
if (variablesTemp == null){
variablesTemp = variables;
}
if (checkAssigneeList(flowElementLod, variablesTemp).contains(entity.getAssignee())){
skipFunction(entity,processInstanceId);
}
}
}
/**
* 发起人自选获取自选节点内容
*
*/
public Map<String, Object> customExportVariableMap(Map<String, Object> variables){
List<JSONObject> variablesList = JSON.parseArray((String) variables.get("variables"), JSONObject.class);
if (variablesList == null){
return null;
}
Map<String, Object> variableTemp = new HashMap<>();
for (JSONObject variable : variablesList) {
variableTemp.put((String)variable.get("name"),variable.get("value"));
}
return variableTemp;
}
/**
* 执行自动跳过方法
* @param entity 任务
* @param processInstanceId 实例ID
*/
private void skipFunction(TaskEntity entity, String processInstanceId){
logger.info("task_skip开始:processInstanceId={}, taskId为: {}", processInstanceId, entity.getId());
// 1、添加跳过节点意见
Authentication.setAuthenticatedUserId(entity.getAssignee());
taskService.addComment(entity.getId(), processInstanceId, ProcessActionEnum.SKIP.name(), "");
//在连续相邻节点审批 :使用线程变量 存储上一节点审批人
threadLocal.set(entity.getAssignee());
//2、完成审批
taskService.complete(entity.getId());
}
/**
* 获取单节点审批人
* @param flowElementLod 任务
* @param variables 变量
* @return 处理人
*/
private String checkAssignee(UserTask flowElementLod, Map<String, Object> variables){
String assignee = flowElementLod.getAssignee();
Matcher regexMatcher = REGEX_PATTERN.matcher(assignee);
regexMatcher.find();
String variable = regexMatcher.group(1);
return (String)variables.get(variable);
}
/**
* 获取assigneeList
* @param flowElementLod 任务
* @param variables 变量
* @return 处理人
*/
private List<String> checkAssigneeList(UserTask flowElementLod, Map<String, Object> variables){
String inputDataItem = flowElementLod.getLoopCharacteristics().getInputDataItem();
Object o = variables.get(inputDataItem);
return (List<String>)o;
}
}
设计思路: 采用全局监听的方法去控制跳过节点的方法触发,本方法的好处是使用监听器去辅助用户完成相邻节点是同一人审批的功能,历史流程表中都会记录审批日志。
如果有其他问题 请私信联系作者本人