Activiti5 工作流入门教程

最近把Activiti给研究了一下,因此想写一下有关工作流方面的知识,把自己的学习心得也给分享一下,省的在学习的道路上,走很多弯路。

准备工作:

activiti5 软件环境

1)  JDK1.6或者更高版本

2)  数据库, mysql, oracle等

3)  支持activiti5运行的jar包  http://activiti.org/download.html

4)  开发工具intellij idea 2018 (eclipse也可以)

 

初始化数据库操作

下面就来初始化数据库,等会就可以看到经常用的23张数据库表了。

  public static void main(String[] args) throws Exception {

        ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
        //定义连接mysql数据库
        configuration.setJdbcDriver("com.mysql.jdbc.Driver");
        configuration.setJdbcUrl("jdbc:mysql://localhost:3306/test2?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8");
        configuration.setJdbcUsername("root");
        configuration.setJdbcPassword("mysql@@123");

       /*  public static final String DB_SCHEMA_UPDATE_FALSE = "false";操作activiti23张表的时候,如果表不存在,就抛出异常,不能自动创建23张表
         public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop";每次操作,都会先删除表,再创建表
         public static final String DB_SCHEMA_UPDATE_TRUE = "true";如果表不存在,就创建表,如果表存在,就直接操作		*/
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        //activiti核心对象(流程引擎)
        ProcessEngine processEngine = configuration.buildProcessEngine();
        System.out.println("processEngine:"+processEngine);

    }

会根据你指定的数据库名称创建表(若指定名称的数据库不存在会自动创建该数据库,原本存在的表不会消失)。

新创建的数据库表如下图所示:

数据库结构解析:

学习Activiti主要是学习自带的23张数据表,那么这篇博客就来分析一下这23张表,下面是分类。

与部署对象和流程定义相关的表

act_ge_property:用来生成下一个主键信息

act_re_deployment:部署数据表,一次部署可以添加多个资源,资源会被保存到资源表(act_ge_bytearray)中;而部署的信息,则保存到部署表中。

act_re_procdef:流程定义表,如果发布部署的文件是流程文件,除了将内容保存到资源表外,还会解析流程文件的内容,形成特定的流程定义数据,保存到此表中。

act_ge_bytearray:资源表,用来保存资源相应的信息

与流程实例,执行对象,任务相关的表

act_ru_execution:(下面的几个表都是以该表的id为外键关联)正在执行的流程实例表,当流程启动后,会产生一个流程实例,同时会产生相应的执行流,那么流程实例和执行流数据均会被保存到act_ru_execution表中。

act_hi_procinst:流程实例从开始到结束所经历的时间,与act_ru_execution对应。

act_hi_actinst:所有活动的历史表(包括所有的节点),与act_ru_execution对应。

act_ru_task:正在执行的任务列表(只有正在执行的任务,该表中才有数据),与act_ru_execution对应。

与流程变量相关的表

act_ru_variable: 正在执行的流程变量表,用来保存在整个流程执行过程中用到的变量信息

act_hi_varinst :历史的流程变量表,与act_ru_variable正好对应

与组任务表相关的表

act_ru_identitylink :正在执行的组任务表 

act_hi_identitylink:历史的人员表

与组织结构相关的表  

act_id_group :工作流中的角色表  

act_id_user :工作流中的用户表  

act_id_membership:中间表,关联关系表


下面就通一个例子,来把我们上述的表给串起来:  

首先是部署和流程相关方面的描述,从开始到结束是一个完整的流程,所以就是所谓的一次部署act_re_deployment

然后在整个流程中,我们可能会涉及到多个流程的文件,此时我们会把文件存储到act_ge_bytearray表中,

然后把每个文件的详细信息存储到act_re_procdef中。

部署完流程完后,就要开始执行我们的流程,从开始节点算起,每个节点都是一个流程实例,正在执行的会存储到act_ru_execution,如图,我们执行了一个ID为727503BUSINESS_KEY_为 910的流程:

执行完后,此表就会删除该条数据,正在执行/执行完毕的历史记录将存储到 act_hi_procinst

如下图BUSINESS_KEY_=909的流程已经执行结束,910的流程正在执行中(909在上面的act_ru_execution表中已经不存在)

中间涉及到的用户任务节点,也就是UserTask节点,正在执行的节点会存储到act_ru_task中

节点对应的user_id存在`act_ru_identitylink`中,这样就能绑定到流程对应的人了。

历史节点存储到act_hi_actinst

 

如果在流程中间执行中,涉及到变量的传递,比如我们想把流程的执行者,作为变量来传递的话,就会存储到act_ru_variableact_hi_varinst中。

 

act_ru_variable表:

用java代码构建数据,map的key值与流程图中的${teachers}对应,启动流程时将map传入

各节点对应的数据(人员的工号)

最后,在整个流程执行过程中,若设计到人员的操作信息,存储到act_id_user 中,如果设计到组的概念的话,那么某个用户是属于某个组的,就好比工作里面的部门一样,某些职责只能某些部门来执行,因此在Activi中也涉及到了组的概念,存储到act_id_group ,他们之间的联系用第三张表act_id_membership来关联。
 

实战demo

首先用idea的actiBPM插件制作一个请假流程的second_approve.bpmn20.bpmn 文件

然后将bpmn文件复制一份,将文件名后缀改为xml格式

接下来,我们写一个测试代码来执行这个流程。

先配置pom文件,引入所需要的jar包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.lucasma.activiti</groupId>
    <artifactId>activiti6-helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.16</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>6.0.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>20.0</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.176</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.11</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

java代码

package cn.lucasma.activiti.helloworld;


import com.google.common.collect.Maps;
import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.impl.form.DateFormType;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

/**
 * @Author Lucas Ma
 * @Date 2018/6/17 下午9:35
 * <p>
 * 启动类
 */
public class DemoMain {

    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DemoMain.class);

    public static void main(String[] args) throws ParseException {

        LOGGER.info("启动程序");
        // 创建流程引擎
        ProcessEngine processEngine = getProcessEngine();

        // 部署流程定义文件
        ProcessDefinition processDefinition = getProcessDefinition(processEngine);

        // 启动运行流程
        ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition);

        // 处理流程任务
        processTask(processEngine, processInstance);

        LOGGER.info("结束程序");

    }

    private static void processTask(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
        Scanner scanner = new Scanner(System.in);
        while (processInstance != null && !processInstance.isEnded()) {
            TaskService taskService = processEngine.getTaskService();
            List<Task> list = taskService.createTaskQuery().list();
            LOGGER.info("待处理任务数量 [{}]", list.size());
            for (Task task : list) {

                LOGGER.info("待处理任务 [{}]", task.getName());
                Map<String, Object> variables = getMap(processEngine, scanner, task);
                taskService.complete(task.getId(), variables);
                processInstance = processEngine.getRuntimeService()
                        .createProcessInstanceQuery()
                        .processInstanceId(processInstance.getId())
                        .singleResult();
            }
        }
        scanner.close();
    }

    private static Map<String, Object> getMap(ProcessEngine processEngine, Scanner scanner, Task task) throws ParseException {
        FormService formService = processEngine.getFormService();
        TaskFormData taskFormData = formService.getTaskFormData(task.getId());
        List<FormProperty> formProperties = taskFormData.getFormProperties();
        Map<String, Object> variables = Maps.newHashMap();
        for (FormProperty property : formProperties) {
            String line = null;
            if (StringFormType.class.isInstance(property.getType())) {
                LOGGER.info("请输入 {} ?", property.getName());
                line = scanner.nextLine();
                variables.put(property.getId(), line);
            } else if (DateFormType.class.isInstance(property.getType())) {
                LOGGER.info("请输入 {} ? 格式 (yyyy-MM-dd)", property.getName());
                line = scanner.nextLine();
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                Date date = dateFormat.parse(line);
                variables.put(property.getId(), date);
            } else {
                LOGGER.info("类型暂不支持 {}", property.getType());
            }
            LOGGER.info("您输入的内容是 [{}]", line);

        }
        return variables;
    }

    private static ProcessInstance getProcessInstance(ProcessEngine processEngine, ProcessDefinition processDefinition) {
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
        LOGGER.info("启动流程 [{}]", processInstance.getProcessDefinitionKey());
        return processInstance;
    }

    private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
        RepositoryService repositoryService = processEngine.getRepositoryService();
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        deploymentBuilder.addClasspathResource("second_approve.bpmn20.xml");
        Deployment deployment = deploymentBuilder.deploy();
        String deploymentId = deployment.getId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploymentId).singleResult();
        LOGGER.info("流程定义文件 [{}],流程ID [{}]", processDefinition.getName(), processDefinition.getId());
        return processDefinition;
    }

    private static ProcessEngine getProcessEngine() {
       ProcessEngineConfiguration cfg = ProcessEngineConfiguration.
                                        createStandaloneInMemProcessEngineConfiguration();
        cfg.setProcessEngineName("测试流程..");

  /*      ProcessEngineConfiguration config = ProcessEngineConfiguration.
                                        createProcessEngineConfigurationFromResource("second_approve.bpmn20.xml");
        config.setActivityFontName("测试流露出");*/

        //创建processEngine
        ProcessEngine processEngine = cfg.buildProcessEngine();

        String name = processEngine.getName();
        String version = ProcessEngine.VERSION;
        LOGGER.info("流程引擎名称{},版本{}", name, version);


        //返回processEngine引擎对象
        return processEngine;
    }
}

Demo执行结果:

由于笔者也是刚开始研究工作流,目前只能写到这里。后续将继续完善,将activity与spring结合部署到项目中。

 

 

  • 10
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值