
简介这份资源是一套基于Java、Spring Boot与Vue.js构建的WMS仓库管理系统并集成微信小程序端面向计算机相关专业的毕业设计学生及需要仓库管理实战项目的开发者。系统覆盖入库、出库、库存管理、订单处理等核心业务Web端与小程序端双端协同用户可通过微信小程序随时查看库存、下单与追踪物流适合作为毕设选题或全栈学习参考。压缩包为zip格式整体约12.24MB内含后端Java源码、Spring Boot配置文件、Vue组件、数据库SQL脚本、微信小程序配置及需求分析与设计文档等覆盖软件开发全流程。目前已有217人学习下载。读者可从中获取完整的项目结构、前后端接口设计思路与数据库表结构对照文档理解需求分析到编码测试的各个环节并借鉴小程序与后端联调的实践方式为自身毕设或类似系统开发提供可复用的参考方案。1. 从一张 Excel 库存表到扫码入库WMS 仓库管理系统到底在解决什么很多中小仓库的日常是这样的入库靠一张 Excel 表出库靠微信群喊一声月底盘点发现账实差异几十件谁也说不清货去哪了。基于 Java Spring Boot Vue 的 WMS 仓库管理系统加上一个微信小程序扫码端解决的正是这个场景——把「收货、上架、拣货、复核、盘点」这几个动作从纸面搬到系统里让每一次库存变动都有单据、有操作人、有时间戳。它适合谁适合有 13 个仓库、SKU 在几百到几千量级、想自己掌控源码做二次开发的团队也适合 Java 后端工程师拿它练手一个完整的前后端分离项目。核心链路其实就一句话Spring Boot 提供 REST 接口和库存事务Vue 做 PC 端管理后台微信小程序做现场扫码作业三者共用一套库存数据。2. 技术选型与整体架构为什么是 Spring Boot Vue 小程序三件套2.1 后端为什么选 Spring Boot 而不是传统 SSMWMS 的业务特点是「事务密集 单据状态流转多」。一次入库要同时写库存主表、库存流水、入库单状态任何一步失败都得回滚。Spring Boot 的声明式事务Transactional配合 MyBatis 或 MyBatis-Plus能把这类多表操作收敛在一个 service 方法里比手写 XML 配置的 SSM 省掉大量样板代码。另一个现实原因是生态分页用 PageHelper、权限用 Spring Security 或 Sa-Token、定时任务用Scheduled做库存预警这些都是现成轮子。选型时要注意版本搭配。热搜里常出现「springboot版本太高」的抱怨根源多是 JDK 版本和依赖不匹配。我一般这样定基线组件推荐版本区间说明JDK8 / 11 / 1717 是当前 LTS新项目优先Spring Boot2.7.x 或 3.x3.x 要求 JDK 17别混用MyBatis-Plus3.5.x与 Boot 3 需用对应 starterMySQL5.7 / 8.08.0 注意时区和驱动类名Vue2.7 或 3.x2.7 兼容选项式写法迁移成本低提示Spring Boot 3 把javax.*换成了jakarta.*如果你抄的教程还是javax.servlet启动就会报类找不到这是新手最常见的翻车点。2.2 前端 Vue 与小程序的分工边界一个常见误区是「有了小程序就不需要 PC 后台」。实际上两者的使用场景完全不同PC 端 Vue 后台负责商品档案维护、入库单审核、报表导出、权限配置这类重操作微信小程序负责现场扫码——收货员拿手机扫商品条码确认数量拣货员扫库位码核对。小程序不适合做复杂表格和批量操作PC 端不适合拿着手机在货架间走动。所以架构上小程序和 Vue 后台调用的是同一套后端接口只是权限角色不同。后端用 JWT 或 Token 区分登录来源小程序端登录走wx.login换 openidPC 端走账号密码。这样库存数据只有一份不会出现「小程序改了 PC 端看不到」的脏数据。2.3 数据库表设计的核心几张表WMS 的表不用多但几张核心表必须设计对否则后期改起来很痛wms_goods商品档案含 SKU 编码、条码、规格、单位wms_warehouse/wms_location仓库与库位库位编码是扫码的关键wms_stock库存主表字段是「商品 仓库 库位 数量」唯一索引建在这三者上wms_stock_record库存流水每次增减都插一条用于追溯wms_inbound_order/wms_outbound_order入库单、出库单及明细库存主表的唯一索引是重点。很多新手把库存直接存在商品表的一个quantity字段里结果多库位场景直接崩掉。正确做法是库存按「商品 库位」维度存汇总数量靠SUM查询或缓存。3. 后端落地库存事务、单据状态机与接口设计3.1 用 Spring Boot 写一个不会超卖的入库接口库存操作最怕并发。两个人同时拣同一批货如果不加锁就会出现超卖。下面是一个入库接口的核心写法用乐观锁或行锁保证数量正确Service public class StockService { Autowired private WmsStockMapper stockMapper; Autowired private WmsStockRecordMapper recordMapper; /** * 入库增加指定库位的库存并写一条流水 * param goodsId 商品ID * param locationId 库位ID * param qty 入库数量必须为正 */ Transactional(rollbackFor Exception.class) public void inbound(Long goodsId, Long locationId, Integer qty) { if (qty null || qty 0) { throw new BizException(入库数量必须大于0); } // 先查库存行加行锁SELECT ... FOR UPDATE WmsStock stock stockMapper.selectForUpdate(goodsId, locationId); if (stock null) { // 首次入库初始化一行 stock new WmsStock(); stock.setGoodsId(goodsId); stock.setLocationId(locationId); stock.setQuantity(qty); stockMapper.insert(stock); } else { stock.setQuantity(stock.getQuantity() qty); stockMapper.updateById(stock); } // 写流水记录变动前后 WmsStockRecord record new WmsStockRecord(); record.setGoodsId(goodsId); record.setLocationId(locationId); record.setChangeQty(qty); record.setType(INBOUND); record.setCreateTime(new Date()); recordMapper.insert(record); } }逻辑说明整个方法包在Transactional里库存更新和流水写入要么都成功要么都回滚。selectForUpdate对应 SQL 的SELECT ... FOR UPDATE在事务内锁住这一行防止并发修改。参数qty做了正数校验避免有人传负数把入库变成出库。参数怎么调如果并发量不大中小仓库通常如此行锁足够如果 QPS 高可以改用乐观锁版本号字段更新时WHERE version #{version}失败就重试。rollbackFor Exception.class不能省否则受检异常不会触发回滚这是血泪经验。3.2 单据状态机别让入库单卡在「待审核」WMS 的单据有状态流转待审核 → 已审核 → 部分入库 → 已完成 / 已取消。状态机写不好就会出现「单子审核了但库存没加」这种玄学问题。我的做法是把状态变更收敛到一个方法里用枚举定义合法流转public enum InboundStatus { PENDING, // 待审核 APPROVED, // 已审核 PARTIAL, // 部分入库 FINISHED, // 已完成 CANCELED; // 已取消 // 判断当前状态能否流转到目标状态 public boolean canTransferTo(InboundStatus target) { switch (this) { case PENDING: return target APPROVED || target CANCELED; case APPROVED: return target PARTIAL || target FINISHED || target CANCELED; case PARTIAL: return target PARTIAL || target FINISHED; default: return false; } } }逻辑说明canTransferTo把合法流转规则集中在一处service 层调用前先判断非法流转直接抛异常。这样即使前端传了错误的状态值后端也能拦住。参数上PARTIAL可以自流转多次部分入库FINISHED和CANCELED是终态不能再变。3.3 接口设计给小程序和 Vue 共用一套 REST 规范后端接口要同时服务 PC 和小程序所以返回结构必须统一。我一般用这样的响应体{ code: 200, msg: success, data: { } }分页接口统一传pageNum、pageSize返回total和list。小程序端因为网络可能不稳定接口要做幂等——比如入库提交带一个前端生成的requestId后端用它做去重防止用户连点两次提交导致重复入库。这个requestId存 Redis 设 5 分钟过期即可不需要落库。4. 前端与小程序Vue 后台和扫码端的实现要点4.1 Vue 后台路由、权限与打包进 Spring BootVue 后台的骨架是登录页 布局页 各业务页。路由用vue-router权限控制用路由守卫登录后拿到角色动态生成可访问的路由表。热搜里「vue路由」「vue插槽」「vue打包放进springboot中」都是高频问题这里说打包这一步。Vue 打包后是静态文件放进 Spring Boot 的src/main/resources/static目录然后配一个转发规则让非接口请求都指向index.htmlConfiguration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 静态资源目录 registry.addResourceHandler(/**) .addResourceLocations(classpath:/static/); } }逻辑说明前端路由是 history 模式时刷新页面会请求后端如果后端没有对应接口就 404。解决办法是在 Nginx 或 Spring Boot 里配置 fallback 到index.html。参数上addResourceLocations指向打包产物所在目录注意结尾的斜杠不能少。4.2 微信小程序扫码端登录与手机号获取小程序端第一步是登录。流程是小程序调wx.login拿 code → 传给后端 → 后端用 code 换 openid 和 session_key → 生成自己的 token 返回。热搜里「微信小程序登录获取手机号」也是常见需求获取手机号需要用户点击授权按钮后端拿code调接口解密注意这个接口需要企业主体的小程序才能用。// 小程序端登录 wx.login({ success(res) { if (res.code) { wx.request({ url: https://your-domain/api/wx/login, method: POST, data: { code: res.code }, success(r) { // 存 token后续请求带上 wx.setStorageSync(token, r.data.data.token) } }) } } })逻辑说明wx.login的 code 只能用一次且 5 分钟过期所以必须马上传给后端。后端换 openid 时用的是小程序的appid和secret这两个值放配置文件不要写死在代码里。参数上wx.request的域名必须在小程序后台配置为合法域名否则真机调试会失败——这是新手最容易卡住的地方。4.3 扫码入库小程序调扫码 API 的完整链路扫码是小程序端的核心。用wx.scanCode拿到条码内容再调后端接口查商品或库位wx.scanCode({ onlyFromCamera: true, // 只允许相机扫码防止从相册选图作弊 scanType: [barCode, qrCode], success(res) { // res.result 是扫到的内容比如商品条码 wx.request({ url: https://your-domain/api/goods/byBarcode, data: { barcode: res.result }, header: { Authorization: wx.getStorageSync(token) }, success(r) { if (r.data.code 200) { // 展示商品信息让用户填数量 this.setData({ goods: r.data.data }) } else { wx.showToast({ title: 未找到商品, icon: none }) } } }) } })逻辑说明onlyFromCamera: true强制走相机避免有人从相册选一张条码图反复提交。scanType限定条码类型减少误扫。参数上res.result就是条码字符串后端按条码查商品档案。如果扫的是库位码就换成查库位接口逻辑一样。5. 避坑与排查WMS 上线后最容易翻车的 5 个地方5.1 库存对不上先查流水再看主表现象盘点时发现系统库存和实物差几件。原因多半是某次操作只更新了主表没写流水或者流水写了但事务回滚了主表没回滚。解决库存主表只作为「当前快照」任何对账都以wms_stock_record流水为准用流水SUM出来的数量去核对主表差异行就是问题点。排查时按商品 库位分组查流水比盯着主表看有效得多。5.2 小程序真机请求失败域名和 HTTPS 没配现象开发者工具里一切正常真机预览接口全部报错。原因小程序要求所有请求域名必须 HTTPS 且在小程序后台配置为合法域名开发者工具可以勾选「不校验合法域名」绕过真机不行。解决把后端接口挂到有证书的域名上在小程序后台「开发设置」里填 request 合法域名。本地调试阶段可以用内网穿透工具临时给个 HTTPS 地址但上线必须用正式域名。5.3 Spring Boot 打包后静态资源 404现象本地npm run dev正常打包放进 Spring Boot 后页面白屏或 404。原因Vue 打包的publicPath默认是/如果部署在子路径下就找不到资源或者 history 模式刷新 404。解决vue.config.js里设publicPath: ./相对路径后端配 fallback 到index.html。另外确认打包产物确实复制到了static目录Maven 打包时target/classes/static里要有文件。5.4 并发入库导致数量错乱现象两个收货员同时扫同一商品入库最终数量比实际少。原因没用行锁或乐观锁两个事务读到同一个旧值再各自加。解决如 3.1 所述用SELECT ... FOR UPDATE锁行或加 version 字段做乐观锁。中小仓库并发低行锁足够如果锁等待严重再考虑把库存扣减改成 Redis 原子操作 异步落库但这样一致性要额外处理不建议新手一上来就做。5.5 单据状态乱跳前端传了非法状态现象入库单从「待审核」直接变成「已完成」跳过了审核。原因后端接口没校验状态流转前端传什么就存什么。解决所有状态变更走 3.2 的状态机校验非法流转抛异常。另外前端按钮要根据状态置灰但后端校验不能省——前端可以绕过后端是最后一道防线。6. 进阶技巧用库存流水做一套可追溯的对账报表系统跑起来之后真正体现价值的是「能查账」。我一般会在流水表基础上做两个视图或查询一个是按商品 库位的实时库存一个是按时间段的出入库汇总。实时库存可以直接查主表但为了验证准确性我会写一个对账 SQL用流水汇总去比对主表-- 用流水汇总核对库存主表找出差异 SELECT r.goods_id, r.location_id, SUM(CASE WHEN r.type INBOUND THEN r.change_qty WHEN r.type OUTBOUND THEN -r.change_qty ELSE 0 END) AS flow_qty, s.quantity AS stock_qty FROM wms_stock_record r LEFT JOIN wms_stock s ON r.goods_id s.goods_id AND r.location_id s.location_id GROUP BY r.goods_id, r.location_id, s.quantity HAVING flow_qty IFNULL(stock_qty, 0);逻辑说明这条 SQL 把每条流水的增减量按商品 库位汇总再和库存主表比对HAVING只输出不一致的行。参数上type字段的取值要和代码里写入时保持一致否则汇总会漏。建议每天凌晨跑一次结果推送给管理员差异行人工核查。再进一步可以给流水表加操作人、单据号字段这样任何一笔库存变动都能追溯到「谁、在什么时候、因为哪张单子」改的。小程序端扫码时把操作人 token 带上后端从 token 解析用户 ID 写入流水。这套追溯能力在客户投诉「货少了」的时候特别有用能直接定位到具体环节而不是全仓库翻一遍。我自己踩过的坑是早期图省事流水表只记了商品和数量没记库位结果多库位场景下对账完全没法做只能加字段重刷历史数据。所以表设计阶段宁可多留两个字段也别等上线后补。做 WMS 这行库存准不准是命根子流水就是你的后悔药。希望帮到你。本文还有配套的精品资源点击获取