毕业设计做到一半,最怕的不是需求复杂,而是拿到一套看起来功能齐全的源码,本地却怎么都跑不起来。最近在复现“衣依”服装销售平台系统时,我把整个前后端链路完整梳理了一遍,从SpringBoot2到Vue3,再到MyBatis-Plus和MySQL8.0,把设计逻辑、启动顺序和踩过的一些坑全部记录下来。如果你正准备做电商类Java Web项目,或者想拿一套完整系统作为参考,这篇内容应该对你有帮助。
1. 项目定位与功能范围:这个“衣依”系统到底做了什么
1.1 为什么叫“衣依”,目标用户是谁
“衣依”这个名字本身就有服装行业“穿衣搭配、贴身陪伴”的含义,项目的定位是一个面向普通消费者的服装销售平台,同时也包含了后台管理端。用户端解决的是“逛、查、买、跟单”这条购物主线,管理端解决的是“商品、订单、用户、库存、分类”这些运营数据怎么维护的问题。对于复现者来说,它的核心参考价值不在于“商城”这个概念有多新鲜,而在于它把电商业务中最关键的几个闭环都落到了代码里:用户注册登录、商品展示检索、购物车、下单扣库存、支付状态回跳、订单生命周期管理。把这一套链路跑通,基本上对Java Web业务系统的开发节奏就有了完整的体感。
1.2 前台购物与后台管理的功能清单
前台功能围绕“让用户顺利完成一次购买”来设计,核心模块包括用户注册与登录、商品分类浏览、商品搜索、商品详情、购物车管理、订单确认与提交、订单列表与详情、支付结果模拟回调。从用户视角看,这个系统的路径非常直接:进首页,逛分类,搜关键词,点进详情,加入购物车,去结算,提交订单,模拟支付完成,然后在“我的订单”里查看状态。
后台功能则围绕“让运营人员管理平台数据”来设计,包含管理员登录、商品管理(新增、编辑、上下架)、分类管理、用户管理、订单管理(发货、查看详情)、库存管理。后台的主要价值是把前台产生的业务数据可视化,并且支撑前台正常运转,比如商品下架后前台不再展示,库存扣减后不能超卖。
角色划分很清晰,普通用户走前台购物流程,管理员走后台维护流程。权限上通过登录状态和控制层拦截区分两类角色,这一点在复现时需要重点关注,因为很多新手项目容易忽略对未登录用户越权操作的拦截。
1.3 系统的核心业务闭环
整个系统最重要的业务闭环是:用户浏览商品 → 加入购物车 → 提交订单 → 扣减库存 → 模拟支付 → 更新订单状态 → 管理员发货。这套闭环里隐藏着电商项目最常见的几个设计难点:购物车数据是存Redis还是存MySQL、下单时库存怎么扣才能避免超卖、订单状态在不同节点怎么流转、支付回调怎么保证幂等。后面我会单独展开讲这几个点,因为只看一遍源码和真正动手踩一遍坑,理解深度完全不同。
2. 技术栈选型:为什么是SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0
2.1 后端选型逻辑
SpringBoot2在这个项目里几乎是自然选择。相比Spring MVC那一套繁琐的XML配置,SpringBoot2通过自动配置把项目骨架搭好,内嵌Tomcat,打成一个Jar包就能直接启动。配合Maven做依赖管理,搭建周期可以压缩到很短。对于课程设计、毕业设计或者个人练手项目来说,SpringBoot2是目前资料最全、遇到问题最容易搜到解决方案的技术版本。为什么选2而不是3?因为2.x版本对JDK8的兼容性最好,很多实验室或本机环境默认装的就是JDK8,不需要额外升级环境,稳定性也经过大量线上项目验证。
MyBatis-Plus选它的理由更实际:它是在MyBatis基础上做的增强工具,提供通用的Mapper接口、条件构造器、分页插件、逻辑删除和自动填充功能。项目里大量的单表CRUD操作,如果用原生MyBatis写XML,你会被大量枯燥的insert、update语句淹没,而MyBatis-Plus可以让你把注意力放在业务逻辑上,代码量能省掉三成以上。
2.2 前端与工程化选型逻辑
Vue3是当前前端框架的主流方向,配合Vite构建工具,开发环境热更新速度比Vue2时代的Webpack快得多。项目用Vue3 + Vite + Vue Router + Pinia(或Vuex)这套组合来开发前台和管理端,组件化开发的思路贯穿始终。对于后端开发者来说,Vue3的Composition API刚开始可能不太习惯,但它的逻辑复用能力更强,比如购物车数量、用户登录态这些全局数据抽成store之后,组件之间传递数据会清爽很多。我建议你复现的时候重点看vite.config.js里的代理配置,这是前后端联调的关键,后面启动步骤会专门讲到。
2.3 版本组合的兼容性经验
选技术栈不能只看单个框架版本,还要看它们之间的兼容性。我使用的组合是:JDK8 + SpringBoot2.7.x + MyBatis-Plus3.5.x + MySQL8.0 + Vue3.2.x + Vite3.x + Node16。这个组合是目前比较成熟稳定的搭配。需要注意MySQL8.0的驱动包名是com.mysql.cj.jdbc.Driver,跟MySQL5.x的com.mysql.jdbc.Driver不一样,如果不改配置,启动时会直接报找不到驱动类。另外,MySQL8.0默认的认证插件是caching_sha2_password,有些版本的连接池驱动不支持,需要留意驱动版本不要太旧。
3. 数据库设计:支撑销售闭环的10多张核心表
3.1 从订单倒推表结构
设计电商系统数据库时,我习惯从“订单”这个终点倒推。用户下单之后,订单表里需要记录谁买的、收货地址、总金额、订单状态、下单时间;一个订单会包含多种商品,所以要有订单明细表,记录商品快照、数量、单价;购物车需要用户ID加商品ID的对应关系;商品需要挂在分类下,所以有分类表;商品还要管库存,所以有库存表。把这些关系列出来,数据库的核心表就出来了。
“衣依”系统里大概包含这些表:用户表(user)、管理员表(admin)、商品表(product)、分类表(category)、购物车表(cart)、订单表(orders)、订单明细表(order_item)、收货地址表(address)、库存表(stock)、轮播图表(banner)等。每张表之间有明确的外键关系,但在实际编码时,MyBatis-Plus项目通常不会在数据库层面强加物理外键,而是在业务代码里维护逻辑关系,这样在删除、更新数据时会更灵活,也符合互联网项目的常见做法。
3.2 核心表与字段设计示例
商品表是最值得细看的。它的字段不仅包含商品名称、描述、主图、价格这些基础信息,还会包含分类ID、销量、上架状态、创建时间。价格字段必须用decimal类型而不是float或者double。
订单表的状态字段是整个系统的状态机核心。我建议设计成int类型,用数字代表不同状态:0待付款、1待发货(已付款)、2已发货、3已完成、4已取消。在代码里可以用常量类统一定义,避免魔法值散落在业务代码中。
3.3 金额、库存、状态管理的设计细节
数据库设计里最容易出问题的三个地方是金额、库存和状态。金额用的是decimal(10,2),保证精度不丢失;库存字段用int并加上>=0的约束;状态字段则是用int加上注释说明每个数字的含义。对于前后端交互的JSON,金额要做成字符串或者元为单位,避免JavaScript浮点数精度问题。这些细节在写接口时最容易忽略,但却是系统能不能在生产环境稳定使用的关键。
4. 后端业务实现:从登录鉴权到下单扣库存的完整链路
4.1 统一返回与全局异常处理
复现项目后我首先看的是后端代码的结构。“衣依”系统后端采用传统的分层架构:Controller → Service → Mapper,Controller负责接收参数和返回结果,Service负责业务逻辑,Mapper负责数据库操作。为了让前端拿到统一格式的数据,系统定义了一个Result类,包含code、message、data三个字段。成功时code为200,失败时code为500或者其他业务码。前端只需要判断code的值即可决定如何处理响应,不需要去解析各种不同的异常抛出格式。
全局异常处理用@RestControllerAdvice配合@ExceptionHandler实现,业务异常(例如库存不足、未登录)抛出BizException,由全局处理器捕获后返回给前端提示;系统异常则记录日志,返回通用错误信息。这样做的好处是Controller层代码会很干净,不需要到处写try-catch。我建议你自己写项目时一定要养成这个习惯,否则每张表的CRUD接口都会塞满重复的异常处理代码。
4.2 JWT登录鉴权与拦截器
用户登录成功后,后端返回一个token,前端存到localStorage,后续每次请求在请求头里带上Authorization: Bearer 。后端通过SpringMVC拦截器拦截需要登录的接口,解析token并取出用户ID,放入请求上下文。“衣依”项目对游客和管理员做了区分:普通接口只要求登录即可访问,管理端接口则校验管理员身份。复现时一定要注意拦截器放行了哪些路径,比如登录接口、注册接口、商品列表接口需要放行,但购物车、提交订单、后台商品管理必须拦截。如果路径配置错了,就会出现登录后依然提示未登录,或者游客能直接访问后台接口的严重问题。
4.3 商品检索与购物车
商品列表接口支持按分类查询和按关键词模糊查询,利用MyBatis-Plus的LambdaQueryWrapper动态拼装查询条件。这里最有参考价值的是分页功能:MyBatis-Plus提供Page对象,配合分页插件,只需要传当前页码和每页条数,就可以自动计算出总记录数并返回分页结果。购物车模块的核心是“用户+商品”的唯一性:同一个商品被重复加入时,不新增记录,而是把数量累加。修改数量、勾选商品、删除商品、清空购物车这些操作都是围绕购物车表做CRUD,但要注意在返回给前端的数据里,把商品详情、库存、上下架状态一起关联返回,否则前端展示时会缺信息。
4.4 下单流程与库存防超卖
下单是后端最核心的代码,主要步骤包括:根据购物车勾选的商品ID列表查询商品信息,检查商品是否上架、库存是否充足,计算总金额,生成订单号,往订单表和订单明细表插入数据,扣减库存,最后清空购物车中已下单的商品。整个过程必须放在同一个事务里,任何一个环节失败都要回滚,否则会出现订单创建了但库存没扣,或者购物车少了一半商品的问题。在扣库存的SQL上,项目采用了条件更新的方式:UPDATE stock SET stock = stock - #{count} WHERE product_id = #{productId} AND stock >= #{count},这条SQL自带库存判断,能够有效防止并发情况下的超卖。这一行代码是整个下单流程的精髓,建议重点理解。
订单号不要用数据库自增ID,建议生成规则是:年月日时分秒+随机数(或用户ID),目的是让订单号不连续,既能保护业务隐私,也能避免别人通过订单号推测销售数据。
4.5 支付回调与订单状态推进
生产级电商系统会对接真正的支付渠道,而“衣依”作为课程设计项目,采用的是模拟支付的方案:用户点击“去支付”后弹出一个模拟支付页面,输入测试专用的支付密码或直接确认后,前端调用支付接口,后端将订单状态从待付款更新为待发货。这个设计虽然不如真实支付复杂,但保留了支付回调的核心逻辑——只有接口成功返回且订单状态合法流转,才会更新状态。我在复现时注意到一个细节,处理支付回调的接口必须设计成幂等逻辑,如果前端重复点击或网络重试导致回调被调用多次,订单状态不能从“待发货”又变回“待付款”,要加上状态判断,只处理当前状态等于待付款的订单。这段代码值得反复阅读,因为它将来对接真实支付系统时的思路是一样的。
5. 前端Vue3落地:页面组织、状态管理与接口联调
5.1 工程结构与路由拆分
前端部分需要先理解工程目录结构。“衣依”项目把前台用户端和后台管理端放在一起,通过路由划分模块。用户端页面放在views/user下,包括首页、分类页、商品列表页、商品详情页、购物车页、订单确认页、订单列表页、登录注册页;管理端页面放在views/admin下,包括数据概览、商品管理、分类管理、订单管理、用户管理。路由配置里使用了路由懒加载,访问到某个页面时才加载对应组件,减少首屏体积。Vite的别名配置也值得注意,@符号指向src目录,导入组件时不用写长长的相对路径。
5.2 状态管理与登录态
前端使用Pinia管理全局状态,其中用户信息、购物车数量是两个最重要的store。用户登录成功后,将用户信息和token写入store并持久化到localStorage,刷新页面时再拉回来。路由守卫是这个环节的关键:如果访问购物车或订单相关页面而本地没有token,则跳转到登录页;如果访问管理端页面而当前用户不是管理员,则跳回首页。这种前端路由守卫配合后端接口拦截的写法,双保险,能很好地避免用户通过改URL越权访问。
5.3 核心页面逻辑
商品列表页要考虑加载状态和空数据状态,避免用户看到白屏;搜索功能需要处理防抖,至少300毫秒后再请求后台,避免每次输入都触发查询。商品详情页的关键是“库存不足,加入购物车按钮置灰”,这时候后端返回的库存字段起着决定性作用。购物车页面则是把store里的数据映射到界面,每一行展示商品的图片、名称、单价、数量、小计,支持勾选与全选,并实时计算合计金额。订单确认页是数据交汇处:展示收货地址、商品清单、金额明细,提交订单成功后清空已购购物车商品,再跳转到订单列表页查看状态。这些页面本身并不复杂,但前后端字段对齐很容易出问题,比如后端返回单价是分还是元,商品图片存的是完整路径还是相对路径。
5.4 与后端联调的常见问题
前后端联调时最常见的问题是跨域和路径错误。项目解决跨域采用两种方式:后端配置CorsFilter,允许所有来源的跨域请求;前端在vite.config.js里配置proxy代理,把/api路径转发到后端服务端口。使用代理的好处是浏览器看到的请求始终在同一个域名下,不触发跨域拦截,开发环境下更稳妥。如果复现时前端请求不到数据,我强烈建议先打开浏览器开发者工具看Network面板,检查请求是否到了后端、状态码是多少、响应体是什么,不要一开始就在代码里找bug。通常问题出在:代理路径没写对、请求头没带token、后端端口写错、返回格式和前端封装不一致。
6. 从零跑通项目的完整步骤与踩坑记录
6.1 环境准备
需要准备的工具如下:JDK 8,确保Java环境变量配置正确;Maven 3.6以上,用来管理后端依赖;MySQL 8.0,创建数据库并执行项目提供的SQL脚本;Node.js 16以上,用于前端依赖安装和项目启动;另外建议准备一个趁手的IDE,后端用IDEA,前端用VS Code。环境准备阶段最容易翻车的地方是JDK版本和MySQL版本不一致,比如本机装的是JDK17却运行SpringBoot2,某些旧版本依赖会报模块化错误。
6.2 启动顺序与关键配置
严格按顺序执行可减少很多无谓的错误。第一步是导入后端项目并等待Maven下载依赖,修改application.yml里的数据库连接配置,把账号密码和localhost改成自己的本地配置(datasource连接串、MySQL账号、密码);第二步是初始化数据库,在Navicat或命令行里执行sql脚本,确认各个表生成成功后再启动后端,启动成功的标志是控制台输出启动耗时并监听8080端口;第三步是前端项目安装依赖,npm install可能耗时较长,可以换用淘宝镜像源加速,修改vite.config.js里的target指向后端实际端口(通常是http://localhost:8080);第四步启动前端,npm run dev,通过浏览器访问地址,完成一个完整的注册、登录、下单流程就算跑通了。这里需要特别强调,先启动后端再启动前端,否则前端连不上接口会出现一堆网络错误提示。
6.3 我踩过的几个坑
第一个坑是MySQL8.0时区问题。报错会出现“The server time zone value”字样,在数据库连接URL后面加上serverTimezone=Asia/Shanghai即可解决。
第二个坑是MyBatis-Plus分页不生效。项目里配置了分页插件后,如果查询结果没有按预期分页,多半是配置文件里的Mapper扫描路径写错,或者分页插件没有注入到容器中。
第三个坑是前端路由刷新后404。开发环境下使用Vite的history模式,刷新某个子页面时如果后台没有配置fallback到index.html,就会出现404。解决方法是在vite.config.js里配置server.historyApiFallback = true,生产环境则由Nginx配置try_files来解决。
第四个坑是跨域导致登录后拿不到用户信息。登录接口通了,但后续请求携带token后依然被拦截。这个问题通常不只是跨域,还需要在后端拦截器里添加CORS响应头,或者统一使用代理方式解决。
7. 部署与二次开发建议
7.1 部署要点
项目可以打成Jar包部署到云服务器,前端构建后生成dist目录,交给Nginx托管。部署时要注意三点:后端Jar包启动时指定数据库连接信息,比如java -jar clothing-platform.jar --spring.datasource.password=xxx,避免配置文件里硬编码密码被泄露;前端构建时通过环境变量指定API请求地址,如果是Nginx代理则配置 /api → 后端服务地址;文件上传目录要单独映射,商品图片如果上传到本地磁盘,重启服务器后需要保证目录路径不变。
7.2 二次开发方向
在此基础上扩展性能会得到很大提升。一是加入Redis缓存:把商品列表、首页轮播、分类信息等热点数据缓存起来,降低MySQL压力;二是加入搜索增强:引入Elasticsearch做商品检索之前,可以先优化MySQL的LIKE查询或建立索引;三是接入真实支付:把模拟支付接口替换为第三方支付沙箱环境,核心逻辑依然是校验回调签名、幂等状态更新,只是多一个网关对接;四是增加秒杀场景:把下单、扣库存的逻辑独立出来,结合消息队列削峰,这一块已经属于进阶内容了。
最后再补充一句,复现这类项目最有价值的方式不是把代码跑通就结束,而是自己去改一个需求。比如给商品加一个品牌字段,从前端表单到数据库再到列表接口完整走一遍,你会发现整个系统在你脑子里会迅速串成一张网,下次做类似项目时效率会有质的提升。