资讯动态

SpringBoot+Vue+MyBatis+MySQL商城系统源码实战解析

发布时间:2026/10/6 9:56:14 来源:尧图企业网站定制
做电商类管理系统最烦的不是写功能而是找一套能直接跑起来、代码结构清楚、前后端没有乱七八糟耦合的完整源码。这套企业级网上服装商城管理系统正好是SpringBootVueMyBatisMySQL的经典组合前台商城、后台管理、商品订单、用户权限全都有。本地拉下来启动一次就知道这不是那种只有登录注册的玩具项目而是从商品上架、多规格SKU到购物车、下单、库存扣减、模拟支付、后台发货整条电商链路都是通的完整工程。它适合谁三种人最对口一是正在学SpringBoot和Vue想找一个完整业务闭环做参考的开发者二是需要交毕业设计或者接外包想拿成熟代码当底子快速改出来的同学三是公司要做一个小型B2C商城想找个鸟枪级起点做二次开发的团队。下面我从架构选型、核心模块、数据库与MyBatis细节、前端Vue工程、部署和排查五个方向把跑通这个项目过程中真正值得讲的东西都拆一遍。1. 项目整体设计与架构拆解1.1 为什么是SpringBootVueMyBatis这套组合先聊一个很多人忽略的问题电商项目方案那么多为什么这套源码选了SpringBootVueMyBatis而不是SSM老框架也不是SpringCloud微服务首先是SpringBoot。它对比传统的SSM最大的改进是“配置即代码”。在SSM时代一个项目光Spring和MyBatis的整合XML配置就能写上百行各种扫描包、事务管理器、数据源bean新人折腾一天都可能跑不起来。SpringBoot通过starter机制把这些默认行为固化下来引入一个spring-boot-starter-web内嵌Tomcat直接java -jar就能跑。这个项目的核心诉求是快速交付、方便二次开发SpringBoot是当下最务实的后端底座没有之一。然后是Vue。这里要强调“前后端分离”这个架构决策。老式商城用JSP或者Thymeleaf做服务端渲染页面逻辑和后端代码混在一起改个按钮样式都要重启服务。而这套项目用Vue做前端单页应用后端只负责输出JSON接口两边通过HTTP通信。好处很直接前端可以独立开发独立部署后端接口也能被小程序或者App端复用。很多小团队做商城一开始就前后端分离其实是为了少走回头路。再说MyBatis。电商系统里查询条件变化极多——商品列表要按分类、价格区间、品牌、关键词、上架状态组合筛选订单列表要按时间、状态、用户多维度查询。这类场景用MyBatis的动态SQL可以用if、where、foreach这些标签在XML里灵活拼SQL可控性比Hibernate的自动生成SQL强得多。Hibernate在简单CRUD上确实省事但一旦进入多表关联、复杂统计、性能优化阶段反而会因为你不知道它怎么生成SQL而吃亏。电商这种强数据、强查询的业务半自动的MyBatis是更好的选择。MySQL就不用多说了开源、免费、生态成熟配合InnoDB引擎支持事务和外键订单、库存这类强一致性业务完全能扛住中小规模流量。这套技术组合放到今天仍然是新项目里性价比最高的一套没有动辄引入微服务和分布式中间件控制住了复杂度值得细读。1.2 前后台模块划分与核心业务链路整个项目拆成两个子工程来看前台商城系统和后台管理系统。前台面向C端用户核心模块包括用户注册登录、首页轮播图与推荐位、商品分类浏览、商品搜索与多条件筛选、商品详情、购物车、订单提交、支付模拟、个人中心订单列表、收货地址管理。后台面向运营人员模块包括管理员登录、仪表盘统计、商品管理SPU和SKU维护、库存管理、分类管理、品牌管理、订单管理订单列表、发货操作、用户管理、轮播图和公告配置。两条链路的交汇点是订单这也是整个项目我最建议优先精读的部分用户浏览商品→加入购物车→提交订单→扣减库存→模拟支付→后台发货→确认收货。围绕这条链路前后台各模块像齿轮一样咬合在一起。数据流大概是这样的Vue页面调用Axios封装的API接口请求进入SpringBoot的ControllerService层完成业务逻辑MyBatis映射器操作MySQL数据库结果再逐层返回给前端渲染。值得注意的一个设计是后台和前台虽然共用同一个后端服务和数据库但通过url前缀区分接口区域比如/api/portal/**和/api/admin/**。这个区分既方便拦截器做权限控制也方便将来把两个端拆成独立服务。这一点看起来简单却是很多自研项目一开始没规划好、后期改到哭的地方。2. 服装商品与订单核心模块的实现要点2.1 多规格SKU设计服装商城与普通电商的差别电商系统里有两个概念必须分清SPU和SKU。SPU是商品聚合信息比如“男士黑色休闲西装外套”用户搜索和浏览时看到的是SPUSKU是具体可下单的型号比如“这款外套 黑色XL码”是一个SKU“黑色M码”是另一个SKU每个SKU有独立的库存、价格和货号。普通商品项目可以不做SKU一件商品就一个价格一个库存但服装这类商品不做SKU就根本没法用。服装的规格维度通常是两个颜色和尺码。颜色不只是个字符串还经常关联到商品相册里对应的图片也就是用户切换颜色时详情页主图跟着切换尺码则涉及各尺码的库存数量。这套项目里SKU设计我建议重点看两张表的配合一张是商品表SPU维度存标题、主图、详情、分类一张是SKU表存规格组合、价格、库存、货号。SKU表的字段大致长这样id、spuId、colorName、colorImage、sizeName、price、stock、skuCode、status。实际做页面交互时前端拿到一个SPU下所有SKU的数组用颜色和尺码两个维度分组构建“规格矩阵”。用户选了黑色、L码前端就去匹配是否存在这组SKU有就显示价格和库存没有就提示缺货。这个逻辑不复杂但很考验前后端的数据契约是否一致——我们当时为了快速联调直接让后端返回扁平SKU列表由前端自行匹配省掉了规格维度表项目短期能跑但如果你的规格特别复杂比如服装还有版型、季节建议在数据库里拆成规格名、规格值两张表更规范。还要提一个容易漏的点SKU货号。线下服装门店和仓库经常要扫码盘点货号要跟吊牌一致。我们在后端自定义了货号生成规则前缀是品牌缩写中间是年份和季节后面是顺序号比如JH-24Q3-0078。这样每次新增SKU时后端自动生成保证唯一。如果你接手的是别的商城源码建议先检查SKU表里有没有这个字段没有的话后面接入仓储系统会很痛苦。2.2 下单链路与库存扣减的可靠方案整个项目里含金量最高的我认为是订单提交时的库存扣减逻辑。很多人会把它做成“先检查库存够不够够就做减法”但这里有两个坑并发超卖和事务边界。并发超卖很好理解两个用户同时下单同一件衣服的同一个尺码库存只剩1件两个请求同时读到库存为1都判断可购买都去做扣减结果库存变成了-1这就是超卖。这个项目采用的方式是数据库更新时带上库存条件也就是所谓的乐观锁思路SQL大致是UPDATE sku SET stock stock - 1 WHERE id #{skuId} AND stock 1注意这里的stock 1条件如果在并发下只有一个请求能更新成功对UPDATE影响行数为0的那个SKU说明库存已经不足事务回滚用户会收到“库存不足”的提示。这比先SELECT再UPDATE的安全得多也不需要给表加锁是单体商城项目里性价比最高的方案。事务边界同样关键下单操作不只是扣库存还要创建订单主表记录、生成订单明细、清空购物车相关项、记录库存流水。任何一个环节失败都不能留下半成品数据所以要整体包在Transactional里。我的建议是锁尽量在事务内短时间持有扣库存的UPDATE放前面订单明细插入放后面缩短持锁时间能减少并发下的事务等待。订单状态机也是个值得完整读一遍的点。一个订单要经历待支付→已支付→已发货→已完成→已取消这些状态而取消还能细分为“超时未支付自动取消”和“用户手动取消”。这个项目里用一个整型字段status存状态码配合状态枚举类统一管理。如果你后续要加售后功能务必把状态流转收敛到Service里不要散落在Controller层到处改否则状态会越改越乱排查问题的时候你根本不知道谁把订单改成了什么状态。3. 数据库表设计与MyBatis落地细节3.1 核心表结构、字段规范与索引策略数据库是一个商城项目的底子。这套源码的表不算多但每张表怎么建、字段怎么起名很值得琢磨。核心表大概有这些用户表user、分类表category、品牌表brand、商品表product、SKU表sku、购物车表cart_item、订单表orders、订单明细表order_item、收货地址表address。几个关键的规范细节先说透。第一金额字段一律用DECIMAL(10,2)绝对不要用FLOAT或DOUBLE。浮点数在计算机里是近似存储0.10.2会变成0.30000000000000004涉及钱的数据不准是会出大事的。订单金额、商品单价、运费这些字段用DECIMAL才能保证精确计算。第二逻辑删除。商品和订单一般不做物理删除表里加deleted字段默认0删除时UPDATE成1这样历史数据还能追溯。第三时间字段用datetime不要用varchar存时间字符串否则排序和范围查询都是灾难。索引策略上项目里给几个高频查询条件加了索引这是必考也必用的经验订单表的用户ID加普通索引业务上“我的订单”页面按用户查最频繁订单表的创建时间加索引后台列表经常按时间排序SKU表的spu_id加索引因为商品详情页要查该SPU下所有SKU商品表的状态和分类ID做联合索引前台列表筛选“某分类下上架商品”是最高频的SQL。这里提个容易被忽略的坑索引不是越多越好尤其是频繁更新的表索引太多会拖慢INSERT和UPDATE。再补一个MySQL版本相关的细节。这套项目用MySQL 5.7完全没问题用8.0也能跑但要改驱动和连接参数这个问题后面部署章节细说。建表字符集记得设置utf8mb4而不是utf8因为utf8在MySQL里最多存3字节遇到生僻字、emoji等4字节字符会报错或乱码。3.2 MyBatis动态SQL、主键回填与缓存要点拿到源码后建议先把MyBatis的Mapper和XML文件结构捋一遍。这套项目的Mapper层分了接口和XML映射接口里定义方法XML里写SQL。有一个实用的小语法——useGeneratedKeys和keyProperty——在插入购物车、订单时用了很多次。它的作用是插入成功后把数据库自增主键直接回填到实体对象的ID字段里比如插入订单后立刻拿到订单号去生成明细避免再查一次数据库insert idinsertOrder parameterTypecom.shop.entity.Orders useGeneratedKeystrue keyPropertyid INSERT INTO orders (user_id, order_no, total_amount, status, created_at) VALUES (#{userId}, #{orderNo}, #{totalAmount}, #{status}, NOW()) /insert动态SQL是MyBatis的灵魂。商品列表的多条件筛选是最典型场景前端可能传分类ID、品牌ID、最低价、最高价、关键词、上下架状态每种条件都可选可不选。如果用Java代码去拼SQL又要拼接又要防注入很难维护。XML里用where加if就清爽得多select idselectProductList resultTypecom.shop.entity.Product SELECT * FROM product where if testcategoryId ! null AND category_id #{categoryId} /if if testbrandId ! null AND brand_id #{brandId} /if if testminPrice ! null AND price gt; #{minPrice} /if if testkeyword ! null and keyword ! AND title LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY id DESC /selectwhere标签会自动去掉多余的AND或者OR这个细节解决了最让人头痛的SQL拼接边界问题。另外批量插入订单明细时用了foreach这也是固定套路注意collection对应参数列表、item是循环变量名别写岔。关于缓存再说两句。这套源码里MyBatis默认开启一级缓存也就是同一个SqlSession内相同查询直接走内存不查库二级缓存默认是关闭的原因很简单多表Join场景下缓存的一致性很难保证。商城数据变化频繁商品被改价、库存被调整如果开着二级缓存不及时刷新用户看到的就是脏数据。所以我的建议是电商项目用MyBatis二级缓存要极其保守尽量不用需要缓存的时候交给Redis去管热点数据。这也是后面你二次开发时最应该记住的一条原则数据库缓存不是加了就快而是加对了才快。4. 前端Vue工程化与关键页面实现4.1 工程目录、接口封装与跨域联调前端这块用的Vue脚手架工程整体目录结构很标准src/api放接口定义src/views放页面组件src/router放路由配置src/store放全局状态src/utils放工具函数。很多新手拿到源码会直接去看页面我的建议是先看utils/request.js因为那是整个前端请求的枢纽。axios实例封装是这个工程的重头戏。它做了几件事设置基础URL指向后端接口地址、请求拦截器里把登录后存下来的token塞进请求头、响应拦截器里统一处理业务状态码。举个例子后端约定返回格式是{ code: 200, message: success, data: {...} }当code为401时说明token失效前端直接跳转登录页并清空本地会话。这一套封装做完所有页面请求都不用自己处理鉴权和错误弹窗省掉的重复代码量非常可观真实企业项目里也会这么做。跨域问题是最多人卡住的地方。开发阶段前端跑在Vue的dev server上比如localhost:8080后端跑在localhost:8081浏览器会拦截跨域请求。工程里在vue.config.js配置了devServer的proxy代理把前端的/api请求转发到后端地址这样浏览器看到的始终是同源请求不需要后端开启CORS宽松策略// vue.config.js module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }这里重点提醒一下不要把target写死成局域网IP如果你要拿手机连同一个局域网调试target最好是localhost或者本机实际IP但要注意后端能不能从这个IP接收到请求很多同学卡在这一步其实是后端没监听对外网卡。4.2 路由守卫、权限控制与核心交互逻辑商城的权限体系分两块前台用户和后台管理员。前端怎么区分靠的是登录时后端返回的角色字段以及本地存储里的token。项目里路由配置拆成静态路由和动态路由静态路由是登录页、商品浏览这些公开页面动态路由是后台管理页面根据角色动态注册。路由守卫是Vue Router里被大量使用的一个能力。它的核心逻辑在router.beforeEach里每次路由跳转前做三件事判断访问的页面是否需要登录通过路由meta的requiresAuth字段控制、判断本地是否有token、判断当前用户角色是否能访问该路由。要是没登录就乱点购物车和订单页会直接弹到登录页。这个设计我在多个商城项目里都用过属于必会技能。状态管理方面购物车是使用全局状态最典型的场景。用户加了几个商品、数量怎么变、全选还是单选、总价怎么算这些数据如果只是在各个组件里自行传递会乱成一锅粥。vuex里建一个cart模块把购物车列表存到state然后通过getter计算总价、选中数量和总件数组件里只负责触发action。另外购物车数据在登录后和用户绑定存到后端数据库这样换设备登录购物车还在这是商城的基础体验。很多简单源码把购物车只放在localStorage刷新不丢但换个浏览器就丢建议你自己二次开发的时候把购物车落到后端注意同步合并的逻辑这个工程的做法值得参考。商品列表页的规格筛选交互也是前端里比较讲究的部分。左侧分类树、顶部品牌和价格区间每次筛选条件变化就重新请求列表接口同时路由query参数会记录当前筛选条件这样刷新页面后筛选状态不丢还能方便分享链接给朋友。这里的核心点是“条件驱动请求”把筛选条件封装成对象提交给后端但别忘记在请求前处理空值字段否则后端动态SQL里老要写多余的判空还容易查出脏数据。5. 部署上线与高频问题排查实录5.1 本地环境搭建与前后端联调全流程一套完整源码拿到手第一件事不是读代码而是让它在本地跑起来。这个项目的前后端分离环境要求大致是JDK 1.8或者11、Maven 3.6、Node 14、MySQL 5.7或8.0、一个趁手的IDE后端用IntelliJ IDEA前端用VS Code。后端启动的步骤分四步第一步在MySQL里创建数据库把项目里提供的sql脚本导入进去建库时要选utf8mb4字符集第二步打开后端工程找到application.yml改数据库连接信息包括URL、用户名、密码这里要注意如果是MySQL 8.0驱动类名要写成com.mysql.cj.jdbc.Driver同时URL里加上?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8不然很容易碰到时区报错和中文乱码第三步确认Maven依赖能下载完因为这些依赖都在中央仓库首次构建会比较慢第四步启动SpringBoot的启动类看到Tomcat started on port(s): 8081就说明后端起来了。前端启动同样简单在frontend目录下执行npm install装依赖然后npm run serve启动开发服务器默认端口8080。这里有个经验如果npm install报错优先看Node版本是不是太新或者太老Vue CLI工程在Node 17以上的版本容易出现OpenSSL错误解决办法是set NODE_OPTIONS--openssl-legacy-provider或者直接装Node 16长期支持版省心。前后端联调时浏览器打开http://localhost:8080用管理员账号登录后台随便点几个页面如果能正常加载数据说明代理和接口都通了。我建议第一次跑通后先别急着看页面效果直接打开浏览器F12看网络请求观察接口返回的数据结构这比看源码理解得更快。5.2 高频踩坑问题速查表本地跑通和二次开发过程中我整理了一份高频踩坑速查表基本覆盖了最常见的报错现场现象大概率原因解决办法启动报Unknown database数据库没导入SQL脚本或者库名和配置不一致重新导入sql脚本核对application.yml中的库名连接报Access denied for user数据库账号或密码错误用root账号在MySQL里重置密码或者配置正确的账号报Public Key Retrieval is not allowedMySQL 8.0的caching_sha2_password插件问题JDBC URL加allowPublicKeyRetrievaltrue中文乱码表字符集不是utf8mb4或连接参数没加密建库建表统一utf8mb4连接串带characterEncodingutf8后端接口能通前端请求404代理路径或后端ContextPath不匹配检查vue.config.js的proxy路径和后端路由前缀前端图片不显示图片存储路径是本地绝对路径后端没传到对应目录确认上传目录存在或在配置里修改访问映射购物车/订单提示库存不足SKU的缓存或乐观锁条件不对检查扣库存SQL是否带了stock 1条件检查事务是否回滚打包后部署页面刷新404前端路由用了history模式Nginx没配置fallbackNginx配置try_files $uri $uri/ /index.html;这里挑两个重点展开。第一个是Public Key Retrieval is not allowed这是MySQL 8.0默认加密方式导致的新版驱动默认禁止从服务器上取公钥解决方式有两种你选一种就行JDBC URL里加allowPublicKeyRetrievaltrueuseSSLfalse或者在MySQL里把用户的插件改回mysql_native_password。第二个是前端history路由404问题开发模式不会暴露上线后用Nginx部署一定要把所有非静态文件请求都rewrite到index.html不然用户直接在地址栏输入/product/1001或者刷新页面就是404这个坑我见过太多次了。5.3 生产环境部署与上线注意点项目最终上线和生产环境相关的几个问题提前说清能少走很多弯路。前端构建执行npm run build产物在dist目录。最省事的部署方式是让前端项目和后端在同一个域下避免跨域。做法是把dist目录里的文件拷到SpringBoot的src/main/resources/static/下重新打包后端一个jar包搞定所有。这种方式适合小项目、流量不大、服务器就一台的场景。推荐的方式是用Nginx独立托管前端后端单独跑一个端口通过Nginx的location配置把/api反向代理到后端服务。这种方式前后端解耦将来扩服务或者加CDN都方便生产环境更规范。数据库上线前必做的事我列一下把deleted字段默认值设为0给高频查询字段添加索引修改MySQL的sql_mode避免因为严格模式导致线上插入失败。还有务必定时备份最简单的方式是配置crontab每天凌晨跑一次mysqldump备份文件放到另一块磁盘或者对象存储别和数据库放同一台机器。很多上线事故都是硬盘坏了或者数据库被误删没有备份就全完了。文件上传也是个容易忽略的点。商品图片一般传到本地磁盘的某个目录但生产环境里如果程序换了服务器图片路径就失效了。二次开发的时候建议把文件存储抽象出来本地开发用磁盘存储正式环境切到对象存储OSS或者云存储都行。这套源码里图片上传的逻辑很简单但作为一个扩展点值得你改造时优先动它。5.4 我用这个项目二次开发的几点心得最后聊点实操之外的东西。我带团队做过一个类似项目的定制客户要一个小型服装品牌的官网商城要求有商品展示、在线下单、后台管货。当时没有从零开发直接拿这套SpringBootVueMyBatisMySQL的项目做底子整个过程下来有几个深刻感受。第一底子够不够硬要看订单和库存设计。很多网上流传的商城源码页面做得花哨结果订单表只有一个字段没有明细表库存扣减就是简单的减一完全没法商用。这套项目在核心交易链路上没偷懒表结构、乐观锁、事务都到位了改起来很有底气。第二二次开发最大的成本在前端页面改造。客户要定制UI改样式、改组件、改路由Vue的前端架构足够灵活页面组件化得很好我们改了主题色、加了品牌首页的专题活动模块工作量可控。如果是一套JSP项目UI改造几乎要全部重写效率完全不是一个量级。第三性能瓶颈很快会出现在单机MySQL上。项目上线初期待遇不错日均几百单完全没压力但后来赶上一次促销活动瞬时流量冲高之后数据库连接数和慢查询都上来了。这次经历让我明白任何单体架构都有天花板所以改造时提前预留了扩展点热点商品数据走Redis缓存订单提交通过MQ削峰图片静态资源放CDN。这些不是一上来就要做的但架构上先预留接口后面加才不伤筋动骨。如果你拿这个项目做毕业设计我建议重点突出两个亮点一个是SKU多规格设计与实现一个是基于数据库乐观锁的防超卖方案。这两块有理论、有代码、有踩坑过程答辩时比单纯说“我做了个商城系统”有说服力得多。如果你拿来接项目我建议第一版先把商品管理和订单流程跑通给客户演示的时候用真实数据走一遍全流程比任何PPT都管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑