design\project\学习Activiti工作流
Activiti工作流5.22集成心得与 Spring 集成解决 Activiti 子流程 BusinessKey 问题与 Spring 集成用的现成的集成好的 activiti-5.22.0 ruoyi-activiti使用了大神已经已经集成好了的栗子删除了 tk-mapper 依赖,个人强迫症,喜欢光速启动由于博主开发的是传统单一应用,删除了 eurika 依赖,更换 pom个人将前端换成了
·
Activiti工作流5.22集成心得
与 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 该有多好,就没有这么多破事了!
- 怎样优雅的和业务集成
- 怎样优雅的赋值任务候选人
- 如何创建7.0中才有的 globalTaskListener
- 如何使用监听器动态赋值 任务候选人
- 怎样在 Activiti5.22.0 中使用 BusinessKey
- 如何给子任务 businessKey 赋值
- 在 activiti 首个事务提交前,如何获得 businessKey
- 在 TimerTask 中如何获得 businessKey
1. 怎样优雅的和业务集成
- 给 activiti 中的手工任务添加 ID(completed)
- 为每个手工处理任务创建同名的处理 bean
- 构建简单 bean 工厂,将这些处理 bean 缓存 userTaskServiceMap 里头
- 通过 taskDefKey 从 TskContext 工厂中动态获得当前手工任务处理类
2. 怎样优雅的赋值任务候选人
2.1. 如何创建7.0中才有的 globalTaskListener
- 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=========++++++++++++++");
}
}
}
- 通过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));
}
}
- 将 DelegateBPMNParserHandler 注册给 activiti 的 processEngine
// 创建全局 globalTaskListeners
List<BpmnParseHandler> taskListeners = new ArrayList<BpmnParseHandler>();
taskListeners.add(new DelegateBPMNParserHandler());
processEngineConfiguration.setCustomDefaultBpmnParseHandlers(taskListeners);
2.2. 如何使用监听器动态赋值 任务候选人
- 在 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);
}
}
- auditors 哪儿来的?你不是每个手工类里面都创建了一个 bean 来处理么?让这个 bean 来处理啊!
怎样在 Activiti5.22.0 中使用 BusinessKey
- 那么有个问题,工作流怎么和业务表挂钩呢?使用 businessKey,但是
- 工作流一旦复杂起来,子流程啊,调用啊,事件触发啊,事件触发啊,都不会自动继承 businessKey
- 工作流是每个异步任务一个事务的,也就是说,在
runtimeService.startProcessInstanceById
的时候如果立即就有需要处理的任务的话,由于工作流还没有提交,只能通过缓存自行传递 businessKey 的 - 如果是 timmerTask 异步定时任务,也会有获取不到 businessKey 的情况发生
如何给子任务 businessKey 赋值
- 一般的,我们使用
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
- 大爷们可能会问,你,你的
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
- 那你那个
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
更多推荐
已为社区贡献1条内容
所有评论(0)