Springboot+Vue电脑商城系统:源码梳理、部署踩坑与讲代码心得 从零做一个SpringbootVue电脑商城系统我的源码梳理、部署踩坑和讲代码的心得作为一个做过不少全栈练习项目的人我见过太多同学卡在同一个地方框架学了一堆demo跑通了但一提到“完整项目”就发怵。今天要聊的这套基于SpringbootVue的电脑商城系统就是非常适合用来打通全栈任督二脉的典型项目。它不像秒杀系统那样动不动就分布式、消息队列也不像CRUD后台那样简单到没有成就感而是恰好卡在一个“麻雀虽小五脏俱全”的甜点上有商品、购物车、订单、支付回调、权限控制前端还要处理路由守卫、状态管理、接口联调。这套系统的源码、部署文档和代码讲解我会用实际做过的方式拆开细讲包括哪些部分值得花时间看、哪些地方容易写废、部署时哪个环节最坑。如果你是准备用这个项目做毕业设计或者想通过一个完整案例把Springboot和Vue串明白这篇文章应该能让你少走不少弯路。1. 电脑商城这个业务场景为什么特别适合全栈练手先说结论商城类系统是极少数“业务复杂度够用但技术深度刚好不劝退”的项目类型。我做过的几个练手项目里博客系统偏简单后台管理系统偏枯燥而电脑商城这种带明确商品属性和交易流程的场景天然能驱动你写出有逻辑的代码而不是为了用技术而堆技术。1.1 业务链路完整前端后端都能练到真东西一个电脑商城要跑通至少需要这些业务动作用户注册登录、商品分类浏览、商品详情查看、加入购物车、确认订单、生成支付订单、模拟支付回调、订单状态更新。这条链路下来后端要处理的不只是增删改查还有库存扣减、订单号生成、支付状态校验这些稍微带点味道的逻辑。前端这边也不是纯静态页购物车状态要跨页面共享没登录就不能下单下单成功要跳到订单列表——这些正好逼着你用Vuex和路由守卫。我自己体会最深的点是商城系统的“状态”特别多比如购物车数量、订单状态、用户登录态。每个状态放在哪里管理是放在组件内部、Vuex里还是后端返回这个思考过程比写代码本身值钱得多。你做完一遍之后再去面试被问“前端如何管理状态”或者“后端如何保证订单数据一致性”脑子里是有画面感的而不是背概念。1.2 角色划分清晰方便控制项目规模一套合格的电脑商城系统通常分前台用户端和后台管理端。用户端负责浏览商品、下单、查看订单管理端负责商品上下架、分类管理、订单发货。这两部分共享同一套后端接口但前端是两个独立工程后端也天然分成普通用户接口和管理员接口两层。这样拆的好处是你能在一套项目里同时练到“面向用户的交互设计”和“面向数据的管理界面”还不用担心代码糊在一起。我做的时候是先把用户端跑通再补管理端这样迭代压力小很多。很多人上来就想着一次把两边做完结果前端写了一堆重复页面后端接口也分不清哪些是给谁用的。建议你反过来先按角色把页面列清楚再设计API路径前缀比如/api/user/**和/api/admin/**分开这样后面加权限拦截就非常顺。1.3 选电脑品类比服装、生鲜更省心这个可能很多人没提到但实际做的时候区别很大。电脑商品属性相对规整比如品牌、CPU、内存、显卡、价格、库存不太需要像服装那样处理多规格颜色、尺码和SKU联动。生鲜类还要搞保质期、冷链啥的。电脑品类一张表基本能搞定即便拆两张规格表也清晰。对练手项目来说这能避免在业务建模上消耗过多精力把时间留给框架和代码质量。所以如果你正在纠结做什么项目听我一句别上来搞秒杀也别搞社交电商先把一个规规矩矩的电脑商城做利索比什么都强。2. 源码结构怎么组织才不乱我的分包思路和容易踩的坑拿到一套源码第一件事不是急着跑而是先看目录结构。结构合理不合理直接决定你后面改代码、写部署文档、给人讲代码的体验。我见过太多项目代码全堆在controller里一个类写两千行这种项目就算跑起来你也很难跟别人讲明白更别说维护了。2.1 后端分包按业务模块切而不是按技术层切后端用Springboot最常见的分包有两种思路一种是按技术层分controller、service、mapper所有业务混在一起另一种是按业务模块分比如user、product、cart、order、admin每个模块内部再分controller和service。我强烈推荐后者。虽说按层分包在超大型项目里也常见但对电脑商城这种中等体量项目按业务模块分包读起来最直观——你找一个“订单功能”直接进去order包看里面就那三五个类一眼扫完。我推荐的结构大概是这样的com.example.mall ├── common // 通用类统一返回结果、异常处理、常量 ├── config // 配置类跨域、拦截器、MybatisPlus配置 ├── controller // 各业务模块的controller放在一个包下面 ├── entity // 实体类对应数据库表 ├── mapper // 数据访问层接口 ├── service // 业务逻辑层接口和实现 └── util // 工具类JWT生成解析、订单号生成这里有个细节要注意common里的统一返回结果类一定要在一开始就写好。我喜欢定义一个ResultT字段包含code、message、data所有接口都返回这个结构。别嫌麻烦等你写前端的时候就知道前端axios拦截器统一处理返回结构是多么省事的事情。如果不统一前端每个请求都要单独判断代码会非常啰嗦。2.2 前端分包按页面视图切公共组件单独拎出来Vue前端这块src目录下的组织方式对后期维护影响也很大。我做这个项目时是这样分的src ├── api // 所有接口请求函数按模块分文件 ├── assets // 静态资源 ├── components // 公共组件比如商品卡片、订单列表项 ├── router // 路由配置 ├── store // Vuex状态管理 ├── utils // 请求封装、token管理 └── views // 页面视图按用户端和管理端分目录特别强调一下api目录。很多人会直接在页面里写axios.get(...)一开始项目小没事等页面多了你会发现接口路径满天飞改一个后端地址要全局搜索。正确的做法是把所有接口按模块集中到api文件夹里比如product.js里放商品相关的所有请求函数order.js里放订单相关的。这样做还有一个好处每个接口的入参和返回结构都在一个地方前端排查问题时非常明确。2.3 最容易写脏的两个地方第一是全局异常处理。很多新手项目里try-catch满屏飞其实Springboot可以用RestControllerAdvice做全局异常捕获业务代码里根本不需要那么多try-catch。自定义一个业务异常类BizException在service里该抛就抛全局处理器统一返回失败结果前端拿到的永远是同一套JSON格式。这个要是做好了代码整洁度能上一个档次。第二是时间格式化。数据库里存的是datetime后端返回给前端的就是不规范的ISO字符串前端又要额外做处理。Springboot里可以直接在application.yml里配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8这一行配置解决前后端时间格式打架的经典问题。我见过不少项目卡在这上面前端拿到的日期是2025-08-09T12:30:45.00000:00然后各种找解析库其实后端一行配置就能搞定。3. 核心业务模块的开发逻辑商品、购物车、订单不能马虎商城的核心模块其实就三块商品浏览、购物车管理、订单流转。每一块都有它的业务特点写的时候想清楚逻辑比直接上手敲代码重要得多。3.1 商品列表与搜索分页参数和条件拼接是基本功电脑商城的商品列表基本都需要分页加条件查询。可以用MybatisPlus提供的Page对象配合LambdaQueryWrapper条件搜索就是动态拼接eq、like这些方法。这里的关键点是前端传过来的筛选条件后端要做空值判断不能前端不传价格区间的时候你后端直接SQL报错。我记得自己第一次写分页时傻乎乎地手写SQL的LIMIT offset, size还要自己算offset后来发现MybatisPlus的Page分页直接帮你处理了。你只需要这样PageProduct page new Page(current, size); LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); wrapper.eq(StringUtils.isNotBlank(brand), Product::getBrand, brand); wrapper.like(StringUtils.isNotBlank(keyword), Product::getName, keyword); wrapper.orderByDesc(Product::getSales); productMapper.selectPage(page, wrapper);注意StringUtils.isNotBlank这个判断它决定了条件是否加入SQL。这个模式可以推广到所有列表查询接口。还有一个小坑价格筛选用的是between如果前端只传了一个价格上限或下限你得代码里判断再拼条件if (minPrice ! null) { wrapper.ge(Product::getPrice, minPrice); } if (maxPrice ! null) { wrapper.le(Product::getPrice, maxPrice); }这些细节看起来琐碎但都是真实商城系统里一定会遇到的。3.2 购物车后端存还是前端存我的选择和建议购物车是很多初学全栈的人纠结的点。最省事的方式是纯前端用Vuex存购物车数据放本地不请求后端。但这样做的问题很明显用户换一台设备购物车就没了而且订单生成时还得把购物车数据传后端重新校验多一层风险。如果你是做正经项目我建议后端存一张购物车表字段就是user_id、product_id、quantity前端负责调用接口同步事实。后端存购物车的好处是下单的时候可以直接从数据库读用户购物车条目来创建订单库存校验和金额计算都在后端完成前端没法篡改价格。前端这边Vuex里维护一个cartCount数字用户的每次添加、删除购物车操作都调后端接口成功后再更新本地状态。这样购物车数量在页头就能实时显示。3.3 订单状态机从待付款到已发货的流转订单模块是整个系统里最值得多花时间研究的。我的订单表会设计一个状态字段用数字表示0待付款、1待发货、2待收货、3已完成、-1已取消。每次状态变更都必须校验当前状态是不是目标状态的前置状态。比如待发货订单不能直接跳到已完成必须先经历待收货。用一个专门的OrderStatus枚举来管理这些状态位别到处写魔法数字public enum OrderStatus { UNPAID(0, 待付款), SHIPPED(1, 待发货), RECEIVED(2, 待收货), COMPLETED(3, 已完成), CANCELED(-1, 已取消); // getter和setter... }下单的时候要做的操作比较多最好在service层加事务注解Transactional。大致流程是校验购物车、计算总价、生成订单号、扣库存、清空购物车。其中任何一个环节失败整个操作都要回滚不然会出现订单建了但库存没扣的脏数据。记得当时我的项目里库存扣减是直接UPDATE product SET stock stock - #{count} WHERE id ? AND stock #{count}用SQL层面的条件来防止超卖。虽然商城系统并发不会特别高但这个写法本身就是个好习惯。3.4 支付模块模拟支付回调怎么设计才像那么回事真实商城要对接支付宝微信支付但练手项目一般都做模拟支付。模拟支付要设计得像真实流程前端发起支付请求后跳到一个模拟收银台页面点击“确认支付”后端收到请求后执行一个payOrder逻辑把待付款订单改成待发货同时更新支付流水记录。我这里强烈建议加一张支付流水表记录订单号、支付金额、支付方式、支付时间。这样你的订单列表能显示哪些订单支付过管理端也能看流水项目显得完整很多。模拟支付接口的回调地址写在前端路由里可以起一个/pay/success的页面支付成功后展示结果然后跳转订单详情。这一套做完后续如果要接入真实支付只需要换掉收银台那段逻辑其他代码都不用大改。4. 前端工程化要点路由、权限、请求封装一次讲明白前端这部分Vue项目最核心的两个点一个是路由该怎么配才能防住未登录用户另一个是axios怎么封装才能不写重复代码。4.1 路由守卫和权限控制商城系统的用户端要求登录后才能进购物车和订单页面管理端要求登录且角色是管理员才能进后台。实现方式是Vue Router的beforeEach守卫里做检查router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else if (to.meta.isAdmin !isAdmin()) { next(/) } else { next() } })关键是要在路由的meta字段里标注权限信息。比如/admin下的子页面都设置meta: { requiresAuth: true, isAdmin: true }用户下单相关的设置requiresAuth就够。这里踩过的坑是管理员登录后也需要有用户端的能力所以角色判断时要用管理员标识位去放行而不是只判断普通用户token。4.2 axios封装拦截器处理token和错误提示前端请求后端的标准姿势是封装一个request.js统一配置baseURL、超时时间在请求拦截器里加token响应拦截器里剥出data或者统一报错。我写出来的版本大概是这样的import axios from axios import { Message } from element-ui import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) 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) { Message.error(res.message || 请求失败) if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.message)) } return res.data }, error { Message.error(error.message || 网络异常) return Promise.reject(error) } )注意baseURL: /api这个配置是配合前端工程里的devServer.proxy把请求转发到后端这样在开发环境没有跨域问题。部署到生产时Nginx也要做对应的反向代理。这一块别偷懒直接写死完整的后端地址也行但换环境就要改代码很麻烦。4.3 Vuex到底要存哪些东西很多初学者会把后端给的商品列表、订单列表全部塞进Vuex这是错误用法。Vuex适合存全局共享且低频变化的数据比如用户信息、购物车数量、未读消息。商品列表这种每次进页面都要刷新而且数据量大的数据建议在组件里调用API拿到后存本地data或者ref里即可。我这套系统的Vuex模块只设了三个user存token和用户信息cart存购物车条目和数量app存侧边栏折叠这种全局UI状态。这几个模块之间要互不依赖修改一个不影响另一个。这样代码逻辑非常清晰排查问题也容易。4.4 组件通信的几种常见方式新手往往纠结组件传值。商城用的最多的就是父组件给子组件传商品对象子组件emit增加购物车事件跨层级的用户信息用Vuex。有一个很实用的点商品卡片组件ProductCard接收product对象点击“加入购物车”时不应该自己调接口而是通过this.$emit(add-to-cart, product)把事件抛给商品列表页由列表页统一处理。这样做的好处是组件可复用以后你在首页、搜索页、管理端都放同一个商品卡片不需要改卡片内部逻辑。5. 部署文档是给谁看的我写部署文档的思路和几个必填重点标题里提到的“部署文档”是这类源码资料里特别关键的产物。很多人写部署文档只写一句“把项目打包扔到服务器上就行”这种等于没写。部署文档的第一原则是假设读者是一个只装了JDK和Maven的人他能按你的文档一步步把系统跑起来过程中不需要自己猜。所以部署文档要详细到“执行哪条命令、在哪个目录下执行、看到什么输出算成功”。5.1 本地开发环境部署文档要这样写先讲本地跑通的流程。后端需要的环境是JDK8和Maven 3.6数据库MySQL 5.7或8.0。文档第一步要写清楚如何创建数据库比如给出一条初始化SQL命令CREATE DATABASE mall_db DEFAULT CHARACTER SET utf8mb4;然后导入项目里提供的mall.sql文件。这种细节如果文档里不写新手真的会卡在“找不到表”上。第二步是在application.yml里改数据库的用户名密码并确认端口没被占用。第三步是用mvn spring-boot:run启动后端看到Tomcat started的日志就算成功。前端本地开发是npm install安装依赖然后npm run serve启动开发服务器。这里有个很容易踩的坑npm install可能因为Node版本太高而报错我项目里用的依赖版本比较旧切换Node版本到16左右比较稳。文档里最好明确写出来“推荐使用Node.js 16.x版本低于14或高于18可能导致依赖安装失败”。5.2 生产环境部署Nginx和进程守护不能少生产环境我建议用Linux服务器装Nginx、JDK、Maven、MySQL。后端打包成jarmvn clean package -DskipTests然后放到服务器目录下用nohup java -jar mall.jar mall.log 21 启动。但这样启动的进程没有守护进程挂了不会自动重启所以文档里最好推荐用systemd配置服务或者至少用nohup并解释日志文件怎么看。例如tail -f mall.log这个命令要写进去不然启动失败用户也不知道去哪看原因。前端构建是npm run build生成dist目录把dist里的文件上传到服务器某个目录然后配置Nginx。Nginx配置算部署文档里最容易写翻车的地方核心要点是把/api开头的请求反代到后端端口server { listen 80; server_name your_server_ip; location / { root /home/www/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里try_files那行必须要有不然刷新首页或深链接路由会404因为Vue是单页应用所有路径都该指向index.html。我第一次部署就是漏了这行一刷新全白屏排查半天。5.3 部署文档里要有问题排查小表格写部署文档时我习惯加一个“常见问题”小节把最容易碰到的问题和解决手段列成表格。比如端口占用、数据库连接失败、前端代理超时。别觉得这是多余的读者部署不上来就会跑去问开发者你一次性写清楚双方都省时间。现象可能原因解决方案后端启动报Port 8080被占用端口冲突修改application.yml里的server.port或杀占用进程连不上数据库用户名密码不对检查账号密码用Navicat先测连接前端登录后立即退出token请求头没传检查axios拦截器里Authorization设置的拼写刷新页面404Nginx未配置try_files在location /里加上try_files $uri $uri/ /index.html;这种表格在部署文档里特别有用相当于给读者一个快速定位渠道。6. 代码讲解怎么做才不算白讲我的讲解结构与表达技巧最后说说“代码讲解”这件事。很多拿着源码分享的人会从头到尾逐行读代码听的人昏昏欲睡讲的人也累。我自己的经验是代码讲解要分三条线主线讲业务逻辑辅线讲设计意图暗线讲避坑经验。6.1 先画业务流程图再讲代码不管分享给谁第一件事不是打开IDE而是把系统的业务流程讲清楚。比如“用户从商品列表页点进详情页加入购物车进购物车页勾选商品下单跳支付页支付成功返回订单列表”这个过程用文字加箭头描述出来。思维正常的人只要明白业务长什么样再去看代码每行代码都能对应上业务环节理解成本直接减半。6.2 每个模块挑3个关键代码点讲透彻一个Controller几十行没必要全讲。我通常每个模块只挑最核心的三处一处是路由设计讲URL和HTTP方法怎么对应操作一处是参数校验逻辑讲哪些参数不能为空为什么不能为空还有一处是数据返回格式讲为什么返回Result而不是裸数据。比如讲订单模块时重点讲Transactional和状态枚举的配合这是真正的业务痛点。而像RestController这种注解一句话带过就好不用展开。6.3 讲代码时多用“为什么”少用“是什么”我发现自己刚做分享时特别爱讲“这段代码做了什么”但听的人关心的是“为什么这样做”。同样是购物车接口讲“这里是判断用户没有登录就返回401”远不如讲“如果这里不判断登录状态后端的库存扣减就会因为找不到用户ID而出错所以必须提前拦截”。这种“因为所以”的结构能帮听众建立因果链条而不是名词堆砌。还有一个高效做法刻意讲一个“错误写法”。比如说库存扣减先展示最容易犯的“先查库存再判断能不能扣”的写法然后告诉大家在并发情况下这个写法会超卖再展示正确的“带条件更新的SQL”写法。这个对比下来听的人印象会极其深刻。6.4 讲代码前准备一份小抄哪怕是博主或者讲师讲代码前都该准备一份提纲。我的小抄很简单每个模块下面列三个关键词。比如商品模块就是“分页、条件拼接、图片上传”订单模块就是“事务、状态机、防超卖”。开讲前自己看一眼就能保证全程不跑偏也不会漏掉关键点。7. 从开发到讲完这套项目还留了哪些可扩展的接口做完整套系统再回头看你会发现它能扩展的点其实挺多的。比如商品模块可以加一个商品评论功能订单模块可以加优惠券用户模块可以加积分体系。这些扩展都不难操作路径清晰加表、加实体、加service逻辑、加controller接口、前端加页面和API方法。对于想拿这套项目做二次开发或者写论文的人来说这种可扩展性相当重要。还有一个我自己比较推荐的方向给项目补充单元测试。Springboot的测试算好写比如测订单service的库存扣减有没有生效SpringBootTest配上一个临时数据库就能跑。虽然商城项目做单元测试有点“重”但面试时能主动提这个很加分。部署方面剩下的优化空间也有不少前后端分离后可以改成Docker Compose一键启动把MySQL、Redis、后端jar、前端nginx一起编排起来。这样一来部署文档可以进一步简化读者只需要装Docker跑一条命令。我们现在的项目还没有引入Redis但商品热点列表其实很适合做缓存加一个Redis模块会让项目的技术含量更强。代码讲解的后续也可以做成视频加断点调试的录屏比纯讲代码直观得多。至少在我自己带人做全栈项目时效果对比非常明显——对着代码干讲对方容易走神开一个调试器在行号位置打断点看到购物车数据一步步加进去比任何解释都有效。8. 最后说几个折腾这个项目时我被坑得最惨的地方如果非要把这套项目从开发到部署再到讲完挑几个最痛的教训我想说是下面几个。第一个坑是后端跨域。开发环境用了Vite的代理没感觉但生产环境Nginx配置里忘了写proxy_set_header Host $host;结果后端拿到的用户IP全变成Nginx地址登录记录和日志排查直接乱掉。后来我养成一个习惯前端所有请求都用/api前缀后端接口统一放在/api下Nginx只需一条location /api/就全搞定。第二个坑是前端打包后路由历史模式的问题。Vue默认使用createWebHistory如果Nginx没有try_files指令刷新路由页面就会404。当时我以为代码写错了折腾好久才发现是nginx配置问题。后来我干脆写成createWebHashHistory虽然URL多一个#但对练手部署省心很多。如果要用history模式就在部署文档里用醒目字标出Nginx配置要求。第三个坑是数据库删表顺序。项目里表有外键关联如果手滑删某张表先删了父表后面会报外键约束错误。后来我准备了一个完整的mall.sql重新执行时直接全库重建省掉了这种低级烦恼。第四个坑是讲代码时只顾着讲后端或前端忽略了联调过程。后来我发现听的人最感兴趣的反而是“前端怎么调后端、后端怎么返回给前端、遇到接口报错怎么查”。这个联调链路才是全栈项目的灵魂所以我通常会把每个功能拆成“前端动作”、“后端接口”、“数据库变化”三栏来展示比如加入购物车这个操作对应前端调用POST /api/cart/add后端往cart表插入一条记录前端Vuex的cartCount加一。这样的讲解方式比单讲任何一部分都有用。整套项目做下来从源码到部署文档再到给人讲明白每一块逻辑我最大的体会是技术本身没有多高深但把它组织成一个别人能看懂、能复现、能扩展的形态这本身就是一门手艺。如果你正打算对着这套SpringbootVue电脑商城系统下手我的建议是先把商品浏览、购物车、下单支付这条主链路跑通再补管理端和优化项最后写部署文档和代码讲解稿。按这个顺序来你得到的不仅是一个系统更是一套完整的全栈表达框架。