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

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

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

相关文章

Python 开发植物大战僵尸杂交版辅助【全网最详细_查找 + 代码编写一体化零基础也能学会】

目录 辅助最终展示效果 一、文章介绍 二、工具介绍 三、基址搜索 3.1、寻找阳光基址 3.2、寻找卡槽冷却基址 3.3、寻找僵尸刷新时间基址 3.4、寻找大阳光刷新时间基址 3.5、寻找植物编号基址 3.6、寻找场上僵尸数量基址 3.7、寻找僵尸 X 坐标基址 3.8、通过找到的僵…

《昇思25天学习打卡营第1天|QuickStart》

说在前面 曾经接触过华为的910B服务级显卡&#xff0c;当时基于910B做了一些开发的工作&#xff0c;但是总感觉做的事情太低层&#xff0c;想要能自顶向下的了解下&#xff0c;因此开始了MindSpore的学习。另外也想给予提供的显卡&#xff0c;简单尝试下llm模型的训练&#xf…

数据库mysql-对数据库和表的DDL命令

文章目录 一、什么是DDL操作二、数据库编码集和数据库校验集三、使用步骤对数据库的增删查改1.创建数据库2.进入数据库3.显示数据库4.修改数据库mysqldump 5.删除数据库 对表的增删查改1.添加/创建表2.插入表内容3.查看表查看所有表查看表结构查看表内容 4.修改表修改表的名字修…

SpringBootWeb 篇-入门了解 Swagger 的具体使用

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Swagger 介绍 1.1 Swagger 和 Yapi 的使用场景 2.0 Swagger 的使用方式 2.1 导入 knife4j 的 maven 坐标 2.2 在配置类中加入 knife4j 相关配置 2.3 设置静态资源…

oracle控制文件详解以及新增控制文件

文章目录 oracle控制文件1、 控制文件包含的主要信息如下&#xff1a;2、查看目前系统的控制文件信息&#xff0c;主要是查看相关的字典视图 oracle新增控制文件 oracle控制文件 控制文件是一个很小的二进制文件(10MB左右)&#xff0c;含有数据库结构信息&#xff0c;包括数据…

Open3D 点云Kmeans聚类算法

目录 一、概述 1.1算法介绍 1.2实现步骤 二、代码实现 三、实现效果 3.1原始点云 3.2聚类后点云 前期试读&#xff0c;后续会将博客加入该专栏&#xff0c;欢迎订阅Open3D与点云深度学习的应用_白葵新的博客-CSDN博客 一、概述 1.1算法介绍 聚类是一种将数据集分组的方…

Next.js的静态生成和服务端渲染,你搞懂了吗?

Next.js的静态生成和服务端渲染&#xff0c;你搞懂了吗&#xff1f; 嘿&#xff0c;各位前端小伙伴们&#xff01;今天咱们来聊聊Next.js中那令人又爱又恨的静态生成&#xff08;Static Generation&#xff09;和服务端渲染&#xff08;Server-side Rendering&#xff09;。这…

双语|如何给教授/教职员发送电子邮件

斯坦福大学提出建议&#xff0c;指导学生如何给教授或者教职员发送电子邮件&#xff0c;这些建议对于访问学者、博士后及联合培养博士也很适用&#xff0c;故知识人网小编用双语对照的形式进行节选转发。 Whether youre writing a professor to ask for an extension or to loo…

多平台支持,制作的电子画册随时随地都可以查看

​在数字化的时代背景下&#xff0c;电子画册以其便捷的传播方式、丰富的视觉表现形式&#xff0c;赢得了大众的喜爱。它不仅能够在个人电脑上展现&#xff0c;还能通过智能手机、平板电脑等多种移动设备随时随地被访问和浏览。这种跨平台的支持&#xff0c;使得无论你身处何地…

高精度定位与AI技术的深度融合——未来智慧世界的钥匙

引言在当今迅速发展的科技时代&#xff0c;精确定位和人工智能&#xff08;AI&#xff09;技术正在快速推动各领域的创新与变革。高精度定位结合AI技术所产生的融合效应&#xff0c;正在加速智慧城市、智能驾驶、智能物流以及许多其他领域的实现。这篇文章将详细探讨高精度定位…

基于Java技术的校园台球厅人员与设备管理系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;Eclipse、Navicat、Maven 系统展示 首页 用户注册界面 球桌信息…

C++基础编程100题-023 OpenJudge-1.4-03 奇偶数判断

更多资源请关注纽扣编程微信公众号 http://noi.openjudge.cn/ch0104/03/ 描述 给定一个整数&#xff0c;判断该数是奇数还是偶数。 输入 输入仅一行&#xff0c;一个大于零的正整数n。 输出 输出仅一行&#xff0c;如果n是奇数&#xff0c;输出odd&#xff1b;如果n是偶…

Twelve Labs:专注视频理解,像人类一样理解视频内容

在当今数字化世界中&#xff0c;视频已成为人们获取信息和娱乐的主要方式之一。 AI视频生成领域的竞争也很激烈&#xff0c;Pika、Sora、Luma AI以及国内的可灵等&#xff0c;多模态、视频生成甚至也被视为大模型发展的某种必经之路。然而&#xff0c;与文本生成相比&#xff…

深入解析发生 OOM 的三大场景

深入解析 OOM 的三大场景 什么是 OOM&#xff1f;一、堆内存溢出 ( Heap OOM )原因分析解决方案 二、栈内存溢出&#xff08;Stack OOM&#xff09;原因分析解决方案 三、方法区内存溢出&#xff08;Metaspace OOM&#xff09;原因分析解决方案 在Java应用程序开发中&#xff0…

记录一次Nginx的使用过程

一、Docker安装配置nginx 1.拉取镜像 docker pull nginx2.创建挂载目录 启动前需要先创建Nginx外部挂载目录文件夹 主要有三个目录 conf&#xff1a;配置文件目录log&#xff1a;日志文件目录html&#xff1a;项目文件目录&#xff08;这里可以存放web文件&#xff09; 创建挂…

oslo_i18n学习小结

背景 代码均为开源代码 基于yoga版本&#xff0c;需要对openstack某服务做翻译&#xff0c;了解到oslo_i18n有翻译功能&#xff0c;配置oslo_i18n来给组件进行翻译 用法 用法 每个服务自己会带一个i18n.py的文件&#xff0c;如果要对日志进行翻译&#xff0c;从i18n导入_&…

逆变-TI视频课笔记

目录 1、全桥逆变 1.1、全桥逆变SPWM仿真 2、半桥逆变 2.1、本课小结 3、多重逆变&#xff08;间接的“交-直-交-直”变流&#xff09; 3.1、多电平逆变的目的 3.2、单逆变桥 3 电平控制时序 3.3、大功率设备的功率因数 3.4、本课小结 视频链接&#xff1a;文字…

Zed 编辑器发布了原生 Linux 版本

由 Rust 编写、GPU 加速的 Zed 文本编辑器终于提供了正式的 Linux 原生版本&#xff01;在过去的几个月里&#xff0c;Zed 的 Linux 支持取得了长足的进步&#xff0c;现在已经进入了更正式的阶段。 今天&#xff0c;这款由前 Atom 开发人员创建的现代开源代码编辑器现在在 Li…

vue vite+three在线编辑模型导入导出

文章目录 序一、1.0.0版本1.新增2.编辑3.导出4.导入 二、2.0.0版本1. 修复模型垂直方向放置时 模型会重合4. 修复了导出导入功能 现在是1:1导出导入5. 新增一个地面 视角看不到地下 设置了禁止编辑地面 地面设置为圆形6. 新增功能 可选择基本圆形 方形 圆柱形等模型以及可放置自…

网络 闲聊

闲谈 闲话 网络安全——>网络空间安全 网络空间&#xff1a;一个由信息基础设备组成互相依赖的网络 继&#xff1a;海、陆、空、天、的第五大空间 信息安全的一个发展&#xff1a; 通信保密阶段---计算机安全---信息系统安全---网络空间安全 棱镜门事件 棱镜计划&…