恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Spring Boot水果管理系统:库存预警与订单事务全解析
首页
资讯中心
/
Spring Boot水果管理系统:库存预警与订单事务全解析
Spring Boot水果管理系统:库存预警与订单事务全解析
发布时间:2026/10/20 2:10:44
水果管理系统这个题目我见过太多次了也帮不少同学审过代码。它是计算机毕业设计里的“稳选题”业务链路刚好卡在一个很舒服的位置比图书管理复杂又比电商平台简单既有常规的增删改查又有库存预警、订单流转、销售统计这些能写进论文的亮点。基于Spring Boot的水果销售与库存管理系统本质上就是把水果店的进货、上架、售卖、盘点、结算这些日常动作搬到线上让老板打开页面就能看到今天卖了多少、还剩多少、哪些水果该补货了。这篇内容适合两类人一类是正在写毕业论文、想快速搞清楚怎么搭和怎么讲的毕业生另一类是拿完整项目练手、想把Spring Boot全家桶串起来的开发者。我按实际开发顺序来讲重点放在能直接落地的方案和踩过的坑上。1. 项目定位与整体设计思路1.1 先认清业务主线功能模块自然就出来了很多同学一上来就急着建工程、写登录页结果模块写到一半全乱套。我的习惯是先模拟水果店的一天早上进货把苹果、香蕉往货架摆登记成本和数量中午顾客来买选了几样水果结账晚上打烊前核对剩余库存算今天的营业额看看哪些水果快卖完要补货。这条线捋清楚系统的功能模块就顺理成章了。站在这个角度系统至少需要拆成这些模块系统登录与员工管理区分管理员和普通员工控制谁能进后台。水果分类与水果档案分类是商品的“货架”档案是商品的基础信息。采购进货模块每次进货登记数量、价格、供应商库存自动增加。库存台账与预警实时展示当前库存库存低于阈值时给出提示。销售订单模块选商品、加购物车、结算下单库存自动扣减。客户管理模块记录客户姓名、电话、积分为后续统计做铺垫。报表统计模块按天、按月汇总营业额做销售趋势图和商品销量排名。按这个思路拆答辩时导师问“为什么设计这个模块”你可以直接说它是业务里的哪一步而不是按框架功能硬凑。系统最终要解决的就是两个字台账。过去水果店用本子记账现在把账本变成数据库把人工算账变成系统自动算。1.2 技术选型这样定稳中带亮点技术栈不是越新越好关键是你能讲清楚。我做这类项目推荐一套组合Spring Boot做基础框架MyBatis-Plus做持久层MySQL存数据Thymeleaf结合Bootstrap做页面图表用ECharts。这个组合很现实理由如下。Spring Boot把Tomcat、依赖管理、配置都封装好了不用在环境上折腾太久。MyBatis-Plus能把单表增删改查压缩到几乎不写SQL省下来的时间去设计订单和库存这种核心业务。Thymeleaf加Bootstrap是前后端不分离的方案比Vue分离少写一层接口文档和跨域处理演示时也少几个翻车点。那是不是完全不能用Vue不是不能用而是加了之后工作量会翻倍答辩时还容易被打断追问“前端token过期怎么处理”如果准备不充分反而扣分。如果你想把技术亮点做足可以在认证上引入Spring Security或者在热点数据上引入Redis做缓存。但前提是你能把原理讲透否则就保持简单。毕业设计评分看的不是技术数量而是你对系统的理解深度。1.3 项目目录结构与开发推进节奏包结构建议按职责分清晰好解释com.example.fruit ├── controller // 控制层 ├── service // 业务层 ├── mapper // 数据访问层 ├── entity // 实体类 ├── config // 配置类如拦截器、资源映射 └── common // 统一返回结果、异常处理、工具类开发节奏建议按“登录到分类再到商品再到库存然后是进货和销售最后做统计和客户”的顺序推进。前几个模块是在练基本功中间两个是核心业务后面是加分项。先跑通一条主链路再补页面千万不要一上来就调图表图表永远是最后锦上添花的东西。2. 数据库设计先把表立住2.1 核心表的字段这样定数据库是整个系统里最重要的一层表结构歪了后面写再多代码都别扭。按角色和业务拆核心表一共七张管理员表、水果分类表、水果信息表、采购进货表、销售订单表、订单明细表、客户表。其中水果信息表里直接放库存数量和预警阈值。对于水果店铺这种单人作业场景库存和商品放同一张表就够了查询简单也够用。四张基础表的字段设计如下表名主要字段设计要点admin_userid, username, password, real_name, role, status, create_timeusername加唯一索引password存加盐后的密文role区分管理员与员工fruit_categoryid, name, sort_order, deleted, create_timename加唯一索引sort_order控制分类展示顺序fruitid, category_id, name, price, unit, stock, warning_stock, image, status, deleted, create_timecategory_id做普通索引stock和warning_stock是库存预警核心customerid, name, phone, points, remark, create_timephone可加普通索引points记录消费积分三张业务表的字段设计如下表名主要字段设计要点purchase_recordid, fruit_id, fruit_name, price, num, amount, supplier, purchase_date, remark, create_timefruit_id关联水果表amount由price乘num得出sale_orderid, order_no, customer_id, total_amount, pay_type, status, remark, create_timeorder_no加唯一索引status标记订单状态create_time加索引用于统计sale_order_itemid, order_id, fruit_id, fruit_name, price, num, amountorder_id加索引fruit_name冗余一份防止水果改名后历史订单显示异常订单明细冗余水果名称是有意为之。如果以后水果改名或者被删除历史订单仍然能显示当时的商品名这是企业应用里的通用做法答辩时提这个细节会很加分。2.2 三个容易想当然的设计细节第一金额字段必须用decimal不能用float或double。水果按斤卖价格可能到小数点后两位浮点数在累加时会出现0.30000000000000004这种误差。第二重量字段要用decimal(10,3)不是int因为可能出现0.5斤、1.25斤这种数量。第三逻辑删除要统一所有需要保留历史的表都加deleted字段。物理外键建议不要加。很多同学喜欢在表上建外键约束结果删除分类时提示有商品关联删除不了很尴尬。正确做法是不建物理外键在查询时用JOIN关联数据一致性通过代码保证。删除分类前先判断分类下有没有启用中的商品有就提示“该分类下存在商品不能删除”体验更好。索引不是越多越好但下面三处值得加分类表的name、水果表的category_id、订单表的create_time和order_no。这些是查询和统计的高频字段。2.3 先塞一份演示数据再开写建完表先别急着写业务把演示数据塞进去后面每一步都有东西可看。分类给四类苹果类、柑橘类、时令瓜果、热带水果。水果给十几个品种其中一两个设置成低于预警值方便演示预警功能。商品售价要有零头比如苹果8.5元每斤订单统计出来才真实。INSERT INTO fruit_category(name, sort_order) VALUES (苹果类, 1), (柑橘类, 2), (时令瓜果, 3), (热带水果, 4); INSERT INTO fruit(category_id, name, price, unit, stock, warning_stock, status) VALUES (1, 红富士, 8.50, 斤, 20.000, 5.000, 1), (2, 赣南脐橙, 6.80, 斤, 2.000, 5.000, 1), (4, 海南香蕉, 3.50, 斤, 30.000, 8.000, 1);上面第二条数据库存已经低于预警值登录系统后首页预警区域就能直接看到效果。建议再多插几条不同日期的销售订单图表统计时数据才不会空。3. 核心功能实现把下单和库存这条主链路跑通3.1 登录认证与拦截器5分钟搭完登录是系统第一道门。毕业设计用Session加拦截器足够实现简单不容易出错。先写一个登录拦截器判断Session里有没有当前用户没有就重定向到登录页。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(/login); return false; } return true; } }然后把它注册进配置类放行登录页和静态资源。Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /css/**, /js/**, /images/**); } }密码不能存明文至少做一层MD5加盐处理或者直接用BCrypt。答辩时被问到“密码怎么保证安全”能答出“数据库存的是哈希值不是原文登录时比对哈希结果”这个回答就够了。3.2 图片上传与资源映射避开丢图大坑水果档案里最好带图片不然列表页太素。图片上传最大的坑是把文件写进了项目源码目录重新部署后图片全丢。推荐把图片统一放到项目目录外的一个uploads文件夹再通过资源映射让页面访问。Override public void addResourceHandlers(ResourceHandlerRegistry registry) { String path System.getProperty(user.dir) /upload/; registry.addResourceHandler(/upload/**) .addResourceLocations(file: path); }注意路径要以斜杠结尾。上传文件时用UUID或者时间戳重命名文件名不要直接用用户上传的原始文件名避免中文文件名和路径分隔符引发的问题。页面显示时直接用“/upload/文件名”访问即可。3.3 库存台账与预警一条SQL把缺货清单带出来库存预警是毕业设计里最值得讲的功能之一实现起来并不复杂。水果表里已经有stock和warning_stock两个字段缺货就是库存小于等于预警值。列表查询里加一个过滤条件即可。SELECT id, name, price, unit, stock, warning_stock FROM fruit WHERE deleted 0 AND status 1 AND stock warning_stock ORDER BY stock;前端把预警状态的商品标红首页统计缺货数量。进货流程要做好事务采购进货表新增一条记录同时更新fruit表的stock字段stock等于原库存加本次进货数量。如果中间失败两个操作都要撤回。预警值设计成商品自带的字段而不是写死这样不同水果可以有不同的补货策略比如香蕉卖得快预警值设高榴莲卖得慢预警值设低。答辩时解释这个设计比简单写一个固定阈值更有说服力。3.4 销售下单与库存扣减这段代码是项目亮点整个系统最拿得出手的代码就是创建订单这一段。购物车结算时后端要做的事情看着不多但步骤必须严格生成唯一订单号插入订单主表循环插入每条订单明细逐条扣减库存这四步必须在同一个事务里执行否则会出现库存扣了订单没生成或者订单生成了库存没扣的情况。Transactional(rollbackFor Exception.class) public SaleOrder createOrder(OrderCreateDTO dto) { String orderNo SO System.currentTimeMillis() RandomUtil.randomNumbers(4); SaleOrder order new SaleOrder(); order.setOrderNo(orderNo); order.setCustomerId(dto.getCustomerId()); order.setStatus(0); saleOrderMapper.insert(order); for (OrderItemDTO item : dto.getItems()) { int rows fruitMapper.deductStock(item.getFruitId(), item.getNum()); if (rows 0) { throw new RuntimeException(库存不足 item.getFruitName()); } SaleOrderItem detail new SaleOrderItem(); detail.setOrderId(order.getId()); detail.setFruitId(item.getFruitId()); detail.setFruitName(item.getFruitName()); detail.setPrice(item.getPrice()); detail.setNum(item.getNum()); detail.setAmount(item.getPrice().multiply(item.getNum())); saleOrderItemMapper.insert(detail); } return order; }库存扣减的SQL是防超卖的关键。Update(UPDATE fruit SET stock stock - #{num} WHERE id #{id} AND stock #{num}) int deductStock(Param(id) Integer id, Param(num) BigDecimal num);这行SQL的逻辑在于数据库更新时锁住这一行并且要求库存大于等于购买数量才允许更新。受影响行数为0就说明库存不够直接抛出异常让事务回滚。这比先查库存再判断的写法安全得多并发场景下也不容易超卖。3.5 销售统计与图表把数据变成经营看板统计模块最能体现系统价值。让老板看到今天卖了多少、最近七天的走势、哪个水果卖得好比表格直观得多。报表数据不用单独建表全是SQL聚合查询。SELECT DATE_FORMAT(create_time, %Y-%m-%d) AS day, COUNT(*) AS order_count, SUM(total_amount) AS total_amount FROM sale_order WHERE status IN (1, 2) AND create_time BETWEEN #{start} AND #{end} GROUP BY day ORDER BY day;如果订单状态里0代表待支付、1代表已支付、2代表已完成、3代表已取消统计营业额时要把已取消的过滤掉。分类占比按sale_order_item里的fruit_name分组求和销量Top5按照每个商品的销售数量排序。前端接ECharts后端返回List结构页面用Ajax拉数据再配置option。时间参数要统一格式。MySQL的create_time是datetime类型Java传参时注意LocalDateTime和字符串的转换否则很容易查不到数据。4. 常见问题与避坑技巧4.1 事务不生效最常见也最致命事务失效是这类项目里出现频率最高的问题而且症状隐蔽。一个典型场景是调用创建订单方法里面库存扣减失败但程序没有报错事务也没有回滚最后订单生成了库存却没扣。原因通常是这几个。第一类内部方法自调用。同一个类里的方法直接调用本类方法不会经过Spring代理Transactional注解不生效。解决方法把下单逻辑放到另一个类里或者在当前类注入自己的Bean再调用。第二异常被try-catch吞掉。事务只在抛出运行时异常时回滚你在方法内部catch住异常不重新抛出来事务就认为一切正常。第三方法不是public。Spring默认只能代理public方法私有方法上的事务注解无效。第四数据库引擎不是InnoDB。MyISAM引擎不支持事务检查数据库表引擎配置。排查时最笨但最有效的办法在事务方法里故意抛一个运行时异常看数据库是否回滚。能回滚说明配置正常不能就要从上面四个原因逐一查。4.2 日期时间与格式一串小问题全在这接口返回的LocalDateTime字段前端经常显示成一段数字或者时间少8小时。解决办法是在字段上加格式化注解JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8) private LocalDateTime createTime;MySQL连接串记得加上serverTimezoneAsia/Shanghai否则在部分环境下会出现时区偏移。写查询SQL时日期比较用BETWEEN要谨慎处理边界比如统计某天数据最好的方式是create_time大于等于当天零点小于第二天零点而不是单纯套字符串。4.3 端口占用、图片丢失与删除受限的应对端口占用是个高频问题。修改application.yml里的server.port换一个端口就行不要反复强行kill进程。图片上传后访问404检查addResourceLocations路径结尾有没有斜杠以及上传的文件名是否带异常字符。删除分类提示有外键关联多半是建表时加了物理外键方案是去掉物理外键改用逻辑删除删除前先查分类下是否存在商品。金额计算出现精度问题把float和double换成BigDecimal。数据库里decimal字段映射到Java端用BigDecimal接收业务计算也用BigDecimal不要用字符串拼接。5. 部署、测试与答辩通关清单5.1 打包部署与数据备份到了验收阶段把项目打成jar丢到服务器上是最省事的。本地执行打包命令mvn clean package -DskipTests服务器装好JDK和MySQL。Spring Boot 2.x对应JDK8Spring Boot 3.x需要JDK17版本注意对应上。上传jar和数据库脚本启动命令nohup java -jar fruit-system.jar log.log 21 数据库备份用mysqldump定时任务每天凌晨备份一次保留最近一周的备份文件。答辩前至少完整部署演练一遍不要等到那天才现找问题。5.2 测试用例设计直接写进论文附录系统做完一定要有一份测试记录论文里能直接用。按模块写用例每个用例包含操作步骤、预期结果、实际结果。这份测试表建议放在论文附录纸质档案和电子版各留一份。模块测试场景操作步骤预期结果登录密码错误输入错误密码登录提示用户名或密码错误不进入后台分类删除有商品的分类删除苹果分类提示该分类下存在商品不能删除库存预警触发将某水果库存改为低于预警值首页预警列表出现该商品订单下单减库存购买3斤苹果后查看库存苹果库存对应减少3斤统计营业额过滤查询包含已取消订单的数据营业额不包含取消订单这份案例表不用太长覆盖核心功能即可。每个模块选两个典型场景整份表格控制在两页以内。5.3 答辩演示的建议顺序答辩时间通常很短演示别从登录页开始一个功能一个功能点。建议顺序先讲业务背景一句话带出系统解决的核心痛点然后演示库存预警这是视觉冲击力最强的功能列表标红很醒目接着演示从下单到库存扣减的完整流程顺势引出事务和防超卖SQL的设计最后切到统计图表证明系统能对经营真正有帮助。登录、员工管理这些基础模块一句话带过就行。技术原理问题要提前准备为什么库存和商品放一张表为什么订单明细要冗余商品名称为什么库存扣减用条件更新而不是先查后改。这三个问题都能答顺项目答辩基本就稳了。最后说句实在话这类管理系统真正拉开差距的不是界面多漂亮而是库存和订单这条主链路有没有做严谨。把表结构建清楚、事务写对、库存扣减的条件更新用上你的项目就不只是“能跑”而是“能讲”。选了这个题目的同学按这个顺序推一遍心里基本就有底了。