
做计算机毕业设计SpringBoot Vue.js 的电商系统大概是每年出现频率最高的题目之一。我带过的学弟学妹里一开口就是“老师我想做个商城”的能占三分之一。为什么这个选题这么热门因为它的技术栈足够主流、功能边界足够清晰而且演示效果非常直观——登录、逛商品、加购物车、下单、后台管理整套流程走下来答辩老师一眼就能看出你完成的是一个完整闭环的电商系统。这篇文章我会从系统设计、后端实现、前端开发、打包部署到避坑实录完整拆解一套可以照着做的毕业设计级方案。这套方案适合计算机相关专业毕业生、准备用SpringBoot和Vue.js做课设的在校生以及刚学完SSM想找个练手项目的开发者。不管你是想照搬思路还是准备改进成自己的设计读完应该能少踩不少坑。1. 项目整体设计与思路拆解1.1 为什么这个选题值得做先说结论电商系统是本科毕业设计里“性价比”最高的选题之一。第一参考资源极多。GitHub上有大量开源的商城项目无论遇到什么问题基本都能搜到解决方案这对毕设来说很关键——你不太容易把自己卡死。第二技术栈足够主流。后端SpringBoot几乎成了Java后端开发的默认选项前端Vue.js也是国内中小型公司和培训机构最常教的框架。这个组合写在简历上不会有“过时”的感觉。第三功能范围可大可小。本科阶段不需要做到京东淘宝的水平用户端加管理端跑通核心交易流程就能构成一个完整的毕设故事。一个典型的电商系统会覆盖用户、商品、购物车、订单、支付、后台管理等场景每个模块都能做出深度的东西。这就是答辩时的底气——你既能讲“业务闭环完整”又能单独拎出某个模块讲“技术上有细节”。1.2 前后端分离架构选它而不是JSP的三个原因很多学校教材里教的还是JSP或者Thymeleaf模板渲染那一套但我在实际开发中强烈建议毕设用前后端分离架构。原因有三更贴近企业真实开发方式。现在做项目通常在接口层面就是前后端分工后端只负责输出JSON前端管展示和交互。开发效率反而高。虽然是单人毕设但前后端分离后你可以先定好接口文档再分别写代码定位问题更快。部署方案灵活。前端构建出来是一堆静态文件想放进后端static目录也行想单独放到Nginx上也行。对比维度前后端分离SpringBoot Vue.js传统单体JSP/Thymeleaf页面渲染后端返回JSON前端渲染后端直接渲染HTML开发分工前后端可并行前后端耦合较深部署方式静态资源与后端可分开部署通常在同一个Web容器调试方式浏览器Network直接看接口请求经常要连着模板一起查答辩观感“工程化”思路清晰容易被认为“做得像课设”当你在答辩时说出“我们采用前后端分离架构后端通过RESTful接口提供服务前端使用Vue进行SPA页面渲染”至少能给评委一个“这学生懂现代开发模式”的第一印象。1.3 功能模块怎么划分必做与加分项毕设最忌讳的就是功能铺太大最后烂尾。我建议把电商系统分成两个端来做用户端C端注册、登录商品分类浏览与搜索商品列表分页、商品详情展示购物车管理添加、修改数量、删除、选中)下单选择收货地址、生成订单个人中心订单列表、取消订单、确认收货管理端B端商品管理新增、编辑、上下架、删除分类管理订单管理查看订单、修改发货状态这些属于“必做核心闭环”。如果学有余力再加这些加分项支付宝/微信沙箱支付不用真开商家号Redis缓存首页热门商品图形验证码或短信验证码模拟即可商品库存扣减与超卖防护Excel批量导出订单我的建议是先把核心闭环跑通有余力再上亮点。一个能稳定走通“登录-浏览-加购-下单-管理”流程的系统绝对比一个半吊子的“全功能平台”更值钱。2. 后端核心设计与实现2.1 技术选型详解不要盲目用最新版很多同学一上来就装最新版Spring Boot 3.x和JDK 21结果配置半天跑不起来。我用实际经验告诉你如果不是为了写论文里某个“新特性”做亮点毕设首选JDK 8 Spring Boot 2.7.x。为什么因为Spring Boot 3.x要求JDK 17及以上有些老教程、老依赖和新版本之间存在兼容问题。MyBatis-Plus在Spring Boot 2.7下用得贼稳Swagger/Knife4j也没有版本折腾的风险。技术服务于项目不服务于“追求最新”。推荐组合如下组件选择说明JDK1.8稳定、兼容性最好Spring Boot2.7.x成熟稳定版本MyBatis-Plus3.5.x避免手写大量基础SQLMySQL8.0主流性能足够Redis可选用于缓存和验证码JWTjjwt 0.9.x登录鉴权接口文档Knife4jSwagger增强版界面更友好工具库Lombok简化实体类代码你会发现这套技术栈在网上几乎能找到所有答案。毕设期间时间宝贵把时间花在调试框架版本上非常不值。2.2 数据库设计决定系统上限的地方后端开发里有一句话叫“设计不好数据库后面写接口全是泪”。电商系统核心表并不多大概就是这些用户表user分类表category商品表product购物车表cart收货地址表address订单表orders订单项表order_item下面给一个可以直接落地的核心建表SQL思路。用户表不需要太复杂CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(255) NOT NULL COMMENT 密码加密存储, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像, phone varchar(20) DEFAULT NULL COMMENT 手机号, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY idx_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;商品表要注意几个点价格用decimal(10,2)避免浮点精度问题库存量用int需要一个status字段做上下架逻辑同时保留create_time和update_time。CREATE TABLE product ( id bigint(20) NOT NULL AUTO_INCREMENT, category_id bigint(20) DEFAULT NULL, name varchar(255) NOT NULL, subtitle varchar(255) DEFAULT NULL COMMENT 副标题, main_image varchar(500) DEFAULT NULL COMMENT 主图, detail text COMMENT 商品详情, price decimal(10,2) NOT NULL, stock int(11) NOT NULL DEFAULT 0, sales int(11) NOT NULL DEFAULT 0 COMMENT 销量, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 1上架 0下架, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;订单表和订单项表是重中之重。订单表里我会冗余一份收货地址快照收货人、电话、详细地址直接存进订单而不是通过外键去关联地址表。这样做符合实际电商场景——就算用户后来改了默认地址历史订单展示的仍然是当时下单的地址。订单项表则要把商品名称、商品图片、单价、数量都冗余进去因为商品可能会被删除或改名。订单状态我习惯用数字枚举0待支付、1已支付、2已发货、3已完成、4已取消。前后端统一约定显示文案代码里可以做一个枚举类维护。2.3 后端工程结构一个可复用的SpringBoot分层模板我见过不少同学一上来就把所有代码堆在Controller里。刚开始看着挺痛快到后面改一个逻辑要翻半天下载而且答辩时很难讲清楚。推荐的分层结构是下面这样com.mall ├── controller // 接收请求返回结果 ├── service // 业务逻辑 │ └── impl ├── mapper // MyBatis-Plus接口 ├── entity // 数据库实体 ├── dto // 接收前端参数的对象 ├── vo // 返回给前端展示的对象 ├── config // 全局配置跨域、拦截器、MyBatis-Plus分页 ├── common // 统一返回结果类Result、状态码 ├── exception // 全局异常处理 ├── interceptor // 登录拦截器 └── util // JWT、密码加密等工具请求流转是这样的Controller接收参数-参数转成DTO-Service层处理业务逻辑-Mapper操作数据库-返回VO数据-统一包装成Result返回给前端。为什么要有DTO和VO而不是直接传Entity因为数据库实体类不代表前端需要的数据结构。比如前端只需要商品的名称、价格、主图但实体里还带着detail详情直接用实体返回不仅字段冗余还有可能暴露不该暴露的数据。毕业设计的代码里体现这一层会很加分。2.4 登录鉴权JWT 拦截器是怎么配合的如果你想在毕业设计里躲开Session用JWT做认证恭喜你选对了。JWT的本质是解决一个问题前后端分离后后端服务不再维护Session状态怎么识别请求是否登录答案是登录成功后签发一个包含用户标识的加密令牌前端保存令牌之后每次请求都在请求头里带回后端验签之后就能确认身份。Token由三段组成Header加密算法信息、Payload存放用户id、过期时间等、Signature签名。登录的核心流程用户提交用户名密码后端校验密码用BCrypt比对不要用MD5明文成功后根据用户ID生成Token前端把Token存到localStorage后续请求头部带上Authorization: Bearer token后端拦截器校验Token若有效则放行并把用户信息放到ThreadLocal拦截器里注意设置白名单。登录、注册、商品浏览、商品搜索这些接口不需要登录就能访问public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从request头中获取token String token request.getHeader(Authorization); // 校验失败则返回401由前端跳转到登录页 } }注册拦截器时排除掉 /api/user/login、/api/user/register、/api/product/** 等路径。不要自己死记路径要养成分模块梳理白名单的习惯。2.5 典型接口商品分页查询与购物车商品列表是电商系统里最常用的接口我用MyBatis-Plus实现代码非常简洁GetMapping(/page) public ResultIPageProductVO page(RequestParam(defaultValue 1) int pageNum, RequestParam(defaultValue 12) int pageSize, RequestParam(required false) Long categoryId, RequestParam(required false) String keyword) { LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); wrapper.eq(categoryId ! null, Product::getCategoryId, categoryId) .like(StringUtils.hasText(keyword), Product::getName, keyword) .eq(Product::getStatus, 1) .orderByDesc(Product::getSales); PageProduct page productMapper.selectPage(new Page(pageNum, pageSize), wrapper); return Result.success(page); }分页为什么好用因为它是所有列表页的通用逻辑商品、订单、用户管理全部复用这一套思路。这也是MyBatis-Plus分页插件存在的意义——不用手写LIMIT也不用数页码公式。购物车这块有个经典问题存数据库还是存Redis我的建议是毕设阶段直接存MySQL表里逻辑透明断电不丢数据也方便你展示“购物车表增删改查”。如果你想在答辩里聊Redis如何提高并发访问吞吐、如何保存用户会话可以在热门商品缓存、验证码缓存这些点去体现而别把购物车往Redis里塞否则查询购物车时还得解决缓存和数据库一致性问题工作量直接翻倍。购物车接口不要每张表裸写SQL。加购时先按userId和productId查是否已存在相同记录存在则累加数量不存在才新增。删除和修改数量用主键id操作即可。查询购物车列表时要联表查出商品的最新价格、图片、上下架状态而不是直接拿购物车表里的快照价否则用户一看购物车价格变了又不理解。3. 前端Vue.js核心实现3.1 前端工程搭建与目录规划如果你是从零开始我建议直接走 Vue3 Vite Element Plus Pinia Vue Router Axios。Vite启动速度比Webpack快一个量级毕设开发时改代码的热更新体验非常舒服。工程目录我习惯这样建src ├── api // 所有接口请求函数 │ ├── user.js │ ├── product.js │ ├── cart.js │ └── order.js ├── assets // 图片、样式 ├── components // 公共组件商品卡片、分页、空状态 ├── router // 路由配置 ├── store // Pinia状态管理 ├── utils │ └── request.js // axios封装 ├── views │ ├── home // 首页 │ ├── product // 商品列表和详情 │ ├── cart // 购物车 │ ├── order // 订单 │ ├── login // 登录注册 │ └── admin // 后台管理 └── App.vue接口函数统一放在api目录好处是页面里不用分散到处写axios调用。组件抽不抽商品卡片组件强烈建议抽出来因为首页热销商品、商品列表页、秒杀区域可能都会用同一套展示样式。抽组件是前端评分的重要观察点之一。3.2 页面与组件拆解你不需要花大量精力去原创UI用Element Plus组件库重点是把业务逻辑做扎实。用户端核心页面建议按下面这样拆首页轮播图商品分类入口热销商品列表。热销商品直接调商品分页接口传sorthot。商品列表页左侧分类菜单右侧根据分类展示商品支持关键词搜索、价格排序、分页。商品详情页展示主图、价格、库存、数量选择器两个关键按钮是“加入购物车”和“立即购买”。购物车页勾选商品、修改数量、删除商品底部计算合计金额。注意合计金额要跟随勾选状态和数量联动。结算页选择收货地址、展示商品明细和总价、提交订单。订单列表页按订单状态Tab切换待支付订单可以取消已发货订单可以确认收货。管理端页面则相对简单商品管理表格支持搜索、上下架、弹窗编辑、订单管理表格查看详情、修改状态。不要小看“空态设计”购物车为空、订单列表为空时让页面显示友好的提示和去逛逛按钮。细节上的完整度很影响答辩观感。3.3 axios封装与路由守卫前端最容易被小看但其实特别重要的是请求封装。在utils/request.js里做统一处理const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动携带token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer token } return config }) // 响应拦截器统一处理业务码 request.interceptors.response.use(response { const res response.data if (res.code ! 200) { // 401处理清除登录状态跳转登录页 return Promise.reject(res) } return res })路由守卫也很重要。前端路由需要判断哪些页面需要登录才能访问比如购物车、结算页、个人中心、管理后台。在router.beforeEach里做检查router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })Token存在localStorage而不是内存中的原因是刷新页面后Vue实例重建内存数据会丢而localStorage持久化不会丢。毕业设计演示时最尴尬的瞬间莫过于刷新后用户状态消失然后又重新登录。3.4 联调细节跨域、Token、字段不一致前后端分离最折腾人的就是联调。说实话后端接口通了前端页面也写完了联调阶段依然会有很多小问题。我踩得最多的三类跨域问题。开发环境下你的Vite运行在5173端口后端在8080端口浏览器默认跨域拦截。最简单的解决方式是配置前端代理让请求走前端开发服务器转发// vite.config.js server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }另外后端也得配置CorsConfig允许跨域两层的保险更稳。字段命名不一致。后端喜欢createTime前端写成create_time结果页面显示undefined。解决方法是联调时先确认后端的VO字段或者用Knife4j/Apifox先定义好接口文档。强烈建议你从项目第一天就在Apifox里维护接口文档前端按文档写后端按文档做。请求方式不匹配。前端用POST body传JSON后端却用RequestParam接参数就会报参数缺失。先约定复杂对象用RequestBody JSON简单参数用RequestParam这类问题能少一半。4. 实操过程与避坑指南4.1 开发环境与版本搭配最后这里给一套我实测下来非常稳定的版本组合照着搭基本不会再被环境坑工具版本建议JDK1.8Maven3.6.3 或 3.8.xSpringBoot2.7.18MyBatis-Plus3.5.2MySQL8.0.xNode.js16 ~ 18Vue CLI / ViteVite 4.x Vue 3.4Element Plus2.x特别提醒一句Node版本不要太追新。Vite在Node 18和20上都没有问题但个别情况下旧项目在Node 20会有兼容问题。如果你跟着网上的旧教程走Node 16最稳妥。4.2 按照什么节奏开发不容易烂尾结合我多年帮人改毕设的经验给你一个5周节奏参考。别觉得5周太久——你真正有效的开发时间可能没这么多。第1周梳理需求列出功能清单画出页面草图。完成数据库建表和Apifox接口文档初版。第2周搭建SpringBoot工程完成统一返回结构、全局异常处理、登录注册、商品分类和商品管理接口。第3周完成购物车和订单模块、收货地址接口管理端商品管理接口的完善。第4周搭建Vue工程完成用户端核心页面从登录到商品浏览再到购物车下单流程能走通。第5周完成管理端页面整体联调测试打包部署录制演示视频补文档。这个节奏的核心是“后端先跑通前端再接入”。如果你先把前端写得漂漂亮亮结果没有后端数据最后只能把数据死写在页面里答辩时稍微一改URL就露馅。4.3 打包部署两种方式毕设基本都要现场演示建议你把系统完整部署到本地或者云服务器上。这里给出两种部署方案。方案一前端打包后放进SpringBoot最简单# 前端构建 npm run build # 将dist目录下的文件复制到后端 static 目录 cp -r dist/* src/main/resources/static/ # 后端打包 mvn clean package # 启动 java -jar target/mall.jar --server.port8080然后访问http://localhost:8080就能看到整个系统。需要注意一个经典坑Vue Router如果使用history模式刷新二级页面时会404。最简单的方法是路由直接用createWebHashHistory()路径变成#/home虽然丑一点但永远不会404。如果坚持用history模式需要在后端加一个转发控制器把非API请求都转发到index.html。方案二前后端分别部署更工程化后端打包成jar单独运行前端构建出的dist目录放到Nginx的html目录。Nginx里配置反向代理location /api/ { proxy_pass http://localhost:8080/api/; }这样浏览器里访问Nginx地址请求后端时Nginx自动转发也没有跨域问题。这个方案虽然多配一个Nginx但答辩时你可以理直气壮地说“我把前后端拆开部署用Nginx做反向代理”。整体技术感更强。4.4 常见问题与排查实录这部分都是我在实际写这种系统时踩过的坑整理成速查表建议收藏。问题表现常见原因解决办法启动报“Web server failed to start. Port 8080 was already in use”端口被占用换端口server.port8081或者杀掉占用进程连接数据库报错Access denied密码错误或用户权限不对检查MySQL账号密码用客户端工具先测通查询时区报错MySQL连接URL没带时间参数JDBC URL加serverTimezoneAsia/Shanghai前端请求接口后报CORS错误后端未允许跨域后端加全局CorsFilter或前端配置代理登录后刷新页面用户信息丢失Token只存在Vue内存变量里存localStorage刷新后重新读取JWT解析报错Signature mismatch秘钥不一致或token被截断检查前后端秘钥是否一致请求头格式是否正确Maven下载依赖巨慢默认源在国外在settings.xml配置阿里云镜像商品图片不显示图片用了绝对路径指向本地磁盘用后端静态资源映射或把图片放到static/upload目录下npm install报错Node版本过高或版本解析冲突按推荐版本降级Node删除node_modules重新安装排查的建议就一条先看后端日志再看Network网络面板。后端日志能告诉你是不是参数没接住、SQL报错、NPENetwork面板能告诉你请求有没有发出去、响应码是什么、返回体是什么。70%的联调问题在这两步都能定位到。5. 最后给你两个很小但很管用的习惯做毕设和做企业项目最大的区别是你不仅要写代码还要在十几分钟答辩里让评审老师相信这系统是你独立完成的。我自己带过不少人有两个习惯每次都推荐。第一个习惯是边做边截图记录。把数据库设计草图、接口调试截图、页面效果图、功能测试录屏都存在一个项目文件夹里。等到写论文时你才知道这些东西有多香。第二个习惯是答辩前录一份完整的演示视频存到手机里。线上答辩或者现场投影出问题时视频一秒顶上。哪怕只是按操作流程走一遍也能兜住最尴尬的演示事故。电商系统这个题目虽然常见但深入进去你会发现它是学习后端开发最好的练兵场你需要考虑数据结构、接口设计、状态流转、用户体系、交易链路。把这个流程跑通之后后续你想研究Spring Cloud微服务拆分、Redis缓存、消息队列削峰都可以往这套系统上叠加。起步稳一点后面才能跑得远。