springboot整合flowable的简单使用

news2024/12/26 22:40:47

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

环境:

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

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

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

相关文章

管理后台项目-07-菜单管理和动态展示菜单和按钮

目录 1-菜单管理 1.1-菜单管理列表 1.2-添加|修改功能 1.3-删除菜单 2-动态菜单按钮展示 2.1-路由文件的整理 2.2-动态展示不同的路由 1-菜单管理 当用户点击菜单管理的时候&#xff0c;会展示当前所有菜单&#xff0c;树型结构展示...并且可以对菜单进行新增编辑删除操…

倾斜摄影超大场景的三维模型在网络发布应用遇到常见的问题浅析

倾斜摄影超大场景的三维模型在网络发布应用遇到常见的问题浅析 倾斜摄影超大场景的三维模型在网络发布应用时&#xff0c;常见的问题包括&#xff1a; 1、加载速度慢。由于数据量巨大&#xff0c;网络发布时需要将数据文件分割成多个小文件进行加载&#xff0c;这可能会导致页…

Sonatype Nexus兼容apk格式仓库

Sonatype Nexus兼容apk格式仓库 sonatype/nexus3 当前最新版本&#xff1a;sonatype/nexus3:3.52.0 查看nexus支持的仓库格式 创建一个nexus 容器&#xff1a; docker run -d -p 8081:8081 --name nexus sonatype/nexus3:3.52.0查看启动日志&#xff1a; docker logs nexu…

HTML5画布(图像)

案例1&#xff1a; <!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"><title></title><script>window.onloadfunction(){var cdocument.getElementById("myCanvas");var cxt c.getConte…

Vue3 手把手按需引入 Echarts

背景&#xff1a;新项目采用 Vue3 作为前端项目框架&#xff0c;避免不了要使用 echarts&#xff0c;但是在使用的时候&#xff0c;出现了与 Vue2 使用不一样的地方&#xff0c;所以特别记下来&#xff0c;希望给到有需要的同学一些帮助。 下载Echarts依赖 # 自己使用的yarn y…

《Odoo开发者模式必知必会》—— 缘起

Odoo作为业界优秀的开源商务软件&#xff0c;在全球范围内拥有广泛的使用者。在领英国际&#xff0c;可以搜索到全球很多国家都有大量odoo人才需求的招聘信息。在国内&#xff0c;虽然已经有为数不少的企业&#xff0c;他们或者已经使用odoo&#xff0c;或者正在了解odoo&#…

支付宝异步通知说明

如何设置异步通知地址 不同接口接收异步通知设置方式不同&#xff0c;可查看 哪些接口支持触发异步。 设置 notify_url 接收异步 对于支付产生的交易&#xff0c;支付宝会根据原始支付 API 中传入的异步通知地址 notify_url&#xff0c;通过 POST 请求的形式将支付结果作为参…

从零开始学习CTF的完整指南

前言 想要学习CTF却不知从何开始&#xff1f;本文提供了一份完整的指南&#xff0c;从Linux系统基础、网络协议基础、二进制分析、Web安全、杂项题型以及算法与密码学等方面&#xff0c;为零基础小白提供了学习路线和知识点概述。 网络安全 网络安全是 CTF 的基础&#xff0…

还不知道怎么 Mock ,用这 6款工具

以下是几个常用的国外可以mock测试的工具&#xff0c;供参考&#xff1a; MockServer: MockServer 是一个开源的 API mock 测试工具&#xff0c;提供了强大的模拟服务器和 mock 服务功能。MockServer 支持多种语言和格式&#xff0c;包括 Java、.NET、REST、SOAP 等。 WireMoc…

优思学院|做质量管理有七大工具,都是什么?

质量管理七大工具&#xff08;Seven Basic Quality Tools&#xff09;是由日本质量大师石川馨于20世纪50年代首次提出&#xff0c;这些工具被广泛应用于制造业和服务业的质量管理实践中&#xff0c;优思学院认为这七个工具除了是质量人常用的工具之外&#xff0c;也可作为学习六…

OpenGL光照:光照基础

引言 现实世界的光照是极其复杂的&#xff0c;而且会受到诸多因素的影响&#xff0c;这是以目前我们所拥有的处理能力无法模拟的。因此OpenGL的光照仅仅使用了简化的模型并基于对现实的估计来进行模拟&#xff0c;这样处理起来会更容易一些&#xff0c;而且看起来也差不多一样。…

Windows环境下运行StableDiffusion常见问题

目录 常见问题 一、问题1&#xff1a;22.2.2➡23.1.1 Torch is not able to use GPU 解决方案 二、问题2&#xff1a;exit code:128 CLIP did not run sucessfully 解决方案 三、问题3&#xff1a;exit code:128 open-clip did not run sucessfully 解决方案 四、问题4…

工业数字智能化常用系统简介

文章目录 QMS1&#xff0c;IPQC&#xff08;过程检&#xff09;2&#xff0c;OQC&#xff08;出货检&#xff09;3&#xff0c;SPC&#xff08;统计工序控制&#xff09;4&#xff0c;Andon&#xff08;安灯&#xff09;5&#xff0c;其他 MDMMES QMS 质量管理体系&#xff0c;…

【虚拟机】在Windows11上下载安装VMware虚拟机以及Ubuntu(Linux)详细操作

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

获取速卖通aliexpress分类详情 API接口

aliexpress分类详情API接口是速卖通提供的一种产品数据接口&#xff0c;可以帮助速卖通卖家快速地将产品分类、属性、价格等信息&#xff0c;通过 aliexpress API接口来快速生成产品描述、图片、视频等产品信息&#xff0c;让卖家可以更方便地管理自己的产品&#xff0c;快速获…

凌波微课讲师文章|福建农林大学周顺桂团队ISME J:首次发现嗜热病毒参与超高温堆肥过程中碳氮养分转化过程

第一作者&#xff1a;廖汉鹏 通讯作者&#xff1a;周顺桂&#xff0c;Ville-Petri Friman 在线发表时间&#xff1a;2023.04.08 论文网页&#xff1a;https://doi.org/10.1038/s41396-023-01404-1 DOI号&#xff1a;10.1038/s41396-023-01404-1 图片摘要 成果简介 近日&a…

《程序员面试金典(第6版)》面试题 16.05. 阶乘尾数

题目描述 设计一个算法&#xff0c;算出 n 阶乘有多少个尾随零。 示例 1: 输入: 3输出: 0解释: 3! 6, 尾数中没有零。 示例 2: 输入: 5输出: 1解释: 5! 120, 尾数中有 1 个零 说明: 你算法的时间复杂度应为 O(log n) 。 解题思路与代码 这道题&#xff0c;乍一看很简单…

大数据之Hadoop分布式计算框架MapReduce

这里写目录标题 一、MapReduce概述二、MapReduce编程模型简述三、MapReduce词频统计案例mvn clean package 四、词频统计案例进阶之Combiner五、词频统计案例进阶之Partitioner六、案例二介绍 一、MapReduce概述 Hadoop MapReduce 是一个分布式计算框架&#xff0c;用于编写批处…

p69 内网安全-域横向 CobaltStrikeSPNRDP

数据来源 SPN&#xff08;Secret Private Network缩写&#xff09;_百度百科 (baidu.com) 演示案例 域横向移动RDP传递-Mimikatz域横向移动SPN服务-探针&#xff0c;请求&#xff0c;导出&#xff0c;破解&#xff0c;重写域横向移动测试流程一把梭哈-CobaltStrike初体验 案例…

python+nodejs+php+springboot+vue 企业员工健康体检预约管理系统

目 录 1 引言 1 1.1 研究的目的及意义 2 1.2 研究的主要内容 2 1.3 本文的组织结构 2 2 平台开发相关技术 3 2.1python技术的简介 3 2.2 django框架 4 2.3 MYSQL数据库 4 2.4 MySQL环境配置 5 2.5 B/S架构 5 3 软件系统需求及可行性分析 …