SpringBoot+Vue网上购物系统毕设实战:从零搭建到答辩全记录

发布时间:2026/10/15 21:47:47
SpringBoot+Vue网上购物系统毕设实战:从零搭建到答辩全记录 刚拿到这个毕设题目的时候我第一反应是“又是网上购物系统”。但等我把整个项目从零搭完发现这个看起来烂大街的题目其实恰恰是最能检验Web开发综合能力的“试金石”。一个基于SpringBoot与Vue.js的在线商城交易平台不是简单的增删改查它涵盖了数据建模、接口设计、前端交互、权限控制、订单状态流转、支付对接等多个环节几乎把JavaWeb开发的主干知识全部串了一遍。这篇文章不是从教科书角度讲原理而是把我实际做完这套系统的思路、步骤和踩坑记录写出来。不管你基础一般还是已经入门只要打算用SpringBootVue做毕设或项目实战这篇内容都可以直接参考。1. 为什么“网上购物系统”能成为毕设常青树1.1 一个题目覆盖完整Web开发链路很多人觉得购物系统太老套想选个“智能推荐”“大数据分析”之类的题目来显示技术含量结果往往在开题之后就卡住了。我的看法是毕设评分最看重三样东西——选题价值、技术难度、工作量而网上购物系统这三样都能占住关键看你怎么做。从开发链路看商城项目要求你完成数据建模用户、商品、订单、购物车这些实体之间有明确的主外键关联要求你设计RESTful接口前后端通过JSON交互要求你处理前端动态渲染Vue组件、路由、状态管理全都要用上还要求你搞定权限控制普通用户和管理员是不同的角色更别说订单状态机、支付回调这些东西了每一块单独拿出来都能当成一次小型面试题。我做这套系统时的真实感受是它不像纯管理系统的CRUD那样枯燥也不像算法类项目那样脱离实际业务。商城每一个功能都对应真实电商场景做起来有画面感。能力一般的同学可以做简化版单商家、纯CRUD、页面直接套模板能力强的同学可以做多级商品分类、SKU库存扣减、JWT鉴权、分布式会话、管理端数据图表。同一套题深浅自由这也是它长盛不衰的根本原因。1.2 难度分层与适合人群我接触过的同学里做这个题目的无非三种情况。第一种是后端还没完全吃透主要想通过项目把SpringBoot的流程走通。这种情况建议把重点放在接口代码质量和数据库设计上页面可以少写一点用简化版Vue页面展示核心功能就够。第二种是前后端都想练目标是一份完整的前后端分离项目经验。这种情况最适合按本文的完整方案来做SpringBoot提供APIVue独立部署联调时自己处理跨域、Token、文件上传。第三种是想拿高分、冲优秀毕设。这种情况不能只做基础功能至少要有订单事务一致性处理、库存防超卖方案、JWT拦截器、以及一个有说服力的亮点模块比如数据可视化报表或者基于Redis的热点商品缓存。很多人问这个题目是不是太“大众化”了我的回答是题目大众化不可怕实现深度大众化才可怕。同一个题目有人做到60分有人做到90分评阅老师一眼就能分辨出来。2. 技术路线权衡从JSP到SpringBootVue的取舍2.1 为什么放弃传统SSMJSP前几年JavaWeb毕设的主流配置是Spring SpringMVC MyBatis页面用JSP。学校教材更新慢很多实验室项目还在用这种方式。但说句实在话现在出去找工作或者看开源社区的热门项目已经很少有人再从JSP写页面了。JSP的痛点在于前后端耦合太严重。Java代码和HTML标签混在一起改个页面样式要重启服务接口和页面根本没法独立调试。SpringBoot解决了后端配置繁琐的问题把传统SSM的三层XML配置压缩成少量注解和自动配置内嵌Tomcat让项目一键启动。这不是要抛弃SpringMVC的思想反而SpringBoot底层就是SpringMVC那一套学校教的内容没有浪费。对于毕设来说还有一个很现实的考量搭环境的时间越少留给业务实现的时间越多。SpringBoot Vue这个组合能让前后端开发完全并行我当时就是先和后端同学约定好接口格式两个人同时开工联调阶段反而比传统方式更顺利。2.2 前后端分离到底带来了什么有人觉得前后端分离只是个时髦概念放到毕设项目里意义不大。实际上它的价值非常具体。第一页面不再依赖后端模板引擎渲染前端是一个独立工程用Vite启动后浏览器实时刷新开发体验比JSP不知道舒服多少。第二后端接口可以单独用Postman测试不需要页面配合出现问题时能快速定位是接口问题还是页面问题。第三管理端和用户端虽然是两套界面但Vue组件化之后很多公共组件可以复用比如商品卡片、分页组件、弹窗确认框。审阅老师看项目演示的时候一般会要求启动一个后端服务和一个前端服务看到前后端分离的工程结构本身就是一个印象分。2.3 技术栈清单与选型理由下面是我实际使用的技术栈清单每一项的选择都有明确理由。层次技术选型理由后端框架SpringBoot 2.7长期维护稳定版自动配置简化开发社区资料多ORM框架MyBatis-Plus单表CRUD不用手写SQL分页插件好用数据库MySQL 8免费、普及度高支持事务和复杂查询登录鉴权JWT Spring MVC拦截器无状态登录前端存储Token适合前后端分离前端框架Vue 3 Vite组合式API写起来灵活Vite启动极快UI组件库Element Plus表格、表单、弹窗等组件开箱即用构建工具MavenJava项目主流依赖管理方案支付模块沙箱环境模拟毕设场景无需真实资金但流程必须完整这里有一个实际注意事项如果学校硬性要求使用SSM框架也不要慌你可以用SpringBoot的底层能力去兼容SpringMVC的编程模型只是在开题报告里把技术路线描述成“基于Spring生态的JavaWeb方案”答辩时重点讲清Controller、Service、Mapper的分层逻辑基本都能过关。3. 商城系统的核心模块拆解与数据库建模3.1 用户端功能模块的边界划分前台用户端并不是模块越多越好而是要把每个模块的边界理清楚。我最终保留的用户端模块是这些注册登录、首页商品展示、商品分类筛选与搜索、商品详情、购物车、确认订单页、订单列表与订单详情、取消订单与确认收货、个人中心地址管理。听起来很常规但每一个模块拆开都有细节。比如商品详情页要展示图片轮播、价格、库存、规格参数购物车要支持修改数量、勾选结算、删除条目确认订单页要选择收货地址、展示商品清单、计算总金额。如果时间紧张模块优先级应该这样排商品浏览必须完整这是门面→ 下单主链路注册登录、购物车、订单必须打通→ 个人中心地址管理、订单操作→ 辅助功能搜索、分类筛选。先把主干跑通再考虑锦上添花。3.2 管理端功能模块的优先级排序管理端是很多人容易轻视的部分但老师演示时第一件事往往就是打开后台看你能不能添加商品、修改价格、处理订单。管理端我建议按顺序完成四块功能。商品管理是核心中的核心包括商品维护、上下架、库存修改这些直接支撑用户端展示订单管理紧随其后包括订单列表、查看详情、发货操作、取消订单分类管理相对简单但必须做否则商品无处归类用户管理只需要做到分页列表和状态禁用即可不要过度设计。如果还有精力可以在后台加一个简单的数据统计页面统计商品数量、用户数量、订单总量、销售额总和。这个页面在答辩时特别出彩因为它让系统看起来像一个完整的产品而不是一堆功能的拼凑。3.3 数据库建模最容易被卡住的关键点数据表设计决定了项目能走多远。我第一版表结构就是随手建的结果做到一半频繁加字段差点推倒重来。后来我按下面的核心表重新建模一次到位。核心表包括用户表user、商品分类表category、商品表product、购物车条目表cart_item、订单主表orders、订单明细表order_item、收货地址表address、管理员表admin。-- 订单主表 CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单编号, user_id BIGINT NOT NULL, total_amount DECIMAL(10,2) NOT NULL COMMENT 订单总金额, status TINYINT NOT NULL COMMENT 0待付款 1已付款 2已发货 3已完成 4已取消, receiver_name VARCHAR(50) NOT NULL, receiver_phone VARCHAR(20) NOT NULL, receiver_address VARCHAR(200) NOT NULL, create_time DATETIME NOT NULL, pay_time DATETIME, update_time DATETIME ); -- 订单明细表 CREATE TABLE order_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL, product_id BIGINT NOT NULL, product_name VARCHAR(100) NOT NULL COMMENT 商品快照名称, product_image VARCHAR(255), price DECIMAL(10,2) NOT NULL COMMENT 成交单价, quantity INT NOT NULL, subtotal DECIMAL(10,2) NOT NULL, KEY idx_order_id (order_id) );为什么要拆成订单主表和订单明细表因为一个订单对应多个商品每个商品的单价、数量、小计必须单独记录。这里有个很容易忽略的细节订单明细里的商品名称和价格是“快照”不是外键关联实时商品表。如果商品后来改了名字或改了价格历史订单里的记录也必须保持不变这就是快照字段存在的意义。金额字段必须用DECIMAL(10,2)Java实体里用BigDecimal绝不能使用double或float。用double存钱累计几笔之后就会出现0.0000004这种精度问题这在订单系统里是不可接受的。商品表一定要留逻辑删除字段deleted不要物理删除。因为订单表可能还引用着这个商品ID一旦物理删除关联查询就断了。逻辑删除的做法是加一个TINYINT字段0正常1已删除查询时统一过滤。4. 从零搭建的完整实操记录4.1 后端工程初始化与代码组织创建SpringBoot项目时建议直接使用Spring Initializr。JDK选8或11都可以依赖选Spring Web、Lombok、Validation然后手动引入MyBatis-Plus和MySQL驱动。dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependencyapplication.yml里的核心配置要写清楚数据源、MyBatis-Plus的驼峰映射和日志输出。spring: datasource: url: jdbc:mysql://localhost:3306/shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0包结构我推荐这样组织controller、service、mapper、entity、dto、config、common。controller只负责接收参数和返回结果业务逻辑全放servicemapper只写数据库操作。这个分层能在答辩时直接展示“高内聚低耦合”的工程思想。一开始就要写统一返回结果类R和全局异常处理器不然后面几十个接口会写出一堆重复代码。Data public class RT { private Integer code; private String msg; private T data; public static T RT success(T data) { RT r new R(); r.setCode(200); r.setMsg(success); r.setData(data); return r; } public static T RT fail(Integer code, String msg) { RT r new R(); r.setCode(code); r.setMsg(msg); return r; } }全局异常处理的思路是统一捕获业务异常和未知异常避免把堆栈信息直接暴露给前端。4.2 前端工程初始化与路由设计前端我选择Vue3 Vite Element Plus。创建工程之后先安装依赖然后规划路由结构。我的做法是把用户端和管理端拆成两套布局通过路由前缀区分。用户端路由长这样const routes [ { path: /login, component: Login }, { path: /, component: Layout, redirect: /home, children: [ { path: home, component: Home }, { path: cart, component: Cart, meta: { requiresAuth: true } }, { path: orders, component: OrderList, meta: { requiresAuth: true } }, { path: product/:id, component: ProductDetail } ] } ];管理端单独设一块区域路径以/admin开头并且路由守卫要求管理员角色。前端路由守卫只负责用户体验真正的权限校验必须在后端完成这个意识要建立起来。Axios封装是我强烈建议认真做的一步。给axios实例设置baseURL为/api请求拦截器从localStorage取Token并放进请求头响应拦截器统一处理业务码。这样所有页面调用接口时不用重复写Token逻辑代码会清爽很多。service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] token; } return config; }); service.interceptors.response.use( response { const res response.data; if (res.code 200) { return res; } if (res.code 401) { localStorage.removeItem(token); router.push(/login); } return Promise.reject(new Error(res.msg)); }, error Promise.reject(error) );4.3 前后端联调跨域、Token与图片路径前后端分离项目里联调阶段最容易出问题的就是跨域和Token传递。开发环境跨域我推荐用Vite的proxy代理而不是在后端开CORS。配置vite.config.jsserver: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }前端所有请求都走/api前缀Vite在开发时把它转发到后端8080端口浏览器看到的始终是同源请求跨域问题就绕开了。Token传递要注意请求头的命名一致。登录成功后后端返回token前端存到localStorage之后每次请求在拦截器里带上。后端用一个拦截器校验请求头里的token解析出用户ID和角色。这里有一个细节拦截器是Spring MVC层面的实现比过滤器更合适因为它可以注入Service去查数据库而过滤器拿不到Spring容器管理的Bean。文件上传路径是个容易翻车的细节。商品图片上传后如果直接存到项目运行目录重新部署可能丢失。建议把上传目录放到项目外部的固定路径然后通过后端配置虚拟路径映射对外暴露访问地址。Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/uploads/**) .addResourceLocations(file:D:/shop-uploads/); } }这样前端图片地址可以写成window.location.origin /uploads/xxx.jpg无论开发还是部署都一样。5. 毕设级项目必须避开的坑5.1 功能堆砌先主链路后亮点第一个坑是功能规划失控。商城能加的东西太多了秒杀、优惠券、积分、推荐算法每个听起来都能当亮点。但毕设周期有限功能每多一个开发和调试成本就翻一倍。我见过有同学一口气规划了十几个模块结果到答辩前一周购物车还是坏的。我建议把项目切分成“必须完成的主链路”和“有余力才做的亮点区”。主链路是注册登录 → 浏览商品 → 加入购物车 → 下单 → 支付 → 管理端发货 → 确认收货。这条链路里任何一个环节断了系统都是不能演示的。先把主链路打磨到稳定无bug再考虑秒杀、报表这类加分项顺序不能颠倒。5.2 金额精度用double算账就是给自己埋雷金钱计算是电商系统最容易出bug的地方。如果金额字段在Java里用double存数据库用FLOAT存结算时加减乘除会出现很诡异的精度问题比如订单金额总和变成了199.9999999。这个问题不是玄学是二进制浮点表示的天生缺陷。Java侧必须统一使用BigDecimal数据库侧金额字段统一DECIMAL(10,2)前端传递金额参数时后端一律用字符串接收再转BigDecimal。计算总价时用BigDecimal的add和multiply方法不要用原生运算符号。这个规则从建表第一天就要执行不要等出了问题再改。5.3 订单事务多表操作不能掉以轻心下单这个操作涉及多张表插入订单主表、插入订单明细表、扣减商品库存、清空购物车。任何一个步骤失败前面的操作就要撤销否则就会出现“订单没生成但库存被扣了”或者“库存扣了但订单不存在”的脏数据。解决思路就是给下单方法加Transactional事务注解让这四步操作在同一个事务里要么全部成功要么全部回滚。Transactional(rollbackFor Exception.class) public Long createOrder(OrderCreateDTO dto) { // 1. 生成订单主表记录 Orders order buildOrder(dto); ordersMapper.insert(order); // 2. 批量插入订单明细 for (OrderItemDTO item : dto.getItems()) { orderItemMapper.insert(buildItem(order.getId(), item)); } // 3. 扣减库存 stockMapper.deductStock(item.getProductId(), item.getQuantity()); // 4. 清空购物车 cartMapper.deleteByUserId(dto.getUserId()); return order.getId(); }但这里有几个事务失效的经典场景必须提醒同类内部方法调用会绕过Spring的代理导致事务注解不生效所以事务方法必须从外部入口调用异常如果被try-catch吃掉事务也无法回滚所以catch之后要抛出RuntimeException方法必须是public的private方法上的Transactional根本没意义。5.4 登录鉴权后端校验才是安全底线前端路由守卫只是控制页面跳转真正的接口安全必须后端把关。我见过一些项目只在前端判断了有没有token后端接口全部裸奔这等于把整个系统的数据都暴露了。后端实现鉴权有三步写一个JWT工具类生成和解析token写一个Spring MVC拦截器从Header里取token并解析用户信息在WebMvcConfig里注册拦截器设置哪些接口放行、哪些接口必须登录。管理员接口还要额外校验角色。这里我建议把用户信息放到ThreadLocal里这样后续Service方法可以直接从上下文取当前用户ID不用每个接口都传userId参数。下线或超时场景通过token过期时间控制比较简单直接。5.5 上传图片的路径坑本地能跑部署就翻车开发时图片上传到本地目录没感觉换一台电脑或者重新部署就会出现图片丢失、路径错误的问题。解决方式上面说过用外部固定目录存储文件再通过虚拟路径映射对外暴露。这样上传文件和项目本身是分离的重新打包部署也不会影响图片数据。从环境变量读取上传路径也是一种做法只不过毕设场景用固定路径足够。图片访问要禁止用户直接访问磁盘绝对路径绝对路径一旦暴露也容易成为安全隐患。统一走/uploads虚拟路径是最稳的。6. 答辩准备与项目复盘6.1 老师最常追问的几个“为什么”答辩环节考验的不只是代码能不能跑更考验你对项目整体逻辑的理解程度。我整理了老师最常见的几个追问和合适的回答思路。追问角度合适的回答思路为什么用SpringBoot自动配置减少XML配置内嵌Tomcat让部署简单生态成熟底层依然是SpringMVC思想购物车为什么要存数据库保证用户换设备后数据不丢结构清晰诚实说明Redis方案更优但该场景不需要引入额外组件怎么防止库存超卖当前方案是下单时同步扣减库存更优方案是乐观锁版本号或Redis预扣减订单状态怎么流转待付款 → 已付款 → 已发货 → 已完成以及取消分支配合状态字段流转支付怎么做的模拟沙箱环境阐述真实支付流程中回调验签的核心思路即可数据库为什么拆分主表和明细表主表记录订单整体信息明细表记录商品快照防止商品更改后影响历史订单这些问题只要在开发过程中想清楚“为什么这么做”答辩时就能从容应对。最忌讳的是只背答案不理解因为老师的追问往往一环扣一环。6.2 做完这个项目之后还能往哪个方向扩展如果你做完上述功能还有余力或者想把项目写进简历当作项目经验可以考虑这几个方向。第一个是商品搜索升级把数据库LIKE查询改成Elasticsearch或者对接一个轻量级搜索引擎展示对中文分词和搜索相关性的理解。第二个是热点数据缓存给首页商品和热门商品加Redis缓存讲解缓存穿透、击穿、雪崩的应对思路。第三个是做管理端的数据看板用ECharts展示近七天的订单趋势、品类销售分布。第四个是增加多商家模块把单平台升级成平台商家用户的三端系统这只是把表结构扩一扩但答辩的复杂度立刻就上去了。扩展要挑一个方向做透不要四个都做一半。6.3 一个过来人的实话做这个项目最大的体会是“完成”和“完美”之间差的往往不是技术而是对业务细节的态度。一个能正确计算金额、库存准确、订单状态正确的商城系统比一个页面漂亮但逻辑漏洞百出的系统有价值得多。我个人比较推荐的做法是先完整跑通一条主链路每一个环节都记录下来再回头去补边界情况。比如购物车没勾选能不能下单、库存不足时提示是否友好、订单取消后库存是否回滚这些细节才是真正拉开差距的地方。这套项目的架构和代码我现在还会拿来当作讲前后端分离的典型例子因为它涵盖的知识点足够多但又不至于复杂到让人望而却步。如果你正在为毕设定题发愁不妨就把这个“老题目”做出一点新意把主链路做扎实把一个亮点做深入做成90分的版本。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询