springboot整合flowable工作流引擎的简单使用

内容来自网络整理,文章最下有引用地址,可跳转至相关资源页面。若有侵权请联系删除

环境:

mysql5.7.2
springboot 2.3.9.RELEASE
flowable 6.7.2

采坑:

1.当前flowable sql需要与引用的pom依赖一致,否则会报library version is '6.6.0.0', db version is 5.99.0.0 Hint: Set <property name="databaseSchemaUpdate" to value="true" or value="create-drop" (use create-drop for testing only!) in bean processEngineConfiguration in flowable.cfg.xml
在这里插入图片描述
在这里插入图片描述2.启动的时候会报类型转换异常,跟了断点是这里数据库是datetime类型,源码中是直接用Object接收(实际类型是LocalDateTime),然后当做String类型强转使用
在这里插入图片描述

很奇怪的是公司的项目同样的版本,没有做任何修改都是正常的,难道是我的mysql版本问题?

service:


import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Api(tags = "工作流接口")
@RestController
@RequestMapping("/Flow")
public class FlowController {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ProcessEngine processEngine;


    /**
     * 添加报销
     *
     * @param userId    用户Id
     * @param money     报销金额
     */
    @ApiOperation(value = "添加报销")
    @GetMapping(value = "add")
    public String addExpense(@RequestParam String userId, @RequestParam Integer money) {
        //启动流程
        HashMap<String, Object> map = new HashMap<>();
        map.put("taskUser", userId);
        map.put("money", money);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("Expense2", map);
        return "提交成功.流程Id为:" + processInstance.getId();
    }


    /**
     * 获取审批管理列表
     */
    @ApiOperation(value = "获取审批管理列表")
    @GetMapping(value = "/list")
    public Object list(@RequestParam String userId) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
        for (Task task : tasks) {
            System.out.println(task.toString());
        }
        return tasks.toArray().toString();
    }

    /**
     * 批准
     *
     * @param taskId 任务ID
     */
    @ApiOperation(value = "批准")
    @GetMapping(value = "/apply")
    public String apply(@RequestParam String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new RuntimeException("流程不存在");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("outcome", "通过");
        taskService.complete(taskId, map);
        return "processed ok!";
    }


    /**
     * 拒绝
     */
    @ApiOperation(value = "拒绝")
    @GetMapping(value = "reject")
    public String reject(@RequestParam String taskId) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("outcome", "驳回");
        taskService.complete(taskId, map);
        return "reject";
    }



    /**
     * 生成流程图
     *
     * @param processId 任务ID
     */
    @ApiOperation(value = "生成流程图")
    @GetMapping(value = "processDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, @RequestParam String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();

        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();

        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, false);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }


}
public class BossTaskHandler implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("老板");// 这里就是接入我们自己的项目需要自定义的审批人
    }

}

public class ManagerTaskHandler implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("经理");
    }

}


config

import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * 字体设置
 */
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {

    @Autowired
    private RepositoryService repositoryService;


    @Override
    public void configure(SpringProcessEngineConfiguration engineConfiguration) {
        engineConfiguration.setActivityFontName("宋体");
        engineConfiguration.setLabelFontName("宋体");
        engineConfiguration.setAnnotationFontName("宋体");

    }



    /**
     * 通过 InputStream 流部署流程定义
     * @return 部署流程对象,是流程定义、图像、表单等资源的容器
     */
    @Bean
    public Deployment deploy() {
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("报销流程.bpmn20.xml")
                .name("ExpenseProcess")
                .deploy();
        System.out.println("deploy.getId() = " + deploy.getId());
        System.out.println("deploy.getName() = " + deploy.getName());
        return deploy;
//        return createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in)
//                .name(name).tenantId(tenantId).category(category).deploy();
    }

}

resource:

pom文件

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>mybatis</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--flowable工作流依赖-->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-process</artifactId>
            <version>6.7.2</version>
        </dependency>
        <!--swagger ui 框架-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.9</version>
            <exclusions>
                <exclusion>
                    <artifactId>springfox-swagger2</artifactId>
                    <groupId>io.springfox</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>
    </dependencies>

application.yaml(其中xxxx部分替换成自己的)

server:
  port: 8082
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: xxxx
    password: xxxxx
    url: jdbc:mysql://xxxxxx.top:9306/flow?characterEncoding=utf-8&useSSL=false&useTimezone=true
    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:
  database-schema-update: false
  #关闭定时任务JOB
  async-executor-activate: false

swagger:
  enabled: true

log4j.properties

log4j.rootLogger=DEBUG

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

报销流程.bpmn20.xml(其中xxxx部分替换成自己的)

<?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:flowable="http://flowable.org/bpmn" 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"
             typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef">
    <process id="Expense2" name="ExpenseProcess" isExecutable="true">
        <documentation>报销流程</documentation>
        <startEvent id="start" name="开始"></startEvent>
        <userTask id="fillTask" name="出差报销" flowable:assignee="${taskUser}">
            <extensionElements>
                <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler">
                    <![CDATA[false]]></modeler:initiator-can-complete>
            </extensionElements>
        </userTask>
        <exclusiveGateway id="judgeTask"></exclusiveGateway>
        <userTask id="directorTak" name="经理审批">
            <extensionElements>
                <flowable:taskListener event="create"
                                       class="com.xxxx.xxxx.ManagerTaskHandler"></flowable:taskListener>
            </extensionElements>
        </userTask>
        <userTask id="bossTask" name="老板审批">
            <extensionElements>
                <flowable:taskListener event="create"
                                       class="com.xxxx.xxxx.BossTaskHandler"></flowable:taskListener>
            </extensionElements>
        </userTask>
        <endEvent id="end" name="结束"></endEvent>
        <sequenceFlow id="directorNotPassFlow" name="驳回" sourceRef="directorTak" targetRef="fillTask">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="bossNotPassFlow" name="驳回" sourceRef="bossTask" targetRef="fillTask">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow1" sourceRef="start" targetRef="fillTask"></sequenceFlow>
        <sequenceFlow id="flow2" sourceRef="fillTask" targetRef="judgeTask"></sequenceFlow>
        <sequenceFlow id="judgeMore" name="大于500元" sourceRef="judgeTask" targetRef="bossTask">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money > 500}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="bossPassFlow" name="通过" sourceRef="bossTask" targetRef="end">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="directorPassFlow" name="通过" sourceRef="directorTak" targetRef="end">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="judgeLess" name="小于500元" sourceRef="judgeTask" targetRef="directorTak">
            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money <= 500}]]></conditionExpression>
        </sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_Expense">
        <bpmndi:BPMNPlane bpmnElement="Expense" id="BPMNPlane_Expense">
            <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
                <omgdc:Bounds height="30.0" width="30.0" x="285.0" y="135.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="fillTask" id="BPMNShape_fillTask">
                <omgdc:Bounds height="80.0" width="100.0" x="405.0" y="110.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="judgeTask" id="BPMNShape_judgeTask">
                <omgdc:Bounds height="40.0" width="40.0" x="585.0" y="130.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="directorTak" id="BPMNShape_directorTak">
                <omgdc:Bounds height="80.0" width="100.0" x="735.0" y="110.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="bossTask" id="BPMNShape_bossTask">
                <omgdc:Bounds height="80.0" width="100.0" x="555.0" y="255.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
                <omgdc:Bounds height="28.0" width="28.0" x="771.0" y="281.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
                <omgdi:waypoint x="315.0" y="150.0"></omgdi:waypoint>
                <omgdi:waypoint x="405.0" y="150.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
                <omgdi:waypoint x="505.0" y="150.16611295681062"></omgdi:waypoint>
                <omgdi:waypoint x="585.4333333333333" y="150.43333333333334"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="judgeLess" id="BPMNEdge_judgeLess">
                <omgdi:waypoint x="624.5530726256983" y="150.44692737430168"></omgdi:waypoint>
                <omgdi:waypoint x="735.0" y="150.1392757660167"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="directorNotPassFlow" id="BPMNEdge_directorNotPassFlow">
                <omgdi:waypoint x="785.0" y="110.0"></omgdi:waypoint>
                <omgdi:waypoint x="785.0" y="37.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="37.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="110.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="bossPassFlow" id="BPMNEdge_bossPassFlow">
                <omgdi:waypoint x="655.0" y="295.0"></omgdi:waypoint>
                <omgdi:waypoint x="771.0" y="295.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="judgeMore" id="BPMNEdge_judgeMore">
                <omgdi:waypoint x="605.4340277777778" y="169.56597222222223"></omgdi:waypoint>
                <omgdi:waypoint x="605.1384083044983" y="255.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="directorPassFlow" id="BPMNEdge_directorPassFlow">
                <omgdi:waypoint x="785.0" y="190.0"></omgdi:waypoint>
                <omgdi:waypoint x="785.0" y="281.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="bossNotPassFlow" id="BPMNEdge_bossNotPassFlow">
                <omgdi:waypoint x="555.0" y="295.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="295.0"></omgdi:waypoint>
                <omgdi:waypoint x="455.0" y="190.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>

RepositoryService提供了在编辑和发布审批流程的 api。主要是模型管理和流程定义的业务 api。

1.提供了带条件的查询模型流程定义的api
repositoryService.createXXXQuery()
例如:
repositoryService.createModelQuery().list() 模型查询 
repositoryService.createProcessDefinitionQuery().list() 流程定义查询

repositoryService.createXXXXQuery().XXXKey(XXX) (查询该key是否存在)

2.提供一大波模型与流程定义的通用方法
模型相关(【本人亲测这里获取模型貌似不行,有知道的联系我】)
repositoryService.getModel()  (获取模型)
repositoryService.saveModel()  (保存模型)
repositoryService.deleteModel() (删除模型)
repositoryService.createDeployment().deploy(); (部署模型)
repositoryService.getModelEditorSource()  (获得模型JSON数据的UTF8字符串)
repositoryService.getModelEditorSourceExtra()  (获取PNG格式图像)

3.流程定义相关
repositoryService.getProcessDefinition(ProcessDefinitionId);  获取流程定义具体信息
repositoryService.activateProcessDefinitionById() 激活流程定义
repositoryService.suspendProcessDefinitionById()  挂起流程定义
repositoryService.deleteDeployment()  删除流程定义
repositoryService.getProcessDiagram()获取流程定义图片流
repositoryService.getResourceAsStream()获取流程定义xml流
repositoryService.getBpmnModel(pde.getId()) 获取bpmn对象(当前进行到的那个节点的流程图使用)

4.流程定义授权相关
repositoryService.getIdentityLinksForProcessDefinition() 流程定义授权列表
repositoryService.addCandidateStarterGroup()新增组流程授权
repositoryService.addCandidateStarterUser()新增用户流程授权
repositoryService.deleteCandidateStarterGroup() 删除组流程授权
repositoryService.deleteCandidateStarterUser()  删除用户流程授权

RuntimeService处理正在运行的流程。

runtimeService.createProcessInstanceBuilder().start() 发起流程
runtimeService.deleteProcessInstance() 删除正在运行的流程
runtimeService.suspendProcessInstanceById() 挂起流程定义
runtimeService.activateProcessInstanceById() 激活流程实例
runtimeService.getVariables(processInstanceId); 获取表单中填写的值
runtimeService.getActiveActivityIds(processInstanceId)获取以进行的流程图节点 (当前进行到的那个节点的流程图使用)

runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState(); 终止流程

HistoryService在用户发起审批后,会生成流程实例。historyService 为处理流程实例的 api,但是其中包括了已经完成的和未完成的流程实例。

historyService.createHistoricProcessInstanceQuery().list() 查询流程实例列表(历史流程,包括未完成的)
historyService.createHistoricProcessInstanceQuery().list().foreach().getValue() 可以获取历史中表单的信息
historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); 根绝id查询流程实例
historyService.deleteHistoricProcessInstance() 删除历史流程
historyService.deleteHistoricTaskInstance(taskid); 删除任务实例
historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list()  流程实例节点列表 (当前进行到的那个节点的流程图使用)

TaskService对流程实例的各个节点的审批处理。

流转的节点审批
taskService.createTaskQuery().list() 待办任务列表
taskService.createTaskQuery().taskId(taskId).singleResult();  待办任务详情
taskService.saveTask(task); 修改任务
taskService.setAssignee() 设置审批人
taskService.addComment() 设置审批备注
taskService.complete() 完成当前审批
taskService.getProcessInstanceComments(processInstanceId); 查看任务详情(也就是都经过哪些人的审批,意见是什么)
taskService.delegateTask(taskId, delegater); 委派任务
taskService.claim(taskId, userId);认领任务
taskService.unclaim(taskId); 取消认领
taskService.complete(taskId, completeVariables); 完成任务

任务授权
taskService.addGroupIdentityLink()新增组任务授权
taskService.addUserIdentityLink() 新增人员任务授权
taskService.deleteGroupIdentityLink() 删除组任务授权
taskService.deleteUserIdentityLink() 删除人员任务授权

ManagementService主要是执行自定义命令。

managementService.executeCommand(new classA())  执行classA的内部方法,classA要实现Command,重写execute方法

IdentityService用于身份信息获取和保存,这里主要是获取身份信息。

identityService.createUserQuery().userId(userId).singleResult();  获取审批用户的具体信息
identityService.createGroupQuery().groupId(groupId).singleResult(); 获取审批组的具体信息

主要几张表介绍
ACT_RU_TASK 每次启动的流程都会再这张表中,表示代办项, 流程结束会删除该流程数据
ACT_RU_EXECUTION 流程执行过程表, 会存该流程正在执行的过程数据, 流程结束会删除该流程数据
ACT_RU_VARIABLE 流程变量表, 流程中传的参数都会再该表存储, 流程结束会删除该流程数据
ACT_HI_PROCINST 历史运行流程, 当流程处理完了, 在ACT_RU_* 表中就不会有数据, 可以在该表中查询历史
ACT_HI_TASKINST 历史运行的task信息,
ACT_RE_PROCDEF 流程模板记录,同一个key多次发布version_字段会递增
ACT_RE_DEPLOYMENT 部署的流程模板, 可以启动流程使用的

摘抄自:
https://www.cnblogs.com/xfeiyun/p/16185713.html
https://www.cnblogs.com/codeMedita/p/15972476.html
https://blog.csdn.net/qq_42277412/article/details/121830797
https://blog.csdn.net/qq_42715450/article/details/126634276
多人审批:https://blog.csdn.net/qq_17798399/article/details/123424189

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

进程/线程 状态模型详解

前言&#xff1a;最近操作系统复习到线程的状态模型&#xff08;也可以说进程的状态模型&#xff0c;本文直接用线程来说&#xff09;时候&#xff0c;网上查阅资料&#xff0c;发现很多文章都说的很不一样&#xff0c;有五状态模型、六状态模型、七状态模型.......虽然都是对的…

[Python]爬虫基础——urllib库

urllib目录 一、简介二、发送请求1、urlopen()函数2、Request()函数 三、异常处理四、解析URL五、分析Robots协议 一、简介 urllib库是Python内置的标准库。包含以下四个模块&#xff1a; 1、request&#xff1a;模拟发送HTTP请求&#xff1b; 2、error&#xff1a;处理HTTP请…

前端技术——css

1.CSS的引入 【1】为什么要学习CSS? 如果只用HEML画页面的话--->这个页面就是页面上需要的元素罗列起来&#xff0c;但是页面效果很差&#xff0c;不好看&#xff0c;为了让页面好看&#xff0c;为了修饰页面。所以我们需要用到CSS。 CSS的作用&#xff1a;修饰HTML页面…

linux操作手册

开机&关机 指令 shutdown -h now 立刻进行关机 shutdown -h num num分钟后执行关机 shutdown -r now 现在重启计算机 halt 关机 rebboot 重启计算机 sync 把内存的数据同步到磁盘 注意事项 无论是重启还是关闭系统&#xff0c;都必须先执行sync&#xff0c;将内存…

使用无标注的数据训练Bert

文章目录 1、准备用于训练的数据集2、处理数据集3、克隆代码4、运行代码5、将ckpt模型转为bin模型使其可在pytorch中运用 Bert官方仓库&#xff1a;https://github.com/google-research/bert 1、准备用于训练的数据集 此处准备的是BBC news的数据集&#xff0c;下载链接&…

camunda表达式如何使用

在Camunda中&#xff0c;表达式是一种灵活的方式&#xff0c;可以用于在流程定义和表单中计算和处理数据。表达式可以在Camunda的各个环节中使用&#xff0c;例如服务任务、网关、表单、条件等。 以下是Camunda表达式的一些常见用途&#xff1a; 1、计算值&#xff1a;表达式可…

卢北辰:数据点亮梦想,能力驱动人生 | 提升之路系列(九)

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

ipad有必要用手写笔吗?电容笔和Apple pencil区别

与Apple Pencil最大的不同之处&#xff0c;在于普通的电容笔并不具备着重力压感&#xff0c;而是会给人一种倾斜的压感。如果不是频繁作画&#xff0c;那就用一支普通的电容笔。这种电容笔不但可以用于办公室&#xff0c;也可以用于记笔记、做练习。再说了&#xff0c;一支苹果…

【C++修炼之路:二叉搜索树】

目录&#xff1a; 二叉搜索树的概念构建一颗二叉树二叉树的查找二插树的插入 二叉树的删除删除右子树的最小节点 写一个中序来走这个二叉搜索树递归版删除&#xff08;recursion&#xff09;递归版插入&#xff08;recursion&#xff09;递归版查找&#xff08;recursion&#…

python+java+nodejs基于vue的企业人事工资管理系统

根据系统功能需求分析&#xff0c;对系统功能的进行设计和分解。功能分解的过程就是一个由抽象到具体的过程。 作为人事数据库系统&#xff0c;其主要实现的功能应包括以下几个模块&#xff1a; 1.登录模块 登录模块是由管理员、员工2种不同身份进行登录。 2.系统管理模块 用户…

中级软件设计师备考---软件工程1

目录 经典的模型敏捷开发方法【的分类】信息系统开发方法【的分类】结构化设计---内聚与耦合结构化设计---系统结构/模块结构 需求的分类 经典的模型 瀑布模型&#xff1a;最早的一类、适用于需求明确的项目、结构化的典型代表 原型模型&#xff1a;先构造一个建议的系统原型再…

【系统集成项目管理工程师】计算题专题一

一、决策树和期望货币值 1、项目经理向客户推荐了四种供应商选择方案。每个方案损益值已标在下面的决策树上。根据预期收益值&#xff0c;应选择设备供应商 A.供应商1B.供应商2C.供应商3D.供应商4 解题&#xff1a; 供应商 1&#xff1a;60% * 10000 &#xff08;-30000&am…

Oracle SQL执行计划操作(13)——其他相关操作

该类操作主要包括以上未进行讲解的其他相关操作。根据不同的具体SQL语句及其他相关因素,如下各操作可能会出现于相关SQL语句的执行计划。 1)SELECT STATEMENT 检索表中数据。该操作出现于通过select语句检索表中数据时产生的执行计划。该操作具体如图15-1中节点0所示。 图1…

RISC-V OS(老师的OS) 基于 汪辰老师的视频笔记

前言 最后面没写完&#xff0c;以后再补。。。 RISC-V OS RVOS 介绍 操作系统定义 操作系统&#xff08;英语&#xff1a;Operating System&#xff0c;缩写&#xff1a;OS&#xff09;是一组系统软件程序&#xff1a; 主管并控制计算机操作、运用和运行硬件、软件资源。提…

SPSS如何进行对应分析之案例实训?

文章目录 0.引言1.对应分析2.多重对应分析 0.引言 因科研等多场景需要进行数据统计分析&#xff0c;笔者对SPSS进行了学习&#xff0c;本文通过《SPSS统计分析从入门到精通》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;本文对对应分析进行阐述。 1.对应分析 &a…

55、RK3588使用MPP编码yuv到h264、解码h264到yuv模块开发和测试

基本思想&#xff1a;需要使用独立模块代码去实现自己的逻辑功能&#xff0c;所以在基于官方源码基础上&#xff0c;和参考附录几个官方链接&#xff0c;搞出一版rk3588编码测试和解码测试demo 测试视频/生成h264/生成yuv 链接: https://pan.baidu.com/s/1HbpeqMJb8HcgFpzaKh…

【Linux学习】多线程——线程控制 | 线程TCB

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 线程控制 | 线程TCB &#x1f9f0;线程控制&#x1f3b4;线程创建&#x1f3b4;线程结束&#x1…

写作业用白光还是暖光?盘点色温4000K的护眼台灯

台灯的白光或者暖光指的是台灯的色温&#xff0c;低色温的光线看起来发黄发红&#xff0c;高色温的光线发白发蓝。 如果灯光的光源是高品质光源&#xff0c;本身没有蓝光问题&#xff0c;那么色温的选择对护眼的影响是比较少的&#xff0c;更多的是对人学习工作状态&#xff0c…

Linux 之 vi 文本编辑器(二)

1、文本编辑器简介 Linux 中最常用的文本编辑器&#xff1a; vi&#xff1a;类 Unix 系统中默认的文本编辑器 vim&#xff1a;vi 编辑器的增强版本&#xff0c;习惯上也称 vi vi 文本编辑器的作用和特性&#xff1a; vi 可以执行插入、删除、查找、替换等众多文本操作&…

Leetcode268. 丢失的数字

Every day a leetcode 题目来源&#xff1a;268. 丢失的数字 解法1&#xff1a;排序 代码&#xff1a; /** lc appleetcode.cn id268 langcpp** [268] 丢失的数字*/// lc codestart class Solution { public:int missingNumber(vector<int> &nums){int n nums.s…
最新文章