☰
SpringBoot+Vue+MyBatis+MySQL企业级服装商城系统全栈实战解析
2026/10/11 19:16:53 网站建设 项目流程

做服装商城项目之前,我其实一直觉得它跟通用电商没有本质区别:无非是商品、购物车、订单、支付那一套。直到真上手之后才发现,服装行业的业务约束比想象中复杂得多。尺码库存要锁死,颜色款式不能乱配,换季上架下架频繁,促销规则五花八门,这些都需要一套能拿在自己手里的完整系统来兜底。这套基于SpringBoot + Vue + MyBatis + MySQL的企业级网上服装商城管理系统,正好把这条链路完整串了起来。它不是一个只能演示登录注册的空壳,而是把商品、库存、订单、支付、优惠、后台权限这些模块都落地了的全栈项目,适合拿来做二次开发底座,也适合正在为毕设或简历项目发愁的同学完整吃透。

后端用 SpringBoot 做业务核心,Vue 做用户端和后台管理界面,MyBatis 负责跟 MySQL 打交道,整个技术栈非常主流。本文不打算逐行念代码,而是按照我从拆源码到本地跑通、再到思考上线的实际路径,把架构模块、服装行业建模、部署步骤、联调坑点、上线加固这几个关键环节一次讲透。

1. 这套系统为什么值得拆:架构选型与功能地图

我先说结论:这个技术组合放在今天依然是“性价比最高”的全栈电商方案之一。不是说微服务不好,而是对于绝大多数服装商城项目而言,单体架构配合清晰的模块划分,开发和维护成本远低于一上来就拆十几个服务。

1.1 从技术栈看选型逻辑

SpringBoot 解决的是后端基础设施问题。它把配置自动装配、内嵌容器、依赖管理这些事情藏起来,让我们可以把精力集中在商品、订单这些业务逻辑上。Vue 则负责前端交互,用户端页面和后台管理界面都可以复用组件,开发效率很高。MyBatis 在这套组合里看起来最“朴素”,但它给了一个非常实在的好处:SQL 掌握在开发者手里。服装商城涉及复杂的多表查询、库存条件更新,用 MyBatis 写 XML 里的 SQL 反而容易调优和排查问题。

MySQL 作为存储层,对这个体量的项目完全够用。只要索引建得合理、事务边界控制好,几千个 SKU、几万条订单对它来说压力不大。

有人可能会拿这套组合跟一些前后端不分离的老技术相比,或者觉得应该上 Spring Cloud。我个人的建议是,先分清项目目标:如果是练手、毕设、创业初期验证业务,单体 + 经典三件套比分布式更适合;如果已经有明确的千万级流量预期,再考虑微服务也不迟。技术焦虑往往来自过度设计,而不是选型不够新。

1.2 完整版源码里通常有哪些模块

拿到源码后,第一件事不是急着跑起来,而是先看目录结构和数据库脚本,弄清楚这套系统到底覆盖了哪些业务。一个完整的服装商城管理端和用户端,模块划分大致如下:

  • 用户模块:注册、登录、个人信息、收货地址管理。
  • 商品模块:商品分类、品牌、SPU/SKU 管理、上下架、商品详情。
  • 搜索与筛选:按名称搜索、按分类浏览、按颜色/尺码/价格筛选。
  • 购物车模块:加入购物车、修改数量、删除、选中结算。
  • 订单模块:下单、订单列表、订单详情、取消、确认收货。
  • 支付模块:对接支付渠道、支付回调、退款状态同步。
  • 营销模块:优惠券、满减活动、限时折扣。
  • 后台管理:管理员登录、权限控制、商品管理、订单管理、数据统计。

这些模块如果是分开的散件,那不难。难的是它们彼此之间怎么协作。比如下单时要扣库存、生成订单、计算优惠,还要防止并发超卖,这是整套系统最考验设计能力的地方。源码的价值恰恰在于,它把这些协作逻辑完整地呈现出来了。

2. 服装商城的核心建模:SKU、库存与订单状态设计

通用电商项目的商品建模往往比较简单,一个商品表加一张图片表就能撑起来。服装商城不行,因为服装天然有多规格属性。尺码、颜色、版本这些组合在一起,会让数据表结构迅速膨胀,处理不好就会变成一个巨型笛卡尔积。

2.1 商品表与前端的分离:SPU 和 SKU 是关键

服装商城建模的第一步,是把“商品”和“具体可卖的商品”分开理解。

SPU 是消费者看到的商品条目,比如“某品牌春季薄款连衣裙”,它包含标题、主图、详情描述、所属分类。SKU 是这个商品下具体可以下单购买的库存单位,比如“连衣裙-白色-M码”“连衣裙-黑色-L码”。在数据库里两张表主外键关联,SKU 表保存具体的颜色、尺码、价格、库存数量。

在数据库设计里,SKU 表的字段通常是这样的思路:

CREATE TABLE product_spu ( id BIGINT PRIMARY KEY AUTO_INCREMENT, spu_name VARCHAR(200) NOT NULL, category_id BIGINT NOT NULL, brand_id BIGINT, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE product_sku ( id BIGINT PRIMARY KEY AUTO_INCREMENT, spu_id BIGINT NOT NULL, sku_code VARCHAR(64) NOT NULL UNIQUE, color_name VARCHAR(50), size_name VARCHAR(50), price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, sale_count INT DEFAULT 0, status TINYINT DEFAULT 1, INDEX idx_spu (spu_id), INDEX idx_status (status) );

这里有个细节值得抄作业:SKU 编码一定要是唯一且可识别的。可以根据“分类编码 + 商品序号 + 颜色编码 + 尺码编码”拼一串规则编码,这样仓库发货、Excel 导出、以后对接 ERP 系统都方便。很多项目偷懒直接让数据库自增 ID 作为 SKU 编码,到了后期对账的时候会非常痛苦。

2.2 库存扣减必须用条件更新,不能裸 update

服装商城最常见的并发场景就是爆款商品开卖瞬间,大量请求同时扣同一个 SKU 的库存。如果代码写成先查库存、判断是否充足、再执行更新,那么在高并发下很容易出现超卖。

正确做法是让数据库在更新时做条件判断,用一条 SQL 把查询和更新合并成一个原子操作:

UPDATE product_sku SET stock = stock - #{buyCount} WHERE id = #{skuId} AND stock >= #{buyCount}

这条 SQL 返回的影响行数如果为 0,说明库存不足,直接提示用户“库存不够”。这比 Java 代码里 synchronized 或者分布式锁简单可靠得多,也正好体现出 MyBatis 的价值——SQL 完全可控。配合 Spring 的@Transactional事务,库存和订单表就能保证一起成功或一起回滚。

2.3 订单状态机:订单不是非黑即白的

订单模块是另一个容易踩坑的地方。新手往往用一个status字段硬编码状态,然后用一堆 if 判断到处流转,最后状态散落各处,改了一个漏了两个。成熟的商城项目,订单状态通常统一设计成一组常量或枚举:

  • 待付款
  • 已付款待发货
  • 已发货待收货
  • 已完成
  • 已取消
  • 售后中

这里需要特别注意两个隐藏点:一是超时未支付订单的自动关闭,一般通过定时任务扫描“创建时间超过 30 分钟且状态为待付款”的订单,执行关闭并把冻结的库存恢复。二是支付回调的幂等处理,回调可能重复到达,所以处理逻辑必须保证同一笔订单只会被更新一次状态,否则会出现重复发货之类的严重事故。

2.4 服装行业特有的促销与清仓场景

服装的生意逻辑跟 3C 数码不一样,它讲究换季清仓。源码如果支持营销模块,那促销方式往往不止是“打折”,还可能有满减、优惠券叠加、指定分类参与活动、会员价区分。新手做的时候容易把所有优惠逻辑揉在一个订单金额计算方法里,后期改规则时痛不欲生。

建议的做法是:把“价格计算”和“优惠计算”拆开。商品原价加总后,依次应用单品折扣、满减、优惠券、会员折扣,每一步生成一条优惠记录明细,最后汇总成应付金额。这样每一笔订单为什么是这个价格,都能追溯清楚。

3. 从 0 到 1 本地跑通:环境准备、数据库初始化与启动顺序

很多同学拿到完整源码后,连 README 都没看完就直接启动,结果报错一个接一个。其实只要照着正确顺序来,最快半小时就能把前后端都跑起来。

3.1 环境版本先对齐

先别急着装最新版。很多启动失败根本不是代码问题,是环境版本不匹配。这个技术栈我建议按下面的版本组合来准备:

工具推荐版本说明
JDK1.8 或 11SpringBoot 2.x 系列建议 JDK 8/11 都兼容
Maven3.6.3 或 3.8.x依赖下载稳定
Node.js14.x 或 16.xVue 前端构建够用,太新的 Node 偶尔有兼容问题
MySQL5.7 或 8.05.7 最稳,8.0 注意时区配置

以上版本是我实测中很少出问题的组合。如果本地已经有更高版本,也不一定不能用,但出现问题时不排除版本因素。

3.2 初始化数据库:先把数据脚本导进去

源码目录下通常会有一个.sql文件,里面包含建库、建表、初始化数据。用命令行操作最直接:

mysql -uroot -p -e "CREATE DATABASE IF NOT EXISTS clothing_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -uroot -p clothing_mall < db/clothing_mall.sql

这里有个经验:数据库字符集一定要用 utf8mb4,不要用 utf8。服装商品名称和详情里经常有特殊符号、生僻字,utf8 在 MySQL 5.7 里并不是真正的全量 Unicode 支持,用 utf8mb4 才能避免入库报错。

初始化完成以后,可以用 Navicat 或命令行看一眼数据表数量和关键表里的数据,比如product_sku表里是否已经有商品、sys_user表里是否已经有管理员账号。确认有数据再启动后端,能提前排除“数据库连错”或“脚本没导入成功”的问题。

3.3 启动后端:先看配置,再跑命令

后端启动前,先打开application.yml或application.properties,找到数据库连接部分,改成你自己的账号密码:

spring: datasource: url: jdbc:mysql://localhost:3306/clothing_mall?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456

这里特别提醒:serverTimezone一定不要省。MySQL 8.0 默认时区跟 Java 本地时区不一致,不配置的话查出来的时间字段会差几个小时,或者直接启动报错。另外注意useSSL=false,本地不需要 SSL 加密。

配置改好后,在后端根目录执行:

mvn clean package -DskipTests

如果电脑上没有安装 Maven 命令,可以用 IDEA 打开项目,等依赖下载完,直接运行启动类。第一次下载依赖会比较慢,如果遇到下载失败,建议在 Maven 设置里换成国内镜像源。

Maven 打包成功后,可以在 target 目录下看到一个 jar 文件,直接跑:

java -jar target/clothing-mall-0.0.1-SNAPSHOT.jar

看到Started开头的日志就说明后端启动成功了。SpringBoot 默认端口是 8080,如果本地冲突,可以在配置里改成 9090。

3.4 启动前端:npm 安装与代理配置

前端部分进入项目目录后,先安装依赖:

npm install

如果这一步特别慢或者报错,大概率是网络源问题,换一下镜像源就好:

npm install --registry=https://registry.npmmirror.com

依赖装完后,注意看前端项目里的环境配置文件。Vue 项目一般有.env.development文件,里面的VITE_API_BASE_URL指向后端地址。默认是:

VITE_API_BASE_URL=http://localhost:8080

如果后端改成了 9090,这里要对应改。其次,如果存在vite.config.js,里面的 devServer 代理也可能改一次,确保前端请求能转发到后端。

server: { port: 3000, proxy: { '/api': { target: 'http://localhost:9090', changeOrigin: true } } }

最后启动:

npm run dev

浏览器访问http://localhost:3000,看到商城首页就说明前端环境通了。此时用管理员的账号密码登录后台,进去能看到商品管理、订单管理等菜单,整套系统就基本盘活了。

4. 联调阶段最容易翻车的五个细节

只要不是第一次碰全栈项目,几乎都会在前后端联调阶段卡上几个小时。我这里列了五个真实项目里出现频率最高的问题,都是那种“报错不明显、查半天才发现”的类型。

4.1 跨域问题:代理配了为什么还是报跨域

前端跑在 3000 端口,后端跑在 9090 端口,浏览器默认会拦截跨域请求。如果你用的是 Vite,建议优先用代理解决,而不是在后端加一堆 CORS 配置。代理配置好以后,前端代码里的请求路径都写/api这样的相对路径,不要写完整的http://localhost:9090,这样才能走代理转发。

如果后端开启了自定义拦截器,还要注意拦截器是否会拦截预检请求。OPTIONS请求在跨域时一定不能被拦截器拦截,否则预检失败,浏览器会直接把请求拦下,控制台提示 CORS error,排查半天最后发现是拦截器没放行。

4.2 时间字段相差 8 小时

这个问题几乎是必踩。明明是同一个new Date(),存到数据库再查出来就差了 8 个小时。解决方式分三个层面,缺一不可:

  1. 数据库连接 URL 加serverTimezone=Asia/Shanghai。
  2. MySQL 时区设置为+8:00,可在连接工具里执行SET global time_zone = '+8:00';。
  3. Java 实体类日期字段用LocalDateTime,不要用java.util.Date,避免 toString 时输出默认的 UTC 时间。

做完这三步,绝大多数时间偏差都能解决。如果还是不对,再检查前端展示时有没有做本地时区转换。

4.3 文件上传成功但图片立刻 404

商品主图、品牌 Logo 上传后,接口提示成功,但数据库里只存了一个相对路径。刷新页面图片就 404。这个问题通常是静态资源映射没配置好。

SpringBoot 默认只会映射classpath:/static/下的静态资源。如果上传文件保存到了服务器磁盘的某个目录,必须手动把那个目录映射成可访问的 URL:

@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceHandler("file:" + uploadPath + "/"); } }

这里我还要额外提醒一句:本地开发时如果把上传目录放在项目内部,重启项目后这个目录可能被清理,导致图片全部丢失。建议直接放到一个固定的外部路径,比如/data/mall/upload,跟项目源码分离。

4.4 接口返回格式不统一,前端解析全靠 catch

有的项目里,一个接口返回{code:200, data:{}},另一个接口直接返回一段 JSON,还有的出错了只返回个500状态码。前端封装 axios 的时候,只能到处写分支判断。

比较好的做法是统一定义一个后端返回对象:

public class Result<T> { private Integer code; private String message; private T data; }

所有接口都返回这个结构,后端异常统一被@RestControllerAdvice捕获,转成{code:500, message:"服务器异常"}。前端 axios 拦截器只解析一次code,等于把整个项目的错误处理逻辑收敛到了一处。这个设计对于大型商城项目尤其重要,因为页面多、接口多,不统一后期就是灾难。

4.5 Token 过期了但页面不跳转

登录模块一般用 JWT Token 维持会话。很多学联调的同学最容易忽略的是:前端请求拦截器只负责把 Token 塞进 Header,但没处理 Token 过期后的全局跳转。结果是用户看到一个转圈的页面,控制台报 401,人却还留在原地。

正确的做法是在 axios 响应拦截器里统一判断状态码 401,然后清空本地 Token、跳转到登录页,或者用刷新 Token 的机制重新换取登录态。这样无论哪个接口过期,用户都会被引导回登录入口,体验会好很多。

5. 从“能跑”到“敢上线”:性能、安全与部署加固

本地跑通只是开始。真正要把这套商城系统部署到公网,服务真实用户,还有几个必须做的加固动作。这些内容其实不少,我挑其中最关键的几条讲。

5.1 数据库索引:先把慢 SQL 找出来

商城系统的数据库是核心,索引建不好,接口再优化也是白搭。可以从这几个维度检查:

  • 订单表查询条件里如果有user_id和status,就应该建联合索引。
  • 商品列表页按分类和状态筛选,应该在category_id和status上建索引。
  • 搜索功能如果用到模糊查询LIKE '%关键词%',索引会失效,数据量大以后需要考虑全文索引或专门的搜索组件,而不是硬抗。
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);

也可以开启 MySQL 慢查询日志,看看哪些 SQL 超过 1 秒,然后结合EXPLAIN看执行计划。这一步对新手来说是最快的性能排查入门方式。

5.2 缓存:先加 Redis 还是先别加

很多人一上来就说要加 Redis,但“能力越大,责任越大”。缓存能扛热点,但也会引入缓存穿透、缓存击穿、缓存一致性问题。如果项目体量还没到每秒几百次访问,我的建议是:先把 MySQL 查询优化好,再考虑用缓存。

如果确实要加,优先缓存这些热点数据:商品分类树、首页轮播图、热门商品列表。缓存更新策略用最简单的“更新数据库后删除缓存”,下次查询再回源数据库。这个策略虽然不能说绝对完美,但实现简单,适合这套代码架构。

5.3 安全加固:密码、权限与防重

源码里如果用了明文密码存储,上线前必须改掉。Spring Security 或者自带的工具类都可以实现 BCrypt 加密。管理员后台要特别注意权限控制,不要把敏感接口全部暴露给普通用户。

支付环节里,不管是真实支付渠道还是模拟支付,回调接口都要做签名校验。很多商城源码为了本地开发方便,会开放一个“模拟支付成功”的接口,上线时忘记关掉,等于送给攻击者一个免费下单的通道,这个一定要检查。

5.4 部署上线:Nginx + 后端包 + 备份策略

部署方式有一套很成熟的做法:前端打包成静态文件后交给 Nginx 托管,后端打包成 jar 用 systemd 守护进程运行,MySQL 每天定时备份。

前端构建:

npm run build

生成的dist目录,扔到服务器 Nginx 的 html 目录下。Nginx 配置里除了托管静态文件,还要反向代理/api请求到本地的 9090 端口。

location /api/ { proxy_pass http://127.0.0.1:9090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }

数据库备份可以写一条简单 cron:

mysqldump -uroot -p你的密码 clothing_mall > /backup/mall_$(date +%Y%m%d).sql

定期把备份文件拉走,别只存在同一台服务器上,否则服务器磁盘坏了备份也一起没了。

6. 最后再分享一点我的实现体会

这套 SpringBoot + Vue + MyBatis + MySQL 的服装商城源码,我认为最大的学习价值不在于某个炫酷的页面,而在于它完整展示了电商业务从建模到落地的闭环。我自己在重写类似项目时,最大的改变是学会了“先跑通再优化”:第一遍顺序不重要,先把订单和库存闭环做出来,第二遍再重构代码结构和加缓存。很多同学一开始就想着加入 Redis、加 MQ、加分布式事务,结果所有复杂度都压在一个原型上,最后连基本功能都完成不了。如果你现在手头正需要一套服装商城的完整源码做参考,我的建议是先按本文的流程跑一遍,再拆出商品、订单、权限这三条线逐行读,读完之后你会对整个电商系统的演进逻辑有完全不同的理解。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询