接手这套基于Spring Boot Vue的电脑商城系统源码时我最深的感受是真正难住人的往往不是代码本身而是源码、部署文档、代码讲解三者对不上号。解压包打开前后端目录结构清清楚楚但数据库初始化脚本藏在哪个目录、JWT密钥怎么配、前端打包后怎么和后端处在一个服务下、跨域代理在开发和生产环境有什么区别这些问题足以让一个刚接触前后端分离项目的人折腾一整天。这篇博客就是干这件事的把电脑商城系统从源码结构、核心代码逻辑、数据库设计到部署运维的完整链路过一遍。适合三类人看——准备拿这类项目做毕业设计的在校生、刚接手前后端分离外包项目的初级开发、以及想快速搭一套商城Demo做技术验证的测试同学。我尽量把代码讲解部分落到具体类和具体路由上部署部分直接给命令和配置不搞虚的。1. 这套系统的定位与技术选型逻辑1.1 电脑商城系统到底包含哪些模块商城系统听起来大落到代码上其实就几个核心闭环用户注册登录、商品分类浏览与搜索、商品详情、购物车管理、订单提交与状态流转、后台管理商品上下架、订单处理、用户管理。在此基础上按项目规模可以再加轮播图管理、收货地址、支付回调模拟等模块。这套电脑商城系统的功能设计走的正是这个主流路线。前台面向普通用户包含首页商品展示、分类筛选、商品搜索、购物车、订单结算后台面向管理员包含商品管理增删改查、库存设置、订单管理发货、状态修改、用户管理。没有过度设计每一个模块都能对应到数据库表适合学习和二次开发。1.2 为什么是Spring Boot Vue而不是其他组合很多人问为什么不选更轻量的方案比如Flask React或者更重的Spring Cloud全家桶。我的看法是Spring Boot Vue是目前国内中小型项目和毕业设计中覆盖率最高、资料最全、招聘需求量最大的组合之一选它有非常现实的理由。后端用Spring Boot核心优势在于起步快。内嵌Tomcat不用单独部署容器Spring Data JPA或MyBatis-Plus帮你把数据库操作简化到接口级Spring Security JWT做登录鉴权有一整套成熟方案。对于商城这种典型的CRUD密集型业务Spring Boot的生态能覆盖90%需求不需要引入微服务那套复杂性。前端选Vue优势在于上手门槛低、组件化思维清晰、状态管理有Vuex或Pinia兜底。商城页面的核心交互是商品列表、筛选、购物车数量加减、订单流程跳转Vue的响应式数据和组件通信机制非常契合。Element UI这类组件库能直接把后台管理页面的表格、表单、弹窗全部搞定开发效率极高。真正重要的是这套组合的学习曲线配比新手花两周能跑通前后端联调花一个月能理解大部分源码逻辑。换成Spring Cloud那套光是注册中心、配置中心、网关就够喝一壶的和商城业务本身没什么关系。2. 后端源码拆解从登录鉴权到订单状态机2.1 统一返回体和全局异常处理的约定拿到源码第一步不是看业务代码而是看它的基础设施约定。这套系统里有一个贯穿所有Controller的类——统一返回体Result所有接口的返回格式都是{ code, message, data }。 code200表示成功401表示未登录或token失效500表示业务异常。这个约定极其重要因为前端所有的axios响应拦截器都依赖这个结构做统一处理。对应地全局异常处理器用RestControllerAdvice捕获各类异常业务异常直接抛出ServiceException由全局处理器转成统一返回格式。这样做的好处是Controller层代码非常干净只需要关注业务参数不用每个方法都写try-catch。代码讲解时一定要先看这两个类理解了它们再看Controller会轻松很多。2.2 JWT登录鉴权的完整链路登录模块是这套系统里鉴权设计的核心。用户提交用户名和密码后后端通过UserService.login()验证账号密码成功后生成JWT令牌返回给前端。这个令牌不是随便一个字符串它内部包含userId、username和过期时间用HMAC-SHA256算法配合密钥签名。前端拿到token后存储在localStorage中每次请求在axios请求拦截器里取出token并放到Authorization头。后端通过OncePerRequestFilter或拦截器统一从请求头解析token解析成功就把用户信息放入ThreadLocal上下文供后续业务代码直接获取当前登录用户。关键细节在密码存储上。源码里用户表存的不是明文密码而是BCryptPasswordEncoder加密后的密文。BCrypt每次加密同一个明文会得到不同的密文但matches()方法可以验证原文和密文是否匹配。这是一种单向hash加盐的加密方式数据库泄露了也无法反推出原始密码。这套设计你在任何正规项目中都应该坚持哪怕是Demo。2.3 购物车与订单模块的设计思路购物车模块相对简单购物车表以userId为维度存储商品条目一个用户对应多条商品记录字段包含商品ID、数量、选中状态。加入购物车时先查是否已有该商品有则数量累加没有则新增记录。计算总价时前端拿购物车列表数据循环累加后端提供批量获取商品信息的接口。订单模块就要严谨得多因为涉及库存扣减和状态流转。一条订单的生命周期大概是待付款 - 已付款 - 已发货 - 已完成 / 已取消。源码中用一个status字段表示订单状态同时有orderStatus记录操作时间流。下单操作的核心逻辑是校验库存、扣减库存、生成订单主表记录、生成订单明细表记录、清空对应购物车条目这几步必须在一个事务内执行否则会出现库存扣了但订单没生成的数据错误。这里我想强调一个关键操作扣库存不是先查库存够不够再update而是直接在SQL里做条件更新UPDATE product SET stock stock - #{quantity} WHERE id #{productId} AND stock #{quantity}影响行数为1才表示扣减成功。这能有效避免高并发下超卖的问题属于库存操作的最佳实践。3. 前端Vue源码讲解路由、接口封装与页面流转3.1 目录结构和路由配置的阅读方式Vue项目的src目录一般分这么几块api放所有接口请求定义、assets放静态资源、components放公共组件、router放路由配置、store放状态管理、views放页面组件、utils放工具函数。这套电脑商城系统的目录结构基本沿用了这个约定阅读理解成本很低。路由配置分两条线前台商城路由和后台管理路由。前台路由如/home首页、/product/:id商品详情、/cart购物车、/checkout结算后台路由如/admin/products商品管理、/admin/orders订单管理。后台所有路由包在一个需要登录验证的父路由下通过路由守卫beforeEach检查本地token是否存在不存在就跳转到登录页。这是Vue Router最常见的鉴权方式不是绝对安全但作为页面级权限控制完全够用。3.2 Axios封装与前端鉴权的配合直接看utils/request.js这个文件它是所有HTTP请求的入口。内部创建了一个axios实例设置baseURL和超时时间然后挂载两个拦截器。请求拦截器从localStorage读取token并加到请求头响应拦截器根据后端返回的code字段做判断code200直接返回datacode401清除本地登录信息并跳转登录页code500弹出错误提示。这套封装的指标意义在于所有页面组件里的业务代码不需要关心HTTP细节调用接口时只需要写login(data).then(res { ... })异常处理统一收敛到了拦截器层。如果你想改token的存储方式比如从localStorage换成cookie只需要改这一个文件。3.3 商品列表、购物车与结算页面的数据流转商品列表页是典型的列表-筛选-跳详情模式。页面挂载时调用getProducts({ categoryId, keyword, pageNum, pageSize })获取数据渲染到卡片组件中点击卡片跳转/product/:id。分类筛选通过监听路由参数变化重新请求数据实现。购物车页的数据不是从后端一次拉全部而是进入页面后调用getCartList()获取当前用户的购物车条目再根据条目中的商品ID批量请求商品详细信息。为什么这样设计因为购物车表存的是商品ID和数量快照商品名称、价格、图片这些信息在商品表中通过join查询也可以但前后端分离模式下更常见的做法是交给前端做数据拼装后端接口保持职责单一。结算页则是购物车状态的汇聚点。选中的商品条目集合传给订单确认页展示商品明细、计算总金额不含运费、填写收货地址、提交订单。提交后后端返回订单号前端跳转到订单成功页。这套流程里的Vuex或Pinia主要负责购物车选中状态在页面间的共享比每个页面单独传参要可靠得多。4. 数据库表设计与Redis缓存的引入点4.1 核心表结构一览拿到SQL文件先看这几张核心表理解它们的关系基本就理解了整套系统的数据底座表名核心字段说明userid, username, password, phone, avatar用户表password存BCrypt密文categoryid, name, parent_id, sort商品分类表支持两级分类productid, category_id, name, subtitle, main_image, price, stock, status商品表status控制上下架cart_itemid, user_id, product_id, quantity, checked购物车条目表orderid, order_no, user_id, total_amount, status, address_id订单主表order_itemid, order_id, product_id, product_name, product_price, quantity订单明细表冗余商品快照addressid, user_id, receiver, phone, province, city, detail收货地址表注意order_item的设计它冗余了商品名称和价格快照。为什么因为商品可能改名、改价甚至下架但订单历史必须保持当时成交的信息。这个设计思想叫快照模型是所有电商订单系统的共通做法。4.2 Redis在哪些场景真正起作用这套系统引入Redis主要是两个场景首页轮播图缓存和商品详情缓存。轮播图这种基本不变化的数据每次都查MySQL浪费严重Redis以String类型存JSON数组设置过期时间读取时先查缓存再查库。商品详情同样如此热点数据的访问频率高、更新频率低适合缓存。第二个场景是购物车临时状态和验证码。登录验证码存Redis并设置5分钟过期比存session更合适因为Redis天然支持过期删除。不过要泼一盆冷水缓存一致性处理是这类项目最容易翻车的地方。商品信息被后台修改后如果不清缓存前台会一直展示旧数据。稳妥的做法是在商品更新接口里同步删除对应缓存key让下次请求重新回源数据库。简单粗暴但有效推荐在学习阶段这么用了解Cache Aside Pattern旁路缓存的核心思想比盲目引入Caffeine或消息队列要有价值得多。5. 部署文档实测从环境搭建到前后端联调上线5.1 环境版本配套是第一个大坑部署文档写得再详细版本配套问题依然能卡住一群人。电脑商城系统的部署文档里如果只写需安装JDK、Maven、Node、MySQL那等于没写。我从实践角度给出一个稳定的版本组合组件推荐版本注意点JDK1.8 / 8u202Spring Boot 2.x最稳妥的JDK版本Maven3.6.x不要用3.9.x以下的老版本配JDK8会有兼容问题Node.js14.x或16.xVue 2项目建议Node 14过高版本可能出现node-sass编译失败MySQL5.7或8.0注意8.0的驱动配置要加serverTimezoneAsia/ShanghaiRedis6.xWindows下用官方移植版或Docker都行为什么反复强调版本因为项目源码用的Spring Boot 2.x Vue 2.x是老搭档而很多同学下载的是最新的JDK17、Node20、MySQL8.x编译时的报错五花八门但根因基本都是版本跨度太大。记住一个原则优先复现项目开发时的版本环境而不是全部用最新版。5.2 后端启动的完整操作链路后端启动分四步初始化数据库、改配置、编译打包、启动运行。数据库初始化最简单但也最容易漏在MySQL中执行项目sql目录下的mall.sql脚本注意字符集用utf8mb4不然中文会乱码。然后打开application.yml修改数据源地址、用户名、密码如果有Redis也一并确认Redis连接配置。Maven项目构建的核心命令是mvn clean package -DskipTests.jar文件生成在target目录下直接java -jar mall-server.jar如果启动报端口被占用用--server.port8081覆盖端口如果报数据库连接失败检查MySQL是否启动、账号密码是否和yml一致、是否是MySQL 8的驱动问题。还有一个常见坑私服仓库下载依赖慢导致打包卡住可以给Maven换阿里云镜像源这个在settings.xml中配置。5.3 前端构建的两种部署形态前端部分先安装依赖再构建npm install npm run build构建产物在dist目录。到这里出现了分支选择形态一前后端分离部署Nginx转发API请求。把dist目录下的文件拷贝到Nginx的html目录Nginx配置里加一条转发规则把/api开头的请求代理到后端8080端口。这是生产环境最常见的做法前端静态资源由Nginx托管后端API独立运行。形态二前端打包后放进Spring Boot一体化部署。把dist目录的静态文件拷贝到Spring Boot的src/main/resources/static目录下重新打包。这样访问8080端口就能同时拿到页面和API适合毕设演示、小并发场景。之前很多搜索词里提到vue打包放进springboot中指的就是这个操作。两种形态各有优劣。形态一更专业、更接近真实公网环境形态二部署简单、不需要额外装Nginx。如果只是本地跑通看效果形态二最快。5.4 跨域问题什么时候才需要处理跨域是前后端分离项目几乎必踩的坑但很多人没搞清楚什么时候需要处理。开发环境下前端用webpack-dev-server的proxy代理浏览器请求的是前端开发服务器的地址由开发服务器转发到后端不存在跨域。生产环境下如果用Nginx把/api转发到后端Nginx的proxy_pass也天然规避了跨域。所以这两种方式都不需要后端开启CORS。什么时候需要后端配合开CORS前端静态文件和后端API不在同一个域名或端口下且没有中间层做代理转发时。这时候浏览器会拦截响应需要在Spring Boot里配置CorsFilter或使用CrossOrigin注解。代码讲解时建议把三种情况都讲明白让读者理解跨域是浏览器策略而非后端限制。6. 代码讲解之外的实战经验常见坑与优化方向6.1 三个容易让人怀疑人生的报错场景场景一分页查询失效。商城系统商品列表肯定用分页如果用的是MyBatis-Plus的分页插件需要手动在配置类里注册PaginationInnerInterceptor。漏掉这一步分页接口会返回全量数据或者分页参数不生效而且没有任何报错排查起来比较隐蔽。场景二localStorage被清空。刷新页面后发现用户登录态丢失大概率不是因为后端token过期而是前端在初始化登录状态时读取localStorage的时机不对或者路由守卫把无token状态一律当成未登录处理。这时候要在路由守卫里区分首次加载和登录超时两种情况。场景三修改商品后前台不生效。这个基本可以确定是缓存没清。检查商品管理端的更新接口有没有删除Redis中对应的商品缓存key如果没有那就是代码讲解里提到的缓存一致性问题。6.2 性能和代码层面的优化建议首先是接口层面的优化。商品列表页第一次加载慢大概率是查询没有走索引给category_id、status这些查询条件建上索引会有明显改善。其次是图片资源的处理商品主图不要直接用原图前端使用缩略图减少流量消耗。代码层面的优化重点是Service层的边界。现在的代码模式基本都是Controller直接调Service但订单模块的逻辑比较复杂建议把下单、支付回调、取消订单这类带有业务状态机的操作独立成Service避免一个Service类越写越臃肿。如果要做扩展可以引入事件机制比如订单创建成功后发布事件异步清理购物车、发送通知让主链路更轻。6.3 这个系统还能往哪些方向扩展基础功能跑通之后可以沿着两个方向做扩展。业务方向加入优惠券模块用户领取优惠券、下单时校验并使用、扣减库存和优惠券核销要在同一事务中完成。技术方向引入消息队列RocketMQ或RabbitMQ处理订单超时未付款自动取消这是电商场景非常经典的延迟消息应用。无论往哪个方向扩展建议都先画一张模块依赖图哪些模块是基础服务用户、商品哪些是核心交易链路购物车、订单、库存哪些是辅助功能优惠券、积分、评论。理清依赖关系后再动手扩展时不用频繁改动已有代码结构。最后再分享一点个人经验网上这类源码部署文档代码讲解的资源很多但真正值钱的不是源码本身而是把它跑起来、读明白、敢改动的那段过程。建议拿到源码后先不看讲解自己对着部署文档独立跑一遍这个过程中踩的每一个坑都比看十遍源码讲解学到的东西多。