☰
SpringBoot+Vue3蛋糕销售管理系统:从零搭建到答辩全攻略
2026/10/8 15:04:18 网站建设 项目流程

每年到了春招和毕设季,我这边就会集中收到一批类似的咨询:Java方向、SpringBoot后端、Vue前端、想做一个电商类管理系统。问得最多、最后交付率也最高的,就是基于SpringBoot+Vue3的蛋糕销售管理系统。从选题本身就能看出它为什么受欢迎——业务链路完整,商品、购物车、订单、支付、统计全都有,但规模又不会大到一个人做不完;而且蛋糕这种商品自带好看的图片展示场景,做完拿去答辩,视觉效果很占便宜。这篇就把我从零搭这套系统、以及帮学生填坑的全过程拆开讲一遍,照着做至少能让你少走两三个星期的弯路。

1. 项目定位与需求拆解:毕业设计到底要做成什么样

1.1 一个蛋糕销售系统为什么成了“稳妥之选”

很多人一上来就问“哪个毕设题目最容易过”,我的回答一直是:别找最容易的,找最稳的。所谓稳,就是技术栈主流、业务逻辑完整、演示效果好、答辩时每个模块都能讲出设计理由。奶油蛋糕销售系统恰好全中。

它本质上是个轻量级电商系统,核心链路是“用户浏览商品—加入购物车—下单—支付—商家发货—用户确认收货”,这条链路每一段都是一个独立的功能点,单拿出来都能写一段实现思路。对比传统的图书管理系统、员工管理系统,蛋糕销售系统多出了购物车、订单状态、库存扣减、销售统计这些真实业务场景,天然更容易展示个人能力。

另一个现实因素是,这个选题在网上已经有大量开源版本和参考资料,初版跑起来不难。难点在于怎么把它做出差异化:比如增加会员积分、蛋糕DIY定制、限时折扣、数据大屏、评论功能,这些都是评审老师愿意看到的亮点。整套系统做下来,功能边界刚好控制在一个人两三周能完成的范围内,不会像中台系统那样遥遥无期,也不会像图书管理那样让人一眼看穿是“课设级别”。

1.2 用户端和管理端的功能边界

毕设答辩最忌讳“功能一堆但说不清楚”。我建议开工前先把角色和功能边界画清楚,我用得最多的整理方式就是角色-功能对照表。

角色核心功能备注
普通用户注册登录、浏览商品、分类筛选、关键词搜索、查看详情、加入购物车、下单、模拟支付、查看订单、取消订单、个人信息修改不需要做真实支付,用模拟支付状态切换即可
管理员登录后台、商品分类管理、商品CRUD、上下架、库存调整、订单管理、发货操作、用户管理、销售数据统计、轮播图管理后台界面用Element Plus搭建,表格+表单弹窗是主力

用户端页面通常包含首页、商品列表、商品详情、购物车、订单确认、订单列表、个人中心这几个页面;管理端则是一个带侧边栏的布局,里面挂商品管理、订单管理、用户管理和数据统计四个子页面。这套页面结构基本是任何电商类项目的通用模板,做蛋糕和做手机没本质区别。

还有一点很容易被忽略:管理系统里必须内置一个管理员账号,并且要在数据库初始化脚本里预置,否则答辩老师问“后台怎么登录”就尴尬了。

1.3 哪些是必做项,哪些是加分项

很多同学一上来就想着做秒杀、做优惠券、做消息推送,我一般会劝住。毕业设计的核心是完成一个逻辑闭环,而不是功能堆砌。

必做项是:登录鉴权、商品展示、购物车、下单、订单管理、商品管理、数据统计。这七个模块覆盖了“前台用户操作”和“后台管理操作”两条完整链路,足够支撑一篇像样的论文和十几分钟的演示。

加分项我建议从下面几个方向挑一两个做就够了:

  • 订单超时未支付自动取消,用Spring定时任务或延迟队列实现;
  • 数据统计维度扩展,加入按月份对比、按分类占比,用ECharts画趋势图;
  • 角色权限细化,比如普通管理员不能删除订单;
  • 简单优惠券系统,下单时校验券码并抵扣金额。

加分项的价值在于答辩时能让老师看到“这个人有扩展思维”,而不是说“我只会照着教程敲”。

2. 技术选型与架构设计:为什么这套组合最省心

2.1 SpringBoot和Vue3各自解决什么问题

后端选SpringBoot基本没有争议。它内嵌Tomcat、自动配置、生态成熟,Java方向的毕业设计选它,几乎等于默认答案。具体版本上,我建议分两种情况:如果你的JDK环境是8,就用SpringBoot 2.7.x;如果是17以上,可以直接上3.x。注意SpringBoot 3.x的包名从javax改成了jakarta,网上很多老教程里的import语句会报错,这不是你写错了,是版本差异。

前端选Vue3而不是Vue2,也是同样的逻辑。Vue3的组合式API(setup语法)写起来比选项式API更干净,配合Vite启动速度极快,再加上TypeScript的话,项目结构会显得更专业。不过毕设一般不建议强行上TypeScript,时间有限,JavaScript就够了。

选这套组合的核心原因不是为了赶潮流,而是因为答案可查、错误可搜。SpringBoot + Vue3的社区资料足够多,遇到问题搜索引擎基本都能找到答案,这对时间紧张的毕设来说比技术本身更重要。

2.2 后端代码怎么分层,前端目录怎么组织

我见过太多学生把所有代码塞进一个Controller里,几百行下来自己都看不懂。这里的花不了多少时间,但能让答辩印象分差出一截。

后端推荐按经典分层结构:

cakeshop-backend/ ├── src/main/java/com/example/cake/ │ ├── controller/ # 接收请求,参数校验 │ ├── service/ # 业务逻辑,事务控制 │ ├── mapper/ # MyBatis-Plus的Mapper接口 │ ├── entity/ # 数据库表对应的实体 │ ├── dto/ # 前端传入的参数对象 │ ├── vo/ # 返回给前端的视图对象 │ ├── config/ # 配置类、拦截器注册 │ ├── common/ # 统一返回体、异常处理 │ └── utils/ # JWT、密码加密等工具 └── resources/ ├── mapper/ # 复杂SQL的XML文件 └── application.yml

前端目录相对自由,但最好也形成习惯:

cakeshop-frontend/ ├── src/ │ ├── api/ # 每个功能模块的接口请求 │ ├── views/ # 页面级组件 │ ├── components/ # 复用组件(商品卡片、分页等) │ ├── router/ # 路由配置 │ ├── store/ # Pinia状态管理 │ └── utils/request.js # axios实例封装

分层不是为了好看,而是为了让逻辑边界清晰。答辩时老师问“订单金额在哪里计算的”,你可以直接回答在Service层而不是Controller层,这就是一个加分的细节。

2.3 数据库设计:订单和商品的表关系一次讲清

数据库设计是整个项目的地基。很多项目后期改来改去,就是因为表设计不合理。我常用的表结构包含以下几张:

  • user(用户表):id、username、password、nickname、avatar、phone、role、createTime,其中role区分管理员和普通用户;
  • category(分类表):id、name、sort;
  • product(商品表):id、categoryId、name、description、price、stock、image、status、sales、createTime;
  • cart(购物车表):id、userId、productId、quantity、checked;
  • orders(订单表):id、orderNo、userId、totalAmount、status、receiverName、receiverPhone、receiverAddress、createTime、payTime;
  • orderItem(订单明细表):id、orderId、productId、productName、productImage、price、quantity。

这里有两个关键设计点。

第一,订单中要冗余商品名称和商品图片。因为商品信息可能会被修改,如果订单明细只存productId,之后商品改价或改名,历史订单的数据就不对了。

第二,金额字段不要用float或double,用decimal(10,2)。浮点数在计算总价时会出现精度问题,这是在毕业答辩里被经常问到的一个基础题,也是真实项目中非常重视的点。

我建议外键在逻辑上保持关联,但不在数据库层面创建物理外键。理由也很直接:物理外键在删除和更新时容易产生约束麻烦,而且项目中用MyBatis-Plus操作时,物理外键反而影响效率。表关系通过Java代码来控制,这在现代化开发里是主流做法。

3. 核心功能模块实现:关键代码思路与细节拆解

3.1 登录鉴权:JWT和拦截器怎么配合

这套系统最核心的公共逻辑就是登录鉴权。方案我推荐JWT,而不是传统的Session,原因是前后端分离架构下Session天然存在跨域和共享问题,而JWT无状态、适合接口鉴权。

登录流程大致是:

  1. 用户输入用户名密码,后端校验通过后生成一个有效期2小时的token,把用户id和role放进token里;
  2. 前端把token存在localStorage,每次请求时在axios请求拦截器里带上Header;
  3. 后端写一个拦截器,对所有非白名单接口进行token校验,校验失败返回401;
  4. 前端路由守卫判断本地有没有token,没有就跳转登录页。

密码存储这块,一定不要用明文或MD5。Spring Security crypto包里自带BCryptPasswordEncoder,加盐哈希,一个单词就能完成加密和校验,比MD5靠谱得多。

我见过不少学生把“登录成功”当成token校验,其实是两回事。Controller里只要校验用户信息是否正确即可,至于token的有效性、过期时间,全部交给拦截器统一处理。这样后续加接口时,不需要每个Controller都写一遍校验逻辑,也便于答辩时讲“统一鉴权”这个设计思路。

3.2 购物车和下单核心逻辑

购物车不是简单的增删改查,里面藏着两个容易忽略的点。

第一个是“加入购物车”接口的幂等性。用户反复点击“加入”同一个商品时,正确的行为是购物车中该商品数量+1,而不是插入两条记录。所以后端判断逻辑应该是:先根据userId和productId查购物车表,有记录就update数量,没有记录才insert。

第二个是“选中商品”状态。购物车里通常有全选、单选功能,订单页只会展示被勾选的商品。所以cart表里的checked字段一定要设计进去,否则下单时没法区分哪些商品需要结算。

下单接口是整个系统里事务最密集的地方。一个完整的下单需要做四件事:

  1. 根据购物车选中记录组装订单明细;
  2. 创建一个订单主记录,计算总金额;
  3. 扣减对应商品库存;
  4. 清空已下单的购物车记录。

这四个操作必须放在同一个事务里,否则会出现“库存扣了但订单没生成”或者“订单生成但购物车没清空”的脏数据。在SpringBoot里给Service方法加上@Transactional注解就行,非常简单但非常重要。

还有一个经验:下单时的总金额一定不能直接信任前端传来的数值,必须由后端根据商品当前价格重新计算。这个点经常被答辩老师拿来问,也是真实电商里防刷单的基本逻辑。

3.3 订单状态流转与事务控制

订单状态是用一个整数字段表示的,我用的是:

状态值含义触发动作
0待支付下单成功
1已支付/待发货用户模拟支付
2已发货管理员点击发货
3已完成用户确认收货,或发货后自动完成
4已取消用户取消订单

订单状态机的核心是“状态的合法流转方向”。比如待支付订单可以取消,已支付订单不能再取消,只能走发货流程。实现上可以简单在Service层加if判断,也可以写一个状态转换工具类统一管理。我建议毕设用if判断即可,但把判断逻辑集中在OrderService里,不要散落在Controller各个方法中。

另外,关于“支付”这个动作,毕设不需要对接支付宝或微信支付,只需要提供一个“模拟支付”按钮,点击后把订单状态从0改成1,并记录支付时间。但可以在论文里提一句“预留了真实支付接口”,这种话术是合理的扩展说明。

定时检查超时未支付订单可以作为加分项,用Spring的@Scheduled注解实现,每30秒扫描一次前5分钟创建的待支付订单,把超时订单取消并回滚库存。这个功能实现不复杂,但演示效果很酷,也算是有真实业务价值的模块。

3.4 后台统计:怎么用SQL做出销售报表

管理后台的统计页面是整个项目最直观的“成果展示区”。我一般用ECharts配合后端统计接口来做。

统计接口的SQL写法是核心。比如近7天销售趋势,可以按日期分组聚合:

SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS day, SUM(total_amount) AS amount FROM orders WHERE status IN (1, 2, 3) AND create_time >= DATE_SUB(CURDATE(), INTERVAL 6 DAY) GROUP BY day ORDER BY day;

这就是一个非常典型的“按时间维度统计销售额”的SQL,后端返回给前端一个day和amount的数组,前端直接用ECharts的折线图渲染。

分类销售占比的统计稍微复杂一点,需要关联订单明细表和商品表,按分类聚合。这个SQL在MyBatis的XML里写比较清晰,也不会把业务逻辑塞进Java代码里。

后台仪表盘上还会用几个数字卡片展示:商品总数、用户总数、今日订单数、今日销售额。这四个指标我都是用简单SQLcount和sum查出来的,不需要搞实时计算,给答辩演示完全够用。

数据统计模块的加分点在于“图表联动的技巧”:点击分类占比饼图的一块,下面的商品销量排行表格能联动过滤出该分类的数据。前端监听ECharts的点击事件,再带着分类ID请求接口,大概几十行代码,但演示效果能提升一个档次。

4. 实操过程:从环境搭建到打包演示

4.1 环境准备与初始化配置

动手前先确认本机环境:JDK、Maven、Node.js、MySQL、IDEA、VSCode。版本不一定要最新,但必须互相兼容。这里列一份我常用的环境组合,照着配基本不会出问题:

组件推荐版本说明
JDK8或17SpringBoot2.7用8,3.x用17
Maven3.6.3+配阿里云镜像,不然依赖下载很慢
Node.js16.20+低于16跑不动新版Vite
MySQL5.7或8.08.0记得调密码加密规则
SpringBoot2.7.18最稳,教程最多

后端工程初始化用IDEA的Spring Initializr,勾选Spring Web、MySQL Driver、MyBatis框架这几个依赖。如果用的是SpringBoot3.x,MyBatis依赖要改成mybatis-plus-spring-boot3-starter,这里很多人会踩版本坑。

前端工程用Vite创建:在命令行执行npm create vite@latest,选择Vue模板,然后依次安装vue-router、pinia、axios、element-plus、echarts。依赖安装完成后,先跑一次npm run dev确认项目能启动,再开始写代码。不要一上来就复制别人的完整源码,连本地环境都没验证过,出了问题很难定位。

还有一个很容易被忽略的配置:前端开发代理。在vite.config.js里加上一段:

server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }

这样前端请求/api开头的接口会转发到后端8080端口,开发阶段不用处理跨域。很多学生卡在跨域问题上,其实一个代理配置就解决了。

4.2 后端接口开发要点

后端开发的顺序建议按照“基础功能->核心链路->统计数据”分阶段推进,不要东一榔头西一棒子。我通常先写统一返回体Result和全局异常处理,再写登录鉴权,然后做商品查询和分类查询,最后做购物车和订单。

统一返回体的作用是避免每个接口返回格式不一致。一般长这样:

public class Result<T> { private Integer code; private String message; private T data; // 静态方法 success() / error() }

配合@RestControllerAdvice做全局异常捕获,业务里只需要专注写业务逻辑,错误处理自动返回统一格式。答辩讲“统一异常处理”时能说出这么一套,专业度马上就上来了。

接口的路径命名我习惯用资源名词的复数形式,比如/api/products、/api/orders/list,而不是/api/getProductList。然后注意区分@RequestBody和@RequestParam:前端传JSON对象用@RequestBody,传单个参数或表单用@RequestParam。前后端联调时大多数404和参数丢失问题都出在这。

实体类不要直接作为Controller的返回对象。原因很简单:User实体里包含password字段,一返回就把密码哈希暴露给前端了。正确做法是单独建VO,只返回需要的字段。

4.3 前端对接后端:请求封装与路由守卫

前端开发的顺序是:先搭好路由和页面框架,再封装接口请求,然后逐个页面联调。我用axios做了封装,核心逻辑在utils/request.js里:

const request = axios.create({ baseURL: '/api' }); request.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = token; } return config; }); request.interceptors.response.use( response => { const res = response.data; if (res.code === 401) { router.push('/login'); } return res; } );

请求拦截器加token,响应拦截器统一处理业务状态码,这套模式几乎所有管理系统都在用。写好之后,每个页面的接口调用都会非常干净。

路由守卫是另一个容易被忽略的点。在router/index.js里配置全局前置守卫:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.meta.requiresAuth && !token) { next('/login'); } else if (to.path === '/login' && token) { next('/'); } else { next(); } });

这样用户没登录时访问任何需要登录的页面都会被自动弹回登录页。前端路由守卫和后端拦截器配合起来,就是一套完整的前后端鉴权体系,正好可以当答辩素材。

4.4 打包部署:最省事的单jar演示方案

答辩演示时最怕“环境依赖太多”,到了现场电脑上少了某个依赖项目就跑不起来。所以我强烈推荐把前后端打包成单个SpringBoot jar的部署方式。

具体做法是:前端执行npm run build,把生成的dist目录整个复制到SpringBoot项目的src/main/resources/static目录下。然后重新打包后端:

mvn clean package

这样打出来的jar里既包含后端接口,又包含前端静态资源,部署时只需要一个命令:

java -jar cakeshop.jar

浏览器访问http://localhost:8080就能看到整个系统。这个方案不用装Node环境、不用装Nginx、不用配反向代理,对答辩现场的临时电脑来说就是最稳的部署方式。

如果你要部署到云服务器上,就用稍微正规一点的做法:前端dist目录交给Nginx托管,Nginx把/api路径反向代理到SpringBoot的8080端口。这样静态资源和接口分开,后续更新前端页面时不需要重新打包Java应用,维护起来更舒服。

5. 常见问题与排查实录:联调和答辩的实战经验

5.1 联调阶段最容易踩的5个坑

前后端联调基本占据了整个项目开发的一半时间,我把最容易遇到而且搜不到直接答案的问题整理一下。

第一个是跨域报错。虽然vite代理能解决大部分,但如果你直接访问后端的非代理接口,比如在浏览器里打开localhost:8080/api/products,会被CORS策略拦住。解决方式是在SpringBoot的Config里配置一个CorsFilter,允许来自localhost:5173的请求。代理方案和CorsFilter只需要选一个就好,别两个都用导致配置叠加。

第二个是MyBatis-Plus的字段映射问题。数据库里的user_id字段,实体类里是userId,默认开启驼峰转换才认识。在application.yml里一定要加:

mybatis-plus: configuration: map-underscore-to-camel-case: true

第三个是JSON时间格式。后端返回的Date默认是一串时间戳数字,前端显示成“1234567890”非常难看。在实体类时间字段上加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss"),立即解决。

第四个是Vue打包后刷新404。这是因为history路由模式在服务器上没有对应的路径。开发阶段没问题,打包部署后就出现刷新空白或404。要么后端配置转发所有非接口路径到index.html,要么在前端路由里把history改成hash模式,毕设我用后者,一个字符串就搞定。

第五个是图片资源404。商品图片在后端本机某个目录,打包后路径找不到。规范做法是把图片上传路径配置到application.yml里,然后加一个WebMvc的资源映射,把本地目录映射到/upload/**路径。不要用绝对路径写死,否则换机器就废。

5.2 部署运行中那些“环境问题”

部署阶段的问题往往比开发阶段更让人抓狂,因为很多是和本机环境绑定的小瑕疵。

端口占用算是最常见的。8080被其他程序占用时,启动日志会报端口被占用错误,修改application.yml里的端口再重启就行。我习惯改成8090,避免和本地其他服务撞车。

数据库连接失败也是高发问题。MySQL8.0默认的认证插件是caching_sha2_password,而老驱动用的是mysql_native_password,连接时会报“Unable to load authentication plugin”。解决办法是创建连接用户时指定:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';

另一个坑是数据库连接URL忘记带时区参数导致的日期错误。MySQL8.0要求必须声明serverTimezone,我一般用:

jdbc:mysql://localhost:3306/cake_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false

这个配置要写清楚,带utf8字符集和中文字段都没问题,否则中文乱码会折腾得很烦。

还有一点是SpringBoot版本过高导致Maven依赖冲突。如果你在创建工程时选了3.2以上的版本,而项目里又引用了很多自动配置和插件,可能遇到接口方法签名找不到的问题。调试成本高,不如一开始就用2.7.18,把所有精力花在功能开发上。

5.3 答辩前要重点准备的设计题

答辩时的底层原理问题,往往比代码本身更重要。根据我带项目的经验,有几个问题几乎必被问到,模板和答案我整理成了下面这个表。

常见问题推荐回答思路
为什么用JWT不用Session?前后端分离项目后端无状态,JWT不占服务端内存,扩展性好;Session需要共享存储处理多节点问题。
为什么不用数据库物理外键?物理外键会导致插入、删除性能开销,且分布式场景不适用;逻辑外键通过应用层控制,灵活性更高。
订单金额为什么用BigDecimal?double有精度误差,Money类型一般都用decimal,计算都在数据库或后端Java中做,不在前端计算。
登录密码如何保证安全?BCrypt加盐哈希,密码不落库;哈希不是加密,不可逆;就算数据库泄露也无法还原明文。
项目有哪些可扩展的点?可以提优惠券、限时秒杀、消息通知、数据大屏、支付接口真实接入,再从订单状态机扩展退款流程。
购买商品时库存不足如何处理?下单前先校验库存,扣库存用乐观锁或where stock>=quantity的CAS语句,防止超卖。

这些问题在论文里也有对应章节,提前准备好,答的时候用三句话讲清原理,再补一句“我在项目里是怎么做的”,基本就能稳住。

最后再分享一个个人经验:如果你拿到了一套现成源码,不要急着改业务,先把它跑起来,再按“登录入口->商品列表->购物车->下单->后台管理”的顺序把代码读一遍,搞清楚数据流是怎么走的。然后在运行数据上做几笔测试,看看库存、订单、统计有没有一致性问题。我自己每次拿到新项目都是这套流程,看起来多花了半天,实际上后面改起来速度快很多。这个蛋糕销售系统也是一样,把核心链路的数据流转理解了,剩下的二次开发和答辩准备都只是时间问题。

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

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

立即咨询