ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringBoot+Vue超市管理系统实战:从环境搭建到库存事务与二次开发

SpringBoot+Vue超市管理系统实战:从环境搭建到库存事务与二次开发

简介:这份资源是面向计算机专业毕业设计场景的超市管理系统完整项目源码,采用SpringBoot后端与Vue.js前端的前后端分离架构,适合正在准备毕设或需要学习企业级Web开发的学生与初中级开发者参考。压缩包共约2000个文件,涵盖1614个js、233个css、61个java、43个vue及xml、json、html、sql等类型,分别对应前端脚本与样式、后端业务逻辑、页面组件、配置与数据库初始化脚本,整体约23.17MB。项目围绕商品库存、销售数据、会员信息、促销活动与供应链等业务展开,包含用户管理、库存管理、销售管理、报表统计和系统设置等模块,并附数据库脚本便于初始化运行。已有87人学习下载,可帮助读者理解前后端分离项目的目录组织、接口调用与模块划分,为毕设选题、代码复现和功能扩展提供较完整的参考样本。

1. 超市管理系统:一套能跑通进销存的 SpringBoot + Vue 全栈骨架

接手一套超市管理系统的源码,最怕的不是代码多,而是跑不起来——依赖装不上、数据库连不通、前后端跨域报错,折腾一整天连登录页都进不去。这套基于 SpringBoot + Vue 的超市管理系统,把商品、库存、订单、会员、供应商这几条超市核心业务线串成了一条完整链路,前端用 Vue 做单页交互,后端用 SpringBoot 提供 REST 接口,数据库走 MySQL。它适合两类人:一是毕业设计阶段需要一套结构完整、业务闭环的项目做参考和二次开发;二是刚转全栈、想拿一个真实业务场景练手前后端联调的人。下面按「这套东西是什么 → 怎么把它跑起来 → 业务模块怎么改 → 坑在哪」的顺序拆,每一步都落到能抄的命令和配置上。

2. 环境准备与项目结构:把前后端两条腿先站稳

拿到源码包,第一件事不是急着npm install,而是先看清目录结构和版本约束。这类 SpringBoot + Vue 项目通常分成两个独立工程:后端一个 Maven 工程,前端一个 Vue CLI 或 Vite 工程,中间靠 HTTP 接口通信。版本对不上是最常见的翻车点,比如后端用了 SpringBoot 3.x 而 JDK 还是 8,启动直接报Unsupported class file major version;前端用了 Vue 3 的语法却在 Vue 2 的脚手架里跑,编译阶段就炸。

2.1 版本矩阵与依赖清单

先把下面这张表对着自己的机器核一遍,缺什么补什么。版本号以源码包里的pom.xml和package.json为准,这里给的是这类项目的典型配置区间,实际以文件内声明为准。

组件典型版本作用核对位置
JDK8 或 17后端运行时pom.xml的java.version
Maven3.6+后端构建命令行mvn -v
SpringBoot2.7.x / 3.x后端框架pom.xml父工程
MySQL5.7 / 8.0数据存储application.yml
Node.js14 / 16 / 18前端构建package.jsonengines
Vue2.x 或 3.x前端框架package.json

核对完版本,后端先跑一次依赖解析,把本地仓库该下的包下全,避免启动时才暴露缺包:

# 进入后端工程根目录,跳过测试快速拉依赖并编译 mvn clean compile -DskipTests # 如果公司或学校网络对中央仓库不友好,检查 settings.xml 是否配了镜像 mvn -v

clean清掉旧的target产物,compile只编译不打包,-DskipTests跳过测试用例——第一次拉依赖时测试代码可能因为缺库而失败,先跳过能更快看到编译结果。如果这一步报某个依赖找不到,八成是版本号在仓库里不存在,去pom.xml里把对应依赖的版本改成实际存在的稳定版。

2.2 数据库初始化与连接配置

后端能编译不代表能启动,数据库是第二道关。源码包里一般带一个.sql脚本,先建库再导表:

# 登录 MySQL 后执行,库名以脚本内为准,这里用 supermarket 举例 mysql -u root -p -e "CREATE DATABASE supermarket DEFAULT CHARACTER SET utf8mb4;" # 导入表结构和初始数据,注意路径换成脚本实际位置 mysql -u root -p supermarket < ./sql/supermarket.sql

建库时显式指定utf8mb4,是因为超市系统里商品名、会员备注常出现中文和特殊符号,用utf8存 emoji 或生僻字会截断。导完表去改后端配置文件,把连接信息对齐:

# application.yml 关键片段 spring: datasource: url: jdbc:mysql://localhost:3306/supermarket?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver

serverTimezone=Asia/Shanghai这个参数别省,MySQL 8 驱动对时区敏感,不配会在插入订单时间时抛The server time zone value is unrecognized。characterEncoding=utf8配合库的utf8mb4,保证中文不乱码。改完启动后端:

mvn spring-boot:run

看到控制台打印 Tomcat 端口和启动耗时,说明后端这条腿站稳了。端口默认 8080,如果被占用,在application.yml里改server.port。

2.3 前端启动与接口代理

前端工程单独开一个终端:

# 进入前端目录 cd frontend # 安装依赖,国内网络慢可以临时换源 npm install --registry=https://registry.npmmirror.com # 启动开发服务器 npm run serve

npm run serve是 Vue CLI 的启动命令,Vite 工程则是npm run dev,看package.json的scripts字段确认。启动后浏览器访问控制台给出的地址,通常是 8080 或 8081。前端调后端接口会跨域,正规做法是在vue.config.js里配代理,而不是让后端加@CrossOrigin到处撒:

// vue.config.js module.exports = { devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', // 后端地址 changeOrigin: true, pathRewrite: { '^/api': '' } // 按后端实际前缀调整 } } } }

changeOrigin: true让代理转发时改写 Host 头,避免后端做来源校验时拒绝;pathRewrite决定前端请求的/api/xxx转发到后端时是否去掉/api前缀,这一条要和后端 Controller 的@RequestMapping对齐,对不上就是一片 404。前后端都起来、登录页能正常请求验证码或登录接口,环境这关才算过。

3. 核心业务模块拆解:商品、库存、订单怎么串起来

环境跑通只是入场券,这套系统真正值钱的地方在于它把超市的进销存逻辑落成了可读的代码。超市业务的核心矛盾是「库存要准、订单要快、账要对得上」,对应到代码就是商品表、库存表、订单主从表之间的联动。理解这条链路,二次开发时才知道改哪里不会牵一发动全身。

3.1 商品与库存的数据模型

商品和库存通常拆成两张表:商品表存名称、条码、分类、进价、售价这些相对静态的信息,库存表存当前数量、预警阈值、最近更新时间这些动态信息。拆开的好处是商品信息改动不影响库存流水,库存频繁变动也不用锁整行商品记录。典型字段设计如下:

表关键字段说明
productid, barcode, name, category_id, purchase_price, sale_price条码唯一,价格用 decimal
stockid, product_id, quantity, warn_threshold, update_timeproduct_id 外键关联
stock_recordid, product_id, change_type, change_qty, order_id出入库流水,可追溯

价格字段一定用decimal(10,2)而不是float,浮点数在金额累加时会出现0.1 + 0.2 = 0.30000000000000004这种玄学误差,对账时能让人抓狂。库存变动不要直接update stock set quantity = quantity - 1了事,正确做法是同时写一条stock_record流水,出问题时能顺着流水倒查是哪一笔订单扣错了。

3.2 订单创建与库存扣减的事务处理

下单是整套系统里最容易出并发问题的地方。两个收银台同时卖最后一件商品,如果扣库存不加锁,就会出现超卖。SpringBoot 里用@Transactional包住「写订单 → 扣库存 → 写流水」三步,保证要么全成功要么全回滚:

@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private StockMapper stockMapper; @Transactional(rollbackFor = Exception.class) public void createOrder(OrderDTO dto) { // 1. 写订单主表,拿到订单 id Order order = buildOrder(dto); orderMapper.insert(order); // 2. 逐条扣库存,带条件更新防止超卖 for (OrderItem item : dto.getItems()) { int affected = stockMapper.deductStock(item.getProductId(), item.getQty()); if (affected == 0) { // 库存不足,抛异常触发整体回滚 throw new BizException("商品库存不足: " + item.getProductId()); } // 3. 写库存流水 stockMapper.insertRecord(item.getProductId(), item.getQty(), order.getId()); } } }

关键在deductStock的 SQL 要带条件,而不是先查再改:

<!-- StockMapper.xml --> <update id="deductStock"> UPDATE stock SET quantity = quantity - #{qty}, update_time = NOW() WHERE product_id = #{productId} AND quantity >= #{qty} </update>

WHERE quantity >= #{qty}是防超卖的核心,数据库行锁保证并发下只有一个事务能扣成功,返回影响行数为 0 就说明库存不够,抛异常让整个订单回滚。rollbackFor = Exception.class别漏,默认只回滚运行时异常,业务里抛的受检异常不回滚,订单写了库存没扣,账就乱了。这套写法是这类项目里最该吃透的一段,改业务时优先保住这个事务边界。

3.3 前端页面与接口联调

前端这边,商品列表、库存预警、订单管理通常是几个独立路由页面,用 axios 统一封装请求。联调阶段最常见的动作是改接口地址和看返回结构:

// src/utils/request.js import axios from 'axios' const request = axios.create({ baseURL: '/api', // 走 vue.config.js 里的代理 timeout: 10000 }) request.interceptors.response.use( res => { // 后端统一返回 { code, msg, data },这里按实际结构调整 if (res.data.code !== 200) { return Promise.reject(new Error(res.data.msg)) } return res.data.data }, err => Promise.reject(err) ) export default request

baseURL: '/api'和前面代理的pathRewrite是一对,改了一边必须改另一边。响应拦截器里判断业务码,把data层剥出来给页面用,页面里就不用每次res.data.data层层剥。联调时打开浏览器 Network 面板,看请求 URL、请求体、响应体三样,404 查代理和 Controller 路径,401 查登录态和拦截器,500 去后端控制台看堆栈,基本能定位八成问题。

4. 避坑与排查:跑不起来时先看这几处

这套项目在本地复现时,报错往往集中在几个固定位置。下面几条是血泪经验里出现频率最高的,按「现象 → 原因 → 解决」记,下次遇到直接对号入座。

4.1 启动报数据库连接失败

现象:后端启动时抛Communications link failure或Access denied for user。原因通常是 MySQL 没启动、端口不是 3306、密码错,或者application.yml里 url 的库名和实际建的不一致。解决:先用mysql -u root -p手动登一次确认服务活着,再核对 yml 里的 host、port、库名、账号密码四项,逐项对齐。MySQL 8 还要确认驱动类是com.mysql.cj.jdbc.Driver,老驱动类名会报找不到。

4.2 前端请求全部 404

现象:页面能打开,但所有接口都 404,Network 里请求地址看着也对。原因多半是pathRewrite配错,或者后端 Controller 的@RequestMapping前缀和前端baseURL叠加后多了一层。解决:在浏览器里复制一个失败请求的完整 URL,和后端 Controller 上标注的路径逐段比对,重点看/api有没有被重复拼接或该去没去。改完代理记得重启前端 dev server,代理配置不热更新。

4.3 中文乱码

现象:商品名、会员名在页面上显示成问号或方块。原因有三层:数据库字符集不是utf8mb4、连接 url 没带characterEncoding、前端页面 meta 没声明 UTF-8。解决:按「库 → 连接 → 页面」顺序查,建库时DEFAULT CHARACTER SET utf8mb4,url 带useUnicode=true&characterEncoding=utf8,HTML 模板里<meta charset="utf-8">,三层都对齐才不会漏。

4.4 库存扣成负数

现象:并发下单后库存出现负值。原因就是扣库存用了「先查再改」或者 update 没带quantity >= qty条件。解决:把扣减 SQL 改成带条件的原子更新,判断影响行数,为 0 就抛异常回滚。已经出现负库存的,写个脚本按stock_record流水重算一遍当前库存修正。

4.5 打包部署后接口 404 或白屏

现象:本地npm run serve正常,npm run build后丢到服务器上白屏或接口全挂。原因通常是前端打包后的静态资源路径不对,或者后端没配静态资源映射和 history 路由回退。解决:检查vue.config.js的publicPath,部署在子路径下要改成对应前缀;后端如果用 SpringBoot 托管前端产物,确认静态资源目录和index.html回退配置,刷新非根路由不 404。

5. 二次开发与验证:把模板改成自己的业务

跑通只是起点,这套源码真正的用法是当骨架往自己业务上改。改之前先做一件事:把「商品 → 库存 → 订单」这条主链路完整走一遍,用真实数据验证事务和库存扣减对不对,这是判断这套代码能不能用的硬标准。

验证方法很直接:造一个库存为 1 的商品,开两个浏览器标签同时提交订单,看是否只有一个成功、库存是否变成 0 而不是 -1、失败那单是否没有留下脏数据。这一步过了,说明事务和并发控制是可靠的,可以放心在它上面加业务。加业务时优先动「边缘表」,比如会员积分、供应商结算,别一上来就改订单和库存的核心逻辑,那两块牵涉面最广。

二次开发常见的扩展方向有三个:一是加报表,按日/按月统计销售额和毛利,SQL 从订单主从表聚合即可;二是加权限,把菜单和接口按角色拆开,Spring Security 或简单的拦截器都能做;三是接打印,把小票模板做成前端可配置。改的时候守住一条:任何涉及金额和库存的写操作,都要走事务、都要留流水,这是这类系统不出账务事故的底线。

我自己的习惯是,每次改完核心链路,都强制用「并发下单 + 库存对账」这一套走一遍再提交,宁可多花十分钟,也别等上线后对着负库存和错账熬夜排查。希望这套拆解能帮你少走几个弯路,把这份源码真正用起来。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进