☰
基于SpringBoot+Vue的网上蛋糕售卖店管理系统开发实战
2026/10/13 3:59:32 网站建设 项目流程

做蛋糕店线上售卖系统这个题目,我前阵子刚完整地落过一套,就是标题里说的“基于SpringBoot+Vue的网上蛋糕售卖店管理系统”,技术栈是Java+SpringBoot+MySQL+MyBatis,前端用的Vue,整套源码跑通。这篇文章不聊虚的,直接把一个蛋糕店的在线销售和管理系统从零做出来的思路、数据库怎么设计、后端接口怎么拆、前端页面怎么接、下单到发货整个业务闭环怎么走通,以及我实测遇到的坑全部写出来。适合正在做同类课题的学生、想接外包不知道怎么下手的新人,或者想给线下蛋糕店做一套点单后台的开发者参考。

废话不多说,按我实际动手的顺序来。

1. 系统定位与整体技术选型

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

我接触过几个线下蛋糕店老板,他们真正的痛点是很具体的:顾客打电话订蛋糕,说不清款式,店员凭记忆报价,高峰期记错单、漏单是常事;想要统计哪个蛋糕卖得好,翻纸本子翻到头疼;遇到节日促销,改价格要一张一张贴标签。所以这个系统表面的需求是“做一个网站”,本质上是把线下门店的展示、下单、订单管理、商品管理、数据统计全部线上化。

在做之前我习惯先列角色和核心功能清单。这个系统拆成两类用户,线上顾客和管理员:

  • 顾客端:注册登录、浏览轮播图、按分类看蛋糕、查看商品详情、加入购物车、提交订单、查看订单状态、确认收货。
  • 管理端:登录后台,维护轮播图、管理蛋糕分类、添加/编辑/上下架蛋糕商品、处理订单(发货、取消)、查看会员列表、简单数据统计。

这个功能清单一列出来,项目范围就很清楚了。它不是一个简单的展示型官网,而是有完整业务流的前后端分离项目。这里也要提醒一句:做这种管理系统,最忌讳一上来就写代码,先把角色和功能边界定了,后面所有表结构和接口开发都跟着这个走。

1.2 为什么选SpringBoot+Vue+MySQL+MyBatis这套组合

现在Java Web项目的技术方案太多了,SpringCloud、微服务、各种ORM框架都能做,但这类单体管理系统用这套组合是性价比最高的。我自己的判断标准很简单:团队熟悉度、部署成本、需求复杂度。

SpringBoot解决了传统SSM项目配置地狱的问题,内嵌Tomcat意味着打出一个jar包扔服务器上就能跑,不需要单独装Tomcat。MyBatis这套东西,半自动ORM,SQL自己写,对于订单查询、商品联表这种复杂查询,可控性比全自动的JPA强很多。MySQL对这种规模的业务量绰绰有余,蛋糕店一天能有多少订单?就算做活动峰值也就是几百单,MySQL完全扛得住。Vue负责前端页面和交互,组件化开发让页面维护成本低很多。

对比一下其他方案。比如用SpringCloud拆微服务,对于这种业务量就是给自己找麻烦;用JPA虽然开发快,但复杂的统计查询和动态排序条件,写起来反而别扭;前端用JSP做服务端渲染,用户体验一般,且和后端代码耦合严重,后面想加移动端接口会很痛苦。前后端分离是当前的主流模式,尤其这种需要顾客端+管理端两套界面的项目,拆开之后互相不干扰,这是最终选择这套技术栈的核心原因。

1.3 项目结构规划与分层设计

项目结构我拆成两个独立的工程,后端一个SpringBoot工程,前端一个Vue工程,这样部署和开发互不干扰。后端按标准的分层架构来组织,Controller层接收请求和参数校验,Service层处理业务逻辑和事务,Mapper层负责SQL交互,Entity层映射数据库表。

很多初学者喜欢把业务逻辑全部写在Controller里,一个方法几百行,后面维护起来谁都救不了。我的习惯是:Controller只做参数接收和结果封装,Service才是业务核心。比如提交订单这个动作,Service层要做库存校验、生成订单主表和明细表、扣减库存,至少三步操作,必须放在同一个事务方法里。这些逻辑放Controller里,写是可以写,但一旦出问题,排查和复用都是灾难。

前端Vue工程我也做了拆分,顾客端和管理端复用同一个工程,通过路由和权限控制区分。顾客访问/下的页面,管理员访问/admin下的页面,后端接口根据用户角色校验权限,前端根据登录用户角色动态渲染菜单。这样比拆两个Vue工程省事得多,代码复用也方便。

2. 数据库设计与后端核心实现

2.1 数据库表设计:从分类到订单的8张核心表

数据库设计是这类管理系统最见功底的部分。我的习惯是先在纸上画出业务关系,再落成表结构。一个蛋糕在线销售系统,核心实体有用户、分类、蛋糕、购物车、订单、订单明细、收货地址、轮播图,落成8张主表。

用户表是最基础的,字段有主键ID、用户名、密码、手机号、头像、注册时间、状态。密码这里必须强调:绝对不要明文存储。我实际见过太多项目的数据库里密码直接可见,这是上线后被脱库的第一风险点。这个系统里我用BCrypt加密,SpringSecurity自带的工具类就行,加密后存数据库,校验时用match方法比对。

蛋糕分类表很简单,分类ID、分类名称、排序号、显示状态。这里加了一个排序字段,很多新手会忽略,导致后台调整分类顺序时只能改代码。蛋糕信息表是商品的核心表,字段比较多:蛋糕ID、分类ID、蛋糕名称、主图、描述、价格、原价(划线价展示用)、库存、销量、是否上架、创建时间。价格字段用DECIMAL(10,2),不用FLOAT或DOUBLE,避免精度丢失。库存字段后面要做并发扣减,注意类型和条件更新。

购物车表记录用户ID、蛋糕ID、数量、加车时间,一个用户对同一个蛋糕保证唯一记录,多次加购只改数量。订单表字段要仔细设计:订单编号、用户ID、收货人、联系电话、收货地址、订单总金额、支付方式、订单状态、下单时间、支付时间、发货时间、完成时间。订单状态我用数字表示,不是用字符串,状态机设计后面单独说。订单明细表记录订单里每个蛋糕的快照信息,蛋糕名称、图片、单价、数量、小计,这是整个表设计里最容易忽略的地方。

2.2 订单明细为什么要做商品快照

订单明细表是我认为这套系统里最该重点讲的表。很多人设计订单明细时,直接关联蛋糕表,查询时就通过order_id查蛋糕表拿名称和价格。听着没毛病,但实际运营中一定有坑:用户下单后,管理员把蛋糕下架了,或者改了价格,甚至删除商品,订单明细关联不到商品,历史订单页面直接报错。更要命的是,用户下了一个100块钱的蛋糕,第二天蛋糕涨到120,订单详情里显示的如果是关联表的当前价格,那用户和商家双方都说不清。

正确做法是在订单明细表冗余商品快照字段:下单时把蛋糕名称、主图URL、单价原样COPY进订单明细表。这样订单一旦生成,跟商品表彻底解耦,后续商品怎么改都不影响历史订单。这个经验是我做电商项目踩过坑后才养成的习惯,做任何包含交易流程的系统,订单明细必须快照,没有例外。

订单表还有一个容易忽略的点:订单编号不要用数据库自增ID直接展示给用户,因为自增ID是连续的,用户从订单号能反推平台一天的订单量,存在商业信息泄露风险,也容易被恶意遍历。我给订单编号用了“日期+随机数”的方式,比如20250607123000001这种格式,再加一个唯一索引,既好看又不泄露规律。

2.3 订单状态机:数字常量的设计逻辑

订单状态这块,我建议不要用字符串如“待支付”“已支付”直接存数据库。原因很简单:如果你改了展示文案,比如“待支付”改成“等待买家付款”,你得更新数据库里所有历史订单的记录;另外中文字符串在数据库里占用空间比数字大,索引效率也低。

我用的方案是数字常量加TypeHandler映射,在Java枚举里定义订单状态,数据库存数字,返回给前端时转换成对应状态文案。实际项目里我在OrderStatusEnum里定义了这么几个状态:0待支付、1已支付待发货、2已发货、3已完成、4已取消。状态流转规则写在Service层:待支付可以取消或支付;已支付只能发货;已发货用户才能确认收货变成已完成。前端根据状态值渲染不同的按钮和文案。

这里要注意:状态流转必须走后端校验,绝对不能信前端的传参。有的项目直接让前端提交一个状态值过来改订单状态,那相当于把系统敞开给黑客,随便构造个请求就能把订单改成已完成。正确做法是后端根据当前状态和用户角色,计算下一个合法状态,前端只能提交操作动作,比如“发货”这个动作,而不是直接提交目标状态。

2.4 核心接口实现:登录、首页数据、提交订单

后端接口设计遵循REST风格,返回统一的结果对象,包含状态码、消息、数据三个字段。比如{code:200, message:"操作成功", data:{...}},前端Axios拦截器统一判断code,少写很多重复的错误处理代码。

登录接口的逻辑是:接收用户名和密码,查询用户,密码校验通过后生成Token返回前端。Token我用JWT,登录后后端生成Token,里面包含用户ID和角色信息,后续所有需要认证的接口都从Token里取用户身份。用JWT的好处是服务端无状态,不需要存Session,多个实例部署时也不会出现Session不一致的问题。Token失效时间我设成24小时,到期后前端跳登录页重新登录。

首页数据接口是顾客端第一次打开页面就要请求的,我聚合了轮播图、热门蛋糕、新品推荐三个数据。后端的做法是提供一个类似getHomeData的聚合接口,内部依次查轮播表、销量排序的蛋糕、按上架时间倒序的蛋糕,一次请求返回全部。这样前端只需要发一次请求,不用调三个接口。这里做了一个小小的接口聚合优化,实际体验就是首页加载速度快了很多。

提交订单是整个系统最核心的接口,逻辑比较多。用户请求传收货地址ID、购物车选中项列表、备注。后端要做的事:校验用户登录状态、从收货地址表查收货信息、根据购物车和蛋糕表算出总金额、生成订单主表和明细表、扣减蛋糕库存、清空购物车。这一串操作必须放在同一个事务里,任何一步失败都要全部回滚,否则会出现订单生成了但库存没扣,或者库存扣了但订单没生成的情况。我用@Transactional注解搞定事务边界,这个注解用起来很简单,但要注意事务方法必须通过Spring代理调用才生效,同一个类内部方法自调用是不起作用的。

2.5 库存扣减的并发处理

蛋糕销量好的时候,多个用户同时下单,库存扣减容易出超卖问题。传统的做法是先查库存,判断数量够不够,够就执行减库存的Update。这种做法在并发场景下有问题:两个请求同时查到库存是5,都判断够,都执行扣减,库存变成3,但生成了两个订单,实际卖了2个蛋糕,逻辑上没问题;但如果只剩最后1个,两个请求同时查库存都是1,同时执行扣减,库存变成-1,这就出事故了。

我用的是条件更新方案,一条Update语句搞定:UPDATE cake SET stock = stock - #{num} WHERE cake_id = #{id} AND stock >= #{num},这条语句执行后返回影响行数,如果返回0说明库存不足,后端起异常回滚事务。这种方式利用了数据库行锁的原子性,不需要额外的分布式锁,代码也简单,对蛋糕店这种体量的业务完全够用。这里再补充一句:UPDATE跑完后,如果订单后续步骤失败回滚了,库存也会跟着回滚,因为整个提交订单方法在一个事务里,这个机制非常关键。

3. 前端Vue实现与联调细节

3.1 Vue项目搭建与路由权限设计

前端项目我用Vue2配合ElementUI,Vue2的生态在当前存量项目里还是大头,ElementUI的组件文档全,社区方案多,对做管理系统这种后台界面的场景来说效率非常高。如果你想用Vue3+ElementPlus,逻辑是一样的,换成对应API即可。工程用Vue CLI初始化,安装路由、Vuex、Axios、ElementUI这几个核心依赖。

页面上顾客端包含:首页、蛋糕列表页(带分类筛选)、蛋糕详情页、购物车页、订单确认页、订单列表页、注册登录页。管理端包含:后台首页、商品管理页、分类管理页、订单管理页、轮播图管理页、用户管理页。我在路由配置里把顾客端和管理端的页面放在不同的父路由下,管理端所有路由都包在一个统一校验的beforeEach守卫里面。

路由守卫是权限控制的第一道防线:进入管理端页面时先检查本地有没有Token,没有或者失效就跳转登录页。但要注意,前端路由守卫只是用户体验层面的拦截,真正保护接口安全的是后端接口对Token的校验。前端隐藏了管理端入口,不代表接口就安全了,这个安全意识要贯穿整个开发过程。

3.2 Axios封装与Token自动携带

Axios请求封装是前端工程的基建,我多数项目的写法都差不多:创建一个Axios实例,设置基础URL,请求拦截器里从localStorage拿Token并加到请求头Authorization字段,响应拦截器里统一处理后端返回的业务状态码。Token失效时后端返回401状态码,响应拦截器统一跳转到登录页,并清理本地登录状态,不用每个接口单独写一遍。

这里有一个经验要分享:localStorage里存的Token,如果用户手动清除了浏览器缓存,或者Token过期了,前端组件没刷新前还是能操作页面,点提交按钮时接口会报401,这时候要给出友好的提示而不是一个红红的报错框。我在响应拦截器里对401做了特殊处理,弹出“登录状态已过期,请重新登录”,然后延迟跳转登录页,这是用户体验上很见功力的小细节。

跨域问题也是这种前后端分离项目绕不开的。开发阶段我推荐在Vue项目的vue.config.js里配置代理,把/api前缀的请求转发到后端的localhost:8080。这样前端请求看起来是相对路径,不存在跨域问题。如果前后端部署在不同域名,后端就得单独配置CORS,允许指定域名访问。我在后端的WebMvcConfigurer里配置了跨域规则,开发和生产都能直连后端接口,这个后面就不受代理的限制了。

3.3 蛋糕图片上传与访问路径处理

蛋糕图片管理是后台商品维护里最琐碎的部分。后端我提供一个图片上传接口,接收MultipartFile,保存到服务器本地指定目录,返回图片访问的相对路径。数据库里存的是图片相对路径,比如/upload/cake/202506071230.jpg,前端拿到这个路径拼上服务器地址就能访问。

这里最大的坑是保存路径和访问映射。很多人直接把文件存在项目里的某个静态目录下,部署后用jar包启动就会发现图片没了,因为SpringBoot的jar包打包后,项目内部目录是不能往里面写文件的,运行时的文件写入根本落不到磁盘。我踩过这个坑后,改成把上传目录配置成服务器外部的绝对路径,比如/data/upload/,然后通过SpringBoot的静态资源映射配置,把/upload/**这个URL映射到磁盘路径。这样任何环境下部署,只要配置文件里的路径存在,图片上传和访问都不会出问题。

文件大小限制也要提前设置好,SpringBoot默认单个上传文件大小是1MB,蛋糕高清图基本都会超过。我在配置文件里把spring.servlet.multipart.max-file-size和max-request-size都调到10MB,不然传大图时SpringBoot直接抛异常,错误提示还不明显。前端传图片时我加了预览效果和压缩处理,ElementUI的Upload组件自带本地预览,大图在前端用canvas压缩到合理尺寸后再上传,可以大幅提高上传成功率。

3.4 前后端接口联调的节奏把控

前后端联调阶段是这类项目最耗时间的。我的做法是先把接口文档定死,前后端都按文档开发,联调时直接套用。接口文档我习惯用Apifox或者Swagger这类工具,后端代码写完自动生成Swagger文档也行,但为了接口字段的规范性和可读性,我倾向于先写Markdown接口约定,把每个接口的URL、请求参数、响应示例写清楚,再让前后端各自开发。

联调时最常见的还是字段类型不一致的问题。后端返回一个id字段是Long类型,前端JS里大数字会丢失精度,如果ID超过16位用Number会失真,这个不碰到不会注意。所以后端返回给前端的主键字段,尤其是订单编号这种超长数字,我统一转成字符串返回,前端读取时就不用担心精度问题了。时间字段也是,后端默认返回的是带T的ISO格式,前端显示时乱七八糟,我在后端的全局配置里把日期格式化为yyyy-MM-dd HH:mm:ss,一刀切解决。

4. 从展示到发货:核心业务流程的完整闭环

4.1 用户端下单一整个流程是怎么走通的

把核心业务串起来看,这个系统的灵魂就是“从顾客看到蛋糕到商家发货”这条链路。我把这条链路完整走一遍,每一步对应哪些表和哪些接口,就非常清晰了。

顾客打开首页,请求getHomeData,后端返回轮播图列表、按销量排序的蛋糕列表、按上架时间倒序的蛋糕列表,页面渲染出来。顾客点击某个蛋糕,跳到详情页,请求蛋糕详情接口,返回蛋糕信息、所属分类名称、库存数量。如果库存为0,页面上的“加入购物车”按钮置灰,这个是前端判断库存字段。加入购物车时,后端接口要做一件事:如果购物车表里已有这个用户和这个蛋糕的记录,数量加一,否则新增一条记录。

顾客进入购物车页,列出所有购物车项,每一项能从购物车表联查蛋糕表拿最新名称、图片、价格展示。这里有个细节:购物车里显示的价格应该读数据库拿到最新价格,而不是用户加入购物车时前端页面上看到的价格。结账时跳转订单确认页,选择或新增收货地址,确认结算金额。提交订单后走上面说的那个核心方法,生成订单主表+明细表,扣库存,清购物车。模拟支付环节,我没有真的接支付平台,做了模拟支付按钮:点击后订单状态从待支付变成已支付待发货。你真要接微信支付或支付宝,只需要在这个位置替换成对应支付接口回调即可。

4.2 管理端如何承接订单并处理发货

顾客下了单,管理端的法宝在后台订单管理页体现。管理员登录后台,进入订单管理页,默认展示全部订单列表。我按订单状态让管理者筛选:待付款、待发货、已发货、已完成、已取消,各状态用不同颜色的标签区分,一眼就能看清业务情况。

管理员点击“发货”按钮,后端接口做的事情很明确:先校验当前订单状态必须是“已支付待发货”,再把订单状态改为“已发货”,写入发货时间。我这里加入了状态校验,不是管理员点了发货就能发,非法状态流转后端会拒绝。发货单号这个东西,真正的电商系统要填快递公司、快递单号,我在这个系统里预留了字段,页面也加了输入项,但没做强校验,这算一个可以继续完善的点。用户端订单列表页会自动刷新看到状态变化,这就是前后端分离下,状态机 + 状态文案渲染的威力。

4.3 销量统计与商品推荐的数据来源

首页的热门蛋糕和推荐蛋糕,背后是销量和上架时间这两个字段。这里有个容易搞错的点:销量到底在哪一步累加?我看过一些实现,在订单生成时就给商品表销量加一,结果用户下单不付款,销量虚高。正确做法是在订单支付成功或者确认收货后才给销量累加。因为这个系统做了模拟支付,我在支付成功这一步给蛋糕表销量加数量,也就是用户点击模拟支付按钮后,蛋糕的销量才变化。这个细节决定了首页推荐数据是否真实可靠,显得很关键。

首页的新品推荐逻辑是ORDER BY create_time DESC,最近上架的蛋糕排前面。如果商品下架了,上架状态is_shelves为0时,所有查询都要带上状态过滤条件,不然顾客还能浏览到下架商品,提交订单时又提示库存不足,体验很差。这些查询条件我统一封装在Mapper的SQL里,后端Service层不用到处传状态判断。

5. 常见问题排查与避坑实录

5.1 开发过程中最典型的8个报错汇总

这类项目做到最后,很大一部分时间花在排错上。我把开发过程中遇到的、以及后来给别的同学看项目时发现的典型问题整理成了一张速查表,每一条都是真实会踩到的坑。

问题现象根本原因解决方案
前端请求后端接口404Controller类上的RequestMapping路径与前端请求不一致,或Controller未被Spring扫描到检查Controller注解和包扫描路径,利用日志确认接口注册
MyBatis执行报Invalid bound statementMapper接口和XML文件namespace不匹配,或XML文件没有编译到classes目录检查namespace与方法ID对应,确认MyBatis XML扫描配置
查询结果字段全是null数据库字段下划线命名与Java驼峰属性未映射开启map-underscore-to-camel-case配置,或resultMap手动映射
金额出现0.30000000000000004使用了FLOAT/DOUBLE存储金额数据库用DECIMAL,Java用BigDecimal
页面时间显示为“2025-06-07T12:30:00”SpringBoot默认Jackson序列化ISO格式全局配置日期格式为yyyy-MM-dd HH:mm:ss
上传大图报文件大小超限SpringBoot默认单文件最大1MB配置文件设置multipart大小限制
跨域接口请求被浏览器拦截前端与后端域名不同后端配置CORS或前端代理转发
同一个订单被提交两次前端多次点击提交,后端未做幂等校验前端按钮加载状态禁用,后端提交订单前校验并加防重

5.2 排错思路与调试方法

面对这些报错,我的排错顺序是固定的:先看浏览器Network里请求有没有发出、返回的HTTP状态码是多少,然后用Postman直接调后端接口看是否复现,如果Postman能通而浏览器不通,就是跨域或请求头的问题;如果两边都不通,就是后端接口本身的问题,把日志级别调到DEBUG,看MyBatis打印出来的SQL是否正确,再往下就是逐段断点。

这里分享一个特别实用的方法:MyBatis层出了SQL问题,优先看控制台打印的SQL语句,对比一下和预期有什么差异。如果SQL里有参数占位,但参数值看起来不对,基本就是Maper接口参数的注解没写对,用了多个参数没有加@Param。这类问题花了我不少时间,后来我写完一个Mapper方法就立即看控制台SQL,提前发现问题,不做大量代码后统一调试。

5.3 上线前必须补强的几个安全点

开发环境跑通不容易,但上线前有些事不做掉,部署到公网上就是挨打的份。密码加密要确认所有用户密码都是BCrypt密文,登录校验用匹配方法,这一步基本决定用户数据安全底线。JWT过期时间别设太长,24小时差不多了,太久等于Token泄露后给别人留了操作窗口。后端所有查询用户数据的接口,都要从Token里取用户ID做权限校验,不能查询接口直接把用户ID当参数传,否则越权风险极高。管理端接口要做角色权限校验,普通用户请求管理端接口必须拒绝。为了快速开发有些人这些都没做,运行是能运行,但离能上线还差一个安全加固的距离。

管理端操作要记录操作日志,尤其是商品上下架、订单发货这些关键动作。我在这套系统里加了一个简单的操作日志表,记录管理员ID、操作内容、操作时间,虽然界面没做专门展示,但数据已经落在库里。真要出问题,排查是谁在什么时候改了数据,有这个日志和数据表结构能省掉大量扯皮。

6. 项目完成后还能往哪些方向扩展

系统跑通之后,后面如果想继续往深做,我心里有一套清晰的扩展路线。最直接的是把模拟支付替换成真实支付,对接微信支付或支付宝的Native支付接口,用户下单后生成支付二维码,扫码支付完成后异步通知后端更新订单状态,这是从“演示项目”走向“商用项目”最重要的一步。购物车和订单确认页面已经预留了支付方式字段,对接成本不高。

数据统计可以做一张管理端首页的可视化看板,用ECharts展示近7天订单量趋势、蛋糕销量排行Top10、分类销售占比,数据源都从订单表和蛋糕表聚合查询。这个扩展对管理层有实际价值,也是面试或者答辩时很好的亮点。

首页的高频接口可以考虑加一层缓存。比如Redis把首页数据缓存起来,5分钟过期,能把数据库压力降一大截。移动端适配也是个方向,把顾客端Vue工程通过媒体查询做成响应式布局,或者直接用HBuilderX打包成小程序/移动App,后端接口复用,基本不用改。

整体做下来,我的体会是:这种管理系统真正难的地方反而在数据库设计和业务状态的管理上,技术栈的东西反而是次要的。把数据关系理清楚,把状态流转规范好,把边界情况处理好,代码写起来是顺理成章的事。蛋糕店管理系统只是这套骨架的一个实例,换成本数管理系统、买菜商城、二手交易平台,核心逻辑基本都是这套。有了这个底子,后面接类似需求,心里是有底的。最后再提醒一个事情:整套系统跑通后,记得把数据库脚本、初始化数据、部署文档一起归档,交付出去的时候,别人照着文档能自己把环境搭起来,这才是真正的完整源码。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询