Flowable工作流入门完整SpringBoot案例

news2024/12/25 11:37:44

文章目录

    • 一 、Flowable 的出现是为了什么
    • 二、Flowable 的优势
    • 三、常见的Java类/实例
      • 3.1 ProcessEngine
      • 3.2 RepositoryService
      • 3.3 ProcessDefinition
      • 3.4 Deployment
      • 3.5 RuntimeService
      • 3.6 ProcessInstance
      • 3.7 TaskService
      • 3.8 JavaDelegate
      • 3.9 其他
    • 四、核心数据库表
      • 4.1 数据库
      • 4.2 通用数据表(2个)
      • 4.3 历史表(8个,HistoryService接口操作的表)
      • 4.4 用户相关表(4个,IdentityService接口操作的表)
      • 4.5 流程定义、流程模板相关表(3个,RepositoryService接口操作的表)
      • 4.6 流程运行时表(6个,RuntimeService接口操作的表)
    • 五、在SpringBoot中简单使用
      • 5.1 Flowable入门之表的生成
      • 5.2 请假审批案例的实现
        • 5.2.1 项目结构
        • 5.2.2 application.yml
        • 5.2.3 holiday-request.bpmn20.xml
        • 5.2.4 HolidayRequestDTO
        • 5.2.5 HolidayRequestProcessService
        • 5.2.6 HolidayRequestProcessServiceImpl
        • 5.2.7 CallExternalSystemDelegate
        • 5.2.8 SendRejectionMail
        • 5.2.9 HolidayRequestProcessController
        • 5.2.10 运行&结果展示
    • 附录
      • 参考文章
      • 项目地址

一 、Flowable 的出现是为了什么

工作流(Workflow),是指对于一项业务,按照规定的流程,逐级传递、申请、执行等,并且受到了严格控制的一种业务过程。
BPM(Business Process Management)是指对于某项业务的整个生命周期进行全面管理的一种模式,最核心的内容包括了工作流、决策、交互等。在这些管理过程中,人员、系统等资源都是可以被自动调度的,以致达到更高效、完善的管理目的。
Flowable工作流是一款基于Java的轻量级开源工作流引擎,它支持BPMN2.0规范、CMMN规范,同时也提供REST API以及JavaAPI,支持Spring,Spring Boot等框架。借助Flowable工作流,企业可以快速构建出符合自己实际业务的工作流

二、Flowable 的优势

1、轻量级:Flowable工作流是一款轻量级的工作流引擎,启动快、体积小,且可以嵌入Java应用中使用。

2、开放源代码:Flowable工作流是一个开源的工作流引擎,保证企业在使用过程中不会受到第三方的监控与限制。

3、商用友好:Flowable工作流允许企业在商业环境下进行使用,并且提供Flowable Task一类的额外服务。

三、常见的Java类/实例

3.1 ProcessEngine

流程引擎实例,线程安全,一般一个工作流只需要初始化一次。
代码示例见本文的 5.1 小节。

3.2 RepositoryService

可以通过流程引擎(ProcessEngine)获取到。使用RepositoryService可以根据xml文件路径创建一个新的部署(Deployment),并调用 Deployment#deploy() 实际执行。
代码示例如下(其中xml是放在src/main/resources中):

RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()
 .addClasspathResource("holiday-request.bpmn20.xml")
 .deploy();

3.3 ProcessDefinition

流程定义实例,包含了流程的基本信息。通过RepositoryService获取,可以根据部署ID(Deployment)查询到对应的流程的基本信息,如流程的名字。首先获取到ProcessDefinitionQuery实例,再根据这个Query实例去查询1或多个流程定义实例。
代码示例如下,查询了1个流程定义实例:

ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
 .deploymentId(deployment.getId())
 .singleResult();
System.out.println("Found process definition : " + processDefinition.getName());

3.4 Deployment

部署实例,通过RepositoryService获取。
部署之后可以查询流程定义,也可以启动流程实例。
部署的代码示例见 3.2 小节。
PS:当xml文件放在 src/main/resources/processes/ 里时,会被自动部署。

3.5 RuntimeService

运行时服务实例,用来启动一个部署了的流程实例。
通过流程引擎获取,启动时可以传递参数。
代码示例如下:

RuntimeService runtimeService = processEngine.getRuntimeService();
Map<String, Object> variables = new HashMap<>();
variables.put("employee", employee);
variables.put("nrOfHolidays", nrOfHolidays);
variables.put("description", description);
ProcessInstance processInstance =
 
runtimeService.startProcessInstanceByKey("holidayRequest", variables);

3.6 ProcessInstance

流程实例,通过 RuntimeService 启动一个流程获取。

3.7 TaskService

任务服务实例,常用于查询、完成任务。

3.8 JavaDelegate

用于定义任务执行后自动执行到的一个逻辑。这是一个服务任务。

3.9 其他

1、IdentityService 用于管理(创建,更新,删除,查询……)组与用户。
2、FormService是可选服务。也就是说Flowable没有它也能很好地运行,而不必牺牲任何功能。
3、HistoryService暴露Flowable引擎收集的所有历史数据。要提供查询历史数据的能力。
4、ManagementService通常在用Flowable编写用户应用时不需要使用。它可以读取数据库表与表原始数据的信息,也提供了对作业(job)的查询与管理操作。
5、DynamicBpmnService可用于修改流程定义中的部分内容,而不需要重新部署它。例如可以修改流程定义中一个用户任务的办理人设置,或者修改一个服务任务中的类名。

四、核心数据库表

4.1 数据库

1、Flowable的所有数据库表都以ACT_开头。第二部分是说明表用途的两字符标示符。服务API的命名也大略符合这个规则。
2、ACT_RE_: 'RE’代表repository。带有这个前缀的表包含“静态”信息,例如流程定义与流程资源(图片、规则等)。
3、ACT_RU_: 'RU’代表runtime。这些表存储运行时信息,例如流程实例(process instance)、用户任务(user task)、变量(variable)、作业(job)等。Flowable只在流程实例运行中保存运行时数据,并在流程实例结束时删除记录。这样保证运行时表小和快。
4、ACT_HI_: 'HI’代表history。这些表存储历史数据,例如已完成的流程实例、变量、任务等。
5、ACT_GE_: 通用数据。在多处使用。

4.2 通用数据表(2个)

act_ge_bytearray:二进制数据表,如流程定义、流程模板、流程图的字节流文件;
act_ge_property:属性数据表(不常用);

4.3 历史表(8个,HistoryService接口操作的表)

act_hi_actinst:历史节点表,存放流程实例运转的各个节点信息(包含开始、结束等非任务节点);
act_hi_attachment:历史附件表,存放历史节点上传的附件信息(不常用);
act_hi_comment:历史意见表;
act_hi_detail:历史详情表,存储节点运转的一些信息(不常用);
act_hi_identitylink:历史流程人员表,存储流程各节点候选、办理人员信息,常用于查询某人或部门的已办任务;
act_hi_procinst:历史流程实例表,存储流程实例历史数据(包含正在运行的流程实例);
act_hi_taskinst:历史流程任务表,存储历史任务节点;
act_hi_varinst:流程历史变量表,存储流程历史节点的变量信息;

4.4 用户相关表(4个,IdentityService接口操作的表)

act_id_group:用户组信息表,对应节点选定候选组信息;
act_id_info:用户扩展信息表,存储用户扩展信息;
act_id_membership:用户与用户组关系表;
act_id_user:用户信息表,对应节点选定办理人或候选人信息;

4.5 流程定义、流程模板相关表(3个,RepositoryService接口操作的表)

act_re_deployment:部属信息表,存储流程定义、模板部署信息;
act_re_procdef:流程定义信息表,存储流程定义相关描述信息,但其真正内容存储在act_ge_bytearray表中,以字节形式存储;
act_re_model:流程模板信息表,存储流程模板相关描述信息,但其真正内容存储在act_ge_bytearray表中,以字节形式存储;

4.6 流程运行时表(6个,RuntimeService接口操作的表)

act_ru_task:运行时流程任务节点表,存储运行中流程的任务节点信息,重要,常用于查询人员或部门的待办任务时使用;
act_ru_event_subscr:监听信息表,不常用;
act_ru_execution:运行时流程执行实例表,记录运行中流程运行的各个分支信息(当没有子流程时,其数据与act_ru_task表数据是一一对应的);
act_ru_identitylink:运行时流程人员表,重要,常用于查询人员或部门的待办任务时使用;
act_ru_job:运行时定时任务数据表,存储流程的定时任务信息;
act_ru_variable:运行时流程变量数据表,存储运行中的流程各节点的变量信息;

五、在SpringBoot中简单使用

以下以Mysql数据库为例,java版本为11,开发工具使用Idea。

5.1 Flowable入门之表的生成

首先创建一个普通的Maven项目,引入依赖如下:

<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.8.0</version>
</dependency>

<!--mysql8-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

<!--日志-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>

随后定义 log4j.properties文件:

log4j.rootLogger=DEBUG, CA

log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n

然后是一个普通的Java类,定义Main方法(这里只打印了执行引擎实例):

package org.feng.flowable;

import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;

public class MainFlowableClient {

    /**
     * 执行引擎(现成安全的对象,在一个应用中只需要初始化一次)
     */
    private static final ProcessEngine processEngine;
    static {
        // 单机处理引擎配置信息实例
        StandaloneProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration();
        // 连接数据库的信息
        cfg.setJdbcUrl("jdbc:mysql://localhost:3306/flowable?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC");
        cfg.setJdbcUsername("root");
        cfg.setJdbcPassword("123456");
        cfg.setJdbcDriver("com.mysql.cj.jdbc.Driver");
        // 设置了true,确保在JDBC参数连接的数据库中,数据库表结构不存在时,会创建相应的表结构。
        cfg.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        // 通过配置获取执行引擎
        processEngine = cfg.buildProcessEngine();
    }

    public static void main(String[] args) {
        System.out.println(processEngine);
    }
}

启动main方法后的效果是,会连接到指定的数据库,如果库中没有Flowable对应数据库,或结构发生变化,会自动创建或更新表。

5.2 请假审批案例的实现

在官方手册中有一个请假审批的案例,讲的比较详细。
在这里插入图片描述

大致流程是这样的:发起一个开始事件,随后一个工作流引用到用户任务(Approve or reject request),分配给到经理,经理需要决定通过请求还是拒绝请求。然后使用一个排他网关,将流程实例路由到批准或者驳回。如果批准,则需要将申请注册至某个外部系统,并跟着另一个用户任务,将经理的决定通知给申请人。当然也可以改为发送邮件。如果驳回,则为雇员发送一封邮件通知他。
依据以上示例编写Java代码实现。

5.2.1 项目结构

项目中对oneTask任务、请假流程(holidayRequest)任务做了处理。本小节只关注请假流程。
并且数据表前面已经创建好了。

在这里插入图片描述
项目依赖使用 springboot 2.7.12 版本:

		<dependency>
			<groupId>org.flowable</groupId>
			<artifactId>flowable-spring-boot-starter</artifactId>
			<version>6.8.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.28</version>
		</dependency>

5.2.2 application.yml

spring:
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost:3306/flowable?serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    hikari:
      read-only: false
      #客户端等待连接池连接的最大毫秒数
      connection-timeout: 60000
      #允许连接在连接池中空闲的最长时间(以毫秒为单位)
      idle-timeout: 60000
      #连接将被测试活动的最大时间量
      validation-timeout: 3000
      #池中连接关闭后的最长生命周期
      max-lifetime: 60000
      #最大池大小
      maximum-pool-size: 60
      #连接池中维护的最小空闲连接数
      minimum-idle: 10
      #从池返回的连接的默认自动提交行为。默认值为true
      auto-commit: true
      #如果您的驱动程序支持JDBC4,我们强烈建议您不要设置此属性
      connection-test-query: SELECT 1
      #自定义连接池名称
      pool-name: myHikarCp

flowable:
  # 设置了true,确保在JDBC参数连接的数据库中,数据库表结构不存在时,会创建相应的表结构。
  database-schema-update: false
  #关闭定时任务JOB
  async-executor-activate: false

5.2.3 holiday-request.bpmn20.xml

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:flowable="http://flowable.org/bpmn"
             typeLanguage="http://www.w3.org/2001/XMLSchema"
             expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef">

    <!--
        每一步都需要有一个id属性,标记当前流程的步骤,name是可选的
        当流程开始执行的时候,执行过程会从startEvent开始,跟着sequenceFlow往下走

        startEvent -> approveTask -> approved -> externalSystemCall -> holidayApprovedTask -> assign:employee -> approveEnd
                                  -> reject -> sendRejectionMail -> rejectEnd
      -->

    <process id="holidayRequest" name="Holiday Request" isExecutable="true">

        <startEvent id="startEvent"/>
        <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

        <userTask id="approveTask" name="Approve or reject holiday request" flowable:candidateGroups="managers"/>
        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>

        <exclusiveGateway id="decision"/>
        <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
                 ${approved}
              ]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow  sourceRef="decision" targetRef="sendRejectionMail">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
                 ${!approved}
               ]]>
            </conditionExpression>
        </sequenceFlow>

        <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     flowable:class="org.feng.service.task.CallExternalSystemDelegate"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

        <userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
        <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

        <serviceTask id="sendRejectionMail" name="Send out rejection email"
                     flowable:class="org.feng.service.task.SendRejectionMail"/>
        <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>

        <endEvent id="approveEnd"/>

        <endEvent id="rejectEnd"/>
    </process>
</definitions>

5.2.4 HolidayRequestDTO

package org.feng.dto;

public class HolidayRequestDTO {
    /**
     * 员工名
     */
    private String employee;
    /**
     * 休假天数
     */
    private Integer nrOfHolidays;
    /**
     * 备注
     */
    private String description;

    // 篇幅原因省略get/set方法,自己写的时候要加上
}

5.2.5 HolidayRequestProcessService

package org.feng.service;

import org.feng.dto.HolidayRequestDTO;

public interface HolidayRequestProcessService {

    void holidayRequest(HolidayRequestDTO holidayRequestDTO);
}

5.2.6 HolidayRequestProcessServiceImpl

package org.feng.service.impl;

import org.feng.dto.HolidayRequestDTO;
import org.feng.service.HolidayRequestProcessService;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

@Service
public class HolidayRequestProcessServiceImpl implements HolidayRequestProcessService {

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private HistoryService historyService;

    @Override
    public void holidayRequest(HolidayRequestDTO holidayRequestDTO) {
        // 组装参数
        Map<String, Object> variables = new HashMap<>(8);
        variables.put("employee", holidayRequestDTO.getEmployee());
        variables.put("nrOfHolidays", holidayRequestDTO.getNrOfHolidays());
        variables.put("description", holidayRequestDTO.getDescription());

        // 启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayRequest", variables);
        // 查询任务,组是“managers”
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
        // 打印查询到的任务信息
        System.out.println("You have " + tasks.size() + " tasks:");
        for (int i = 0; i < tasks.size(); i++) {
            System.out.println((i + 1) + ") " + tasks.get(i).getName());
        }
        // 获取要执行的任务:查询到的最后一个task
        if (!CollectionUtils.isEmpty(tasks)) {
            Task task = tasks.get(tasks.size() - 1);
            Map<String, Object> processVariables = taskService.getVariables(task.getId());
            System.out.println(processVariables.get("employee") + " wants " +
                    processVariables.get("nrOfHolidays") + " of holidays. Do you approve this?");

            // 生成随机数,从而决定请求是通过还是拒绝
            boolean approved = ThreadLocalRandom.current().nextInt(1, 10) > 5;
            System.out.println(approved ? "do approved" : "do rejected");
            variables = new HashMap<>();
            variables.put("approved", approved);

            // 执行完成
            taskService.complete(task.getId(), variables);
        }

        // 历史数据
        List<HistoricActivityInstance> activities =
                historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(processInstance.getId())
                        .finished()
                        .orderByHistoricActivityInstanceEndTime().asc()
                        .list();

        for (HistoricActivityInstance activity : activities) {
            System.out.println(activity.getActivityId() + " took "
                    + activity.getDurationInMillis() + " milliseconds");
        }
    }


    @PostConstruct
    private void deploy() {
        // 手动部署
        Deployment deployment = repositoryService.createDeployment()
                // 从xml文件读取流程
                .addClasspathResource("holiday-request.bpmn20.xml")
                // 执行部署
                .deploy();

        // 根据部署ID获取流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();

        // 输出流程名
        System.out.println("Found process definition : " + processDefinition.getName());
    }
}

5.2.7 CallExternalSystemDelegate

package org.feng.service.task;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;

public class CallExternalSystemDelegate implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("Calling the external system for employee "
                + execution.getVariable("employee"));
    }
}

5.2.8 SendRejectionMail

package org.feng.service.task;

import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;

public class SendRejectionMail implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("Send Rejection Email ");
    }
}

5.2.9 HolidayRequestProcessController

package org.feng.controller;

import org.feng.dto.HolidayRequestDTO;
import org.feng.service.HolidayRequestProcessService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class HolidayRequestProcessController {

    @Resource
    private HolidayRequestProcessService holidayRequestProcessService;

    @PostMapping("/holidayRequest")
    public void holidayRequest(@RequestBody HolidayRequestDTO holidayRequestDTO) {
        holidayRequestProcessService.holidayRequest(holidayRequestDTO);
    }
}

5.2.10 运行&结果展示

启动SpringBoot项目,随后访问rest接口:

POST http://localhost:8080/holidayRequest
Content-Type: application/json

{
  "employee": "bob",
  "nrOfHolidays": 3,
  "description": "就请假而已"
}

多次调整参数,可以看到不同的结果。
部分结果展示:
请求通过时:

You have 1 tasks:
1) Approve or reject holiday request
bob wants 3 of holidays. Do you approve this?
do approved
Calling the external system for employee bob
startEvent took 3 milliseconds
_flow_startEvent__approveTask took 0 milliseconds
approveTask took 1122 milliseconds
_flow_approveTask__decision took 0 milliseconds
_flow_decision__externalSystemCall took 0 milliseconds
decision took 1 milliseconds
externalSystemCall took 1 milliseconds
_flow_externalSystemCall__holidayApprovedTask took 0 milliseconds

请求拒绝时:

You have 1 tasks:
1) Approve or reject holiday request
bob wants 3 of holidays. Do you approve this?
do rejected
Send Rejection Email 
_flow_startEvent__approveTask took 0 milliseconds
startEvent took 0 milliseconds
approveTask took 927 milliseconds
_flow_approveTask__decision took 0 milliseconds
_flow_decision__sendRejectionMail took 0 milliseconds
decision took 2 milliseconds
_flow_sendRejectionMail__rejectEnd took 0 milliseconds
sendRejectionMail took 1 milliseconds
rejectEnd took 1 milliseconds

附录

参考文章

Flowable工作流入门看这篇就够了-腾讯云开发者社区-腾讯云
Flowable工作流详解_笔记大全_设计学院
Flowable官网
Flowable官方用户手册
Flowable流程引擎入门

项目地址

https://gitee.com/fengsoshuai/flowable-demo

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

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

相关文章

CET4写译学习

学习记录笔记&#xff1a; 05.四级写译技巧(上)_哔哩哔哩_bilibili 不会的东西不要往上写。寻找可以替换的词。 保证写的所有内容都是正确的。 切题&#xff0c;论证清楚。 要有自己的观点&#xff0c;然后去论证。 词汇&#xff0c;语法&#xff0c;句子结构都整好。 文…

【软件测试】接口测试工具APIpost

说实话&#xff0c;了解APIpost是因为&#xff0c;我的所有接口相关的文章下&#xff0c;都有该APIpost水军的评论&#xff0c;无非就是APIpost是中文版的postman&#xff0c;有多么多么好用&#xff0c;虽然咱也还不是什么啥网红&#xff0c;但是不知会一声就乱在评论区打广告…

Linux日志

rsyslog系统日志管理 哪类程序产生的什么日志放到什么地方 处理日志的进程 第一类&#xff1a; rsyslogd&#xff1a;系统专职日志程序&#xff0c;处理绝大部分日志记录&#xff0c;系统操作相关的信息&#xff0c;如登录信息&#xff0c;程序启动关闭相关信息&#xff0c…

C#语言实现4K图片放大缩小和平移显示性能的速度测试

在介绍“熊猫视图.Net图形控件”系列文章中&#xff0c; 【“熊猫视图.Net图形控件”介绍链接】https://blog.csdn.net/mosangbike/article/details/126026801有对显示图像文件的测试结果&#xff0c;当时测试的不太严谨。今天抽时间详细测试了一下。 从网上找了一张Jpg图像作…

亲宝宝 实习 面经

目录 1.char varchar 长度是字符数还是字节数 编码格式2.整型数据类型3.decimal及其实现4.慢查询5.索引失效6.explain7.for foreach性能差异8.数据库事务隔离级别9.binlog redolog 二阶段提交10.redis数据类型11.redis实现消息队列12.mybatis传参方法13.insert返回主键 1.char …

数据库实验一 数据库和数据表的建立、修改和删除

任务描述 本关任务&#xff1a;建立数据库 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 如何创建数据库&#xff0c;显示已经建立的数据库 相关知识 创建数据库 创建数据库是在系统磁盘上划分一块区域用于数据的存储和管理。 命令格式&#xff1a; CREATE DATABA…

粮油智能制造MES追溯系统源码

粮油生产加工MES追溯系统源码 粮油生产加工MES追溯系统&#xff0c;实现从种植、加工、检验、销售各个环节的数据采集。 粮油MES质量管控防伪溯源系统可广泛用于粮油生产加工领域。实现种植主体、种植基地、生产计划、压榨、精炼、包装、销售、物料管理、检验检测等各个环节的…

Java对象拷贝MapStruct

介绍 编译期即可生成对象复制代码。简单理解&#xff0c;功能定位org.springframework.beans.BeanUtils。 官网&#xff0c;GitHub-MapStruct。 入门 maven项目引入依赖&#xff1a; mapstruct&#xff1a;包含必要注解&#xff0c;如Mappingmapstruct-processor&#xff1…

Cereal 靶机

环境准备 靶机链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;bcj2 虚拟机网络链接模式&#xff1a;桥接模式 攻击机系统&#xff1a;kali linux 2021.1 信息收集 1.探测目标靶机 arp-scan -l 2.nmap -p- -A -T4 192.168.1.107 探测目标靶机开放端口和服务 漏…

我是如何入门网络安全?有什么自学心得?

我是如何入门&#xff0c;网络安全的 那年我高三毕业的时候要填志愿前几天 我妈问我想学什么专业。 我说&#xff0c;想学网络设计、或者计算机、网络安全工程师 那时候还比较年轻&#xff0c;也对网络&#xff0c;计算机这方面感兴趣嘛 于是我妈和我爸决定让我学网管。 我…

卡尔曼滤波与组合导航原理(十三)无迹卡尔曼滤波UKF

一、蒙特卡洛仿真 蒙特卡洛仿真的基本原理是通过生成大量的随机样本&#xff0c;以近似地估计实际事件的概率和预测结果。 它是以蒙特卡洛赌场命名的&#xff0c;因为它使用随机数和概率统计的方法来模拟现实世界中的各种情况和结果&#xff0c;就像在赌场中抛骰子或发牌一样。…

LeetCode_Day6 | 有效的字母异位词、两个数组的交集、快乐数、两数之和!

LeetCode_哈希表 242.有效的字母异位词1.题目描述2.题解 349.两个数组的交集1.题目描述2.题解 202.快乐数1.题目描述2.题解思路(官方题解啊&#xff01;看了好几遍真难) 3.算法4.代码实现5.复杂度分析 1.两数之和1.题目描述2.哈希表法3.代码实现 242.有效的字母异位词 1.题目描…

《三》TypeScript 中函数的类型

TypeScript 允许指定函数的参数和返回值的类型。 函数声明的类型定义&#xff1a;function 函数名(形参: 形参类型, 形参: 形参类型, ...): 返回值类型 {} function sum(x: number, y: number): number {return x y } sum(1, 2) // 正确 sum(1, 2, 3) // 错误。输入多余的或者…

CAN总线竞争与仲裁机制分析

1、CAN总线的接口 (1)CAN总线是串行、差分信号、异步总线&#xff0c;传输数据用两根信号线组成的差分信号线&#xff1b; (2)CANH和CANL是一组双绞线&#xff0c;两根线的电平差值表示逻辑1和0&#xff1b; 2、CAN总线显性、隐形电平 显性电平代表逻辑0&#xff0c;隐形电平代…

【Prometheus】mysqld_exporter+Grafana+AlertManager预警

环境 prometheus-2.44.0 mysqld_exporter-0.14.0 grafana-enterprise-9.1.2-1.x86_64.rpm alertmanager-0.25.0. 简介 mysql_exporter是用来收集MysQL或者Mariadb数据库相关指标的&#xff0c;mysql_exporter需要连接到数据库并有相关权限。既可以用二进制安装部署&#xff0c;…

08.JavaWeb-SpringMVC

2.SpringMVC Spring框架是一个开源的轻量级框架&#xff0c;SpringMVC是Spring的一个子框架 2.1SpringMVC工作机制 2.1.1 DispatcherServlet前端控制器 【不需要程序员开发】 作用&#xff1a;作为接受请求&#xff0c;响应结果&#xff0c;相当于转发器&#xff0c;中央处理…

『Jenkins』最新版Jenkins安装与Git集成—CentOS 7安装的详细教程

&#x1f4e3;读完这篇文章里你能收获到 图文形式安装Jenkins在Jenkins集成Git并进行的配置感谢点赞收藏&#xff0c;避免下次找不到~ 文章目录 一、准备工作1. 安装Java Development Kit (JDK 11) 二、安装Jenkins1. 下载和安装最新版的Jenkins2. 启动Jenkins服务3. 将Jenkin…

【线性代数】

求解线性方程组 右乘向量/矩阵 把左边的矩阵拆成一个个列向量&#xff0c;右边的向量表示对左边列向量组的线性组合。 [ c o l 1 c o l 2 c o l 3 ] [ 3 4 5 ] [ 3 c o l 1 4 c o l 2 5 c o l 3 ] \left[\begin{array}{c} col_{1} & col_{2} & col_{3} \end{array}\…

类的三大特性——>封装

目录 理论知识 数据抽象&#xff1a; 封装&#xff1a; 细节知识 访问控制与封装 访问控制 STL使用封装 理论知识 类的基本思想是数据抽象、和封装。 数据抽象&#xff1a; 是一种依赖接口和实现分离的一种编程技术&#xff1a; 接口&#xff1a;包括用户所能执行的操…

点燃创作灵感:Prompt 实践指南揭秘!让 ChatGPT 更智能的六种策略(上)

在和 ChatGPT 对话中&#xff0c;Prompt 提示词的选择&#xff0c;如果遵循以下六种策略&#xff0c;我们将获得更好、更符合要求的回答。 这些策略&#xff0c;后几种更适合在编程调用 ChatGPT API 时使用&#xff0c;不过也适用直接和 ChatGPT 对话&#xff0c;让它更好的理解…