【工作流引擎】Flowable快速入门(一) 基础API使用

【工作流引擎】Flowable快速入门(一) 基础API使用,第1张

【工作流引擎】Flowable快速入门(一) 基础API使用
  • 一、获取ProcessEngine
    • 1.1 原生构建
    • 1.2 ProcessEngine API
    • 1.3 服务说明
    • 1.4 Spring 依赖注入
  • 二、流程部署
    • 2.1 部署流程定义
    • 2.2 流程部署
      • 2.2.1 xml文件和String 部署
      • 2.2.2 BpmnModel 部署
    • 2.3 流程部署查询
    • 2.4 流程图片查询
    • 2.5 流程XML查询
    • 2.6 流程挂起与激活
    • 2.7 流程删除
  • 三、流程启动
    • 3.1 无参启动
    • 3.2 带参数启动
    • 3.3 设置流程发起人
    • 3.4 运行实例查询
    • 3.5 激活或挂起
  • 四、任务
    • 4.1 任务查询
    • 4.2 完成任务
    • 4.3 评论
    • 4.4 附件
    • 4.5 参数
  • 五、历史任务
    • 5.1 历史任务查询

本Demo在搭建好环境的基础上,可参考【工作流引擎】Flowable Spring Boot集成

一、获取ProcessEngine 1.1 原生构建

在不使用Spring 注入的情况下,可直接通过ProcessEngineConfiguration 配置构建ProcessEngine

//1、创建ProcessEngineConfiguration实例,该实例可以配置与调整流程引擎的设置
ProcessEngineConfiguration cfg=new StandaloneProcessEngineConfiguration()
        //2、通常采用xml配置文件创建ProcessEngineConfiguration,这里直接采用代码的方式
        //3、配置数据库相关参数
        .setJdbcUrl("jdbc:mysql://localhost:3306/flowable_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8&nullCatalogMeansCurrent=true")
        .setJdbcUsername("root")
        .setJdbcPassword("root")
        .setJdbcDriver("com.mysql.jdbc.Driver")
        // 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
        .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
//4、初始化ProcessEngine流程引擎实例
ProcessEngine processEngine=cfg.buildProcessEngine();
1.2 ProcessEngine API
public interface ProcessEngine extends Engine {

    /** the version of the flowable library */
    String VERSION = FlowableVersions.CURRENT_VERSION;

    /**
     * Starts the execuctors (async and async history), if they are configured to be auto-activated.
     */
    void startExecutors();

    RepositoryService getRepositoryService();

    RuntimeService getRuntimeService();

    FormService getFormService();

    TaskService getTaskService();

    HistoryService getHistoryService();

    IdentityService getIdentityService();

    ManagementService getManagementService();

    DynamicBpmnService getDynamicBpmnService();

    ProcessMigrationService getProcessMigrationService();

    ProcessEngineConfiguration getProcessEngineConfiguration();
}
1.3 服务说明

RepositoryService 流程存储服务

管理流程定义文件xml及静态资源的服务

对特定流程的暂停和激活

流程定义启动权限管理

类内部重要的成员有:

deploymentBuilder 部署文件构造器

deploymentQuery 部署文件查询器

ProcessDefinitionQuery 流程定义文件查询对象

Deployment 流程部署文件对象

ProcessDefinition 流程定义文件对象

BpmnModel 流程定义的java格式

RuntimeService 流程运行控制服务

启动流程及对流程数据的控制

流程实例(ProcessInstance)与执行流(Execution)的查询

触发流程 *** 作,接收消息和信号

RuntimeService启动流程及变量管理

启动流程的常用方法(id,key,message)

启动流程可选参数(businessKey,variables,tenantId)

变量(variables)的设置和获取

TaskService

对用户任务UserTask的管理和流程的控制

设置用户任务的权限信息(设置候选人等)

针对用户任务添加任务附件,任务评论和事件记录

TaskService对Task的管理和控制

Task对象的创建和删除

查询Task,驱动Task节点完成执行

Task相关参数变量variable设置

1.4 Spring 依赖注入

通过ProcessEngine 获取RepositoryService 对象

@Autowired
 private ProcessEngine processEngine;

 @Test
 public void testProcessEngine(){
     RepositoryService repository = processEngine.getRepositoryService();
     System.out.println(repository.toString());
 }

在Spring 中可以直接将ProcessEngine可以获取的服务对象直接注入,例如:RepositoryService 注入

@Autowired
private RepositoryService repositoryService;
二、流程部署 2.1 部署流程定义

BPMN2.0介绍

我们要构建的流程是一个非常简单的请假流程。Flowable引擎需要流程定义为BPMN 2.0格式,这是一个业界广泛接受的XML标准。 在Flowable术语中,我们将其称为一个流程定义(process definition)。一个流程定义可以启动多个流程实例(process instance)。流程定义可以看做是重复执行流程的蓝图。 在这个例子中,流程定义定义了请假的各个步骤,而一个流程实例对应某个雇员提出的一个请假申请。

BPMN 2.0存储为XML,并包含可视化的部分:使用标准方式定义了每个步骤类型(人工任务,自动服务调用,等等)如何呈现,以及如何互相连接。这样BPMN 2.0标准使技术人员与业务人员能用双方都能理解的方式交流业务流程。

这个流程应该已经十分自我解释了。但为了明确起见,说明一下几个要点:

  • 我们假定启动流程需要提供一些信息,例如雇员名字、请假时长以及说明。当然,这些可以单独建模为流程中的第一步。 但是如果将它们作为流程的“输入信息”,就能保证只有在实际请求时才会建立一个流程实例。否则(将提交作为流程的第一步),用户可能在提交之前改变主意并取消,但流程实例已经创建了。 在某些场景中,就可能影响重要的指标(例如启动了多少申请,但还未完成),取决于业务目标。

  • 左侧的圆圈叫做启动事件(start event)。这是一个流程实例的起点。

  • 第一个矩形是一个用户任务(user task)。这是流程中人类用户 *** 作的步骤。在这个例子中,经理需要批准或驳回申请。

  • 取决于经理的决定,排他网关(exclusive gateway) (带叉的菱形)会将流程实例路由至批准或驳回路径。

  • 如果批准,则需要将申请注册至某个外部系统,并跟着另一个用户任务,将经理的决定通知给申请人。当然也可以改为发送邮件。

  • 如果驳回,则为雇员发送一封邮件通知他。

一般来说,这样的流程定义使用可视化建模工具建立,如Flowable Designer(Eclipse)或Flowable Web Modeler(Web应用)。

此部分介绍来自: Flowable BPMN 用户手册

2.2 流程部署 2.2.1 xml文件和String 部署

holiday-request.bpmn20.xml (存放在项目资源目录下)


<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
  xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
  xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
  xmlns:flowable="http://flowable.org/bpmn"
  typeLanguage="http://www.w3.org/2001/XMLSchema"
  expressionLanguage="http://www.w3.org/1999/XPath"
  targetNamespace="http://www.flowable.org/processdef">

  <process id="oa_leave_1" name="OA 请假1" isExecutable="true">

    <startEvent id="startEvent"/>
    <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

    <userTask id="approveTask" name="Approve or reject request"/>
    <sequenceFlow sourceRef="approveTask" targetRef="decision"/>

    <exclusiveGateway id="decision"/>
    <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
      <conditionExpression xsi:type="tFormalExpression">
        
      conditionExpression>
    sequenceFlow>
    <sequenceFlow  sourceRef="decision" targetRef="sendRejectionMail">
      <conditionExpression xsi:type="tFormalExpression">
        
      conditionExpression>
    sequenceFlow>

    <serviceTask id="externalSystemCall" name="Enter holidays in external system"
        flowable:class="org.flowable.CallExternalSystemDelegate"/>
    <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

    <userTask id="holidayApprovedTask" name="Holiday approved"/>
    <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

    <serviceTask id="sendRejectionMail" name="Send out rejection email"
        flowable:class="org.flowable.SendRejectionMail"/>
    <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

    <endEvent id="approveEnd"/>

    <endEvent id="rejectEnd"/>

  process>

definitions>

文件标红问题可以通过安装Flowable BPMN visualizer插件解决
Flowable BPMN visualizer 可以进行绘制
本文件无非被插件正确打开是缺少 标签及数据

第2至11行看起来挺吓人,但其实在大多数的流程定义中都是一样的。这是一种样板文件,需要与BPMN 2.0标准规范完全一致。

每一个步骤(在BPMN 2.0术语中称作活动(activity))都有一个id属性,为其提供一个在XML文件中唯一的标识符。所有的活动都可以设置一个名字,以提高流程图的可读性。

活动之间通过顺序流(sequence flow) 连接,在流程图中是一个有向箭头。在执行流程实例时,执行(execution)会从启动事件沿着顺序流流向下一个活动。

离开排他网关(带有X的菱形)的顺序流很特别:都以表达式(expression)的形式定义了条件(condition) (见第25至32行)。当流程实例的执行到达这个网关时,会计算条件,并使用第一个计算为true的顺序流。这就是排他的含义:只选择一个。当然如果需要不同的路由策略,可以使用其他类型的网关。

这里用作条件的表达式为 a p p r o v e d , 这 是 {approved},这是 approved{approved == true}的简写。变量’approved’被称作流程变量(process variable)。流程变量是持久化的数据,与流程实例存储在一起,并可以在流程实例的生命周期中使用。在这个例子里,我们需要在特定的地方(当经理用户任务提交时,或者以Flowable的术语来说,完成(complete)时)设置这个流程变量,因为这不是流程实例启动时就能获取的数据。

现在我们已经有了流程BPMN 2.0 XML文件,下来需要将它部署(deploy) 到引擎中。部署一个流程定义意味着:

  • 流程引擎会将XML文件存储在数据库中,这样可以在需要的时候获取它。
  • 流程定义转换为内部的、可执行的对象模型,这样使用它就可以启动流程实例。

将流程定义部署至Flowable引擎,需要使用RepositoryService,其可以从ProcessEngine对象获取。使用RepositoryService,可以通过XML文件的路径创建一个新的部署(Deployment),并调用deploy()方法实际执行:

@SpringBootTest
public class DeploymentTest {


    @Autowired
    private RepositoryService repositoryService;

    /**
     * 通过文件部署
     */
    @Test
    public void deploymentByFile(){
        //部署流程
        Deployment deployment=repositoryService.createDeployment()
                .addClasspathResource("bpmn/holiday-request.bpmn20.xml")
                .name("请假流程")
                .deploy();

    }
    /**
     * 通过文件部署
     */
    @Test
    public void deploymentByXML(){
        //部署流程
        String xml = "holiday-request.bpmn20.xml 内容,此处略";
        Deployment deployment=repositoryService.createDeployment()
                .addString("holiday-request.bpmn20.xml",xml)
                .name("请假流程")
                .deploy();

    }
}

这里直接通过Spring注入的方式使用,也可以使用手动ProcessEngine 或者注入ProcessEngine 去获取RepositoryService对象去进行 *** 作
这里实现了文件和xml方式

DeploymentBuilder 接口可以在不同场景下选择不同的接口来部署

public interface DeploymentBuilder {

    DeploymentBuilder addInputStream(String resourceName, InputStream inputStream);

    DeploymentBuilder addClasspathResource(String resource);

    DeploymentBuilder addString(String resourceName, String text);

    DeploymentBuilder addBytes(String resourceName, byte[] bytes);

    DeploymentBuilder addZipInputStream(ZipInputStream zipInputStream);

    DeploymentBuilder addBpmnModel(String resourceName, BpmnModel bpmnModel);

    /**
     * If called, no XML schema validation against the BPMN 2.0 XSD.
     * 
     * Not recommended in general.
     */
    DeploymentBuilder disableSchemaValidation();

    /**
     * If called, no validation that the process definition is executable on the engine will be done against the process definition.
     * 
     * Not recommended in general.
     */
    DeploymentBuilder disableBpmnValidation();

    /**
     * Gives the deployment the given name.
     */
    DeploymentBuilder name(String name);

    /**
     * Gives the deployment the given category.
     */
    DeploymentBuilder category(String category);

    /**
     * Gives the deployment the given key.
     */
    DeploymentBuilder key(String key);
    
    /**
     * Gives the deployment the given parent deployment id.
     */
    DeploymentBuilder parentDeploymentId(String parentDeploymentId);

    /**
     * Gives the deployment the given tenant id.
     */
    DeploymentBuilder tenantId(String tenantId);

    /**
     * If set, this deployment will be compared to any previous deployment. This means that every (non-generated) resource will be compared with the provided resources of this deployment.
     */
    DeploymentBuilder enableDuplicateFiltering();

    /**
     * Sets the date on which the process definitions contained in this deployment will be activated. This means that all process definitions will be deployed as usual, but they will be suspended from
     * the start until the given activation date.
     */
    DeploymentBuilder activateProcessDefinitionsOn(Date date);

    /**
     * Allows to add a property to this {@link DeploymentBuilder} that influences the deployment.
     */
    DeploymentBuilder deploymentProperty(String propertyKey, Object propertyValue);

    /**
     * Deploys all provided sources to the process engine.
     */
    Deployment deploy();

}
2.2.2 BpmnModel 部署


以此流程为例

/**
  * 通过BpmnModel部署
  */
@Test
public void deploymentByBpmnModel(){
    //BpmnModel构建
    BpmnModel bpmnModel = new BpmnModel();
    StartEvent start = new StartEvent();
    start.setId("start");
    start.setName("开始节点");

    SequenceFlow flow1 = new SequenceFlow();
    flow1.setId("flow1");
    flow1.setName("开始节点-->请假申请");
    flow1.setSourceRef("start");
    flow1.setTargetRef("userTask");

    UserTask userTask = new UserTask();
    userTask.setId("userTask");
    userTask.setName("请假申请");

    //请假申请-->网关处理
    SequenceFlow flow2 = new SequenceFlow();
    flow2.setId("flow2");
    flow2.setName("请假申请-->网关处理");
    flow2.setSourceRef("userTask");
    flow2.setTargetRef("exclusiveGateway");
    //拍它
    ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
    exclusiveGateway.setId("exclusiveGateway");


    SequenceFlow flow3 = new SequenceFlow();
    flow3.setId("flow3");
    flow3.setName("请假大于 3 天");
    flow3.setSourceRef("exclusiveGateway");
    flow3.setTargetRef("hrTask");
    flow3.setConditionExpression("${day>3}");

    UserTask hrTask = new UserTask();
    hrTask.setId("hrTask");
    hrTask.setName("HR 审批");

    SequenceFlow flow4 = new SequenceFlow();
    flow4.setId("flow4");
    flow4.setName("HR 审批--> 结束");
    flow4.setSourceRef("hrTask");
    flow4.setTargetRef("endEvent");


    SequenceFlow flow5 = new SequenceFlow();
    flow5.setId("flow5");
    flow5.setName("请假小于等于 1 天");
    flow5.setSourceRef("exclusiveGateway");
    flow5.setTargetRef("endEvent");
    flow5.setConditionExpression("${day<=1}");

    EndEvent end = new EndEvent();
    end.setId("endEvent");
    end.setName("结束节点");

    Process process=new Process();
    process.setId("process1");
    process.setName("process1");

    process.addFlowElement(start);
    process.addFlowElement(flow1);
    process.addFlowElement(userTask);
    process.addFlowElement(flow2);
    process.addFlowElement(exclusiveGateway);
    process.addFlowElement(flow3);
    process.addFlowElement(hrTask);
    process.addFlowElement(flow4);
    process.addFlowElement(flow5);
    process.addFlowElement(end);

    bpmnModel.addProcess(process);

    //部署流程
    Deployment deployment=repositoryService.createDeployment()
            .addBpmnModel("holiday-request-3.bpmn20.xml",bpmnModel)
            .name("请假流程-BpmnModel")
            .deploy();

}
2.3 流程部署查询

可以通过repositoryService的create**Query()来查询当前部署的流程和流程定义信息

/**
 * 查询已经部署的流程
 */
@Test
public void searchDeployment(){
    //查询流程定义
    List<Deployment> list = repositoryService.createDeploymentQuery().list();
    for (Deployment deployment : list) {
        System.out.println("deployment ==> "+deployment);
    }
}

/**
 * 查询已经部署的流程定义
 */
@Test
public void searchProcessDefinitionList(){
    List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
    for (ProcessDefinition processDefinition : list) {
        System.out.println("processDefinition id ==> "+ processDefinition.getId());
        System.out.println("processDefinition ==> "+processDefinition.getId()+" , "+processDefinition.getName()+"  key:"+ processDefinition.getKey());
    }
}
2.4 流程图片查询
/**
     * 读取xml
 *
 * @param deployId
 * @return
 */
public InputStream readImage(String deployId) {
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();

    //获得图片流
    DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
    //输出为图片
    return diagramGenerator.generateDiagram(
            bpmnModel,
            "png",
            Collections.emptyList(),
            Collections.emptyList(),
            "宋体",
            "宋体",
            "宋体",
            null,
            1.0,
            false);

}

@SneakyThrows
@Test
public void readImageBase64Test(){
	String deploymentId = "5001";
    InputStream inputStream = readImage(deploymentId);
    String base64FromInputStream = getBase64FromInputStream(inputStream);
    System.out.println("data:image/xxx;base64,"+base64FromInputStream);
}
/**
 * 将inputstream转为Base64
 *
 * @param is
 * @return
 * @throws Exception
 */
private String getBase64FromInputStream(InputStream is) throws Exception {
    // 将图片文件转化为字节数组字符串,并对其进行Base64编码处理
    byte[] data = null;

    // 读取图片字节数组
    try {
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while ((rc = is.read(buff, 0, 100)) > 0) {
            swapStream.write(buff, 0, rc);
        }
        data = swapStream.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                throw new Exception("输入流关闭异常");
            }
        }
    }

    return Base64.byteArrayToBase64(data);
}

输出base64编码,在前面加上data:image/xxx;base64, 就可以显示流程图片

不过如果按文章来,此时执行会发现报空指针异常,前文提供的官方xml和BpmnModel都为此部分留下了个坑,原因和官方demo的xml使用Flowable BPMN visualizer插件无法正常打开一个原因,缺少 标签及布局数据
对于此问题可以通过两种解决方法

  • 解决方法一: 设计器绘制
    设计图通过设计绘制,自然会存在布局数据标签,即可解决此问题
  • 解决方法二: 添加自适应布局

添加布局依赖

<dependency>
    <groupId>org.flowablegroupId>
    <artifactId>flowable-bpmn-layoutartifactId>
    <version>6.7.0version>
dependency>

添加自动布局代码

//自动布局
BpmnAutoLayout bpmnAutoLayout = new BpmnAutoLayout(bpmnModel);
bpmnAutoLayout.setTaskHeight(120);
bpmnAutoLayout.setTaskWidth(120);
bpmnAutoLayout.execute();

完整代码

/**
 * 读取xml
 *
 * @param deployId
 * @return
 */
public InputStream readImage(String deployId) {
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();

    //获得图片流
    DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
    //自动布局
    BpmnAutoLayout bpmnAutoLayout = new BpmnAutoLayout(bpmnModel);
    bpmnAutoLayout.setTaskHeight(120);
    bpmnAutoLayout.setTaskWidth(120);
    bpmnAutoLayout.execute();
    //输出为图片
    return diagramGenerator.generateDiagram(
            bpmnModel,
            "png",
            Collections.emptyList(),
            Collections.emptyList(),
            "宋体",
            "宋体",
            "宋体",
            null,
            1.0,
            false);

}

图片输出结果

此处显示的是通过BpmnModel部署创建的流程图,也侧面怎么BpmnModel部署逻辑正常

2.5 流程XML查询
public InputStream readXml(String deployId){
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
    return repositoryService.getResourceAsStream(deployId,processDefinition.getResourceName());
}

@SneakyThrows
@Test
public void readXmlTest(){
	String deploymentId = "5001";
    InputStream inputStream = readXml(deploymentId );
    String result = new BufferedReader(new InputStreamReader(inputStream))
            .lines().collect(Collectors.joining(System.lineSeparator()));
    System.out.println(result);
}
2.6 流程挂起与激活
/**
 * 激活或挂起
 */
@Test
public void activateOrsuspend(){
    boolean suspend = false;
    // 激活
    if (suspend) {
        repositoryService.suspendProcessDefinitionById("delegate-event:1:4b63f76b-c308-11ec-bc05-004238a4ec73");
    } else { // 挂起
        repositoryService.activateProcessDefinitionById("delegate-event:1:4b63f76b-c308-11ec-bc05-004238a4ec73");

    }
}
2.7 流程删除
/**
 * 通过ID删除
 */
@Test
public void delDeployment(){
	String deploymentId = "5001";
    //删除流程
    repositoryService.deleteDeployment(deploymentId ,true);

}
三、流程启动 3.1 无参启动
/**
 * 无参启动流程
 */
@Test
public void startProcess(){
    //启动流程
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("流程ID");
    System.out.println("提交成功.流程Id为:" + processInstance.getId());
}

也可以通过**runtimeService.startProcessInstanceById(processInstanceId)**来启动

3.2 带参数启动

部署带参数的XML,互斥网关两条路径通过传递的day参数判断选择路径


<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.flowable.org/processdef">
  <process id="oa_leave_2" name="请假流程2">
    <startEvent id="startNode1" name="开始">
      <outgoing>Flow_1is46qvoutgoing>
    startEvent>
    <exclusiveGateway id="Gateway_1sybsgh">
      <incoming>Flow_1is46qvincoming>
      <outgoing>Flow_1es8ukeoutgoing>
      <outgoing>Flow_1c3krhpoutgoing>
    exclusiveGateway>
    <sequenceFlow id="Flow_1is46qv" sourceRef="startNode1" targetRef="Gateway_1sybsgh" />
    <sequenceFlow id="Flow_1es8uke" name="大于20天" sourceRef="Gateway_1sybsgh" targetRef="Activity_0d6qxdi">
      <conditionExpression xsi:type="tFormalExpression">${day>20}conditionExpression>
    sequenceFlow>
    <userTask id="Activity_0d6qxdi" name="张三">
      <incoming>Flow_1es8ukeincoming>
      <outgoing>Flow_0c4ggivoutgoing>
    userTask>
    <sequenceFlow id="Flow_1c3krhp" name="小于等于20天" sourceRef="Gateway_1sybsgh" targetRef="Activity_04nn7ft">
      <conditionExpression xsi:type="tFormalExpression">${day<=20}conditionExpression>
    sequenceFlow>
    <userTask id="Activity_04nn7ft" name="李四">
      <incoming>Flow_1c3krhpincoming>
      <outgoing>Flow_1lxbseboutgoing>
    userTask>
    <endEvent id="Event_1negaxv">
      <incoming>Flow_1lxbsebincoming>
      <incoming>Flow_0c4ggivincoming>
    endEvent>
    <sequenceFlow id="Flow_1lxbseb" sourceRef="Activity_04nn7ft" targetRef="Event_1negaxv" />
    <sequenceFlow id="Flow_0c4ggiv" sourceRef="Activity_0d6qxdi" targetRef="Event_1negaxv" />
  process>
definitions>

带参数部署

/**
 * 带参数部署
 */
@Test
public void startProcessAddParameter(){
    //设置参数
    Map<String, Object> param = new HashMap<>();
    param.put("day","10");
    //启动流程
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oa_leave_2",param);
    System.out.println("提交成功.流程Id为:" + processInstance.getId());
}
3.3 设置流程发起人


部署带参数的带流程发起人,区别在于startEvent 节点和发起人申请的userTask 节点传递用户发起用户


<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:flowable="http://flowable.org/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.flowable.org/processdef">
  <process id="oa_leave_3" name="请假流程3">
    <startEvent id="startNode1" name="开始" flowable:initiator="INITIATOR">
      <outgoing>Flow_1is46qvoutgoing>
    startEvent>
    <sequenceFlow id="Flow_1is46qv" sourceRef="startNode1" targetRef="Activity_0q7htuq" />
    <userTask id="Activity_0q7htuq" name="发起人申请" flowable:candidateGroups="" flowable:dataType="fixed" flowable:assignee="${INITIATOR}">
      <incoming>Flow_1is46qvincoming>
      <outgoing>Flow_10kn8otoutgoing>
    userTask>
    <sequenceFlow id="Flow_10kn8ot" sourceRef="Activity_0q7htuq" targetRef="Gateway_1sybsgh" />
    <exclusiveGateway id="Gateway_1sybsgh">
      <incoming>Flow_10kn8otincoming>
      <outgoing>Flow_1es8ukeoutgoing>
      <outgoing>Flow_1c3krhpoutgoing>
    exclusiveGateway>

    <sequenceFlow id="Flow_1es8uke" name="大于20天" sourceRef="Gateway_1sybsgh" targetRef="Activity_0d6qxdi">
      <conditionExpression xsi:type="tFormalExpression">${day>20}conditionExpression>
    sequenceFlow>
    <userTask id="Activity_0d6qxdi" name="张三">
      <incoming>Flow_1es8ukeincoming>
      <outgoing>Flow_0c4ggivoutgoing>
    userTask>
    <sequenceFlow id="Flow_1c3krhp" name="小于等于20天" sourceRef="Gateway_1sybsgh" targetRef="Activity_04nn7ft">
      <conditionExpression xsi:type="tFormalExpression">${day<=20}conditionExpression>
    sequenceFlow>
    <userTask id="Activity_04nn7ft" name="李四">
      <incoming>Flow_1c3krhpincoming>
      <outgoing>Flow_1lxbseboutgoing>
    userTask>
    <sequenceFlow id="Flow_1lxbseb" sourceRef="Activity_04nn7ft" targetRef="Event_1negaxv" />
    <sequenceFlow id="Flow_0c4ggiv" sourceRef="Activity_0d6qxdi" targetRef="Event_1negaxv" />
    <endEvent id="Event_1negaxv">
      <incoming>Flow_1lxbsebincoming>
      <incoming>Flow_0c4ggivincoming>
    endEvent>
  process>
definitions>

设置发起人代码

/**
 * 设置流程发起人id启动一个流程
 */
@Test
public void startProcessByInitiator(){
    //设置流程发起人id 流程引擎会对应到变量:INITIATOR
    Authentication.setAuthenticatedUserId("user");
    //启动流程
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oa_leave_3");
    //这个方法最终使用一个ThreadLocal类型的变量进行存储,也就是与当前的线程绑定,所以流程实例启动完毕之后,需要设置为null,防止多线程的时候出问题。
    Authentication.setAuthenticatedUserId(null);
    System.out.println("提交成功.流程Id为:" + processInstance.getId());
    System.out.println("提交成功.流程实例Id为:" + processInstance.getProcessInstanceId());

}

通过Authentication.setAuthenticatedUserId设置启动用户来记录发起用户数据信息

3.4 运行实例查询
 /**
 * 运行实例查询
 */
@Test
public void list(){
    List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
    for (ProcessInstance processInstance : list) {
        System.out.println(processInstance.getStartTime()+"|"+processInstance.getProcessInstanceId()+"|"+
                processInstance.getStartUserId()+"|"+processInstance.getProcessDefinitionName()+":"+
                processInstance.isSuspended());
    }
}
3.5 激活或挂起
/**
 * 激活或挂起
 */
@Test
public void activateOrsuspend(){
    int state = 2;
    // 激活
    if (state == 1) {
        runtimeService.activateProcessInstanceById("流程实例");
    }
    // 挂起
    if (state == 2) {
        runtimeService.suspendProcessInstanceById("流程实例");
    }
}

流程实例ID以实际为准,挂载情况可以查询查看
被挂起的实例是不能进行 *** 作的

四、任务 4.1 任务查询
 @Autowired
 private TaskService taskService;

/**
 * 查询所有任务
 */
 @Test
 public void searchTaskAll(){
     //获取需要审批的任务
     List<Task> tasks = taskService.createTaskQuery().list();
     for (Task task : tasks) {
         System.out.println("task ==> "+task+" ProcessInstanceId = "+task.getProcessInstanceId()+" assignee : "+task.getAssignee());
     }

 }
 /**
  * 获取指定用户需要审批的任务
  */
 @Test
 public void searchTaskByAssignee(){
     //获取指定用户需要审批的任务
     List<Task> tasks = taskService.createTaskQuery().taskAssignee("user").list();
     for (Task task : tasks) {
         System.out.println("task ==> "+task+" assignee : "+task.getAssignee());
     }
 }
4.2 完成任务
/**
 * 无参数完成任务
 */
@Test
public void completeTask(){
    taskService.complete("任务ID");
}

/**
 * 有参数完成任务
 */
@Test
public void completeTaskParam(){
    Map<String,Object> param = new HashMap<>();
    param.put("day","30");
    taskService.complete("任务ID",param);
}

3.2流程可以直接通过无参完成,3.3由于一开始未设置天数可以在完成时候进行补充

4.3 评论
/**
 * 添加评论
 */
@Test
public void addComment(){
    Authentication.setAuthenticatedUserId("admin");
    taskService.addComment("任务ID","fe7a3640-c2d4-11ec-a222-004238a4ec73","record note 2");

}

/**
 * 查询任务评论
 */
@Test
public void getComment(){
    List<Comment> taskComments = taskService.getTaskComments("任务ID");
    for (Comment taskComment : taskComments) {
        System.out.println(taskComment.getFullMessage());
        System.out.println(taskComment.getUserId());
    }
}

/**
 * 查询实例评论
 */
@Test
public void getProcessInstanceComment(){
    System.out.println("=========ProcessInstance Comment==============");
    List<Comment> taskComments = taskService.getProcessInstanceComments("流程实例ID");
    for (Comment taskComment : taskComments) {
        System.out.println(taskComment.getFullMessage());
        System.out.println(taskComment.getUserId());
    }
}
4.4 附件
/**
 * 创建附件
 * 可以指定类型、名称和详情,通过URL存储
 */
@Test
public void createAttachment(){
    // 添加附件(地址位于/url/test.png)到task中
    taskService.createAttachment("type","任务ID",
            "任务ID",
            "name",
            "desc",
            "url");
}

/**
 * 根据任务ID获取附件
 */
@Test
public void getAttachment(){
    // 查询附件
    List<Attachment> attachmentList = taskService.getTaskAttachments("任务ID");
    for(Attachment attachment : attachmentList){
        System.out.println("type="+attachment.getType()+",description="+attachment.getDescription()+",url"+attachment.getUrl());
    }
}

/**
 * 根据流程实例获取ID
 */
@Test
public void getAttachmentByProcessInstanceId(){
    // 查询附件
    List<Attachment> attachmentList = taskService.getProcessInstanceAttachments("流程实例ID");
    for(Attachment attachment : attachmentList){
        System.out.println("attach="+attachment.getDescription()+","+attachment.getUrl());
    }

}
4.5 参数
/**
 * 获取参数数据
 */
@Test
public void getVariables(){
    Map<String, Object> variables = taskService.getVariables("任务ID");
    System.out.println(variables);
}
五、历史任务 5.1 历史任务查询
@Autowired
private HistoryService historyService;

/**
* 历史流程实例查询
*/
@Test
public void historicProcessInstanceQuery(){
   List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processInstanceId("b3fbd774-c2dd-11ec-bcbc-004238a4ec73").list();
   for (HistoricProcessInstance historicProcessInstance : list) {
       System.out.println(historicProcessInstance.getProcessDefinitionName());
       System.out.println(JSON.toJSONString(historicProcessInstance));
   }
}

/**
* 查询实例历史任务
*/
@Test
public void createHistoricTaskInstanceQuery(){
   List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
           //按结束时间正向排序
           .orderByHistoricTaskInstanceEndTime().asc()
           .processInstanceId("b3fbd774-c2dd-11ec-bcbc-004238a4ec73").list();
   for (HistoricTaskInstance historicTaskInstance : list) {
       System.out.println("task "+historicTaskInstance.getName()+" == "+historicTaskInstance.getAssignee());
   }
}

/**
* 查询用户历史任务
*/
@Test
public void createHistoricTaskInstanceQueryByTaskAssignee(){
   List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
           //按结束时间正向排序
           .orderByHistoricTaskInstanceEndTime().asc()
           .taskAssignee("user").list();
   for (HistoricTaskInstance historicTaskInstance : list) {

       System.out.println("task "+historicTaskInstance.getName()+" == "+historicTaskInstance.getAssignee());
   }


}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/730820.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存