当前位置: 首页 > news >正文

SpringBoot 集成 Activiti 7 工作流引擎

一. 版本信息

  1. IntelliJ IDEA 2023.3.6
  2. JDK 17
  3. Activiti 7

二. IDEA依赖插件安装

        安装BPM流程图插件,如果IDEA的版本超过2020,则不支持actiBPM插件。我的IDEA是2023版本我装的是 Activiti BPMN visualizer 插件。

  1. 在Plugins 搜索 Activiti BPMN visualizer 安装
  2. 创建BPMN文件

  3. 使用视图模式打开bpmn.xml

三. 创建SpringBoot 集成 activiti7

  1. 使用 IDEA 创建SpringBoot项目
  2. 设置项目参数
  3. 在 pom.xml 依赖配置文件中添加(Mysql,Lombok,activiti7)依赖
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com</groupId><artifactId>activiti-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>activiti-demo</name><description>activiti-demo</description><properties><java.version>17</java.version></properties><dependencies><!-- web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- mysql依赖 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.2.0</version></dependency><!-- lombok依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- mybatis数据访问层 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.7</version></dependency><!-- activiti7 工作流引擎依赖  --><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>7.1.0.M6</version></dependency><!-- 模块测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
    
  4. 创建本地连接的数据库
    创建数据库  activiti
    CREATE DATABASE `activiti` /*!40100 DEFAULT CHARACTER SET utf8 */;
    创建数据库表 user
    -- activiti.`user` definitionCREATE TABLE `user` (`ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`NAME` varchar(100) DEFAULT NULL COMMENT '名称',`AGE` varchar(100) DEFAULT NULL COMMENT '年龄',`CREATED_BY` varchar(32) DEFAULT NULL COMMENT '创建人名称',`CREATED_TIME` datetime DEFAULT NULL COMMENT '创建时间',`CREATED_ID` varchar(32) DEFAULT NULL COMMENT '创建人ID',`UPDATED_BY` varchar(32) DEFAULT NULL COMMENT '更新人名称',`UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新时间',`UPDATED_ID` varchar(32) DEFAULT NULL COMMENT '更新人ID',PRIMARY KEY (`ID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
    添加一条测试数据
    INSERT INTO activiti.`user`
    (ID, NAME, AGE, CREATED_BY, CREATED_TIME, CREATED_ID, UPDATED_BY, UPDATED_TIME, UPDATED_ID)
    VALUES(1, '小王', '24', NULL, NULL, NULL, NULL, NULL, NULL);
  5. 添加 application.yml 配置文件
    spring:application:name: activiti-demodatasource:#url切换数据库之后如果对应数据库名称和路径有变动,需要修改urlurl: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
    
  6. 添加 activiti.cfg.xml 配置文件(文件名不能随便改)
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans [<!ELEMENT beans (bean)*><!ATTLIST beansxmlns CDATA #REQUIREDxmlns:xsi CDATA #REQUIREDxsi:schemaLocation CDATA #REQUIRED><!ELEMENT bean (property)*><!ATTLIST beanid CDATA #REQUIREDclass CDATA #REQUIRED><!ELEMENT property (#PCDATA)><!ATTLIST propertyname CDATA #REQUIREDvalue CDATA #REQUIRED>]>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- processEnqine Activiti 的流程引擎 --><bean id="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/><property name="jdbcUrl"value="jdbc:mysql://localhost:3306/activiti?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;useSSL=false&amp;nullCatalogMeansCurrent=true"/><property name="jdbcUsername" value="root"/><property name="jdbcPassword" value="root"/><!-- activiti 数据库表处理策略 --><!-- databaseSchemaUpdate 属性的值可以设置为以下几种 --><!-- none:这是默认值,表示不对数据库模式做任何变更,应用程序启动时不会检查数据库表结构是否与实体类匹配--><!-- true:设置为 true 时,Spring会在应用程序启动时检查数据库表结构,并在发现不匹配时自动创建或修改表结构以匹配实体类定义。这相当于执行了数据库迁移--><!-- create:与 true 类似,但 create 会在每次启动时删除并重新创建表,这可能会导致数据丢失,因此使用时需要谨慎--><!-- create-drop:在每次启动应用程序时创建表,在关闭应用程序时删除表。这通常用于测试环境--><!-- validate:在启动时验证数据库表结构是否与实体类定义匹配,如果不匹配则抛出异常,但不会自动进行任何更改--><property name="databaseSchemaUpdate" value="true"/></bean>
    </beans>
    
  7. 启动SpringBoot项目成功
  8. 开始添加一个查询数据测试接口(Controller,Service,Mapper,Entity)
    Controller类
    package com.activitidemo.act.controller;import com.activitidemo.act.entity.UserEntity;
    import com.activitidemo.act.service.impl.UserServiceImp;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import jakarta.annotation.Resource;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;/*** <p>*  前端控制器* </p>** @author ningbeibei* @since 2024-09-26*/
    @RestController
    @RequestMapping("/user-entity")
    public class UserController {@Resourceprivate UserServiceImp userService;@PostMapping("/user")public Object getUser(@RequestBody UserEntity user){QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();queryWrapper.eq("id",user.getId());return userService.getBaseMapper().selectList(queryWrapper);}}
    Service 类
    package com.activitidemo.act.service;import com.activitidemo.act.entity.UserEntity;
    import com.baomidou.mybatisplus.extension.service.IService;/*** @author ningbeibei* @since 2024-09-26*/
    public interface UserService extends IService<UserEntity> {}
    
    package com.activitidemo.act.service.impl;import com.activitidemo.act.mapper.UserMapper;
    import com.activitidemo.act.service.UserService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.stereotype.Service;
    import com.activitidemo.act.entity.UserEntity;/*** <p>* 服务实现类* </p>** @author ningbeibei* @since 2024-09-26*/
    @Service
    public class UserServiceImp extends ServiceImpl<UserMapper, UserEntity> implements UserService {
    }
    
    Mapper 类
    package com.activitidemo.act.mapper;import com.activitidemo.act.entity.UserEntity;
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import org.apache.ibatis.annotations.Mapper;/*** <p>*  Mapper 接口* </p>** @author ningbeibei* @since 2024-09-26*/
    @Mapper
    public interface UserMapper extends BaseMapper<UserEntity> {}
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.activitidemo.act.entity.UserEntity"></mapper>
    
    Entity 类
    package com.activitidemo.act.entity;import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Getter;
    import lombok.Setter;import java.io.Serializable;
    import java.time.LocalDateTime;/*** @author ningbeibei* @since 2024-09-26*/
    @Getter
    @Setter
    @TableName("user")
    public class UserEntity implements Serializable {private static final long serialVersionUID = 1L;@TableField("ID")private Integer id;@TableField("NAME")private String name;@TableField("AGE")private int age;@TableField("CREATED_BY")private String createdBy;@TableField("CREATED_TIME")private LocalDateTime createdTime;@TableField("CREATED_ID")private String createdId;@TableField("UPDATED_BY")private String updatedBy;@TableField("UPDATED_TIME")private LocalDateTime updatedTime;@TableField("UPDATED_ID")private String updatedId;}
    
    目录结构
  9. 使用Postman接口测试工具,测试接口是否正常

四. Activiti 使用步骤

Activiti 主要流程操作步骤:

  1. 定义流程:按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来。
  2. 部署流程:把画好的流程定义文件,加载到数据库中,生成表的数据。
  3. 启动流程:使用 java 代码来操作数据库表中的内容。
  4. 处理任务:操作流程当中的各个任务。

1. 定义流程

2. 初始库表、定义、部署、操作任务代码

创建测试类

测试代码:

package com.activitidemo;import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.FileCopyUtils;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;@SpringBootTest
class ActivitiDemoApplicationTests {// 创建 ProcessEngine 对象
//    private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//    /**
//     * 初始化数据库表:第一种方式
//     */
//    @Test
//    public void testInitOne() {
//        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//        System.err.println("processEngine = " + processEngine);
//    }/*** 初始化数据库表* 通过读取 activiti.cfg.xml 配置文件*/@Testpublic void testInitTwo() {ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");ProcessEngine processEngine = configuration.buildProcessEngine();System.err.println("processEngine = " + processEngine);}/*** 流程部署*/@Testpublic void testDeploy() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取 repositoryService 对象RepositoryService repositoryService = processEngine.getRepositoryService();// 进行部署Deployment deployment = repositoryService.createDeployment().addClasspathResource("process/leave.bpmn20.xml").addClasspathResource("process/leave.bpmn20.png").name("请假流程").deploy();// 输出部署的一些信息System.out.println("流程部署ID:" + deployment.getId());System.out.println("流程部署名称:" + deployment.getName());System.out.println("流程部署成功");}/*** 启动流程实例*/@Testpublic void testStartProcess() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取 runtimeService 对象RuntimeService runtimeService = processEngine.getRuntimeService();// 1.根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave");// 2.根据流程定义id启动流程实例
//        ProcessInstance instance = runtimeService.startProcessInstanceById("leave:1:4");// 获取流程实例的相关信息System.out.println("流程定义的id = " + instance.getProcessDefinitionId());System.out.println("流程实例的id = " + instance.getId());System.out.println("启动流程成功 ");}/*** 查询待办任务*/@Testpublic void testSelectTodoTaskList() {String assignee = "李四";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();// 使用面对对象方式查询数据库List<Task> tasks = taskService.createTaskQuery().processDefinitionKey("leave").taskAssignee(assignee)// 返回多个结果.list();// 只返回一个结果// .singleResult();// 自定义 sql 查询// taskService.createNativeTaskQuery();// 获取流程实例的相关信息for (Task task : tasks) {System.out.println("流程定义的id = " + task.getProcessDefinitionId());System.out.println("流程实例的id = " + task.getProcessInstanceId());System.out.println("任务id = " + task.getId());System.out.println("任务名称 = " + task.getName());}}/*** 指定用户去完成任务待办:多人审批在这操作,改变审核人名称就行了*/@Testpublic void testCompleteTask() {String assignee = "李四";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();List<Task> tasks = taskService.createTaskQuery().processDefinitionKey("leave").taskAssignee(assignee).list();if (tasks != null && !tasks.isEmpty()){// 当前流程图所限制,只能做审核同意的动作for (Task task : tasks) {taskService.complete(task.getId());}}}/*** 审批添加备注*/@Testpublic void testAddComment() {String assignee = "张三";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();List<Task> tasks = taskService.createTaskQuery().processDefinitionKey("leave").taskAssignee(assignee).list();if (tasks != null && !tasks.isEmpty()) {// 当前流程图所限制,只能做审核同意的动作for (Task task : tasks) {// 添加备注taskService.addComment(task.getId(), task.getProcessInstanceId(), assignee + "表示同意");taskService.complete(task.getId());}}}/*** 查询审批历史*/@Testpublic void testSelectHistoryTask() {String processInstanceId = "2501";String assignee = "张三";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 查看历史信息HistoryService historyService = processEngine.getHistoryService();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();// 获取历史审核信息List<HistoricActivityInstance> userTask = historyService.createHistoricActivityInstanceQuery().activityType("userTask")// 指定实例的id.processInstanceId(processInstanceId).taskAssignee(assignee).finished().list();for (HistoricActivityInstance instance : userTask) {System.out.println("任务名称 = " + instance.getActivityName());System.out.println("任务开始时间 = " + instance.getStartTime());System.out.println("任务结束时间 = " + instance.getEndTime());System.out.println("任务耗时 = " + instance.getDurationInMillis());// 获取审批批注信息List<Comment> taskComments = taskService.getTaskComments(instance.getTaskId());if (!taskComments.isEmpty()){System.out.println("审批批注 = " + taskComments.get(0).getFullMessage());}}}/*** 查询流程相关信息*/@Testpublic void testDefinitionQuery() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 获取流程定义集合List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave")// 最新的一个版本.latestVersion().list();// 遍历集合for (ProcessDefinition definition : processDefinitionList) {System.out.println("流程定义id = " + definition.getId());System.out.println("流程定义名称 = " + definition.getName());System.out.println("流程定义key = " + definition.getKey());System.out.println("流程定义版本 = " + definition.getVersion());System.out.println("流程部署id = " + definition.getDeploymentId());System.out.println("===============");}}/*** 资源文件下载*/@Testpublic void testDownloadResource() throws IOException {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 获取流程定义集合List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave")// 按照版本降序.orderByProcessDefinitionVersion()// 降序.desc().list();// 获取最新那个ProcessDefinition definition = list.get(0);// 获取部署idString deploymentId = definition.getDeploymentId();// 获取bpmn的输入流InputStream bpmnInputStream = repositoryService.getResourceAsStream(deploymentId, definition.getResourceName());// 获取png的输入流
//        InputStream pngInputStream = repositoryService.getResourceAsStream(deploymentId, definition.getDiagramResourceName());String resourcePath = "C:/Users/ASUS/Desktop/" + File.separator + definition.getResourceName();File file = new File(resourcePath);if (!file.exists()) {file.getParentFile().mkdirs();}String diagramResourcePath = "C:/Users/ASUS/Desktop/" + File.separator + definition.getDiagramResourceName();file = new File(diagramResourcePath);if (!file.exists()) {file.getParentFile().mkdirs();}//复制文件FileCopyUtils.copy(bpmnInputStream, Files.newOutputStream(Paths.get(resourcePath)));
//        FileCopyUtils.copy(pngInputStream, Files.newOutputStream(Paths.get(diagramResourcePath)));}/*** 删除已经部署的流程定义*/@Testpublic void testDeleteDeploy() {// 删除已经部署的流程定义String deploymentId = "45001";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 删除流程定义,如果改流程定义已有流程实例启动,则会报错
//        repositoryService.deleteDeployment(deploymentId);// 设置 true,级联删除流程定义,即使有启动的实例也可以删除repositoryService.deleteDeployment(deploymentId, true);}/*** 启动流程,需要进行 BusinessKey 绑定流程实例*/@Testpublic void testStartBindBusinessKey() {String businessKey = "1";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = processEngine.getRuntimeService();// 根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的// 在启动流程的时候将业务key加进去ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave", businessKey);// 获取流程实例的相关信息System.out.println("流程定义id = " + instance.getProcessDefinitionId());System.out.println("流程实例id = " + instance.getId());System.out.println("业务标识 = " + instance.getBusinessKey());}/*** 跑到下一个节点,需要进行审批了,此时需要获取 BusinessKey 进而获取请假单信息*/@Testpublic void testGetBusinessKey() {// 1、获取李四的待办信息ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();TaskService taskService = processEngine.getTaskService();List<Task> task = taskService.createTaskQuery().taskAssignee("李四").processDefinitionKey("leave").list();// 2、获取 businessKey// 获取流程实例idString processInstanceId = task.get(1).getProcessInstanceId();RuntimeService runtimeService = processEngine.getRuntimeService();ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();String businessKey = processInstance.getBusinessKey();System.out.println("业务标识 = " + businessKey);}/*** 流程定义挂起与激活*/@Testpublic void testSuspendAllProcessInstance() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 获取流程定义对象ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();boolean suspended = processDefinition.isSuspended();// 输出流程定义状态System.out.println("流程定义状态:" + (suspended ? "已挂起" : "已激活"));String processDefinitionId = processDefinition.getId();if (suspended) {repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);System.out.println("流程id:" + processDefinitionId + "已激活");} else {repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);System.out.println("流程id:" + processDefinitionId + "已挂起");}}/*** 流程实例挂起与激活*/@Testpublic void testProcessInstance() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = processEngine.getRuntimeService();// 获取流程定义对象List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").list();// 遍历集合for (ProcessInstance processInstance  : processInstanceList) {boolean suspended = processInstance.isSuspended();// 输出流程定义状态System.out.println("流程实例状态:" + processInstance + "," + (suspended ? "已挂起" : "已激活"));String processDefinitionId = processInstance.getId();if (suspended) {runtimeService.activateProcessInstanceById(processDefinitionId);System.out.println("流程实例id:" + processDefinitionId + "已激活");} else {runtimeService.suspendProcessInstanceById(processDefinitionId);System.out.println("流程实例id:" + processDefinitionId + "已挂起");}}}
}

相关文章:

SpringBoot 集成 Activiti 7 工作流引擎

一. 版本信息 IntelliJ IDEA 2023.3.6JDK 17Activiti 7 二. IDEA依赖插件安装 安装BPM流程图插件&#xff0c;如果IDEA的版本超过2020,则不支持actiBPM插件。我的IDEA是2023版本我装的是 Activiti BPMN visualizer 插件。 在Plugins 搜索 Activiti BPMN visualizer 安装创建…...

UVM初学篇 -(22)UVM field_automation 域的自动化机制

field_automation机制是域的自动化的机制&#xff0c;这个机制的最大的优点是可以对一些变量进行批量的处理&#xff0c;比如对象拷贝、克隆、打印之类的变量。 一、 成员变量的注册 使用field_automation机制首先要用uvm_field 系列宏完成变量的注册&#xff0c;类中的成员变…...

STL二分查找

本课主要介绍容器部分里面的二分查找函数。涉及的函数有 3 个&#xff0c;这 3 个函数的强两个输入参数都和迭代器有关&#xff0c;或者说参数是可以迭代的&#xff0c;而第三个参数则是你要查找的值。 1. binary_search binary_search 的返回结果是 bool 值&#xff0c;如果找…...

啤酒游戏—企业经营决策沙盘

感谢黄浦区文华学院的邀请&#xff0c;今年是为南房集团开展系统思考培训的第二年。我们现在为客户设计的一整年系统思考训练中&#xff0c;会将系统环路结构图与真实议题研讨作为前置内容&#xff0c;让大家在理解整体框架后&#xff0c;再体验麻省理工学院系统动力学著名的“…...

尚硅谷-react教程-求和案例-@redux-devtools/extension 开发者工具使用-笔记

## 7.求和案例_react-redux开发者工具的使用(1).npm install redux-devtools/extension(2).store中进行配置import { composeWithDevTools } from redux-devtools/extension;export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))) src/redux/s…...

【动手学强化学习】part2-动态规划算法

阐述、总结【动手学强化学习】章节内容的学习情况&#xff0c;复现并理解代码。 文章目录 一、什么是动态规划&#xff1f;1.1概念1.2适用条件 二、算法示例2.1问题建模2.2策略迭代&#xff08;policyiteration&#xff09;算法2.2.1伪代码2.2.2完整代码2.2.3运行结果2.2.4代码…...

【python爬虫实战】爬取全年天气数据并做数据可视化分析!附源码

由于篇幅限制&#xff0c;无法展示完整代码&#xff0c;需要的朋友可在下方获取&#xff01;100%免费。 一、主题式网络爬虫设计方案 1. 主题式网络爬虫名称&#xff1a;天气预报爬取数据与可视化数据 2. 主题式网络爬虫爬取的内容与数据特征分析&#xff1a; - 爬取内容&am…...

初识Linux · 动静态库(incomplete)

目录 前言&#xff1a; 静态库 动态库 前言&#xff1a; 继上文&#xff0c;我们从磁盘的理解&#xff0c;到了文件系统框架的基本搭建&#xff0c;再到软硬链接部分&#xff0c;我们开始逐渐理解了为什么运行程序需要./a.out了&#xff0c;这个前面的.是什么我们也知道了。…...

华为OD机试 - 匿名信(Java 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;E卷D卷A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加…...

通过rancher2.7管理k8s1.24及1.24以上版本的k8s集群

目录 初始化实验环境 安装Rancher 登录Rancher平台 通过Rancher2.7管理已存在的k8s最新版集群 文档中的YAML文件配置直接复制粘贴可能存在格式错误&#xff0c;故实验中所需要的YAML文件以及本地包均打包至网盘. 链接&#xff1a;https://pan.baidu.com/s/1oYX4eGoBtW_R-7i…...

text-align的属性justify

text-align常用的属性是left、center、right&#xff0c;具体的可参考css解释&#xff0c;今天重点记录的对象是justify justify 可以使文本的两端都对齐在两端对齐文本中&#xff0c;文本行的左右两端都放在父元素的内边界上。然后&#xff0c;调整单词和字母间的间隔&#x…...

使用python自制桌面宠物,好玩!——枫原万叶桌宠,可以直接打包成exe去跟朋友炫耀。。。

大家好&#xff0c;我是小黄。 今天我们使用python实现一个桌面宠物。只需要gif动态图片就行。超级简单容易上手。 #完整源代码可在下方图片免费获取 一&#xff1a;下载相关的库文件。 我们本次使用到的库文件为&#xff1a;tkinter和pyautogui 下载命令&#xff1a; pip…...

使用 ASP.NET Core 8.0 创建最小 API

构建最小 API&#xff0c;以创建具有最小依赖项的 HTTP API。 它们非常适合需要在 ASP.NET Core 中仅包括最少文件、功能和依赖项的微服务和应用。 本教程介绍使用 ASP.NET Core 生成最小 API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是使用控制器。 有关在最小 …...

气候服务平台ClimateSERV2.0简介(python)

1 简介 ClimateSERV 2.0允许开发从业者、科学家/研究人员和政府决策者可视化和下载历史降雨数据、植被状况数据以及 180 天的降雨和温度预报&#xff0c;以增进对农业和水资源供应相关问题的理解并做出改进的决策。 这些数据可以通过 Web 应用程序直接访问&#xff0c;也可以…...

Docker | centos7上对docker进行安装和配置

安装docker docker配置条件安装地址安装步骤2. 卸载旧版本3. yum 安装gcc相关4. 安装需要的软件包5. 设置stable镜像仓库6. 更新yum软件包索引7. 安装docker引擎8. 启动测试9. 测试补充&#xff1a;设置国内docker仓库镜像 10. 卸载 centos7安装docker https://docs.docker.com…...

React--》掌握Valtio让状态管理变得轻松优雅

Valtio采用了代理模式&#xff0c;使状态管理变得更加直观和易于使用&#xff0c;同时能够与React等框架无缝集成&#xff0c;本文将深入探讨Valtio的核心概念、使用场景以及其在提升应用性能中的重要作用&#xff0c;帮助你掌握这一强大工具&#xff0c;从而提升开发效率和用户…...

python爬虫百度图片

直接给代码&#xff0c;可直接用&#xff0c;个人需要修改的地方有两处&#xff1a; self.directory 这是本地存储地址&#xff0c;修改为自己电脑的地址&#xff0c;另外&#xff0c;**{}**不要删spider.json_count 10 这是下载的图像组数&#xff0c;一组有30张图像&#x…...

前端开发:Vue中数据绑定原理

Vue 中最大的一个特征就是数据的双向绑定&#xff0c;而这种双向绑定的形式&#xff0c;一方面表现在元数据与衍生数据之间的响应&#xff0c;另一方面表现在元数据与视图之间的响应&#xff0c;而这些响应的实现方式&#xff0c;依赖的是数据链&#xff0c;因此&#xff0c;要…...

CTF-RE 从0到N: TEA

TEA TEA&#xff08;Tiny Encryption Algorithm&#xff0c;轻量加密算法&#xff09; 是一种简单、快速的对称加密算法。它是一个分组加密算法&#xff0c;通常用于加密 64 位的数据块&#xff0c;并使用 128 位的密钥。TEA 是一种“费斯妥结构”&#xff08;Feistel structu…...

python 使用PIL获取图片长宽

在Python中&#xff0c;你可以使用Pillow库&#xff08;PIL的一个分支和替代品&#xff09;来获取图片的长和宽。Pillow提供了丰富的图像处理功能&#xff0c;包括获取图像的基本属性&#xff0c;如尺寸。 以下是一个简单的示例&#xff0c;展示了如何使用Pillow库来获取图片的…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

Golang——9、反射和文件操作

反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一&#xff1a;使用Read()读取文件2.3、方式二&#xff1a;bufio读取文件2.4、方式三&#xff1a;os.ReadFile读取2.5、写…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...