SpringBoot+Vue+MySQL在线商城系统源码解析与实战部署指南

发布时间:2026/9/26 18:17:43
SpringBoot+Vue+MySQL在线商城系统源码解析与实战部署指南
1. 项目概览与架构设计思路这套在线商城系统我用了一段时间也带着几个同事一起调过、跑通、部署过整体给我的感觉是结构标准、业务覆盖完整、入手门槛低非常适合做课程设计、毕业设计或者是刚接触前后端分离项目的人用来理解商用系统的基本盘。先说这个项目到底是什么。它的全称是ONLY在线商城系统信息管理系统源码技术栈非常明确SpringBoot 负责后端接口和业务逻辑Vue 负责前端页面和交互渲染MySQL 负责数据存储。三个部分各自独立又能互相协作典型的前后端分离开发模式。而且它自称可直接运行这意味着项目里已经内置了初始化数据、前后端联调配置甚至可能包含了数据库脚本省去了从零搭建的基础工作量。1.1 这套系统是做什么的在线商城系统说白了就是把线下商场的商品浏览、下单购买、订单管理、后台维护这些环节搬到线上。常见的用户端功能比如商品列表、商品详情、加入购物车、提交订单、模拟支付、个人中心、收货地址管理管理端功能包括商品发布、商品上下架、库存管理、订单处理、用户管理、分类管理、数据概览等。这套系统基本把这些模块都覆盖到了虽然不像淘宝那样有完整的分布式推荐系统、秒杀系统、搜索引擎但作为一套中小型商用平台或教学项目功能性是足够完整的。从我实际跑到的情况看这套系统比较适合这几类人在校学生尤其是Java方向或软件工程方向需要课程设计、毕业设计源码的初级程序员想通过完整的全栈项目来提升SpringBoot、Vue、MySQL综合能力的小团队创业验证需要快速搭一个商城不过度定制拿源码改改就能用的想从前端转全栈但找不到合适练手项目的这个项目规模刚好。1.2 技术组合选型的思考为什么要用SpringBoot Vue MySQL而不是SSHSpringStrutsHibernate或者更重的微服务架构这里是我自己的理解。SpringBoot现在已经是Java后端开发的事实标准之一它最大的价值就是约定优于配置把原来Spring MVC、Spring配置各种XML文件的繁琐工作全部干掉内置Tomcat注解驱动开发启动一个项目就是main方法的事情。而且它的自动配置机制让开发者只需要关心业务代码不用关心组件的装配细节。Vue在前端框架里火了很多年它相比React上手曲线更平缓模板语法直观双向绑定特性在表单类业务场景比如商城里的购物车数量修改、后台商品编辑里特别好用。Vue 2.x或Vue 3.x都有成熟的Element UI组件库表格、弹窗、表单验证这些后台系统高频操作都有现成组件。MySQL就更不用说了开源、免费、稳定、生态好中小规模并发场景下性能完全够用最重要的是会的人多遇到问题百度就能找到答案。商城系统对事务的要求比较高库存扣减、订单生成MySQL的InnoDB引擎提供了很好的事务支持这也是它作为这个项目存储层的核心原因。这套组合选型的逻辑很简单不求最新最炫但求稳定好用、生态成熟、学习成本可控。这一点在项目里体现得很清楚所有模块都是标准实现思路不搞花活这对阅读源码和理解业务非常有帮助。1.3 安全合规说明在展开技术细节之前还是要先交代一句我自己拿到源码之后第一件事不是急着启动而是先花点时间通读了一遍项目结构、数据库脚本和关键配置项。因为任何网络上流传的源码即使再可直接运行也要自己做一次安全确认。这既是职业习惯也是对团队负责。后面我会单独用一节讲清楚这种安全确认的流程。2. 系统整体架构与功能模块拆解2.1 前后端分离架构下的请求流转这个项目的前后端分离体现在目录结构上——前端工程和后端工程是两个独立的代码仓库。前端通过HTTP请求本质上是JSON格式的RESTful API调用后端接口后端处理完业务逻辑后返回JSON数据前端拿到数据后渲染页面。整个请求流转过程大致是用户在浏览器里访问Vue项目页面用户在页面上的操作比如点击加入购物车触发Vue组件中的方法方法通过axios或fetch发起HTTP请求到SpringBoot接口SpringBoot的Controller层接收请求并将参数传给Service层处理业务逻辑Service层调用Mapper层操作数据库的接口读写MySQL数据执行结果逐层返回最终以JSON形式回传给前端Vue根据返回的数据更新页面DOM用户看到反馈结果。这个架构的好处在于前后端开发可以并行推进、互不干扰前端只需要知道接口的入参和出参。同时如果未来有App端、小程序端的接入需求后端接口可以直接复用不需要重写。2.2 用户端功能模块详解从实际使用的角度来说用户端是绝大多数人打开这个系统后第一眼看到的界面。它包含的核心模块有用户注册与登录基于Token的认证方式用户注册后密码使用加盐哈希存储Spring Security或简单的JWT实现登录成功后获得一个有效的访问令牌。这个模块是所有业务的前提因为后续的下单、查看订单等操作都需要校验用户身份。商品浏览支持商品按分类浏览、关键词搜索、分页展示。商品卡片上会显示缩略图、价格、销量、库存等信息。这里背后涉及商品表的联表查询、图片二进制存储或静态资源路径映射等。商品详情页展示商品大图、详细介绍、价格变化、库存状态并提供加入购物车和立即购买按钮。这个页面是转化率的核心它的数据接口往往还需要附带评论或销量统计。购物车管理用户可以将多个商品加入购物车在购物车页面可以修改购买数量、删除商品、计算总价。购物车数据是存储在服务端的关联用户ID而不是仅仅存在浏览器localStorage里这样换设备数据不丢。订单确认与提交从购物车或商品详情页进入订单确认页面填写收货地址、选择配送方式确认金额后提交订单。这个步骤涉及多张表的联动生成订单主表记录、生成订单子表记录每个商品一条、扣减库存。个人中心查看个人资料、修改密码、查看订单列表、查看订单详情、确认收货。这个模块更多是状态查询和展示逻辑相对简单。2.3 管理端功能模块详解管理端是给商城的运营人员或管理员使用的包含的功能比用户端更偏向数据操作分类管理新增、修改、删除商品分类分类通常支持两级结构父分类和子分类。商品管理核心是商品表CRUD操作。单品模型里最重要的字段是名称、价格、库存、分类ID、封面图、描述、状态上架/下架。管理员的日常操作基本都集中在这里。订单管理查看所有用户的订单按状态筛选待付款、待发货、已发货、已完成、已取消进行发货操作这对应着订单状态的流转。用户管理查看注册用户列表、禁用/启用账号。商城类系统通常还会做用户等级或积分体系这套系统功能相对基础但基础CRUD是齐全的。2.4 权限控制的设计逻辑在商城系统里用户和管理员必须被分开。普通用户只能访问和自己相关的业务功能管理员才能访问后台管理模块。从代码层面来看这个项目在SpringBoot里通过拦截器或Spring Security过滤器来对请求进行鉴权。处理逻辑是这样的前端在登录成功后会获得一个token之后的每次请求在HTTP Header中带上这个token后端拦截器对请求路径进行匹配如果请求的是后台管理接口比如 /admin/ 开头的接口就会校验当前token对应的用户角色是否为管理员如果不是则直接返回401或403错误码。Vue前端那边也有配套的路由守卫机制。在路由配置里会给后台管理相关的路由添加一个meta标记全局前置守卫检查用户状态和角色如果没有权限就直接跳转到登录页或404页面。这种前端拦截 后端校验的双重机制才是完整的权限控制方案。光做前端拦截是防不住绕过访问的后端才是最后一道安全闸门。3. 后端SpringBoot核心模块实现思路3.1 项目分层架构解析打开SpringBoot后端工程包结构一般是按照Controller → Service → Mapper的经典三层架构组织的。比如com.only.shop ├── controller # 请求入口接收HTTP参数返回JSON结果 ├── service # 业务逻辑层处理核心业务规则 │ └── impl # Service接口的实现类 ├── mapper # 数据访问层一般搭配MyBatis的Mapper接口 ├── entity # 实体类对应数据库表结构的Java对象 ├── vo # 视图对象用于向前端返回定制化的数据结构 ├── dto # 数据传输对象用于接收复杂的请求参数 ├── config # 配置类比如跨域配置、拦截器注册 ├── common # 通用工具类、统一返回结果封装、异常处理 └── ... # 其他辅助模块如JWT工具类、常量定义这种分层的核心好处是职责单一、可维护性强。Controller层只做参数接收和结果包装不写业务Service层专注业务逻辑Mapper层专注SQL语句。如果将来要换数据库只需要修改Mapper层的SQL实现如果要把业务逻辑抽成微服务Service层也可以方便地分离出去。3.2 统一返回结果封装前后端分离的项目里接口返回的数据格式必须统一。这个项目的common包下通常会有一个Result类里面定义了三个核心字段code状态码、message提示信息、data业务数据。接口返回格式大致是{ code: 200, message: 操作成功, data: { token: eyJhbGciOiJIUzI1NiJ9..., userInfo: { id: 1, username: onlyuser } } }这样做的好处非常直观前端在axios响应拦截器里统一处理返回结果根据code判断是进入正常流程还是弹出错误提示。如果每个Controller都自己拼返回格式迟早会出现字段名不一致、状态码含义混乱的问题。这也是我在阅读这个项目时比较看重的一点统一返回结果是工程化规范的基础。在我自己的团队里这个规范是写进代码评审清单里的硬指标。3.3 热门接口的业务逻辑拆解这里我挑几个核心接口把它们的业务处理逻辑拆开讲讲。登录接口接收用户名和密码先校验验证码如果有的话再根据用户名查出用户记录比对密码哈希比对成功后生成JWT Token返回给前端。这里有几个细节关卡用户是否存在账号是否被禁用密码是否匹配是否需要在Token里携带到期时间和角色信息。添加购物车接口接收商品ID和数量先检查商品ID是否存在且处于上架状态再查当前购物车是否已存在该商品记录——如果存在就累加数量否则新增一条记录。这里还要做库存校验不能超过库存上限。提交订单接口这是系统中最复杂的单机事务。主要步骤是接收订单参数商品ID列表、数量列表、收货地址ID校验商品状态和库存是否充足计算订单总金额单价 × 数量也可以支持优惠减免逻辑生成订单主记录状态为待付款生成订单明细记录每个商品一条扣减库存更新商品表的库存字段清空对应的购物车记录返回订单ID和应付金额给前端。这六步操作必须在一个事务里完成任何一个步骤失败都要全部回滚。SpringBoot里的实现通常是在Service方法上加上Transactional注解一旦方法内抛出运行时异常Spring会自动进行事务回滚。这个接口是整个系统里最考验后端功力的一部分如果对事务传播机制和异常处理理解不透彻很容易出现库存扣了订单没生成这种脏数据。给个简单的库存扣减SQL示例如果你拿到的源码用的是MyBatis的XML方式核心语句一般长这样update iddeductStock UPDATE product SET stock stock - #{quantity} WHERE id #{productId} AND stock #{quantity} /update这里用stock quantity作为条件可以在数据库层面防止扣成负数属于一种乐观锁的简化版本。这一步在这个业务里是必须加的防护。支付回调接口因为是商城系统支付环节通常走模拟支付或者通过支付宝/微信沙箱环境联调。调用流程是前端调起支付页面用户在沙箱环境输入支付密码支付平台向系统后台发送异步通知回调接口后端验证签名后修改订单状态为已付款。如果使用的是RabbitMQ或Redis的延迟定时任务还可以实现自动取消超时未付款订单。3.4 数据库连接与事务配置SpringBoot的项目配置文件application.yml里数据库相关配置是最核心的一块。一般会长这样spring: datasource: url: jdbc:mysql://localhost:3306/only_mall?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true几个容易踩坑的配置点serverTimezoneAsia/Shanghai必须明确设置不然MySQL连接时容易报时区错误map-underscore-to-camel-case这个配置可以把数据库的user_name自动映射到Java类的userName字段不用写大量的resultMap映射规则mapper XML文件的路径要跟实际目录一致否则会启动报错找不到Mapper绑定。事务配置方面SpringBoot默认就开启了事务管理无需额外XML配置只要在类上或方法上使用Transactional即可。但需要注意一点——事务只在Spring容器管理的Bean里才能生效如果你自己在Service里用new创建了一个业务对象去调用带事务注解的方法那事务是不会生效的。这是我调试时发现不少新手会忽略的细节。4. 前端Vue核心实现与页面交互设计4.1 前端工程结构与目录规划Vue工程是前端的骨架。通过Vite或者Vue CLI创建项目后目录结构大致是src ├── api # 所有接口请求封装每个模块一个文件 ├── assets # 静态资源图片、样式文件 ├── components # 公共组件比如商品卡片、订单状态标签 ├── router # 前端路由配置 ├── store # 全局状态管理Vuex/Pinia ├── views # 页面组件 │ ├── home # 首页相关 │ ├── product # 商品相关 │ ├── cart # 购物车页面 │ ├── order # 订单页面 │ ├── user # 个人中心 │ └── admin # 后台管理相关页面 ├── utils # 工具函数比如localStorage封装、时间格式化 ├── App.vue # 根组件 ├── main.js # 入口文件 └── ...前端项目非常讲究组件化复用。比如商品卡片在首页、搜索结果页、猜你喜欢列表里都会出现那就应该抽成一个通用组件通过props传入商品数据。如果你拿到的源码里面是一个页面复制多套商品卡片代码那说明作者前期图快没做好复用你可以把它作为自己的一个优化练手点。4.2 路由守卫与登录状态管理商城系统涉及到用户信息展示和购物车数量展示这些信息在很多页面都需要用到。如果每个页面都自己请求一次后端接口又慢又重复所以要用Vuex或Pinia把用户信息、购物车数量作为全局状态存储起来。在Vuex里会有类似这样的结构// store/modules/user.js const state { token: localStorage.getItem(token) || , userInfo: JSON.parse(localStorage.getItem(userInfo)) || null } const mutations { SET_TOKEN(state, token) { state.token token localStorage.setItem(token, token) }, SET_USERINFO(state, info) { state.userInfo info localStorage.setItem(userInfo, JSON.stringify(info)) }, CLEAR_USER(state) { state.token state.userInfo null localStorage.removeItem(token) localStorage.removeItem(userInfo) } }路由守卫的作用就是判断用户是否登录。比如在router.beforeEach里判断用户要访问的页面是否需要登录权限如果需要且token为空就直接重定向到登录页并带上redirect参数登录成功后跳回原页面。这种体验细节很影响系统可用性。比如用户浏览商品详情页时想下单点击立即购买后如果没登录会被带到登录页登录完成后自动跳回商品详情页整个流程就非常顺滑。如果一个系统登录完跳到首页用户还得自己重新找商品体验就差很多。4.3 商品搜索与分页展示的前端处理商品列表页的前端逻辑核心是搜索条件和分页参数的请求拼接。搜索条件一般有关键字、分类ID、排序方式价格升序、销量优先、当前页码、每页条数。这些参数会在Vue组件的data中维护当用户点击搜索或调整排序时组件重新调用API携带着新的参数向后端发起请求。分页通常是这样的请求参数// 请求商品列表 async function fetchProducts() { const params { page: currentPage, pageSize: pageSize, keyword: keyword, categoryId: categoryId, sort: sortType } const res await getProductList(params) total res.data.total productList res.data.records }前端拿到total总数后用分页组件的current-page和total属性绑定切换到下一页时触发翻页事件重新加载数据。这套逻辑在几乎所有后台系统的列表页面里都是一样的套路掌握了它任何一个管理系统的列表页你都能上手。4.4 页面间状态共享购物车数据流购物车在各个页面如何保持一致性是前端设计里比较有趣的一个点。常规思路是页面初始化时通过getCartData()接口加载当前用户的购物车列表存储到Vuex或Pinia里。当用户在当前页点击加入购物车成功时本地购物车数量更新然后立即调用一次获取购物车的接口确保后续页面显示同步。如果项目不想用Vuex也有一些替代方案比如在父组件统一加载数据用event bus或事件广播的形式通知子组件。但从维护性角度看我用Vuex/Pinia管理购物车状态会舒服得多因为购物车在至少三个页面商品详情页、购物车页、全局header图标都有展示集中治理是长远之选。下单成功后还需要从购物车数据里移除已下单的商品这一步如果漏掉就会出现用户已经付款了但购物车里还挂着那些商品的情况。在代码里要明确下单成功接口返回后前端重新拉取一次购物车数据别偷懒靠前端filter过滤完事以接口返回的最新状态为准。4.5 后台管理页面的表格交互后台管理系统的前端页面基本都是Element UI的表格组件加弹窗表单组合。商品管理页面的大致逻辑是页面加载后调用/admin/products接口获取商品分页数据渲染在el-table里点击新增按钮打开el-dialog弹窗里面是动态表单分类选择、名称输入、价格输入、库存输入、图片上传提交时把表单数据封装为JSON发给后端接口成功后会刷新表格、提示成功。这个交互模式覆盖了后台系统90%以上的CRUD场景。图片上传部分如果项目用的是将图片base64编码后存进数据库这个方法在数据量小的时候没问题但数据库会快速膨胀生产环境不推荐。比较合理的做法是把图片上传到本地静态资源目录或对象存储服务数据库里只存图片路径这样接口返回轻、数据库小、页面加载也快。5. 数据库设计分析与MySQL关键操作5.1 核心数据表结构梳理这个商城系统的数据库核心表一般包括user用户表、category分类表、product商品表、cart购物车表、order订单主表、order_item订单明细表、address收货地址表、admin管理员表。我挑几张核心表的结构来梳理。product商品表的核心字段字段名类型说明idBIGINT主键nameVARCHAR(100)商品名称category_idBIGINT所属分类IDpriceDECIMAL(10,2)单价stockINT库存数量cover_imgVARCHAR(255)封面图片路径descriptionTEXT商品详情描述salesINT销量statusTINYINT上架状态0下架1上架create_timeDATETIME创建时间order订单主表的核心字段字段名类型说明idBIGINT主键order_noVARCHAR(32)订单编号尽量唯一user_idBIGINT下单用户IDtotal_amountDECIMAL(10,2)订单总额statusTINYINT状态0待付款、1已付款、2已发货、3已完成、4已取消address_idBIGINT收货地址IDcreate_timeDATETIME下单时间pay_timeDATETIME支付时间order_item订单明细表的核心字段字段名类型说明idBIGINT主键order_idBIGINT关联订单主表IDproduct_idBIGINT商品IDproduct_nameVARCHAR(100)商品快照名称product_imageVARCHAR(255)商品快照图片priceDECIMAL(10,2)成交单价快照quantityINT购买数量这里有个值得注意的细节为什么在明细表里要冗余product_name和product_image这些字段因为商品名称和图片是可能被后台修改的一旦修改历史订单里的信息不应该跟着变。快照机制保证了订单数据的不可变性。这种设计思路在电商系统里是一种经典实践可以说是必会的要点。5.2 外键与索引设计先说外键很多基于SpringBoot MyBatis的实战项目在表设计时已经不再优先使用数据库外键约束。原因不是外键不好而是在高并发写入场景下外键约束会造成额外的数据库开销而且查询时如果需要跨表联查通常通过业务层的关联逻辑完成。这个项目里的表关系更多是通过字段命名来实现逻辑上的外键关联比如order_item.order_id指向order.id。索引设计方面数据库脚本里至少应该能看到的常用索引user表唯一索引uk_username用户名为登录名必须唯一product表普通索引idx_category_id分类筛选、普通索引idx_status上下架筛选order表普通索引idx_user_id用户查自己的订单、唯一索引uk_order_no订单号必须唯一。索引不是越多越好每增加一个索引都会降低写入性能、占用存储空间。对一个小型商城系统来说上述几组索引已经够用了几个高频查询点都能覆盖到。5.3 数据库初始化与数据脚本这个项目可直接运行的重要保障就是它提供了完整的SQL初始化脚本。通常脚本文件会放在项目根目录或/db/sql目录下面文件名类似only_mall.sql或init.sql。拿到脚本后操作流程是在MySQL里创建一个数据库比如CREATE DATABASE only_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;切换到该数据库USE only_mall;导入脚本source /path/to/only_mall.sql;脚本本身会自动建表并往里插入一些演示数据比如十几个商品、几个分类、一个管理员账号、一个普通用户账号。这就解释了为什么项目能直接运行——不需要你再手工造数据启动后就有内容可看。这里还要特别提醒一点拿到脚本后先自己执行一遍确认无误后再对接项目而不要一上来就跑到项目配置里去改数据库密码。因为脚本里的字符集、引擎类型、数据格式如果和本地环境不匹配后续排查起来会凭空多出很多麻烦。5.4 事务在订单业务中的实际应用前面提到过提交订单操作需要多表操作这里结合MySQL的InnoDB引擎展开说说。使用MyBatis时在SpringBoot的Service实现类上加了Transactional之后Spring容器会把这一层的方法包在一个事务里。也就是说从方法开始所有Mapper的写操作都在同一个数据库连接上执行直到方法返回成功后统一提交。我根据实际经验给出一个更严谨的写法思路。扣除库存时如果单纯使用UPDATE product SET stock stock - 1 WHERE id ?在高并发场景下两个请求同时读到了stock1然后同时执行减1最后stock会变成-1这就不对了。但如果在WHERE条件中加上stock 0只有一个请求能成功更新另一个请求affected rows为0业务层再抛异常回滚这样可以有效防止超卖。这种细节是区分一个项目是能跑还是能上线的分水岭。多数课程设计项目只关注跑通我认为一套好源码至少要在这种并发边界上有防护意识。6. 环境准备与项目启动完整指南6.1 本地环境要求想把这个项目跑起来需要准备的软件环境如下软件版本建议说明JDK1.8或11SpringBoot 2.x版本对应JDK8较新版本可能要求17看项目pom文件Maven3.6后端构建工具管理依赖Node.js16或18前端构建工具Vue项目需要npm进行依赖安装MySQL5.7或8.0数据库8.0要注意驱动版本兼容IDEIDEA或VSCode后端用IDEA最舒服前端VSCode轻量Navicat/Workbench可选数据库管理工具可视化管理更方便环境准备环节最容易出问题的是版本匹配。比如JDK版本过高、单体或SpringBoot版本过低可能出现编译不通过的情况。建议先看一眼后端pom.xml里的spring-boot-starter-parent版本再决定安装哪个版本的JDK。6.2 后端项目启动步骤第一个阶段是数据库初始化。按照上面提到的SQL导入方法把初始化脚本导入本地MySQL。注意MySQL的字符集要使用utf8mb4避免中文字符乱码。第二个阶段是修改配置。在后端项目的src/main/resources/application.yml或application.properties文件中修改数据库账号密码为本地环境值。如果端口被占用可以修改server.port。第三个阶段是编译启动。在项目根目录下打开命令行执行mvn clean install -DskipTests接着去target目录查看打包出来的jar文件或者直接在IDEA里启动主类。通常主类在com.only.shop包下类名是OnlyMallApplication之类的。启动成功后控制台会打印SpringBoot banner并输出一句类似Started OnlyMallApplication in 5.2 seconds的日志。保持这个窗口开着后端服务就在运行了。6.3 前端项目启动步骤前端启动需要先在命令行进入前端工程目录执行npm install如果package-lock.json存在推荐用npm ci代替npm install这样可以锁定依赖版本安装速度也更快、不会因为意外升级导致接口不兼容。网络不佳或镜像源不稳定的情况下可以预设使用国内镜像npm config set registry https://registry.npmmirror.com依赖安装完成后执行npm run serve如果项目是基于Vue CLI的控制台会输出App running at: http://localhost:8080用浏览器打开这个地址就能访问前端首页。这里要额外核对一件事——前端配置的后端接口地址是否正确。在很多Vue项目的src/utils/request.js或src/api/index.js里axios的baseURL可能是http://localhost:8088要确保端口跟后端实际启动的端口一致。6.4 跨域问题处理前后端分离部署最容易遇到的问题就是跨域CORS。前端服务器运行在8080端口后端API运行在8088端口两个端口不同浏览器会拦截跨域请求。解决方式通常有三种后端开发环境配置CORS最常见在SpringBoot里写一个WebMvcConfigurer配置类重写addCorsMappings方法允许指定来源或所有来源访问。前端代理转发也常见在Vue的vue.config.js里配置devServer.proxy把/api前缀的请求转发到后端地址。生产环境中用Nginx做反向代理前端和后端共用同一个域名。我在调试这个项目的时候直接用方式2原因很简单改了后端CORS配置等于允许所有来源访问不太安全前端代理只在开发环境生效不影响生产部署。配置起来也很简单module.exports { devServer: { proxy: { /api: { target: http://localhost:8088, changeOrigin: true } } } }6.5 部署到服务器的建议如果想让项目在局域网或服务器上长期运行我的建议是这样的前端执行npm run build构建出dist/静态目录用Nginx托管dist/目录配好root和index后端执行mvn package把项目打成可执行的jar包通过java -jar方式启动或者用Systemd配置成服务Nginx里做一个反向代理把/api路径的请求转发到SpringBoot的8088端口数据库生产环境不要用root账号新建一个最小权限的专有账号敏感配置项数据库密码、密钥不要硬编码在配置文件里可以用环境变量或配置中心管理。有一点很值得注意jar包方式和IDE启动方式对资源占用不同如果服务器内存紧张可以通过调整JVM参数来控制占用。比如java -Xms128m -Xmx256m -jar only-mall.jar这样可以显著降低对小内存云主机的压力。6.6 安全排查与项目接管建议回到我前面提到的安全确认环节。任何从一个未知来源获得的源码在真正运行之前我都建议执行这样几个动作检查配置文件application.yml、.env等文件中是否包含可疑的服务器地址、奇怪的网络回调地址检查依赖清单pom.xml和package.json中是否有不常见或拼写可疑的依赖坐标检查代码中的彩蛋搜索全局关键词比如curl、 exec、 Runtime、 URLConnection、eval确认没有隐藏的外部请求或控制逻辑数据库脚本确认初始化脚本里只有建表和插入数据语句没有可疑的存储过程或计划任务。这不是不信任他人的代码而是这一行的基本安全意识。尤其是数据库连接信息如果原代码内置的是一个互联网地址务必要改成自己的本地地址。7. 常见问题与排查技巧实录7.1 启动类报错端口被占用现象后端启动时抛出Web server failed to start. Port 8088 was already in use.排查用命令行查看端口占用情况netstat -ano | findstr 8088找到占用端口的进程用任务管理器结束对应进程即可。或者更省事的方式是直接修改application.yml里的server.port换一个不冲突的端口同时要记得同步修改前端request.js中的baseURL或本地代理配置。7.2 数据库连接失败Access denied for user现象java.sql.SQLException: Access denied for user rootlocalhost (using password: YES)排查99%的原因是application.yml里填写的密码与实际数据库密码不一致或者MySQL用户本身不允许本地或远程登录。先在命令行手动连一下数据库验证账号密码mysql -u root -p确认密码没错后再检查MySQL是否开启了远程访问权限如果SpringBoot项目跑在另一台机器上SELECT host, user FROM mysql.user;确保当前用户和host匹配。7.3 前端安装依赖失败现象npm install的时候一直报错或者卡在某个依赖上。排查最常见的是网络问题导致部分registry请求超时。解决方案大致是这样npm config set registry https://registry.npmmirror.com npm install如果还不行试试清缓存npm cache clean --force还有一个容易忽略的点是Node.js版本问题。旧版本Vue CLI可能不支持Node 18以上的版本建议安装nvmnode version manager来切换Node版本而不要硬着头皮往下跑。7.4 登录功能正常但商品图片不显示现象页面能打开但商品图片都是裂开的。排查这是比较典型一个案例。如果项目使用本地文件存储上传的图片而数据库中保存的图片路径是一个静态资源映射比如/upload/xxx.jpg那么需要在SpringBoot的配置里添加静态资源映射规则或者在Nginx里配上对应的location路径。如果图片是从外链读取的那可能是网络或IP限制问题。处理方式通常是这样的Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file:/真实路径/upload/); } }7.5 前端请求405或404错误现象登录时点击按钮抓包发现接口返回404或405。排查这种情况往往是两个问题混在一起URL路径不匹配确认前端调用的接口路径和后端Controller里的mapping路径是否完全一致包括大小写、正斜杠请求方法不匹配前端用了POST后端Controller的方法是GetMapping或者前端传参方式是JSON后端方法却要求表单字段。这种问题用浏览器的F12开发者工具看一下Network面板重点看请求URL、请求方法和响应体内容基本上就能立刻定位原因。7.6 订单提交失败、事务回滚现象点击提交订单后提示库存不足或下单失败但是部分数据已经写入了数据库。排查这种现象说明事务没有正确回滚。常见的坑有Transactional注解加在了私有方法或内部调用方法上Spring通过代理实现事务私有方法、同类内部调用不走代理事务无效方法里手动catch了异常没有重新抛出导致Spring感知不到异常事务不会触发回滚数据库表用的是MyISAM引擎不支持事务需要改为InnoDB。注意第三点容易在此项目中遇到。如果SQL脚本建表时我见过用默认引擎的情况注意确认一下SHOW TABLE STATUS WHERE Name order;的结果里Engine是不是InnoDB即可不是的话就不能保证事务性。7.7 导入SQL时报错或乱码现象SQL脚本导入时提示语法错误或者导入后页面中文显示乱码。排查先确认脚本本身的编码格式使用UTF-8编码再确认导入时客户端连接字符集比如命令行导入前先设一下mysql --default-character-setutf8mb4 -u root -p同时检查数据库和表是否也是utf8mb4字符集。有时候是脚本里用了某些函数或语法只适用于高版本MySQL比如MySQL 8.0支持的写法在5.7里就会报错需要根据自己所装的MySQL版本进行调整。7.8 其他LESS常见但值得注意的小问题这类问题我干脆做个快速整理问题现象最可能原因最快对策前端页面白屏控制台报Vue相关错误依赖版本冲突或未正确安装检查package.json必要时重装node_modules修改了数据库但页面数据不刷新浏览器缓存或者用户状态未更新硬刷新CtrlShiftR或确认是否使用了缓存策略Token过期后接口报错前端拦截器未统一处理401状态在axios响应拦截器中加入401跳转到登录页的逻辑后台管理页面无法访问当前用户不是管理员角色用脚本内置的管理员账号登录并检查roleId字段图片上传失败上传目录无写权限给上传目录赋Linux可写权限比如chmod -R 755 upload8. 总结复盘与二次开发建议8.1 从这份源码里学到了什么把这个项目完整跑通后它的价值不仅是能运行还在于它清晰展示了一套基本的电商系统由哪些部分构成。我复盘了一下至少能在三个方面受益后端方面从三层架构、统一返回结果、事务管理到库存扣减的并发控制这是一个浓缩的标准业务开发流程前端方面路由守卫、状态管理、表格CRUD、axios封装这些都是前端岗位面试常问、日常开发常写的东西数据库方面表设计、索引、字符集选择、数据初始化每一步都对应着真实项目的部署和运维需求。如果只是把代码下载下来启动一遍就完事那学到的东西有限。我更推荐的方式是拿着源码做一个破坏性实验比如故意删掉一个索引前后做一次性能对比给某个接口添加一个参数追踪它从前端到后端再到数据库的完整流转链路或者给登录接口加一个验证码模块看看JWT和用户状态是怎么管理的。这些实验做完你才算真的拥有了这套代码。8.2 适合二次开发的方向基于这套商城系统如果你有时间和精力可以在下面这几个方向上做功能迭代接入真实支付比如微信支付和支付宝的完整对接把模拟支付替换为真实交易流程需要商户资质和相关环境增加秒杀或促销模块比如限时折扣、满减计算、优惠券领取与使用这是电商业务中拉动用户活跃度的常见环节引入Redis缓存把首页热门商品、分类信息、用户Token等热点数据放到缓存中降低MySQL的压力完成前端体验优化比如增加商品多图轮播、SKU规格选择颜色、尺寸、评价功能、收藏功能做数据分析在后台增加销售统计报表、用户活跃度图表接入ECharts可视化。其中引入Redis那一步可以和系统已有的Token存储机制结合用Redis做Token的自动续期和提前失效是一个收益率挺高的改动点。但若只是个人学习阶段的演示项目也不要一上来就把方案搞得过于庞大先跑通再做合理盲区上的增强会更稳妥。8.3 一点掏心窝的建议最后分享一个我自己的习惯拿到任何一份源码的第一天我都会先在本地把数据库脚本执行一遍紧接着把项目启动一遍然后花一个下午的时间把核心表之间的关联搞明白在纸上画出它们的关系图。这个动作看似基础但它能让我后面一周的调试都顺畅得多。如果你在这个项目上遇到什么奇怪的问题不妨按照章节七的排查清单走一遍。如果排查不掉还可以看日志——SpringBoot的日志输出里有很多线索比如SQL执行错误、远程接口超时、依赖加载失败等等。日志是最忠实的朋友不要嫌它长它通常已经告诉了你答案。