恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

SpringBoot+Vue网上超市实战:前后端分离单体应用从设计到部署

  • 首页
  • 资讯中心
  • /
  • SpringBoot+Vue网上超市实战:前后端分离单体应用从设计到部署

相关资讯

工业大模型落地实践:私有化部署、知识库与LoRA微调全攻略 2026/10/22 3:15:00
视觉机器狗识别赛代码实战:从HSV颜色阈值到运动控制闭环 2026/10/22 3:15:00
工业大模型落地实战:小白可复用的RAG与微调避坑指南 2026/10/22 3:15:00

最新资讯

AI安全治理框架3.0落地实践:90天将责任落实到岗位
AI+心理模型+影视IP:人格测试互动世界的完整搭建指南
微信小程序开发实战:从零到上架,用WorkBuddy三周跑通剧本快读全流程
AI智能体开发实战:从需求分析到工具调用与稳定上线的完整指南
用WorkBuddy从零开发剧本快读小程序:技术选型与实操指南
GLM-Turbo手把手部署指南:本地推理与API调用实战

今日推荐

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南
压缩空气储能与热网热惯性联合优化调度:热电联产系统建模与求解
若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

SpringBoot+Vue网上超市实战:前后端分离单体应用从设计到部署

发布时间:2026/10/22 3:15:00
SpringBoot+Vue网上超市实战:前后端分离单体应用从设计到部署 1. 为什么这么老的组合到现在还能打项目定位与技术选型剖析先聊一个很多同学问过我的问题现在微服务、分布式、中台概念满天飞为什么还要做一个前后端分离的单体应用项目我的答案很直接——因为绝大多数刚入门或准备找工作的开发者根本驾驭不了微服务那套复杂体系。你让他讲清楚一个订单在多个服务之间怎么流转他大概率答不上来但你要是让他把一个单体项目从数据库表设计到前端页面渲染完整讲明白他反而能讲得头头是道。这就是网上超市系统这类项目经久不衰的核心原因它规模不大不小刚好能把一套完整的技术栈串起来又不会因为业务复杂度太高而让新手陷入泥潭。我选择SpringBoot Vue MyBatis MySQL这套组合也是权衡之后的结果。SpringBoot不用多解释它就是当前Java后端事实上的标准入门框架几乎所有的企业级项目都在用它Vue作为前端框架在国内的普及率不用我多说很多公司甚至直接把Vue写进了JD里MyBatis作为持久层框架它的SQL可控性在中小型项目中体现出明显的优势不像JPA那样在复杂查询时让人抓狂MySQL更是基础中的基础。这套组合放在2024年的今天依然能打原因就是它的学习曲线相对平缓生态成熟找工作面试时也足够应对大多数问题。网上超市这个业务场景选得也很巧。它不是一个纯CRUD的玩具项目而是包含了商品分类、购物车、订单、库存这些彼此关联的业务模块数据表之间有关系前端页面之间有跳转逻辑这就逼着你必须去思考表结构怎么设计、接口怎么划分、状态怎么流转。同时它的业务又足够直观——你在网上买东西把东西加进购物车下单付款这些流程每个人都体验过理解成本几乎为零。你不需要先去学习什么供应链金融或者医疗合规这种行业背景知识才能上手写代码业务理解的门槛低你就可以把精力全部放在技术实现上。这个项目适合谁来写我总结下来主要是三类人。第一类是临近毕业准备找工作的学生需要一个拿得出手的项目来丰富简历第二类是在职但想转行Java开发的人需要用项目来建立自己的作品集第三类是已经工作但想系统梳理前后端分离开发流程的同学通过这种麻雀虽小五脏俱全的项目来补齐自己的知识短板。如果你是这三类人之一这篇博客就是为你准备的。2. 从一张表开始数据库设计的完整思路很多人做项目喜欢上来就建表建到一半发现字段不够又去加加的时候发现关联关系又乱了最后表结构改得一塌糊涂。我在这个项目里换了个思路先把用户下单的场景从头到尾走一遍把涉及的实体全部列出来再根据实体之间的关系去设计表。2.1 核心业务实体的梳理网上超市的核心流程是这样的用户登录浏览商品把商品加入购物车生成订单完成支付查看订单状态。顺着这个流程我们可以很容易地提炼出以下实体用户User包含账号、密码、昵称、联系方式、收货地址等信息商品分类Category用于对商品进行归类管理商品Product包含商品名称、描述、价格、库存、图片等信息购物车Cart记录用户选中的商品及数量订单Order记录用户的一次下单行为订单明细OrderItem记录订单中包含的具体商品项这些实体之间的关系也很清晰用户与订单是一对多订单与订单明细是一对多商品与订单明细是一对多商品与分类是多对一。2.2 关键表结构的设计细节我把几张核心表的建表SQL拿出来说。首先是用户表CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 密码MD5加密存储, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, address VARCHAR(255) DEFAULT NULL COMMENT 收货地址, role TINYINT NOT NULL DEFAULT 0 COMMENT 角色0-用户1-管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有一个容易被忽视的点密码一定要加密存储绝不可以用明文。这个项目里我用了MD5加盐的方式虽然MD5在安全级别上不算顶尖但对于学习项目来说已经足够更重要的是它让你建立密码不可以明文存数据库这个意识。你在面试时可以提到这个点会加分不少。然后是商品表和订单相关表我把重点放在订单表上CREATE TABLE order ( id INT NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单号, user_id INT NOT NULL COMMENT 用户ID, total_price DECIMAL(10,2) NOT NULL COMMENT 订单总金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0-待付款1-已付款2-已发货3-已完成4-已取消, receiver_name VARCHAR(50) NOT NULL COMMENT 收货人姓名, receiver_phone VARCHAR(20) NOT NULL COMMENT 收货人电话, receiver_address VARCHAR(255) NOT NULL COMMENT 收货地址, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意两个关键设计。第一order_no订单号必须唯一而且我建议在生成策略上做文章比如用时间戳加随机数来生成避免使用自增ID直接暴露订单数量这个业务数据。第二订单表里直接冗余了收货人信息而不是只存user_id然后去用户表里查。为什么这么做因为下单人的收货地址可能会变如果订单只关联用户ID下单之后再改地址就说不清了。冗余存储本质上是业务的快照这是电商系统里很经典的设计思路。2.3 数据库设计的几个避坑经验我在给很多同学review表结构的时候发现大家最容易犯的错误有三个。一是字段类型选用不当比如存储价格用FLOAT结果算金额的时候出现精度丢失。金额一定用DECIMAL这是底线。二是缺乏统一的命名规范有的表用下划线有的表用驼峰关联字段命名不一致写SQL的时候总是在猜。三是缺少必要的索引很多同学建表只建了主键然后查询的时候发现慢得要死却不知道要给外键字段、状态字段加普通索引。提示表名不要用order、user这种MySQL的保留字如果非要用得写成\order这种带反引号的形式。我建议在真实项目中给核心业务表加个业务前缀比如mall_user、mall_order既避免关键字冲突也方便多业务模块扩展。3. 环境准备与部署一套能跑通的才是好项目我给这套代码写部署教程的时候最深的一个体会是代码能跑和教程能让别人也跑起来是两码事。很多人在自己的电脑上开发时依赖的是本地的各种隐式环境换一台机器就抓瞎。所以我把环境准备的部分写得很细甚至细到了下载哪个版本的JDK这种程度。3.1 本地开发环境的搭建清单按照我实际测试过的组合这里列出推荐的版本和安装注意事项组件推荐版本说明JDK1.8 或 11建议使用1.8兼容性最好很多公司生产环境仍以JDK8为主Maven3.6.3用于后端依赖管理和构建Node.js14.16.0 用于前端Vue项目的运行与构建npm6.x 或 7.x与Node.js配套建议使用nrm管理镜像源MySQL5.7 或 8.05.7资料多、坑少8.0性能更好但不兼容老驱动的连接配置IDEIntelliJ IDEA VSCode后端用IDEA前端用VSCode各司其职环境这块容易踩的坑有三个。第一Java和Maven的环境变量配了之后要重启终端才能生效很多同学配置完直接在原来的窗口里测java -version发现还是旧版本就以为自己配错了。第二MySQL 8.0的驱动类变成了com.mysql.cj.jdbc.Driver而且连接URL里面必须加serverTimezoneUTC之类的时区参数否则会报时区错误。第三Vue项目拉取依赖的时候淘宝镜像源是默认的http://registry.npm.taobao.org但有些地区的网络访问它反而比官方源更慢建议根据自己实际网速选择源。3.2 后端项目的启动步骤后端是标准的SpringBoot项目启动流程如下修改application.yml中的数据库连接信息包括URL、用户名、密码执行项目根目录下的sql/init.sql初始化数据库表结构和测试数据在IDEA中打开项目等待Maven自动下载依赖完成运行MallApplication.java中的main方法浏览器访问http://localhost:8080/api/product/list如果能返回JSON数据说明启动成功这里我特别说一下初始化SQL这个环节。我提供了完整的建表和测试数据脚本但很多同学习惯手动在Navicat里一条条执行容易漏掉某条语句导致后面报错。正确做法是使用命令行或者IDE的数据库工具来执行整个SQL文件确保表和数据的完整性。有个细节值得注意执行SQL脚本之前务必确认数据库的字符集设置。我默认建库语句写的是UTF8MB4如果你用的客户端工具默认字符集是latin1可能导致中文乱码。遇到乱码先检查两处一是库表的字符集是不是utf8mb4二是JDBC连接URL里有没有指定characterEncodingutf8。3.3 前端项目的启动步骤前端部分是基于Vue 2 Element UI搭建的启动流程也很直接# 进入前端项目目录 cd mall-web # 安装依赖 npm install # 启动开发服务 npm run serve启动完成后访问http://localhost:8082如果页面正常打开然后登录、浏览商品这些功能都能用说明前后端打通了。重点要说的是跨域问题。前端的开发服务器默认跑在8082端口后端接口在8080端口两者的端口不同浏览器就会发起跨域请求。我在后端配置了全局的CORS策略允许跨域请求。如果你是自己从零开发忘了配CORS浏览器控制台会报Access-Control-Allow-Origin相关的错误页面显示请求失败但后端日志里却能看到请求进来了——这个现象非常典型。4. 后端代码的骨架与核心业务实现整个后端项目的代码结构是按经典的分层架构来组织的。我列出目录结构让你先有个整体印象src/main/java/com/mall/ ├── controller # 控制器层接收请求返回JSON数据 ├── service # 业务逻辑层处理核心业务规则 ├── mapper # MyBatis的Mapper接口 ├── entity # 实体类对应数据库表结构 ├── config # 配置类如CORS配置、拦截器配置 ├── common # 公共类如统一返回结果、异常处理等 └── MallApplication.java # 启动类4.1 统一返回结果与全局异常处理很多初学者写接口的时候喜欢直接从Controller返回一个Map或者干脆返回一个List结果就是前端拿到的数据结构五花八门有些接口是{code:0, data:{...}}有些接口又是[{...}]对接的时候简直想死。我在项目里定义了一个统一的响应结构public class ResultT { private Integer code; // 状态码200成功500失败 private String message; // 提示信息 private T data; // 返回数据 public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.message success; result.data data; return result; } public static T ResultT error(String message) { ResultT result new Result(); result.code 500; result.message message; return result; } }所有Controller的返回值都是ResultT类型。做这一步看起来只是多了几个泛型类但它带来的收益非常大首先是前端可以统一处理成功和失败的判断逻辑不用为每个接口定制解析方式其次是你可以在Result里方便地扩展分页信息、时间戳等字段而不需要改动调用方代码。配合统一返回结果我还加了全局异常处理器。所有业务抛出的异常最终都通过RestControllerAdvice捕获转成统一的Result.error()返回。这样做的一个好处是接口层不会因为未捕获的异常而裸奔出5xx状态码加一堆堆栈信息的HTTP响应你可以在日志里记录详细堆栈但返回给前端的是友好的错误提示。4.2 用户登录与JWT鉴权流程网上超市的用户模块除了注册登录之外还要区分普通用户和管理员两种角色。我用的是JWTJSON Web Token来做身份认证。登录流程是这样的用户提交用户名和密码后端正向比对密码是否匹配存储时是MD5加盐后的密文验证通过后生成一个JWT令牌其中包含用户ID和角色信息前端把JWT存进localStorage之后的每次请求都在Authorization头里带上这个令牌后端通过拦截器验证令牌的有效性并从中提取用户信息生成JWT的核心代码我贴一下String token Jwts.builder() .setSubject(String.valueOf(user.getId())) // 以用户ID为主题 .claim(role, user.getRole()) // 角色信息放在claim里 .setExpiration(new Date(System.currentTimeMillis() 3600 * 1000)) // 1小时过期 .signWith(SignatureAlgorithm.HS256, secretKey) .compact();这里有个很容易踩的坑JWT密钥不要硬编码在代码里而且长度不能太短。很多教程直接写String secretKey abc;结果用HS256算法时直接报错signing key cannot be less than 256 bits for HS256。我项目里用的是从配置文件读取的方式并且提供一个比较长的密钥字符串。这个细节看起来不起眼但能帮你省掉不少排查时间。4.3 购物车模块状态与计算逻辑的实现购物车是网上超市的核心交互之一。后端接口主要做了三件事添加到购物车、修改购物车商品数量、删除购物车商品。这里面比较值得说的是购物车中商品价格的计算逻辑。我设计购物车表cart时字段包括id、user_id、product_id、quantity。每次前端请求我的购物车时后端并不是直接查询cart表返回数据而是做一次关联查询从商品表里取出最新的单价与数量相乘后得出小计金额。为什么要这么做因为商品的单价是浮动的可能管理员调整了价格但购物车里存的是用户加购那一刻的单价快照。如果直接返回购物车的旧价格用户结算时看到的金额可能和付款时不一致容易引发纠纷。业务逻辑落到代码上大致是这样的过程// 获取购物车列表关联商品表查询最新价格 ListCartVO cartList cartMapper.selectCartListWithProduct(userId); // 计算总价 BigDecimal totalPrice cartList.stream() .map(item - item.getPrice().multiply(new BigDecimal(item.getQuantity()))) .reduce(BigDecimal.ZERO, BigDecimal::add);在实际做这个功能的时候我觉得最需要注意的其实是修改数量时对库存的判断。用户在购物车里连续点号把数量加到1000后端不能照单全收要实时去校验商品表的库存字段。库存不足时应该直接返回库存不足的错误提示。这个逻辑放在后端而不是前端来做是因为前端校验可以被绕过比如直接模拟请求而库存扣减的真实约束必须由后端保证。4.4 订单生成的业务流程与事务管理订单模块是这个项目里事务性最强的部分。一次完整的下单操作需要同时完成以下几件事校验购物车中的商品是否存在、是否还有库存生成订单主表记录写入订单状态为待付款根据购物车内的商品列表生成订单明细记录扣减商品库存清空已下单的购物车这五步操作必须做到要么全部成功要么全部失败否则就会出现订单生成了但库存没扣或者购物车清了但订单没生成这种数据不一致问题。所以我在Service层的下单方法上加上了Transactional注解。关于事务我多说一句。很多人在学习阶段只知道加注解却不知道为什么加、什么时候去掉。在这个项目里我觉得值得关注的地方是Transactional失效的几个典型场景。比如方法被private修饰导致AOP代理不生效在同一个类的内部调用自带事务的方法绕过了代理对象异常被try-catch吞掉事务感知不到异常就不会回滚。这些都是我在帮人查为什么下单后库存没扣减这类问题时反复遇到的情况。下单方法的核心逻辑大致如下Transactional(rollbackFor Exception.class) public boolean createOrder(Long userId) { // 1. 查询当前用户的购物车数据关联商品表 // 2. 依次校验商品状态及库存 // 3. 生成订单号写入订单表 // 4. 遍历购物车生成订单明细表记录 // 5. 扣减库存UPDATE product SET stock stock - #{count} WHERE id #{productId} AND stock #{count} // 6. 清空当前用户的购物车 // 7. 返回订单ID }注意到第5步的SQL没有用先查库存再判断的两步操作而是用一条UPDATE ... WHERE stock #{count}的原子操作返回的受影响行数如果是0则说明库存不足。这种写法可以避免在并发场景下超卖。虽然说单体项目并发量不会太大但从一开始就养成用条件更新代替查改分离的习惯对你未来接触高并发场景会很有帮助。5. 前端页面的实现逻辑与关键交互前端部分我用的是Vue 2 Vue Router Element UI Axios。选择Vue 2而不是Vue 3一个很现实的原因是很多公司的存量项目仍然是Vue 2面试时你写出Vue 2经验更容易匹配岗位需求另外一个原因是网上的Vue 2 Element UI生态资料极其丰富遇到问题搜索起来效率高。5.1 前端路由与页面结构页面结构按角色分为两部分用户端和管理员端。用户端的页面有首页商品列表展示支持按分类筛选和关键词搜索商品详情页展示商品完整信息支持加入购物车购物车页展示购物车商品修改数量删除结算订单列表页查看当前用户的历史订单和状态个人中心页修改个人信息和收货地址管理员端的页面有后台首页统计订单数量、商品数量等基础数据商品管理页商品的增删改查分类管理页商品分类的维护订单管理页处理订单状态的流转发货、取消等这样的页面规划前端路由可以设计成嵌套路由加权限控制的方式。用户在路由守卫里根据localStorage中解析出的角色信息进行跳转拦截普通用户访问/admin路径时会被强制重定向到首页。这个逻辑虽然简单但却是很多人容易忽略的权限校验雏形。5.2 Axios封装与Token的统一携带如果你看过很多烂代码你会发现每个页面里的Ajax请求都是各写各的有的拿到数据后console.log一下就不管了没有错误处理有的请求成功时要显示一个loading失败的又要隐藏代码重复严重。所以我在项目里统一封装了一个request.js的Axios实例。封装的核心点有两个。一个是请求拦截器里自动携带Token你不需要在每个请求里手动拼Authorization头而是拦截器统一从localStorage读取并写入headers。另一个是响应拦截器里统一处理HTTP 401状态一旦发现Token过期或无效自动清空本地登录状态并跳转到登录页让用户重新登录。这个机制看似简单但它是前后端分离应用中会话维持的标准打法。Axios封装的骨架大致如下import axios from axios const request axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) // 请求拦截器携带token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) // 响应拦截器统一处理状态码 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { this.$message.error(res.message) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) window.location.href /login } return Promise.reject(error) } ) export default request5.3 商品列表页的动态渲染思路商品列表页是整个前端最典型的列表 搜索 分页场景这段代码几乎可以复用到所有后台管理页面。页面用的是Element UI的el-table组件搜索区绑定了商品名称关键词和分类ID点击搜索按钮时重新拉取第一页数据。这里有一个容易被忽略的细节商品图片的处理。数据库里存的是图片的相对路径或者外链前端渲染el-image时如果路径是/static/image/xxx.jpg需要拼上服务器的访问前缀。我在项目里统一用Vue的全局过滤器或者计算属性处理了这个拼接避免每个页面各写一遍。分页的逻辑我也强调一下。前端传给后端的参数是pageNum和pageSize后端返回的数据结构是包含list、total、pages的分页对象。这里前端拿到total之后还要自己维护当前页数currentPage改页数或改每页条数时都要重新调用接口。很多新手在这一步容易出bug比如点下一页数据没刷新、切换每页条数还是从第一页开始——其实问题就出在忘了把pageNum重置为1。5.4 购物车交互的体验细节购物车页面是我自己在前端部分花心思最多的地方。因为是典型的选择商品 - 结算流程所以交互要考虑下面几个点每个购物车条目前面有复选框需要计算已选中的商品总金额商品数量有加减号和手动输入两种修改方式输入非法值时要自动回退到原值商品下架非上架状态时前端显示已失效并禁用结算这些交互看起来琐碎但它们非常贴近真实电商项目的要求。也是你在面试时可以展示的项目亮点你可以说我在做购物车时并没有简单地一股脑把购物车里的所有商品都去结算而是支持了勾选部分商品进行结算并且后端也提供了对应的参数设计传入选中的购物车项ID列表。这一点已经可以说明你在流程设计上有自己的思考。6. 后端Controller与前端对接的协作方式前后端分离的项目最容易出现的痛点就是接口定义不统一前端联调时反复扯皮。我在做这个项目的过程中总结了一套还算高效的协作方式分享出来供参考。6.1 接口契约先行在写代码之前我会先把接口文档以表格的形式整理出来字段名、数据类型、是否必填都写清楚。比如商品列表接口接口路径GET /api/product/list请求参数pageNum页码、pageSize每页条数、keyword关键词可选、categoryId分类ID可选返回结构{ code, message, data: { list, total } }字段说明list为商品列表total为总数在接口文档里特别要注明字段命名风格。很多前后端对接问题根源就在于后端使用了user_name这种下划线命名而前端定义的参数叫userName结果传递过去全是undefined。为了省事我在后端返回给前端的VO视图对象里把所有字段都定义成驼峰命名如productName、categoryId与前端保持一致。如果你用的是MySQL的下划线物理表结构也不需要慌MyBatis里可以直接配置map-underscore-to-camel-case: true来自动映射。6.2 联调阶段最容易出现的三类问题第一类是数据类型不一致。MySQL的INT对应Java的Integer没问题但DECIMAL映射到Java后如果直接返回给前端JSON序列化时可能会出现精度丢失或者变成字符串的尴尬情况。我处理金额统一用BigDecimal并在字段上加上JsonSerialize(using ToStringSerializer.class)注解保证前端不丢精度。第二类是日期格式不一致。后端默认返回的Date类型序列化成JSON是时间戳格式前端拿到的是一个长数字直观看不出是哪一天。解决方案是在配置里统一设定日期格式化如spring.jackson.date-formatyyyy-MM-dd HH:mm:ss或者使用LocalDateTime配合JsonFormat注解。第三类是接口路径不对。比如后端的RequestMapping(/api/product)下有一个GetMapping(/list)实际访问路径是/api/product/list——但这个规则新手经常记混一会儿以为没有/api前缀一会儿又多写一层。这要求双方都要遵守写好的接口文档不要自己脑补路径。6.3 用Postman先自测再交给前端我在把接口交给前端联调之前习惯先用Postman把主要接口全部过一遍。这个习惯帮我拦下了不少低级失误比如某个接口忘了传某个必填参数会报500、某个接口在没有Token时会直接返回未授权等。自测通过后再让前端同学用真实的页面流程去操作。调试期间另一个实用的工具是浏览器的开发者工具Network面板。当前端同学反馈页面请求失败时第一步不是去翻后端日志而是先看Network面板里这个请求的响应码和响应体。如果是401说明Token问题如果是500再去后端日志排查如果是200但页面数据不对再检查数据处理逻辑。这个排查顺序能节约你一半以上的联调时间。7. 多角色权限控制与后台管理的实现要点网上超市系统不是一个只有用户端的前台页面它还包含了一个功能完整的管理后台。我在做后台管理时有两个地方值得展开说说。7.1 管理员与普通用户的权限区分后端侧的权限控制我是通过拦截器加角色判断来实现的。拦截器统一拦截/api/admin/**的请求在JWT解析后查看角色是否为管理员如果不是则直接返回403错误。这里有个细节JWT里的角色信息是签发时写入的如果用户角色在Token存活期内发生了变化比如管理员被封禁本次Token里带的信息仍然显示为管理员。对于学习项目来说这种处理方式可以接受但如果你在做真实项目就要考虑引入更完善的服务端Session控制或权限框架比如Spring Security Redis的思路。前端的权限控制我前面提到了路由守卫。但我再补充一个重要细节前端做权限控制的初衷只是为了优化用户体验后端才是安全屏障。也就是说哪怕前端可以随便隐藏或显示某个菜单后端也必须对每个受保护接口做严格的权限验证。我在项目里特意在前端隐藏了管理入口这个链接但即使有人手动在地址栏输入/admin路径后端也会拦截他。这个安全和体验分离的原则无论项目大小都适用。7.2 商品与分类管理的CRUD套路后台管理的商品模块本质上是一张表的增删改查。但我在这个模块里还嵌入了一个有价值的思路分类和商品之间使用外键约束时删除分类前要检查该分类下是否还有商品。如果直接删除分类商品表里的categoryId就会变成悬空引用导致商品在前端无法正确展示分类名称。我在Service层添加了一个校验逻辑if (productMapper.countByCategoryId(categoryId) 0) { throw new BusinessException(该分类下还有商品无法删除); }这一段虽然只有几行但它是典型的数据完整性业务约束面试时你可以顺势讲出为什么分类删除不能一刀切这个业务层面的思考。7.3 后台首页统计数据的简易实现后台首页要展示一些核心统计数据比如商品总数、订单总数、用户总数、待发货订单数。这个功能放在别的项目里可能会被做成复杂的报表中心但我用最简单的方式实现在DashboardController里写了几个count查询一次性汇总返回给前端。这类统计SQL虽然简单但有一个实际生产中非常值得注意的原则不要在主业务表上频繁执行全表COUNT统计数据量大了会拖垮数据库。你可以考虑用汇总表或者缓存来应对。但在学习项目中直接COUNT是没有问题的只要你别无脑地在高并发场景下也这么干就行。这个知识点可以作为一个扩展点写在简历的项目描述里面试官常会顺着问。8. 踩坑实录从零部署到项目跑通的完整排查链路很多同学拿着教程部署项目总是卡在一些奇奇怪怪的问题上。我把自己在绿豆超市项目上传到Gitee、发布到博客、再提供给读者下载后遇到的高频问题的排查过程整理成一个链路希望能给你一些参考。8.1 MyBatis XML文件编译后找不到的经典报错很多同学第一次跑SpringBoot MyBatis项目时会报这样一个错误Invalid bound statement (not found): com.mall.mapper.ProductMapper.selectProductList排查思路是这样的先看ProductMapper.java接口上的方法名是否和XML文件中的id完全一致包括大小写再看接口的全限定名是否和XML中的namespace一致。两个都对了还报错那么大概率是XML文件没有被Maven复制到target目录——根因是Maven默认只会把src/main/resources下的文件拷贝到classpath而你把XML文件放在了src/main/java的Mapper接口旁边Maven默认不会把它当作资源处理。解决方案也很简单在pom.xml里增加资源配置指定build resources resource directorysrc/main/java/directory includes include**/*.xml/include /includes /resource /resources /build或者更推荐的做法把XML文件统一放在src/main/resources/mapper/目录下并在application.yml里指定mybatis: mapper-locations: classpath:mapper/*.xml我遇到不少读者在评论区贴日志问这个问题他们几乎都犯了这个把XML放在Java目录下却不知道需要额外配置的错误。8.2 前端npm run serve之后白屏或接口404这个问题我至少被问了二十次。场景是这样的前端通过npm run serve启动成功了控制台也没报错但打开页面发现白屏或者接口全部显示404。白屏问题的排查链路是这样的先打开浏览器控制台的Console面板看有没有JavaScript报错。最常见的报错是Cannot read property prototype of undefined——这是Element UI版本和Vue版本不匹配导致的。我项目里用的是Element UI 2.15.x配Vue 2.x如果你强行装了Element UI 3.x那是给Vue 3用的就会白屏。接口404问题的排查链路先看控制台请求的URL是什么。如果请求的是http://localhost:8082/api/product/list而实际后端是http://localhost:8080/api/product/list那就是前端代理没配好。在vue.config.js里配置代理是解决这个问题的标准方式module.exports { devServer: { port: 8082, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }配完代理后要重启npm run serve不是刷新页面就行。这又是个容易卡住新手的地方改完配置文件不重启开发服务配置压根不会生效。8.3 登录成功但跳转后用户信息丢失有读者反馈登录成功后页面明明显示用户名了但刷新一下又变成未登录需要重新登录。问题出在登录成功后的前端处理逻辑上。正确的流程应该是登录接口返回JavaWebToken令牌前端保存到localStorage同时把用户基本信息昵称、头像等保存在内存中的Vuex store里。刷新时Vuex里的数据会清空此时要有一个地方根据Token去请求获取当前用户信息的接口把用户信息重新拉回来填补空白。如果刷新后丢失用户信息要么是没有在应用启动时调用获取用户信息接口要么是后端获取当前用户信息这个接口没处理好从Token解析用户ID的逻辑。排查时打开Network面板看刷新后有没有发一个/api/user/info的请求如果没有就去检查main.js或路由守卫里的初始化逻辑。8.4 部署到服务器后的静态资源路径问题本地开发时图片能显示部署到服务器后图片全裂了。这个问题的排查重点在于判断图片地址是相对路径还是绝对路径。如果数据库里存的是/static/img/1.jpg这种相对路径而你的前端打包后部署在Nginx的/usr/share/nginx/html下那么浏览器访问http://ip/static/img/1.jpg时就会404。解决思路有两种。一种是把图片路径改成完整URL比如http://ip:8080/static/img/1.jpg另一种是把静态资源目录和Nginx的根目录对齐。考虑到可移植性我项目里采用的是存储相对路径 前端拼接服务器地址的方式这样换服务器只需要改一个地方。注意不要直接把图片上传到SpringBoot项目的classpath静态资源目录里因为重新打包覆盖文件时上传的图片会被清空。更好的做法是上传到独立的/data/upload目录再通过Nginx配置一个/upload的路径映射到那里。这已经是一个接近生产级的设计思路了。9. 让简历项目变成面试谈资还差哪些加分项代码跑通、部署成功、能演示这只是第一步。如果你准备用这个项目找工作那我建议你在下面几个方向上做一次深度复盘把会写代码变成能讲清楚问题。9.1 缓存与并发场景的预演面试官非常喜欢问如果你这个系统的商品详情页被大量访问你会怎么优化这个问题是引导你往缓存的方向走。你可以这样回答先用Redis缓存热点商品信息减少对数据库的查询压力缓存空值要设置较短的过期时间防止缓存穿透商品更新时主动删除缓存而不是等它自己过期这样可以保证数据的一致性。另外还有一个经典问题下单时库存超卖怎么办我前面提到的UPDATE product SET stock stock - #{count} WHERE id #{id} AND stock #{count}就是回答的核武器。你要把这个方案的原理说清楚数据库行锁在更新时会给这行数据加锁后面的更新操作会阻塞等待直到前一个事务提交或回滚。这个机制天然防止了超卖。如果能再补充对于极高并发场景可以用Redis预扣库存异步落库这种进阶方案就更有说服力了。9.2 文件上传与富文本的扩展思路网上超市的商品描述如果只是纯文本显得有点单薄。我有一个很自然的扩展思路可以引入一个富文本编辑器比如WangEditor或UEditor让管理员在商品编辑页输入带格式的描述图片也能插入。这就需要后端提供一个通用的文件上传接口。这个扩展方向看起来小但它能串联起一系列知识点文件上传的MultipartFile接收、存储路径规划、静态资源映射或Nginx访问、大小限制配置、类型校验只允许jpg/png等防止上传恶意脚本。如果你能在简历里写实现了商品图片上传与管理功能这本身就是一块完整的业务闭环。9.3 单元测试与日志规范化的习惯说实话大部分学习项目是没有单元测试的甚至是连日志打印都很少。如果你能在订单生成方法里加上关键步骤日志开始下单、扣库存成功、订单生成成功、购物车清空完成再给核心业务比如下单逻辑写几个单元测试用例你和其他候选人的差距就体现出来了。我比较推荐用SpringBootTest做集成测试最核心的一层意义是它能帮你在改造代码后快速验证核心流程没有跑偏。而且这个习惯一旦培养起来在真实工作中价值极大。9.4 代码规范命名、注释与提交信息有几个不太好量化但一眼就能看出工程素养的习惯包名统一用com.mall类名、方法名用驼峰常量用UPPER_CASEController只做参数校验和转发不写业务逻辑接口注释写清楚参数和返回值的含义Git提交信息写清楚做了什么而不是修改了代码。这些看起来是小细节但干净代码和不干净代码的差异在面试官浏览你项目的那一刻就已经决定了印象分。10. 部署上线从本地到服务器的一种完整实践我对部署教程的定义是拿到手跟着敲能跑起来。上面已经讲了很多本地启动的过程最后我再补一段服务器部署的真实经验这部分我踩过不少坑。10.1 后端打包与运行在本地把后端项目打成可执行Jar包mvn clean package -DskipTests打包产物在target/mall.jar。上传到服务器后用java -jar mall.jar启动。注意服务器上要有JDK8环境。如果你想让日志输出到文件里可以这样启动nohup java -jar mall.jar /data/logs/mall.log 21 这里最容易遇到的问题是端口占用和内存不够。SpringBoot默认端口是8080如果服务器上已经有程序占了8080你可以用--server.port8090来临时指定。内存方面云服务器如果是2G内存默认JVM参数可能分配得过大导致启动失败可以用-Xms256m -Xmx512m来限制堆内存。10.2 前端打包与Nginx配置前端打包npm run build构建产物在dist目录。把dist目录里的文件上传到服务器的/usr/share/nginx/html。然后在Nginx的配置里做两件事一是把/api路径反向代理到后端的http://127.0.0.1:8080二是把路由历史模式mode: history下的刷新404问题用try_files来解决。Nginx关键配置片段server { listen 80; server_name your_server_ip; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files $uri $uri/ /index.html;这一行解决的是Vue Router的history模式下的刷新页面404问题。如果你用的是hash模式URL里有#就不会有这个问题但URL不美观。我这里选择的是history模式所以必须配这一步。10.3 备份与版本管理部署上线还有一个经常会漏掉的东西数据库备份。我分享一个简单的定时备份脚本思路用mysqldump把数据导出到压缩文件并保留最近N天的备份。这个脚本虽小但能救命。在真实使用这个部署方案时务必要把配置文件里的数据库密码改成服务器上实际数据库的密码不要因为赶时间把开发环境的密码直接复制上去。另外服务器数据库要提前设置好远程访问的权限策略避免把端口裸暴露在公网上。11. 写在代码之外的几点体会代码层面的内容讲得差不多了最后分享几个我在带人做这个项目过程中的真实感受。第一不要总是想着找一个大项目。很多初学者觉得网上超市这种项目太小、太简单了转身去找秒杀系统、仿淘宝全栈项目结果因为业务复杂度太高学了两个月还在环境配置阶段打转最后连一个完整功能都跑不通。项目是为了服务你的成长阶段而不是反过来让你被项目压垮。第二能跑通第一步永远比追求完美重要。我在教程里见过有同学一上来就纠结要不要用Redis要不要引入消息队列其实对一个单体学习项目来说这些都属于过度设计。先把基本功能完整地走通再考虑在哪个点上去做微创新反而更容易积累成就感。第三做项目过程中最重要的是沉淀出为什么。我在这个项目里反复强调的事项比如金额用DECIMAL、密码要加盐、事务要处理库存一致性、接口要统一返回结构这些都不是死记硬背的教条而是从真实业务和失败中提炼出来的经验。你如果能用自己的语言把每个为什么讲清楚那么在面试中你就已经打败了九成只会照抄代码的同学。我建议你拿到这个项目源码之后不要急着下载、解压、运行、截图然后丢进简历。给自己一个下午的时间把数据库表和模块划分搞清楚然后亲自动手去写一个模块哪怕是很简单的商品详情接口也比跑通一个完整项目更有价值。做项目真正练的不是跟随步骤而是独立解决问题的能力和对业务向前推导的敏锐度。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号