与 Spring 集成

  • 博主用的现成的 ruoyi-activiti-5.22.0
    • 地址 ruoyi-activiti
    • 由于博主开发的是传统单一应用,删除了 eurika 等依赖
      • 删除了 tk-mapper 依赖,博主是个喜欢光速启动的强迫症
      • 博主将前端换成了 element 实现,基本上就是更换下控件了,这个比较简单
  • 集成好后可以看到,原作者已经对 activiti 做了二次封装 美滋滋😊✔
<!-- 单一应用所需的依赖 -->
<properties>
	<activiti.version>5.22.0</activiti.version>
	<mapper.starter.version>2.1.5</mapper.starter.version>
</properties>
<!-- Activiti -->
<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-spring-boot-starter-basic</artifactId>
	<version>${activiti.version}</version>
	<exclusions>
		<exclusion>
			<artifactId>activation</artifactId>
			<groupId>javax.activation</groupId>
		</exclusion>
		<exclusion>
			<artifactId>mybatis</artifactId>
			<groupId>org.mybatis</groupId>
		</exclusion>
	</exclusions>
</dependency>
<!-- Activiti流程图 -->
<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-diagram-rest</artifactId>
	<version>${activiti.version}</version>
	<exclusions>
		<exclusion>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
		</exclusion>
		<exclusion>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-crypto</artifactId>
		</exclusion>
		<exclusion>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<!-- Activiti在线设计 -->
<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-modeler</artifactId>
	<version>${activiti.version}</version>
	<exclusions>
		<exclusion>
			<artifactId>commons-logging</artifactId>
			<groupId>commons-logging</groupId>
		</exclusion>
		<exclusion>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
		</exclusion>
		<exclusion>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-crypto</artifactId>
		</exclusion>
		<exclusion>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

要注意启动类上的配置

@SpringBootApplication(exclude = {
		DataSourceAutoConfiguration.class,
		org.activiti.spring.boot.SecurityAutoConfiguration.class
		})

Activiti 的 BPMN 规范

缺失,待补

Activiti 使用过程中遇到的主要问题

oK 集成完毕后,博主主要解决了以下问题,在此记录分享:

要是博主一开始就是用的 activiti-7.0.0.RC 该有多好,就没有这么多破事了!

  1. 怎样优雅的和业务集成
  2. 怎样优雅的赋值任务候选人
    • 如何创建7.0中才有的 globalTaskListener
    • 如何使用监听器动态赋值 任务候选人
  3. 怎样在 Activiti5.22.0 中使用 BusinessKey
    • 如何给子任务 businessKey 赋值
    • 在 activiti 首个事务提交前,如何获得 businessKey
    • 在 TimerTask 中如何获得 businessKey
1. 怎样优雅的和业务集成
  1. 给 activiti 中的手工任务添加 ID(completed)
    completed
  2. 为每个手工处理任务创建同名的处理 bean
    completed 处理bean
  3. 构建简单 bean 工厂,将这些处理 bean 缓存 userTaskServiceMap 里头
    缓存tskContext简单工厂中
  4. 通过 taskDefKey 从 TskContext 工厂中动态获得当前手工任务处理类
    环节
2. 怎样优雅的赋值任务候选人
2.1. 如何创建7.0中才有的 globalTaskListener
  1. globalTaskListener 监听器
@Component("globalTaskListener")
public class MyExecutionListener implements ExecutionListener,TaskListener {
	@Override
	public void notify(DelegateTask delegateTask) {
		String eventName = delegateTask.getEventName();
		if (BizTskConstants.TASK_EVENT_CREATE.endsWith(eventName)) {
		}else if (BizTskConstants.TASK_EVENT_ASSIGNMENT.endsWith(eventName)) {
//			System.out.println("assignment========++++++++++++++");
		}else if (BizTskConstants.TASK_EVENT_COMPLETE.endsWith(eventName)) {
			System.out.println("complete===========++++++++++++++");
		}else if (BizTskConstants.TASK_EVENT_DELETE.endsWith(eventName)) {
			System.out.println("delete=============++++++++++++++");
		}
	}
	@Override
	public void notify(DelegateExecution exec) throws Exception {
//		String activityId = execution.getCurrentActivityId();
//		 String processDefinitionId = execution.getProcessDefinitionId (); // Get the process definition id 
// 
//		ProcessDefinitionEntity processDefinitionEntity=(ProcessDefinitionEntity) execution.getEngineServices().getRepositoryService()
//				.getProcessDefinition(processDefinitionId); 
 
//		 ActivityImpl activityImpl = processDefinitionEntity.findActivity (activityId); // Get Events The event instance id 
//		TaskDefinition taskDef = (TaskDefinition)activityImpl.getProperties().get("taskDefinition");
//		 String zpr = taskDef.getAssigneeExpression () == null ? "": taskDef.getAssigneeExpression().getExpressionText (); // agents.
//		 Set <Expression> userCodes = taskDef.getCandidateUserIdExpressions (); // candidates
//		 Set <Expression> roleCodes = taskDef.getCandidateGroupIdExpressions (); // candidate set
		 // Get the information end approver
		 //start
		 String eventName = exec.getEventName();
		if (BizTskConstants.EXECUTION_EVENT_START.equals(eventName)) {
		}else if (BizTskConstants.EXECUTION_EVENT_END.equals(eventName)) {
			//System.out.println("end=========++++++++++++++");
		}
		else if (BizTskConstants.EXECUTION_EVENT_TAKE.equals(eventName)) {
			//System.out.println("take=========++++++++++++++");
		}
	}
}
  1. 通过DelegateBPMNParserHandler 注册 globalTaskListener
public class DelegateBPMNParserHandler extends UserTaskParseHandler {
    private static Logger logger = LoggerFactory
            .getLogger(DelegateBPMNParserHandler.class);

    protected void executeParse(BpmnParse bpmnParse, UserTask userTask) {
        logger.debug("bpmnParse : {}, userTask : {}", bpmnParse, userTask);
        super.executeParse(bpmnParse, userTask);
        
        TaskDefinition taskDefinition = (TaskDefinition) bpmnParse
                .getCurrentActivity().getProperty(PROPERTY_TASK_DEFINITION);
        
        ActivitiListener activitiListener = new ActivitiListener();
        activitiListener.setEvent(TaskListener.EVENTNAME_CREATE);
        activitiListener.setEvent(ExecutionListener.EVENTNAME_START);
        activitiListener
                .setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        activitiListener.setImplementation("#{globalTaskListener}");
        
        taskDefinition
                .addTaskListener(TaskListener.EVENTNAME_CREATE, bpmnParse
                        .getListenerFactory()
                        .createDelegateExpressionTaskListener(activitiListener));
        
        taskDefinition
        .addTaskListener(ExecutionListener.EVENTNAME_START, bpmnParse
                .getListenerFactory()
                .createDelegateExpressionTaskListener(activitiListener));
        
    }
}
  1. 将 DelegateBPMNParserHandler 注册给 activiti 的 processEngine
 // 创建全局 globalTaskListeners
 List<BpmnParseHandler> taskListeners = new ArrayList<BpmnParseHandler>();
 taskListeners.add(new DelegateBPMNParserHandler());
 processEngineConfiguration.setCustomDefaultBpmnParseHandlers(taskListeners);
2.2. 如何使用监听器动态赋值 任务候选人
  1. 在 2.1 中注册的 globalTaskListener 中 有 3 个事件 create、assignment、complete、delete (😒1,2,3……)我们选择在 assignment 之前的 create 中添加任务候选人
// 如果是多实例任务
if (tskService.isMultiInst()) {
	Object subUser = delegateTask.getVariable(BizTskConstants.CONFIRM_MULTIINST_USER);
	if (subUser != null)
    	taskService.addCandidateUser(delegateTask.getId(), subUser.toString());
	}
}
// 添加审核候选人
if (!tskService.isMultiInst()) {
	for (String auditor : auditors)
	{
		taskService.addCandidateUser(delegateTask.getId(), auditor);
	}
}
  1. auditors 哪儿来的?你不是每个手工类里面都创建了一个 bean 来处理么?让这个 bean 来处理啊!
    获得任务执行者
怎样在 Activiti5.22.0 中使用 BusinessKey
  1. 那么有个问题,工作流怎么和业务表挂钩呢?使用 businessKey,但是
    • 工作流一旦复杂起来,子流程啊,调用啊,事件触发啊,事件触发啊,都不会自动继承 businessKey
    • 工作流是每个异步任务一个事务的,也就是说,在 runtimeService.startProcessInstanceById 的时候如果立即就有需要处理的任务的话,由于工作流还没有提交,只能通过缓存自行传递 businessKey 的
    • 如果是 timmerTask 异步定时任务,也会有获取不到 businessKey 的情况发生
如何给子任务 businessKey 赋值
  1. 一般的,我们使用 BusinessKeyInjectionActivitiEventListener 来解决 businessKey 不传递给子流程的问题
public class BusinessKeyInjectionActivitiEventListener implements ActivitiEventListener {
	
    private Logger log = LoggerFactory.getLogger(BusinessKeyInjectionActivitiEventListener.class);
    
    @Override
    public void onEvent(ActivitiEvent event) {
    	switch (event.getType()) {
    		case PROCESS_STARTED:
	    		if (event instanceof ActivitiProcessStartedEvent) {
	    			ActivitiProcessStartedEvent processStartedEvent = (ActivitiProcessStartedEvent) event;
	    			ExecutionEntity execEntity = (ExecutionEntity) processStartedEvent.getEntity();
	    			if (StringUtils.isEmpty(execEntity.getProcessBusinessKey())) {
	    				String key = getSuperProcessInstanceBusinessKey(execEntity);
	    				if (StringUtils.isNoneEmpty(key)) {
	    					execEntity.setBusinessKey(key);
	    					execEntity.updateProcessBusinessKey(key);
	    				}
	    			}
	    		}
	    		break;
            case TASK_CREATED:
                if (event instanceof ActivitiEntityEvent) {
                    ActivitiEntityEvent activityEntityEvent = (ActivitiEntityEvent) event;
 
                    TaskEntity taskEntity = (TaskEntity) activityEntityEvent.getEntity();
                    ExecutionEntity exEntity = taskEntity.getExecution();
                    
                    String key = exEntity.getBusinessKey();
                    log.debug("获取当前任务的流程实例的businessKey:{}",key);
                    if(StringUtils.isEmpty(key)){
                    	key = getSuperProcessInstanceBusinessKey(exEntity);
                        // 项目首个事务还未完成,从线程中取得
                        if (StringUtils.isEmpty(key)) {
                        	key = MyExecutionListener.bussinessKey.get();
                        }
                        log.debug("获取当前任务 上一个流程实例的businessKey:{}",key);
                        log.debug("设置当前流程实例的businessKey:{}",key);
                        exEntity.setBusinessKey(key);
                        //让businessKey生效 此处非常关键。
                        exEntity.updateProcessBusinessKey(key);
                    }
                    break;
                }
            default:
                break;
        }
    }
在 activiti 首个事务提交前,如何获得 businessKey
  1. 大爷们可能会问,你,你的 MyExecutionListener.bussinessKey.get(); 是个什么鬼
// 前面讲到,工作流是遇到异步任务才会提交事务的
public static ThreadLocal<String> bussinessKey = new ThreadLocal<String>();
// 所以呢,在事务提交前,只能将 businessKey 缓存到业务里头洛
MyExecutionListener.bussinessKey.set(business.getId() + "");
bizBusinessService.startProcess(business, variables);
MyExecutionListener.bussinessKey.remove();

更正:使用 ThreadLocal 来传递 businessKey 是不需要的,只需要在 startProcess 的外层套一层事务,这样,让工作流和当前业务处理处于同一个事务中,就能查询到未提交的数据啦

在 TimerTask 中如何获得 businessKey
  1. 那你那个 getSuperProcessInstanceBusinessKey 又是什么鬼,这个是解决 TimerTask 中死活获取不到 businessKey 的问题的
/**
 * 从父流程中找 businessKey
 * @param exEntity
 * @return
 */
private String getSuperProcessInstanceBusinessKey(ExecutionEntity exEntity) {
    if (exEntity != null && StringUtils.isNotEmpty(exEntity.getBusinessKey())) {
    	return exEntity.getBusinessKey();
    } else if (exEntity != null && exEntity.getSuperExecution() != null) {
    	return getSuperProcessInstanceBusinessKey(exEntity.getSuperExecution());
    } else if (exEntity != null && exEntity.getParent() != null) {
    	return getSuperProcessInstanceBusinessKey(exEntity.getParent());
    }
    return null;
}

当前,升级 activiti 到 7.0 就没有以上这些弯弯绕绕啦!😁 源代码ruoyiact-vue

Logo

快速构建 Web 应用程序

更多推荐