ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot + Vue 前后端分离库存管理系统:从源码跑通到业务改造实践

Spring Boot + Vue 前后端分离库存管理系统:从源码跑通到业务改造实践 简介这是一套基于SpringBoot和Vue的库存管理系统源码采用B/S与MVC架构开发语言为Java可在Windows环境下运行。资源面向计算机相关专业在校学生、教师及企业学习者适合作为课程设计或毕业设计的参考项目。整个压缩包共431个文件大小21.16MB其中包含117个Java后端代码、60个Vue前端组件配合svg、png、jpg等图标图片与js、css页面样式以及xml、yml配置文件和bat运行脚本批处理脚本可辅助快速搭建启动环境整体前后端结构清晰完整。目前已有95人学习下载。项目代码均经过测试运行成功关键位置附有中文注释便于理解库存管理中的核心业务流程和编码思路既可直接启动体验也可在此基础上自行调试、扩展模块。需注意资源定位为参考资料而非定制需求适合有一定Java基础的学习者借鉴使用。1. 为什么这套 Spring Boot Vue 库存管理系统值得你花一个下午跑通如果你手头正压着一个 Java 课程设计、毕业设计或者公司小仓库还在用 Excel 记出入库那么“基于 Spring Boot 和 Vue 的库存管理系统源码”就是最划算的起步点。它前后端分离后端是 Spring Boot 提供 REST 接口前端是 Vue 页面调接口渲染表格代码里带中文注释照着改不费劲。这套东西能解决的核心问题很具体商品信息管理、入库出库登记、库存数量自动更新、简单统计报表。适合两类人——想交作业的学生和想用最低成本搭一套内部小系统的开发。花一个下午把它跑通你就同时看到了 Spring Boot 和 Vue 是怎么在真实项目里协作的后面改业务只是换表换字段的事。2. 把前后端跑起来数据库初始化、后端启动与 Vue 页面首次加载这是这套源码最基础也是最容易卡住的一步。很多人拿到代码先看业务逻辑结果环境没搭好连登录页都见不到后面的分析全无从谈起。我建议严格按照顺序来先看目录结构再初始化数据库然后启动后端最后启动前端。不要跳步不然你会分不清报错是后端问题还是前端问题。2.1 先看懂目录前后端分离的源码结构长什么样拿到源码先别急着运行花十分钟把目录结构过一遍。前后端分离的项目通常是一个根目录下放着两个子项目一个后端一个前端。这套库存管理系统也符合这个惯例。inventory-management/ ├── backend/ # Spring Boot 后端工程 │ ├── src/main/java │ │ └── com/inventory │ │ ├── controller/ # 接收前端请求的 REST 接口层 │ │ ├── service/ # 业务逻辑层比如库存扣减 │ │ ├── mapper/ # MyBatis 的数据库访问接口 │ │ ├── entity/ # 与数据库表对应的实体类 │ │ └── config/ # 拦截器、跨域等配置 │ ├── src/main/resources │ │ ├── mapper/ # MyBatis 的 XML 文件写 SQL 的地方 │ │ └── application.yml # 端口、数据库连接等配置 │ └── pom.xml ├── frontend/ # Vue 前端工程 │ ├── src │ │ ├── views/ # 页面组件比如商品列表、入库单 │ │ ├── router/ # 前端路由控制页面跳转 │ │ ├── api/ # 封装 axios 请求的接口文件 │ │ └── main.js # Vue 入口文件 │ ├── package.json │ └── vue.config.js # 开发代理配置解决跨域 └── sql/ └── inventory.sql # 数据库初始化脚本后端的关键在mapper和service两层controller只是薄薄一层转发。前端的关键在api目录所有后端请求都汇总在这里改接口地址只动一个文件。这里给你一个判断代码质量的技巧看一下entity里的字段是不是和数据库表字段一一对应再看service里有没有实际业务逻辑而不仅是 CRUD。库存系统真正值钱的逻辑在出库时的库存扣减和库存不足的校验如果这些写在controller里说明项目分层不清晰你拿它做二次开发会很难受。2.2 初始化数据库字符集、排序规则和建表顺序库存管理系统的数据库脚本通常会一次性建好所有表包括用户表、商品表、库存表、入库记录表、出库记录表。这一步最常见的坑是字符集没选对导致后面插入中文数据变成乱码。CREATE DATABASE IF NOT EXISTS inventory_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE inventory_db; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role VARCHAR(20) DEFAULT admin, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;执行建库脚本时注意utf8mb4和utf8mb4_general_ci这两个参数。utf8mb4是 UTF-8 的超集能存下生僻字和 Emojiutf8mb4_general_ci是通用的排序规则。如果你用旧版的 MySQL 5.5可能需要换成utf8但 MySQL 5.7 以上直接utf8mb4就好。关于执行顺序我建议先把整个 SQL 脚本在 Navicat 或命令行里完整跑一遍确认日志里没有报错再启动后端。有些脚本里带了外键约束如果表之间存在引用关系建表顺序错了会直接失败。如果脚本跑出Cannot add foreign key constraint优先看是不是前置表还没建。这个错误我在帮同事排查时见过不下十次。还有一个容易被忽略的细节登录账号的初始化数据通常在脚本最底部是一段INSERT INTO user。确认一下默认用户名和密码是什么以及密码是明文还是 MD5 加密存储。这决定了你第一次登录时该填什么。2.3 启动后端application.yml 里 3 个必改参数后端能不能在你这台机器上跑起来基本都写在application.yml里。这套源码的配置通常包含端口、数据库连接、MyBatis 映射文件路径三个关键部分。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/inventory_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.inventory.entity configuration: map-underscore-to-camel-case: true第一个必改参数是spring.datasource.url里的数据库名称如果建库时叫别的名字这里要同步改。第二个是username和password改成你本机 MySQL 的账号密码。第三个是server.port如果你的 8080 端口被占用改成 8081但前端代理也要跟着改。启动方式有两种。如果你装了 Maven在backend目录下执行mvn spring-boot:run如果你用 IDEA直接打开backend文件夹等待 Maven 依赖下载完运行主类里带SpringBootApplication注解的那个Application.java。这种方式好处是报错时能直接看 IDE 的控制台断点调试也方便更适合新手。启动成功的标志是控制台出现Tomcat started on port(s): 8080。如果这里报了Access denied for user root说明数据库账号密码不对如果报Unknown database说明库没建或者 URL 里的库名写错了。这些报错信息都写得很直白不要慌逐字读一下就能定位。2.4 启动前端npm install 到浏览器出现登录页后端起来了前端反而容易在依赖安装上翻车。Vue 项目的package.json里写了一大堆依赖npm install能不能一次过很大程度上取决于你的 Node.js 版本。cd frontend npm install npm run servenpm install装的是package.json里声明的依赖装完会生成node_modules目录。这个过程耗时取决于网络几分钟到十几分钟都正常。如果中途报错多数是网络问题导致的可以配置 npm 镜像源后再重试。npm config set registry https://registry.npmmirror.comnpm run serve启动开发服务器默认监听 8080 端口但前后端都是 8080 会冲突所以 Vue CLI 会自动换到 8081。启动成功的标志是控制台显示App running at Local: http://localhost:8081。这时的跨域问题由vue.config.js里的代理解决。常见配置是这样module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这里的含义是前端页面上所有以/api开头的请求都会转发到http://localhost:8080这个后端地址由后端处理。在前端代码里你看到的请求路径可能是/api/goods/list而实际上后端接口是http://localhost:8080/goods/list。这个映射关系如果对应不上接口就会 404。所以你在改前端接口时第一件事是去api目录看看封装的路径是不是带/api前缀再和后端的RequestMapping路径比对。浏览器里输入http://localhost:8081看到登录页这一步就算完成了。3. 读透核心业务代码库存管理系统的入库、出库与数据流转项目跑起来只是开始你真正要交付的是能讲清楚业务、能按需求改代码的能力。库存管理系统这套源码里最值得读的是登录态控制、表结构设计、库存扣减的事务逻辑以及前端页面如何跟后端接口对接。把这四块吃透面试被问“库存系统怎么设计的”你就有话可说。3.1 登录态怎么保持后端拦截器与前端路由守卫的分工库存系统不是所有页面都能直接访问得登录后才能操作。怎么判断用户有没有登录这套源码通常的做法是后端用拦截器校验 Token 或 Session前端用路由守卫控制页面跳转。Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); // 从 Token 里解析出用户信息没有则返回 401 if (token null || token.isEmpty()) { response.setStatus(401); return false; } // 这里可以加缓存查询验证 Token 是否有效 return true; } }这段代码是拦截器的核心逻辑每个请求进来先看请求头里有没有Authorization字段没有就直接返回 401前端拿到 401 就知道要跳回登录页。注意注释里提到的逻辑顺序先判空再查有效性顺序反了会多一次无意义的查询。对应的前端路由守卫在src/router里核心代码类似router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path ! /login !token) { next(/login) } else { next() } })前端只看本地有没有 Token不校验它是否过期因为校验是后端的事。这套分工明确前端管跳转后端管验证。你会看到一个整体逻辑登录成功时后端返回一个 Token前端存进localStorage之后每次 axios 请求都带上这个 Token。如果 Token 过期后端返回 401前端清掉本地 Token 并跳到登录页。3.2 盘点这 5 张核心表goods、stock、in_record、out_record、user库存管理系统再怎么复杂核心表也逃不过这五张。你在阅读源码时应该先看 SQL 脚本里的建表语句把表之间的关系捋清楚。这里我给出最典型的设计大部分库存源码都是在这个基础上加字段。表名核心字段作用userid, username, password, role登录用户role 区分管理员和普通操作员goodsid, name, spec, unit, price商品基础信息规格和单位是库存系统的关键stockid, goods_id, quantity, warn_stock库存数量一个商品对应一条记录in_recordid, goods_id, quantity, operator, create_time入库记录每一条都对应一次入库操作out_recordid, goods_id, quantity, operator, create_time出库记录与入库记录结构对称你可能注意到这里拆了两张表goods和stock而不是把库存数量直接写在商品表里。这个设计有两个原因一是商品信息和库存数量变化的频率不一样商品信息很少改库存数量每次出入库都在变二是拆开以后做库存流水追溯时只查stock表就够了不会把商品基础信息也拖出来。如果你拿到的源码把库存数量合在商品表里业务简单时没问题但每次查询都要多带一个字段统计也容易乱。3.3 库存扣减的事务代码为什么不能只写一条 update库存管理系统最核心的业务是入库和出库。入库时库存增加出库时库存减少同时要往记录表里插一条流水。这两个操作必须同时成功或同时失败否则就会出现库存变了但流水没记或者流水记了库存没变的尴尬情况。Spring Boot 里处理这个靠的是Transactional注解。Service public class StockServiceImpl implements StockService { Autowired private StockMapper stockMapper; Autowired private OutRecordMapper outRecordMapper; Override Transactional(rollbackFor Exception.class) public void outStock(Integer goodsId, Integer quantity, String operator) { // 1. 查库存判断是否够扣 Stock stock stockMapper.selectByGoodsId(goodsId); if (stock null || stock.getQuantity() quantity) { throw new RuntimeException(库存不足); } // 2. 扣减库存 stockMapper.decreaseStock(goodsId, quantity); // 3. 写入出库记录 OutRecord record new OutRecord(); record.setGoodsId(goodsId); record.setQuantity(quantity); record.setOperator(operator); outRecordMapper.insert(record); } }Transactional这个注解的作用是方法里所有数据库操作在同一个事务里任何一个步骤抛异常前面的操作全部回滚。举个例子decreaseStock执行成功但insert出库记录时数据库报错事务回滚后库存数量会恢复原样。这里有一个值得留意的细节判断库存够不够和扣减库存之间在高并发场景下存在时间差。两个请求同时读到库存是 10都判断够扣然后一起扣最后库存变成负数。这套源码如果追求简单可能不会处理这个问题但你去面试时可以主动提这个点说明自己知道并发控制。常见的优化方案是把判断放进 SQL 里比如用UPDATE stock SET quantity quantity - #{quantity} WHERE goods_id #{goodsId} AND quantity #{quantity}让数据库来保证原子性。3.4 Vue 列表页到表单提交一个完整业务的前端接线看完了后端再看前端怎么把这些接口串起来。商品管理页是最典型的例子页面加载时调用接口拿商品列表点击新增时弹出表单提交时再调用另一个接口。// src/api/goods.js import request from /utils/request export function getGoodsList(params) { return request({ url: /api/goods/list, method: get, params }) } export function addGoods(data) { return request({ url: /api/goods/add, method: post, data }) }这段代码封装了两个接口查询商品列表和新增商品。params是 GET 请求的查询参数data是 POST 请求的请求体。request是从/utils/request导入的 axios 实例通常在里面做了 baseURL 统一设置和 Token 拦截器。// src/views/GoodsList.vue核心片段 export default { data() { return { goodsList: [], queryParams: { page: 1, pageSize: 10, keyword: } } }, created() { this.loadGoods() }, methods: { async loadGoods() { const res await getGoodsList(this.queryParams) this.goodsList res.data.records }, async handleAdd() { await addGoods(this.form) this.loadGoods() } } }created()是 Vue 组件的生命周期钩子页面创建后自动调用loadGoods拉取数据。这里注意一点接口返回的数据结构通常是{ records: [...], total: 100 }这样的分页格式records才是列表数据total是总数。很多从没做过前后端分离的新手会在这里卡住——数据拿到了但页面上什么都不显示原因往往是取错了字段名。前端接线的思路是固定的先看api目录里的接口定义再看页面组件的created生命周期里调用了哪个方法最后在methods里看它怎么把返回值赋给data中的变量。三个环节对上了页面就出来了。4. 运行避坑地图新手跑这套源码最容易翻车的 5 个环节跑这套库存管理系统遇到的坑其实高度雷同基本都是环境问题而不是代码问题。这章我按踩坑频率从高到低列出 5 个每条都按现象、原因、解决三步写清楚。4.1 启动报错Public Key Retrieval is not allowed现象后端启动直接失败控制台报Public Key Retrieval is not allowed有时还伴随着Communications link failure。原因MySQL 8.0 默认使用caching_sha2_password认证插件JDBC 驱动连接时如果没有允许公钥检索就会拒绝连接。如果直接把application.yml里的驱动改成com.mysql.jdbc.Driver又会因为新旧驱动包名不一致报别的错。解决在 JDBC URL 末尾追加allowPublicKeyRetrievaltrue注意要用连接已有参数同时确认用的是com.mysql.cj.jdbc.Driver这个新版驱动类名。url: jdbc:mysql://localhost:3306/inventory_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue这条配置放进去之后重启后端就不会再报这个错。我记得第一次遇到时根本没往加密插件上想还以为自己密码写错了折腾了半天。现在看到这个报错基本秒懂。4.2 前端页面渲染出来了但登录时接口 404现象浏览器能打开登录页输入密码点登录控制台显示请求地址 404或者接口请求直接失败。原因大概率是前端代理和后端实际接口路径对不上。前端代理把/api开头的请求转发给后端但后端的RequestMapping路径里可能本身就带了/api也可能是完全没有/api前缀。如果前端/api/goods/list被代理到后端localhost:8080/api/goods/list而后端接口是localhost:8080/goods/list就会 404。解决先在后端控制台确认接口实际路由。更快的办法是直接看浏览器开发者工具里请求的完整 URL然后对比后端controller类上的RequestMapping前缀。如果后端路径里没有/api那就去掉vue.config.js代理里的/api前缀或者在后端的 Controller 上加一个/api前缀让两边保持一致。这里没有玄学就是逐字对比路径。4.3 建表脚本执行时报Cannot add foreign key constraint现象执行 SQL 脚本建表时某张表创建失败报外键约束相关的错误后面的表也全部没建。原因脚本里有一个很典型的顺序问题——子表先建父表后建。比如stock表引用了goods表的id但goods表还没建出来外键约束自然创建不了。有的脚本注释里会标注建表顺序但很多仓库代码并不保证顺序。解决如果脚本是完整的一个文件不能手动拆开执行看看脚本开头有没有SET FOREIGN_KEY_CHECKS 0;这条语句。临时关闭外键检查可以绕过建表顺序问题不影响后续使用。没有的话就手动拆分脚本先建父表再建子表。拆的时候注意商品表、用户表是第一梯队库存表和出入库记录表是第二梯队。SET FOREIGN_KEY_CHECKS 0; -- 原脚本内容 SET FOREIGN_KEY_CHECKS 1;4.4 前端npm install一直卡住或报 ERESOLVE现象npm install执行后长时间没有进度或者直接报ERESOLVE unable to resolve dependency tree。原因Node.js 版本跟项目依赖不兼容最常见的是 Node 17 以上安装老版本 Vue CLI 项目时触发依赖解析冲突另一个是高并发请求 npm 官方源被限制导致下载慢。搜一下这类的报错对话你会发现问题大多集中在版本上。解决先看package.json里的 Vue 版本和 Vue CLI 版本对 Node 的要求一般 Vue CLI 4 建议 Node 14 或 16Vue CLI 5 可以上 Node 18。ERESOLVE报错时可以试试加--legacy-peer-deps参数安装这个参数的作用是跳过自动检查 peerDependencies 版本冲突很多老项目靠它就能装过。npm install --legacy-peer-deps如果项目比较老Node 版本高于 17建议直接装一个 Node 16用 nvm 切版本再装一次省得以后每次装依赖都提心吊胆。4.5 端口被占用导致启动失败现象后端或前端启动时日志里直接报Port 8080 was already in use或者EADDRINUSE。原因很简单你机器上别的程序把端口占了。常见占 8080 的有本机其他 Java 进程、IDE 内置终端、甚至以前启动还没关掉的服务。解决确认哪个进程占用了端口然后按需处理。在 Windows 上用netstat -ano | findstr :8080在 macOS 或 Linux 上用lsof -i :8080。查到进程 PID 后确认不是重要系统服务再结束它。如果你不想动那个进程改application.yml里的端口也一样。前端 8081 被占同理改vue.config.js里的port即可。# Windows 查找 8080 端口占用 netstat -ano | findstr :8080 # Linux / macOS 查找 8080 端口占用 lsof -i :80805. 把源码改成你自己的项目三个低成本落点与我的维护习惯跑通和读懂只是第一步你最终要把这套源码变成“自己的项目”。这不需要重写只需要在几个安全的位置动手改就能在答辩或内部演示时理直气壮地说这是我的。第一个落点是改前端的品牌信息把index.html里的标题、登录页的 Logo 和系统名称换掉再调一下src/assets里的颜色变量两三个文件就能让界面不复刻原版。第二个落点是给库存列表加一个置顶搜索框前端加一个keyword参数后端在goodsMapper.xml里加一个模糊查询条件整体改动量不大但在演示时很加分而且能体现你理解了 MyBatis 的动态 SQL。第三个落点是把统计页从假数据换成真实接口在controller里加一个聚合查询方法用SUM和GROUP BY从in_record和out_record表汇总出入库数量这一步能讲十分钟。我处理这类源码的习惯是先跑通再备份后改动。拿到代码先原样跑起来确认可用后立刻把整个目录打一个 zip 备份后面改乱了大不了回滚到最初状态等于有了后悔药。改动时坚持一次只动一个功能点改完跑一遍再动下一个不然前后端同时改出错了根本不知道是哪边的问题。这套库存管理系统本身不复杂按这个节奏推进大概两三天就能交出一个像样的作品期间学到的东西也比直接“跑通就交差”多得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表