最近在调查activiti工作流的事。项目上打算做一个工作流服务,给微服务中的一些需要流程定义的服务用。所以我尝试用spring boot集成activiti。以下是我调查中遇到的一些问题和一些调查到的结果。
activity cloud 不知道怎么使用,但是看官方解释比较适合云服务(spring cloud)便于扩展,但是activity cloud 貌似是云平台。因为不会搭建,所以就放弃了。activity7 与 activity6 相比,7 多了processruntime与taskruntime 两个类。这两个类需要与security 一起使用,官方里对于TaskRuntime API 有这样一段话:
Something important to notice here, is that in order to interact with the TaskRuntime API as a user, you need to have the role: ACTIVITI_USER (Granted Authority: ROLE_ACTIVITI_USER) .
因为我项目还没和spring security 集成起来,所以就只好不用这两个类了。我的做法是用activity7 的core,但是还是用 6.0使用方式。
继承方式很简单,在pom里引入就OK了
<activiti-dependencies.version>7.0.0.SR1</activiti-dependencies.version> ... ... <!--activiti starter--> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> </dependency> <!-- Activiti生成流程图 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-image-generator</artifactId> </dependency>
首先是基本25张表
ACT_GE_* : “GE”代表“General”(通用)
ACT_HI_* : “HI”代表“History”(历史) 这些表中保存的都是历史数据,比如执行过的流程实例、变量、任务,等等
ACT_ID_* : “ID”代表“Identity”(身份)这些表中保存的都是身份信息,如用户和组以及两者之间的关系。如果Activiti被集成在某一系统当中的话,这些表可以不用,可以直接使用现有系统中的用户或组信息
ACT_RE_* : “RE”代表“Repository”(仓库) 这些表中保存一些‘静态’信息,如流程定义和流程资源(如图片、规则等)
ACT_RU_* : “RU”代表“Runtime”(运行时)这些表中保存一些流程实例、用户任务、变量等的运行时数据。Activiti只保存流程实例在执行过程中的运行时数据,并且当流程结束后会立即移除这些数据,这是为了保证运行时表尽量的小并运行的足够快;
其中 history-level: full 这个参数关系着是否会生成 ACT_HI_* 的系列表。 ACT_ID_* 这个系列的表在7里已经没有了。
none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
参照: activiti学习笔记(十八) History其次,如果使用的是mysql数据库,在通一个mysql服务器上不能有两个用于activiti的基础数据库。例如,数据库A已经有一套表结构了,如果打算将schema 切换到b想再生成一套新的表结构,这样是会生成失败的。下面连接里楼主说的就是这种情况,我debug代码追踪的结果也和楼主讲的一样,只要删除掉其中一个,就能正常运行了。 参照:一个MySql实例自动创建多个Activiti数据库问题
如果是想在yml下配置,用这样的方式就能正常生成表 并启动。
spring: activiti: db-history-used: true database-schema: activity #指定数据库schema history-level: full async-executor-activate: true #开启异步,定时任务 database-schema-update: true
关于 database-schema-update:
flase: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常。
true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建。
create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。
drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)。
如果是想用bean自己配置,参考以下方式。 我第一次用bean配置的时候,出现了表无法生成的情况。一开始没有使用SpringProcessEngineConfiguration,并且调用configuration.buildProcessEngine() 生成ProcessEngine,不知道为什么就是无法将HistoryLevel的值设进去,后来修改了写法后就成功了。
@Bean public SpringProcessEngineConfiguration processEngineConfiguration(DataSource dataSource, PlatformTransactionManager manager){ SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration(); configuration.setDataSource(dataSource); configuration.setTransactionManager(manager); configuration.setAsyncExecutorActivate(true); configuration.setHistoryLevel(HistoryLevel.FULL); configuration.setDatabaseSchema("activity"); configuration.setDbHistoryUsed(true); configuration.setDatabaseSchemaUpdate(DB_SCHEMA_UPDATE_TRUE); configuration.setEnableDatabaseEventLogging(true); //是否开启dblog return configuration; }
实际上在7里, IdentifyService, FormService 已经没有了。基本上剩下的service对应的就是数据库里每个类别的表的操作。
使用serviceTask,通过实现JavaDelegate 来实现调用外部restful接口
<serviceTask id="vacation_service" name="假期处理" activiti:delegateExpression="${jumpService}"> <extensionElements> <activiti:field name="restUrl"> <activiti:string><![CDATA[http://stores/decreaseDay]]></activiti:string> </activiti:field> <activiti:field name="param"> <activiti:string><![CDATA[day,businessKey,action]]></activiti:string> </activiti:field> </extensionElements> </serviceTask>
@Service public class JumpService implements JavaDelegate { private final static Logger logger = LoggerFactory.getLogger(JobServiceImpl.class); //restTemplate 配置了ribbon @Autowired RestTemplate restTemplate; //调用地址 private Expression restUrl; // 参数 private Expression param; public void setRestUrl(Expression restUrl) { this.restUrl = restUrl; } public void setParam(Expression param) { this.param = param; } @Override public void execute(DelegateExecution delegateExecution) { //取得从xml中传过来的参数列表 String params = (String) param.getValue(delegateExecution); //取得从xml中传过来的地址 String strUrl = (String) restUrl.getValue(delegateExecution); //TODO:通过调用restTemplate来调用其他微服务接口,或者暴露的restful接口 } }
参照:activiti6.x调用RESTful服务例子
如果要使用groovy 脚本,需要引入jar包到pom中
<dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.5.7</version> <type>pom</type> </dependency>
编写一个获取spring bean的工具类
@Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; public static Object getBean(String name){ return applicationContext.getBean(name); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } }
然后就可以在脚本中引入工具类后,在脚本中使用这些bean来完成业务了
<scriptTask id="cancel_claim" name="取消认领" scriptFormat="groovy"> <script> import org.cango.activity.util.SpringContextUtil import org.activiti.engine.TaskService import org.activiti.engine.task.Task def taskService = SpringContextUtil.getBean("taskServiceBean"); def task = taskService.createTaskQuery().taskId(taskId).singleResult(); if (task == null) { println("审核任务ID:"+ taskId+"查询到任务为空!"); }else if(task.getAssignee()){ taskService.unclaim(taskId); println("审核任务ID:"+ taskId+"取消认领完成"); }else{ println("审核任务ID:"+ taskId+"未被认领"); } </script> </scriptTask>
值得注意的是,生成activiti的五大类的bean的地方是在 AbstractProcessEngineConfiguration 这个类中。所以向要使用什么类,在这个类中查看具体的类名。
以上就是到目前为止的调查结果。如果还有新的内容会在后面更新~
附件参照: activiti 7 官方说明文档 、 activiti 6 官方使用文档