activiti工作流使用,附自定义节点跳转代码

工作流定义:

	业务过程的部分或整体在计算机应用环境下的自动化。

它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。
Activiti是一个针对企业用户、开发人员、系统管理员的轻量级工作流业务管理平台,其核心是使用Java开发的快速、稳定的BPMN2.0流程引擎。

生命周期:

工作流生命周期分为5步:

1.	定义:流程定义,
2.	发布:打包后在系统平台中发布,
3.	执行:流程引擎按照定义的流程处理路线执行业务
4.	监控:收集任务结果,根据不同结果进行处理。
5.	优化:优化流程

特点:

数据持久化:使用mybatis
Service接口:service方式调用
流程设计器-插件Activiti-Modeler
原生支持spring
分离运行与历史数据

SpringBoot集成:

使用activiti-spring-boot-starter-basic:

pom文件引入依赖:

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-freemarker</artifactId>
		</dependency>

		<!-- mysql 驱动依赖-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<!-- Druid DataSource -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>${druid.version}</version>
		</dependency>

        <!-- activiti -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>${activiti.version}</version> 
        </dependency>
        
		<dependency>
		     <groupId>org.activiti</groupId>
		     <artifactId>activiti-json-converter</artifactId>
		     <version>${activiti.version}</version>
		     <exclusions>
				<exclusion>
					<groupId>org.activiti</groupId>
      				<artifactId>activiti-bpmn-model</artifactId>
				</exclusion>
		     </exclusions>
    	</dependency>
    	
	    <dependency> 
		    <groupId>org.apache.xmlgraphics</groupId> 
		    <artifactId>batik-codec</artifactId> 
		    <version>${apache.xmlgraphics.version}</version> 
	    </dependency> 
	    <dependency> 
		    <groupId>org.apache.xmlgraphics</groupId> 
		    <artifactId>batik-css</artifactId> 
		    <version>${apache.xmlgraphics.version}</version> 
	    </dependency> 
	    <dependency> 
		    <groupId>org.apache.xmlgraphics</groupId> 
		    <artifactId>batik-svg-dom</artifactId> 
		    <version>${apache.xmlgraphics.version}</version> 
	    </dependency> 
	    <dependency> 
		    <groupId>org.apache.xmlgraphics</groupId> 
		    <artifactId>batik-svggen</artifactId> 
		    <version>${apache.xmlgraphics.version}</version> 
	    </dependency>
	   
	</dependencies>

此pom文件同时集成了Activiti-Modeler,如出现无法启动情况springframework相关问题,原因为版本不一致导致:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot instantiate interface org.springframework.context.ApplicationListener : org.springframework.boot.logging.ClasspathLoggingApplicationListener

解决方式:
添加parent统一依赖

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.8.RELEASE</version>
		<relativePath/>
	</parent>

配置yml文件:

数据库连接配置:
activiti基础配置:

# activiti default configuration
#建表规则 :true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建。
#          flase: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常。
#          create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。
#          drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
spring.activiti.database-schema-update = true
#校验流程文件,默认校验resources下的processes文件夹里的流程文件
spring.activiti.check-process-definitions = true
#自定义流程文件位置
spring.activiti.process-definition-location-prefix = classpath:/process/
#    process-definition-location-suffixes:
#      - **.bpmn
#      - **.bpmn20.xml
#记录历史等级 可配置的历史级别有none, acitivity, audit, all
spring.activiti.history-level = full

使用:

activiti-spring-boot-starter可直接通过注入service方式进行调用。

核心API:

在这里插入图片描述

ProcessEngine(流程引擎):在Activiti中最核心的类,其他的类都是由他而来。
调用ProcessEngines的getDefaultProceeEngine方法时会自动加载classpath下名为activiti.cfg.xml文件。

RepositoryService管理流程定义
RuntimeService执行管理,包括启动、推进、删除流程实例等操作
TaskService任务管理
HistoryService历史管理(执行完的数据的管理)
IdentityService组织机构管理
service使用方式心得:

service都是通过调用Query对象再拼接条件进行查询,第一次使用建议先熟悉常用表(流程定义表,任务表,历史表…)query对象的一般为具体数据库名称,需要查哪个表的数据就进行哪个查询对象的创建。

ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().orderByDeploymentId().desc();

此方法实际调用sql为:

SELECT DISTINCT RES.* FROM ACT_RE_PROCDEF RES ORDER BY RES.DEPLOYMENT_ID_ DESC LIMIT 15 OFFSET 0

完整请假流程代码(附带动态回退指定节点):

package com.activiti6.controller;

import com.activiti6.controller.editor.TestCommand;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName TestController
 * @Description 测试
 * @Author Dy
 * @Date 2020/8/14 16:54
 * @Version 1.0
 **/
@Controller
public class TestController {
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    RuntimeService runtimeservice;
    @Autowired
    TaskService taskservice;
    @Autowired
    ManagementService managementService;
    @Autowired
    HistoryService historyService;

    //1、部署流程资源【第一种方式:classpath】
    @GetMapping("/deploy1")
    public void deploy1() {
        Deployment deployment = repositoryService//获取流程定义和部署对象相关的Service
                .createDeployment()//创建部署对象
                .name("请假测试回退")//声明流程的名称
                .addClasspathResource("processes/test.bpmn")//加载资源文件,一次只能加载一个文件
                .deploy();//完成部署
        System.out.println("部署ID:" + deployment.getId());//1
        System.out.println("部署时间:" + deployment.getDeploymentTime());
    }


    //创建流程实例 请假
    /*
        start   部署任务
     */
    @RequestMapping(value = "/start", method = RequestMethod.POST)
    public void start() {
        //创建流程实例
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("applyuserid", "123");
        ProcessInstance starttest = runtimeservice.startProcessInstanceByKey("test", "user1", variables);
        System.out.println("流程部署id:" + starttest.getDeploymentId());
        System.out.println("流程实例id:" + starttest.getId());
        System.out.println("活动id:" + starttest.getActivityId());
    }

    @RequestMapping(value = "/complete/{actId}", method = RequestMethod.GET)
    public void complete(@PathVariable String actId) {
        Task task = taskservice.createTaskQuery().processInstanceId(actId).singleResult();
        System.out.println("任务id:" + task.getId());
        taskservice.claim(task.getId(), "group2");
        HashMap<String, Object> map = new HashMap<>();
        map.put("reason","true");
        taskservice.complete(task.getId(),map);
    }



    /**
     * 获取流程线信息
     *
     * @param procdefId 流程id
     */
    @GetMapping(value = "/procdef/info")
    public void prepare(@RequestParam("procdefId") String procdefId) {
        HashMap<Object, Object> map = new HashMap<>();
        // 获取bomn对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(procdefId);
        //获取Process
        Process process = bpmnModel.getProcesses().get(0);
        //获取所有FlowElement
        Collection<FlowElement> flowElements = process.getFlowElements();
        for (FlowElement flowElement : flowElements) {
            //如果是任务节点
            if (flowElement instanceof UserTask) {
                //获取连线信息
                UserTask userTask = (UserTask) flowElement;//节点转换
                userTask.getCandidateGroups();
//                List<SequenceFlow> incomingFlows = userTask.getIncomingFlows();
//                for (SequenceFlow incomingFlow : incomingFlows) {
//                    System.out.println(incomingFlow.getId()+"=="+ incomingFlow.getName());
//                }


            }
        }
    }

    /**
     * 自定义回退
     *
     * @param actId
     */
    /*
        activiti 6去除pvm包,所有流程定义相关内容通过bpmnModel获取
     */
    @GetMapping(value = "/rollback")
    public void rollBack(@RequestParam("actId") String actId, @RequestParam("key") String key) {
        String taskId = "";
        String tagNode = "";
        //获取当前任务id
        Task task = taskservice.createTaskQuery().processInstanceId(actId).singleResult();
        System.out.println("任务id:" + task.getId());
        taskId = task.getId();

        //获取目标
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        Collection<FlowElement> flowElements = bpmnModel.getProcesses().get(0).getFlowElements();
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof UserTask) {
                //获取用户任务
                UserTask userTask = (UserTask) flowElement;//节点转换
                if (userTask.getName().equals(key)) {
                    tagNode = userTask.getId();
                    break;
                }
            }
        }
        //跳转
        managementService.executeCommand(new TestCommand(taskId, tagNode));
    }

    /**
     * 历史活动查询
     */
    @GetMapping("/his")
    public void history(@RequestParam("actId") String actId){
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().
                processInstanceId(actId).
                finished()
                .activityType("userTask")
                .orderByActivityId()
                .list();
        for (HistoricActivityInstance hai : list) {
            System.out.println("活动ID:"+hai.getId());
            System.out.println("流程实例ID:"+hai.getProcessInstanceId());
            System.out.println("活动名称:"+hai.getActivityName());
            System.out.println("办理人:"+hai.getAssignee());
            System.out.println("开始时间:"+hai.getStartTime());
            System.out.println("结束时间:"+hai.getEndTime());
            System.out.println("=================================");
        }

    }

}

package com.activiti6.controller.editor;

import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.ActivitiEngineAgenda;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;

/**
 * @ClassName TestService
 * @Description TODO
 * @Author Dy
 * @Date 2020/8/17 11:37
 * @Version 1.0
 **/
public class TestCommand implements Command<Object> {

    /**
     * 当前节点
     */
    private String taskId;

    /**
     * 目标节点
     */
    private String targetNodeId;

    public TestCommand(String currentTaskId, String targetNodeId) {
        this.taskId = currentTaskId;
        this.targetNodeId = targetNodeId;
    }

    public String getTaskId() {
        return taskId;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    public String getTargetNodeId() {
        return targetNodeId;
    }

    public void setTargetNodeId(String targetNodeId) {
        this.targetNodeId = targetNodeId;
    }


    @Override
    public Object execute(CommandContext commandContext) {
        ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
        TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();
        TaskEntity taskEntity = taskEntityManager.findById(this.taskId);
        ExecutionEntity executionEntity = executionEntityManager.findById(taskEntity.getExecutionId());
        Process process = ProcessDefinitionUtil.getProcess(executionEntity.getProcessDefinitionId());
        taskEntityManager.deleteTask(taskEntity, "移动节点", true, true);
        FlowElement targetFlowElement = process.getFlowElement(targetNodeId);
        executionEntity.setCurrentFlowElement(targetFlowElement);
        ActivitiEngineAgenda agenda = commandContext.getAgenda();
        agenda.planContinueProcessInCompensation(executionEntity);
        return null;
    }
}

流程图:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:activiti="http://activiti.org/bpmn"
             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:tns="http://www.activiti.org/test"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             expressionLanguage="http://www.w3.org/1999/XPath"
             id="m1597395350401"
             name=""
             targetNamespace="http://www.activiti.org/test"
             typeLanguage="http://www.w3.org/2001/XMLSchema">
   <process xmlns="" id="test" isClosed="false" isExecutable="true" name="请假测试"
            processType="None">
      <startEvent id="_2" name="请假"/>
      <userTask activiti:candidateGroups="组长,经理" activiti:exclusive="true" id="_3" name="项目经理">
         <documentation id="_3_D_1">修改</documentation>
      </userTask>
      <endEvent id="_4" name="结束"/>
      <sequenceFlow id="_5" sourceRef="_2" targetRef="_3"/>
      <userTask activiti:candidateGroups="总监组" activiti:exclusive="true" id="_6" name="总监审批"/>
      <exclusiveGateway gatewayDirection="Unspecified" id="_7" name="ExclusiveGateway"/>
      <sequenceFlow id="_8" sourceRef="_3" targetRef="_7"/>
      <endEvent id="_9" name="EndEvent"/>
      <sequenceFlow id="_10" name="拒绝" sourceRef="_7" targetRef="_9">
         <conditionExpression xsi:type="tFormalExpression">${reason=='false'}</conditionExpression>
      </sequenceFlow>
      <sequenceFlow id="_11" name="同意" sourceRef="_7" targetRef="_6">
         <conditionExpression xsi:type="tFormalExpression">${reason=='true'}</conditionExpression>
      </sequenceFlow>
      <sequenceFlow id="_12" sourceRef="_6" targetRef="_4"/>
   </process>
   <bpmndi:BPMNDiagram xmlns=""
                       documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0"
                       id="Diagram-_1"
                       name="New Diagram">
      <bpmndi:BPMNPlane bpmnElement="test">
         <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
            <omgdc:Bounds height="32.0" width="32.0" x="105.0" y="280.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNShape>
         <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
            <omgdc:Bounds height="55.0" width="85.0" x="235.0" y="275.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNShape>
         <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
            <omgdc:Bounds height="32.0" width="32.0" x="895.0" y="280.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNShape>
         <bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
            <omgdc:Bounds height="55.0" width="85.0" x="555.0" y="275.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNShape>
         <bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7" isMarkerVisible="false">
            <omgdc:Bounds height="32.0" width="32.0" x="395.0" y="285.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNShape>
         <bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9">
            <omgdc:Bounds height="32.0" width="32.0" x="385.0" y="435.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNShape>
         <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_4">
            <omgdi:waypoint x="640.0" y="302.5"/>
            <omgdi:waypoint x="895.0" y="296.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNEdge>
         <bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="_2" targetElement="_3">
            <omgdi:waypoint x="137.0" y="296.0"/>
            <omgdi:waypoint x="235.0" y="302.5"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNEdge>
         <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_3" targetElement="_7">
            <omgdi:waypoint x="320.0" y="302.5"/>
            <omgdi:waypoint x="395.0" y="301.0"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNEdge>
         <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_7" targetElement="_6">
            <omgdi:waypoint x="427.0" y="301.0"/>
            <omgdi:waypoint x="555.0" y="302.5"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNEdge>
         <bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_7" targetElement="_9">
            <omgdi:waypoint x="406.0" y="312.0"/>
            <omgdi:waypoint x="406.0" y="435.80131584642936"/>
            <bpmndi:BPMNLabel>
               <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
            </bpmndi:BPMNLabel>
         </bpmndi:BPMNEdge>
      </bpmndi:BPMNPlane>
   </bpmndi:BPMNDiagram>
</definitions>
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值