Springboot 整合Flowable工作流框架搭建

news2025/7/19 16:52:10

我们在开发自动化办公软件时经常会遇到各种审批流程功能,这个使用就需要使用到工作流引擎。目前主流的工作流引擎有ActivitiFlowablecamunda,其中Flowable是在Activiti的基础上开发出来的,基于BPMN2.0协议,它包括 BPMN(Business Process Model and Notation)引擎、CMMN(Case Management Model and Notation)引擎、DMN(Decision Model and Notation)引擎、表单引擎(Form Engine)等模块。

这里我们基于springboot和PostgreSQL数据库,介绍一下如何搭建一个基础的Flowable工作流框架。

创建项目,添加依赖

  1. 下图是我在线创建的springboot项目,只添加了Spring Web和Postgre SQL Driver两个依赖。
    创建项目
  2. 添加Flowable依赖
<dependency>
	<groupId>org.flowable</groupId>
	<artifactId>flowable-spring-boot-starter</artifactId>
	<version>6.7.2</version>
</dependency>
  1. 配置数据库链接

    这里要解释一下,网上有的博文说,添加完数据库,启动服务,就会在数据库里自动创建一堆表,本人测试需要创建bpmn20.xml文件后,数据库才会自动生成对应的表。

spring:
  datasource:
    username: postgres
    password: postgres
    url: jdbc:postgresql://192.168.2.172/test

到这里已经完成了项目的基础配置。

bpmn20.xml文件制作

在整个工作流框架中,比较核心的是bpmn20.xml文件的制作,如果之前没有一定的基础,制作整个文件还是有点难度的。idea的插件市场里有一个Flowable BPMN visualizer插件,可以在idea中直接用来制作bpmn20.xml文件。
插件安装
安装插件后,在resources文件夹下创建processes文件夹,流程文件默认放在此文件夹下。idea中右键创建文件,选择BPMN 2.0 file
创建文件
注意文件名要以**.bpmn20.xml**结尾。在创建的文件上,右键选择View BPMN。
编辑流程文件
在弹出的编辑器中,即可进行相关的绘制操作。新手,注意,在编辑器上右键可以创建节点。
流程图
网上也有一些其他工具可以用来绘制的,都可以尝试。我的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: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="ask_for_leave" name="ask_for_leave" isExecutable="true">
    <startEvent id="sid-8f75290b-6d4f-4304-98e3-592be1f19a4e"/>
    <endEvent id="sid-4d3f3115-90f9-4cad-a8d3-df415f8aaa9d"/>
    <userTask id="sid-ca4fe38f-af1d-47ad-8646-d045a82fcb6a" name="请假"/>
    <userTask id="sid-7b7c9585-5913-49fe-910f-fea79e53518c" name="组长审批" flowable:assignee="${taskUser}"/>
    <userTask id="sid-627ef23c-3f54-4536-bda9-bbf0d3243230" name="经理审批" flowable:assignee="${taskUser}"/>
    <exclusiveGateway id="sid-becd3cfd-2ede-4df6-8916-83d9e4b2eb5f" />
    <sequenceFlow id="sid-b1bdf216-3433-4052-ae80-ad753e16aed7" sourceRef="sid-becd3cfd-2ede-4df6-8916-83d9e4b2eb5f" targetRef="sid-627ef23c-3f54-4536-bda9-bbf0d3243230" name="通过">
      <conditionExpression xsi:type="tFormalExpression">${finishFlag==\"YES\"}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-0b327947-1e7f-48c3-8047-e8afc3d15005" sourceRef="sid-8f75290b-6d4f-4304-98e3-592be1f19a4e" targetRef="sid-ca4fe38f-af1d-47ad-8646-d045a82fcb6a"/>
    <sequenceFlow id="sid-e5703760-35e2-497f-9501-7b616d5c1d02" sourceRef="sid-ca4fe38f-af1d-47ad-8646-d045a82fcb6a" targetRef="sid-7b7c9585-5913-49fe-910f-fea79e53518c"/>
    <sequenceFlow id="sid-17b98be9-ef29-48a8-8cf6-cfde4be9d7c9" sourceRef="sid-7b7c9585-5913-49fe-910f-fea79e53518c" targetRef="sid-becd3cfd-2ede-4df6-8916-83d9e4b2eb5f"/>
    <serviceTask id="sid-ddda9b4d-414e-40d0-aaa1-1a8bbac4d49a" flowable:exclusive="true" name="发送失败消息" flowable:class="com.work.flow.listener.TestServiceListener"/>
    <sequenceFlow id="sid-7f8d7cc2-ddd0-4cb4-9ce6-81e8b42f4717" sourceRef="sid-becd3cfd-2ede-4df6-8916-83d9e4b2eb5f" targetRef="sid-ddda9b4d-414e-40d0-aaa1-1a8bbac4d49a" name="不通过">
      <conditionExpression xsi:type="tFormalExpression">${finishFlag==\"NO\"}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-95ed6745-c15f-4489-a9a5-93f4aeea1f43" sourceRef="sid-ddda9b4d-414e-40d0-aaa1-1a8bbac4d49a" targetRef="sid-4d3f3115-90f9-4cad-a8d3-df415f8aaa9d"/>
    <exclusiveGateway id="sid-03e9886b-d80c-4a5a-8916-76f2b6683727" />
    <sequenceFlow id="sid-e8f7bf08-9b5b-4f0b-b0aa-c1a74d42e870" sourceRef="sid-03e9886b-d80c-4a5a-8916-76f2b6683727" targetRef="sid-ddda9b4d-414e-40d0-aaa1-1a8bbac4d49a" name="不通过">
      <conditionExpression xsi:type="tFormalExpression">${finishFlag==\"NO\"}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-fc8f3bf6-d817-4d6b-9dbd-f15d11032fae" sourceRef="sid-627ef23c-3f54-4536-bda9-bbf0d3243230" targetRef="sid-03e9886b-d80c-4a5a-8916-76f2b6683727"/>
    <endEvent id="sid-f7bc1956-c2b1-4036-99fa-0318f6da4221"/>
    <sequenceFlow id="sid-2a97e964-4e84-4936-9356-14b7b7b6ecb4" sourceRef="sid-03e9886b-d80c-4a5a-8916-76f2b6683727" targetRef="sid-f7bc1956-c2b1-4036-99fa-0318f6da4221" name="通过">
      <conditionExpression xsi:type="tFormalExpression">${finishFlag==\"YES\"}</conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_ask_for_leave">
    <bpmndi:BPMNPlane bpmnElement="ask_for_leave" id="BPMNPlane_ask_for_leave">
      <bpmndi:BPMNShape id="shape-44c54737-7c91-4844-8210-7db7f6853bb6" bpmnElement="sid-8f75290b-6d4f-4304-98e3-592be1f19a4e">
        <omgdc:Bounds x="-245.0" y="-65.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-12f82f19-3220-442d-893b-b139db4834e4" bpmnElement="sid-4d3f3115-90f9-4cad-a8d3-df415f8aaa9d">
        <omgdc:Bounds x="-155.0" y="15.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-146cee26-7a5b-4da7-9bbb-6c392e645b94" bpmnElement="sid-ca4fe38f-af1d-47ad-8646-d045a82fcb6a">
        <omgdc:Bounds x="-180.0" y="-75.0" width="55.0" height="50.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-bd5bc59b-ab57-4e3c-8dfa-cb2827d9a331" bpmnElement="sid-7b7c9585-5913-49fe-910f-fea79e53518c">
        <omgdc:Bounds x="-75.0" y="-80.0" width="80.0" height="60.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-cc2b2bce-04fd-4641-ae8b-419d9f79c2b0" bpmnElement="sid-627ef23c-3f54-4536-bda9-bbf0d3243230">
        <omgdc:Bounds x="185.0" y="-97.5" width="100.0" height="55.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-43f90d29-b7d6-4a74-8acb-1fc7f3f29289" bpmnElement="sid-becd3cfd-2ede-4df6-8916-83d9e4b2eb5f">
        <omgdc:Bounds x="55.0" y="-80.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-9eca388f-9baf-4a0b-8201-892c738b6ad0" bpmnElement="sid-b1bdf216-3433-4052-ae80-ad753e16aed7">
        <omgdi:waypoint x="95.0" y="-60.0"/>
        <omgdi:waypoint x="185.0" y="-70.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-a5e04944-60fb-4696-ad16-c8092d6c0bb5" bpmnElement="sid-0b327947-1e7f-48c3-8047-e8afc3d15005">
        <omgdi:waypoint x="-215.0" y="-50.0"/>
        <omgdi:waypoint x="-180.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-6fb6a4c1-3b9a-4238-a66b-10f3d8571af1" bpmnElement="sid-e5703760-35e2-497f-9501-7b616d5c1d02">
        <omgdi:waypoint x="-125.0" y="-50.0"/>
        <omgdi:waypoint x="-75.0" y="-50.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-e23fcf4e-1309-4639-a40e-9f0fe3fb42b7" bpmnElement="sid-17b98be9-ef29-48a8-8cf6-cfde4be9d7c9">
        <omgdi:waypoint x="5.0" y="-50.0"/>
        <omgdi:waypoint x="55.0" y="-60.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-d6c43ede-95db-49fe-a468-0c8678deff6f" bpmnElement="sid-ddda9b4d-414e-40d0-aaa1-1a8bbac4d49a">
        <omgdc:Bounds x="-20.0" y="7.5" width="110.0" height="75.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-80b91b72-184a-4add-878d-a0553d8fdd04" bpmnElement="sid-7f8d7cc2-ddd0-4cb4-9ce6-81e8b42f4717">
        <omgdi:waypoint x="75.0" y="-40.0"/>
        <omgdi:waypoint x="62.5" y="7.5"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-92accc38-c426-45f8-bf82-4ab1d8b6625b" bpmnElement="sid-95ed6745-c15f-4489-a9a5-93f4aeea1f43">
        <omgdi:waypoint x="-20.0" y="45.0"/>
        <omgdi:waypoint x="-125.0" y="30.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-aed10d1f-11b7-4c70-91ca-63514182f93f" bpmnElement="sid-03e9886b-d80c-4a5a-8916-76f2b6683727">
        <omgdc:Bounds x="225.0" y="15.0" width="40.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-1a9651dc-930e-4642-b2f3-4ba0b47fe97d" bpmnElement="sid-e8f7bf08-9b5b-4f0b-b0aa-c1a74d42e870">
        <omgdi:waypoint x="225.0" y="35.0"/>
        <omgdi:waypoint x="90.0" y="45.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-5e55bb58-f07a-45d5-bb8a-8c13a6c0b9dd" bpmnElement="sid-fc8f3bf6-d817-4d6b-9dbd-f15d11032fae">
        <omgdi:waypoint x="260.0" y="-42.5"/>
        <omgdi:waypoint x="245.0" y="15.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-12be209b-f581-4e87-b480-41c60acb8d8e" bpmnElement="sid-f7bc1956-c2b1-4036-99fa-0318f6da4221">
        <omgdc:Bounds x="255.0" y="100.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-7aba8c58-63e5-4c57-b3d7-679ad58b6388" bpmnElement="sid-2a97e964-4e84-4936-9356-14b7b7b6ecb4">
        <omgdi:waypoint x="245.0" y="55.0"/>
        <omgdi:waypoint x="270.0" y="100.0"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

接口开发

  1. 防止流程图乱码配置

    package com.work.flow.config;
    
    import org.flowable.spring.SpringProcessEngineConfiguration;
    import org.flowable.spring.boot.EngineConfigurationConfigurer;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
    
    
        @Override
        public void configure(SpringProcessEngineConfiguration engineConfiguration) {
            engineConfiguration.setActivityFontName("宋体");
            engineConfiguration.setLabelFontName("宋体");
            engineConfiguration.setAnnotationFontName("宋体");
        }
    }
    
    
  2. 发起流程接口

    package com.work.flow.controller;
    
    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.PostMapping;
    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;
    
    @RestController
    public class TestController {
        @Autowired
        RuntimeService runtimeService;
    
        @Autowired
        TaskService taskService;
    
        @Autowired
        RepositoryService repositoryService;
    
        @Autowired
        ProcessEngine processEngine;
    
        @GetMapping( "/add")
        public String addExpense(@RequestParam String userId) {
            //启动流程
            HashMap<String, Object> map = new HashMap<>(4);
    
            //name="客服代表" flowable:candidateGroups="${customerServiceId}"
    
            map.put("userId", userId);
    
            //<process id="adviceApply" name="投诉建议" isExecutable="true">
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("ask_for_leave", map);
            return "提交成功.流程Id为:" + processInstance.getId();
        }
    }
    
    

    发起申请

  3. 获取流程图接口

@GetMapping("/pic")
    public void showPic(HttpServletResponse resp, @RequestParam String processId) throws Exception {
        System.out.println("收到请求"+processId);
        List<ProcessInstance> instances  =runtimeService.createProcessInstanceQuery().list();
        for (ProcessInstance instance : instances) {
            System.out.println(instance.getId());
        }

        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (pi == null) {
            return;
        }
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(processId)
                .list();

        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 = resp.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
            System.out.println("运行结束");
        }
    }

测试流程图

  1. 流程推进接口
/**
     * 流转
     *
     * @param processId 流程id
     */
    @GetMapping("/apply")
    public String apply(@RequestParam String processId,@RequestParam String userId,@RequestParam boolean isPass) {
        //查询当前办理人的任务ID
        Task task = taskService.createTaskQuery()
                //使用流程实例ID
                .processInstanceId(processId)
                //任务办理人
                .singleResult();
        if (task == null) {
            throw new RuntimeException("流程不存在");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("taskUser", userId);
        String pass = "NO";
        if(isPass){
            pass = "YES";
        }
        map.put("finishFlag", pass);
        taskService.complete(task.getId(), map);
        return "processed ok!";
    }

推进流程
走流程后,再次查看流程图
新流程图

总结

  1. 总体bpmn20.xml的配置比较麻烦,需要一定的专业知识
  2. 上手还是有一定的难度的

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

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

相关文章

Convolutional Neural Networks for Sentence Classification

摘要 We report on a series of experiments with convolutional neural networks (CNN) trained on top of pre-trained word vectors for sentence-level classification tasks. We show that a simple CNN with little hyperparameter tuning and static vectors achieves e…

安装ROS+ROS命令行工具的使用

1.添加ROS软件源 $ sudosh -c echo "deb http;/packages.ros.org/ros/ubuntu $(sb_release -sc) main">/etc/apt/sources.list.d/ros-latest.list 2.添加密钥 $ sudo apt-key adv --keyserver hkp:/ keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B17…

我希望在 26 岁时知道的 36 岁时知道的职业作弊代码

当您开始抓住您不具备资格的机会时&#xff0c;您的职业生涯就会发展。我像关在笼子里的狮子一样被困在金融事业中。然后通过一系列离奇的事件&#xff0c;我发明了自己的工作。这使我获得了难得的机会&#xff0c;并建立了最终取代我工作的在线业务。这些作弊码让一些人称之为…

I/O 多路复用:select/poll/epoll

url&#xff1a;9.2 I/O 多路复用&#xff1a;select/poll/epoll | 小林coding &#xff08;仅供自己学习使用&#xff09; 为什么要使用I/O多路复用技术 TCP Socket 调用流程是最简单、最基本的&#xff0c;它基本只能一对一通信&#xff0c;因为使用的是同步阻塞的方…

Linux作为主力机--Manjaro 22.0.4

1、对操作系统的看法 个人是做软件开发的&#xff0c;已经使用Manjaro作为主力机两年多了&#xff0c;真的是特别喜欢这个操作系统。经过两年的打磨&#xff0c;个人16年的惠普老电脑加上这个Manjaro 22.0.4操作系统完全可以再战五年&#xff0c;完全满足日常的办公使用&#…

ctf pwn基础-3

学习pwn的第三天&#xff0c;今天是ret2text。 目录 基础 实例讲解 实例讲解2 基础 ret2text就是ROP中最简单的&#xff0c;然后的意思就是我们利用栈溢出&#xff0c;来修改eip的值&#xff0c;让他输出的时候&#xff0c;输出我们想要执行的本身已有的代码&#xff0c;通常…

利用Splunk构建SOC-SOC建设漫谈及splunk的角色

零、免喷符 SOC部门小菜鸟一枚&#xff0c;此乃自闭学安全的笔记记录&#xff0c;行文潦草&#xff0c;随性笔记。 通过上一篇的勒索病毒案例&#xff0c;已经了解到Splunk的强大之处。Splunk那么死贵&#xff0c;他的角色是怎样的&#xff0c;又是怎么和安全及SOC联系起来的…

基于BP神经网络的性别识别,BP神经网络详细原理,自编码神经网络代码,神经网络案例之18

目标 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的基本结构 BP神经网络的神经元 BP神经网络的激活函数&#xff0c; BP神经网络的传递函数 数据 神经网络参数 基于BP神经网络 性别识别的MATLAB代码 效果图 结果分析 展望 背影 男人体内蛋白质比例大&#xff0c;女生…

Windows下 IDEA编译调试 hive2.3.9

Windows下 IDEA编译调试 hive2.3.9 环境 IDEA 2021.2 JDK1.8&#xff08;试过用高版本的JDK17编译&#xff0c;不兼容编译不过&#xff09; 一个Hadoop集群&#xff0c;涉及配置文件core-site.xml&#xff0c;hdfs-site.xml&#xff0c;yarn-site.xml&#xff0c;mapred-sit…

并发与多线程

目录 第一节 并发基本概念及实现&#xff0c;进程&#xff0c;线程基本概念 &#xff08;1&#xff09;并发&#xff0c;进程&#xff0c;线程的基本概念和综述 &#xff08;1.1&#xff09;并发 &#xff08;1.2&#xff09;可执行程序 &#xff08;1.3&#xff09;进程 …

物理服务器与云服务器备份相同吗?

自从云计算兴起以来&#xff0c;服务器备份已经从两阶段的模拟操作演变为由云服务器备份软件执行的复杂的多个过程。但是支持物理服务器和虚拟服务器之间的备份相同吗?主要区别是什么?我们接下来将详细讨论这个问题。 物理服务器与云服务器备份的区别 如果您不熟悉虚拟服务器…

qt QCustomPlot学习

QCustomPlot 是一个基于Qt的画图和数据可视化C控件。QCustomPlot 致力于提供美观的界面&#xff0c;高质量的2D画图、图画和图表&#xff0c;同时为实时数据可视化应用提供良好的解决方案。 该绘图库专注于制作美观、出版物质量高的2D绘图、图形和图表&#xff0c;并为实时可视…

数据库专题

请简洁描述 MySQL 中 InnoDB 支持的四种事务隔离级别名称&#xff0c;以及逐级之间的区别&#xff1f; 默认隔离级别 mysql repeatable-read oracle read-committed 脏读&#xff1a;不可重复读&#xff1a;幻读&#xff1a; CHAR 和 VARCHAR 的区别&#xff1f;…

公众号运营之竞品分析,教你拆解公众号

知己知彼&#xff0c;百战不殆&#xff0c;公众号运营亦是如此。 当运营者只关注自己账号的时候&#xff0c;很容易陷入某个误区中出不来。这个时候就要拓宽我们的视野&#xff0c;多去看看“外面的世界”&#xff0c;不要只局限于自己的一片小天地中。 看看同领域优秀公众号…

stm32f407探索者开发板(二十二)——通用定时器基本原理讲解

文章目录一、三种定时器的区别二、通用定时器特点2.1 功能特点描述2.2 计数器模式三、通用定时器工作过程四、附一、三种定时器的区别 STM32F40x系列总共最多有14个定时器 三种&#xff08;4&#xff09;STM32定时器区别 二、通用定时器特点 2.1 功能特点描述 STM3 F4的通…

PHY设备驱动

1. 概述 MAC控制器的驱动使用的是platform总线的连接方式&#xff0c;PHY设备驱动是基于device、driver、bus的连接方式。 其驱动涉及如下几个重要部分&#xff1a; 总线 - sturct mii_bus (mii stand for media independent interface) 设备 - struct phy_device 驱动 - struc…

零日漏洞发展格局及防御策略

在过去的一年半中&#xff0c; 在野利用的零日漏洞数量持续飙升 &#xff0c;这些软件制造商尚不知晓的漏洞正在被国家行为体黑客组织和勒索软件团伙滥用。 今年上半年&#xff0c;Google Project Zero统计了近20个零日漏洞&#xff0c;其中 大部分针对微软、苹果和谷歌构建的…

【《C Primer Plus》读书笔记】第13章:文件输入/输出

【《C Primer Plus》读书笔记】第13章&#xff1a;文件输入/输出13.1 与文件进行通信13.1.1 文件是什么13.1.2 文本模式和二进制模式13.1.3 I/O的级别13.1.4 标准文件13.2 标准I/O13.3 一个简单的文件压缩程序13.4 文件I/O&#xff1a;fprintf()、fscanf()、fgets()和fputs()13…

【LVGL】学习笔记--(1)Keil中嵌入式系统移植LVGL

一 LVGL简介最近emwin用的比较烦躁&#xff0c;同时被LVGL酷炫的界面吸引到了&#xff0c;所以准备换用LVGL试试水。LVGL(轻量级和通用图形库)是一个免费和开源的图形库&#xff0c;它提供了创建嵌入式GUI所需的一切&#xff0c;具有易于使用的图形元素&#xff0c;美丽的视觉效…

极光笔记 | 埋点体系建设与实施方法论

PART 01 前 言随着网络技术的发展&#xff0c;从粗犷型到精细化运营型&#xff0c;再到现在的数字化运营&#xff0c;数据变得越来越细分和重要&#xff0c;不仅可以进行策略调整&#xff0c;还可以实现自动化的精细化运营。而数据价值的起点就是埋点&#xff0c;只有合理地埋点…