news 2026/9/28 5:51:14

基于JavaWeb的仓库管理系统:十三大模块与部署实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于JavaWeb的仓库管理系统:十三大模块与部署实战解析

简介:一套基于JavaWeb的仓库管理系统完整项目包,面向Java初学者、系统分析与设计课程学员及企业信息化实践者。系统采用B/S架构,基于Servlet、JSP、JDBC与HTML/CSS/JS实现,并结合人工智能思路支持库存预警与智能预测,覆盖登录注册、商品管理、库存管理、入库出库、订单报表、权限管理等十三个功能模块,可作为毕业设计或课程项目的直接参考。资源共367个文件,压缩包约9.61MB,以95个Java源文件为核心,并包含HTML页面49个、JavaScript脚本42个、CSS样式9个、SQL脚本以及配置类文件,前端资源与后端代码分离,结构清晰。已有151人学习浏览,适合需要快速搭建Web仓库管理系统或学习JavaWeb完整开发流程的读者。内容包含数据库脚本与全套源码,便于对照业务流程理解各模块实现,也支持在此基础上进行二次扩展与功能定制。

1. 为什么一套 JavaWeb 仓库管理系统值得顺手拆着用

仓库管理系统是 JavaWeb 课程设计和入职练手项目里出现频率最高的一类,但网上能下载到的版本大多只有登录、CRUD 和几张表,凑不够一个完整的业务闭环。这份《基于 JavaWeb 实现的仓库管理系统》把十三类功能模块打包在一个工程里,从登录注册、商品档案、供应商客户、出入库单、库存盘点,到订单关联、权限角色、数据备份和智能预测接口都有对应页面和后台逻辑,而不是只给一个壳子。系统基于 B/S 架构,前端用 LayUI 组织界面,后端走 Servlet + JSP + JDBC 的经典 JavaWeb 路线,数据库建议 MySQL 8.0。对正在做毕业设计、JavaWeb 课设,或者刚进公司想快速理解进销存业务流转的人来说,这套代码能直接跑起来改着用,比从零搭框架省掉一大半时间。

2. 拆解十三模块:从页面骨架到权限控制

2.1 前端资源与整体布局

拿到 zip 包先看目录结构,压缩包里很干净,典型的 JavaWeb 工程目录。

src/main/java src/main/webapp src/main/resources

webapp 目录下集中了前端资源,其中 static 或 js/css 目录里能看到系统用到的前端框架文件。

layui.css public.css layer.css index.css layui.mobile.css laydate.css dtree.css dtreefont.css code.css

LayUI 是这套系统的界面基础,后台管理类页面用它非常合适。laydate 负责日期控件,出入库单据的入库日期、出库日期都靠它;dtree 用于树形菜单,商品分类、部门结构这类层级数据用它渲染;layer 是弹层组件,提示、确认框、新增编辑弹窗都是它。公共样式 public.css 和 index.css 里存放的是自定义样式,如果觉得页面不好看,改这两个文件即可,不需要动 LayUI 源码。

页面层面,系统的主框架是一个典型的后台布局,左侧导航树,顶部顶栏,右侧内容区域。这部分的 index.jsp 是整个系统的容器页,登录成功后会跳转到这里,后续所有功能页面都通过 iframe 方式嵌到内容区。

2.2 十三个模块的完整职能划分

公共模块包含四个,注册登录模块负责用户身份认证,所有业务页面必须经过登录拦截,权限管理模块给不同角色分配不同菜单与按钮权限,系统设置模块处理基础参数、日志和备份,数据备份单独拆分出来,说明设计者意识到仓库系统的数据敏感性。核心业务模块占大头,商品管理维护商品档案,包括编码、名称、规格、供应商、条形码、单位、预警上下限,库存管理实时展示各商品当前库存数量,库存盘点支持按仓库或按商品触发盘点单,盘点差异自动生成盈亏记录,入库管理登记采购入库或退货入库,出库管理处理销售出库与领料出库,订单管理把客户订单和出入库单关联起来,客户和供应商两个模块虽然功能和页面相似,但数据是两套,不能合并,报表统计模块提供出入库流水、库存台账、周转率等汇总数据。第十三个模块是智能预测模块,代码里采用的不是真正的 AI 模型训练,而是基于历史出库记录的规则引擎,用移动平均值和阈值判断做需求预测,这个模块放在后端的 service 层,有专门接口可以调用。

2.3 导航菜单与顶栏的联动逻辑

菜单生成不是写死在 HTML 里的,而是动态从权限表读取,再递归渲染成 dtree 结构。常见做法是登录后把用户角色 ID 传给菜单接口,后端返回该角色可见的菜单列表,前端再构建树形数据。菜单表有三个关键字段,menu_id 主键,parent_id 父级 ID,menu_url 对应功能页面的访问路径。

菜单构造时最容易踩的坑是层级关系处理,dtree 要求数据必须有父子关联,后端返回时直接把 parent_id 拼到 data 里就行,前端不要试图自己解析路径。

iframe 的内容区加载则是一个通用的页面载入函数,菜单点击后拿到 menu_url,拼上 contextPath 之后塞进 iframe 的 src。

顶栏右侧一般放当前登录用户、退出按钮、修改密码入口,点击退出时先调后端销毁 session,再跳回登录页,这个流程在 LoginServlet 里处理。注意退出接口和登录拦截器的路径匹配关系,通常需要在过滤器里放行登录页、登录接口、静态资源三种类型的请求,否则会出现登录页上不显示 CSS 样式的问题。

3. 环境搭建与 IDEA 运行配置:拿到压缩包后怎么跑起来

3.1 War 包部署还是源码导入

仓库系统的源码目录里如果存在 pom.xml,那它就是 Maven 工程,需要按 Maven 项目导入;如果只有 src 和 WebContent,那它就是普通 Web 工程,IDEA 导入时需要配置 Web 方面。下载到的 zip 包里常见的情况是 Maven 工程,根目录有 pom.xml、mvnw、mvnw.cmd 这类文件。

mvnw.cmd 是 Maven Wrapper 的 Windows 运行脚本,这个文件的存在说明项目作者用了 Maven Wrapper 来固定 Maven 版本。IDEA 中不需要直接双击这个文件,正常导入 Maven 工程后,IDEA 会调用本机的 Maven 配置,但如果本机 Maven 版本和项目要求不一致,构建时会出现依赖解析异常。

数据库脚本一般在 sql 或 database 目录下,执行时先看脚本头部注释里的库名和字符集,常见的是 utf8mb4。MySQL 8.0 执行脚本时要注意 JDBC 连接串里的驱动类名是 com.mysql.cj.jdbc.Driver,同时需要加上 serverTimezone 参数,否则连接会报 CST 时区错误。

3.2 Tomcat 配置与启动顺序

运行前需要确认三个地方。第一是项目输出路径,IDEA 里默认输出到 target 目录,可以用 war exploded 方式部署,这样改完 Java 代码重新编译后,浏览器刷新就能生效,不用每次重启 Tomcat。第二是 JDK 版本,老项目经常用 JDK 8 编译,如果本地装的是 JDK 11 以上,某些老版本依赖会报错。第三是 Artifact 名称,建议把 Application context 设置为空或者 /,这样访问时不需要额外带上下文路径。

Tomcat 端口默认是 8080,如果本机端口被占用,改 Server 配置里的 HTTP port 即可,同时要把右上角的 URL 地址改成新端口,否则 IDEA 启动完成后自动打开的浏览器仍然访问旧端口,容易误判为启动失败。

启动完成后,浏览器输入 http://localhost:8080,系统会自动跳转到登录页面。如果出现 404 或白屏,先看 IDEA 控制台有没有报 Servlet 初始化异常,再看数据库连接是否成功。

3.3 数据库账号密码和配置文件匹配

数据库连接信息通常在 src/main/resources 下的 db.properties 或 jdbc.properties 里。

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/warehouse?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456

改这里的密码时,注意数据库里的用户表初始数据密码也需要一致,否则登录时认证不过。很多系统的登录密码字段存的是 MD5 值,比如 admin 用户的密码在数据库里是 21232f297a57a5a743894a0e4a801fc3,这是 admin 的 MD5,不是明文。如果需要新增测试账号,直接用在线 MD5 工具生成对应密码的密文再插入用户表即可。

4. 核心业务实现:入库、出库、库存预警的完整链路

4.1 商品表设计与库存字段的取舍

商品表是整个系统的核心,字段设计直接决定后续代码的复杂度。

CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, product_code VARCHAR(50) NOT NULL UNIQUE, product_name VARCHAR(100) NOT NULL, category_id INT, supplier_id INT, unit VARCHAR(20), spec VARCHAR(100), stock INT DEFAULT 0, min_stock INT DEFAULT 10, max_stock INT DEFAULT 1000, price DECIMAL(10,2), status TINYINT DEFAULT 1, create_time DATETIME );

库存字段 stock 直接存在商品表里是一个常见做法,适合中小型仓库系统,查询库存时不需要 JOIN 多张流水表。但代价是每次入库出库必须在一个事务里同时更新商品表的 stock 字段和流水表,否则数据不一致。

min_stock 和 max_stock 是库存预警的边界值,预警判断不需要实时扫描,而是在每次出入库提交后检查一次当前库存是否越界。

4.2 入库单生成的代码路径

入库操作分为两步,第一步是保存入库单主表信息,第二步是循环保存入库单明细,同时更新商品库存。后端 Servlet 接收前端提交的 JSON 数据,核心逻辑如下。

public boolean doInbound(HttpServletRequest request) { // 解析入库单主表参数 String inboundCode = "RK" + System.currentTimeMillis(); Integer supplierId = Integer.valueOf(request.getParameter("supplierId")); String operator = (String) request.getSession().getAttribute("username"); // 解析商品明细 JSON JSONArray items = JSON.parseArray(request.getParameter("items")); Connection conn = null; try { conn = DBHelper.getConnection(); conn.setAutoCommit(false); // 插入入库单主表 String sqlMain = "INSERT INTO inbound (inbound_code, supplier_id, operator, create_time) VALUES (?, ?, ?, NOW())"; PreparedStatement psMain = conn.prepareStatement(sqlMain, Statement.RETURN_GENERATED_KEYS); psMain.setString(1, inboundCode); psMain.setInt(2, supplierId); psMain.setString(3, operator); psMain.executeUpdate(); ResultSet rs = psMain.getGeneratedKeys(); int inboundId = 0; if (rs.next()) { inboundId = rs.getInt(1); } // 循环插入明细并更新库存 for (int i = 0; i < items.size(); i++) { JSONObject item = items.getJSONObject(i); int productId = item.getIntValue("productId"); int quantity = item.getIntValue("quantity"); BigDecimal price = item.getBigDecimal("price"); String sqlDetail = "INSERT INTO inbound_item (inbound_id, product_id, quantity, price) VALUES (?, ?, ?, ?)"; PreparedStatement psDetail = conn.prepareStatement(sqlDetail); psDetail.setInt(1, inboundId); psDetail.setInt(2, productId); psDetail.setInt(3, quantity); psDetail.setBigDecimal(4, price); psDetail.executeUpdate(); String sqlUpdate = "UPDATE product SET stock = stock + ? WHERE id = ?"; PreparedStatement psUpdate = conn.prepareStatement(sqlUpdate); psUpdate.setInt(1, quantity); psUpdate.setInt(2, productId); psUpdate.executeUpdate(); } conn.commit(); return true; } catch (Exception e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { DBHelper.closeConnection(conn); } }

这里最关键的逻辑是必须手动开启事务,setAutoCommit(false) 之后,主表、明细、库存更新任何一步失败,全部回滚。实际开发中常见错误是明细循环里某条数据插入失败,但前面几条已经提交了,导致入库单数量与库存增加量对不上。解决的办法就是统一事务,并且注意库存更新语句用的是 stock = stock + ?,而不是直接赋值一个绝对数量,这样并发操作时不容易出现覆盖问题。

前端入库页面提交时会传商品 ID 和数量数组,如果入库单里重复加同一商品,建议先在前端做合并,否则明细表里会出现两条相同商品数据。合并逻辑简单,遍历时检查数组中是否已经有相同 productId,有就在原数量上加。

4.3 出库扣减库存的锁问题

出库逻辑和入库类似,伪代码不重复,但有一个额外的坑:扣减库存时必须判断库存是否充足。

String sqlCheck = "SELECT stock FROM product WHERE id = ? FOR UPDATE";

查询库存时加上 FOR UPDATE,把这一行锁住,然后再判断库存是否大于等于出库数量。如果不用行锁,两个用户同时出库同一商品,都可能通过库存判断,最后导致库存变成负数。

尝试下面这个场景:商品库存是 5 件,两个出库单各出 4 件。不加锁的情况下,两个请求都读到库存 5,都判断 5 >= 4,然后都执行 stock = stock - 4,最后库存变成 -3。加了 FOR UPDATE 之后,第二个请求会等待第一个请求提交事务后才读到新的库存值 1,然后判断 1 >= 4 失败,单据被拒绝。

4.4 库存预警轮询与阈值判断

预警模块可以做成 Servlet 定时任务,也可以做成用户点击时实时查询。比较实用的是在出库和入库的后置逻辑里直接调用检查方法。

public List<Product> checkStockWarning() { String sql = "SELECT * FROM product WHERE stock <= min_stock OR stock >= max_stock AND status = 1"; // 执行查询并返回结果 }

预警列表生成后,前端 Dashboard 页面会在商品数量低于下限时用红色标记,高于上限时用黄色标记。设置 min_stock 和 max_stock 时要根据实际业务来定,食品类有保质期的商品建议设置下限偏高,避免备货不足;五金配件类体积大、占用仓库位置多的,设置上限偏低,防止库存堆积。

5. 报表统计与智能预测:纯 SQL 时代的数据分析

5.1 出入库统计报表的核心汇总语句

报表模块在 JavaWeb 项目里通常不需要引入大数据组件,几条聚合 SQL 就能满足绝大多数场景。

SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS stat_date, SUM(CASE WHEN operation_type = 'IN' THEN quantity ELSE 0 END) AS inbound_qty, SUM(CASE WHEN operation_type = 'OUT' THEN quantity ELSE 0 END) AS outbound_qty FROM inventory_log WHERE create_time >= ? AND create_time <= ? GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') ORDER BY stat_date;

这条 SQL 用了条件聚合,一行数据同时算出当天的入库总量和出库总量。要注意日期范围参数的类型,前端传过来的是字符串日期,如果带时分秒,比如 2025-06-01 12:00:00,那么查询 6 月 1 日当天数据时要在 SQL 里用 DATE_FORMAT 对 create_time 做格式化后再比较,或者在传参时截断到当天零点。

库存周转率报表稍微复杂一些,需要一段子查询先算出某段时间的平均库存,再拿出库总量除以平均库存。

SELECT p.product_name, SUM(il.quantity) / ((AVG(p.stock) + MAX(p.stock)) / 2) AS turnover_rate FROM inventory_log il JOIN product p ON il.product_id = p.id WHERE il.operation_type = 'OUT' AND il.create_time BETWEEN ? AND ? GROUP BY p.product_name

5.2 智能预测模块的规则引擎实现

摘要里提到人工智能,压缩包内代码层面的实现更多是轻量级的规则判断。常见做法是把预测逻辑做成一个独立的 service 类,输入某商品过去 30 天的出库明细,输出未来 7 天的预测需求量。

public int predictDemand(List<Integer> dailyOutboundList) { // 如果数据样本不足,返回默认值 if (dailyOutboundList == null || dailyOutboundList.size() < 7) { return 10; } int sum = 0; int len = dailyOutboundList.size(); List<Integer> recent = dailyOutboundList.subList(Math.max(0, len - 7), len); for (int val : recent) { sum += val; } int avg = sum / recent.size(); int max = Collections.max(recent); // 简单阈值规则:如果最近一周的最大单日出库量超过平均值 2 倍,说明有异常波动 if (max > avg * 2) { return (int)(avg * 1.5 + max * 0.5); } return (int)(avg * 1.2); }

这套模式对应的是库存管理里的移动平均预测法,准确率肯定不如真正的机器学习模型,但对于 JavaWeb 课设和中小企业的仓库管理系统来说,意义在于证明系统具备“预测”能力,且不需要安装任何 PyTorch 或 TensorFlow 环境。如果后续要升级成真实 AI 预测,可以在不影响现有代码的情况下把 predictDemand 方法的实现替换成训练好的模型接口。

6. 避坑记录:七个真实的菜鸟踩坑现场

6.1 UTF-8 乱码与 Tomcat 编码配置

现象:页面上的中文商品名称显示成问号。 原因:Tomcat 8 及以上版本默认 URI 编码不是 UTF-8,而 IDEA 控制台的输出编码默认 GBK,两层叠加后导致 GET 请求参数里的中文乱码。 解决:在 server.xml 的 Connector 配置里加一行 URIEncoding="UTF-8",同时确认项目的 web.xml 里有 CharacterEncodingFilter,过滤范围覆盖 /*。

6.2 IDEA 运行 javaweb 项目配置时报错:404 找不到页面

现象:Tomcat 正常启动,但浏览器打开 localhost:8080 显示 404。 原因:Artifact 部署名称和浏览器访问路径不一致,最常见的是 Application context 设置成了 /warehouse_war_exploded,导致访问时要带一长串前缀。 解决:打开 Run Configuration,在 Deployment 选项卡里把 Application context 改为 / 或者统一改成项目名,之后 IDEA 自动打开的地址和实际部署路径一致。

6.3 mysql8.0 zip 包方式安装后连接失败

现象:代码里 JDBC 连接数据库抛出 ClassNotFoundException 或 Communications link failure。 原因:MySQL 8.0 的驱动类名从 com.mysql.jdbc.Driver 改成了 com.mysql.cj.jdbc.Driver,且连接串要求显式指定时区。 解决:在 pom.xml 或 lib 目录确认使用 mysql-connector-java 8.x 版本,配置文件按前面 3.3 节的写法改 connection URL。

6.4 zip 伪加密导致压缩包解压报错

现象:下载下来的 zip 解压时提示文件损坏,但文件列表能看到完整目录。 原因:部分压缩包被人为标记为“伪加密”,解压软件读加密标志位后提示需要密码,其实文件本体没有真正加密。 解决:用 7-Zip 打开压缩包,右键测试压缩档,如果提示错误位置在加密标志位而不是数据区,用 7-Zip 重新解压一次即可,不需要任何密码工具。

6.5 库存流水正确但商品库存表对不上

现象:出入库单正常,但商品表里的库存总数和所有流水明细加减后的结果不一致。 原因:代码里更新库存的 SQL 用了 stock = ?,直接把查询到的旧库存加数量后回写,如果两次操作之间没有加锁,后一次覆盖了前一次的写入。 解决:统一改成 stock = stock + ? 风格的自增减语句,并配合事务与行锁。

6.6 前端权限按钮没显示但后端接口未拦截

现象:用户角色没有删除权限,菜单里看不到删除按钮,但用 Postman 直接调用删除接口能成功删除。 原因:权限仅做了前端展示控制,后端 Servlet 未校验角色。 解决:给每个写操作 Servlet 增加角色判断,参考代码里已有的 AuthFilter,把所有写操作的 URL 纳入过滤器管理,登出时清理 session 中保存的角色信息。

6.7 LayUI 弹窗内容不刷新

现象:新增商品弹窗提交后,列表页依然是旧数据,需要手动刷新浏览器。 原因:新增弹窗用 layer.open 打开,提交数据之后列表页 iframe 没有重新加载。 解决:在父页面回调里调用子 iframe 的刷新函数,或者用 window.parent.layui.table.reload。

7. 上线前必做的四件小事:顺手把系统收尾得更完整

7.1 手工造一批真实业务数据验证报表

登录后台后依次录入 10 个商品、3 个供应商、2 个客户,然后做两笔入库、一笔出库、一笔退货,再去报表模块看统计数据是否自洽。如果发现出入库汇总对不上,多半是 SQL 里日期范围条件写错了。

7.2 把 Tomcat 根目录改成项目目录

生产部署时把 war 包解压后的目录重命名成 ROOT,替换 Tomcat 的 webapps/ROOT,这样浏览器不需要带项目名。如果必须保留项目名,记得把代码里所有请求路径都带上 contextPath,否则 CSS 和 JS 会全部 404。

7.3 数据库定时备份脚本

MySQL 环境下的另一种备份方式。

#!/bin/bash BACKUP_DIR=/backup/mysql DATE=$(date +%Y%m%d) mysqldump -uroot -p123456 warehouse > $BACKUP_DIR/warehouse_$DATE.sql find $BACKUP_DIR -name "*.sql" -mtime +30 -exec rm {} \;

这段脚本里,mysqldump 的 -p 参数后面不要留空格,密码直接拼接。find 那条命令用来清理 30 天前的旧备份文件。如果有条件,把备份目录挂到独立磁盘或远程存储,避免服务器硬盘故障时备份一起丢失。

7.4 用浏览器开发者工具检查接口返回

打开 F12 的网络面板,逐个点击入库、出库、预警、报表功能,看接口返回的 HTTP 状态码和 JSON 数据结构。这一步能找出很多隐藏问题,比如某个接口返回 500,控制台不报错但页面不显示数据。

这套系统整体按 JavaWeb 传统路线实现,没有引入重量级框架,维护成本低,适合先跑通再扩展。从那以后我每次拿到的课程设计压缩包都会先按这个流程走一遍,先解压看目录、再改数据库配置、最后核对前后端路径是否一致,已经成了一种习惯,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 5:50:46

Windows基础安全加固:从账户权限到日志审计的完整实践

我在“智榜样一阶段”的02课里学的是Windows操作系统基础安全&#xff0c;这门课没有讲怎么用电脑&#xff0c;也没有教怎么装系统&#xff0c;而是沉下来把Windows当作一个被攻击的目标来看。坦白讲&#xff0c;Windows大概是很多人接触的第一台电脑系统&#xff0c;日常使用太…

作者头像 李华
网站建设 2026/9/28 5:50:46

AI辅助MBA论文写作:9款实用工具搞定开题报告与文献综述

每次带MBA论文班&#xff0c;我都会遇到几乎一模一样的开场白&#xff1a;老师&#xff0c;开题报告不知道怎么写&#xff0c;文献综述更是看了三十篇论文还是一团乱麻。说实话&#xff0c;这个困境我很理解。MBA论文和普通学术论文完全是两种生物&#xff0c;它要求你用学术框…

作者头像 李华
网站建设 2026/9/28 5:50:46

OpenClaw+企微管家:企业微信自动化效率提升300%实战评测

前阵子我一直在给团队做企业微信侧的自动化&#xff0c;接触了不少方案&#xff0c;OpenClaw这个开源智能体框架算是让我眼前一亮。结合微盛企微管家提供的消息通道&#xff0c;我把它们拼成了一个叫“企微管家Claw”的自动化工具链&#xff0c;跑通之后&#xff0c;原本每天要…

作者头像 李华
网站建设 2026/9/28 5:50:22

图推提示图:用图结构推理构建可视化提示词路径

1. 项目起源&#xff1a;先说我为什么要做图推提示图先说结论&#xff1a;我最近做了个小项目&#xff0c;名字叫图推提示图&#xff0c;核心功能不是做一张静态提示卡&#xff0c;而是用图结构推理的方式&#xff0c;把提示词工程里最核心的“下一步该怎么走”自动画成一棵提示…

作者头像 李华
网站建设 2026/9/28 5:49:47

玩转EC11编码器:AB相相位差与双平台读取实战

先别急着看代码&#xff0c;玩转EC11编码器&#xff0c;搞清楚AB相那点“相位差”的事儿比什么都重要。无论是在Arduino上写个旋转菜单&#xff0c;还是在STM32上搞个精准的电机调速旋钮&#xff0c;EC11都是最常用、最皮实、也最容易被忽视细节的器件。很多人直接把A、B两脚接…

作者头像 李华
网站建设 2026/9/28 5:48:29

STM32G431工业CAN节点设计:FreeRTOS+Flash高可靠实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华