前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >activiti的简单使用说明

activiti的简单使用说明

作者头像
用针戳左手中指指头
发布2021-03-02 14:56:58
1.8K0
发布2021-03-02 14:56:58
举报
文章被收录于专栏:学习计划

相关表说明

  • ACT_RE_*: 具有RE前缀的表,包含静态信息,如进程定义和进程资源(图像、规则等)。repository
  • ACT_RU_ **:包含RU的表表示进程实例、用户任务、变量、作业等的运行时数据的运行时表。Activiti 仅在进程实例执行期间存储运行时数据,并在进程实例结束时删除记录。这样可保持运行时表小而快速。runtime
  • ACT_ID_ **:ID标识的表表示信息表,如用户、组等。identity
  • **ACT_HI_****:包含HI的是历史数据表,如过去的流程实例、变量、任务等。history
  • ACT_GE_*:数据,用于各种用例。general

集成配置

我使用SpringBoot进行集成

代码语言:javascript
复制
  spring:
      activiti:
        #校验、部署流程文件
        check-process-definitions: true
        #自动更新数据库结构
        database-schema-update: true
        #流程定义文件存放目录
        process-definition-location-prefix: classpath:/processes/
        #检查身份信息表是否存在
        db-identity-used: true

使用讲解

下面两个是activiti的官方文档说明和api说明

java Doc https://www.activiti.org/javadocs/

activiti

https://www.activiti.org/userguide/

它有这么几个服务

代码语言:javascript
复制
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 提供了处理流程实例不同步骤的结构和行为
RuntimeService runtimeService = processEngine.getRuntimeService();
// 提供运营管理和操作流程实例的服务
RepositoryService repositoryService = processEngine.getRepositoryService();
// 提供有关任务相关功能的服务
TaskService taskService = processEngine.getTaskService();

ManagementService managementService = processEngine.getManagementService();
// 提供对用户和群组的管理
IdentityService identityService = processEngine.getIdentityService();
// 提供activiti引擎收集的历史记录信息服务
HistoryService historyService = processEngine.getHistoryService();

FormService formService = processEngine.getFormService();

RepositoryService:提供运营管理和操作流程实例的服务

RuntimeService:提供了处理流程实例不同步骤的结构和行为

TaskService:提供有关任务相关功能的服务

IdentityService:提供对用户和群组的管理

FormService:也可以启动一个流程

HistoryService:提供activiti引擎收集的历史记录信息服务

ManagementService:job任务查询和数据库操作

DynamicBpmnService:无需重新部署就能修改流程定义内容

注册上面的服务为spring bean:

代码语言:javascript
复制
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.io.IOException;

@Configuration
public class ActivitiConfig {
    private Logger logger = LoggerFactory.getLogger(ActivitiConfig.class);

    @Value("${spring.activiti.database-schema-update}")
    private String databaseSchemaUpdate;

    @Value("${spring.activiti.db-identity-used}")
    private boolean dbIdentityUsed;

    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Bean
    public ProcessEngine processEngine(DataSourceTransactionManager transactionManager, DataSource dataSource) throws IOException {
        logger.info("==========activiti=======开始==============");
        SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();

    /*  自动部署已有的流程文件
    作用相当于 (据bpmn文件部署流程repositoryService.createDeployment().addClasspathResource("singleAssignee.bpmn").deploy();)*/

        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourceLoader.CLASSPATH_URL_PREFIX + "processes/*.bpmn");
        configuration.setTransactionManager(transactionManager);
        //设置数据源
        configuration.setDataSource(dataSource);
        //是否每次都更新数据库
        //configuration.setDatabaseSchemaUpdate(databaseSchemaUpdate);
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
        //configuration.setAsyncExecutorActivate(false);
        configuration.setDeploymentResources(resources);
        //设置是否使用activti自带的用户体系
        configuration.setDbIdentityUsed(dbIdentityUsed);
        return configuration.buildProcessEngine();
    }

    /**
     * 工作流仓储服务
     * @param processEngine
     * @return
     */
    @Bean
    public RepositoryService repositoryService(ProcessEngine processEngine) {
        return processEngine.getRepositoryService();
    }

    /**
     * 工作流运行服务
     * @param processEngine
     * @return
     */
    @Bean
    public RuntimeService runtimeService(ProcessEngine processEngine) {
        return processEngine.getRuntimeService();
    }

    /**
     * 工作流任务服务
     * @param processEngine
     * @return
     */
    @Bean
    public TaskService taskService(ProcessEngine processEngine) {
        return processEngine.getTaskService();
    }

    /**
     * 工作流历史数据服务
     * @param processEngine
     * @return
     */
    @Bean
    public HistoryService historyService(ProcessEngine processEngine) {
        return processEngine.getHistoryService();
    }

    /**
     * 工作流管理服务
     * @param processEngine
     * @return
     */
    @Bean
    public ManagementService managementService(ProcessEngine processEngine) {
        return processEngine.getManagementService();
    }

    /**
     * 工作流唯一服务
     * @param processEngine
     * @return
     */
    @Bean
    public IdentityService identityService(ProcessEngine processEngine) {
        return processEngine.getIdentityService();
    }
}

部署的三种方式

它支持的部署方式有classpath、inputStream、zip/bar格式压缩包.

  • classpath方式 通过读取classpath资源文件可以部署方式可以读取bpmn和xml文件资源。 两个的读取方式一样,只是文件类型不一样
代码语言:javascript
复制
// 读取bpmn
// 创建构建器
Deployment dep = repositoryService.createDeployment()
    // 添加资源
    .name("测试流程").addClasspathResource("processes/testProcess.bpmn")
    // 执行部署
    .deploy();

// 读取xml
Deployment dep = repositoryService.createDeployment()
    .name("测试流程").addClasspathResource("processes/testProcess.xml")
    .deploy();
1593592336834
1593592336834
  • inputstream方式
代码语言:javascript
复制
		String fileName = "processes/testProcess.bpmn";
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName);
// inputstream部署
        repositoryService.createDeployment()
                .name("processes.zip")
                .addInputStream("testProcess.bpmn", inputStream)
                .deploy();

// 启动流程
        Map<String,Object> map = new HashMap<>();
        map.put("user1","ali");
        ProcessInstance p1 = runtimeService.startProcessInstanceByKey("myProcess", map);

        System.out.println("部署流程-id:"+p1.getId());
        System.out.println("部署流程-name:"+p1.getName());
1593592151589
1593592151589
代码语言:javascript
复制
		String zipFileName = "processes/processes.zip";
        System.out.println("部署包:"+zipFileName);
//        ZipInputStream inputStream = new ZipInputStream(new FileInputStream(zipFileName));
        // 读取资源路径下
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(zipFileName);

        repositoryService.createDeployment()
                .name("processes.zip")
                .addZipInputStream(new ZipInputStream(inputStream))
                .deploy();
// 启动流程
        Map<String,Object> map = new HashMap<>();
        map.put("userId","aliZip");
        ProcessInstance p1 = runtimeService.startProcessInstanceByKey("zipProcess1", map);
        ProcessInstance p2 = runtimeService.startProcessInstanceByKey("zipProcess2", map);

        System.out.println("部署流程1-id:"+p1.getId());
        System.out.println("部署流程1-name:"+p1.getName());

        System.out.println("部署流程2-id:"+p2.getId());
        System.out.println("部署流程2-name:"+p2.getName());
1593592270876
1593592270876

在部署后会创建对应的流程定义,数据库表里流程定义的key即为流程图(bpmn)里的流程定义ID,并且会分配一个版本,格式为:key:value,它会根据key匹配,相同的版本+1。

image-20210221172302414
image-20210221172302414

启动

启动步骤一般是和部署分开的。

发起流程由runtimeService.startProcessInstanceByKey() 发起,他有四个重载方法,完全能够应对我们的日常开发:

代码语言:javascript
复制
startProcessInstanceById(String processDefinitionId)
startProcessInstanceById(String processDefinitionId, Map<String,Object> variables)
startProcessInstanceById(String processDefinitionId, String businessKey)
startProcessInstanceById(String processDefinitionId, String businessKey, Map<String,Object> variables)

参数说明:

processDefinitionId:流程定义id,流程图中的id,数据库表中的key

variables:要传递到下一环节的变量

businessKey:上下文参数,可以理解位业务关键字,区别业务系统的键

下面以一个简单流程示例说明

1593592740538
1593592740538
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<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:activiti="http://activiti.org/bpmn" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="myProcess" name="My process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <endEvent id="endevent1" name="End"></endEvent>
    <userTask id="user_task_1" name="组长审批" activiti:assignee="${user1}"></userTask>
    <userTask id="user_task_2" name="领导审批" activiti:assignee="${user2}"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="user_task_1"></sequenceFlow>
    <sequenceFlow id="flow2" sourceRef="user_task_1" targetRef="user_task_2"></sequenceFlow>
    <sequenceFlow id="flow3" sourceRef="user_task_2" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="345.0" y="20.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="345.0" y="280.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="user_task_1" id="BPMNShape_user_task_1">
        <omgdc:Bounds height="55.0" width="105.0" x="310.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="user_task_2" id="BPMNShape_user_task_2">
        <omgdc:Bounds height="55.0" width="105.0" x="310.0" y="181.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="362.0" y="55.0"></omgdi:waypoint>
        <omgdi:waypoint x="362.0" y="70.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="362.0" y="125.0"></omgdi:waypoint>
        <omgdi:waypoint x="362.0" y="181.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="362.0" y="236.0"></omgdi:waypoint>
        <omgdi:waypoint x="362.0" y="280.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

部署后的流程,可以创建多个实例,所以只要部署一次就可以了。

如果流程图中有配置assignee,那么要在参数中加上assignee对应的变量键值

在启动时,assignee的参数是param最上层的,而在之后的任务中的assignee参数是在variable这层下面。

代码语言:javascript
复制
{
"processInitKey":"myProcess",
"user1":"sss"
}
代码语言:javascript
复制
    public String startProcess(Map<String, Object> param) {
        ProcessInstance process = runtimeService.startProcessInstanceByKey(String.valueOf(param.get("processInitKey")), param);
        System.out.println("流程启动=============");
        System.out.println("processId: "+ process.getId());
        return process.getId();
    }

任务 task

用户任务 user task

以上一节的流程图为例

1594017200145
1594017200145

这里的流程沿用上面的流程

流程比较简单,只有启动、结束和user task,用户任务的表示就和图中红框一样,xml表示:

代码语言:javascript
复制
  <userTask id="user_task_1" name="组长审批" activiti:assignee="${user1}"></userTask>

属性:

activiti:assignee :直接分配任务给指定用户

activiti:candidateUsers :指定用户为任务候选人

activiti:candidateGroups : 指定用户组的用户为任务候选人

自定义属性配置,这个我还没有看

还可以配置描述:

代码语言:javascript
复制
<userTask id="theTask" name="Schedule meeting" >
  <documentation>
	  ......
  </documentation>
</userTask>

form 属性和form key还不知道,需要补充

在视图上配置的话比较简单,常用的是配置assignee和listener,上面的【组长审批】【领导审批】的assignee分别配置了{user1}和{user2},使用符号

查询任务

TaskQuery、TaskInfoQuery提供了查询方法,它的获取方式是taskService.createTaskQuery(),查询方法比较多,这里列举几个

代码语言:javascript
复制
// 根据任务ID查询
T taskId(String taskId);
// 根据任务名称查询
T taskName(String name);
// 根据任务名称列表查询
T taskNameIn(List<String> nameList);
// 模糊任务名称查询
T taskNameLike(String nameLike);
// 根据分配给指定用户查询
T taskAssignee(String assignee);
// 根据分配给指定用户模糊查询
T taskAssigneeLike(String assigneeLike);
// 根据分配给指定用户列表查询
T taskAssigneeIds(List<String> assigneeListIds);
// 委托人委托的任务
T taskOwner(String owner);
// 根据流程实例id查询
T processInstanceId(String processInstanceId);
// 根据流程实例id列表查询
T processInstanceIdIn(List<String> processInstanceIds);
// 根据路程定义key查询
T processDefinitionKey(String processDefinitionKey);
// 创建时间之前
T taskCreatedBefore(Date before);
// 创建时间之后
T taskCreatedAfter(Date after);

这里我只列举两个查询下面测试可能用到的,因为都是一样的写法。

第一种:

根据用户ID查询

代码语言:javascript
复制
/**
     * 通过用户名查询该用户的所有任务
     * @param userId
     * @return
     */
    public String queryByUser(String userId) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).list();
        List<TaskVo> result = new ArrayList<>();
        addTaskVo(result, tasks);
        // printTaskInfo(tasks);
        return JSON.toJSONString(result);
    }

第二种:

根据流程发起人查询,这种方式会查出该流程发起人下的所有任务

代码语言:javascript
复制
 /**
     * 通过发起者查询该用户发起的所有任务
     * @param userId
     * @return
     */
    public String queryByInitiator(String userId) {
        // 先查出流程实例
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().startedBy(userId).list();

        List<TaskVo> result = new ArrayList<>();
        for (ProcessInstance processInstance : list) {
            // 遍历查出任务对象
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
            addTaskVo(result, tasks);
            // printTaskInfo(tasks);
        }
        return JSON.toJSONString(result);
    }

审核任务

走到【组长审批】这一环节后,想要继续往下走,就需要完成当前任务,完成任务需要对应的用户去审批,然而,它本身没有去判断当前用户的功能,所以这个由指定用户审批的功能要自己实现(即在上一个环节你指定了用户,但是在当前环节依然可以由别人去审核完成)

代码语言:javascript
复制
 /**
     * 根据 taskid 审核任务
     */
    public boolean audit(AudiaVo audiaVo) {
        boolean result = false;

        // 审核需要判断处理人是否有其权限(需要查询用户表和用户组表)
//        Task task = taskService.createTaskQuery().taskId(audiaVo.getTaskId()).singleResult();
//        if (StringUtils.isEmpty(task.getAssignee())) {
//            return result;
//        }
//
//        // 查询组
//        GroupQuery groupQuery = identityService.createGroupQuery().groupMember(audiaVo.getUserId());
//        if (groupQuery == null) {
//            logger.info("无用户组信息");
//        }

        // 设置当前处理人
        Authentication.setAuthenticatedUserId(audiaVo.getUserId());
        // 添加评论
        taskService.addComment(audiaVo.getTaskId(), audiaVo.getProcessInstanceId(), audiaVo.getComment());
        // 完成任务
        taskService.complete(audiaVo.getTaskId(), audiaVo.getVariable());
        return true;
    }

它完成一个任务,需要受理人、任务ID、下一个任务的参数,如果需要发表评论则还需要流程实例id

代码语言:javascript
复制
{
  "taskId": "132520",
  "userId": "ali",
  "comment": "ok",
  "variable": {
    "isAgree": 1
  }
}

委托任务

处理人可以委托任务给其他人员,和审核任务一样,他没有自动校验委托人是否有权限

代码语言:javascript
复制
  /**
     * 委托任务
     * @param param
     */
    public void owner(Map<String, String> param) {
        String taskId = param.get("taskId");
        String userId = param.get("userId");
        // todo 判断是否有委托权限
        taskService.delegateTask(taskId, userId);
    }

服务任务 Service Task

示例:

1593776921122
1593776921122
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<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:activiti="http://activiti.org/bpmn" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="process2" name="process2" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="审批" activiti:assignee="${userId}"></userTask>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow>
    <serviceTask id="servicetask1" name="不同意" activiti:class="com.lry.delegate.TaskDelegate">
      <extensionElements>
        <activiti:field name="isAgree">
          <activiti:expression><![CDATA[${isAgree}]]></activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="flow3" sourceRef="exclusivegateway1" targetRef="servicetask1">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${isAgree == 0}]]></conditionExpression>
    </sequenceFlow>
    <serviceTask id="servicetask2" name="同意" activiti:class="com.lry.delegate.TaskDelegate">
      <extensionElements>
        <activiti:field name="nextParam">
          <activiti:expression><![CDATA[${nextParam}]]></activiti:expression>
        </activiti:field>
        <activiti:field name="isAgree">
          <activiti:expression><![CDATA[${isAgree}]]></activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="flow4" sourceRef="exclusivegateway1" targetRef="servicetask2">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${isAgree == 1}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow5" sourceRef="servicetask2" targetRef="endevent1"></sequenceFlow>
    <sequenceFlow id="flow6" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_process2">
    <bpmndi:BPMNPlane bpmnElement="process2" id="BPMNPlane_process2">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="120.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="230.0" y="140.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="670.0" y="151.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="395.0" y="148.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
        <omgdc:Bounds height="55.0" width="105.0" x="500.0" y="230.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="servicetask2" id="BPMNShape_servicetask2">
        <omgdc:Bounds height="55.0" width="105.0" x="490.0" y="60.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="155.0" y="167.0"></omgdi:waypoint>
        <omgdi:waypoint x="230.0" y="167.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="335.0" y="167.0"></omgdi:waypoint>
        <omgdi:waypoint x="395.0" y="168.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="415.0" y="188.0"></omgdi:waypoint>
        <omgdi:waypoint x="552.0" y="230.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="415.0" y="148.0"></omgdi:waypoint>
        <omgdi:waypoint x="542.0" y="115.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="542.0" y="115.0"></omgdi:waypoint>
        <omgdi:waypoint x="687.0" y="151.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
        <omgdi:waypoint x="552.0" y="230.0"></omgdi:waypoint>
        <omgdi:waypoint x="687.0" y="186.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

我在【同意】和【不同意】task上绑定了上面的同一个类,并且,添加了两个字段属性,这两个属性的获取方式看下面代码

1594888604635
1594888604635

service task的监听类需要实现JavaDelegate:

代码语言:javascript
复制
public class TaskDelegate implements JavaDelegate{

	// 这里的名称和流程图里配置的字段名称一样
	// 而传递的参数值是expression的表达式
	private Expression nextParam;

	private Expression isAgree;

	@Override
	public void execute(DelegateExecution execution) {
		System.out.println("执行 taskDelegate======== ");
		String value = (String)nextParam.getValue(execution);
		Integer s = (Integer)isAgree.getValue(execution);
// 第一种获取方式
		System.out.println("nextParam  参数值:"+value);
		System.out.println("isAgree   参数值: " + s);
// 第二种获取方式
		System.out.println("2  isAgree " + execution.getVariable("isAgree"));
		System.out.println("2 nextParam " + execution.getVariable("nextParam"));

		System.out.println("审批同意后 执行相应业务");
	}

}

执行完审批任务后,他会根据判断isAgree的参数,然后进入TaskDelegate的execute的方法。

参数:

代码语言:javascript
复制
{
  "taskId": "200007",
  "userId": "bli",
  "comment": "ok",
  "variable": {
    "isAgree": 1,
    "nextParam": "oklsdsdsd"
  }
}
1594888486936
1594888486936

历史任务

常用的应该就是通过用户id查询任务了,这个查询能查询到已经处理完的,和未处理的,包括了所有的任务。

代码语言:javascript
复制
/**
     * 通过用户id查询历史任务
     * @param userId
     * @return
     */
public List<HistoricTaskInstance> history(String userId) {
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).list();
        System.out.println(JSON.toJSONString(list));
        return list;
    }

监听器

代码语言:javascript
复制
// 全局监听器
ExecutionListener
ActivitiEventListener 3.17.1
TaskListener

全局监听器 ExecutionListener

接口中定义了3个事件名称,用来标识流程的开始、结束和节点连线的。

这三个状态以常亮方式定义在BaseExecutionListener

代码语言:javascript
复制
String EVENTNAME_START = "start";
String EVENTNAME_END = "end";
String EVENTNAME_TAKE = "take";

实现的方法notify提供了DelegateExecution对象,它可以操作流程执行对象,具体操作方法可以查看他的API。

代码语言:javascript
复制
void notify(DelegateExecution delegateExecution)

它的使用方式和service task差不多。

实现监听器:

代码语言:javascript
复制
public class CustomExecutionListener implements ExecutionListener {
    private static final long serialVersionUID = 1961119667797372483L;

    private Expression message;

    public Expression getMessage() {
        return message;
    }

    public void setMessage(Expression message) {
        this.message = message;
    }

    @Override
    public void notify(DelegateExecution delegateExecution) {
        String eventName = delegateExecution.getEventName();
        if (BaseExecutionListener.EVENTNAME_START.equals(eventName)) {
            System.out.println("-----------ExecutionListener start----------------");
        } else if (BaseExecutionListener.EVENTNAME_END.equals(eventName)) {
            System.out.println("-----------ExecutionListener end----------------");
        } else {
            System.out.println("-----------ExecutionListener take----------------");
        }
    }
}

下面例子使用上面流程图,不同的是添加了监听器

流程图:

1593673875879
1593673875879
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<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:activiti="http://activiti.org/bpmn" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="myProcess" name="My process" isExecutable="true">
    <extensionElements>
      <activiti:executionListener event="end" class="com.lry.listener.CustomExecutionListener">
        <activiti:field name="message">
          <activiti:string><![CDATA[流程结束]]></activiti:string>
        </activiti:field>
      </activiti:executionListener>
      <activiti:executionListener event="start" class="com.lry.listener.CustomExecutionListener">
        <activiti:field name="message">
          <activiti:string><![CDATA[流程启动]]></activiti:string>
        </activiti:field>
      </activiti:executionListener>
    </extensionElements>
    <startEvent id="startevent1" name="Start">
      <extensionElements>
        <activiti:executionListener event="MyExecutionListener " class="com.lry.listener.CustomExecutionListener"></activiti:executionListener>
      </extensionElements>
    </startEvent>
    <endEvent id="endevent1" name="End"></endEvent>
    <userTask id="user_task_1" name="组长审批" activiti:assignee="${user1}"></userTask>
    <userTask id="user_task_2" name="领导审批" activiti:assignee="${user2}"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="user_task_1">
      <extensionElements>
        <activiti:executionListener event="take" class="com.lry.listener.CustomExecutionListener">
          <activiti:field name="message">
            <activiti:string><![CDATA[第一环节]]></activiti:string>
          </activiti:field>
        </activiti:executionListener>
      </extensionElements>
    </sequenceFlow>
    <sequenceFlow id="flow2" sourceRef="user_task_1" targetRef="user_task_2">
      <extensionElements>
        <activiti:executionListener event="take" class="com.lry.listener.CustomExecutionListener"></activiti:executionListener>
      </extensionElements>
    </sequenceFlow>
    <sequenceFlow id="flow3" sourceRef="user_task_2" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="345.0" y="20.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="345.0" y="280.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="user_task_1" id="BPMNShape_user_task_1">
        <omgdc:Bounds height="55.0" width="105.0" x="310.0" y="70.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="user_task_2" id="BPMNShape_user_task_2">
        <omgdc:Bounds height="55.0" width="105.0" x="310.0" y="181.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="362.0" y="55.0"></omgdi:waypoint>
        <omgdi:waypoint x="362.0" y="70.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="362.0" y="125.0"></omgdi:waypoint>
        <omgdi:waypoint x="362.0" y="181.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="362.0" y="236.0"></omgdi:waypoint>
        <omgdi:waypoint x="362.0" y="280.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

操作步骤:

  1. 点击流程图空白区域
  2. 点击【properties】-》【Listeners】-》【new】
  3. event输入框写start、end、take这三个值中的一个,他们对应着事件名称,不能变
  4. 点击【select class】选择我们写好的监听器CustomExecutionListener
  5. 如果有参数,比如监听器定义Expression类型的属性,还可以新增【fields】,这里配置field作用不是很大,也不好说明,在下一个任务监听器配置时再做说明。
1593680259234
1593680259234

执行方法:

代码语言:javascript
复制
 public String initProcess1() {
        System.out.println("发起流程....");

        Deployment dep = repositoryService.createDeployment().name("测试流程")
                .addClasspathResource("processes/testProcess.bpmn").deploy();

        // 读取xml方式
//        Deployment dep = repositoryService.createDeployment().name("测试流程")
//                .addClasspathResource("processes/test.xml").deploy();

        System.out.println("部署id:" + dep.getId());
        System.out.println("部署名称:" + dep.getName());

        System.out.println("-----------------");

        identityService.setAuthenticatedUserId("startUser");

        Map<String, Object> map = new HashMap<>();
        map.put("user1", "ali");
        ExecutionEntity pi = (ExecutionEntity) runtimeService.startProcessInstanceByKey("myProcess", map);
        System.out.println("启动流程成功!");
        System.out.println("流程实例id:" + pi.getId());
        System.out.println("流程定义id:" + pi.getProcessInstanceId());
        System.out.println("流程实例名称:" + pi.getName());

        System.out.println("-------------------------------------");

        // 查询任务
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        System.out.println("任务ID: " + task.getId());
        System.out.println("任务的办理人: " + task.getAssignee());
        System.out.println("任务名称: " + task.getName());
        System.out.println("任务的创建时间: " + task.getCreateTime());
        System.out.println("流程实例ID: " + task.getProcessInstanceId());

        System.out.println("发起成功....");

        return "success";
    }

执行结果:

启动流程的时候会触发start时间,然后触发take事件(连线)然后到下一个环节(节点)

1593674360738
1593674360738

任务监听器 TaskListener

任务监听器,event支持:create、assignment、complete、delete四种类型,

还有一种就是all,它是上面四种的集合,就是我们在配置是,可以配置这五个值。

这四种类型以常亮方式定义在BaseTaskListener

继续上面的例子,我们再新增一个监听器,实现TaskListener接口。

代码语言:javascript
复制
public class CustomTaskListener implements TaskListener {

    private Expression taskFlag;

    @Override
    public void notify(DelegateTask delegateTask) {
        String eventName = delegateTask.getEventName();
        System.out.println("任务监听器:" + eventName);
        String value = (String)taskFlag.getValue(delegateTask);
        System.out.println("taskFlag:" + value);
        // 获取变量:user2的值
        String user2 = String.valueOf(delegateTask.getVariable("user2"));
        System.out.println("user2: " + user2);
    }
}

event支持:create、assignment、complete、delete、all五个值

image-20210204161123375
image-20210204161123375

然后发起部署请求,并完成任务;

方法参数:{ “taskId”:“207518”, “userId”:“ali”, “comment”:“ok”, “variable”: { “user2”: “bli” } }

image-20210204161447397
image-20210204161447397

修改event为all(需要重新部署),支持所有事件类型,可以看出assignment是在create事件之前的

image-20210204170053319
image-20210204170053319

在很多时候,是在任务监听器中调用我们自己的dao层去查询操作数据库,比如下面代码获取的user2是某个用户id,然后通过spring工具获取dao的bean,然后通过这个bean根据id查询是否有这个用户或是

代码语言:javascript
复制
// 获取变量:user2的值
String user2 = String.valueOf(delegateTask.getVariable("user2"));

查看流程图

使用监听器测试的流程图;

代码语言:javascript
复制
public InputStream tracePhoto(String proInstanceId) {
        // 通过历史表查询历史流程
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(proInstanceId).singleResult();
        if (historicProcessInstance == null) {
            return null;
        }
        // 通过历史流程里的定义id查询bpmn
        BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
        // 获取激活的任务节点
        List<Task> list = taskService.createTaskQuery().processInstanceId(proInstanceId).list();
        List<String> activeActivityIds = new ArrayList<>();
        for (Task task : list) {
            activeActivityIds.add(task.getTaskDefinitionKey());
        }
    // 这里设置字体避免乱码
        return processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator().generateDiagram(bpmnModel, "png", activeActivityIds, new ArrayList<String>(), "宋体", "宋体", "宋体", null, 1.0);

    }
image-20210218113048628
image-20210218113048628

这里需要注意的是要设置字体为宋体,网上朋友说因为不存在arial字体,所以生成的图片是乱码的,我尝试断点看了下,在生成图片的类中,默认是arial。

image-20210218113143788
image-20210218113143788
image-20210218113511521
image-20210218113511521

UEL表达式设置变量

activiti支持uel-value 、uel-method两种方式,uel-method用的不多,不做说明;

对于uel-value的也可以这样:${day > 3}

uel表达式设置变量有以下4个注意点:

  • 如果 UEL表达式中流程变量名不存在则报错。
  • 如果 UEL表达式中流程变量值为空 NULL,流程不按 UEL 表达式去执行,而流程结束 。
  • 如果 UEL表达式都不符合条件,流程结束
  • 如果连线不设置条件,会走 flow 序号小的那条线

简单改造上面流程如下,从组长审批分两个支路,前面有一个类似的使用service task实现,这里是通过变量来进行分流的。

image-20210221165650190
image-20210221165650190
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<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:activiti="http://activiti.org/bpmn" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="uelKey" name="testUel" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="level1" name="组长审批" activiti:assignee="${user1}"></userTask>
    <userTask id="level2" name="领导审批" activiti:assignee="${user2}"></userTask>
    <userTask id="level3" name="经理审批" activiti:assignee="${user3}"></userTask>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="level1"></sequenceFlow>
    <sequenceFlow id="flow2" sourceRef="level1" targetRef="level2">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${day <= 3}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow3" sourceRef="level1" targetRef="level3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${day > 3}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow4" sourceRef="level2" targetRef="endevent1"></sequenceFlow>
    <sequenceFlow id="flow5" sourceRef="level3" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_uelKey">
    <bpmndi:BPMNPlane bpmnElement="uelKey" id="BPMNPlane_uelKey">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="1160.0" y="240.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="level1" id="BPMNShape_level1">
        <omgdc:Bounds height="55.0" width="105.0" x="1125.0" y="350.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="level2" id="BPMNShape_level2">
        <omgdc:Bounds height="55.0" width="105.0" x="1040.0" y="500.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="level3" id="BPMNShape_level3">
        <omgdc:Bounds height="55.0" width="105.0" x="1220.0" y="500.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="1160.0" y="640.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="1177.0" y="275.0"></omgdi:waypoint>
        <omgdi:waypoint x="1177.0" y="350.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="1177.0" y="405.0"></omgdi:waypoint>
        <omgdi:waypoint x="1092.0" y="500.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="1177.0" y="405.0"></omgdi:waypoint>
        <omgdi:waypoint x="1272.0" y="500.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="1092.0" y="555.0"></omgdi:waypoint>
        <omgdi:waypoint x="1177.0" y="640.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="1272.0" y="555.0"></omgdi:waypoint>
        <omgdi:waypoint x="1177.0" y="640.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

分别选中组长审批分出的线,设置其变量条件

image-20210221165957934
image-20210221165957934

当传入如下的参数day<=3,就会进入领导审批,反之进入经理审批

image-20210221170139442
image-20210221170139442
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 相关表说明
  • 集成配置
  • 使用讲解
    • 部署的三种方式
      • 启动
        • 任务 task
          • 用户任务 user task
          • 查询任务
          • 审核任务
          • 委托任务
          • 服务任务 Service Task
          • 历史任务
        • 监听器
          • 全局监听器 ExecutionListener
          • 任务监听器 TaskListener
        • 查看流程图
          • UEL表达式设置变量
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档