springBoot(若依)集成camunda
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: demofilter:create: All tasks#指定数据类型database:type: mysqlschema-update: false#禁止自动部署resources 下面的bpmnauto-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 {@ResourceRepositoryService repositoryService;@Resourceprivate TaskService taskService;@Resourceprivate RuntimeService runtimeService;@ResourceISysUserService systemUserService;@Autowiredprivate ProcessEngine processEngine;@Overridepublic 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();}@Overridepublic String deleteDeployment(String deploymentId) {//这里可以做级联删除,默认为false,级联会删除流程实例和job//repositoryService.deleteDeployment(deploymentId,true);repositoryService.deleteDeployment(deploymentId);return "删除成功";}@Overridepublic 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;}@Overridepublic 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());@Resourceprivate RepositoryService repositoryService;@Resourceprivate TaskService taskService;@AutowiredProcessInstanceService 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 {@Resourceprivate TaskService taskService;@ResourceISysUserService 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) {// 获取流程定义IDString 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 "";}}@Overridepublic 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();}@Overridepublic 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("无法找到流程实例的历史记录,可能流程未结束或已被清理。");}}@Overridepublic HashMap getTaskDateBy(String processInstanceId) {HashMap map=new HashMap();// 初始化流程引擎ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 查询历史流程实例以获取流程定义IDHistoricProcessInstance 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;}@Overridepublic String getCamundaModelById(String processInstanceId) {// 初始化流程引擎ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取RuntimeServiceRuntimeService runtimeService = processEngine.getRuntimeService();// 通过流程实例ID获取流程实例ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();if (processInstance != null) {// 获取流程定义IDString 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("流程实例未找到");}}@Overridepublic 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节点的IDCollection<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 流程实例idString 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;@Autowiredpublic OaTestDemoHandlerService(OaTestDemoMapper oaTestDemoMapper) {this.oaTestDemoMapper = oaTestDemoMapper;}@Overridepublic 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_*系列)的数据一旦记录,通常是不会被修改或删除的,用于长期存储和历史分析。
用途不同:运行时表服务于流程引擎的运行时决策和状态管理,是流程执行的核心数据结构。历史表则服务于审计、监控、报表生成等后处理需求,帮助分析过去发生的流程行为。
数据内容:运行时表包含更多关于流程实例当前状态和执行路径的信息,而历史表则更多关注于流程实例、任务或活动的生命周期记录,尤其是它们的开始、结束和关键状态变更。
相关文章:
springBoot(若依)集成camunda
1、下图为项目结构 2、最外层 pom引入依赖 <properties><!--camunda 标明版本,注意要个自己的Spring 版本匹配,匹配关系自行查询官网--><camunda.version>7.18.0</camunda.version> </properties> 3、common模块引入依赖 …...
【微信小程序知识点】自定义构建npm
在实际开发中,随着项目的功能越来越多,项目越来越复杂,文件目录也变得很繁琐,为了方便进行项目的开发,开发人员通常会对目录结构进行优化调整,例如:将小程序源码放到miniprogram目录下。 &…...
JCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断
JJCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断 目录 JJCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断分类效果格拉姆矩阵图GAF-PCNN-MATTGASF-CNNGADF-CNN 基本介绍程序设计参考资料 分…...
新手教学系列——高效管理MongoDB数据:批量插入与更新的实战技巧
前言 在日常开发中,MongoDB作为一种灵活高效的NoSQL数据库,深受开发者喜爱。然而,如何高效地进行数据的批量插入和更新,却常常让人头疼。今天,我们将一起探讨如何使用MongoDB的bulk_write方法,简化我们的数据管理流程,让代码更加简洁高效。 常规做法:find、insertone…...
C# Winform 自定义事件实战
在C#的WinForms中,自定义事件是一种强大的工具,它允许你创建自己的事件,从而在特定条件下通知订阅者。自定义事件通常用于封装业务逻辑,使代码更加模块化和易于维护。下面我将通过一个实战例子来展示如何在WinForms中创建和使用自…...
Python通过继承实现多线程
本套课在线学习视频(网盘地址,保存到网盘即可免费观看): https://pan.quark.cn/s/677661ea63b3 本节将介绍如何利用Python中的thread模块和threading模块实现多线程,并通过继承threading.Thread类并重写run方…...
记一次项目经历
一、项目需求 1、设备四个工位,每个工位需要测试产品的电参数; 2、每个另外加四个位置温度; 3、显示4个通道电流曲线,16个通道温度曲线; 4、可切换工艺参数; 5、常规判定,测试数据保存到表格内&…...
Elasticsearch 8 支持别名查询
在 Elasticsearch 8 中,使用 Java 高级 REST 客户端进行别名管理的过程与之前的版本类似,但有一些API细节上的变化。以下是如何使用 Java 和 Elasticsearch 8 进行别名操作的例子: 引入依赖 确保你的项目中包含了 Elasticsearch 的高级 RES…...
【Spring Cloud】 使用Eureka实现服务注册与服务发现
文章目录 🍃前言🎍解决方案🚩关于注册中⼼🚩CAP理论🚩常见的注册中心 🎄Eureka🚩搭建 Eureka Server🎈创建Eureka-server ⼦模块🎈引入依赖🎈项目构建插件&am…...
JDK安装详细教程(以JDK17为例)
一、JDK的下载 1. 前往oracle官网下载JDK Java Archive Downloads - Java SE 17 在这里选择对应的JDK版本,我这里就直接选择JDK17的版本了。 然后下载对应的软件包,我这里采用的是Windows的安装程序。 点击上述圈起来的链接即可下载安装包,…...
安装nodejs | npm报错
nodejs安装步骤: 官网:https://nodejs.org/en/ 在官网下载nodejs: 双击下载下来的msi安装包,一直点next,我选的安装目录是默认的: 测试是否安装成功: 输入cmd打开命令提示符,输入node -v可以看到版本,说…...
聊点基础---Java和.NET开发技术异同全方位分析
1. C#语言基础 1.1 C#语法概览 欢迎来到C#的世界!对于刚从Java转过来的开发者来说,你会发现C#和Java有很多相似之处,但C#也有其独特的魅力和强大之处。让我们一起来探索C#的基本语法,并比较一下与Java的异同。 程序结构 C#程序…...
【C++】C++中SDKDDKVer.h和WinSDKVer.h函数库详解
目录 一.SDKDDKVer.h介绍 二.WinSDKVer.h介绍 三.WinSDKVer.h 和 SDKDDKVer.h 的区别 一.SDKDDKVer.h介绍 SDKDDKVer.h 是一个在 Windows 软件开发中常见的头文件,它用于定义软件开发工具包(SDK)和驱动开发工具包(DDK&…...
uni-app 蓝牙传输
https://www.cnblogs.com/ckfuture/p/16450418.html https://www.cnblogs.com/yangxiaobai123/p/16021058.html 字符串转base64:https://www.cnblogs.com/sunny3158/p/17312158.html 将 ArrayBuffer 对象转成 Base64 字符串:基础 - uni.arrayBufferT…...
MBR10200CT-ASEMI智能AI应用MBR10200CT
编辑:ll MBR10200CT-ASEMI智能AI应用MBR10200CT 型号:MBR10200CT 品牌:ASEMI 封装:TO-220 批号:最新 恢复时间:35ns 最大平均正向电流(IF):10A 最大循环峰值反向…...
力扣 爬楼梯
动态规划算法基础篇。 class Solution {public int climbStairs(int n) {int[] f new int[n 1];f[0] 1;f[1] 1;//当爬到n阶楼梯时,可知是由n-1阶或n-2阶楼梯而来for(int i 2; i < n; i) {f[i] f[i - 1] f[i - 2];//后面的每一阶种数由前两个状态得到}ret…...
java设计模式之:策略模式+工厂模式整合案例实战(一)
本文介绍项目中常用的策略模式工厂模式的案例,该案例是针对策略类比较少的情况;下一篇会讲解策略类比较多的案例,下面直接开始: 案例1:项目中对系统中的客户和销售进行事件通知(短信、邮件、钉钉) 首先要有通知的策略…...
国内Ubuntu安装 stable-diffusion教程,换成国内镜像
安装依赖: 首先更新系统并安装Python 3.10和pip: sudo apt update sudo apt install python3.10 python3-pip 设置Python虚拟环境(可选): 安装Python虚拟环境管理工具,并创建激活虚拟环境: su…...
JAVA final详细介绍
一、介绍 final 中文意思: 最后的,最终的. final 可以修饰类、属性、方法和局部变量, 在某些情况下,程序员可能有以下需求,就会使用到final: 1)当不希望类被继承时,可以用final修饰。 //如果我们要求A类不能被其他类继承 //可以使用fin…...
45、tomcat+课后实验
tomcat 1、tomcat tomcat和php一样,都是用来处理动态页面的。 tomcat也可以作为web应用服务器,开源的。 php .php tomcat .jsp nginx .html tomcat 是用Java代码写的程序,运行的是Java的web应用程序。 tomcat的特点和功能:…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
