Activiti工作流的使用

工作流的使用

1.环境搭建,使用spring boot搭建activiti工作流

先下载插件,画activiti流程图使用,idea中下载插件,点击file-->Settings


下载插件

2.在项目中引入依赖

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <slf4j.version>1.6.6</slf4j.version>
    <log4j.version>1.2.12</log4j.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-engine</artifactId>
      <version>7.0.0.Beta1</version>
    </dependency>

    <dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-spring</artifactId>
      <version>7.0.0.Beta1</version>
    </dependency>

    <dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-bpmn-model</artifactId>
      <version>7.0.0.Beta1</version>
    </dependency>

    <dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-bpmn-converter</artifactId>
      <version>7.0.0.Beta1</version>
    </dependency>

    <dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-json-converter</artifactId>
      <version>7.0.0.Beta1</version>
    </dependency>

  <!--  <dependency>
      <groupId>org.activiti</groupId>
      <artifactId>activiti-bpmn-layout</artifactId>
      <version>7.0.0.Beta1</version>
    </dependency>-->

    <dependency>
      <groupId>org.activiti.cloud</groupId>
      <artifactId>activiti-cloud-services-api</artifactId>
      <version>7.0.0.Beta1</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.40</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

    <!-- log start -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <!-- log end -->

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>

    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
  </dependencies>

创建Activiti配置文件activiti.cfg.xml

<!--数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </bean>

    <!--配置Activiti使用的processEngine对象   默认命名为processEngineConfiguration,可以自定义命名,但是需要我们在初始化配置文件的时候加上自定义id 作为参数-->
    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置数据源方式二:-->
        <!--<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>-->

        <!--指定数据库生成策略,代表是否生成表结构-->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>

创建类加载配置文件并生成数据表

@Test
    public void createProcessEngineWithXMLConfig(){
        ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration
                                  .createProcessEngineConfigurationFromResource("activiti.cfg.xml");
                                 //  .createProcessEngineConfigurationFromResource("activiti.cfg.xml",beanid);
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        System.out.println(processEngine);
    }
//另一种加载配置生成数据表的简单写法
//条件:activati配置文件名称默认为:activati.cfg.xml   bean的id=processEngineConfiguration
@test
    public void testGenTable(){
        ProcessEngine processEngine =ProcessEngine.getDefaultProcessEngine();
        System.out.println(processEngine);

     }

3.流程定义,先将流程图bpmn画好

  • 任务的执行人目前指定,填写请假单的assignee为zhangsan,部门经理审批的assignee为lisi


    流程图bpmn
  • 将bpmn文件后缀名修改为xml文件然后文件右击选择diagrams导出png图片


    修改文件后缀

    导出png

    导出

4.代码演示

1.流程部署

  • 影响的数据表
    act_re_deployment 部署信息
    act_re_procdef 流程定信息 每个流程都应该有自己唯一的key
    act_ge_bytearray 保存上传的资源
@RunWith(SpringRunner.class)
@SpringBootTest
public class test {
    @Resource
    private ProcessEngine processEngine;
    @Test
    public void repository() {
        //1.创建processEngine对象
        //ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2.得到RepositoryService实例
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //3.进行部署
        Deployment deployment = repositoryService.createDeployment()//创建deploment对象
                .addClasspathResource("test.bpmn")//添加资源
                .addClasspathResource("test.png")//添加ziyuan
                .name("请假单")//流程名字
                .deploy();//流程的部署
         //4.输出部署的一些信息
        System.out.println(deployment.getId());
        System.out.println(deployment.getName());

    }
  • 第二种流程部署的方式,使用压缩包文件部署流程 在上传服务器是需求 zip文件更方便将流程定义bpmn文件跟pngwenjian压缩,压缩文件为zip格式
    /**
     * 使用压缩包文件部署流程  在上传服务器是需求 zip文件更方便
     *  将流程定义bpmn文件跟pngwenjian压缩,压缩文件为zip格式
     */
    @Test
    public void repositoryByZip() {
        //1.创建processEngine对象
        //ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2.得到RepositoryService实例
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //3.转化出ZipInputStream流对象
        InputStream is=this.getClass().getClassLoader().getResourceAsStream("Bpmn.zip");
        //转换
        ZipInputStream zip=new ZipInputStream(is);
        //4.进行部署
        Deployment deployment = repositoryService.createDeployment()//创建deploment对象
                .addZipInputStream(zip)
                .name("请假单")//流程名字
                .deploy();//流程的部署
        //4.输出部署的一些信息
        System.out.println(deployment.getId());
        System.out.println(deployment.getName());

    }

2.流程的启动

  • 影响的表:
    act_hi_actinst 流程节点的完成情况 时间记录了节点的开始时间和完成时间
    act_hi_identitylink 记录了这个流程的节点参与者信息
    act_hi_procinst 流程实例
    act_hi_taskinst 任务表 节点任务的详情 任务实例
    act_ru_execution 执行表
    act_ru_identitylink 流程实例节点需要完成任务的人 参与者信息
    act_ru_task 记录实例节点当前是哪一个任务 任务
    //流程的启动
    @Test
    public void ActivitiStartInstance(){
        //获取RuntimrService 对象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //创建流程实例  流程定义的key  或者别的参数
        ProcessInstance myTest = runtimeService.startProcessInstanceByKey("myTest");
        //输出实例相关信息
        System.out.println(myTest.getBusinessKey());
        System.out.println(myTest.getDeploymentId());

    }

3.任务列表查看

  • 查询的表
    ACT_RU_TASK
    ACT_RE_PROCDEF
    /**
     * 查看当前用户的任务列表查询
     */
    @Test
    public void ActicitiTaskQuery(){
        //得到TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //根据流程定义的key,负责人assignee来实现当前用户的任务列表查询
        List<Task> taskList = taskService.createTaskQuery()
                .taskAssignee("zhangsan")
                .processDefinitionKey("myTest")
                .list();
        //任务列表展示
        for (Task task:taskList) {
            System.out.println("流程实例id"+task.getProcessDefinitionId());
            System.out.println("任务id"+task.getId());
            System.out.println("任务负责人"+task.getAssignee());
            System.out.println("任务名称"+task.getName());
        }
    }

4.完成任务或者说执行任务,zhangsan完成请假单的填写

  • 影响的表:
    • act_hi_taskinst act_hi_actinst 更新数据 end_time有了记录 并且插入了下一个节点的任务
    • act_hi_identitylink 插入了下一个节点的任务参与者
    • act_ru_execution 中的实例信息ACT_ID_改成了任务节点的id
    • act_ru_identitylink 插入节点任务
    • act_ru_task 数据发生更新 更新成下一个节点任务的信息
    /**
     * 完成任务
     */
    @Test
    public void ActivitiCompleteTask(){
        //得到TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //得到任务
        Task task = taskService.createTaskQuery()
                .taskAssignee("zhangsan")
                //.processInstanceId(lcid)这里一般使用流程的id去联查
                .singleResult();//显示唯一的任务
        //根据任务id完成任务
        taskService.complete(task.getId());//在实际开发中一般是任务查找跟任务的完成结合一起
    }

5. 接下来就是lisi来完成审批任务

  • 影响的表
    • act_ru_task act_ru_identitylink act_ru_execution 所有任务完成后表里跟这次流程相关的记录没了
    • act_hi_taskinst 更新了lisi(最后完成任务的人)的完成时间 end_time
    • act_hi_identitylink 由于是最后环节了 所有没有变化
    • act_hi_actinst 更新了lisi(最后完成任务的人)的完成时间 end_time
    /**
     *接下来就是lisi来完成审批任务
     */
    @Test
    public void ActivitiCompleteTask1(){
        //得到TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //得到任务
        Task task = taskService.createTaskQuery()
                .taskAssignee("lisi")
                //.processInstanceId(lcid)这里一般使用流程的id去联查 或者 key
                .singleResult();
        //根据任务id完成任务
        taskService.complete(task.getId());
    }

6.总结

1.BPMN 的流程定义
2.idea中画出流程定义
3.ProcessEngineConfiguration 类 作用:加载activiti.cfg.xml 配置文件
4.ProcessEngine类 作用 帮助我们获得 各个Service 接口 并且生成工作环境(25张表的生成)
5.Service 接口 作用 帮助我们方便快速的操作数据库相关的表

  • 部署流程定义
  • 方式一:单个文件(bpmn文件,png文件)
  • 方式二:压缩包的方式(先将bpmn 文件和png文件压缩成zip文件)
  • 启动流程实例:RuntimeService startProcessInstanceByKey(“key”)
  • 查看任务:TaskService taskService.createTaskQuery()
  • 完成任务:TaskService taskService.complete()//参数为任务id

7.扩展使用

  • 流程定义的查询
    /**
     * 流程定义的查询
     * */
    @Test
    public void queryProcessDefinition() {
        //得到RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //得到ProcessDefinitionQuery对象, 可以任务是一个查询器
        ProcessDefinitionQuery processDefinitionQuery =repositoryService.createProcessDefinitionQuery();
        //设置条件并查询出当前所有的流程定义   查询条件:流程定义的key
        List<ProcessDefinition> list= processDefinitionQuery.processDefinitionKey("myTest")
                .orderByProcessDefinitionVersion() //通过版本号排序
                .desc().list();//降序
        //输出流程定义的信息
        for (ProcessDefinition processDefinition:list) {
            System.out.println("流程定义ID:"+processDefinition.getId());
            System.out.println("流程定义名称:"+processDefinition.getName());
            System.out.println("流程定义key:"+processDefinition.getKey());
            System.out.println("流程定义版本号:"+processDefinition.getVersion());
//            流程定义ID:myTest:1:90004
//            流程定义名称:请假单
//            流程定义key:myTest
//            流程定义版本号:1

        }
    }
  • 删除已经部署的流程定义
    注意事项:当我们正在执行的这一套流程没有完全审批结束的时候,此时删除流程定义信息会失败,我们可以使用级联删除--> repositoryService.deleteDeployment("90001",true);//true 级联删除 此时会先删除还没有完成的流程节点,最后就可以删除流程定义的信息
    /**
     * 删除已经部署的流程定义
     */
    @Test
    public void deleteProcessDefinition() {
        //得到RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //得到ProcessDefinitionQuery对象, 可以任务是一个查询器
        ProcessDefinitionQuery processDefinitionQuery =repositoryService.createProcessDefinitionQuery();
        //设置条件并查询出当前所有的流程定义   查询条件:流程定义的key
        List<ProcessDefinition> list= processDefinitionQuery.processDefinitionKey("myTest")
                .orderByProcessDefinitionVersion() //通过版本号排序
                .desc().list();//降序
        //输出流程定义的信息
        for (ProcessDefinition processDefinition:list) {
            System.out.println("流程定义ID:"+processDefinition.getId());
            System.out.println("流程定义名称:"+processDefinition.getName());
            System.out.println("流程定义key:"+processDefinition.getKey());
            System.out.println("流程定义版本号:"+processDefinition.getVersion());
            System.out.println("流程定义部署id:"+processDefinition.getDeploymentId());
//            流程定义ID:myTest:1:90004
//            流程定义名称:请假单
//            流程定义key:myTest
//            流程定义版本号:1
//            流程定义部署id:90001
         //删除操作  参数流程部署的id
         repositoryService.deleteDeployment("90001");
             //注意事项,当我们正在执行的这一套流程没有完全审批结束的时候,此时删除流程定义信息会失败
            //若想删除  加一个参数 true  表示强制删除
         repositoryService.deleteDeployment("90001",true);//true 级联删除 此时会先删除还没有完成的流程节点,最后就可以删除流程定义的信息
        }
    }
  • 从Activiti的act_ge_bytearray 表中读取两个资源文件
    将两个文件保存到路径为:D:
    应用场景:因为当客户想查看我们的流程具体有哪些步骤的时候我们需要获取png文件就可以清楚了解流程的步骤
    /**
     * 从Activiti的act_ge_bytearray 表中读取两个资源文件
     */
    @Test
    public void queryBpmnFile(){
        //得到RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();

        //得到ProcessDefinitionQuery对象, 可以任务是一个查询器
        ProcessDefinitionQuery processDefinitionQuery =repositoryService.createProcessDefinitionQuery();
        //查询条件
        processDefinitionQuery.processDefinitionKey("myTest");
        //执行查询操作,查询出想要的流程定义
        ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
        //通过流程定义信息,得到部署id
        String deploymentId = processDefinition.getDeploymentId();
        //通过repositoryService的方法,实现读取图片信息以及bpmn文件信息(输入流)
        //getResourceAsStream()方法参数说明:第一个是部署id,第二个是资源名称
        //processDefinition.getDiagramResourceName() 代表获取png图片资源的名称
        //processDefinition.getResourceName() 代表获取bpmn文件的名称
        InputStream pngip=repositoryService.getResourceAsStream(deploymentId,processDefinition.getDiagramResourceName());
        InputStream bpmnip=repositoryService.getResourceAsStream(deploymentId,processDefinition.getResourceName());
        OutputStream pngos=null;
        OutputStream bpmnos=null;
        //构建出OutputStream流
        try {
            pngos=new FileOutputStream("D:\\"+processDefinition.getDiagramResourceName());
            bpmnos=new FileOutputStream("D:\\"+processDefinition.getResourceName());
            //输入流,输出流转换
            byte[] b=new byte[1024];
            int len=-1;
            while ((len=pngip.read(b,0,1024))!=-1){
                pngos.write(b,0,len);
            }
            while ((len=bpmnip.read(b,0,1024))!=-1){
                bpmnos.write(b,0,len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭流
                pngos.close();
                bpmnos.close();
                pngip.close();
                bpmnip.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  • 查询历史记录
    /**
     * 查询历史记录
     */
    @Test
    public void historyQuery(){
        //得到HistoryService对象
        HistoryService historyService = processEngine.getHistoryService();
        //得到HistoryActivitiInstanceQuery 对象  查询器
        HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
        //设置流程实例id
        historicActivityInstanceQuery.processInstanceId("92501");
        //执行查询
        List<HistoricActivityInstance> list = historicActivityInstanceQuery.list();
        //遍历结果集
        for (HistoricActivityInstance his:list) {
            System.out.println("节点id:"+his.getActivityId());
            System.out.println("节点任务名称:"+his.getActivityName());
            System.out.println("流程定义id:"+his.getProcessDefinitionId());
            System.out.println("实例id:"+his.getProcessInstanceId());
//            节点id:_2
//            节点任务名称:StartEvent
//            流程定义id:myTest:1:90004
//            实例id:92501
//            节点id:_3
//            节点任务名称:填写请假单
//            流程定义id:myTest:1:90004
//            实例id:92501
//            节点id:_4
//            节点任务名称:部门经理审批
//            流程定义id:myTest:1:90004
//            实例id:92501
//            节点id:_5
//            节点任务名称:EndEvent
//            流程定义id:myTest:1:90004
//            实例id:92501
        }
    }

}
  • 流程的挂起与激活
    挂起状态 该流程下的所有流程实例全部暂停,也不允许启动新的流程
    /**
     * 流程的挂起与激活
     * 挂起状态 该流程下的所有流程实例全部暂停,也不允许启动新的流程
     */
    @Test
    public void SuspendProcessInstance(){
        //得到RepositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //查询流程定义对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("myTest").singleResult();//通过流程key
        //获取流程状态
        boolean suspended = processDefinition.isSuspended();
        //获取流程id
        String processDefinitionId = processDefinition.getId();
        //判断
        if (suspended){
            //说明是暂停了,可以进行激活操作
            repositoryService.activateProcessDefinitionById(processDefinitionId,true,null);
        }else{
            //暂停操作
            repositoryService.activateProcessDefinitionById(processDefinitionId,true,null);
        }
    }
  • 单个实例的挂起和激活
    /**
     * 单个实例的挂起和激活
     */
    @Test
    public void SuspendProcessInstance1(){
        //得到RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //查询流程定义对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processDefinitionKey("myTest").singleResult();//通过流程key
        //获取流程状态
        boolean suspended = processInstance.isSuspended();
        //获取流程id
        String processInstanceId = processInstance.getId();
        //判断
        if (suspended){
            //说明是暂停了,可以进行激活操作
            runtimeService.activateProcessInstanceById(processInstanceId);
        }else{
            //暂停操作
            runtimeService.suspendProcessInstanceById(processInstanceId);
        }
    }

下一篇将总结activiti的进阶使用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342