简介:智能仓储管理系统毕业设计/课程作业代码包,面向计算机、物联网及物流工程方向学生,重点展示基于RFID的货物识别、库存流转与嵌入式控制等核心问题,同时为结合人工智能技术做库存预测与路径优化留出扩展空间。压缩包共100个文件,以C/C++源码为主(23个h、17个c、7个cpp),辅以makefile、uvproj/uvopt等Keil工程配置,以及png图表、json配置和调试文件,整体约1.72MB;其中C/C++源文件覆盖底层驱动与业务逻辑,makefile与uvproj负责工程构建,png图片和json配置辅助理解界面与参数设置,结构便于直接导入工程学习。从代码预览可见,项目基于LPC1111(Cortex-M0)平台,包含RC522射频模块驱动、内核底层支持等关键代码,覆盖从RFID刷卡触发、货物信息读取到库存记录更新的完整闭环,并可延伸至入库管理、库存更新与出库调度逻辑。已有162人学习,适合需要掌握RFID仓储系统实现细节、嵌入式软硬件联调思路,或希望快速搭建同类毕设框架的读者,也可作为课程答辩与项目演示的完整参照。此外还提供数据库脚本、前端界面与硬件接口等模块线索,能够帮助理解智能仓储从设备层到业务层的完整技术链路。
1. 智能仓储管理系统代码.zip:毕设清单里的高频选项,先看清它包装了什么
每年三四月份,总有一批人被同一个问题卡住:毕设题目叫“智能仓储管理系统”,手里只有一个从各种渠道拿到的 zip 压缩包。这个包通常装着后端 Java 代码、前端页面、数据库脚本和一份 README,文件名看着齐全,但照着说明一跑就是一连串报错。下面要做的,就是把这包东西从“能解压”推到“能演示、能答辩、能说清自己改了什么”。它适合两类人:一类是课程作业需要交付一个能跑的系统,另一类是毕设选了仓储方向、想用最短路径完成系统实现、把精力留给论文的同学。先给一个反直觉的判断:判断这包代码能不能用,不要先看 .java 文件,先看数据库脚本和配置文件——这两个文件决定你要花多少时间填坑,也基本决定了答辩时老师会往哪个方向追问。
2. 解压后先做三件事:识技术栈、看数据库脚本、核对配置文件
2.1 从 pom.xml 和目录结构判断这包代码属于哪个技术年代
拿到 zip 后第一步不是双击运行,而是解压、开目录。这类项目最典型的布局是 Maven 工程:根目录有 pom.xml,源码在 src/main/java 下,资源文件在 src/main/resources 下,数据库脚本一般在 sql 目录,有的放在 doc、database 目录。先看 pom.xml 里的依赖坐标,就能判断这个项目是近几年的还是十年前的老古董。
smart-warehouse/ ├── pom.xml # Maven 父工程,版本号与依赖全在这 ├── sql/ │ └── warehouse.sql # 数据库初始化脚本,最重要文件 ├── README.md # 作者写的启动说明,可信度要打折 ├── src/main/java/com/warehouse/ # 后端源码 │ ├── controller/ # 接收 HTTP 请求 │ ├── service/ # 业务逻辑层 │ ├── mapper/ # MyBatis 数据访问接口 │ └── entity/ # 数据库实体类 ├── src/main/resources/ │ ├── application.yml # Spring Boot 配置文件 │ └── mapper/ # MyBatis XML 映射文件 └── src/main/webapp/ # 老项目放 JSP 页面,新项目一般没有判断依据有三条。第一条:根目录有 pom.xml 就是 Maven 管理的 Java 项目;没有 pom.xml 而是 build.gradle 就是 Gradle 项目,你本地要装 Gradle;两个都没有还塞满 .class 文件,说明发给你的是编译产物不是源码,这类包建议直接放弃,因为答辩老师一定会问源码在哪,你说不上来。第二条:pom.xml 里出现 spring-boot-starter-parent,是 Spring Boot 项目,打包后 java -jar 就能起;如果看到的是 spring-webmvc、struts2-core 这种旧坐标,说明是 SSM/SSH 老项目,必须部署到 Tomcat。第三条:src/main/webapp 存在,说明项目带有服务端页面;不存在但前端是独立目录(frontend/、web/),说明是前后端分离结构,需要单独启动前端。
两种主流技术栈的选型差异,用一张表就能说清:
| 技术栈 | 典型特征 | 启动方式 | 适合群体 |
|---|---|---|---|
| Spring Boot + MyBatis + Vue | 前后端分离,接口返回 JSON | mvn spring-boot:run / java -jar | 新项目、系统演示效果更好 |
| SSM(Spring + SpringMVC + MyBatis) | JSP 服务端渲染 | war 包部署 Tomcat | 老项目、课程要求 JSP |
| SSH(Struts + Spring + Hibernate) | 年代久远,Hibernate 映射 | Tomcat 6/7 + JDK 7 | 不推荐,坑多且答辩价值低 |
判断完年代后,再看 pom.xml 里的 <java.version> 标签:1.8 对应 JDK 8,17 对应 JDK 17。版本对不上是后面一切启动失败的根源。我一般会先执行 mvn -v 看本地 Maven 和 Java 版本,再决定要不要装另一个 JDK。Windows 上可以在 IDEA 的 Project Structure 里给每个项目单独指定 JDK 路径,不必非要改系统环境变量。
2.2 数据库脚本决定项目上限:表设计里藏着评分点
数据库脚本是整个压缩包里最值得花半小时读完的文件。原因很简单:仓储系统的业务复杂度几乎全在表结构里,代码只是围绕表在做增删改查。一份合格的仓储系统脚本至少应该有货品表、供应商表、入库明细表、出库明细表、库存表、用户表。缺任意一张,对应模块就是页面上的空壳按钮,答辩时一戳就穿帮。
看一段典型的建表脚本:
-- 货品表:存商品基本信息与当前库存 CREATE TABLE `goods` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键', `name` VARCHAR(100) NOT NULL COMMENT '货品名称', `specification` VARCHAR(100) DEFAULT '' COMMENT '规格型号', `unit` VARCHAR(20) DEFAULT '件' COMMENT '计量单位', `stock` INT DEFAULT 0 COMMENT '当前库存数量', `min_stock` INT DEFAULT 0 COMMENT '库存预警下限', `supplier_id` INT DEFAULT NULL COMMENT '关联供应商ID', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='货品表'; -- 入库明细表:每一条入库流水对应一个货品 CREATE TABLE `inbound_item` ( `id` INT NOT NULL AUTO_INCREMENT, `inbound_no` VARCHAR(32) NOT NULL COMMENT '入库单号', `goods_id` INT NOT NULL COMMENT '货品ID', `quantity` INT NOT NULL COMMENT '入库数量', `price` DECIMAL(10,2) DEFAULT 0 COMMENT '入库单价', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_goods_id` (`goods_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='入库明细表';这段脚本有两个设计要点值得注意。一是 goods 表直接存了一个 stock 字段,等于把“当前库存”冗余在货品上,查询库存非常快,但所有出入库操作都必须同时更新这个字段并加上事务保护,否则会数据不一致。二是入库明细独立成表,只记流水不直接改库存,真正的库存变动发生在业务层调用 UPDATE 之后。如果脚本里只有一张“入库单”表而没有明细表,说明这套系统的库存逻辑大概率是直接覆盖数字,没有流水可查,老师问“你如何追溯某批货品的入库历史”,你答不上来。
另外要留意脚本用了什么 SQL 特性来判断数据库版本需求。utf8mb4 和 CURRENT_TIMESTAMP 这两个写法,MySQL 5.7 以上都支持;如果出现 json 类型字段或窗口函数(比如 ROW_NUMBER() OVER),那只能在 MySQL 8.0 上跑。建议直接装 MySQL 8.0,向下兼容性最好,现在多数答辩环境也是 8.0。
2.3 application.yml:数据库名、用户名和密码的对应关系
配置文件是整包代码的钥匙。很多人启动失败的根因不在代码而在配置:zip 里带的 application.yml 是作者自己机器上的现场残留,数据库名、用户名、密码大概率和你本地不一致。最快的方法是把这一节的参数和你本地环境对齐,然后才谈得上启动。
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/warehouse?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.warehouse.entity这一段配置里的每个参数都有具体作用。url 里的 warehouse 是数据库名,必须和 CREATE DATABASE 建出来的名字完全一致,大小写也要一致;username 和 password 是 MySQL 的登录凭据;serverTimezone=Asia/Shanghai 解决 MySQL 8.0 时区默认 UTC 导致的日期偏差 8 小时问题;useSSL=false 消除 MySQL 8.0 的 SSL 警告刷屏;driver-class-name 用的是 com.mysql.cj.jdbc.Driver,这是 MySQL Connector/J 8.x 的驱动类名,老项目里的 com.mysql.jdbc.Driver 在新版本驱动里已被移除。mybatis.mapper-locations 指定 XML 映射文件位置,配错或漏配的表现是项目能启动,但一调用任何查询接口就报 Invalid bound statement (not found)。
提示:改完配置后,先用命令行敲一句 mysql -u root -p 测试本地连接,确认账号密码正确再启动项目。这个动作能把“代码问题”和“环境问题”一次性分开,省掉大量猜疑时间。
3. 本地跑通最小闭环:JDK、MySQL、Maven 的版本对齐与启动命令
3.1 Windows 下 MySQL 的 zip 方式安装与初始化
先处理最容易被卡住的环境环节。很多同学看到 README 里“配置 MySQL 数据库”一行字,就以为装个数据库点下一步就行,结果撞上一堆版本和权限问题。这里给一条在 Windows 上最可控的安装路径,也就是通过 zip 包手动安装 MySQL,顺便把服务、密码、字符集一次配齐。
# 1. 下载 mysql-8.0.x-winx64.zip 并解压到 D:\mysql # 2. 在 D:\mysql 下新建 my.ini,核心配置如下 # [mysqld] # basedir=D:/mysql # datadir=D:/mysql/data # port=3306 # character-set-server=utf8mb4 # [client] # default-character-set=utf8mb4 # # 3. 以管理员身份打开 CMD,执行初始化 mysqld --initialize-insecure # 4. 注册为 Windows 服务并启动 mysqld --install net start mysql # 5. 空密码登录后立即修改 root 密码 mysql -u root -p ALTER USER 'root'@'localhost' IDENTIFIED BY '123456'; FLUSH PRIVILEGES;逐条说明为什么这样做。my.ini 里指定 basedir 和 datadir,MySQL 才找得到目录;character-set-server=utf8mb4 让数据库默认字符集直接是 utf8mb4,省得建表时逐个指定。mysqld --initialize-insecure 初始化数据目录并生成一个空密码的 root 用户,这一招比随机密码的 --initialize 更适合本地调试,因为你不必去翻 err 日志挖临时密码。mysqld --install 把 MySQL 注册成 Windows 服务,之后开机自启;net start mysql 立即启动它。最后一条 ALTER USER 把空密码改成 123456,和前面 application.yml 里的配置对齐。
常见翻车点有两个。一是 my.ini 里的路径中间不要带空格,比如 D:\Program Files\mysql 这种路径在服务注册时会报错,最好直接放 D:\mysql 或 C:\mysql。二是如果执行 mysqld --initialize-insecure 时报缺 MSVCR140.dll 或 msvcp140.dll,说明系统缺 Visual C++ Redistributable 运行库,装对应版本的 VC_redist.x64.exe 再重试,这跟 Java 项目本身无关,纯粹是 Windows 运行环境的问题。装完运行库后记得删掉半成品的 data 目录重新初始化,因为上一次失败可能留下了残留文件,这一步也算是后悔药。
3.2 导入数据库:命令行 source 比可视化工具更稳
数据库装好后,下一步是把 zip 里的 sql 脚本导进去。我见过太多人在 Navicat 里点“运行 SQL 文件”报 1064 语法错误,然后怀疑脚本有问题。实际上多数是编码问题——脚本里的中文注释被以 GBK 读取,语句被截断或乱码。命令行 source 方式是更可控的路径。
# 进入 MySQL 命令行 mysql -u root -p123456 # 建库并指定字符集 CREATE DATABASE warehouse DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE warehouse; # 先设置会话编码再导入,避免脚本中文注释变成乱码 SET NAMES utf8mb4; source D:/毕设/sql/warehouse.sql; # 验证:表数量是否和脚本一致 SHOW TABLES; SELECT COUNT(*) FROM goods;说明几个关键点。CREATE DATABASE 语句里显式指定 utf8mb4,防止 MySQL 默认字符集是 latin1 的情况;SET NAMES utf8mb4 让本次会话的传输编码是 UTF-8,这样脚本里的中文不会在入库时变问号;source 是 MySQL 客户端的导入命令,后面跟脚本文件的绝对路径,注意路径里的反斜杠要改成正斜杠,否则 Windows 下会识别异常。验证方式不是看命令回显,而是 SHOW TABLES 出来的表数量和脚本里 CREATE TABLE 的数量一致。
如果导入仍报错,先用 Notepad++ 打开 sql 脚本,看右下角编码,如果是 ANSI 或 GB2312,转为 UTF-8 无 BOM 再导入。还有一个细节:脚本开头如果有 DROP TABLE IF EXISTS 语句,说明这个脚本设计成可重复执行,导多次也不会坏;如果没有这条,重复导入会报表已存在,要么建一个新库重新导,要么手动 DROP 掉旧表。
3.3 启动后端与前端:依赖下载、打包、运行三步走
后端启动方式由技术栈决定。Spring Boot 项目最简单,Maven 打包后一条 java -jar 就能起;SSM 老项目要装 Tomcat,把 war 包丢进 webapps 目录再启动服务。这里以最常见的 Spring Boot + MyBatis 为例展开。
# 在项目根目录执行,跳过测试代码,打包成可执行 jar mvn clean package -DskipTests # 启动后端 java -jar target/warehouse-0.0.1-SNAPSHOT.jar # 开发调试模式可用热启动 mvn spring-boot:runmvn clean package 里的 clean 清掉上次编译的 target 目录,防止旧 class 残留污染构建;-DskipTests 跳过测试类执行,很多毕设项目里的单元测试会尝试连数据库,依赖还没对齐时跑必挂;target/warehouse-0.0.1-SNAPSHOT.jar 是打包产物,jar 包名取决于 pom.xml 里的 artifactId 和 version。启动时控制台出现 Tomcat started on port(s): 8080 说明后端起来了,如果报端口占用或数据库连接失败,按第 5 章的排查思路走。
前端如果是 Vue 项目,需要在独立的前端目录下安装依赖并启动开发服务器。
# 进入前端目录(一般叫 frontend、web 或 vue-ui) cd frontend # 首次运行必须先安装依赖,生成 node_modules 目录 npm install # 启动开发模式,默认开一个本地端口 npm run devnpm install 最大的坑在网络:部分依赖包下载不稳定,安装中途报错后 node_modules 目录是残留状态。这时候不要反复尝试增量修复,直接删掉 node_modules 和 package-lock.json,换镜像源后重新安装,比逐个排查依赖快得多。npm run dev 成功后,控制台会打印访问地址,一般形如 http://localhost:8081,和后端的 8080 不冲突。如果前后端要同端口部署(生产模式),先 npm run build 构建前端产物,再把 dist 目录放到后端静态资源路径下,毕设阶段用 dev 模式调试最省事。
前后端分离项目启动后,前端页面能打开,不代表接口通。用浏览器打开页面,F12 切到 Network 面板,发起一次登录请求,看请求 URL 是否带正确的 /api 前缀、响应状态码是不是 200。这一眼就能定位是跨域、路径还是后端没起来的问题,比盲目改代码高效得多。
4. 把三条主业务链路读懂:入库、出库、盘点背后的代码逻辑
4.1 入库链路:Controller 只接参数,Service 写事务
仓储系统的核心是三条链路:入库、出库、盘点。答辩时老师可以不看页面,但一定会顺着这三条链路问业务规则是怎么落地的。入库链路的典型写法是:前端提交入库单号、货品 ID、数量和单价,后端在同一个事务里写一条入库明细,同时更新货品表的库存字段。先看 Controller 层:
@RestController @RequestMapping("/api/inbound") public class InboundController { @Autowired private InboundService inboundService; // 前端 POST 一个入库请求,参数封装在 InboundReq 里 @PostMapping("/submit") public Result submit(@RequestBody InboundReq req) { // 参数校验交给 Service,Controller 不写业务判断 inboundService.createInboundAndUpdateStock(req); return Result.success("入库成功"); } }这段代码的定位是薄 Controller:只负责接参数、调 service、返回结果。如果你拿到的代码里 Controller 直接操作 Mapper,要警惕,因为事务注解放在 Service 上才有效,写在 Controller 里会因代理机制失效,这是个高频追问点。下面看真正干活的 Service 层:
@Service public class InboundServiceImpl implements InboundService { @Autowired private InboundItemMapper inboundItemMapper; @Autowired private GoodsMapper goodsMapper; @Override @Transactional(rollbackFor = Exception.class) public void createInboundAndUpdateStock(InboundReq req) { // 1. 写入库明细,记录本次流水 InboundItem item = new InboundItem(); item.setInboundNo(req.getInboundNo()); item.setGoodsId(req.getGoodsId()); item.setQuantity(req.getQuantity()); item.setPrice(req.getPrice()); inboundItemMapper.insert(item); // 2. 更新货品库存:使用 SQL 自增,而不是先查再加 goodsMapper.increaseStock(req.getGoodsId(), req.getQuantity()); } }这里有两个设计得分点。@Transactional(rollbackFor = Exception.class) 声明事务:只要方法内任何一步抛异常,前面写入的明细也会回滚,保证“明细写了但库存没更新”这类不一致状态不会出现。库存更新用 increaseStock 方法,对应 SQL 是 UPDATE goods SET stock = stock + #{quantity} WHERE id = #{goodsId},数据库在一条语句里完成“读出旧值、加新值、写回”,天然原子。如果在 Service 里先 SELECT 查库存,再算新值 UPDATE,两个人同时入库就会丢失更新,这就是老师最爱挖的并发陷阱。毕设数据量不大,但你能说出为什么要用 UPDATE 自增而不是 select + update,就是明显的加分项。
对应的 MyBatis XML 示例代码是这样:
<update id="increaseStock"> UPDATE goods SET stock = stock + #{quantity} WHERE id = #{goodsId} </update>逻辑说明:这条 SQL 没有先查库存,直接执行自增。MySQL 的 UPDATE 在行级有互斥锁,并发场景下两个事务串行执行,各自把 stock 加自己的数量,最终结果是正确的累加。注意 XML 里的 update 标签没有返回值类型声明,Mapper 接口里方法写成 int increaseStock(@Param("goodsId") int goodsId, @Param("quantity") int quantity) 时,拿到受影响行数,等于知道这次更新有没有真正命中货品 ID。
4.2 出库链路:用一条 UPDATE 完成“判断库存 + 扣减”
出库比入库多一层业务规则:库存不足要拦截,不能扣成负数。菜鸟写法是先 SELECT 查库存,判断足够再 UPDATE 扣减,这段代码在并发下存在窗口期:两个请求同时读到库存 5 件,都认为够,都扣 5 件,最后库存变成 -5。常见做法是让数据库在原子操作里完成校验和扣减。
// Mapper 接口中的方法签名 public interface GoodsMapper { // 返回受影响行数:1 表示扣减成功,0 表示库存不足 int decreaseStockWithCheck(@Param("goodsId") int goodsId, @Param("quantity") int quantity); }<update id="decreaseStockWithCheck"> UPDATE goods SET stock = stock - #{quantity} WHERE id = #{goodsId} AND stock >= #{quantity} </update>这条 SQL 的巧妙点都在 WHERE 子句:stock >= #{quantity} 不成立时,UPDATE 匹配不到行,受影响行数是 0,扣减被数据库拒绝。Service 层拿到返回值 0,抛一个“库存不足”的业务异常,前端就能弹出对应提示。这个写法的好处是判断和扣减在数据库内部一条语句完成,不存在读取后到更新前的并发窗口,是库存类系统最常见的落地方案。
Service 层的调用逻辑:
@Transactional(rollbackFor = Exception.class) public void outbound(OutboundReq req) { // 扣减库存,返回值 0 说明库存不够 int rows = goodsMapper.decreaseStockWithCheck(req.getGoodsId(), req.getQuantity()); if (rows == 0) { throw new BusinessException("库存不足,当前可出库数量不足"); } // 扣减成功后写流出库明细 outboundItemMapper.insert(buildItem(req)); }这里有个顺序问题值得说:先扣库存再写明细,还是先写明细再扣库存?在事务里都能保证一致性,但先扣库存的好处是:如果库存不足,根本不会写入一条注定要回滚的明细记录,业务校验更早发生。有同学把判断放在事务外面,两段代码中间隔着网络请求,那事务就白开了,要特别注意。
4.3 盘点与报表:聚合查询与看板数据组装
盘点模块在不少毕设代码里是缺失的,或者只做了个“盘点单”空页面。如果你想在答辩里多一个亮点,盘点逻辑最容易补:生成盘点差异列表,对库存进行修正。报表则是让老师直观看到系统价值的入口,两条 SQL 就能支撑高频展示场景。
-- 月度入库趋势:按天汇总入库总量,给 ECharts 折线图用 SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS day, SUM(quantity) AS total_in FROM inbound_item WHERE create_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') ORDER BY day; -- 库存预警清单:库存低于下限的货品,页面用红色预警列表展示 SELECT g.name, g.specification, g.stock, g.min_stock FROM goods g WHERE g.stock < g.min_stock ORDER BY g.stock ASC;说明这两个查询的价值。第一条按天做 GROUP BY 聚合,返回本月每天的入库总量,前端拿到 [{day:'2026-05-01', total_in:12}, ...] 这种数组,直接喂给 ECharts 画折线图或柱状图,是答辩演示里“系统有分析能力”的直接证据。第二条是预警查询,利用 goods 表里已有的 min_stock 字段,没有这个字段的项目可以补上,属于低成本改造。需要注意:DATE_FORMAT(create_time, ...) 会让普通索引失效,数据量小无所谓,但答辩时能主动说一句“这个查询在大数据量下可以用 BETWEEN 改写以利用索引”,会显得你懂性能。
报表数据在 Controller 层的组装方式一般是:Service 查询返回 List<Map<String, Object>>,Controller 包一层 Result 返回 JSON 数组,前端 axios 拿到后直接渲染。如果项目里用的是 MyBatis-Plus,可以用 QueryWrapper 的 selectMaps 方法;如果原生 MyBatis,用 Map 接收聚合查询结果最省事,不需要额外建 VO 类。
5. 利用最后一周:5 个高频坑的排查手册
5.1 数据库连不上:时区、SSL、root 密码三处一起查
现象:后端启动时报 Communications link failure,或者 Access denied for user 'root'@'localhost' (using password: YES),有的还带一行乱码时区错误。
原因:连不上数据库基本是三类问题叠加。一是 MySQL 8.0 的 JDBC 连接默认启用 SSL 校验,老驱动或旧配置会报 SSL 相关错误;二是时区参数缺失,MySQL 8.0 默认时区是 UTC,连接串里不写 serverTimezone 就报错;三是配置文件里的密码和 MySQL 实际密码不一致,zip 里带的配置是作者本机的密码,到你这儿大概率对不上。
解决:按顺序做三件事。先确认 MySQL 在运行,mysql -u root -p 能进命令行;再确认密码一致,注意 root 密码可能因为之前初始化方式不同(空密码、随机密码、自定义密码)和配置对不上;最后把 url 补全成 jdbc:mysql://localhost:3306/warehouse?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai。如果驱动还是连不上,检查 pom.xml 里 mysql-connector-java 版本是否 8.0+,老驱动不认 MySQL 8 的 caching_sha2_password 认证插件,升级驱动版本是改动量最小的方案。
5.2 中文乱码:三层字符集必须同时是 UTF-8
现象:页面货品名称显示成“è´§å“”这类乱码,数据库里查出来的中文是问号,控制台日志里的中文也异常。
原因:字符集没对齐。所谓三层是数据库表字符集、JDBC 连接字符集、前端页面字符集,只要有一层是 GBK 或 latin1,中文传到下一层就变乱码。最常见的发生点是:导入脚本时没加 SET NAMES utf8mb4,表里的中文已经是乱码,后面再怎么配都救不回来。
解决:直接按三层从下往上改。数据库层执行 ALTER TABLE goods CONVERT TO CHARACTER SET utf8mb4,把全部表转一遍;JDBC 层在 url 里加 characterEncoding=utf8;前端如果是 JSP,文件头要有 <%@ page contentType="text/html;charset=UTF-8" %>,如果是 HTML,确保。Windows 命令行导入脚本时,先执行 SET NAMES utf8mb4 再 source,否则脚本里的中文注释也会变成乱码进入表里。我的习惯是修完这三层后重启后端、刷新页面,再不行就重启 MySQL 服务,因为连接池里的旧连接可能还在用旧编码。
5.3 端口被占用:8080、3306 与前端端口
现象:启动后端时报 Port 8080 was already in use;启动 MySQL 时报端口 3306 被占用;前端 dev 模式起不来,或页面能开但接口全挂。
原因:上一次运行的 java 进程没退出,或者电脑上有其他程序占了端口。8080 被占最常见的是残留的后端进程;3306 被占一般是装了两个 MySQL 实例,或者装了其他数据库软件。
解决:Windows 下用 netstat -ano | findstr 8080 查占用进程的 PID,再用 tasklist | findstr 对应PID 看是什么程序,确认是残留 java 进程后用 taskkill /F /PID 对应PID 杀掉。如果不想杀进程,改端口更省事:application.yml 里 server.port 改成 8081,但必须同步改前端 axios 的 baseURL 里所有 8080 字眼。端口类问题的排错口诀:改后端端口就全局搜 8080,改前端端口就搜 devServer 配置,只改一处等于没改。
5.4 前端请求 404 还是 500:先看 Network 面板再动手
现象:前端页面打开了,但登录、查询所有请求都报错,有的是 404,有的是 500,有的控制台显示 CORS 跨域。
原因:404 大多是路径对不上,比如后端接口是 /api/login,前端请求写成了 /login;500 说明请求到达后端但业务执行报错,要去翻后端控制台异常栈;跨域报错是前后端分离项目没配 CORS 或代理。
解决:打开浏览器 F12 的 Network 面板,点击出错的请求,先看 Request URL 和状态码。404 就把 URL 与后端 Controller 的 @RequestMapping 值逐字对比,特别注意 /api 前缀和大小写;500 就切到后端控制台,看最后一段异常栈,Mapped to 那行能定位是哪个 Controller 方法在执行,后面的异常类型说明问题方向;跨域报错则在后端加一个 WebMvcConfigurer 配置类允许跨域,或者在前端 vue.config.js 里配置 devServer.proxy 把 /api 代理到 http://localhost:8080,二选一即可。别一上来就改代码,先看现象,能省掉一半排查时间。
5.5 依赖下不动、DLL 缺失、JAR 起不来
现象:mvn 命令卡在下载,报 Could not resolve dependencies;启动过程中提示找不到 msvcp140.dll 或 msvcr120.dll;java -jar 执行后窗口一闪就没了。
原因:Maven 依赖下载失败通常是中央仓库连接不稳定,或者 pom.xml 里有私有坐标;DLL 缺失是 Windows 缺 Visual C++ 运行库,跟 Java 代码无关;jar 秒退多半是启动参数或配置错误,但报错信息被吞了看不到。
解决:Maven 下载问题,改 settings.xml 里的 mirror 为国内镜像,然后删除本地仓库(默认 C:\Users\用户名.m2\repository)里的 *.lastUpdated 文件后重试;DLL 缺失装对应版本的 VC_redist.x64.exe,装完重启终端再跑;jar 秒退不要双击启动,用 CMD 命令行 java -jar xxx.jar 在前台运行,报错信息会留在控制台,窗口不会自动关闭。这里要强调一个习惯:不要用双击方式启动 jar,它在 Windows 下出错瞬间窗口就关了,等于黑匣子;用 CMD 运行才能看到真正的异常栈,这个习惯能让你从“不知道哪错了”变成“有一条日志可以查”。
6. 让代码变成自己的:一个预警功能改造与五分钟演示路径
6.1 最小改造:给货品加一个库存预警状态
答辩最大的送命题是“这代码是你写的吗”。解决方案不是重写,而是做一处别人一看就知道你读懂了代码的最小改造。推荐在货品列表加一个“预警状态”列:库存低于 min_stock 显示红色“库存不足”,否则显示“正常”。这个改造涉及实体类、Service、页面三处,链路短且能讲清楚。
// 在 Goods 实体里加一个只读字段,不落库,只用于展示 @TableField(exist = false) private String warningState; // Service 层查询列表时计算预警状态 for (Goods goods : goodsList) { if (goods.getStock() < goods.getMinStock()) { goods.setWarningState("库存不足"); } else { goods.setWarningState("正常"); } }前端在表格对应列用颜色区分两种状态,Element UI 里用 tag 标签就能实现。改完后把代码提交到本地 git,如果愿意更进一步,推到码云这类代码托管平台,答辩时给对方看提交记录,能直观证明这是你亲手改的。再配合一个简单的库存预警 SQL 查出一条记录做演示,这个改造就有了完整闭环:表结构有 min_stock 字段支撑、代码有状态计算、页面有视觉反馈,老师挑不出毛病。
6.2 答辩演示的故事线:从登录到报表一分钟一步
演示顺序比功能多少更重要。我给你的路径是:登录 → 新增一个货品 → 做一次入库(库存从 0 变正数)→ 做一次出库(库存减少)→ 打开报表看入库趋势折线 → 最后点开预警列表展示“库存不足”的红色标记。这条线的逻辑是一个完整的业务故事:建货品、进库、出库、分析、预警,每一步都在前一步基础上自然发生,不需要跳来跳去。
演示前把数据库里的脏数据清一遍,保证入库前货品列表干净;浏览器提前打开,Network 面板关掉,避免答辩现场弹一堆请求记录分散注意力。如果演示中途接口报错,不要慌,说“这个问题我在本地验证过,是当前网络或环境变量导致”,然后切到命令行重新启动后端,比在页面上反复刷新更有说服力。这个项目的价值不在代码量,而在于你能否把入库、出库、盘点三条链路讲出事务、并发控制、聚合查询三个层次。把这些年踩过的坑——乱码、端口、版本不齐——提前演一遍,答辩就是你带着评委看你已经跑通的东西。希望帮到你。
本文还有配套的精品资源,点击获取