ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringBoot+Vue+MySQL网上点餐系统设计与数据库实践

SpringBoot+Vue+MySQL网上点餐系统设计与数据库实践

1. 毕设选题解析:网上点餐平台到底在做什么

1.1 这个系统解决了什么问题

每年带毕设,我都会看到有人把点餐系统做成 JSP 时代那种单体页面,所有代码堆在 Servlet 里,前端套一个 Bootstrap 模板就交差。这种项目不是不能过,但答辩时很难讲出东西。SpringBoot + Vue + MySQL 这套组合做网上点餐系统,才是当下最能体现完整工程能力的配置之一。它覆盖了从数据库建模、后端接口设计、前端交互到部署上线的完整链路,而且业务逻辑足够清晰,评委一听就懂。

先把这个系统的本质说透。网上点餐系统要解决的,其实就是传统餐饮场景里三件事:顾客怎么快速看菜下单,商家怎么高效处理订单,双方怎么确认状态。落到系统里,核心流程是:用户打开首页看到分类和菜品,加入购物车,批量生成订单,模拟支付后商家端收到新订单,接单、完成,用户端实时看到状态变化。听起来不复杂,但把它做成一个能跑通前后端、有事务保证、有权限控制的成品,工作量刚好卡在一个毕业设计的合理范围里。

这个题目最聪明的地方在于,它把电商系统的核心骨架都包含了:商品展示、购物车、订单、状态流转、后台管理。只是把商品换成了菜品。一旦你把这个系统吃透,外卖平台、生鲜配送、校园二手交易这类题目,本质都是同一套东西换皮。所以选这个题,不只是为了交差,更是给以后做更复杂的系统打底子。

1.2 系统角色与功能边界

网上点餐平台的用户角色一般分成两类:顾客端和商家管理端。这里要特别注意,不要把角色设计得过于复杂。

顾客端做的是日常点餐动作:浏览菜品、按分类筛选、把菜加入购物车、修改数量、提交订单、支付(毕设里通常是模拟支付)、查看自己的历史订单和状态。商家端做的是经营动作:维护菜品信息(上架、下架、改价格、改图片)、管理菜品种类、查看所有订单、对订单进行接单/完成操作,还可以顺便看看哪些菜卖得好。

很多同学会忍不住想加一堆功能:会员积分、优惠券、配送员定位、实时聊天。我建议在毕设阶段控制住这种冲动。功能越多,意味着测试用例越多、答辩要解释的边界越多、出 bug 的概率越高。系统边界清晰比功能堆叠重要得多。评委更看重的是你把你承诺的功能做扎实了,而不是你画了一个五彩斑斓的大饼。

1.3 为什么这个题目每年都有人选

网上点餐系统在毕业设计里是常青树,原因很实在。第一,业务场景贴近生活,需求分析容易写,随便一个同学都点过外卖,理解成本极低。第二,技术栈可以做到很主流,SpringBoot 做后端接口、Vue 做前端页面、MySQL 存数据,这就是目前中小型项目最标准的组合,写在简历上不丢人。第三,工作量弹性大,基础版可以做简单的 CRUD,进阶版可以加权限、事务、拦截器、状态机、数据统计,想深入一点的同学完全有机会展示自己。

而且这个题目特别适合前后端分离的架构。因为点餐本身就是一个多端交互的过程,前端负责展示和交互,后端负责业务逻辑和数据,天然的分离场景。不像图书管理这种题目,硬套前后端分离反而显得刻意。

2. 技术选型背后的取舍

2.1 为什么后端选 SpringBoot 而不是 SSM 或 Spring Cloud

后端框架的选择上,很多同学纠结是 SpringBoot 还是传统的 SSM。我的建议很直接:除非你们学校有硬性要求,否则直接用 SpringBoot。

SpringBoot 的核心价值在于自动配置。以前 SSM 写一个项目,要先配置 web.xml、Spring 配置文件、MyBatis 配置文件,还要处理各种 jar 包版本冲突,光把环境跑起来就要折腾一两天。SpringBoot 把这些默认值都给你配好了,内嵌 Tomcat,一个 main 方法直接启动,把精力集中在业务代码而不是配置地狱里。这对毕设来说太关键了,毕竟你的时间是有限的,应该花在订单状态流转、购物车事务这种真正有技术含量的地方。

那为什么不选 Spring Cloud 微服务?我见过有同学为了显得高级,硬把一个点餐系统拆成用户服务、订单服务、菜品服务三个微服务,还引入了注册中心、网关、分布式事务。最后连自己都说不清楚为什么一个订单要跨三个服务。毕设讲究的是逻辑自洽,单体应用加一个合理分层,已经完全够用。微服务不是不能用,但你必须有充分的理由,并且能扛住评委关于分布式复杂度的追问。单纯为了炫技,往往得不偿失。

2.2 为什么前端选 Vue 而不是其他框架

前端这一层,Vue 是目前国内 Java 后端同学最容易上手的选择。它的学习曲线相对平缓,模板语法直观,而且中文资料极其丰富。React 当然也很好,但如果你之前没有 React 基础,突击毕设的时间成本会更高。

Vue 的核心优势是组件化。以前用 JSP 写页面,一个页面里混着 HTML、Java 代码、JS 脚本,改一个样式可能要翻几百行。Vue 把页面拆成独立的组件,比如菜品卡片是一个组件、购物车抽屉是一个组件、订单列表是一个组件,各自的样式和逻辑封装在一起,维护起来非常舒服。配合 Vue Router 做页面跳转、Pinia 或 Vuex 做状态管理,整个前端项目的结构可以做到和后端一样清晰。

还有一个容易忽视的点:Vue 项目通过 Axios 和后端交互,天然就是异步的。这意味着用户加购物车、提交订单的时候页面不会整页刷新,用户体验接近真实的商业应用,这在答辩演示的时候是很加分的。你给评委展示一个流畅的、无刷新的点餐过程,比你嘴上说一百句"用户体验好"都管用。

2.3 MySQL 与 MyBatis Plus:要省事也要可控

数据库选 MySQL 没什么争议,免费、稳定、招人喜欢,几乎所有学校都教这个。持久层框架我有两个建议方向,一个是用 Spring Boot 官方推荐的 Spring Data JPA,另一个是 MyBatis Plus。我更倾向于 MyBatis Plus。

MyBatis Plus 是在 MyBatis 基础上的增强工具,它把单表的增删改查都封装成了现成方法,你不用再写繁琐的 insert、selectById 这类 XML,直接继承 BaseMapper 就能用。这对毕设来说能省下大量重复代码时间。但它又不是黑盒,复杂查询你依然可以自己写 SQL,甚至可以做多表关联查询。这一点很关键,因为答辩时评委很可能让你现场写一个 SQL 或者解释某个查询逻辑,如果你只知道调用现成方法,一问就露馅。

用 MyBatis Plus 时有个习惯要养成:实体类字段和数据库字段的映射要搞清楚。比如数据库字段是 create_time,Java 属性是 createTime,一定要配置好驼峰映射,或者用 @TableField 注解标明,否则查出来的数据永远是 null,而且这种 bug 特别隐蔽,不容易排查。

3. 数据库设计:整个项目的定海神针

3.1 表结构总览与字段说明

跨过技术选型,真正决定项目上限的是数据库设计。很多同学上来就写代码,结果写到订单的时候发现购物车表缺字段,又回头改表,来回折腾。数据库设计一旦定下来,后端的 Service 层、前端的页面结构基本就跟着走了,所以这一步值得多花时间。

网上点餐系统最少需要六张表:用户表、菜品分类表、菜品表、购物车表、订单表、订单明细表。我来逐个说清楚。

用户表字段:id、username、password、role、nickname、phone。role 用来区分顾客和管理员,我习惯用 int 类型,0 表示顾客,1 表示管理员,比字符串比较更高效。password 字段要注意,毕设里用 MD5 加密就行,但答辩时说一句"实际生产应该用 BCrypt"会显得你懂行。

菜品分类表字段:id、name、sort。sort 用来控制分类在首页的展示顺序,这个字段很容易被忽略,但它对前端排序特别有用。菜品表字段:id、category_id、name、price、image、description、status。status 用来表示上架还是下架,0 下架 1 上架,下架菜品在用户端就不能显示,但在管理端还能看到,方便后续重新上架。

购物车表字段:id、user_id、dish_id、quantity。这张表最核心的是要加一个唯一约束:user_id 和 dish_id 联合唯一。这样才能保证同一个用户加同一个菜,是数量加一而不是新增一条记录。

订单表字段:id、order_no、user_id、total_price、status、remark、create_time。order_no 是订单编号,最好用时间戳加随机数生成,不用自增 id 当订单号,因为订单号要对外展示而且不能连续可猜。订单明细表字段:id、order_id、dish_id、dish_name、price、quantity。明细表里冗余了 dish_name 和 price,这个设计很多人不理解,但它是订单系统的精髓,后面我会单独讲。

3.2 冗余字段与状态设计:订单系统的灵魂

订单明细表里存 dish_name 和 price 这种冗余字段,是我认为整个数据库设计里最重要的一个决策。

为什么?因为菜品名称和价格是可变数据。今天鱼香肉丝卖 20 块,明天商家调价到 25 块,如果订单明细表只存 dish_id,那么用户查看历史订单时,显示的可能是调价后的价格,这显然是错误的。用户下单那一刻看到的价格才是用户实际支付的价格,必须把这个当时的价格快照存下来。菜品名称同理,万一商家改了个名,历史订单也要保持原来的名称。

这个道理顺序在答辩时一定要主动讲出来。你告诉评委:"订单明细冗余了菜品名称和价格快照,这样即使菜品后来改价或改名,历史订单依然保持准确。" 评委一听就知道你是理解了订单系统的关键,而不是单纯照着别人的表结构抄。

订单状态字段也很讲究。我建议用 int 类型,约定 0 待付款、1 已付款待接单、2 已接单制作中、3 配送中、4 已完成、5 已取消。用 int 而不是字符串,一方面数据库存储空间小,另一方面在 Java 里配合枚举类做状态流转校验特别自然。但要注意,一定要有一个地方集中记录这些约定的含义,最好的方式是在 Java 代码里写一个枚举类或者常量类,前端页面也用同一种映射。最忌讳的是在 SQL 查询里到处写 where status = 1,然后没人知道 1 代表什么。

3.3 索引设计与逻辑外键

表结构设计完后,还要顺手把索引加上。用户表的 username 字段要建唯一索引,这是登录查询的必经之路,而且必须保证不能有重复用户名。订单表的 order_no 要建唯一索引,防止并发下生成重复订单号。购物车表的 user_id、dish_id 联合唯一索引,前面已经说过。订单表的 user_id 建普通索引,因为用户查看历史订单是高频操作。

外键方面,我建议不要用数据库层面的物理外键,而是用逻辑外键。也就是说,表与表之间的关联关系由 Java 代码来维护,数据库表不声明 FOREIGN KEY。这样做的好处是:第一,删除数据时不会被外键约束卡住,毕设里经常需要清空表数据重新测试,物理外键会带来很多麻烦;第二,性能更好,物理外键在每次插入、更新时都有额外的校验开销。但你也别理解成完全不建立关

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进