乐趣区

activiti-工作流关于acitiviti-和-spring-boot-集成遇到的一些问题总结

最近在调查 activiti 工作流的事。项目上打算做一个工作流服务,给微服务中的一些需要流程定义的服务用。所以我尝试用 spring boot 集成 activiti。以下是我调查中遇到的一些问题和一些调查到的结果。

1. activity7 与 activity6 与 activiti cloud

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>

2. activity 数据表遇到的问题

首先是基本 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 数据库问题

3. activity 与 spring boot 集成时遇到的问题

如果是想在 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;
    }

4. activity 使用时遇到的问题

  • 七大 service 接口


实际上在 7 里,IdentifyService, FormService 已经没有了。基本上剩下的 service 对应的就是数据库里每个类别的表的操作。

  • 如何调用 restful 接口(其他微服务接口)

使用 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 服务例子

  • 关于 scriptTask 脚本任务的使用

如果要使用 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 官方使用文档

退出移动版