news 2026/10/2 18:22:36

Spring Boot进销存毕设项目实战:从源码到跑通的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot进销存毕设项目实战:从源码到跑通的完整指南

简介:面向高校计算机专业毕业设计的基于 Spring Boot 的进销存管理系统源码包,适合 Java 开发者、应届毕业生参考学习。系统以企业物资流与资金流为主线,覆盖采购订单持久化、采购入库与退货、商品入库出库、库存查询、销售订单添加与发货、销售退货、员工培训信息管理等完整业务流程;从源码预览可见包含权限控制、商品管理、出入库处理、销售与退货等核心模块,并运用了 Shiro 权限控制,具备较清晰的分层结构与功能划分,可为毕业设计选题提供完整的项目方案。资源包共 672 个文件,压缩后约 4.07MB,文件类型以 Java 源码(103 个)、class 编译文件(103 个)、HTML 页面(98 个)、JS 脚本(84 个)为主,同时包含 SQL 数据库脚本、XML/JSON 配置文件、CSS 样式及图片素材,既便于直接导入 IDE 阅读源码,也支持前端页面与后端接口对照调试。目前已有 2536 人学习下载,适合需要快速理解并复现一个完整进销存管理系统的学生开发者。

1. 进销存毕设项目:Spring Boot源码+数据库,拿到手先想清楚这三件事

做毕业设计选进销存管理系统的人,比选电商、选博客的多得多,原因很简单:进销存是典型的业务系统,采购、销售、库存、供应商、客户、统计报表,每个模块都够你用Spring Boot完整走一遍CRUD、权限、事务、分页这些Java Web必考技能。这个标题里打包的“项目源码+数据库.zip”,本质就是一份带完整前后端和初始化SQL的Spring Boot参考工程,适合拿来做课程设计、毕业设计,甚至是入职前的练手项目。

但我要先说一句不中听的:下载源码、导入数据库、点运行、看到登录页,这一步谁都会;毕业设计能不能过、答辩能不能说清楚,取决于你能不能讲明白这个系统的数据流和每一个表存在的意义。这篇笔记就是按这个标准来写的——先帮你把这个标题背后的工程拆透,再带你把Spring Boot进销存从配置到跑通、从运行到改造成自己的毕设走一遍。该设置的参数、该踩的坑、答辩时会被问到的问题,都会提到。

2. Spring Boot在进销存系统里的角色:从框架选型到工程骨架

2.1 为什么是Spring Boot:毕设项目选型的第一原则是“稳”

如果你搜过“springboot框架”相关的教程和源码,会发现近三年的Java课程设计案例源码里,Spring Boot已经成了事实标准。原因不复杂:Spring Boot把Spring MVC、Jackson、嵌入式Tomcat、数据源自动配置这些早年需要手写一大堆XML的东西全部自动装配了,你只需要在application.yml里写几行配置就能起一个Web项目。对做毕设的同学来说,选Spring Boot意味着你可以把精力放在“进销存业务逻辑”上,而不是在配置Spring、配置MyBatis上熬两个通宵。

进销存系统还有一个特殊性:它是强事务系统,一个采购入库操作要同时更新库存表、插入单据表、可能还要改供应商的应付账款。Spring Boot默认支持Spring声明式事务,一个@Transactional注解就能搞定。相比之下,像PHP写的那种纯脚本方案,事务控制要靠手写,一旦项目大了很容易漏。所以我的建议是:除非你的毕设题目明确要求别的技术栈,否则Spring Boot + MyBatis + MySQL是进销存毕设最稳的组合。

2.2 解压zip之后先看什么:标准工程结构必须认识

拿到一个“springboot进销存项目源码+数据库.zip”包,解压后你大概率会看到这样一个工程结构。这是约定俗成的Spring Boot项目布局,源码包里十有八九是这门课布置下来的标准模板。了解这个布局,是之后改代码、写论文、画架构图的底气。

inventory-system/ ├── pom.xml // Maven依赖管理,Spring Boot版本 ├── src/main/java/com/example/inventory/ │ ├── InventoryApplication.java // 启动类 │ ├── config/ // 配置类,放拦截器、跨域、MyBatis分页等 │ ├── controller/ // 控制层,接收前端请求 │ ├── service/ // 业务层,处理业务逻辑和事务 │ ├── mapper/ // MyBatis数据访问层接口 │ ├── entity/ // 数据库表对应的实体类 │ └── common/ // 通用返回值、异常处理、工具类 ├── src/main/resources/ │ ├── application.yml // Spring Boot配置文件 │ └── mapper/ // MyBatis的XML映射文件,SQL写在这里 └── sql/ └── inventory.sql // 数据库初始化脚本

这里要特别提醒:resources/mapper目录下的XML文件,是MyBatis的SQL映射文件。很多新手拿到源码后找不到“SQL写在哪”,其实就在这个目录里。业务代码在service层,数据库操作在mapper层,controller只负责接收参数和返回结果——这个分层结构就是毕业设计论文里“系统设计”章节的配图素材,不要照抄别人的图,你得能自己画出来。

如果源码包里还带了前端目录,通常是两种形态:一种是传统模板渲染(用Thymeleaf或JSP),页面放在resources/templates下;另一种是前后端分离(Spring Boot + Vue),前端是单独的vue前端文件夹,需要npm install和npm run dev才能跑起来。后者对应的是热词里“springboot vue前后端分离”的趋势,也意味着你的毕设要额外演示前端打包部署的步骤,工作量更大但看起来更“高级”,答辩时老师通常也更吃这一套。

2.3 登录鉴权与角色权限:进销存系统最容易被卡住的一块

进销存系统的用户,一般分管理员、采购员、销售员、仓管员这几种角色。采购员不能看到销售单,仓管员不能修改商品价格,要实现这个,就要用Spring Boot里的拦截器(HandlerInterceptor)或者过滤器(Filter,比如处理XSS攻击的全局过滤器)配合Session或JWT来做登录拦截和角色校验。

我在不少毕设源码里看到过一种极简方案,很适合接手别人的项目:登录成功后把user对象放进Session,定义一个自定义拦截器,拦截所有除登录页和静态资源之外的请求,从Session里取user,取不到就重定向到登录页。角色权限再在拦截器里加一个if (!"admin".equals(role))判断。这段代码是毕业设计出镜率最高的部分,建议你拿到源码后第一件事就是找到这个拦截器,读懂它并能在答辩时讲出它的工作流程。

@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口和静态资源 if (request.getRequestURI().contains("/login") || request.getRequestURI().contains("/static")) { return true; } // 从Session获取登录用户,取不到就重定向到登录页 Object user = request.getSession().getAttribute("loginUser"); if (user == null) { response.sendRedirect("/login"); return false; } return true; } }

这段代码的逻辑是:所有请求进入Controller之前先过preHandle方法,登录接口和静态资源直接放行,其他请求必须带Session里的loginUser属性,否则就弹回登录页。参数说明:HandlerInterceptor是Spring MVC提供的拦截器接口,preHandle返回true继续执行,false中断请求。另外在Spring Boot中还需要一个WebMvcConfigurer配置类把这个拦截器注册到InterceptorRegistry里,并指定拦截路径addPathPatterns("/**")、排除路径excludePathPatterns("/login", "/css/**", "/js/**")。如果用的是前后端分离方案,这里要换成JWT Token:前端请求带Authorization头,后端写一个OncePerRequestFilter从Header里解析token。两种方案选一个吃透就行。

2.4 分页、事务与全局异常:三个让代码“体面”的细节

进销存的商品列表、入库单列表、销售统计,数据量一大就要分页。如果你在源码里看到的是自己手写LIMIT计算页码,那属于远古写法;现代Spring Boot毕设项目标配是MyBatis分页插件PageHelper。pom依赖加pagehelper-spring-boot-starter,配置里写一行helper-dialect: mysql,然后在service层调用:

PageHelper.startPage(pageNum, pageSize); List<Product> list = productMapper.selectProductList(product); PageInfo<Product> pageInfo = new PageInfo<>(list);

这里PageHelper.startPage(pageNum, pageSize)后面必须紧跟第一条Mapper查询,中间不能插入其他SQL;PageInfo里自带getTotal()、getPages()这些分页参数,前端直接拿这个对象渲染页码。这是“mybatis的分页插件的用法 springboot”这个搜索热词背后的标准答案,也是答辩时老师最爱让你讲的一段代码。

事务方面,进销存里的采购入库、销售出库必须有事务。@Transactional默认只捕获RuntimeException回滚,如果业务里抛出的是受检异常(比如Exception),需要手动指定rollbackFor = Exception.class。全局异常处理用@RestControllerAdvice写一个类,把BusinessException、参数校验异常、兜底异常分别映射到统一返回值。这套代码在进销存源码里是“common”包下的标配,能让你在答辩时说清楚“系统如何保证数据一致性”——这已经超过一半的毕设提升了。

3. 进销存数据库设计:五张核心表与字段取舍

3.1 商品、供应商、客户、库存、订单:先定主体再补关联

进销存的核心是“进、销、存”三个动作,围绕这三个动作至少有五张表:product(商品表)、supplier(供应商表)、customer(客户表)、inventory(库存表)、order(订单表,或拆成采购单和销售单)。顺手再补两张辅助表:user(用户表)和order_item(订单明细表)。数据库设计是毕业设计论文的重头戏,也是答辩连环问的高发区。

下面是一份在毕设源码中最常见的进销存数据库初始化脚本,按这份脚本建出来的库,能支撑起基本的采购入库、销售出库和库存查询:

-- 用户表,保存系统登录账号 CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(50) NOT NULL COMMENT '登录名', `password` varchar(100) NOT NULL COMMENT '密码(MD5/BCrypt加密)', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `role` tinyint NOT NULL DEFAULT 1 COMMENT '角色: 1管理员 2采购员 3销售员 4仓管员', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表'; CREATE TABLE `product` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '商品主键', `product_no` varchar(50) NOT NULL COMMENT '商品编号', `name` varchar(100) NOT NULL COMMENT '商品名称', `category` varchar(50) DEFAULT NULL COMMENT '商品分类', `unit` varchar(20) DEFAULT '件' COMMENT '计量单位', `purchase_price` decimal(10,2) DEFAULT NULL COMMENT '采购单价', `sale_price` decimal(10,2) DEFAULT NULL COMMENT '销售单价', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_product_no` (`product_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'; CREATE TABLE `supplier` ( `id` int NOT NULL AUTO_INCREMENT, `supplier_no` varchar(50) NOT NULL COMMENT '供应商编号', `name` varchar(100) NOT NULL COMMENT '供应商名称', `contact` varchar(50) DEFAULT NULL COMMENT '联系人', `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', `address` varchar(200) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`), UNIQUE KEY `uk_supplier_no` (`supplier_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商表'; CREATE TABLE `customer` ( `id` int NOT NULL AUTO_INCREMENT, `customer_no` varchar(50) NOT NULL, `name` varchar(100) NOT NULL, `phone` varchar(20) DEFAULT NULL, `address` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_customer_no` (`customer_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户表'; CREATE TABLE `inventory` ( `id` int NOT NULL AUTO_INCREMENT, `product_id` int NOT NULL COMMENT '商品ID', `quantity` int NOT NULL DEFAULT 0 COMMENT '当前库存数量', `safe_quantity` int DEFAULT NULL COMMENT '安全库存下限', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_product_id` (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表'; CREATE TABLE `order` ( `id` int NOT NULL AUTO_INCREMENT, `order_no` varchar(50) NOT NULL COMMENT '单据编号', `order_type` tinyint NOT NULL COMMENT '类型: 1采购入库 2销售出库', `partner_id` int NOT NULL COMMENT '供应商或客户ID', `operator_id` int NOT NULL COMMENT '操作人', `total_amount` decimal(12,2) DEFAULT NULL COMMENT '单据总金额', `order_date` datetime NOT NULL COMMENT '单据日期', `remark` varchar(255) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='单据主表'; CREATE TABLE `order_item` ( `id` int NOT NULL AUTO_INCREMENT, `order_id` int NOT NULL COMMENT '单据主表ID', `product_id` int NOT NULL COMMENT '商品ID', `quantity` int NOT NULL COMMENT '数量', `price` decimal(10,2) NOT NULL COMMENT '成交单价', `subtotal` decimal(12,2) NOT NULL COMMENT '小计', PRIMARY KEY (`id`), KEY `idx_order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='单据明细表';

每张表的COMMENT一定要保留,因为答辩时老师很可能会直接打开你的数据库看注释,它能直接体现你有没有认真做设计。order这个表名要注意,它是MySQL的保留字,最好加反引号,或者直接改叫purchase_order和sale_order分开建表,毕设源码里通常用反引号方案,但并不推荐——写在SQL里的时候忘加反引号的概率太高了,这也是很多“导入SQL就报错”的坑。

3.2 出入库流水:把库存变化做成一张只追加的表

如果你看过真正的进销存产品源码,会发现除了上面的inventory表,还会有一张stock_record——库存流水表。每一次入库、出库、盘点调整,都往这张表里插一条记录:商品ID、变动数量(正负)、变动类型、变动前后的库存数量、关联单据号。这么做最大的好处是:任何一个时间点的库存数都能从流水算出来,出了问题可以追溯,而不是只能看到“当前库存是0”的结果。

毕业设计里不一定要求这张表,但如果你想让项目有亮点,加一张流水表是性价比极高的选择。它对应论文里的“系统详细设计”和“数据库设计”两个章节,还能引出“生产环境下库存系统为什么不做直接UPDATE而要做流水追加”这个业务问题。简单的说:库存快照表是结果,库存流水表是过程;有过程的数据才有说服力。

库里还有一类容易被忽略的表是“字典表”,比如dict_product_category存商品分类、dict_unit存计量单位。毕设源码里一般不会建这么细,通常是直接写死在下拉框里。这个不怪你,但我建议把分类和单位保持在前端下拉框配置,而不是硬编码在代码里,这样一个简单改动就能让系统看起来更灵活。

3.3 SQL脚本里的初始数据:管理员账号、演示商品、测试库存

数据库脚本里除了建表语句,还要有初始化数据。最常见的初始数据有三类:

-- 1. 默认管理员账号,密码123456(用MD5加密后的值,不要存明文) INSERT INTO `user` (`username`, `password`, `real_name`, `role`) VALUES ('admin', '$2a$10$D9J0Q6Uw4JzY3HsSxY8xP.2P7wXlL1OiH8WtQ1UZ1oVJ8kIkN6qQq', '系统管理员', 1); -- 2. 演示商品,方便系统启动后就有数据可看 INSERT INTO `product` (`product_no`, `name`, `category`, `unit`, `purchase_price`, `sale_price`) VALUES ('SP001', '示例商品A', '办公用品', '件', 10.00, 15.00), ('SP002', '示例商品B', '耗材', '盒', 20.00, 28.00); -- 3. 初始库存,与商品对应,保证登录后能看到“首页统计”效果 INSERT INTO `inventory` (`product_id`, `quantity`, `safe_quantity`) VALUES (1, 100, 20), (2, 50, 10);

这里对密码加密要单独说:源码包里的用户表初始密码,常见有MD5和BCrypt两种加密。MD5现在已经不建议用于生产系统,但毕设里大量出现,因为老教程多;$2a$10$开头是BCrypt格式,对应spring-security-crypto库或jbcrypt。你用哪个就跟代码里的PasswordEncoder保持一致,否则会登录失败。拿到源码后第一件事就是用application.yml里默认的管理员账号登录一遍,这个动作能过滤掉一半的“密码对不上”问题。

3.4 连接池与SQL日志:运行期排查全靠这两个配置

Spring Boot默认使用HikariCP连接池,不用额外配置就能跑。但有几个参数建议在答辩时能说出来:maximum-pool-size默认10,connection-timeout默认30000毫秒,这两个值在小项目里不用调,但能说清楚“数据库连接池是管理数据库连接的缓存,避免每次请求都重新建立连接”——这句话本身就是数据库热词“mysql的数据库连接池”的考点。更值得动手的是打开SQL日志,它能让你看到MyBatis真正执行的SQL语句:

logging: level: com.example.inventory.mapper: debug

把com.example.inventory.mapper的日志级别设为debug后,控制台会打印MyBatis执行的每一条SQL和参数。排查“页面查不到数据但库里明明有”这类问题,这是最直接的方法。MyBatis还支持配置驼峰映射,让数据库字段product_no自动映射到实体类的productNo属性,在application.yml里写一行map-underscore-to-camel-case: true,可以避免在XML里手写resultMap的繁琐。

4. 从源码到跑通:本地运行Spring Boot进销存项目的完整步骤

4.1 环境准备:JDK、Maven、MySQL的版本搭配

源码包里的pom.xml已经锁定了Spring Boot版本,你本地的环境必须和它匹配。常见的搭配是:Spring Boot 2.7.x对应JDK 1.8或11,Spring Boot 3.x对应JDK 17+。MySQL则用5.7或8.0都行,驱动方面Spring Boot会自动根据URL匹配,但如果遇到“Unable to load authentication plugin 'caching_sha2_password'”这个报错,就是MySQL 8.x默认认证插件和旧驱动不兼容,换用较新的mysql-connector-j版本即可。开发工具就用自己的IDE,IDEA还是Eclipse都影响不大,重点是JDK和Maven仓库要配置好。

Maven依赖下载慢的话,记得改阿里云镜像,settings.xml里加一段<mirror>指向https://maven.aliyun.com/repository/public,能省下大量时间。这一步不算“作弊”,算是国内Java开发的常规操作,热词“springboot配置”里一半的问题都是依赖下载和仓库配置的问题。

4.2 改配置:application.yml里必改的四个参数

Spring Boot进销存项目里,src/main/resources/application.yml是核心配置。拿到源码后,下面这四个位置是必改的:

server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/inventory?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 123456 servlet: multipart: max-file-size: 10MB max-request-size: 10MB mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.inventory.entity configuration: map-underscore-to-camel-case: true pagehelper: helper-dialect: mysql reasonable: true

参数说明:url里的serverTimezone=Asia/Shanghai必写,不然MySQL 8.0会报时区错误;useSSL=false避免本机连接时的SSL警告;mapper-locations指定MyBatis的XML文件位置,如果源码里的XML文件在resources/mapper下,这里就不用动。pagehelper.reasonable: true表示页码越界时自动纠正,比如第0页自动变成第1页,这个小参数能让分页逻辑更健壮。

如果源码是前后端分离版,还要检查跨域配置和前端请求的baseURL。一个非常常见的现象:前端能启动,但一调用接口就报跨域错误,原因就是后端没开CORS。Spring Boot里加一个@CrossOrigin注解或写一个WebMvcConfigurer配置类即可解决。

4.3 导入SQL并启动:在IDE里把项目跑起来的三步

第一步,在你的MySQL里创建一个数据库,名字要和application.yml里的url保持一致。直接在Navicat或命令行里跑:

mysql -uroot -p123456 CREATE DATABASE IF NOT EXISTS inventory DEFAULT CHARSET utf8mb4; use inventory; source /path/to/inventory.sql;

注意Windows下source路径要用正斜杠或转义反斜杠,否则会报找不到文件,这是一个非常经典的小坑。导入成功后,用show tables;确认所有表都已经建立,这一步能定位你是否导对了SQL文件。

第二步,在IDEA里打开项目根目录的pom.xml,让Maven自动下载依赖。等到右侧Maven窗口的依赖列表不再报红,就可以直接运行InventoryApplication这个启动类。运行后看控制台日志,出现Started InventoryApplication in 5.32 seconds这样的内容,说明启动成功。

第三步,打开浏览器访问http://localhost:8080,看到登录页说明整个链路已经通了。如果用默认的admin/123456登录进去之后页面空白或接口报500错误,优先去查日志里的SQL异常,大概率是数据库初始化数据不完整,或字段名和实体类对不上。

4.4 用Postman或浏览器验证进销存核心链路

项目跑通后,先别急着写论文,用手动测试把核心链路走一遍。以销售出库为例:登录后建一张销售单,选客户、选商品、填数量、填单价,提交后去库存页面看该商品的库存数量有没有减少。这一步同时验证了订单表写入、库存表更新和事务回滚逻辑。如果在Spring Boot 里这个流程你没走通,后面写“系统测试”章节时就没有真实数据可写,到时候全靠编,答辩老师一问就露馅。

我一般还会用Postman测一遍登录接口,看返回的JSON格式是否统一。比如登录成功返回{code: 200, data: {token/userInfo}},参数错误返回{code: 400, message: "...原因"},这种统一返回值结构,是毕业设计里“接口设计”章节需要展示的核心内容,也是后续改成前后端分离时的数据约定基础。

5. 进销存毕设源码避坑:5个别人常翻车的点

5.1 启动报时区错误,控制台抛出The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized

现象:Spring Boot启动连接数据库时报时区无法识别,URL里不带serverTimezone或者带了错误的值。

原因:MySQL 8.0以上默认时区设置和JDBC驱动不一致,本质是连接串里缺少时区参数;有时是因为你的MySQL服务在安装时选了中国时区,但驱动拿不到系统时区。

解决:在application.yml的数据库URL末尾加&serverTimezone=Asia/Shanghai,如果之前已经加了还是报错,检查URL里是不是用了中文分号或空格,直接复制我上面给的完整字符串替换最稳。MySQL 5.7一般不会有这个问题,所以这个坑更多是MySQL 8.0用户会遇到。

5.2 明明导入了SQL,但登录时报“表不存在”

现象:进入登录页输入账号密码,点击登录后报Table 'inventory.order' doesn't exist,但show tables里确实没有这张表。

原因:order是MySQL保留字,创建表语句没加反引号时,MySQL会把它当作关键字解析,建表失败。这类表还包括user、group、key等,很多毕设源码里的SQL脚本自己就不带反引号,执行时有一半的表没建成功。

解决:打开inventory.sql,按Ctrl+F搜索一下所有创建表的语句,给疑似保留字的表名统一加上反引号。更省事的办法是,重新执行一遍SQL脚本,执行完不要再单独建表,通过show tables逐个核对。

5.3 保存采购入库单后,库存数量变成负数

现象:采购入库单保存成功,但对应商品的库存反而变成负数。

原因:这是进销存系统最典型的业务逻辑漏洞。源码里保存采购单和更新库存是两个独立SQL,中间没有事务控制,前端连续点击“保存”按钮时并发执行,导致库存被重复扣减;或是业务逻辑里把入库写成减库存。

解决:在service层的入库方法上添加@Transactional(rollbackFor = Exception.class),确保“插入单据”和“更新库存”在同一个事务里,任何一步失败都整体回滚。同时在前端对提交按钮做防重复提交处理,比如提交后置灰2秒。用Postman并发请求测试接口,验证修复效果。

5.4 图片上传后,刷新页面就404

现象:在商品管理里上传了商品图片,当时页面显示正常,刷新后图片加载不出来,控制台报404。

原因:Spring Boot默认的静态资源目录是classpath:/static/,但上传的文件如果保存到了项目运行时的临时目录(如/tmp/或IDE启动时生成的工作目录),重启或刷新后就找不到了。另外一个常见原因是把上传目录写在了resources下,重新编译时文件被覆盖。

解决:在application.yml里配置自定义上传路径,例如upload.path: D:/uploads/,然后写一个自定义ResourceHandler把/upload/**映射到那个物理路径。这样文件不会随着项目重启丢失,也方便答辩时演示真实文件。关键代码是:

@Configuration public class UploadConfig implements WebMvcConfigurer { @Value("${upload.path}") private String uploadPath; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + uploadPath + "/"); } }

addResourceHandler("/upload/**")表示URL中以/upload/开头的请求,addResourceLocations("file:" + uploadPath)表示映射到本地磁盘目录,Spring Boot 会把你定义的配置类自动加载。这里的@Value注解从配置文件读取upload.path的值,本地跑项目时绝对路径比相对路径省心。

5.5 拿到源码后直接改了个项目名,启动就报一堆错

现象:把源码里的包名、项目名改成自己的学号姓名后,项目无法启动,报找不到主类或包不存在。

原因:Spring Boot的启动类扫描规则是:启动类所在包及其子包组件才会被扫描,如果你改了包名但没有同步更新所有import语句,或者启动类里的@SpringBootApplication还在旧的包路径下,都是这个错误。此外pom.xml里的<groupId>、<artifactId>以及resources下的配置文件里的包路径引用也要一起改。

解决:改包名用IDEA的Refactor功能,让IDE帮你全局重命名,而不是手动一个个改;改完检查InventoryApplication.java的包路径是不是和entity/service/controller这些包在同一级。如果项目里有用到type-aliases-package或mapper-locations,也要按com.xxx.inventory的路径同步修改。最稳妥的经验:拿到源码后第一周不要改项目名,先把功能跑通了,最后两三天再统一改名。

6. 把别人的Spring Boot进销存变成自己的毕设:两个动作让答辩加分

6.1 用“操作日志表”替换简单的增删改查记录

大部分进销存毕设源码里没有“操作日志”这个概念,你可以自己加一张operation_log表,专门记录用户登录、采购入库、销售出库、修改商品价格这类关键操作。用Spring Boot的AOP切面写一个注解,给Controller方法加上@OperationLog("采购入库"),切面在方法执行后自动把当前操作用户、操作时间、业务类型、请求参数写入日志表。这张表在论文里可以写成“系统安全管理模块”,演示时还能展示“谁在什么时间做了什么操作”,比光秃秃的CRUD有说服力得多。这个功能的代码量不大,但对答辩评分的提升是实打实的。

6.2 增加一个本系统没有的“库存预警”统计视图

进销存系统最值得加的新功能就是“库存预警”:当库存数量低于安全库存时,在首页顶部醒目提示缺货商品,并推送到商品列表。实现上只需要在inventory表的safe_quantity字段上做文章:写一个Mapper方法selectLowInventoryProducts(),查询条件就是quantity < safe_quantity OR quantity <= 0,然后在首页的Controller里调用它,把结果返回到前端渲染。做一个独立的“预警分析”页面,把缺货商品列表、预计采购金额、供应商联系电话放在一起展示。这个功能能让答辩老师直观感受到“你理解进销存业务的核心痛点”,而不是只会在框架里做数据表格。

结尾说一句我的习惯:每次拿到新的源码包,我都会先做三件事——跑通登录、看清application.yml、翻一遍schema.sql。别小看这三件事,它们决定你后续是花三天改造成自己的毕设,还是花三周和一堆莫名其妙的报错缠斗。如果这篇笔记能让你在开箱运行的时候少走一两个弯路,那这些文字就值得了。希望帮到你。

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

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

SpringBoot自定义logback日志配置实战:核心概念与完整方案

在SpringBoot项目里做自定义logback日志配置&#xff0c;几乎是每个后端开发迟早要面对的事情。很多人一开始觉得“SpringBoot不是自带日志吗&#xff0c;直接sout不行吗”&#xff0c;等到生产环境出了问题没法排查、日志文件把磁盘撑爆、或者想按业务维度把日志分开统计的时候…

作者头像 李华
网站建设 2026/10/2 18:20:21

智能车锥桶识别的嵌入式视觉闭环设计

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

作者头像 李华
网站建设 2026/10/2 18:19:50

零代码AI应用平台落地实践:从工作流编排到智能客服搭建

最近跟几个做SaaS的老朋友聊天&#xff0c;大家不约而同都在折腾同一件事——怎么把手里的AI能力包装成客户能直接用的产品。有的还在用最原始的方式接API、写前端、调prompt&#xff0c;开发周期按周算&#xff1b;有的已经换了思路&#xff0c;直接在零代码AI应用平台上搭&am…

作者头像 李华
网站建设 2026/10/2 18:19:21

微信内置浏览器抓包调试:ADB+Chrome DevTools远程调试实战指南

做微信内置浏览器的抓包调试&#xff0c;我前前后后折腾过不少次&#xff0c;踩过的坑比写出来的代码还多。先说说这件事的典型场景&#xff1a;你在开发H5页面&#xff0c;PC浏览器里一切正常&#xff0c;一到微信里打开就出问题——接口报错、白屏、数据不对&#xff0c;最要…

作者头像 李华
网站建设 2026/10/2 18:19:06

macOS下Git换行符警告CRLF/LF排查与.gitattributes规范化全攻略

1. 先看warning到底在说什么&#xff1a;换行符差异的前因后果在 macOS 上跑git add或git commit时&#xff0c;突然冒出一句&#xff1a;warning: CRLF will be replaced by LF in src/main.py. The file will have its original line endings in your working directory.第一…

作者头像 李华
网站建设 2026/10/2 18:18:45

Cursor 报 401 别慌:把 Base URL 改到 TaoToken 的排查清单

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

作者头像 李华