ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于SpringBoot的无人售货系统:从架构设计到库存一致性实现

基于SpringBoot的无人售货系统:从架构设计到库存一致性实现 我有相当长一段时间都在帮人参谋计算机毕业设计的方向见到最多的选题就是各种“管理系统”——图书、超市、仓库、停车场……说句实话这些题目不是不好而是太同质化了。你答辩的时候往台上一站老师说“下一个”你连展示自己的机会都没有。相比之下“基于SpringBoot的无人售货系统”这个题目我第一眼看到就觉得“这题选得聪明”。它既有传统管理系统的基础增删改查又集成了自动售卖、库存联动、订单支付这些贴近真实商业场景的业务逻辑难度梯度合理技术覆盖面广而且“无人值守”“智能零售终端”这些关键词天然自带亮点。不管你是想拿一个高分的毕设还是想借这个项目在简历上多写一行有分量的经历它都撑得住。这篇内容我就以这套系统为例把从选题拆解、技术选型、数据库设计到核心业务实现的完整思路走一遍。重点讲清楚“为什么这么做”而不只是“做了什么”因为后者在答辩时一问就露馅前者才是你真正能带走的东西。1. 项目定位与整体设计思路1.1 这到底是个什么系统无人售货系统顾名思义就是不需要售货员在场消费者自己完成“选品—下单—支付—取货”全流程的设备。放在校园、写字楼、地铁站这些场景里它就是一台自动售货机但系统本身并不关心硬件长什么样它关心的是背后那套业务逻辑商品数据从哪来、库存怎么扣、订单如何生成、支付结果怎么确认、异常情况怎么处理。很多同学拿到这个题目第一反应是“我要控制硬件”——这是误解。毕业设计阶段的重点应当是业务系统也就是管理端给运营人员用和用户端给消费者用这两块。硬件对接比如扫码后弹开货道通常会抽象成一个接口真实项目里通过MQTT或HTTP长连接下发指令毕设阶段做到“接口预留”即可。这样既控制住了复杂度又能把业务逻辑做深做好。所以本质上这个系统的核心价值在于把一次自动售卖行为完整地数字化、流程化并保证数据在各个环节的一致性和可追溯性。1.2 为什么选择SpringBoot而不是SSH或者SpringCloud这是答辩时大概率会被问到的问题得提前想清楚。SpringBoot的优势在于“约定大于配置”内置Tomcat、自动装配、起步依赖这些机制极大降低了搭建成本。对毕设来说这意味着一周之内就能把框架骨架跑起来把时间省出来打磨业务细节——而不是像早期SSHStrutsSpringHibernate那样光配置XML就耗掉三天。那为什么不直接用Spring Cloud微服务呢因为没那个必要。无人售货系统即便放到真实场景初期也就是几十台设备、几万用户的量级单体应用完全撑得住。多机部署、服务治理、配置中心这些属于“以后再说”的问题。在企业里过度设计同样是忌讳能用一个SpringBoot搞定的事硬拆成五个服务只会给自己挖坑。这套系统我建议只保留一个SpringBoot主服务外加一个MySQL数据库、一个Redis缓存用来做热点数据和分布式锁、可能再加一个WebSocket用于前端页面实时刷新订单状态。没了就这么多。简单、够用、好讲。1.3 技术栈选型与版本踩坑记录为了照顾很多用校园网、下载速度慢、还经常遇到镜像问题的同学我这里直接给一套经过验证的版本组合组件推荐版本说明JDK1.8稳定兼容性好部分老电脑和学校机房也能跑SpringBoot2.7.182.x最后一个版本网上的教程批量适配MyBatis-Plus3.5.x简化CRUD分页插件方便MySQL5.7 或 8.05.7更稳8.0性能更好都行Redis6.x / 7.x做缓存和分布式锁Maven3.8依赖管理前端Vue 2 或 原生HTMLJS取决于你前端底子不用纠结这里有个真实的坑要提醒很多人一上来就装最新的SpringBoot 3.x结果JDK必须升级到17然后发现很多第三方依赖还没适配网上搜到的教程全是旧版本的东西越折腾越崩溃。毕设千万不要追新版本够用就好2.7.18就是一个黄金版本。2. 需求拆解与数据库建模2.1 核心角色与用例梳理做系统之前先想清楚谁在用。这套系统有四个角色消费者用户浏览商品、下单购买、查看订单、申请售后。运营人员管理员管理商品、管理货道、补货、查看统计数据。设备售货终端上报货道状态、接收出货指令。在毕设中它通常模拟为后台的一个“虚拟设备”概念。系统本身自动完成库存扣减、价格计算、订单状态流转。从这个角度出发系统的功能模块就很清晰了用户模块、商品模块、货道模块、订单模块、支付模块、库存模块、统计模块。每一个模块之间都有紧密的数据关联这种关联关系在数据库设计阶段就要理清楚。2.2 数据库表设计的核心思路好的表设计是“改不出来的”只能在一开始就想清楚。下面这几张表我认为是这套系统最核心的部分。商品表product存商品的通用信息比如名称、图片、分类、价格。要注意的是价格字段必须用decimal不能用float这是金融数据的基本常识。float有精度问题一分钱对不上账到答辩时会非常尴尬。CREATE TABLE product ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 商品ID, name varchar(100) NOT NULL COMMENT 商品名称, category_id bigint(20) DEFAULT NULL COMMENT 分类ID, image_url varchar(255) DEFAULT NULL COMMENT 图片地址, price decimal(10,2) NOT NULL COMMENT 销售单价, status tinyint(4) DEFAULT 1 COMMENT 状态1上架0下架, create_time datetime DEFAULT NULL, update_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4;货道表channel是售货系统的特色表。自动售货机的物理结构决定了它有很多“格子”或“货道”每个货道上放着一件商品。所以商品与货道是多对一的关系一个商品可以占多个货道一个货道只能放一种商品简化处理。货道表里最关键的是库存和位置编码CREATE TABLE channel ( id bigint(20) NOT NULL AUTO_INCREMENT, channel_code varchar(20) NOT NULL COMMENT 货道编码如A01, product_id bigint(20) DEFAULT NULL COMMENT 当前放置的商品, capacity int(11) DEFAULT 10 COMMENT 货道容量, stock int(11) DEFAULT 0 COMMENT 当前库存, machine_id bigint(20) DEFAULT NULL COMMENT 所属设备ID, status tinyint(4) DEFAULT 1 COMMENT 状态1可用0禁用, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;订单表orders是整个系统的灵魂。注意“订单表”这个名字不要用order因为它是SQL保留字容易引出一堆诡异问题。订单表的设计有几个细节值得留意order_no订单编号用雪花算法生成保证全局唯一同时设计成字符串。total_amount和pay_amount前者是商品原价后者是实际支付的金额如果后续有优惠活动就用得上了。status订单状态用整数枚举0待支付、1已支付/待出货、2已完成、3已取消、4售后中不要用字符串。库存流水表stock_log这张表非常重要我强烈建议加上。它不是简单地记录当前库存是多少而是记录每一次库存变动的来龙去脉。比如用户买走一瓶水库存从100变成99这张流水表会记下“订单20240101001关联的货道A01库存-1操作类型销售”。一旦后续对不上账你顺着流水查五分钟就能定位问题。没有这张表出了bug只能干瞪眼。2.3 一个容易被忽视但加分的设计关联关系的梳理除了上面讲的四张核心表还需要设备表machine、用户表user)、管理员表admin和分类表category。对于设备表我建议预留一个字段叫location也就是设备摆放地点。这看起来不起眼但后续的“按区域统计销售额”就靠它简历上可以写成“实现了基于地理位置的运营分析能力”价值立刻不一样。表之间的关联关系可以用一句话概括用户下单订单对应多个订单明细每个明细关联到一条货道记录货道关联到属于某台设备的商品。只要在脑中有这张关系图后面写SQL就不会乱。3. 核心业务逻辑与代码实现方案3.1 购买主流程从前端点击到库存扣减整个系统最核心的流程就是“购买”。这个流程我建议在开发之前先画一条流程图文字版然后在代码里严格按照这个流程走用户扫码进入H5页面选择“中瓶可乐3元”点击“购买”后端接收请求根据设备ID和货道编号查询货道信息校验商品状态货道是否可用库存是否大于0生成待支付订单返还给前端一个订单ID同时给一个支付二维码或者调起微信/支付宝支付用户完成支付支付平台回调后端接口后端确认支付成功后调用“出货接口”在模拟环境中直接更新货道库存-1订单状态更新为“已完成”前端通过轮询或WebSocket收到结果展示“出货成功请取走商品”。这个流程看起来简单坑却埋在细节里。第一个坑并发场景下库存扣多了。A和B同时买最后一瓶水两个请求都查到库存1都认为可以下单结果都扣减成功库存变成-1。这就是经典的“超卖”问题。解决方案有两种悲观锁和乐观锁。对于这个场景我推荐使用Redis分布式锁 库存预扣减的组合。// 伪代码Redis分布式锁控制并发出货 String lockKey sale:lock: channelId; boolean locked redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 5, TimeUnit.SECONDS); if (!locked) { throw new BizException(系统繁忙请稍后再试); } try { // 查库存、生成订单、扣库存 int stock channelService.getStock(channelId); if (stock 0) { throw new BizException(商品已售罄); } channelService.deductStock(channelId, 1); orderService.createOrder(...); } finally { redisTemplate.delete(lockKey); }第二个坑支付回调的幂等性。微信/支付宝的回调在极端情况下会重复通知你“支付成功了”。如果你的代码不处理这种情况用户付一次钱系统给他出两次货。解决思路很朴素回调处理之前先查订单状态只有“待支付”状态才允许更新为“已支付”。if (!OrderStatus.PENDING_PAY.equals(order.getStatus())) { // 已经处理过该回调直接返回成功不再执行出货逻辑 return success; }这两个细节在答辩时被追问的概率极高能讲清楚就说明你真的理解了业务系统设计的关键。3.2 库存管理让数据和实物始终对得上库存管理分两个层面一个是进销存视角即运营人员日常怎么管理库存另一个是一致性视角即系统在极端情况下如何保证库存数据准确。从进销存视角看运营人员要能做这些事给某个货道补货操作商品数量写入流水表记录“补货”类型对商品进行上下架操作多个货道同时不可售盘点库存操作把某个货道的实际库存改成某个值写入流水表记录“盘点调整”从一致性视角看要能做到每笔订单都有据可查。推荐的做法是任何库存变动都必须先写流水再改库存这两步要放在一个事务里要么都成功要么都失败。Transactional(rollbackFor Exception.class) public void deductStock(Long channelId, Integer count, String orderNo) { Channel channel channelMapper.selectByIdForUpdate(channelId); if (channel.getStock() count) { throw new BizException(库存不足); } channel.setStock(channel.getStock() - count); channelMapper.updateById(channel); // 写流水 StockLog log new StockLog(); log.setChannelId(channelId); log.setChangeType(SALE); log.setChangeCount(-count); log.setOrderNo(orderNo); stockLogMapper.insert(log); }这里我用了selectByIdForUpdate也就是数据库行锁。配合上面的Redis锁形成“Redis拦截并发请求 DB兜底保证不超卖”的双保险。这套方案在企业里也是常用的直接放进简历的“技术亮点”里完全站得住脚。3.3 管理端一个管理系统该有的样子无人售货系统的管理端和常规管理系统的区别不大但要注意几个功能模块别漏掉补货模块可以做成“按设备查看各货道库存”低于安全库存的货道标红运营人员按提示补货。这个“安全库存预警”是一个很好的加分项实现起来也不难查询时加一个条件stock threshold即可。销售统计模块至少要有三个维度的统计报表按时间日/周/月、按商品哪个卖得好、按设备哪台机器营收高。用MyBatis-Plus的聚合查询配合一个简单的ECharts前端图表就能做出不错的效果。退款/售后模块消费者会点“申请退款”运营人员在管理端处理。这个功能看似简单但涉及财务操作要谨慎设计状态流转比如“申请中→同意退款→原路退回→订单关闭”每一步都要有操作记录。这块做出来管理的完整度一下子就上去了。3.4 用户端H5页面就行别把精力花在花里胡哨的APP上用户端的核心诉求是“快”。打开页面、看到商品、点击购买、扫码支付、取货走人这个链路中任何一步超过三秒都是糟糕体验。所以我不建议你用原生安卓或iOS开发用户端做一个适配手机浏览器的H5页面即可。好处很明显不用安装扫码即用更贴近真实无人售货场景一套代码Android和iOS都能访问开发成本低精力可以留给后端业务逻辑。页面结构也很简单首页商品列表按分类筛选、商品详情弹窗展示价格/库存、确认下单页选择货道、支付页展示二维码、订单结果页成功/失败/取货提示。这个样子就非常完整了。4. 开发顺序、进度安排与常见坑4.1 推荐开发顺序从骨架到肉很多同学做毕设失败不是因为能力不够而是因为不知道从哪里下手东写一块西写一块最后全都乱套。我推荐这样的开发顺序第一阶段1~2周搭环境、跑通骨架。创建SpringBoot项目配置好MyBatis-Plus、MySQL连接、Redis实现一个简单的用户登录接口。目标是让整个链路动起来。第二阶段3~4周完成核心业务。按照“商品模块→货道模块→订单模块→支付回调→库存模块”的顺序一鼓作气把核心流程做完。这个阶段是最重要的也是写代码时间最长的。第三阶段第5周完善管理端和用户端。后端完成了前端就是“套页面调接口”的体力活儿重点放在数据呈现和交互流畅度上。第四阶段第6周打磨细节、写论文、做演示视频。包括异常处理、参数校验、界面美化、文档撰写、测试用例补充、演示流程彩排。4.2 那些年我们踩过的坑毕设专属把常见的坑集中写一下每一个都是我见过学生踩过的版本坑SpringBoot 3.x JDK 17 的组合会把你的配置时间从几小时拉长到几天。别追新用2.7.18 JDK 1.8网上随便搜都有教程适配。时区坑MySQL的时区设置错误会导致插入的时间数据少了8小时。连接串里一定要带serverTimezoneAsia/Shanghai。Lombok坑在很多老版本IDE里Lombok插件没装好会导致代码怎么也编译不过。遇到问题先检查IDE的插件别死磕代码。跨域坑前端和后端端口不同比如前端8080后端9090必须配置跨域过滤器否则浏览器里调接口全是CORS报错。这个在开发第一天就配置好别拖到联调阶段。参考文献坑最后写论文的时候参考文献不要全编至少找两三篇真正看过的中文核心期刊或硕士论文来引用这是论文能过查重和盲审的基础。4.3 “虚拟设备”的模拟方案前面留着硬件对接这个口子没展开这里说清楚。毕设阶段不需要真实硬件但需要在系统里模拟一个设备端。最简单的做法是在后端写一个定时任务随机生成“某设备某货道库存减少”的事件模拟真实售卖行为。这样做的好处是管理端的统计报表有数据可看库存预警功能也有触发场景。更“高级”一点的做法是做一个设备管理页面给每台虚拟设备做一个操作面板管理员可以手动让某台设备“出货”或者“上报故障”。这样演示的时候效果非常好——你可以在答辩现场演示“设备故障→管理员收到报警→禁用该货道→用户端该商品变为不可售”的完整闭环。这一套逻辑通下来答辩老师很难不给高分。5. 功能清单、架构一览与进阶扩展方向5.1 完整功能清单把系统的功能模块整理成一张表方便你核对开发进度也方便你写开题报告和论文目录模块功能点角色用户端商品浏览、分类筛选、下单、扫码支付、订单查询消费者订单中心订单生成、状态流转、支付回调处理、超时取消系统商品管理商品新增/编辑/上下架、分类管理管理员货道管理货道绑定商品、库存查询、补货、盘点管理员设备管理设备增删改查、地理位置维护、状态监控管理员统计报表销售趋势、商品排行、设备营收管理员售后模块退款申请、退款审核、订单关闭双端系统管理管理员账号、权限控制、操作日志管理员5.2 进阶扩展让项目从80分到95分基础功能全部完成大概能拿个80分的水平。如果你还有余力下面这几个方向能显著加分方向一多设备支持与远程运维。把“单机版”扩展成“多设备版”一台售货机就是一条数据记录管理端可以远程查看任意一台设备的状态。这更接近真实商业系统答辩时也可以直接说“设计上考虑了规模化部署”。方向二推荐算法简单版。根据历史订单数据给用户推荐可能喜欢的商品。不需要多复杂基于商品类别的协同过滤或者最简单的“购买最多的商品”推荐就行。但注意推荐一定要有数据支撑不是写死了“推荐可乐”而是根据统计动态计算。方向三库存预测与自动补货提醒。根据过去两周的销售数据预测接下来一周的销量低库存时自动生成“建议补货清单”。这块用线性回归或者简单的移动平均就能实现但效果看起来非常“聪明”。方向四小程序版用户端。如果你前端能力还凑合把H5用户端改成微信小程序体验会更好也更贴近真实无人零售的落地形态。5.3 整体架构与亮点提炼这里给一套可以直接写进论文的架构描述系统采用前后端分离架构后端基于SpringBoot 2.7.18构建使用MyBatis-Plus作为ORM框架MySQL存储业务数据Redis承担缓存和分布式锁职责。前端管理端采用Vue Element-UI用户端采用H5响应式页面。系统按业务划分为用户、商品、货道、订单、支付、库存、统计七大模块实现了从用户扫码下单、在线支付、自动出货到库存联动扣减和运营统计分析的全链路闭环管理。这段文字在论文里叫“系统架构设计”在答辩PPT里叫“项目亮点”在简历里叫“项目描述”。反复打磨这段描述让每一句话都有对应的代码实现支撑。真正高分的毕业设计不是功能最多的那个而是“讲得清楚为什么这么设计”的那个。再说一个个人经验答辩之前一定把自己当作客户完整地走一遍“扫码→下单→支付→取货→查订单”的流程并且把中间可能出现的异常情况库存不足、重复支付、网络超时都尝试一遍。能把异常场景处理得明明白白比多写十个普通接口更能体现你的工程素养。这一点平时开发中体会可能不深但真正到答辩台上老师的几个追问就会让你立刻明白准备充分的价值。
返回列表