资讯详情

宠物商城全栈项目实战:SpringBoot+Vue3+MySQL从设计到部署

📅 2026/10/6 12:55:08 | 华诺云谱 👁 阅读
宠物商城全栈项目实战:SpringBoot+Vue3+MySQL从设计到部署
我自己前后带了几年应届生也评审过不少毕业设计和技术分享一个很深的感受是很多人学过SpringBoot、Vue、MySQL但真正把这三条线串起来做一个完整的全栈项目时还是会卡在工程落地这一层。比如数据库表怎么设计才不返工商品库存和订单扣减怎么保证一致性Vue3的组合式API到底怎么组织代码才不乱——这些都不是靠背八股能解决的。所以看到这个宠物商城项目我反而觉得它踩的点特别准业务规模不大不小正好能把主流技术栈完整走一遍又不会像电商巨头那样复杂到劝退初学者。这个项目的技术选型也很贴近当前企业的主流配置SpringBoot2 Vue3 MyBatis-Plus MySQL8.0。没有盲目追新上SpringBoot3和JDK17也没有用那些已经半退休的老框架属于那种学完就能在工作里用的组合。另外它带了文档这意味着你可以把它当成一个标准模板不只是跑起来看效果而是真正读代码、看设计、理解每个模块为什么这么写。这篇文章我不打算写成一笔一划的源码逐行解读而是从项目整体设计的角度把宠物商城这套系统拆开来讲技术栈为什么这么选、数据库怎么建模、核心业务商品、购物车、订单、登录鉴权在前后端分离下怎么落地、开发过程中最常见的坑和排查方法以及怎么把它顺利跑起来甚至部署上线。无论你是拿它当毕业设计参考还是想作为全栈入门练手项目又或是要快速搭一个电商类系统的原型这篇文章的内容都能让你少走不少弯路。1. 技术栈选型为什么是这套组合而不是别的先聊技术选型。很多初学者拿到一个项目会习惯性地问用了什么技术但很少有人问为什么是这几个技术组合在一起。这一步想明白了后面编码才不会只是抄代码。1.1 SpringBoot2、Vue3、MyBatis-Plus、MySQL8.0各自的定位先看后端。SpringBoot2虽然在2022年底之后进入维护期但目前企业存量项目和技术资料里它仍然是占比最大的版本尤其是SpringBoot 2.7.x这一代既有自动配置的便利又兼容了Spring Cloud老版本的各种生态。对于学习者和做毕设的同学来说SpringBoot2意味着资料多、出问题容易搜到答案、各种starter组件选型成熟踩坑成本低。选它做后端稳是第一位的。Vue3这边则是另一番逻辑。Vue3 Vite 组合式APIComposition API已经不是新东西但直到现在仍然有很多教程在上老式的选项式APIOptions API。这个项目直接用Vue3的组合式API来写个人认为是更有诚意的选择一方面组合式API的逻辑复用能力确实比选项式强太多同一个购物车的逻辑可以抽成useCart()这样的函数到处复用另一方面script setup语法糖极大减少了样板代码配合Vite的按需编译开发体验比Vue2那套流畅得多。现在很多后台管理系统比如若依前后端分离版也都在往Vue3TS迁移提前熟悉这套写法是必要的。MyBatis-Plus不是一个新框架但它在中小型项目里的地位很稳。它本质上是对MyBatis的一次增强内置通用Mapper单表的增删改查几乎不用写XML分页插件直接配一个拦截器就行还有代码生成器可以一键生成entity/mapper/service/controller四层代码。宠物商城这种业务用户表、商品表、订单表、购物车表相当符合它的优势场景几张表的CRUD如果每张都手写XML既浪费时间也看不出水平。记住MyBatis-Plus解决的是常规CRUD效率问题而不是复杂SQL性能问题遇到多表关联和复杂聚合查询时该手写SQL还是得写。MySQL8.0则是数据库侧的默认答案。8.0相比5.7有几个非常实用的提升默认字符集utf8mb4emoji和一些生僻字能直接存支持窗口函数ROW_NUMBER、RANK这类写排名、分组TopN方便得多新增的WITH公共表表达式也能让复杂查询更清晰。更关键的是8.0的安装部署和连接配置在Docker、云数据库、Linux服务器上都已经非常成熟网上随便一搜都是案例对新手友好。1.2 这套组合能解决什么问题又有什么潜在代价这套组合对应的是一个典型的中小型Web业务系统的黄金配置。你需要快速开发一个带用户登录、商品展示、购物车、订单流程的系统同时要求代码结构清晰、后续可维护那么这个组合能让你把主要精力放在业务逻辑而非底层配置上。SpringBoot负责把对象管理、事务、Web配置全部干掉MyBatis-Plus干掉一半SQLVue3Element Plus干掉前端组件和页面交互你真正要写的核心代码是订单状态流转、库存扣减、购物车选中逻辑这些偏业务的东西。当然有得必有失。这套组合在某些场景下也有代价提前知道可以帮你扬长避短MyBatis-Plus对复杂查询不友好多表嵌套关联、动态排序、复杂子查询写XML仍然不可避免强行用Wrapper拼会非常痛苦。宠物商城的后台订单筛选如果有这种需求建议直接写自定义SQL。SpringBoot2毕竟是旧版本如果你有学习SpringBoot3虚拟线程、GraalVM原生镜像的需求或者想熟悉新特性那这个项目就当基础盘来用别死守版本。前后端分离带来了跨域和联调成本前端和后端是两个工程开发时要配代理部署时要用Nginx转发比JSP时代多了一层复杂度。这些代价在宠物商城项目里都是可控的因为业务规模不大恰好能体验带病处理的过程反而比用几千行的复杂项目练手更容易建立完整认知。1.3 通用CRUD服务与代码生成器的使用经验既然标题里特别出现了MyBatis-Plus相关热词这里多说几句通用CRUD这一块。MyBatis-Plus提供了一套BaseMapper和IService你自定义的Mapper接口只要继承BaseMapperT就自动拥有了selectById、insert、updateById、deleteById这些方法。但这套机制有个新手容易用偏的地方过度依赖通用方法会让所有查询都变成先拿出来再内存过滤一旦数据量上来就完蛋。举个例子。宠物商城的商品列表页经常要按分类、价格区间、上下架状态筛选还要分页。如果你用list()把所有商品查出来再在Java里过滤那商品几千条时页面就开始卡了。正确的做法是用MyBatis-Plus的LambdaQueryWrapper构造带WHERE条件的查询让它生成SQL去数据库过滤再用分页插件来处理LIMIT。我见过太多人把Wrapper只当拼条件的工具忽略了它本质上还是会在数据库端执行的——只要条件字段有索引性能不会有问题。代码生成器也值得提。MyBatis-Plus的AutoGenerator可以根据数据库表一键生成实体类、Mapper、Service、Controller。很多新手生成完就直接用结果Controller里全是薄薄的CRUD空壳Service里也没业务逻辑这种代码经不起推敲。我的建议是生成器只用来生成entity和mapper层Service和Controller最好手写因为业务方法比如下单这个动作涉及库存检查、订单创建、购物车清空三个步骤没有任何代码生成器能帮你生成。用生成器图快但业务方法必须自己写干净。2. 宠物商城的整体设计业务模块、数据库建模与工程结构技术选型只是骨架真正决定项目质量的是设计。宠物商城看起来业务简单但麻雀虽小五脏俱全该有的电商模块它一个不少。这一节重点讲整体设计思路这是任何文档里都值得仔细研究的部分。2.1 业务模块梳理与核心流程识别拿到一个宠物商城的需求不要急着写代码先把业务模块画出来。通常包含这样几块用户端注册登录、个人中心、宠物商品浏览、商品详情、购物车管理、提交订单、订单列表、订单详情、可选支付成功后回调。管理端商品管理上下架、改价、改库存、分类管理、订单管理发货、取消、退款、用户管理、加分项轮播图或公告管理。这套业务的核心流程其实只有三条浏览选品流程、购物车结算流程、订单状态流转流程。把这三条流程吃透整个系统的数据库表结构和接口设计就清晰了。以购物车结算流程为例用户端大致是加入购物车 - 选中商品 - 提交订单 - 服务器校验库存和价格 - 生成订单订单状态变为待支付 - 支付成功状态变已付款 - 管理员发货已发货 - 用户确认收货已完成。这个过程涉及多张表的协同更新也是后面讲事务处理的重点。用户浏览商品 → 加入购物车 → 购物车列表勾选 → 提交订单 → 后端校验库存 锁定库存 创建订单 清空购物车 → 支付模拟→ 订单状态更新 → 管理员发货 → 用户确认收货这张流程图画完数据库表怎么设计、Controller需要哪些接口、Service需要哪些事务方法基本就自然推导出来了。流程驱动设计而不是表驱动设计这是做项目规划时最重要、也最容易被忽略的点。2.2 数据库表结构设计实战要点基于上面的流程宠物商城最少需要这些表表名核心字段设计要点user用户表id, username, password, nickname, phone, avatar, create_time密码存的是BCrypt加密后的密文绝不能明文category分类表id, name, parent_id, sort_order用parent_id支持两级分类宠物/用品/食品等product商品表id, category_id, name, subtitle, main_image, detail, price, stock, statusstatus上下架状态price用decimal(10,2)图片存URL路径cart购物车表id, user_id, product_id, quantity, checkeduser_idproduct_id建唯一索引防止重复记录order订单表id, order_no, user_id, total_amount, status, receiver_name, receiver_phone, receiver_address, create_timeorder_no全局唯一状态字段用tinyint存数字对应关系写在常量类里order_item订单明细表id, order_id, product_id, product_name, product_image, price, quantity快照字段很重要订单生成后商品改名改价不影响历史订单address收货地址表id, user_id, receiver_name, receiver_phone, address_detail, is_default一个用户可以多个地址仅默认地址唯一这里有两个设计细节值得展开。第一订单明细表必须做商品快照把下单那一刻的商品名、图片、价格原样复制进来。否则商家改了商品价格用户的历史订单显示也会跟着变这在电商里是绝对不允许的。第二金额字段一律用decimal禁止用float和double。二进制浮点数无法精确表示0.1这种十进制小数订单金额累计、结算时一分钱差异就很麻烦。MySQL8.0的decimal在计算上完全够用别贪那点存储空间。2.3 项目工程结构与后端分层规范工程结构决定代码可维护性宠物商城项目的后端建议按功能模块分包而不是按层分包。什么意思按层分包是controller包下放所有Controller、service包下放所有Service而按功能分包则是module/user下面放UserController、UserService、UserServiceImpl、UserMapper、User实体类。按功能分包的好处在于改一个功能时所有相关文件都在同一个包里不用来回跳目录。这种结构在SpringBoot项目里非常流行尤其是业务模块多时用户、商品、订单、购物车各占一个包。后端分层的大致模式是controller —— 只做参数接收、调用service、返回结果不写业务逻辑 service —— 业务逻辑层事务都在这层处理 mapper —— 数据访问层写SQL和MyBatis-Plus接口 entity —— 实体类对应数据库表结构字段和表字段一一对应 dto/vo —— 数据传输层controller层输入输出用不直接暴露entity前端Vue3工程的结构同样有讲究一般按views页面、components通用组件、api接口请求封装、utils工具函数、router路由配置来划分。api目录下建议一个模块一个文件比如api/user.js里只放登录注册相关的请求方法这样页面里调用时一目了然后端改接口也只要找到对应文件改一处。前后端分离项目的良心一半体现在接口文档另一半体现在前端API目录的组织方式上。2.4 为什么这个项目的文档值得重视标题里特意标了【含文档】这一点不要忽视。很多开源项目源码开放但文档要么缺失、要么只写如何启动一句带过。这个项目既然带了文档大概会包含环境搭建、数据库初始化脚本、接口说明、可能的部署指南。我个人的建议是拿到项目后先花一晚上精读文档启动一遍项目把文档描述的和实际代码对一遍。这一步看似浪费时间实际上是把整个项目从黑盒变成白盒最快的方法做完之后再读代码效率完全不一样。一份好的项目文档至少应该回答JDK、Maven、Node.js版本要求是什么如何创建数据库并导入初始化脚本后端如何配置数据源和Redis如果有前端如何安装依赖和配置代理默认账号和密码是什么各模块的接口大致是干什么的。对照这些信息快速跑通项目你一上来就有了这个系统能做什么的整体感知后面看细节代码时才不会迷路。3. 核心功能实现细节从登录鉴权到订单流转这一节是全文的重头戏我把宠物商城里技术含量最高的几个模块逐一拆开讲。如果你正准备拿这个项目做二次开发或者面试讲解这些内容可以直接当素材讲。3.1 用户登录与JWT鉴权机制解析用户模块表面上只是注册和登录但背后是前后端分离下最核心的鉴权问题。传统的Session方式在前后端分离场景下不太方便跨域要处理Cookie、分布式环境要共享Session所以现在的主流方案是JWTJSON Web Token。JWT本质上是一段携带用户信息的加密字符串服务器签发后发给前端前端在后续请求的Header里带着它服务器验证签名通过就认为请求合法。登录接口的处理流程一般是前端提交用户名密码后端先按用户名查用户拿不到直接返回用户名不存在。拿到用户后用BCryptPasswordEncoder.matches()校验密码密码是注册时BCrypt加密的所以这里比对的是密文不是明文。校验通过后用JWT工具类生成token把用户id、用户名、过期时间塞进token里。返回给前端{ token, userInfo }前端把token存到localStorage或Pinia里。前端在axios请求拦截器里统一加上Authorization: Bearer token头。后端用一个拦截器/过滤器统一校验token校验通过就把用户信息放进ThreadLocal或RequestContext方便后续业务代码直接取当前用户。宠物商城这类系统的鉴权还有一个加分项用户角色区分。普通用户和管理员在同一个用户表里可以用role字段区分。登录成功后前端根据角色决定显示用户端还是管理端页面后端则用Spring Security或拦截器保护管理端接口。用SpringBoot2自带的Spring Security做起来会有一点配置学习成本但胜在专业且不用自己造轮子。提示JWT有个常见问题是无法主动让token失效因为token本身是无状态的。如果项目有修改密码后强制下线这类需求简单做法是把token版本号也写进JWT里改密码时让版本号1老token自然失效。这是我的实战经验许多入门项目不会处理这个细节面试时讲出来却是个亮点。3.2 商品与分类模块图片存储和列表分页的两种思路商品模块是商城门面它的核心难点不在CRUD而在商品图片的存储方案和列表分页查询的性能。图片存储有几种常见选择本地磁盘存储、云存储OSS、Base64直接存库。宠物商城项目一般用前两者。本地磁盘存储的做法是后端配置一个可访问的静态资源映射路径比如/images/**映射到服务器磁盘某个目录上传的图片保存到该目录数据库里存的是相对路径或完整URL。如果是云存储OSS比如阿里云OSS或腾讯云COS则是前端直传或后端转传数据库存URL这种方式生产环境更常用因为无需担心磁盘扩容和图片访问并发问题。分页查询则是另一个重点。用MyBatis-Plus时只需配置一个分页插件拦截器然后Service层调用page(new Page(current, size), wrapper)就能拿到分页结果。但注意以下几个容易出问题的地方分页参数校验页码不能小于1每页大小别设置成能一次查1万条前端传递的current和size要先用Math.max做兜底。条件构造别把筛选字段拼错商品列表常见筛选条件有category_id、status、价格区间、关键词模糊搜索用LambdaQueryWrapper的eq和like即可但关键词搜索字段需要确认是匹配name还是subtitle别两张表字段混着写。分页结果处理查出来的实体类可能带了不必要的字段比如商品详情detail很长如果要返回给前端列表页建议用VO对象只保留列表页需要的字段去掉大文本字段能明显减少网络传输。商品模块还有一个隐藏关卡是库存管理。商品表里的stock字段不是摆设商品详情页要显示剩余库存购物车结算时要检查库存够不够下单成功后要扣减库存。这些场景涉及并发控制我在下一节展开讲。3.3 购物车与订单模块的事务一致性处理购物车模块本身不复杂——无非是增删改查加一个选中/取消选中状态。真正考验功底的是从购物车创建订单这一步因为它涉及多张表的同步更新必须保证要么全部成功要么全部失败这就是数据库事务的应用场景。下单的核心代码逻辑大致是接收请求参数收货地址id、订单里的商品id列表从购物车选中的记录里拿。查询用户选中的购物车记录逐个商品检查商品是否存在、是否上架、库存是否足够。计算订单总金额这里必须用数据库里的商品价格而不是前端传过来的价格防止被篡改。扣减库存UPDATE product SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}。这行SQL是关键在数据库层面做条件更新可以避免并发超卖。插入订单主表记录生成唯一订单号。插入订单明细表多行记录保存商品快照。清空已购买的购物车记录。这7步必须放在同一个Transactional方法里。SpringBoot的事务管理很容易用但也经常有人踩坑常见的有三种事务不生效方法被private修饰、同类内部调用、类未被Spring管理这些都会让Transactional静默失效。我的经验是先确认调用入口是Controller - Service公有方法且代理生效。事务回滚不彻底默认只有RuntimeException才触发回滚如果你在业务里抛了自己定义的Exception子类非运行时异常记得在注解里指定rollbackFor Exception.class。长事务问题事务范围越大、耗时越长数据库连接占得越久。下单这个操作里不要做远程调用比如发短信、调支付接口尽量只做本地数据库操作日志和通知放到事务提交后再做。库存扣减的UPDATE ... WHERE stock #{quantity}写法是个特别值得说的细节。很多初学者写的时候会先查库存然后在Java里判断再执行更新。这种先查后改的方式在高并发下会出问题——两个请求同时查到了库存8都判断够扣然后都去扣减结果库存变成了负数或扣超了。把判断条件放进WHERE里让数据库的锁和原子性来保证一致性才是正解。如果是秒杀级的高并发场景还可以用乐观锁版本号或Redis预扣库存异步落库的方式但宠物商城这种规模用条件更新足够。3.4 接口设计与统一返回结果封装做前后端分离项目统一接口返回格式是工程规范里的第一条。如果没有统一格式有的接口返回{success: true, data: ...}有的返回{code: 200, data: ...}前端写axios响应拦截器时就会疯掉。宠物商城这类项目通常约定一个通用返回体{ code: 200, message: 操作成功, data: { } }code200表示成功非200表示业务失败比如401未登录、403无权限、500服务器异常、1001库存不足等。前端在axios响应拦截器里统一判断code非200则弹出ElMessage提示。后端实现上通常是写一个Result类泛型类Controller的方法统一返回ResultTService层抛业务异常时由全局异常处理器RestControllerAdvice统一捕获并转成对应code的返回体。这样业务代码里就不需要到处写try-catch了。控制器层接口的URL设计也有套路。商品模块是公有的可以匿名访问但购物车和订单模块都需要登录所以接口上要分清楚。常见的URL风格是RESTful的GET /user/info 获取当前登录用户信息 POST /user/login 登录 POST /user/register 注册 GET /product/list?categoryId 商品列表 GET /product/{id} 商品详情 POST /cart/add 加入购物车 GET /cart/list 购物车列表 POST /order/create 创建订单 GET /order/list?status 订单列表按状态筛选 GET /order/detail/{orderNo} 订单详情加上适当的中文注释或SwaggerKnife4j注解接口文档就有了。我在看项目源码时最关注的就是接口是否统一返回ResultT是否对参数做基本校验异常是否被全局捕获。这三个点决定了代码是不是可直接用于生产的干净工程而不是只能本地跑的玩具代码。3.5 Vue3前端页面与状态管理的组织方式前端的价值在于把后端接口变成用户能实际操作的界面。宠物商城的前端整体规模不大但涉及页面不少首页、商品列表、商品详情、购物车、订单确认、订单列表、个人中心、管理后台。用Vue3来组织这些页面有几个关键技术点值得关注。路由设计前端路由按模块划分通常有layout布局组件顶部导航侧边栏内容区然后子路由挂在不同页面组件上。管理端和用户端可以用不同的布局也可以共用一个布局但根据角色动态生成菜单。路由守卫router.beforeEach里检查本地有没有token没有就跳转登录页。状态管理这个项目的状态管理工具大概率是PiniaVue3的官方推荐。购物车数据、登录用户信息、订单状态这些全局共享的数据建议放在Pinia里。特别是购物车如果放在组件内部用ref存切换路由后数据会丢失每次进入页面都要重新请求后端接口体验很差。合理做法是登录后请求一次购物车数据放入Pinia增删改时同步更新Pinia和后端这样页面跳转不会反复加载。组合式API的组织以商品详情页为例可以把获取商品详情、加入购物车、收藏这几个操作抽成useProductDetail()组合函数里面返回响应式数据和方法。这种抽象既让页面组件保持简洁也能在多处复用。很多人写Vue3还是把所有逻辑堆在setup里一眼看过去几百行全是变量和方法定义这其实是把Vue2的methods写法平移过来了没有真正理解组合式API的按功能组织哲学。UI组件库Vue3项目里最常用的还是Element Plus它的表单、表格、弹窗、消息提示组件很成熟。宠物商城后台管理页面基本就是Element Plus的常规用法el-table展示商品列表、el-form做新增编辑表单、el-dialog做弹窗、el-pagination做分页、el-upload做图片上传。这些组件的使用门槛很低但实际开发中“表单校验规则”和“表格分页回显”仍是报错高发区后面排查章节一并讲。4. 开发实战从初始化到跑通全流程的操作记录前面讲了很多设计层面的东西这一节回到动手环节把从零到一跑通这个项目的完整过程梳理一遍每个阶段该注意什么我都标出来。4.1 环境准备与项目初始化检查清单拿到带源码和文档的项目第一步不是急着启动而是按顺序检查环境避免装到一半发现版本对不上。环境项推荐配置踩坑提醒JDKJDK 8 或 JDK 11SpringBoot2一般用JDK8足够JDK17也能跑但个别旧依赖可能报警Maven3.6.x以上下载依赖慢的配置阿里云镜像Node.js16.x或18.xVue3Vite需要Node14但18.x最稳MySQL8.0.x注意数据库连接URL要显式加useSSLfalseserverTimezoneAsia/ShanghaiIDEIDEA后端 VS Code前端后端用IDEA社区版够用前端VS Code装Volar插件数据库初始化这一步尤其容易出问题。项目文档里一般会带sql/xxx.sql文件用Navicat或命令行导入前先确认连接的字符集是utf8mb4。MySQL8.0默认字符集已经是utf8mb4了但如果你的库创建得早可能还是latin1或者utf8mb3导入中文数据会乱码。命令行的保险操作是mysql -u root -p --default-character-setutf8mb4 source /path/to/pet_shop.sql;导入完成后用SHOW TABLES;确认表都建出来了再随便SELECT * FROM user;看一眼中文显示是否正常。这块没问题才继续往后端工程里配置数据源。4.2 后端工程启动与常见配置项解读后端工程一般是标准Maven结构src/main/java和src/main/resources。启动前必改的配置文件是application.yml或application.properties重点看这几项server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/pet_shop?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4 username: root password: 你的密码 redis: # 如果项目用了Redis这里的host和port也要核对 mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0配置里最容易忽略的是serverTimezone。MySQL8.0驱动的时区校验很严格不配或配错可能直接报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。看到这种乱码报错别慌就是时区问题把URL改成serverTimezoneAsia/Shanghai即可。另外注意MyBatis-Plus的logic-delete-field配置。如果项目里用了逻辑删除即删除不是真实DELETE而是把deleted字段置1那所有Mapper方法都会被自动拼接WHERE deleted0条件。这个特性很方便但如果你在XML里手写了SQL一定要记得在条件里也加deleted0否则会出现逻辑删掉的记录被手写SQL查出来的诡异bug。启动后端时控制台会打印SpringBoot的启动banner和端口信息。建议启动后先curl http://localhost:8080/product/list或项目里定义的公开接口确认接口能通再去看前端。4.3 前端工程启动与代理配置前端工程启动相对简单进入前端目录后npm install npm run devnpm install时有两个高频问题一是网络原因导致安装超时可以切换npm config set registry https://registry.npmmirror.com使用镜像源二是依赖版本冲突比如Vite和Node版本不匹配解决方法是删除node_modules和package-lock.json后重新安装。前端开发服务器默认跑在localhost:5173Vite或localhost:8081而后端在8080两者端口不同必然遇到跨域。Vite解决跨域的官方推荐方式是配置server.proxy代理在vite.config.js里写import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })这段配置的含义是前端页面里所有以/api开头的请求都会被Vite开发服务器转发到后端8080端口。于是前端代码里请求URL写成/api/product/list就能访问到http://localhost:8080/product/list。这是开发环境的标准玩法不要在后端代码里写一个允许所有来源跨域的配置来解决开发问题那到生产环境会变成安全隐患。4.4 Docker部署MySQL8.0与Linux环境注意事项本地开发用Navicat连MySQL非常方便但如果你在Windows上装原生MySQL遇到各种权限和字符集问题这太常见了我推荐改用Docker。Docker装MySQL8.0极其简单几步就能起来一个干净环境docker pull mysql:8.0 docker run -d \ --name pet-mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD你的密码 \ -e TZAsia/Shanghai \ -v /mydata/mysql/data:/var/lib/mysql \ mysql:8.0启动后用docker exec -it pet-mysql bash进入容器再用mysql -uroot -p登录执行导入SQL脚本。这个方案的优点是环境隔离、卸载干净、不会污染宿主机适合装过MySQL但搞得一团糟的情况。生产环境或Linux服务器上反过来操作即可——优先在宿主机直接用apt/yum装MySQL8.0或同样用Docker Compose管理后者更容易迁移和备份。如果你真要部署到服务器上我补充几个部署经验后端打包mvn clean package -Dmaven.test.skiptrue生成JAR包用java -jar pet-shop-0.0.1-SNAPSHOT.jar运行或者用nohup/systemd当守护进程。前端打包npm run build生成dist目录里面是纯静态文件用Nginx托管。Nginx配置监听80端口location /指向dist目录location /api/反向代理到后端8080如果前后端不采用统一前缀风格就按实际路由分别配置。这样开发环境的代理逻辑就完美移植到了生产环境。5. 常见问题与排查技巧实录跑项目的过程中一定会遇到各式各样的报错和奇怪现象。这一节把我整理的排查经验按模块写出来基本覆盖全栈项目的各个角落你可以当作速查手册。5.1 数据库连接与编码类问题速查连接报错Access denied for user rootlocalhost多半是账号密码配错或MySQL8.0的认证插件问题。MySQL8.0默认的caching_sha2_password插件对老版本驱动不支持一旦代码里用的MySQL Connector/J版本过旧5.x就会报Unable to load authentication plugin。解决方法是升级驱动为mysql-connector-java8.x或者在MySQL里执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 密码;改成兼容认证方式。中文乱码前端提交中文到后端乱码或后端返回中文到前端乱码优先检查三处——数据库连接URL是否带characterEncodingutf8mb4前端页面是否设了UTF-8Vite项目默认就是数据库表字段的字符集是否为utf8mb4。三处全对乱码问题基本绝迹。启动时MySQL连接超时有些老项目会配spring.datasource.hikari.connection-timeout过短MySQL启动慢或网络波动就连不上可以把超时调大或者确认MySQL服务确实起来了。用docker ps看容器状态用ss -lnt看3306是否监听都是有效排查手段。5.2 MyBatis-Plus使用中的几个高频翻车点Wrapper条件失效写LambdaQueryWrapper链式条件时eq、like的字段名没有用Lambda表达式而是用字符串QueryWrapper一旦数据库字段改名或实体字段和表字段映射错位就会报Unknown column。建议统一用lambdaQuery()或LambdaQueryWrapper的写法编译期就能发现字段是否存在。更新null值不生效MyBatis-Plus的updateById默认会忽略实体里的null字段即想置空某个字段时字段为null它不会生成对应的SET语句。如果你确实需要把字段更新为NULL要用UpdateWrapper的set(column, null)或者实体字段上加TableField(updateStrategy FieldStrategy.IGNORED)。这个坑在管理员把商品下架理由清空这种场景特别容易遇到。分页插件不生效配置了PaginationInnerInterceptor但page查询还是返回所有数据检查你的MyBatis-Plus版本和SpringBoot版本是否兼容。低版本MyBatis-Plus对SpringBoot2.7的支持有坑升级到mybatis-plus-boot-starter最新3.5.x版本基本能解决。逻辑删除和唯一索引冲突逻辑删除的deleted字段如果参与唯一索引比如user_id和product_id在购物车表建了唯一索引删除后再次插入相同记录会因唯一索引冲突而报错。解决方案是把deleted设计成0/1/删除时间戳——逻辑删除时写入当前时间戳而不是固定置1唯一索引自然不冲突。这也是很多实战项目惯用的办法。5.3 前端Vue3Element Plus的常见疑难数据不更新Vue3的响应式系统对reactive包裹的对象有限制直接通过索引替换数组元素或增减对象属性可能不会触发更新。这时候有两个方案一是用ref代替reactive来管理数组二是用Array.prototype.splice或直接给ref.value重新赋值。我见过很多新人纠结页面为什么不刷新其实就是响应式依赖收集的问题搞懂ref和reactive的边界场景能省不少调试时间。跨域问题如果前端请求报了Access-Control-Allow-Origin相关的错误先检查Vite的proxy配置是否正确再检查是不是用了fetch/axios直接请求绝对地址。记住开发环境优先用代理不要在后端代码里靠CrossOrigin(*)关闭所有跨域限制那等于把生产环境的安全防线提前拆掉。Element Plus按需引入带来的问题很多Vue3项目用unplugin-vue-components做组件的按需自动引入好处是打包体积小坏处是一旦某个组件的样式依赖了其他组件的全局样式比如el-select的下拉面板依赖el-popper可能样式丢失。遇到类似问题检查是否需要在main.js里额外引入element-plus/dist/index.css或手动引入相关组件。这一点网上资料不少留个心眼即可。el-table分页和请求参数不同步表格翻页后请求参数还是旧的条件这是因为el-pagination的current-change事件没有和查询条件合并。处理方式是始终用同一个queryParams对象存页号、页码和筛选条件翻页、筛选时都统一调getList()方法。听起来像是基础功但实际项目里这类条件参数和分页参数打架的问题是出现频率最高的。5.4 跨模块问题部署和后端日志排查技巧后端排查故障最依赖的就是日志。SpringBoot默认日志输出到控制台但部署到服务器后建议配置logback将日志写入文件比如logs/pet-shop.log。如果接口返回500先看日志定位是空指针、SQL异常还是业务异常如果是SQL语法错误日志里通常会打印出完整的SQL语句拿这条SQL去Navicat里执行验证比纯看代码效率高得多。接口排查的另一个思路是直接用curl或Postman测接口先绕开前端。这样做能快速判断问题出在后端还是前端。比如商品列表接口500了用Postman带同样的参数请求如果后端报错就是后端代码问题如果后端返回正常那就要去看前端页面的请求参数有没有传错。模块化排查是真正的全栈排错方法论。6. 项目的扩展空间与二次开发建议到这里整套宠物商城系统的核心内容基本讲完了。最后我想聊聊这个项目的天花板和扩展方向。很多人拿到一套源码跑通之后就开始写论文或做演示再往前就不动了。但实际上这个项目非常适合再接几个模块锻炼自己独立设计完整功能的能力。6.1 值得做的扩展方向从易到难如果我是你的话我会按这个顺序加功能收藏功能用户收藏商品新增favorite表user_id product_id加两个接口收藏/取消、列表页面加一个心形按钮。这个功能完全建立在已有代码模式上1天就能改完。商品搜索用MySQLLIKE实现关键词搜索针对商品名和副标题再加一个搜索页一般来说2天搞定。如果想更有挑战可以尝试接入全文索引或Elasticsearch不过宠物商城数据量下有点杀鸡用牛刀。订单自动取消用户下单后如果N分钟未支付自动取消订单并恢复库存。用SpringBoot的Scheduled定时任务扫一遍超时订单即可注意扫的时候要处理并发情况防止和用户手动支付同时操作。秒杀或限时折扣在商品表加discount_price和discount_start/end字段下单时判断是否在折扣期内价格用折扣价。这一步能练到条件判断的边界设计也是个不错的面试讲点。后台数据统计用MySQL8.0的聚合函数和窗口函数统计每日订单量、销售额Top10商品等。结合ECharts做一个简单的数据大屏也是展示能力的亮点。建议不要一上来就上重量级的分布式方案和消息队列那会喧宾夺主把宠物商城做成秒杀中间件演示项目反而失去了业务完成的连贯性。6.2 把项目变成简历和面试加分项的建议如果你打算把这个项目写进简历或用于面试讲解我有个诚恳的建议不要只把它描述成一个宠物商城系统而要说清楚你遇到的挑战和怎么解决的。面试官最感兴趣的不是CRUD的完成度而是几个经典问题的答法库存扣减为什么用UPDATE ... WHERE stock #{quantity}如何避免超卖把条件更新和乐观锁说清楚订单模块怎么保证多表数据一致性把事务、回滚、传播行为说清楚图片上传如何设计本地存储和云存储的取舍是什么前后端分离跨域问题怎么在开发和部署两个阶段分别解决JWT鉴权为什么比Session适合前后端分离它有什么缺点这些问题我在正文里都做了详细展开你把这个项目真正吃透后可以挑其中两三个讲出自己的实操体会远比背八股文有说服力。简历上也可以直接在项目描述里点出这些技术难点效果比罗列框架清单好得多。7. 写在最后的一些实在话我在实际帮人调试这类项目时最大的体会是全栈项目的难点不在于某个单独的框架而在于把框架之间的缝隙填平——后端接口怎么返回前端才方便处理、数据库表结构怎么设计才能少改两次需求、事务边界划到哪里才既安全又不拖垮性能。这套宠物商城项目恰好把这些问题都暴露了一遍我认为它适合沉下心来做而不是跑完就丢。如果你拿到了源码我建议你至少做三件事第一不看任何教程的情况下把数据库的ER图自己画一遍对照表结构和代码核对第二找到下单这个核心业务方法把它的调用链路从Controller到Mapper完整走读一遍看懂事务和库存扣减怎么配合第三自己动手改一个功能比如加一个宠物疫苗接种记录的新模块用已有代码模式去写新功能。做完这三件事这个项目对你的价值就能从跑通变成掌握。最后再分享一个小技巧在你把项目跑起来、准备做二次开发或写总结文档时给代码里的关键业务方法加中文注释哪怕只是两三句比如这里用条件更新防超卖、订单明细存快照防止商品信息变化影响历史订单。理由很简单——过两个星期你回头看自己写的代码最能帮助回忆的不是类名和方法名而是当时为什么要这么写。注释是写给未来的自己看的这个习惯越早养成收益越大。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑