前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >工作流引擎之activiti入门

工作流引擎之activiti入门

作者头像
lyb-geek
发布2018-07-26 10:02:08
5.5K0
发布2018-07-26 10:02:08
举报
文章被收录于专栏:Linyb极客之路Linyb极客之路

1.什么是Activiti

在解释activiti之前我们看一下什么是工作流。 工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。 我的理解是,工作流将一套大的业务逻辑分解成业务逻辑段, 并统一控制这些业务逻辑段的执行条件,执行顺序以及相互通信。 实现业务逻辑的分解和解耦。 Activiti是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调度。 BPMN即业务流程建模与标注(Business Process Model and Notation,BPMN) ,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)。

BPMN的流程图长这样子

activiti5.13使用了23张表支持整个工作流框架,底层使用mybatis操作数据库。这些数据库表为

1)ACT_RE_*: 'RE'表示repository。 这个前缀的表包含了流程定义相关的静态资源(图片,规则等)。 2)ACT_RU_*: 'RU'表示runtime。 运行时表,包含流程实例,任务,变量,异步任务等运行中的数据。流程结束时这些记录会被删除。 3)ACT_ID_*: 'ID'表示identity。 这些表包含用户和组的信息。 4)ACT_HI_*: 'HI'表示history。 这些表包含历史数据,比如历史流程实例,变量,任务等。 5)ACT_GE_*: 通用数据,bytearray表保存文件等字节流对象。

工作流进行的基本过程如下: 定义流程(框架外) -> 部署流程定义 -> 启动流程实例, 框架移动到任务1 -> 拾取组任务 -> 办理个人任务, 框架移动到任务2 -> 拾取组任务 -> 办理个人任务...

组任务是多个用户都可以完成的任务。没有组任务直接办理个人任务; 有组任务需先通过拾取将组任务变成个人任务, 然后再办理。

个人任务/组任务在表中的区别

个人任务: 表act_ru_task的ASSIGNEE段即指定的办理人

组任务: 表act_ru_task的ASSIGNEE段为null, 相关信息在表act_ru_identitylink中, 组任务1见userid段; 组任务2见groupid段, 当然还需查询act_id_xxx表才能精确到人.

2.Activiti的使用

2.1 创建processEngine

processEngine控制着工作流整个流程

代码语言:javascript
复制
public class processEngine {
    @Test
    public void createProcessEngine1() {
        String resource = "activiti-context.xml";    // 配置文件
        String beanName = "processEngineConfiguration";  // 配置文件中bean name
        // 从配置文件创建配置对象
        ProcessEngineConfiguration config = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(resource, beanName);
        // 根据配置创建引擎对象
        ProcessEngine processEngine = config.buildProcessEngine();
    }

    /**
     *  一条语句创建processEngine, 要求:
     * 1、配置文件必须在classpath根目录下
     * 2、配置文件名必须为activiti-context.xml或activiti.cfg.xml
     * 3、工厂对象的id必须为processEngine
     */
    @Test
    public void createProcessEngine2() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    }
}
代码语言:javascript
复制
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    <!-- 配置 -->
    <bean id="processEngineConfiguration"
         class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcDriver"  value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl"  value="jdbc:mysql:///test_activiti"/>
        <property name="jdbcUsername"  value="root"/>
        <property name="jdbcPassword"  value="root"/>
        <!-- 创建processEngine时, activiti自动创建23张表 -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
    <!-- 使用配置创建引擎对象 -->
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
    </bean>
</beans>

当然, 可以与spring进一步整合, 使用spring方式获取processEngine. applicationContext.xml如下

代码语言:javascript
复制
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql:///activiti_day2" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 流程引擎配置对象 -->
    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 注入事务管理器对象 -->
        <property name="transactionManager" ref="transactionManager"/>
        <property name="databaseSchemaUpdate" value="true" />
    </bean>
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration" />
    </bean>
</beans>

2.2 部署流程定义

流程是由用户通过bpmn等文件(底层xml)定义的, 即上面列举的的bpmn流程图

定义好的流程需要部署给activiti才能被其使用

代码语言:javascript
复制
/**
     * 部署流程定义 
     * 一套定义文件只有一个流程定义Key, 但可以被部署多次形成多个版本(部署表里多个id和流程定义表里多个id)
     * 涉及的表:act_re_deployment(部署表)、act_re_procdef(流程定义表)、act_ge_bytearray(二进制表)
     */
    @Test
    public void test() throws FileNotFoundException {
        DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
        // 逐个文件部署
        // deploymentBuilder.addClasspathResource("qjlc.bpmn");
        // deploymentBuilder.addClasspathResource("qjlc.png");
        // 压缩文件打包部署, 推荐
        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(new File("d:\\processDef.zip")));
        deploymentBuilder.addZipInputStream(zipInputStream );

        Deployment deployment = deploymentBuilder.deploy();
    }

2.3 启动流程实例

代码语言:javascript
复制
/**
     * 启动一个流程实例
     * 涉及的表:
     * act_ru_execution(流程实例表), 管理流程进度
     * act_ru_task(任务表), 进行到哪一个流程的哪一个任务, 该由谁完成
     */
    @Test
    public void test() throws Exception{
        String processDefinitionKey = "qjlc";
        //方式一:根据流程定义id启动流程实例
        //String processDefinitionId = "qjlc:6:904";
        //ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById(processDefinitionId);

        //方式二:根据流程定义Key启动流程实例   推荐!流程定义有多个版本时会选择最新版本
        ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey);
    }

2.4 办理任务

代码语言:javascript
复制
/**
    * 办理任务, 办理后框架自动移动到下一任务
    * 涉及的表: act_ru_execution(流程实例表)、act_ru_task(任务表)
    */
    @Test
    public void test() throws Exception{
        String taskId = "1304";
        processEngine.getTaskService().complete(taskId);
    }

2.5 其他操作

代码语言:javascript
复制
/**
    * 查询流程定义
    * 涉及的表:act_re_procdef
    */
    @Test
    public void test(){
        ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
        // 查询条件过滤
        query.processDefinitionKey("qjlc");
        query.orderByProcessDefinitionVersion().asc();
        List<ProcessDefinition> list = query.listPage(0, 10);
        for (ProcessDefinition processDefinition : list) {
            System.out.println(processDefinition.getId());
        }
    }

activiti中查询的套路: processEngine.getXXXService().createXXXQuery().list()/singleResult() processEngine.getRepositoryService().createDeploymentQuery().list(); // 查询部署 processEngine.getRuntimeService().createProcessInstanceQuery().list(); // 查询流程实例 processEngine.getTaskService().createTaskQuery().list(); // 查询个人任务 processEngine.getIdentityService().createUserQuery().list(); // 查询用户 processEngine.getHistoryService().createHistoricActivityInstanceQuery().list(); //查询历史 过滤条件 查询个人任务 query.taskAssignee() 查询组任务 query.taskCandidate()

几个javabean(和表对应): Deployment------act_re_deployment ProcessDefinition-----act_re_procdef ProcessInstance------act_ru_execution Task-----act_ru_task 几个Query对象: DeploymentQuery------act_re_deployment ProcessDefinitionQuery-----act_re_procdef ProcessInstanceQuery------act_ru_execution TaskQuery-----act_ru_task 几个Service: RepositoryService----操作部署表、流程定义表等静态资源信息表 RuntimeService----操作流程实例表、任务表等动态信息表 TaskService-----操作任务表 HistoryService----操作历史表 IdentityService----操作用户表、组表、关系表

代码语言:javascript
复制
// 删除流程定义
    @Test
    public void test1(){
        String deploymentId = "101";  //部署id
        boolean cascade = false;  // 级联删除, 设置为true的话, 有正在跑的流程实例及任务也会被删除
        processEngine.getRepositoryService().deleteDeployment(deploymentId, cascade);
    }
    // 删除流程实例
    @Test
    public void test2() throws Exception{
        String processInstanceId = "1201";
        String deleteReason = "不请假了";  // 可以添加删除原因
        processEngine.getRuntimeService().deleteProcessInstance(processInstanceId, deleteReason);
    }
  // 根据部署id, 获取定义文件
    @Test
    public void test3() throws Exception{
        String deploymentId = "201"; //部署id
        // 先获得定义文件的名字
        List<String> names = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
        for (String name : names) {
            InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, name);
            FileUtils.copyInputStreamToFile(in, new File("d:\\"+name));
            in.close();
        }
    }
    // 根据流程定义id, 获取定义文件
    @Test
    public void test4() throws Exception{
        String processDefinitionId = "qjlc:6:904"; //流程定义id
        InputStream pngStream = processEngine.getRepositoryService().getProcessDiagram(processDefinitionId);
        FileUtils.copyInputStreamToFile(pngStream, new File("d:\\abc.png"));
    }

通过javabean能访问到某些需要的字段, 例如

processInstance.getActivityId() -> 当前执行的任务名

processDefinition.getDiagramResourceName() -> 定义文件中图片的名字

2.6 流程变量

多个任务间可以通过流程变量通信.

流程变量以key-value形式存放, 存于表 act_ru_variable. 在同一流程实例里, 不同方式设置变量, key相同时会覆盖

代码语言:javascript
复制
// 启动流程实例时 设置流程变量
    @Test
    public void test1() {
        String processDefinitionKey = "bxlc";
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("key", "value");
        ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey, variables);
    }
    // 办理任务时 设置流程变量, 更实用!
    @Test
    public void test2() {
        String taskId = "206";
        Map<String, Object> variables = new HashMap<>();
        variables.put("key", "value");
        processEngine.getTaskService().complete(taskId, variables);
    }
    // 通过RuntimeService 设置流程变量
    @Test
    public void test3() {
        String executionId = "201"; // 流程实例id
        Map<String, Object> variables = new HashMap<>();
        variables.put("key", "value");
        //processEngine.getRuntimeService().setVariable(executionId, variableName, value);
        processEngine.getRuntimeService().setVariables(executionId, variables);
    }
    // 通过TaskService 设置流程变量
    @Test
    public void test4() {
        String taskId = "304";
        String key = "key";
        Object value = "value";
        processEngine.getTaskService().setVariable(taskId , key, value);
    }
代码语言:javascript
复制
// 通过RuntimeService 获取流程变量
    @Test
    public void test5() {
        String executionId = "201";
        Object value = processEngine.getTaskService().getVariable(executionId, "user");
        System.out.println(value);
    }
     // 通过TaskService 获取流程变量
    @Test
    public void test6() {
        String taskId = "304";
        Object value = processEngine.getTaskService().getVariable(taskId, "user");
        System.out.println(value);
    }

流程变量还可以通过在定义流程用表达式${}. 框架在该段任务执行前从act_ru_variable表里动态获取

另外, 启动流程实例还有一个重载函数, 除了流程变量variables还能指定业务主键businessKey

processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey, businessKey, variables);

businessKey一般设置为业务表的主键值, 在使用activiti的时候, 通过查询业务表主键, 能方便地查询出业务的最新状态

2.7 组任务

组任务1

代码语言:javascript
复制
// 查询组任务
    @Test
    public void test1() {
        TaskQuery query = processEngine.getTaskService().createTaskQuery();
        // 使用候选人查询组任务
        String candidateUser = "财务二";
        query.taskCandidateUser(candidateUser);
        List<Task> list = query.list();
        for (Task task : list) {
            System.out.println(task.getId());
        }
    }
    // 拾取组任务
    @Test
    public void test2() {
        String taskId = "1102";
        processEngine.getTaskService().claim(taskId , "财务二");
    }
    // 办理组任务, 无需指定办理人
    @Test
    public void test3() throws Exception{
        String taskId = "1102";
        processEngine.getTaskService().complete(taskId);
    }

组任务2

代码语言:javascript
复制
// activiti使用自己的用户与组的权限表, 因此需要设置. 但需注意要与框架外用户/组同步设置
    @Test
    public void test2() {
        // 创建组
        Group group = new GroupEntity();
        group.setId("财务组");
        processEngine.getIdentityService().saveGroup(group);
        // 创建用户
        User user = new UserEntity();
        user.setId("2");
        processEngine.getIdentityService().saveUser(user);
        // 维护用户与组的关系
        processEngine.getIdentityService().createMembership("2", "财务组");
    }
    // 查询组任务
    @Test
    public void test2() {
        TaskQuery query = processEngine.getTaskService().createTaskQuery();
        String candidateUser = "2";
        // 使用候选人过滤
        query.taskCandidateUser(candidateUser);
        // 使用组过滤
        //query.taskCandidateGroup("财务组");
        List<Task> list = query.list();
        for (Task task : list) {
            System.out.println(task.getId());
        }
    }
    // 拾取组任务
    @Test
    public void test3() {
        String taskId = "1902";
        processEngine.getTaskService().claim(taskId , "2");
    }
    // 办理组任务略

2.8 排他网关

设置分支条件

3. 一些使用经验

1)

考虑到工作流中的一个任务, 对应一个业务段, 可以将taskDefinitionKey设置成strus action类的method, 使之具有一定的通用性

2)

两种对流程定义的查询, 后者能获得更多定义的细节信息 processDefinitionEntity.findActivity(taskId) 工作流中某任务的信息

repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult()

(ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linyb极客之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.什么是Activiti
  • 2.Activiti的使用
    • 2.1 创建processEngine
      • 2.2 部署流程定义
        • 2.3 启动流程实例
          • 2.4 办理任务
            • 2.5 其他操作
              • 2.6 流程变量
                • 2.7 组任务
                  • 2.8 排他网关
                  • 3. 一些使用经验
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档