资讯详情

SSM+Vue智能卤菜销售平台:从业务建模到前后端联调实战

📅 2026/10/23 5:27:45 | 华诺云谱 👁 阅读
SSM+Vue智能卤菜销售平台:从业务建模到前后端联调实战
去年帮一位做卤味店的朋友做门店数字化改造时接触到一个很典型的题目智能卤菜销售平台技术选型被限定为SSM加Vue。拿到这类题目的人第一反应多半是“这不就是个增删改查的商城系统嘛”但真正做下去就会发现卤菜这个品类比其他零售项目更考验业务建模能力——保质期短、按斤计价、规格多变、午晚市高峰集中每一处都和常规的图书商城、服饰商城不一样。这篇博客就把我搭这套系统的完整思路记录下来从架构拆解、表结构设计到前后端联调的经典坑给正在做类似实训项目、毕业设计或者打算给自家门店做私域销售工具的人一份能直接落地的参考。1. 卤菜零售的场景痛点与平台定位1.1 卤菜不是标准品它和图书商城差在哪做这套系统之前我专门找了一家卤味店蹲了半个下午观察顾客怎么买、店员怎么称、老板怎么记账。第一件让我印象很深的事是顾客买卤菜不会说“我要一包卤牛肉”而是“半斤牛腱子多加点卤汁”偶尔还要补一句“辣度要微辣”。这个细节直接决定了后面的数据模型不能照搬标准电商模板。图书商城里的商品是典型的标准SKU一本书就是一本价格恒定库存按本计数用户加入购物车之后基本不存在“同一本书今天和明天价格不一样”的问题。卤菜完全不是这样。计量单位是克、份、只混着来同样一只鸭鸭翅和鸭掌是两个商品价格体系还不一样口味差异又衍生出五香、麻辣、酱香等多个版本同一道菜按半斤买和按一斤买折算单价还可能不同。这些约束在普通商城模板里根本没有对应的字段设计。库存逻辑也完全不同。图书卖完可以补货页面显示“采购中”没有任何问题卤菜是后厨当天定量卤制卖完就是卖完顾客晚到一步就买不到。平台需要在菜品上做一个“今日售罄”的标记让顾客不用白跑一趟。更进一步卤菜的最佳食用窗口就是一两天门店每天闭店前可能还要做临期折扣甚至直接下架处理这类运营动作在标准电商系统里很少被考虑。1.2 平台的角色单店和连锁门店的经营辅助工具既然业务如此特殊平台就不该做成大而全的B2C商城。反复权衡之后我定下来的定位是面向单店和少量连锁门店的私域销售工具核心服务三类人。顾客在手机上浏览当日菜品、加购、下单、选择到店自提或预约配送时间店员在后台维护今日菜品、价格、库存处理订单状态老板在数据页看销量排行、滞销菜品和每日营业额。围绕这三类角色整个平台的功能边界就清晰了C端展示与下单、B端后台管理、数据统计三块剩下的花哨功能一律不做。“智能”这两个字在这个平台里并不是指炫酷的算法。更实际的做法是两件事一是根据历史销量计算热销排行把热门菜自动放到首页靠前位置二是给库存设一个阈值当日生产量低于阈值时在后台给出预警提醒后厨追加生产。这两件事用简单的SQL聚合就能完成但对门店经营的指导价值非常直接。热销菜放在前面顾客点单效率会明显提升滞销菜预警后厨就能少做一点减少浪费。1.3 这个项目适合谁来做说句实在的如果你刚学完SSM正发愁怎么把Spring、SpringMVC、MyBatis和Vue串成一个完整的东西这个题目特别合适。它比“通用商城”多了一层业务设计难度又不会像大型平台那样复杂到失控。适合三类人第一类是正在找毕业设计题目的学生卤菜平台的业务故事完整答辩时有东西可讲第二类是刚工作不久、想练前后端分离项目的新人把登录、商品、购物车、订单、后台管理这套链路完整写一遍对SpringMVC的请求流转和MyBatis的SQL组织会有质的理解第三类是真的想给家里卤味店做个小程序的人这个项目跑通之后再包一层小程序壳就能直接试运营。2. SSM与Vue的职责边界这套架构为什么这样搭2.1 为什么选SSM而不是直接上Spring Boot近几年新项目基本都是Spring Boot起步但实训和毕设场景里SSM依然很常见。SSM是三个框架的组合Spring负责管理对象和事务SpringMVC负责接收和处理HTTP请求MyBatis负责把Java对象映射成SQL操作。它没有Spring Boot那种自动装配的便利需要自己写大量的XML或注解配置这恰恰是它的教学价值所在。用Spring Boot写一个接口往往只需要寥寥几个注解底层怎么初始化、DispatcherServlet怎么挂载、自动配置怎么生效都被框架隐藏了。用SSM你被迫理解Servlet容器和Spring容器是怎么通过监听器接起来的理解DispatcherServlet为什么能拦截所有请求理解事务代理是在哪个环节生效。这些理解对排查线上问题非常有帮助尤其是遇到请求进不来、事务没生效这类问题时懂SSM的人看一眼配置就知道卡在哪。当然我也得说句公道话如果是面向真实商业项目从零开发我更推荐Spring Boot。SSM的定位更像是“拆开玩具看内部结构”而Spring Boot是“高效组装”。这篇博客面向的是SSM的项目场景所以下文都按SSM来展开但所有业务建模的思路对Spring Boot同样适用。2.2 Vue在前端承担的工作前后端分离是这套架构的核心特征。Vue不再像JSP那样在服务端拼页面而是纯前端渲染后端只返回JSON数据。Vue在这套系统里主要负责四件事。路由组织是基础。页面分成顾客端和后台管理端两块顾客端有首页、菜品列表、菜品详情、购物车、订单中心后台管理端有菜品维护、订单处理、数据统计。用Vue Router做路径和组件的映射配合路由守卫控制登录跳转。组件化是用来复用UI的。导航栏、菜品卡片、购物车列表、状态标签这些都能抽成组件菜品卡片在首页和列表页复用后端管理里的表格也统一封装避免同一套HTML写好几遍。状态管理解决跨页面共享数据的问题。登录用户的身份信息、购物车角标数量这类数据不可能每个页面都向后端重新拉一次我用状态管理统一保存页面之间切换不需要重复请求。Axios封装是前后端通信的关键。所有请求走同一个实例统一设置请求头、统一处理401跳转、统一弹出错误提示。这一步不做后面接口一多错误处理会非常散乱。2.3 一次加购请求的完整路径理解这套架构最快的方式是完整追踪一次“加入购物车”的请求链路。用户在菜品详情页点击“加入购物车”浏览器的Vue实例通过Axios发起一个POST请求地址是/api/cart/add请求体里带的是菜品ID和数量。请求先到达Tomcat被DispatcherServlet接住它根据URL找到对应的Controller方法。Controller不直接操作数据库而是调用CartServiceService里先判断菜品是否存在、是否上架、库存是否够再调用CartMapper执行SQL。MyBatis把SQL结果映射成Java对象Service把结果包装成统一的Result响应结构Jackson把它序列化成JSON字符串沿着HTTP响应回到浏览器。Vue收到响应后更新购物车数据页面右上角的角标数字跟着变化。整个过程中前端完全不知道SQL长什么样后端也完全不知道页面长什么样双方只靠JSON格式的接口约定交流。这就是前后端分离和传统JSP开发最大的区别JSP模式前后端耦合在一起改一个页面样式可能要从服务端改到HTML分离开发则是两边独立演进只要接口不变前端随便重构后端随便优化。3. 从商品库到订单闭环核心模块的设计取舍3.1 菜品中心价格、规格与库存怎么建模菜品是卤菜平台的核心数据设计得好不好直接决定后面所有模块的复杂度。首先是价格字段。卤菜按斤计价但顾客下单时可能按半斤、按一份、按一只来买。我在菜品表里用了两个概念基础价是“每斤多少分”展示时再根据用户选择的规格折算成实际应付金额。价格用整数分存储不用浮点数这是做电商系统的基本功——浮点运算在金额计算上会有精度误差测试时看不出来订单多了之后对不上账就很麻烦。规格用单独的字段保存内容是字符串比如“半斤”“1斤”“一份3只”。为什么不拆成规格表卤菜门店的规格相对固定拆表反而让购物车和订单明细的关联查询变得复杂。用一个字段描述展示和下单都方便后台维护也省事。库存字段值得多说两句。卤菜的库存不是仓库里的成品库存而是“当日计划生产量”。每天早上店员在后台录入今天要卤多少斤牛肉、多少只鸡爪这个数字就是当日库存。顾客每下一单库存就减一份。卖完就自动标记售罄前端页面直接显示“今日售罄”顾客没法再加购。另外再配一个上下架开关今天不想卖的菜可以提前下架。售罄和上下架是两个独立状态不能混在一起上下架是店员主动控制售罄是库存减少触发的自动状态。3.2 购物车临时数据还是落库数据购物车有两种存法存浏览器本地localStorage或存后端数据库。很多初学者图省事选第一种我也试过后来发现它在订单闭环里会带来麻烦。方案优点缺点适用场景localStorage游客也能用前端零接口开发快换设备不同步结算时价格可能与后端不一致无法做库存预校验纯演示Demo后端购物车表多端同步结算时后端重新算价和库存数据可控需要登录才能用要多建一张表多写增删改查接口有真实下单流程的项目我最终选了后端购物车表。原因很直接结算时必须在后端重新计算价格、检查库存前端购物车只是一个展示镜像。如果把购物车放浏览器里价格和库存都以用户本地的数据为准用户加购的时候明明显示有货下单时后端才发现库存不足体验非常割裂。购物车落库后加购时就能做库存预校验数量超了直接拦截等于把问题提前到最早环节解决。购物车表本身很简单核心字段就是用户ID、菜品ID、数量再加一个创建时间。要注意的是同一个用户把同一道菜加两次的情况插入前先查询存在就更新数量而不是插入新记录否则购物车里会出现两行一模一样的菜。3.3 订单状态机从下单到提货订单模块是整个平台的灵魂我把它设计成一张清晰的状态机避免不同环节各写各的判断逻辑。基本状态有五个待支付、备餐中/待自提、已完成、已取消、已退款。状态含义触发动作待支付订单已创建尚未完成支付用户提交订单备餐中/待自提支付成功后厨开始制作支付成功回调已完成顾客已取货或已送达店员确认取货已取消用户主动取消或超时未支付用户操作/系统判断已退款售后流程完成退款审核通过下单时还会生成一个业务订单号用时间戳加随机数的方式拼接而不是数据库自增ID。自增ID会暴露平台每天有多少单而且后续对接支付平台时业务单号是幂等控制的关键标识不能随便让人猜到规律。库存释放是订单模块最容易漏掉的部分。用户下单后迟迟不支付库存就一直被占用真正想买的人反而买不到。我做了超时释放机制下单时给订单记录一个过期时间比如30分钟支付成功就把状态改成备餐中到时间还没支付在前端查询订单或后端定时任务扫单时把订单状态改成已取消同时把占用的库存返还回去。定时任务可以用简单的定时器框架每五分钟扫一次超时订单就行不用上复杂的消息队列。4. 数据库设计卤菜业务中那些容易被忽略的表关系4.1 核心表结构速览数据库设计直接决定代码好不好写。整个平台最后沉淀出六张核心表用户表、菜品分类表、菜品表、购物车表、订单表、订单明细表。管理员相关字段可以合并进用户表用角色区分也可以单独建一张管理员表看项目规模小项目建议合并省得权限逻辑绕来绕去。用户表的关键字段是用户名、密码、手机号、地址和创建时间。密码必须加密存储绝不能用明文这是所有项目的基本红线。菜品分类表很简单就是分类ID、分类名和排序号。菜品表在上文已经说过核心是分类ID、名称、价格整数分、单位、规格、当日库存、上下架状态、图片地址和备注。购物车表记录用户ID、菜品ID、数量。订单表记录用户ID、业务订单号、总金额、状态、自提时间、备注和创建时间。订单明细表则记录订单ID、菜品ID、菜品名称、价格、规格和数量。这里有个关键点订单明细里的菜品名称和价格是冗余字段不是通过联表现查菜品表得到的。原因下一节专门讲。4.2 订单明细为什么要做菜品快照很多人第一次设计订单表时会想订单明细只要存菜品ID就行了查询的时候联表把菜名和价格带出来。这个想法在演示阶段没问题但放到真实业务里会埋雷。菜品改价了怎么办今天卤牛肉卖68元一斤明天成本上涨改成78元历史订单却应该保持68元。如果订单明细不冗余价格联表查出来的就是78元之前的订单金额全错了。菜品改名、下架也同理一旦菜品被删掉联表查不到记录历史订单直接显示空白。订单是一个发生在过去时刻的业务事件它必须保留那个时刻的快照菜名是什么就是什么价格是多少就是多少。订单明细其实就是一张电子小票小票打印出来之后超市改价不会影响小票上的数字。所以在订单明细表里我稳定存储dish_name和price这两个冗余字段查询用户历史订单时优先展示明细表里的快照字段而不是去联菜品表取当前数据。这样设计虽然多占了一点存储空间但换来了历史数据的绝对稳定也让统计报表的口径保持一致。4.3 索引与查询避免接口越写越慢数据量小的时候怎么查都很快。但随着订单量增长慢查询会悄悄出现。我从一开始就给高频查询字段加了索引订单表按用户ID建索引方便“我的订单”列表查询订单表按状态建索引方便后台按状态筛选订单订单明细表按订单ID建索引菜品表按分类ID建索引方便分类页展示。另一个容易忽视的细节是订单号的唯一索引。业务订单号不允许重复要建唯一索引兜底防止并发情况下生成出重复单号。还有数值类型的比较金额和库存这类字段绝对不要用字符串存储否则排序和范围查询会走错索引性能差距非常明显。报表统计也有一点值得提优先在MySQL里用聚合函数算好结果Java代码只负责接收展示。比如计算热销TOP5一条GROUP BY dish_id ORDER BY SUM(quantity) DESC LIMIT 5就能搞定把结果封装返回给前端即可。习惯把所有数据捞到Java内存里再慢慢算订单量一大内存和响应时间都会出问题。5. 前后端联调跨域、鉴权与图片上传的三个经典坑5.1 跨域配置配了还是拿不到Session前后端分离项目第一个绕不开的坑就是跨域。前端开发服务器跑在5173端口后端Tomcat跑在8080端口浏览器看到端口不一致一律当成跨域请求。解决办法通常是在后端配置CORS允许指定来源访问、允许携带凭证。但这里有个特别隐蔽的问题很多人配置了Access-Control-Allow-Origin为*发现请求能发出去了后端却一直拿不到Session。原因是浏览器规定只要请求携带CookieAccess-Control-Allow-Origin就不能是通配符必须是具体的来源地址。我在项目里就踩过这个坑前端Axios必须设置withCredentials: true后端CORS配置里的allow-credentials也必须为true两者缺一个Session就丢。更省心的做法是不用Session改用自定义Token。用户登录成功后后端生成一个Token返回给前端前端存在localStorage里每次请求在Header中带上。这套方案天然避开跨域Cookie的种种限制前后端分离的场景下更干净。Token本身可以是一串随机值存在Redis里也可以用带签名和过期时间的结构看项目规模选择。我最后选了Token方案因为卤菜平台的用户量不大一个拦截器校验就能搞定不需要引入复杂的认证框架。5.2 拦截器统一校验登录态有了Token鉴权逻辑就可以集中放在SpringMVC拦截器里。我把接口分成三类完全放行的公共接口比如登录、注册、菜品列表、菜品详情必须登录的接口比如加购、查看购物车、提交订单、查看订单必须管理员身份的接口比如后台的菜品维护和订单处理。拦截器做的事情很清晰从请求Header里取Token取不到就返回401让前端跳登录页取到了校验有效期和签名有效就把用户信息放到请求上下文里放行无效也返回401。管理员接口再多一道判断校验当前用户的角色字段。前端配合的是Axios的响应拦截器。一旦收到401不管哪个接口触发的统一清除本地登录状态、跳转到登录页、提示“登录已过期”。这样用户操作中突然遇到鉴权失败体验也是连贯的。初次实现时容易漏掉的是拦截器里放行了POST登录接口之后还要确保OPTIONS预检请求也被放行否则跨域场景下浏览器预检失败前端会报奇怪的大错误。5.3 图片上传后404虚拟路径映射后台管理上传菜品图片文件明明保存成功了前端访问却返回404这个问题几乎每个做文件上传的人都会遇到一次。原因在于文件保存到了本地磁盘的某个目录比如D:/uploads/但Tomcat默认只认识自己工作目录下的静态资源外部磁盘目录不在它的管辖范围内。解决方法是做一层虚拟路径映射。SpringMVC里配置资源处理器把请求路径/uploads/**映射到本地磁盘目录file:D:/uploads/。配置完之后前端访问http://localhost:8080/uploads/xxx.jpg就能正常显示图片。部署到服务器时这个路径要改成服务器上的绝对路径同时注意目录权限。上传本身也有一些值得固化的细节。文件名不能用用户上传的原始名字很可能是中文、带空格甚至带路径符号的存在安全隐患。我在服务端用UUID重新生成文件名再拼接上原始文件的后缀。后缀必须有白名单校验只允许jpg、png、webp这些常见图片格式防止有人上传JSP或HTML文件。最后再限制一下文件大小几百KB的图片足够菜品展示用超过阈值直接拒绝省得磁盘被撑爆。6. 扩展思考从演示项目到可上线系统的增量路径6.1 先活着再智能基础闭环是地基不管“智能”的口号喊得多响先把商品、购物车、订单、后台管理这条闭环跑通再谈别的。很多项目死在中途都是因为一上来就想着加各种花哨功能结果连最基本的“用户下了一单后台能看到并改状态”都没做扎实。智能部分先从数据统计开始。菜品销量排行用上文说的SQL聚合就能实现前端用ECharts画一张柱状图老板打开数据页就能看到哪些菜卖得好。每日营业额折线图同理按天做分组汇总。再往前一步可以加滞销预警查询最近一周销量低于某个阈值的菜品在后台醒目位置提示建议下架或减少生产量。这几件事都不需要机器学习但已经能解决门店经营里最实际的问题。如果想显得更“智能”一点可以加一个简单的次日进货建议取过去七天每天各菜品的平均销量乘以一个波动系数生成建议生产量。这个逻辑用Java写十几行就能完成但在答辩或演示场景里它的说服力远比一个空泛的“人工智能推荐”强得多。6.2 支付、权限与部署的三个增量项从演示项目走向真实可用第一个增量是支付。演示阶段可以做一个模拟支付按钮点一下就视为支付成功。要对接真实支付得先接支付平台的沙箱环境重点理解回调验签和幂等处理支付成功平台会异步通知后端后端要验证签名、确认订单未处理过、更新状态。回调接口必须做幂等否则通知重发时订单状态会被重复更新。第二个增量是权限细化。小项目用拦截器校验角色就够了分支一多建议用RBAC模型角色、权限点分离。管理员、店员、顾客三种角色看到的菜单和能调用的接口都不一样。菜单级控制在前端做路由守卫接口级控制在后端拦截器做两层都加才安全。第三个增量是部署。部署方案可以很简单前端构建产物放到Nginx静态目录后端打war包放Tomcat数据库用MySQL。Nginx把/api/开头的请求反向代理到后端服务前端页面请求走Nginx静态文件。配置时注意后端接口的绝对路径和代理路径要对应常见问题是前端请求地址和后端Context路径对不上导致404。6.3 我做这套系统时觉得最值得借鉴的习惯代码之外的习惯有时候比技术本身更影响项目成败。第一个习惯是接口写完后先自测用测试工具把正常流程和异常流程都跑一遍确认返回的JSON结构符合约定再交给前端联调。第二个习惯是统一字段命名风格数据库字段用下划线Java实体用驼峰中间靠MyBatis的映射规则转换命名不一致会让Mapper里的映射配置写到手软。第三个习惯是统一日志格式在关键节点打印请求参数、业务处理结果、异常堆栈项目出了问题时好的日志能省下大量排查时间。另外有一条红线必须反复强调用户密码不能明文存储用加密算法加盐处理SQL语句全部用参数绑定方式不能拼接字符串否则等着被注入攻击上传文件做类型白名单前端展示用户输入时注意转义。这些安全细节看起来不起眼却是从学生项目走向商业系统的分水岭。这套系统做完之后我又用它给那家卤味店跑了两个星期的模拟数据把菜品、价格、库存、订单整个流程过了好几遍。期间改得最多的地方是库存逻辑——最初我把库存当成普通电商的成品库存来设计后来才发现后厨“当日生产量”才是正确的建模方式。这种从“通用模板思维”转向“具体业务思维”的过程正是做这类平台最有价值的收获。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑