拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SpringBoot+Vue茶叶商城系统全栈实战:架构设计与部署指南

刚拿到这份基于 SpringBoot Vue 的茶叶商城系统源码时我其实没抱太高期待因为市面上打着课程设计源码旗号的商城项目太多了要么结构稀烂要么文档写得像天书。但这套系统跑起来之后倒是让我改观了不少表结构设计得规整前后端功能对得上文档也把部署流程说清楚了。如果你正在找 Java 全栈实战项目、准备毕业设计答辩或者想把手上的项目经验写进简历里这套茶叶商城算是个相当不错的参考范本。我花了两天时间完整跑通了前后端又翻了一遍核心代码和数据库脚本。这篇就把这个项目从头到尾拆给你看包括架构是怎么设计的、数据库表为什么要这么建、前后端核心功能是怎么实现的、以及整个部署过程中我踩过哪些坑。尽量把为什么这么做和实际怎么做都讲透让你拿到的不仅仅是一份能跑通的源码而是真正能吃透的一个全栈项目。1. 项目整体设计与架构拆解1.1 为什么选 SpringBoot Vue 这套组合先说结论这套技术栈是目前国内 Java 后端入门和课程设计最主流、也最好交差的组合没有之一。后端用 SpringBoot核心价值在于约定大于配置。你不需要像 SSM 时代那样写一堆 XML 配置SpringBoot 通过自动配置把大部分基础工作都做好了。对于茶叶商城这种典型的管理系统加 C 端商城SpringBoot 能覆盖从数据持久化、接口暴露到文件上传的完整需求生态成熟出问题网上随便一搜就有方案。前端用 Vue核心价值在于组件化和响应式数据绑定。商城页面有大量重复的 UI 结构——商品卡片、购物车列表、订单状态标签这些用 Vue 组件拆开之后代码复用率极高。而且 Vue 的双向绑定让表单提交、购物车数量修改这类交互写起来非常顺不需要手动操作 DOM。更实际的一点是这套组合的学习成本曲线非常友好。SpringBoot 本身屏蔽了大量底层细节Vue 的入门门槛也比 React 低只要掌握了 JavaScript 基础、组件通信和 Vue Router 路由这几个核心概念跟着这套源码过一遍基本能自己改需求了。补充一个我个人的观点除非你的场景对性能有极端要求否则没必要纠结技术栈够不够新。对于商城这类 CRUD 密集型业务SpringBoot Vue 就是最稳妥的选择能让你把精力放在业务逻辑和数据设计上而不是跟底层框架较劲。1.2 前后端分离架构的核心思路这套系统采用的是标准的前后端分离架构。后端跑在 SpringBoot 内置的 Tomcat 上对外提供纯 JSON 接口前端是独立的 Vue 工程通过 Axios 调用后端接口拿到数据后渲染页面。这种架构的最大好处是关注点分离。后端只负责业务逻辑、数据校验、权限控制前端只负责页面展示和交互。两个人可以并行开发只需要提前把接口文档定义好。就算你是一个人做整套系统分离架构也能让你把调试过程拆开——后端接口用 Postman 测前端页面用浏览器 DevTools 查出了问题能快速定位是接口的问题还是页面渲染的问题。实际项目中前端和后端通常通过代理解决跨域问题。开发环境下前端跑在 8080 端口Vue 默认后端跑在 8081 或 9090 端口Vue CLI 的 devServer 配置了 proxy 把/api开头的请求转发到后端地址。这样浏览器看到的请求都是同一个源不会触发跨域拦截。1.3 功能模块划分茶叶商城的业务场景天然适合做前后端分离演示因为它同时包含了面向普通用户的 C 端功能和面向管理员的后台管理功能覆盖面足够广。这套系统的功能模块大致可以分成这么几块C 端用户模块用户注册登录手机号/用户名 密码JWT 签发 Token商品浏览分类筛选、关键字搜索、商品详情购物车管理加入购物车、修改数量、删除商品、批量结算订单流程提交订单、订单支付模拟、订单取消、确认收货个人中心收货地址管理、我的订单列表、个人信息修改后台管理模块管理员登录商品管理商品上下架、新增/编辑商品、库存调整、商品图片上传分类管理茶叶分类的增删改查订单管理订单列表、发货操作、订单状态修改用户管理查看注册用户列表、禁用/启用账号数据统计简单的销售数据统计首页看板从这套功能设计能看出它的目的不是为了做一个生产级商城而是尽可能多地覆盖经典业务场景把前后端交互的关键技术点都演示一遍。你在答辩的时候可以顺着这个思路讲每个模块分别用到了哪些技术点、解决了什么问题——这比单纯背概念要有说服力得多。2. 数据库设计与核心表结构解析2.1 数据库设计的基本原则拿到这个项目的数据库脚本通常是 SQL 文件第一件事不是急着执行而是先看表结构和表关系。说实话这套脚本的表设计是符合第三范式的没有冗余字段满天飞的情况关系也走得清楚。茶叶商城这种业务核心实体其实就那几类人用户/管理员、商品、订单、购物车、分类。绝大多数电商类课程设计的表结构都逃不出这个框架。设计数据库时有个核心原则先理清实体关系再定表结构最后考虑查询优化。实体关系上典型的对应关系是用户与订单一对多一个用户下多个订单订单与订单项一对多一个订单包含多个商品明细商品与分类多对一多个商品属于同一分类用户与购物车项一对多一个用户购物车里有多个商品条目这套系统在订单和订单项的处理上是规范的没有把商品信息直接塞进订单表里而是拆成订单主表和订单项明细表。这一点很重要因为商品的价格、名称随时可能调整订单一旦生成就必须锁定当时的价格快照。如果直接关联商品表以后商品改价了历史订单的价格就跟着乱了。2.2 核心表结构逐表拆解我大致翻了一下 SQL 脚本核心表大概有这么几张不同的源码版本可能表名有所差异但核心结构类似user用户表id主键自增username用户名password密码MD5 加密存储phone手机号email邮箱avatar头像地址status账号状态1 正常 / 0 禁用create_time注册时间category茶叶分类表id主键name分类名称比如绿茶、红茶、白茶、黑茶、乌龙茶sort_order排序号数字越小越靠前status是否显示product商品表id主键category_id所属分类ID外键关联分类表name商品名比如明前龙井 250g 礼盒装subtitle副标题/卖点描述main_image主图地址detail商品详细介绍富文本price销售价以分为单位或 DECIMALstock库存数量status商品状态1 上架 / 0 下架create_time/update_timecart_item购物车表id主键user_id用户IDproduct_id商品IDquantity数量checked是否选中用于批量结算create_time/update_timeorder订单主表id主键order_no订单编号通常用时间戳加随机数生成user_id下单用户IDtotal_amount订单总金额pay_amount实付金额如果有优惠逻辑status订单状态0 待付款 / 1 待发货 / 2 待收货 / 3 已完成 / 4 已取消receiver_name收货人姓名receiver_phone收货人电话receiver_address收货地址create_time/pay_time/deliver_time/finish_timeorder_item订单明细表id主键order_id订单主表IDproduct_id商品IDproduct_name商品名称快照product_image商品图片快照current_unit_price下单时单价快照quantity购买数量total_price该明细小计address收货地址表id主键user_id用户IDreceiver_name收货人receiver_phone手机号province/city/district省市区detail_address详细地址is_default是否默认地址这几张表你仔细看下来会发现一个规律状态字段贯穿了整个设计的核心。商品有上下架状态订单有流水线状态账号有启用禁用状态。理解了这个你就能明白为什么商城系统这么适合用来学后端——它天然就是一个状态机驱动的业务系统。2.3 为什么要把订单明细独立建表这一点我单独拎出来说因为很多初学者容易在这个地方犯迷糊。有人会问订单里已经有关联的商品ID了为什么还要把商品名称、价格、图片再存一份原因很简单商品信息是会变的。你今天卖 128 元的龙井明天搞活动改成 98 元如果订单表只是关联商品表的 ID那所有历史订单在展示的时候都会跟着显示 98 元这显然是有问题的。订单是交易凭证金额和商品信息必须在下单那一刻冻结下来。所以订单明细表里的product_name、current_unit_price、product_image都是冗余存储的快照数据。虽然违反了严格意义上的第三范式但这是为了业务正确性做的有意冗余。这种为了业务正确而牺牲部分范式的设计思路在真实业务里非常常见你可以在答辩的时候主动提这一点算是加分项。3. 后端核心功能与 SpringBoot 实现3.1 项目初始化与分层结构后端工程结构大致是这个样子的基于标准 Maven 项目布局src/main/java/com/example/tea ├── config // 配置类跨域、拦截器、静态资源映射 ├── controller // 控制层接收请求、返回结果 ├── service // 业务层核心逻辑 │ └── impl // 业务实现类 ├── mapper // 数据访问层MyBatis 的 Mapper 接口 ├── entity // 实体类对应数据库表 ├── dto // 数据传输对象接口入参/出参封装 ├── vo // 视图对象前端展示数据封装 ├── common // 通用类统一返回结果、异常处理、工具类 └── TeaApplication.java // SpringBoot 启动类这个分层结构是标准的 Controller-Service-Mapper 三层架构每层职责单一Controller 只做参数接收和结果封装业务逻辑沉淀在 Service 层数据访问由 Mapper 负责。如果你后期想改业务规则只需要动 Service 层不需要碰 Controller 和 Mapper代码维护起来省心很多。提示拿到源码后建议先找到启动类xxxApplication.java确认SpringBootApplication注解再看application.yml配置文件。配置文件里主要关注三块数据源配置、MyBatis 配置、自定义项目配置比如文件上传路径。3.2 统一返回结果与全局异常处理这套项目做了统一的结果封装Controller 的接口返回的不是裸数据而是一个标准结构的 JSON{ code: 200, message: 操作成功, data: { } }对应的后端代码通常是一个泛型类ResultTpublic class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }配合全局异常处理器RestControllerAdvice后端抛出的所有业务异常都会被拦截并转换成统一格式的 JSON 返回。这样做的好处是前端只需要在 Axios 响应拦截器里判断code字段就能统一处理成功和失败的逻辑不需要每个请求单独写错误分支。这里我强烈建议你改造一下把code用枚举定义好比如 200 成功、400 参数错误、401 未登录、403 无权限、500 服务器错误。这是生产级项目的通用做法也能让代码更规范。3.3 登录与鉴权JWT 方案系统登录这块采用的是 JWTJSON Web Token方案。用户提交用户名密码后后端校验通过生成一个带有用户信息的 Token 返回给前端。前端存在 LocalStorage 里之后每次请求在拦截器里加上Authorization头后端通过拦截器解析 Token 判断用户身份。核心流程// 登录接口逻辑 public ResultLoginVO login(LoginDTO dto) { // 1. 根据用户名查询用户 User user userMapper.findByUsername(dto.getUsername()); // 2. 校验密码MD5 加密比对 if (user null || !MD5Util.md5(dto.getPassword()).equals(user.getPassword())) { return Result.error(用户名或密码错误); } // 3. 校验账号状态 if (user.getStatus() 0) { return Result.error(账号已被禁用); } // 4. 生成 JWT Token String token JwtUtil.createToken(user.getId(), user.getUsername()); LoginVO vo new LoginVO(); vo.setToken(token); vo.setUserInfo(user); return Result.success(vo); }后端一般会有一个拦截器专门校验 Token用户未登录时访问需要鉴权的接口会返回 401 并提示请先登录。关于密码加密这套项目多半用的是 MD5。我个人的建议是如果这个项目你要用于实战或者面试展示最好把密码加密换成 BCrypt。MD5 在 2010 年之后就已经被认为不安全了撞库攻击非常容易破解。BCrypt 内置盐值每次加密结果都不一样安全性完全不在一个量级。改动成本也不高引入spring-security-crypto依赖把加密工具类换掉就行。3.4 商品管理实现的核心逻辑商品模块是商城的基础模块后端主要提供这些接口GET /api/product/list?categoryIdkeywordpageNumpageSizesort分页查询商品列表GET /api/product/detail/{id}商品详情POST /api/admin/product/save新增商品POST /api/admin/product/update修改商品POST /api/admin/product/delete/{id}删除商品或上下架商品列表的分页功能是常见的面试考察点。这套系统用的多半是 MyBatis 的分页插件 PageHelper用起来非常简单public PageInfoProductVO getProductList(ProductQueryDTO dto) { PageHelper.startPage(dto.getPageNum(), dto.getPageSize()); ListProductVO list productMapper.selectProductList(dto); return new PageInfo(list); }PageHelper.startPage()之后紧跟的第一条查询语句就会被自动拦截加上LIMIT分页语句非常方便。但要注意一个坑startPage之后必须紧接着执行查询如果中间穿插了别的数据库操作分页会失效或者作用到错误的查询上。商品图片上传这块SpringBoot 的处理方案是接收MultipartFile保存到本地指定目录然后把这个文件的可访问 URL 存到数据库。比如public String uploadImage(MultipartFile file) { // 1. 生成唯一文件名时间戳随机数原文件名后缀 String filename System.currentTimeMillis() _ file.getOriginalFilename(); // 2. 保存到本地目录 /upload/ String filePath uploadDir filename; file.transferTo(new File(filePath)); // 3. 返回可访问的 URL 路径 return /upload/ filename; }同时需要配置一个静态资源映射类让/upload/**路径映射到本地文件目录否则浏览器访问不到图片。3.5 购物车与订单流程的闭环设计购物车模块是商城业务逻辑里最值得看的部分它是用户从浏览到购买的关键衔接点。后端接口大致有POST /api/cart/add加入购物车GET /api/cart/list查询我的购物车PUT /api/cart/update修改某条购物车记录数量DELETE /api/cart/delete/{id}删除某条购物车记录加入购物车的核心逻辑是如果当前用户购物车里已经存在同一商品就累加数量如果不存在才新增一条记录。这个逻辑看似简单但漏掉的人不少写的时候尤其注意要按user_id和product_id联合查询。下单流程是整个系统的重头戏大概分这几步前端从购物车中勾选需要结算的商品checked字段标记提交订单时前端发送选中的购物车记录 ID 列表和收货地址 ID后端校验库存是否充足创建订单主表记录状态为待付款创建订单明细记录把商品快照信息写入扣减库存删除购物车中已结算的记录返回订单号这一步库存扣减虽然在课程设计里用简单的UPDATE product SET stock stock - #{count} WHERE id #{id} AND stock #{count}就够了但如果你想让项目在面试时有亮点可以主动提一下超卖问题以及乐观锁方案UPDATE product SET stock stock - #{count} WHERE id #{id} AND stock #{count}这条 SQL 本身就是一种乐观锁的写法通过stock #{count}条件保证不会被扣成负数并发场景下也不会出现超卖。能在面试中清楚讲出这一点的候选人和单纯会 CRUD 的人是有明显区别的。支付模块在课程设计里通常是模拟的。一般做法是点击立即支付后前端调用一个模拟支付接口后端收到请求后直接把订单状态从待付款改成待发货并记录支付时间。如果你要把这个做成更真实的演示可以接入支付宝沙箱环境流程上会更有说服力。3.6 后台管理的权限控制后台管理接口/api/admin/**和 C 端接口/api/**通常在拦截器上做了路径区分。管理员请求需要额外校验角色或权限标识最简单的做法是用户表的role字段区分admin和customer管理员登录后生成的 Token 里带上角色信息后台接口校验时如果发现角色不是admin就拒绝访问。这样设计的目的很明确防止普通用户通过猜测接口地址直接操作后台。很多初学者会忽略这一步觉得后台入口页面不展示就行但实际上前端路由只是隐藏了入口接口如果没做鉴权任何人都可以绕过前端直接调用接口搞破坏。这个点在课程设计验收时老师大概率会问到提前想清楚怎么答。4. 前端核心模块与 Vue 实现4.1 前端工程结构与路由设计前端是标准的 Vue 工程如果你拿到的是 Vue 2 版本的源码工程结构大致是src/ ├── api/ // 接口请求封装按模块拆分 ├── assets/ // 静态资源图片、样式 ├── components/ // 公共组件 ├── router/ // 路由配置 ├── store/ // Vuex 状态管理 ├── views/ // 页面视图 │ ├── home/ // 商城首页 │ ├── product/ // 商品列表、商品详情 │ ├── cart/ // 购物车 │ ├── order/ // 订单确认、订单列表、订单详情 │ ├── user/ // 个人中心、收货地址、登录注册 │ └── admin/ // 后台管理页面 ├── App.vue └── main.js路由配置方面前端会区分游客可访问页面和需要登录才能访问的页面。Vue Router 提供了beforeEach路由守卫可以在路由跳转前检查是否有 Token没有就强制跳转到登录页router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) return } next() })我这里要特别提一下拿到源码后先确认这个项目的版本是 Vue 2 还是 Vue 3。Vue 2 搭配 Vue Router 3 和 Vuex 3Vue 3 搭配 Vue Router 4 和 Pinia/Vuex 4。版本不匹配会导致跑不起来。你如果看到main.js里用的是createApp()就是 Vue 3用的是new Vue()就是 Vue 2。4.2 Axios 封装与请求拦截器前端所有 HTTP 请求都通过 Axios 发出通常会在src/utils/request.js里做一个统一的封装。封装的核心逻辑是两块第一块是请求拦截器在每次请求发出前自动加上 JWT Token 头service.interceptors.request.use( config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }, error Promise.reject(error) )第二块是响应拦截器统一处理后端返回的结果。这一步的重点是拿到后端返回的数据后先判断code字段如果是 200 就正常返回数据如果是 401 就清除本地登录信息并跳转登录页其他错误码就弹出错误提示service.interceptors.response.use( response { const res response.data if (res.code 200) { return res } else if (res.code 401) { localStorage.removeItem(token) router.push(/login) return Promise.reject(new Error(请先登录)) } else { Message.error(res.message || 系统错误) return Promise.reject(new Error(res.message)) } }, error Promise.reject(error) )这段代码是整个前端数据流通的核心环节。所有接口请求都走这层统一处理不要在单个页面的请求代码里再单独判断code 200那属于重复劳动。4.3 核心页面商品列表、购物车、订单结算商品列表页面的核心是筛选和分页逻辑。一般是通过 URL 参数读取当前分类 ID然后调用后端接口获取对应分类下的商品再用 Element UI 的el-pagination组件做分页。这里要注意搜索关键字和分类筛选的组合查询前端要把这些条件放在同一个 query 对象里传给后端。购物车页面是前端交互最密集的页面。核心状态包括购物车商品列表、勾选状态、总价格。逻辑上当用户修改商品数量或勾选状态时前端需要实时计算总价。Vue 的computed计算属性非常适合做这件事computed: { totalPrice() { return this.cartList .filter(item item.checked) .reduce((sum, item) sum item.price * item.quantity, 0) } }每次勾选状态变化totalPrice会自动重新计算不需要手动监听和赋值。用户体验上还有一个细节如果用户把购物车数量改成 0合理的交互是弹出确认框询问是否删除该条记录而不是真的允许数量为 0 的数据存在。订单结算页是购物车到订单的过渡页。页面加载时读取购物车中勾选的商品展示商品清单和总价用户填写或选择收货地址后点击提交订单。这里有个很关键的信息传递方式前端通常会把选中的购物车记录 ID 列表通过路由参数或 Vuex store 传给结算页提交订单时携带这批 ID。4.4 后台管理前端的实现思路后台管理前端一般复用同一套 Vue 工程通过路由前缀/admin区分也可能单独拆成一个 admin 子工程。页面围绕几个管理功能展开商品管理页表格展示所有商品支持关键字搜索、上下架切换、编辑弹窗、新增弹窗分类管理页分类列表支持新增、修改、删除订单管理页订单表格按状态筛选点击发货按钮修改订单状态用户管理页用户列表禁用/启用账号这些后台页面在技术上其实没有太多高深的东西核心就是 Element UI 的表格el-table 表单el-form 弹窗el-dialog三板斧。但要注意的是后台管理页面的表单校验一定要做完整。商品价格必须大于 0、库存必须是整数、商品名称不能为空这些校验规则不能只靠后端前端也拦一道用户反馈会更好。5. 完整部署运行流程从零到跑通5.1 环境准备在正式跑这个项目之前先把环境准备好。我实际测试用的环境版本给你参考组件版本建议说明JDK1.8 或 11SpringBoot 2.x 建议 JDK 8 及以上Maven3.6后端依赖管理Node.js14 / 16前端运行环境Vue 2 建议 14/16npm/yarnnpm 6 或 yarn 1.x前端依赖安装MySQL5.7 或 8.0数据库建议 5.7 以上开发工具IDEA VSCode后端和前端分开用不同 IDE 效率更高如果你的 JDK 是 17大概率会遇到 SpringBoot 2.x 版本过低的兼容问题要么换 JDK 8要么把 SpringBoot 版本升上去。网上下载的源码很多是 SpringBoot 2.3.x 或 2.4.x用 JDK 8 跑最稳。5.2 数据库导入数据库是整套系统的地基导入这步千万别出错。第一步启动 MySQL 服务用 Navicat 或其他数据库管理工具创建一个数据库名字建议和项目配置保持一致。如果项目里配置的是tea_mall你就创建同名的库字符集选utf8mb4排序规则选utf8mb4_general_ci。用 utf8mb4 而不是 utf8 的原因很简单utf8mb4 能完整支持 emoji 和生僻字虽然商城系统大概率用不到 emoji但这已经是当前 MySQL 的实际标准了。第二步找到项目 SQL 文件。注意看是建库脚本、建表脚本还是带测试数据的完整脚本。我建议优先导入带测试数据的版本这样前端页面打开后有数据可以看不至于白屏。导入方式用 Navicat 的运行 SQL 文件功能最省事。第三步修改后端的数据库连接配置。打开application.yml或者是application.properties把数据库地址、用户名、密码改成你自己的spring: datasource: url: jdbc:mysql://localhost:3306/tea_mall?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver这里有个高频报错点serverTimezone参数不设置的话高版本 MySQL 驱动会报时区错误。useSSLfalse是关闭 SSL 连接避免本地环境证书问题。5.3 后端启动步骤后端启动相对简单核心就三步第一步用 IDEA 打开后端工程。首次打开时 IDEA 会自动识别 Maven 工程并下载依赖这个过程耗时取决于你的网络情况。如果国内网络下载慢在settings.xml里配置阿里云 Maven 镜像。第二步等依赖下载完成后在 IDEA 右侧 Maven 面板执行clean再执行compile确认编译没有报错。这一步能提前发现问题比直接启动后再报错容易排查。第三步运行启动类。找到TeaApplication.java右键点击 Run。看到 SpringBoot 启动成功的日志Started TeaApplication in xx seconds就说明后端起起来了默认端口一般是 8080。然后在浏览器访问http://localhost:8080/api/health之类的测试接口或者直接用 Postman 调用登录接口验证连通性。注意如果启动时端口被占用在application.yml里改端口即可server: port: 90905.4 前端启动步骤前端启动是新手重灾区我单独拆开写详细点。第一步用 VSCode 打开前端工程目录确认有package.json文件。第二步在 VSCode 的终端里执行npm install这一步是把项目里package.json声明的所有依赖包下载到本地node_modules目录。这里有两个常见问题安装速度极慢或者安装时报各种奇怪的错误。安装慢的主要原因是 npm 默认使用国外源切换到淘宝源能快非常多npm config set registry https://registry.npmmirror.com卸载旧源、验证新源npm config get registry如果npm install过程报错比如 node-sass 这类依赖安装失败大概率是 Node 版本跟依赖不兼容。可以尝试删除node_modules目录和package-lock.json文件后重新安装。第三步安装依赖成功后在终端执行npm run serve启动成功后会显示本地访问地址一般是http://localhost:8081或者http://localhost:8080。打开这个地址就能看到前端页面。提示如果前端请求后端接口报跨域错误检查vue.config.js里的 devServer 配置module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, // 后端地址 changeOrigin: true } } } }配置了代理之后前端访问/api/login时请求会被转发到http://localhost:8080/api/login浏览器侧不跨域。5.5 默认账号说明源码里通常会内置一个管理员账号和一个测试用户账号这类信息一般写在使用文档里。常见的账号密码组合是管理员admin / 123456普通用户user / 123456如果登录不进去先检查后台数据库的user表里的密码字段是不是 MD5 加密后的结果。简单验证方法123456的 MD5 值是e10adc3949ba59abbe56e057f20f883e如果数据库里存的不是这个或者用户表中加密方式不同那就说明需要换一个已知的测试账号或者重新注册。6. 常见问题与排查技巧实录6.1 问题速查表我在跑这套项目时整理了下面这个排查表都是实战中见过的问题和直接有效的解决方案。问题现象可能原因解决方案后端启动报Access denied for user rootlocalhost数据库用户名或密码错误检查application.yml中的连接配置后端报Unknown database tea_mall数据库还没有创建用 Navicat 创建同名数据库后再启动前端页面能打开但列表数据为空后端接口报错或数据库没数据打开浏览器 F12 看 Network 面板定位接口状态码请求报Network Error或跨域前端代理配置错误/后端未启动先访问后端接口地址确认后端在线再检查代理配置图片不显示静态资源映射未配置检查后端配置中/upload/**映射是否生效登录后刷新页面就报 401Token 未存储或拦截器未放行检查前端是否把 Token 保存到 localStorage检查后端是否放行登录接口npm install极慢或卡死npm 源在国外切换为淘宝源registry.npmmirror.com后重试前端启动提示Module not found依赖安装不完整删除node_modules和package-lock.json后重新安装数据库导入中文乱码字符集不对建库时使用utf8mb4导入 SQL 时确认文件编码为 UTF-86.2 我踩过的最深的一个坑版本不匹配这套项目在第一次启动时最让我头疼的问题不是代码逻辑而是环境版本之间的隐性问题。举个例子如果前端工程里某个依赖要求 Node.js 版本不能太高而你本地装的是 Node 18npm install就有可能出现依赖编译失败或者警告。这时候不要硬着头皮往下走最好的办法是装一个 Node 版本管理工具nvm-windows 或者 nvm随时切换 Node 版本哪个版本能让项目跑起来就用哪个。后端也类似SpringBoot 2.x 和 JDK 17 的兼容性并不好具体表现是启动过程不报错但某些反射操作会抛IllegalAccessException或者NoSuchMethodException。遇到这种情况直接确认 JDK 版本换成 8 是最省事的方案。另一个容易忽略的点是 MySQL 驱动的版本。SpringBoot 2.4 之前默认用的是com.mysql.jdbc.Driver6.0 之后改成了com.mysql.cj.jdbc.Driver。项目如果针对新版本 MySQL 配置了 cj 驱动但数据库用的是 5.7反而有可能连不上。遇到连接报错时优先看驱动类是否存在、连接 URL 格式是否匹配。6.3 前后端联调的通用排查方法联调阶段会各种问题我教你这个通用的排查打法第一步碰到前端页面白屏报错的问题先按 F12 打开 DevTools切到 Network 面板刷新页面看请求列表里哪个接口返回了红色4xx/5xx。第二步点击那个失败的请求看 Response 内容。如果返回的是后端错误信息那么问题在后端如果是ERR_CONNECTION_REFUSED说明后端没启动或者端口不对如果是ERR_CERT_AUTHORITY_INVALID一般是走了 https 但本地是 http。第三步如果接口 404确认后端接口路径和前端请求路径是否一致。一个典型的例子后端定义的是/api/product/list前端请求的是/product/list少了/api前缀这个问题经常出现在代理配置和接口定义不一致的场合。第四步如果接口返回的 JSON 结构跟前端预期的不一样比如前端期望response.data.list后端返回的是response.data.records这不是代码报错而是接口字段契约不一致。这种问题在联调阶段非常常见解决方式是前后端统一字段命名或者前端做一层字段映射。这套方法适用于一切前后端分离项目的联调。练熟了以后你排查问题的效率能比大部分人快一倍。7. 项目扩展思路与二次开发建议源码跑通只是开始如果你打算把这个项目用在课程设计答辩或者面试作品上我建议做这几件事来提升项目深度。第一件把密码加密方式从 MD5 换成 BCrypt。改动量不大但是在面试时你可以主动说我考虑到 MD5 的安全问题把密码存储升级成了 BCrypt这是一种自带盐值的慢哈希算法这一句话就能展现出你比同龄人更懂安全意识。第二件给订单模块加上事务管理。目前下单操作涉及多个表的写入如果没有事务中间任何一步出错都会留下脏数据。在后端 Service 方法上加上Transactional注解配合Rollback的机制讲解可以演示你对数据一致性的理解。第三件考虑给商品列表加上缓存。把热门分类的商品列表缓存到 Redis 里设置 30 分钟过期。面试时提到针对热数据引入 Redis 缓存降低数据库压力配合实际代码含金量会高不少。第四件如果有余力把模拟支付换成支付宝沙箱支付。支付宝开放平台申请沙箱环境免费集成起来也不算太复杂整个商城的最后一公里就闭环了演示效果会好非常多。但要注意扩展功能记得按优先级排序核心目标是把现有功能讲清楚、能演示流畅。有些人在答辩前临时塞了一堆功能结果原来能跑的功能反而搞挂了这属于典型的画蛇添足。8. 写在最后这套源码的打开方式我常说课程设计级别的源码价值不在于直接可用而在于改写和学习。拿到这套茶叶商城系统先用两小时把数据库表和代码结构理清楚再花半天跑通前后端然后选一个你最感兴趣的功能模块深挖它的实现细节结合上文提到的业务场景把用户从浏览到下单一整个链路完整走通——这个过程收获的东西比单纯把项目跑起来大多了。我实际操作下来最深的体会是源码里的代码风格不一定最优但它的业务流程和模块划分是清晰的。你可以把它当作一块半成品素材手头有想要实现的功能就往里加碰到不懂的就去查查着查着知识体系就补起来了。这套茶叶商城系统适合拿来当全栈入门的练手项目希望你也能像我一样在两三天里从跑不起来到玩得转。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门