springBoot(若依)集成camunda

news2024/11/15 13:22:48

1、下图为项目结构

2、最外层 pom引入依赖

<properties>
    <!--camunda 标明版本,注意要个自己的Spring 版本匹配,匹配关系自行查询官网-->
    <camunda.version>7.18.0</camunda.version>
</properties>

 3、common模块引入依赖

<!--camunda -->
<dependency>
    <groupId>org.camunda.bpm.springboot</groupId>
    <artifactId>camunda-bpm-spring-boot-starter</artifactId>
    <version>${camunda.version}</version>
</dependency>
<dependency>
    <groupId>org.camunda.bpm.springboot</groupId>
    <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
    <version>${camunda.version}</version>
</dependency>
<dependency>
    <groupId>org.camunda.bpm.springboot</groupId>
    <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
    <version>${camunda.version}</version>
</dependency>

<!-- Camunda BPMN模型处理 -->
<dependency>
    <groupId>org.camunda.bpm.model</groupId>
    <artifactId>camunda-bpmn-model</artifactId>
    <version>${camunda.version}</version>
</dependency>
<dependency>
    <groupId>org.camunda.bpm</groupId>
    <artifactId>camunda-engine</artifactId>
    <version>${camunda.version}</version>
</dependency>

 4、yml配置

# camunda配置
camunda:
  bpm:
    #登录用户信息
    admin-user:
      #用户名
      id: demo
      #密码
      password: demo
    filter:
      create: All tasks
    #指定数据类型
    database:
      type: mysql
      schema-update: false
    #禁止自动部署resources 下面的bpmn
    auto-deployment-enabled: false
    #禁止index 跳转到Camunda 自带的管理页面,默认为true
    #webapp:
      #index-redirect-enabled: true

4、若依要跳过鉴权

security配置:跳过鉴权

.antMatchers("/camunda/**").permitAll()

 

5、启动类加注解 ,如下

@EnableProcessApplication 激活一系列Camunda相关的自动配置,每次启动时自动读取resource下的npmn文件

5、npmn存放的位置

 

7、npmn文件xml和图片,其中办结节点为调用监听

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1ch5lf5" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.24.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.21.0">
  <bpmn:process id="testDemo" name="测试任务" isExecutable="true">
    <bpmn:startEvent id="StartEvent_1" name="发起人">
      <bpmn:extensionElements>
        <camunda:formData>
          <camunda:formField id="proposer" label="申请人" type="string" />
          <camunda:formField id="lineLeader" label="直线领导" type="string" />
          <camunda:formField id="businessId" label="业务id" type="string" />
          <camunda:formField id="deptLeader" label="部门领导" type="string" />
          <camunda:formField id="flowIdea" label="签字意见" type="string" />
        </camunda:formData>
      </bpmn:extensionElements>
      <bpmn:outgoing>Flow_0oxuvns</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:userTask id="Activity_0ku6n8k" name="直线领导" camunda:assignee="${lineLeader}">
      <bpmn:extensionElements>
        <camunda:formData>
          <camunda:formField id="approved" label="是否批准" type="boolean" defaultValue="null" />
          <camunda:formField id="flowIdea" label="签字意见" type="string" />
        </camunda:formData>
        <camunda:executionListener expression="${approved}" event="end" />
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_0oxuvns</bpmn:incoming>
      <bpmn:outgoing>Flow_02zwlzw</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sequenceFlow id="Flow_0oxuvns" sourceRef="StartEvent_1" targetRef="Activity_0ku6n8k" />
    <bpmn:serviceTask id="Activity_12qqf3m" name="办结" camunda:class="com.yuepu.oa.comunda.controller.serviceTask.OaTestDemoHandlerService">
      <bpmn:extensionElements />
      <bpmn:incoming>Flow_11pxoey</bpmn:incoming>
      <bpmn:incoming>Flow_0l7p130</bpmn:incoming>
      <bpmn:outgoing>Flow_09is9cj</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="Event_1g0bd0p">
      <bpmn:incoming>Flow_09is9cj</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_09is9cj" sourceRef="Activity_12qqf3m" targetRef="Event_1g0bd0p" />
    <bpmn:exclusiveGateway id="Gateway_1xg1je9">
      <bpmn:incoming>Flow_02zwlzw</bpmn:incoming>
      <bpmn:outgoing>Flow_0x0c9jk</bpmn:outgoing>
      <bpmn:outgoing>Flow_0l7p130</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sequenceFlow id="Flow_02zwlzw" sourceRef="Activity_0ku6n8k" targetRef="Gateway_1xg1je9" />
    <bpmn:sequenceFlow id="Flow_0x0c9jk" name="同意" sourceRef="Gateway_1xg1je9" targetRef="Activity_0eg4sr2">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${approved}</bpmn:conditionExpression>
    </bpmn:sequenceFlow>
    <bpmn:sequenceFlow id="Flow_11pxoey" sourceRef="Activity_0eg4sr2" targetRef="Activity_12qqf3m" />
    <bpmn:userTask id="Activity_0eg4sr2" name="部门领导" camunda:assignee="${deptLeader}">
      <bpmn:extensionElements>
        <camunda:formData>
          <camunda:formField id="approved" label="是否批准" type="boolean" defaultValue="null" />
          <camunda:formField id="flowIdea" label="签字意见" type="string" defaultValue="null" />
        </camunda:formData>
        <camunda:executionListener expression="${approved==null}" event="start" />
        <camunda:executionListener expression="${flowIdea==null}" event="start" />
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_0x0c9jk</bpmn:incoming>
      <bpmn:outgoing>Flow_11pxoey</bpmn:outgoing>
    </bpmn:userTask>
    <bpmn:sequenceFlow id="Flow_0l7p130" name="拒绝" sourceRef="Gateway_1xg1je9" targetRef="Activity_12qqf3m">
      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${!approved}</bpmn:conditionExpression>
    </bpmn:sequenceFlow>
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="testDemo">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="152" y="102" width="36" height="36" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="154" y="145" width="33" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0y4bxvq_di" bpmnElement="Activity_0ku6n8k">
        <dc:Bounds x="350" y="80" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_059wrju_di" bpmnElement="Activity_12qqf3m">
        <dc:Bounds x="850" y="80" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1g0bd0p_di" bpmnElement="Event_1g0bd0p">
        <dc:Bounds x="1032" y="102" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Gateway_1xg1je9_di" bpmnElement="Gateway_1xg1je9" isMarkerVisible="true">
        <dc:Bounds x="505" y="95" width="50" height="50" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_07ucok0_di" bpmnElement="Activity_0eg4sr2">
        <dc:Bounds x="640" y="80" width="100" height="80" />
        <bpmndi:BPMNLabel />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_0oxuvns_di" bpmnElement="Flow_0oxuvns">
        <di:waypoint x="188" y="120" />
        <di:waypoint x="350" y="120" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="297" y="102" width="33" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_09is9cj_di" bpmnElement="Flow_09is9cj">
        <di:waypoint x="950" y="120" />
        <di:waypoint x="1032" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_02zwlzw_di" bpmnElement="Flow_02zwlzw">
        <di:waypoint x="450" y="120" />
        <di:waypoint x="505" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0x0c9jk_di" bpmnElement="Flow_0x0c9jk">
        <di:waypoint x="555" y="120" />
        <di:waypoint x="640" y="120" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="586" y="102" width="23" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_11pxoey_di" bpmnElement="Flow_11pxoey">
        <di:waypoint x="740" y="120" />
        <di:waypoint x="850" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0l7p130_di" bpmnElement="Flow_0l7p130">
        <di:waypoint x="530" y="145" />
        <di:waypoint x="530" y="390" />
        <di:waypoint x="900" y="390" />
        <di:waypoint x="900" y="160" />
        <bpmndi:BPMNLabel>
          <dc:Bounds x="704" y="372" width="22" height="14" />
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>

 8、流程定义相关接口

controller层

package com.yuepu.oa.comunda.controller;

import com.yuepu.common.core.domain.AjaxResult;
import com.yuepu.common.core.domain.entity.SysUser;
import com.yuepu.common.utils.SecurityUtils;
import com.yuepu.oa.comunda.entity.DeployRequestParam;
import com.yuepu.oa.comunda.service.ProcessDefinitionService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

/**
 * 流程定义相关接口
 * @author lonewalker
 */
@RequestMapping("/process/definition")
@RequiredArgsConstructor
@RestController
public class ProcessDefinitionController {

    private final ProcessDefinitionService processDefinitionService;
    /**
     * 发布流程定义
     * @param requestParam 请求参数
     * @return 提示信息
     */
    @PostMapping("/deploy")
    public AjaxResult deployProcessDefinition(@RequestBody DeployRequestParam requestParam){
        SysUser userInfo = SecurityUtils.getLoginUser().getUser();
        return AjaxResult.success(processDefinitionService.deploy(userInfo,requestParam));
    }


    /**
     * 删除部署
     * @param deploymentId 部署id
     * @return 提示信息
     */
    @DeleteMapping("/deleteDeployment")
    public String deleteDeployment(@RequestParam("deploymentId")String deploymentId){
        return processDefinitionService.deleteDeployment(deploymentId);
    }

    /**
     * 挂起流程定义
     * @param processDefinitionId 流程定义id
     * @return 提示信息
     */

    @PostMapping("/suspendById")
    public String suspendProcessDefinitionById(@RequestParam("processDefinitionId")String processDefinitionId){
        return processDefinitionService.suspendProcessDefinitionById(processDefinitionId);
    }

    /**
     * 根据任务流程id获取模型
     * */
    @PostMapping("/getBpmnModelInstance")
    public AjaxResult getBpmnModelInstance(@RequestParam("processDefinitionId")String processDefinitionId){
        return AjaxResult.success(processDefinitionService.getBpmnModelInstance(processDefinitionId));
    }

    /**
     * 根据任务的key获取当前任务相关实例的最高版本号
     * */
    @PostMapping("/getHighestVersionByKey")
    public AjaxResult getLastVersionByKey(@RequestParam("taskKey")String taskKey){
        return AjaxResult.success(processDefinitionService.getHighestVersionByKey(taskKey));
    }


}





servicer层

package com.yuepu.oa.comunda.service;

import com.yuepu.common.core.domain.entity.SysUser;
import com.yuepu.oa.comunda.entity.DeployRequestParam;

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

/**
 * @author lonewalker
 */
public interface ProcessDefinitionService {

    /**
     * 发布流程定义
     * @param user          用户信息
     * @param requestParam  请求参数
     * @return 提示信息
     */
    String deploy(SysUser user, DeployRequestParam requestParam);

    /**
     * 删除部署
     * @param deploymentId 部署id
     * @return 提示信息
     */
    String deleteDeployment(String deploymentId);

    /**
     * 获取已部署的流程模型
     * @param processDefinitionId 流程定义id
     * @return 提示信息
     */
    String getBpmnModelInstance(String processDefinitionId);

    /**
     * 挂起流程定义
     * @param processDefinitionId 流程定义id
     * @return 提示信息
     */
    String suspendProcessDefinitionById(String processDefinitionId);


    /**
     * 根据任务的key获取当前任务相关实例的最高版本号
     * */
    int getHighestVersionByKey(String taskKey);
}






servicer层



package com.yuepu.oa.comunda.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.yuepu.common.core.domain.entity.SysUser;
import com.yuepu.common.utils.SecurityUtils;
import com.yuepu.common.utils.StringUtils;
import com.yuepu.oa.comunda.entity.DeployRequestParam;
import com.yuepu.oa.comunda.service.ProcessDefinitionService;
import com.yuepu.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.BpmPlatform;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.*;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.variable.VariableMap;
import org.camunda.bpm.engine.variable.Variables;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.Process;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * @author lonewalker
 */
@Slf4j
@RequiredArgsConstructor
@Service("processDefinitionService")
public class ProcessDefinitionServiceImpl implements ProcessDefinitionService {
    @Resource
    RepositoryService repositoryService;

    @Resource
    private TaskService taskService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    ISysUserService systemUserService;

    @Autowired
    private ProcessEngine processEngine;

    @Override
    public String deploy(SysUser user, DeployRequestParam requestParam) {
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource(requestParam.getResourcePath())
                .name(requestParam.getBpmnName())
                .deploy();
        //AssertUtil.checkService(ObjectUtil.isNull(deploy), ServiceExceptionEnum.SERVE_EXCEPTION.getDesc());
        return deploy.getId();
    }

    @Override
    public String deleteDeployment(String deploymentId) {
        //这里可以做级联删除,默认为false,级联会删除流程实例和job
        //repositoryService.deleteDeployment(deploymentId,true);
        repositoryService.deleteDeployment(deploymentId);
        return "删除成功";
    }

    @Override
    public String getBpmnModelInstance(String processDefinitionId) {
        BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(processDefinitionId);
        if (ObjectUtil.isNotNull(bpmnModelInstance)){
            Collection<UserTask> userTasks = bpmnModelInstance.getModelElementsByType(UserTask.class);
            Definitions definitions = bpmnModelInstance.getDefinitions();
            log.info("啥也不是");
        }
        return null;
    }

    @Override
    public String suspendProcessDefinitionById(String processDefinitionId) {
        repositoryService.suspendProcessDefinitionById(processDefinitionId);
        return "挂起成功";
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public int getHighestVersionByKey(String taskKey) {

        // 从流程引擎中获取仓库服务(RepositoryService)。
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 创建流程定义查询,按任务key进行过滤。
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(taskKey);

        // 执行查询并获取所有流程定义。
        List<ProcessDefinition> processDefinitions = query.list();

        // 如果没有找到任何流程定义,则返回-1表示无版本。
        if (processDefinitions.isEmpty()) {
            throw new RuntimeException("没有找到任何流程定义");
        }

        // 找到最高版本的流程定义。
        int highestVersion = processDefinitions.stream()
                .mapToInt(ProcessDefinition::getVersion)
                .max()
                .orElse(-1);

        // 输出最高版本号。
        System.out.println("最高版本号: " + highestVersion);

        return highestVersion;
    }
}

9、流程实例相关接口

package com.yuepu.oa.comunda.controller;


import com.yuepu.common.annotation.Log;
import com.yuepu.common.core.domain.AjaxResult;
import com.yuepu.common.enums.BusinessType;
import com.yuepu.oa.comunda.service.ProcessInstanceService;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;



/**
 * 流程实例相关接口
 *
 * @author lonewalker
 */
@RequestMapping("/process/instance/")
@RestController
public class ProcessInstanceController {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private TaskService taskService;

    @Autowired
    ProcessInstanceService processInstanceService;

    /**
     * 查询平台标签列表-根据平台id
     * {@code @Auth} jln
     */
    @Log(title = "删除流程", businessType = BusinessType.SELECT)
    @GetMapping(value = "/delByFlowId")
    public AjaxResult delByFlowId() {

        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().list();

        for (ProcessDefinition processDefinition : processDefinitions) {
            boolean cascade = true; // 级联删除历史数据
            boolean skipCustomListeners = false; // 不跳过自定义监听器
            boolean skipIoMappings = false; // 不跳过IO映射
            repositoryService.deleteProcessDefinition(processDefinition.getId(), cascade, skipCustomListeners, skipIoMappings);
        }


        return AjaxResult.success();
    }


    @Log(title = "查询流程定义", businessType = BusinessType.SELECT)
    @GetMapping(value = "/processes/def")
    public AjaxResult findProcesses() {
        List<ProcessDefinition> list=repositoryService.createProcessDefinitionQuery().list();
        return AjaxResult.success(list);
    }

    @Log(title = "查询任务", businessType = BusinessType.SELECT)
    @GetMapping(value = "/task")
    public AjaxResult finTasks() {
        return AjaxResult.success(taskService.createTaskQuery().list());
    }

    @PostMapping(value = "/deplopy")
    public String deplopy(@RequestParam String name,String zwName,String type){

      Deployment deployment= repositoryService.createDeployment()
              .name(zwName)
              .addClasspathResource("BPMN/"+name+"."+type).deploy();
        return deployment.getId()+"_"+deployment.getName();
    }




    /**
     * 获取流程实例模型
     * @return 提示信息
     */
    @PostMapping("/getCamundaModelById")
    public AjaxResult getCamundaModelById(@RequestParam String processInstanceId){
        return AjaxResult.success(processInstanceService.getCamundaModelById(processInstanceId));
    }




    /**
     * 根据流程实例id获取流程节点信息,不包含申请人
     */
    @PostMapping("/getNodeAndOwnersByProcessInstanceId")
    public AjaxResult getNodeAndOwnersByProcessInstanceId(@RequestParam String processInstanceId,String processDefinitionId){
        processInstanceService.getNodeAndOwnersByProcessInstanceId(processInstanceId,processDefinitionId);
        return AjaxResult.success();
    }

}




















package com.yuepu.oa.comunda.service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author lonewalker
 */
public interface ProcessInstanceService {


    /**
     * 重新激活任务实例
     * @param businessKey 业务id, 格式:表名_id+业务id
     *  desiredVariableNames, 需要携带的变量名列表
     * @return 提示信息
     */
    String RestartProcessByKeyExample(Set<String> desiredVariableNames,String processInstanceId,String businessKey,String flowIdea);

    void approveOrRejectByBusinessKeyExample(String processInstanceId,  String businessKey,Boolean approve,String flowIdea);

    String getNowNodeUserName(String processInstanceId);

    /**
     * 获取当前进行至节点
     * */
    String getNowCurrentName(String processInstanceId);



    /** 获取当前待处理人userId*/
    String getWaitCurrentName(String processInstanceId);

    Integer ifEndTaskById(String processInstanceId);

    HashMap getTaskDateBy(String processInstanceId);
    String getCamundaModelById(String processInstanceId);

    /**
     * 根据流程实例id获取流程节点信息,不包含申请人
     * */
    Map<String, String> getNodeAndOwnersByProcessInstanceId(String processInstanceId, String processDefinitionId);

}


















package com.yuepu.oa.comunda.service.impl;

import com.yuepu.common.core.domain.entity.SysUser;
import com.yuepu.common.utils.SecurityUtils;
import com.yuepu.common.utils.StringUtils;
import com.yuepu.oa.comunda.service.ProcessInstanceService;
import com.yuepu.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.history.HistoricVariableInstance;
import org.camunda.bpm.engine.history.HistoricVariableInstanceQuery;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.variable.VariableMap;
import org.camunda.bpm.engine.variable.Variables;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.UserTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author lonewalker
 */
@Slf4j
@RequiredArgsConstructor
@Service("processInstanceService")
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    @Resource
    private TaskService taskService;

    @Resource
    ISysUserService systemUserService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public String RestartProcessByKeyExample(Set<String> desiredVariableNames,String processInstanceId,String businessKey,String flowIdea) {
       /*
       这里是重启时还是重启当前版本的流程

       // 初始化流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 获取历史服务和运行时服务
        HistoryService historyService = processEngine.getHistoryService();
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 查询具有特定业务键的已完成历史流程实例,并按结束时间降序排列,取第一条记录(即最新完成的)
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .processInstanceId(processInstanceId)
                .finished()
                .orderByProcessInstanceEndTime().desc()
                .listPage(0, 1) // 从第0个开始,取1个结果
                .stream()
                .findFirst()
                .orElse(null);

        if (historicProcessInstance != null) {
            // 获取流程定义ID
            String processDefinitionId = historicProcessInstance.getProcessDefinitionId();

            // 获取上一次流程实例的所有变量
            Map<String, Object> filteredVariables = new HashMap<>();
            for (HistoricVariableInstance variable : historyService.createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId())
                    .list()) {
                if (desiredVariableNames.contains(variable.getName())) {
                    filteredVariables.put(variable.getName(), variable.getValue());
                }
            }
            filteredVariables.put("flowIdea",flowIdea);

            // 使用相同的业务键和变量重新启动流程实例
            ProcessInstance newProcessInstance = runtimeService.startProcessInstanceById(processDefinitionId, businessKey, filteredVariables);


            System.out.println("流程已重新启动,流程实例ID: " + newProcessInstance.getId());
            return newProcessInstance.getId();
        } else {
            throw new RuntimeException("未找到已完成的流程实例,业务键: " + businessKey);
        }*/

        // 以下是 每次启动时获取最新的版本进行启动,并携带之前的变量值
        // 初始化流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 获取历史服务、运行时服务和仓库服务
        HistoryService historyService = processEngine.getHistoryService();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        RepositoryService repositoryService = processEngine.getRepositoryService();

        // 查询具有特定业务键的已完成历史流程实例,并按结束时间降序排列,取第一条记录(即最新完成的)
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .processInstanceId(processInstanceId)
                .finished()
                .orderByProcessInstanceEndTime().desc()
                .singleResult(); // 直接获取单个结果

        if (historicProcessInstance != null) {
            // 使用流程定义的key和版本号查找最新的流程定义
            String processDefinitionKey = historicProcessInstance.getProcessDefinitionKey();
            int processDefinitionVersion = historicProcessInstance.getProcessDefinitionVersion();
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionKey(processDefinitionKey)
                    .processDefinitionVersion(processDefinitionVersion)
                    .latestVersion() // 确保获取的是最新版本
                    .singleResult();

            if (processDefinition != null) {
                // 获取上一次流程实例的所有变量
                Map<String, Object> filteredVariables = new HashMap<>();
                for (HistoricVariableInstance variable : historyService.createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId())
                        .list()) {
                    if (desiredVariableNames.contains(variable.getName())) {
                        filteredVariables.put(variable.getName(), variable.getValue());
                    }
                }

                // 使用相同的业务键和变量重新启动流程实例
                ProcessInstance newProcessInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), businessKey, filteredVariables);

                System.out.println("流程已重新启动,流程实例ID: " + newProcessInstance.getId());
                return newProcessInstance.getId();
            } else {
                throw new RuntimeException("未找到对应的流程定义,key: " + processDefinitionKey + ", 版本号: " + processDefinitionVersion);
            }
        } else {
            throw new RuntimeException("未找到已完成的流程实例,业务键: " + businessKey);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveOrRejectByBusinessKeyExample(String processInstanceId,String businessKey,Boolean approve,String flowIdea) {

        // 查询与业务键关联的当前任务
        Task currentTask = taskService.createTaskQuery()
                .processInstanceBusinessKey(businessKey)
                .processInstanceId(processInstanceId)
                .active()
                .singleResult();

        if (currentTask != null) {
            // 获取任务的办理人
            String assignee = currentTask.getAssignee();

            // 检查当前用户是否为任务的办理人
            if (SecurityUtils.getUserId().toString().equals(assignee)) {
                // 创建变量映射,可以包含审批结果等信息
                VariableMap variables = Variables.createVariables();
                variables.put("approved", approve);
                variables.put("flowIdea",flowIdea);

                // 完成任务,相当于批准
                taskService.complete(currentTask.getId(), variables);
               // throw new RuntimeException("与业务键 " + businessKey + " 关联的当前任务已被批准。");
            } else {
                throw new RuntimeException("当前登录人不是任务处理人,无法执行操作。");
            }
        } else {
            throw new RuntimeException("没有找到与之相关联的活动任务。");

        }
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public String getNowNodeUserName(String processInstanceId) {

             String assignee="";
            // 查询与业务键关联的当前任务
            Task currentTask = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .active()
                    .singleResult();

            if (currentTask != null) {
                // 获取任务的办理人
                 assignee = currentTask.getAssignee();
            }

        return assignee;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public String getNowCurrentName(String processInstanceId) {
        // 查询与业务键关联的当前任务
        Task currentTask = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .active()
                .singleResult();

        if (currentTask != null) {
            // 获取节点名称
             return currentTask.getName();
        }else{
           return "";
        }

    }

    @Override
    public String getWaitCurrentName(String processInstanceId) {
        // 初始化流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();

        // 查询该流程实例下的所有活动任务
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();

        StringBuilder sb=new StringBuilder();
        for (Task task : tasks) {
            System.out.println("Task ID: " + task.getId());
            System.out.println("Task Name: " + task.getName());
            System.out.println("Assignee: " + task.getAssignee()); // 处理人信息
            if(StringUtils.isNotEmpty(task.getAssignee())){
                SysUser user = systemUserService.selectUserById(Long.valueOf(task.getAssignee()));
                sb.append(user.getNickName()).append(",");
            }
        }
        return sb.toString();
    }

    @Override
    public Integer ifEndTaskById(String processInstanceId) {
        // 初始化流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 使用HistoryService查询历史流程实例
        HistoryService historyService = processEngine.getHistoryService();
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();


        if (historicProcessInstance != null) {
            // 检查流程实例状态
            String state = historicProcessInstance.getState();
            if ("COMPLETED".equals(state)) {
                // 流程实例已经结束。
                return 0;
            } else {
                // 其他状态可能包括"ACTIVE", "SUSPENDED", "TERMINATED"等,具体取决于流程实例的生命周期
                return 1;
            }
        } else {
            throw new RuntimeException("无法找到流程实例的历史记录,可能流程未结束或已被清理。");
        }
    }

    @Override
    public HashMap getTaskDateBy(String processInstanceId) {
        HashMap map=new HashMap();
        // 初始化流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();


        // 查询历史流程实例以获取流程定义ID
        HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService()
                .createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if (historicProcessInstance != null) {
            String processDefinitionId = historicProcessInstance.getProcessDefinitionId();

            // 使用流程定义ID查询流程定义的详细信息,包括版本号
            ProcessDefinition processDefinition = processEngine.getRepositoryService()
                    .createProcessDefinitionQuery()
                    .processDefinitionId(processDefinitionId)
                    .singleResult();

            if (processDefinition != null) {
                map.put("taskId",processDefinition.getId());
                map.put("taskVersion",processDefinition.getVersion());

            } else {
                throw new RuntimeException("未找到对应的流程定义");
            }
        } else {
            throw new RuntimeException("未找到指定的流程实例");
        }
        return map;
    }

    @Override
    public String getCamundaModelById(String processInstanceId) {
        // 初始化流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 获取RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();

        // 通过流程实例ID获取流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if (processInstance != null) {
            // 获取流程定义ID
            String processDefinitionId = processInstance.getProcessDefinitionId();

            // 通过流程定义ID获取BPMN模型实例
            BpmnModelInstance bpmnModelInstance = processEngine.getRepositoryService()
                    .getBpmnModelInstance(processDefinitionId);



            // 将BpmnModelInstance转换为XML字符串
            String bpmnXml =  Bpmn.convertToString(bpmnModelInstance);
            return  bpmnXml;

        } else {
            System.out.println("流程实例未找到!");
            throw new RuntimeException("流程实例未找到");
        }

    }
    @Override
    public Map<String, String> getNodeAndOwnersByProcessInstanceId(String processInstanceId,String processDefinitionId) {

        // 初始化流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 获取流程定义的BPMN模型实例
        BpmnModelInstance bpmnModelInstance = processEngine.getRepositoryService()
                .getBpmnModelInstance(processDefinitionId);

        // 查询历史任务实例获取负责人信息
        HistoryService historyService = processEngine.getHistoryService();

        Map<String, String> userTaskToAssigneeMap = new HashMap<>();
        // 获取申请人

        HistoricProcessInstance historicProcessInstance =
                historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(processInstanceId)
                        .singleResult();

        if (historicProcessInstance != null) {
            String starterUserId = historicProcessInstance.getStartUserId();
            if (starterUserId != null) {
                System.out.println("流程实例发起人(启动用户ID): " + starterUserId);
            } else {
                System.out.println("无法获取发起人信息,可能是因为流程实例是由系统或匿名触发的。");
            }
        } else {
            System.out.println("未找到对应的流程实例。");
        }
            // 获取所有UserTask节点的ID
            Collection<UserTask> userTasks = bpmnModelInstance.getModelElementsByType(UserTask.class);

            for (UserTask userTask : userTasks) {
                String userTaskId = userTask.getId();
                String userTaskName= userTask.getName();
                String variableKey=userTask.getCamundaAssignee();


                HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                        .processInstanceId(processInstanceId)
                        .taskDefinitionKey(userTaskId)
                        .singleResult();

                if (historicTaskInstance != null) {
                    String assignee = historicTaskInstance.getAssignee();
                    userTaskToAssigneeMap.put(userTaskName, assignee != null ? assignee : "未分配");
                } else {
                    // 如果当前没有找到对应的历史任务实例,可能是任务还未到达或已被删除
                    variableKey = extractVariable(variableKey);
                    // 查询启动时设置的变量
                    HistoricVariableInstanceQuery variableQuery = historyService.createHistoricVariableInstanceQuery()
                            .processInstanceId(processInstanceId)
                            .variableName(variableKey);

                    // 获取变量实例
                    HistoricVariableInstance historicVariableInstance = variableQuery.singleResult();

                    if (historicVariableInstance != null) {
                        userTaskToAssigneeMap.put(userTaskName, historicVariableInstance.getValue().toString());

                    } else {
                      throw new RuntimeException("该任务中没有该变量: '"+variableKey);
                    }
                }
        }

        userTaskToAssigneeMap.put("办结","");
        return userTaskToAssigneeMap;


    }


    /**
     * 从形如${...}的字符串中提取变量名。
     *
     * @param input 含有变量模板的字符串,如"${varName}"
     * @return 提取出的变量名,如"varName"
     */
    public static String extractVariable(String input) {
        // 正则表达式匹配${...}中的内容
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}");
        Matcher matcher = pattern.matcher(input);

        if (matcher.find()) {
            // 返回匹配到的第一组内容,即变量名
            return matcher.group(1);
        } else {
            // 如果没有匹配到,则返回null或抛出异常,根据实际情况选择
            return null;
        }
    }

}

10、测试用例


/**
 * OA测试任务
 * @TableName oa_test_demo
 */
@Data
public class OaTestDemo implements Serializable {
    /**
     * 
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 任务key
     */
    private String taskKey;


    /**
     * 状态(0=待审核,1=已完成,2=已驳回)
     */
    private Integer status;

    /**
     * 标题
     */
    private String title;

    /**
     * 
     */
    private String content;

    /**
     * 申请人id
     */
    private Integer proposerId;

    /**
     * 申请日期
     */
    private Date proposerTime;

    /**
     * 签字意见
     */
    private String flowIdea;

    /**
     * 
     */
    private Integer userId;

    /**
     * 
     */
    private Integer deptId;

    /**
     * 更新日期
     */
    private Date updateTime;

    /**
     * 更新人id
     */
    private Integer updateBy;

    /**
     * 是否删除(0=否,1=是)
     */
    private Integer isDeleted;

    /**
     * 当前进行节点名称
     */
    private String currentName;


    /**
     * 流程实例id
     */
    private String actHiProcinstId;

    private String businessKey;

    private static final long serialVersionUID = 1L;


    /** 签字意见*/
    @TableField(exist  = false)
    List<TaskAudit> flowIds;

    private String code;

    private String taskVersion;

    private String taskId;

    /* 流程线*/
    @TableField(exist = false)
    private List<ModelFlowNodeUtilVo> flowMap;
}





以下是关键业务逻辑
发起一个任务流程实例
    @Override
    @Transactional(rollbackFor =RuntimeException.class)
    public Integer startProcessInstanceByKey(SysUser user, OaTestDemo oaTestDemo) {

        
        oaTestDemo.setProposerId(user.getUserId().intValue());
        oaTestDemo.setProposerTime(new Date());
        oaTestDemo.setUserId(user.getUserId().intValue());
        oaTestDemo.setDeptId(user.getDeptId().intValue());
        oaTestDemo.setStatus(0);
        oaTestDemo.setCode(getCode());
        oaTestDemoMapper.insert(oaTestDemo);

        int id=oaTestDemo.getId();
        String businessKey=business+id;
        SysUser leaderInfo = userService.selectUserById(user.getLeaderId());

        // 获取部门领导
        SysUser deptUser=auditUtils.getDeptLeader(user);
        Map<String, Object> paramMap = new HashMap<>(10);
        paramMap.put("proposer",user.getUserId().toString() );
        paramMap.put("lineLeader",leaderInfo.getUserId().toString() );
        paramMap.put("businessId",oaTestDemo.getId().toString() );
        paramMap.put("deptLeader",deptUser.getUserId().toString());
        paramMap.put("flowIdea",flowIdea );

        // 根据流程定义的key和版本号查找流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(oaTestDemo.getTaskKey()) // 替换为你的流程定义key
                .processDefinitionVersion(processDefinitionService.getHighestVersionByKey(oaTestDemo.getTaskKey())) // 替换为你想要的版本号
                .singleResult();

        if (processDefinition == null) {
            throw new RuntimeException("流程定义未找到");
        }


        /*

        关键代码
					runtimeService.startProcessInstanceById(processDefinitionId, businessKey, variables) 是Camunda BPM引擎中用于启动流程实例的一个方法。
	processDefinitionId: 这个参数是一个字符串,代表你想要启动的流程定义的唯一标识符。
businessKey:这也是一个字符串参数,用于给流程实例分配一个业务键。业务键是一个可选的参数,但是它非常有用,因为它可以帮助你在业务层面唯一地标识一个流程实例。例如,如果你的应用程序中每个订单都启动了一个流程实例,你可以使用订单ID作为业务键,这样你就可以通过订单ID轻松地查询到相关的流程实例状态。
	variables: 这是一个Map<String, Object>类型的参数,用于传递流程实例启动时的变量。这些变量可以被流程中的活动所使用,例如,它们可以作为任务表单的预填充值,或者用于决定流程的分支路径。
				*/
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), businessKey, paramMap);
        if(ObjectUtil.isNull(processInstance)){
           throw new RuntimeException(ProcessExceptionEnum.START_PROCESS_INSTANCE_FAIL.getDesc());
        }


// 以下代码是讲流程实例的关键信息绑定到业务表中
        oaTestDemo.setActHiProcinstId(processInstance.getId());
        oaTestDemo.setBusinessKey(businessKey);
        oaTestDemo.setCurrentName(processInstanceService.getNowCurrentName(processInstance.getId()));

        HashMap map=processInstanceService.getTaskDateBy(processInstance.getId());

        oaTestDemo.setTaskVersion(map.get("taskVersion").toString());
        oaTestDemo.setTaskId(map.get("taskId").toString());
        oaTestDemoMapper.updateById(oaTestDemo);
        taskAuditService.addAuditFlow(user,oaTestDemo,AuditInfoEnum.commit.code,flowIdea);

        this.addStartTblFlowMessage(oaTestDemo,false);
        return oaTestDemo.getId();
    }




批准或驳回 
    @Override
    @Transactional(rollbackFor =Exception.class)
    public void approveOrRejectOaTestDemo(OaTestDemo oaTestDemo,Boolean approve) {

    // 关键代码 调用流程实例的审批        processInstanceService.approveOrRejectByBusinessKeyExample(oaTestDemo.getActHiProcinstId(),oaTestDemo.getBusinessKey(),approve,flowIdea);
        SysUser user = userService.selectUserById(SecurityUtils.getUserId());
        Integer ifHaveNextNode= processInstanceService.ifEndTaskById(oaTestDemo.getActHiProcinstId());

    }






重新提交流程实例
       @Override
    @Transactional(rollbackFor =RuntimeException.class)
    public void againSubmit(OaTestDemo oaTestDemo) {
        OaTestDemo oldTestDemo=oaTestDemoMapper.selectById(oaTestDemo.getId());
        if(oldTestDemo==null){
            throw new RuntimeException("未获取到数据,请确定参数是否正确");
        }
        if(!oldTestDemo.getProposerId().equals(SecurityUtils.getUserId().intValue())){
            throw new RuntimeException("非申请人,不支持该操作");
        }
				
				
				// 关键代码 ,以上根据自己项目需要自行编写
        // 定义需要携带的变量名列表,根据实际情况调整
        Set<String> desiredVariableNames = new HashSet<>(Arrays.asList("proposer", "lineLeader", "deptLeader", "businessId"));
				
				// processInstanceId 流程实例id
        String processInstanceId= processInstanceService.RestartProcessByKeyExample(
                desiredVariableNames,
                oldTestDemo.getActHiProcinstId(),
                oldTestDemo.getBusinessKey(),
                flowIdea);
        // 关键代码 ,以下根据自己项目需要自行编写
        oldTestDemo.setActHiProcinstId(processInstanceId);
        oaTestDemoMapper.updateById(oldTestDemo);
       
    }










 11、测试用例中我绑定的监听类

package com.yuepu.oa.comunda.controller.serviceTask;

import com.yuepu.oa.comunda.entity.OaTestDemo;
import com.yuepu.oa.mapper.OaTestDemoMapper;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("oaTestDemoHandler")
public class OaTestDemoHandlerService implements JavaDelegate {

    private final OaTestDemoMapper oaTestDemoMapper;

    @Autowired
    public OaTestDemoHandlerService(OaTestDemoMapper oaTestDemoMapper) {
        this.oaTestDemoMapper = oaTestDemoMapper;
    }

    @Override
    public void execute(DelegateExecution delegateExecution) throws Exception {
        System.out.println("调用监听");

        // 更安全的获取变量值,避免空指针异常
        String businessIdStr = (String) delegateExecution.getVariable("businessId");
        if (businessIdStr != null) {
            Integer businessId = Integer.parseInt(businessIdStr);

            OaTestDemo oaTestDemo = oaTestDemoMapper.selectById(businessId);
            if (oaTestDemo == null) {
                throw new RuntimeException("没有找到businessessId的匹配记录 : " + businessId);
            }

            // 更安全的类型转换
            Boolean approved = (Boolean) delegateExecution.getVariable("approved");
            if (approved != null && approved) {
                oaTestDemo.setStatus(1);
                oaTestDemo.setCurrentName("办结");
            } else {
                oaTestDemo.setStatus(2);
                oaTestDemo.setCurrentName("发起人");
            }
            oaTestDemoMapper.updateById(oaTestDemo);
        }

    }
}

如果需要获取流程线,建议在启动流程实例的时候,通过一个固定变量对象存放到流程实例中,流程实例无法获取还未进行到节点的信息, 如果需要获取为执行到的节点处理人信息, 但是可以通过流程实例id获取流程变量

以下是camunda某些表作用的解释

 http://localhost:8081/api/camunda/app/

ACT_HI_
表示流程历史记录

act_hi_actinst: 执行的活动历史

act_hi_taskinst:执行任务历史

act_hi_procinst:执行流程实例历史

act_hi_varinst:流程变量历史表

ACT_RE_

表示流程资源存储

act_re_procdef:流程定义存储

act_re_deployment: 自动部署,springboot每次启动都会重新部署,生成记录

ACT_RU_

表示流程运行时表数据,流程结束后会删除

act_ru_execution:运行时流程实例
act_ru_task:运行时的任务
act_ru_variable:运行时的流程变量
ACT_GE_
流程通用数据

act_ge_bytearray:每次部署的文件2进制数据,所以如果文件修改后,重启也没用,因为重新生成了记录,需要清掉数据库,或者这个表记录
登录界面


act_hi_actinst

全称: ACT_HI_ACTINST
含义: 此表存储了流程实例中每一个活动(如任务、网关等)的每一次执行的历史记录。每个流程实例中的每个活动都会在这个表中至少有一条记录,如果活动被执行多次,则会有对应多条记录。
主要字段:
ID_: 历史活动实例的唯一标识。
PROC_DEF_ID_: 关联的流程定义ID。
PROC_INST_ID_: 关联的流程实例ID。
EXECUTION_ID_: 执行实例ID,表示活动在哪一个执行路径上被执行。
ACT_ID_: 流程定义中活动的ID。
START_TIME_, END_TIME_: 活动开始和结束的时间戳。
DURATION_: 活动持续的时间。
用途: 用于审计、统计分析或追踪具体活动的执行历史,例如查看某任务何时开始、何时结束,耗时多久等。


act_hi_procinst
全称: ACT_HI_PROCINST
含义: 此表记录了每个流程实例的整体历史信息,每启动一次流程实例,就会在这个表中生成一条记录。
主要字段:
ID_: 历史流程实例的唯一标识。
PROC_DEF_ID_: 启动的流程定义ID。
BUSINESS_KEY_: 流程实例的业务键,用于关联业务数据。
START_TIME_, END_TIME_: 流程实例的启动和结束时间。
DURATION_: 流程实例的总持续时间。
START_USER_ID_: 启动流程实例的用户ID。
用途: 用于查看流程实例级别的信息,如流程实例的总体执行时间、启动者等,适合于监控流程实例的宏观状态和进行整体分析。

act_hi_actinst(执行的活动历史)
记录内容:此表记录了流程实例中每一个流程活动(如服务任务、用户任务、排他网关等)的每一次执行的详细历史信息。这包括了自动活动和人工任务的执行记录。
关键字段:包括ACTIVITY_ID_(活动ID),PROC_INST_ID_(关联的流程实例ID),START_TIME_和END_TIME_(活动开始和结束时间),以及DURATION_(活动执行时长)等。
用途:用于跟踪和分析流程中各个活动的执行情况,例如了解每个活动的执行效率、耗时等,对流程优化和故障排查非常有用。

act_hi_taskinst(执行任务历史)
记录内容:专注于记录用户任务(Human Task)的执行历史,即那些需要用户交互的任务实例。这包括了任务的创建、分配、完成、取消等状态变化。
关键字段:除了包含PROC_INST_ID_(关联的流程实例ID)外,还包括TASK_DEF_KEY_(任务定义的ID),CREATE_TIME_和CLAIM_TIME_(任务创建和认领时间),以及COMPLETED_TIME_(任务完成时间)等。
用途:主要用于追溯和审计用户任务的处理过程,比如查看哪些任务被谁处理,处理时间等,对于管理任务分配和监控个人工作量非常有帮助。

act_hi_procinst(执行流程实例历史)
记录内容:记录整个流程实例从启动到结束的生命周期信息,包括流程实例的整体状态变化和基本信息。
关键字段:包括PROC_INST_ID_(流程实例ID),PROC_DEF_ID_(流程定义ID),BUSINESS_KEY_(业务键,关联外部业务数据),START_TIME_和END_TIME_(流程实例的启动和结束时间),以及DURATION_(流程实例总耗时)等。
用途:适用于分析流程实例的整体执行情况,比如查看流程的平均执行时间、成功率等,对于评估流程性能和优化流程设计至关重要。

总结来说,act_hi_actinst关注流程内部活动的执行细节,act_hi_taskinst专注于用户任务的生命周期管理,而act_hi_procinst则提供了流程实例从头到尾的概览信息。三者结合起来,可以提供全面的流程执行历史数据分析视角。


act_ru_execution(运行时执行实例)
记录内容:此表存储了流程实例及其所有执行路径的当前状态信息。它包含了正在运行或暂停的流程实例、活动、并行分支等的执行情况。也就是说,这个表反映了流程实例在任何给定时刻的动态视图。
关键字段:包括ID_(执行实例ID),PROC_INST_ID_(关联的流程实例ID),PROC_DEF_ID_(流程定义ID),ACT_ID_(当前活动ID,如果有的话),以及状态相关的字段如SUSPENSION_STATE_(挂起状态)、ACTUAL_START_TIME_(实际开始时间)等。
用途:主要用于流程引擎内部管理和查询当前运行中的流程实例状态,比如决定下一步应执行哪个活动,或是处理流程实例的暂停、继续等操作。
与历史表的区别

数据持久性:act_ru_execution表中的数据是临时的,随着流程实例的推进,相关记录可能会被修改或删除。而历史表(如act_hi_*系列)的数据一旦记录,通常是不会被修改或删除的,用于长期存储和历史分析。
用途不同:运行时表服务于流程引擎的运行时决策和状态管理,是流程执行的核心数据结构。历史表则服务于审计、监控、报表生成等后处理需求,帮助分析过去发生的流程行为。
数据内容:运行时表包含更多关于流程实例当前状态和执行路径的信息,而历史表则更多关注于流程实例、任务或活动的生命周期记录,尤其是它们的开始、结束和关键状态变更。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1922586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安卓14中Zygote初始化流程及源码分析

文章目录 日志抓取结合日志与源码分析systemServer zygote创建时序图一般应用 zygote 创建时序图向 zygote socket 发送数据时序图 本文首发地址 https://h89.cn/archives/298.html 最新更新地址 https://gitee.com/chenjim/chenjimblog 本文主要结合日志和代码看安卓 14 中 Zy…

抗量子密码算法:保障未来信息安全的新盾牌

随着量子计算的迅猛发展&#xff0c;传统加密算法正面临着前所未有的挑战。量子计算机利用量子比特的特殊性质&#xff0c;能在极短时间内破解目前广泛使用的公钥加密体系&#xff0c;如RSA、ECC等。这使得我国及全球的信息安全体系遭受严重威胁。为了应对这一挑战&#xff0c;…

知识图谱入门笔记

自学参考&#xff1a; 视频&#xff1a;斯坦福CS520 | 知识图谱 最全知识图谱综述 详解知识图谱的构建全流程 知识图谱构建&#xff08;概念&#xff0c;工具&#xff0c;实例调研&#xff09; 一、基本概念 知识图谱&#xff08;Knowledge graph&#xff09;&#xff1a;由结…

基于LSTM的局部特征提取网络算法原理

目录 一、LSTM的基本原理与结构 1. LSTM的核心结构 2. LSTM的工作原理 二、基于LSTM的局部特征提取 1. 输入处理与序列表示 2. LSTM层处理与特征提取 3. 特征提取的优势与应用 三、实现细节与注意事项 1. 数据预处理 2. 网络结构与参数选择 3. 训练策略与正则化 4.…

Chapter 1:数据结构前言

在数字化的世界里&#xff0c;我们每天都在与数据打交道。然而&#xff0c;你是否曾想过&#xff0c;这些数据是如何被组织、存储和处理的&#xff1f;这就是数据结构的魅力所在。 数据结构&#xff0c;简单来说&#xff0c;就是数据的组织方式。它决定了我们如何高效地访问和操…

【游戏客户端】大话slg玩法架构(三)建筑控件

【游戏客户端】大话slg玩法架构&#xff08;三&#xff09;建筑控件 大家好&#xff0c;我是Lampard家杰~~ 今天我们继续给大家分享SLG玩法的实现架构&#xff0c;关于SLG玩法的介绍可以参考这篇上一篇文章&#xff1a;【游戏客户端】制作率土之滨Like玩法 PS&#xff1a;和之前…

JVM系列 | 垃圾收集算法

JVM系列 | 垃圾收集算法 文章目录 前言如何判断对象已"死"&#xff1f;引用计数法可达性分析算法可达性分析2.0版 | 引用的增强对象的消亡过程回收方法区主要回收目标&#xff1a;回收操作 垃圾收集算法分代收集理论 与 跨代引用假说分代收集理论跨带引用假说 垃圾收…

Oracle数据库加密与安全

Wallet简介&#xff1a; Oracle Wallet(即内部加密技术TDE( Transparent DataEncryption&#xff09; TDE是 Oracle10gR2中推出的一个新功能,使用时要保证Oracle版本是在10gR2或者以上 Wallet配置&#xff1a; 1.创建一个新目录&#xff0c;并指定为Wallet目录 /home/oracle…

论文翻译:Large Language Models for Education: A Survey and Outlook

https://arxiv.org/abs/2403.18105 目录 教育领域的大型语言模型&#xff1a;一项调查和展望摘要1. 引言2. 教育应用中的LLM2.1 概述2.2 学习辅助2.2.1 问题解决&#xff08;QS&#xff09; 2.2.2 错误纠正&#xff08;EC&#xff09;2.2.3 困惑助手&#xff08;CH&#xff09;…

ExcelToDB2:批量导入Excel到IBM DB2数据库的自动化工具

ExcelToDB2&#xff1a;批量导入Excel到IBM DB2数据库的自动化工具 简介 ExcelToDB2是一个可以批量导入Excel到IBM DB2数据库的自动化工具。支持将xls/xlsx/xlsm/xlsb/csv/txt/xml格式的Excel文件导入到IBM DB2等多种原生及国产数据库。自动化是其最大的特点&#xff0c;因为它…

Python爬虫教程第5篇-使用BeautifulSoup查找html元素几种常用方法

文章目录 简介find()和find_all()字符串通过id查找通过属性查找通过.方式查找通过CSS选择器查找通过xpath查找正则表达自定义方法总结 简介 上一篇详细的介绍了如何使用Beautiful Soup的使用方法&#xff0c;但是最常用的还是如何解析html元素&#xff0c;这里再汇总介绍下查询…

数据分析——Python网络爬虫(四){正则表达式}

爬虫库的使用 爬虫的步骤正则表达式正则表达式的流程正则表达式的使用括号的使用管道匹配问号匹配星号匹配加号匹配花括号匹配用点-星匹配所有字符跨行匹配findall方法其他常用字符匹配 例子正则表达式在线测试 爬虫的步骤 #mermaid-svg-zSQSbTxUEex051NQ {font-family:"t…

Web开发 —— 放大镜效果(HTML、CSS、JavaScript)

目录 一、需求描述 二、实现效果 三、完整代码 四、实现过程 1、HTML 页面结构 2、CSS 元素样式 3、JavaScript动态控制 &#xff08;1&#xff09;获取元素 &#xff08;2&#xff09;控制大图和遮罩层的显隐性 &#xff08;3&#xff09;遮罩层跟随鼠标移动 &…

【电脑应用技巧】如何寻找电脑应用的安装包华为电脑、平板和手机资源交换共享

电脑的初学者可能会直接用【百度】搜索电脑应用程序的安装包&#xff0c;但是这样找到的电脑应用程序安装包经常会被加入木马或者强制捆绑一些不需要的应用装入电脑。 今天告诉大家一个得到干净电脑应用程序安装包的方法&#xff0c;就是用【联想的应用商店】。联想电脑我是一点…

使用Lego进行证书的申请和更新

姊妹篇: 使用Let’s Encrypt 申请通配符证书 关于acme 协议 ACME是自动证书管理环境&#xff08;Automatic Certificate Management Environment&#xff09;的缩写&#xff0c;是一个由IETF&#xff08;Internet Engineering Task Force&#xff09;制定的协议标准&#xff0c…

gd32F470串口重定义

c代码&#xff1a; /** Author: Bleaach008* Date: 2024-07-10 17:31:01* LastEditTime: 2024-07-11 09:42:06* FilePath: \MDK-ARMd:\Code\GD32\GD01_UART\MyApplication\Public.c* Description:** Copyright (c) 2024 by 008, All Rights Reserved.*/ /* Includes ----------…

QFileDialog的简单了解

ps&#xff1a;写了点垃圾&#xff08;哈哈哈&#xff09; 现在感觉Qt库应该是调用了Windows提供的这块的接口了。 它继承自QDialog 这是Windows自己的文件夹 这是两者的对比图&#xff1a; 通过看QFileDialog的源码&#xff0c;来分析它是怎么实现这样的效果的。 源码组成…

面试篇-Java-5+设计模式

文章目录 前言一、你知道工厂方法模式吗1.1 你有使用过简单工厂模式吗1.2 你有使用过简单工厂方法模式吗1.3 你有使用过抽象工厂方法模式吗1.4 你有使用过策略模式吗 二、你们项目中是怎么使用设计模式的呢2.1 策略模式 工厂模式 实现不同的方式的登录2.1.1 定义一个登录的接口…

SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测

SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测 目录 SCI一区级 | Matlab实现NGO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现NGO-CNN-LSTM-Mutilhead-Attention北方苍鹰算…

怎么用PPT录制微课?详细步骤解析!

随着信息技术的不断发展&#xff0c;微课作为一种新型的教学形式&#xff0c;因其短小精悍、针对性强等特点&#xff0c;在教育领域得到了广泛的应用。而PPT作为一款常用的演示工具&#xff0c;不仅可以用来制作课件&#xff0c;还可以利用其内置的录屏功能或结合专业的录屏软件…