简介这是一套基于JavaSpringBootVue并配套微信小程序的WMS仓库管理系统完整源码面向计算机相关专业学生及企业开发人员可用于毕业设计、课程设计、大作业或初期项目立项演示也适合作为前后端分离与小程序开发的实战学习案例。资源包共约2000个文件整体约50.71MB其中Java源码473个、编译类文件452个另有Vue组件140个、JavaScript脚本238个、SQL脚本60个及大量png、gif、svg等界面素材涵盖后端服务、前端页面、数据库脚本与静态资源等完整结构。系统功能覆盖物料数据管理、物料Bom管理、物料组管理、物料分类管理以及供应商管理等核心模块代码经过测试可正常运行便于读者理解仓储业务逻辑与前后端交互流程。目前已有489人学习下载适合希望快速上手SpringBootVue项目、积累完整项目经验的学习者参考借鉴。1. 从一张 Excel 物料表到跑起来的 WMS这套 JavaSpringbootVue微信小程序方案到底解决什么问题很多中小制造企业和贸易公司的仓库管理起点往往是一张 Excel 物料表物料编码、规格型号、供应商、安全库存、当前库存几列数据靠人工维护。订单一多采购、仓管、财务各拿一份表版本对不上缺料停线、重复采购、账实不符就成了常态。这套基于 Java Springboot Vue 的 WMS 仓库管理系统加上一个微信小程序端瞄准的正是这个阶段的需求——把物料数据管理、供应商管理、出入库流水从表格搬到有权限、有校验、有留痕的系统里同时让仓管用手机扫码就能完成收发货不必守在电脑前。它适合谁一是想拿一个完整项目练手 Java 全栈的开发者Springboot 做后端、Vue 做管理后台、微信小程序做移动作业端技术栈覆盖了当前招聘市场上出现频率最高的组合二是中小仓库的实际管理者需要一套能自己部署、能改字段、能对接现有流程的轻量系统而不是买一套笨重的商业软件。整份源码加说明的价值不在于功能多全而在于它把「物料主数据 供应商 出入库 小程序扫码」这条最小闭环跑通了你可以顺着它理解一个 WMS 的骨架再按自己业务往里填肉。下面从环境搭建讲到核心表设计再到小程序端联调和踩坑尽量给到能直接抄的步骤和参数。2. 环境搭建与技术选型为什么是 Springboot Vue 微信小程序这套组合2.1 后端选 Springboot 而不是传统 SSM 的理由先说服自己为什么用这套栈。传统 SSMSpring SpringMVC MyBatis配置量大一个 WMS 里物料、供应商、出入库、库存流水、用户权限这些模块光 XML 就能写到你怀疑人生。Springboot 的自动装配把数据源、事务、Web 容器这些默认配好你只需要在application.yml里改连接信息剩下的精力放在业务上。对 WMS 这种「表多、CRUD 密集、还要留操作日志」的系统Springboot 配合 MyBatis-Plus 能省掉大量样板代码分页、逻辑删除、字段自动填充都有现成插件。版本上我一般建议 JDK 8 或 JDK 11 起步Springboot 用 2.7.x 这条稳定线别一上来追 3.x3.x 要求 JDK 17很多老服务器和中间件还没跟上容易在部署环节翻车。数据库 MySQL 5.7 或 8.0 都行字符集统一用utf8mb4物料名称里出现生僻字或特殊符号不会乱码。构建工具用 Maven依赖管理清晰团队协作不容易出岔子。2.2 前端 Vue 与小程序端的分工管理后台用 Vue是因为 WMS 后台大量是表格、表单、弹窗Vue 的响应式和组件化写这类界面效率高。Vue 2 配 Element UI或者 Vue 3 配 Element Plus两套都成熟。物料数据管理这种「一屏几十列、要筛选要导出」的页面Element 的 Table 组件开箱即用省去自己造轮子。路由用 vue-router状态管理用 Vuex 或 Pinia权限控制靠路由守卫加后端返回的菜单树。微信小程序端承担的是移动作业场景仓管拿手机扫物料条码或二维码直接查库存、做收货、做发货。小程序不需要安装扫码即用对一线工人友好。这里要注意小程序端只做「轻操作」复杂的物料建档、供应商维护还是放后台别把小程序做成一个缩小版后台体验会很差。前后端通过 RESTful 接口通信统一用 JSONtoken 放在请求头里做鉴权。2.3 从零把后端跑起来的最小命令下面这段是后端初始化的关键步骤假设你已经装好 JDK 和 Maven。先建库再改配置最后启动。# 1. 创建数据库字符集用 utf8mb4 mysql -u root -p -e CREATE DATABASE wms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 2. 导入初始化脚本脚本里含物料、供应商、库存等基础表 mysql -u root -p wms sql/wms_init.sql # 3. 进入后端目录修改 application.yml 里的数据库账号密码 # 4. 编译并启动 mvn clean package -DskipTests java -jar target/wms-backend-1.0.0.jar --spring.profiles.activedev逻辑说明第一步建库时指定utf8mb4是为了让物料规格里的特殊字符、供应商名称里的多语言都能存。第二步导入的 SQL 脚本通常包含建表和少量字典数据比如物料分类、计量单位。第三步的application.yml里重点改三处spring.datasource.url指向你的库、username和password换成自己的、server.port默认 8080 如果被占用就改。第四步用dev环境启动方便看详细日志。启动成功后访问http://localhost:8080/doc.html如果集成了 Knife4j或对应接口文档地址能看到接口列表就说明后端通了。参数上有个容易忽略的点连接池。默认 HikariCP 的maximum-pool-size是 10WMS 并发不高够用但如果小程序端多人同时扫码建议调到 20 并观察数据库连接数。connection-timeout保持 30 秒别设太短否则网络抖动时频繁报连接超时。2.4 前端 Vue 环境配置与依赖安装前端这边Node.js 版本建议 16.x 或 18.x LTS别用太新的奇数版本某些依赖编译会报错。装完 Node 后配置镜像加速再装依赖。# 查看 node 和 npm 版本确认环境 node -v npm -v # 设置 npm 镜像国内下载依赖快很多 npm config set registry https://registry.npmmirror.com # 进入前端目录安装依赖 cd wms-web npm install # 本地启动开发服务器 npm run serve逻辑说明npm install会读取package.json把 Vue、Element、axios、vue-router 等依赖装到node_modules。如果安装过程中卡在某个包多半是网络问题换镜像或重试即可。npm run serve启动后默认在 8081 或 8082 端口具体看vue.config.js里的devServer.port。前端请求后端的地址通常在.env.development里配置比如VUE_APP_BASE_API /api再通过vue.config.js的proxy代理到 8080这样开发时不会有跨域问题。提示前端依赖装完后如果启动报Node Sass或node-sass相关错误是 Node 版本和 sass 版本不匹配换成sassdart-sass或降 Node 版本即可这是 Vue 老项目最常见的翻车点之一。3. 物料数据管理与供应商模块表结构怎么设计才不返工3.1 物料主数据表的字段取舍物料数据管理是整个 WMS 的地基表设计错了后面全是坑。核心表wms_material我一般会放这些字段物料编码material_code唯一索引、物料名称material_name、规格型号spec、计量单位unit、物料分类category_id、安全库存safe_stock、当前库存stock_qty、状态status、创建时间、更新时间。物料编码必须唯一这是所有出入库流水的关联键别用自增 ID 当业务编码否则换系统时数据迁移会哭。分类字段用单独的wms_material_category表做树形结构支持多级分类比如「原材料 → 五金件 → 螺丝」。安全库存是预警的阈值低于它后台要标红提醒。当前库存这个字段有争议有人主张实时算有人主张冗余存储。我的经验是冗余存储 出入库时同步更新因为 WMS 查询库存的频率远高于出入库实时算流水表在数据量大时会拖慢列表页。但冗余就有对不上的风险所以要配一个定时对账任务每天凌晨用流水重算一遍库存发现差异就告警。3.2 供应商表与物料的多对多关系供应商表wms_supplier放供应商编码、名称、联系人、电话、地址、状态。物料和供应商之间通常是多对多——一个物料可以从多个供应商采购一个供应商供多种物料。所以需要一张中间表wms_material_supplier字段包括物料 ID、供应商 ID、供货价、供货周期、是否默认供应商。这张表在采购下单时特别有用能直接带出默认供应商和参考价。建表时注意外键约束。生产环境我一般不建议加数据库级外键用应用层保证一致性因为外键在批量导入和删除时会带来锁和性能问题。但要在字段上建索引wms_material_supplier的material_id和supplier_id都要有索引否则关联查询会全表扫描。3.3 用 MyBatis-Plus 写物料分页查询物料列表页需要支持按名称、编码、分类筛选还要分页。用 MyBatis-Plus 的Page对象和LambdaQueryWrapper能写得很干净。// MaterialServiceImpl.java public PageMaterialVO pageQuery(MaterialQuery query) { // 构造分页对象当前页和每页条数来自前端 PageMaterial page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperMaterial wrapper new LambdaQueryWrapper(); // 物料编码模糊匹配 wrapper.like(StringUtils.hasText(query.getMaterialCode()), Material::getMaterialCode, query.getMaterialCode()); // 物料名称模糊匹配 wrapper.like(StringUtils.hasText(query.getMaterialName()), Material::getMaterialName, query.getMaterialName()); // 分类精确匹配 wrapper.eq(query.getCategoryId() ! null, Material::getCategoryId, query.getCategoryId()); // 只查未删除的逻辑删除字段 status1 表示有效 wrapper.eq(Material::getStatus, 1); wrapper.orderByDesc(Material::getUpdateTime); // 执行分页查询 PageMaterial result this.page(page, wrapper); // 转成 VO补充分类名称等展示字段 return result.convert(this::toVO); }逻辑说明Page对象封装了分页参数MyBatis-Plus 会自动拼LIMIT。LambdaQueryWrapper用方法引用代替字符串字段名重构时不会因为改字段名而漏改 SQL。like用于模糊搜索eq用于精确匹配条件里第一个参数是布尔值为 false 时该条件不拼进 SQL这样就能动态组合查询。orderByDesc按更新时间倒序最新维护的物料排前面。参数说明pageNum从 1 开始pageSize建议限制上限比如最大 100防止前端传个 10000 把数据库拖垮。status用逻辑删除删除时只改状态不物理删保留历史数据供流水关联。如果物料量很大like %关键词%会导致索引失效可以考虑全文索引或搜索引擎但中小仓库几万条数据用 like 完全够。3.4 供应商与物料的关联维护接口给物料绑定供应商时前端传物料 ID 和一组供应商 ID 加供货信息后端先删旧关联再批量插入保证幂等。Transactional(rollbackFor Exception.class) public void bindSuppliers(Long materialId, ListMaterialSupplierDTO list) { // 先清空该物料已有的供应商关联 materialSupplierMapper.delete( new LambdaQueryWrapperMaterialSupplier() .eq(MaterialSupplier::getMaterialId, materialId)); // 再批量插入新的关联 for (MaterialSupplierDTO dto : list) { MaterialSupplier ms new MaterialSupplier(); ms.setMaterialId(materialId); ms.setSupplierId(dto.getSupplierId()); ms.setSupplyPrice(dto.getSupplyPrice()); ms.setSupplyCycle(dto.getSupplyCycle()); ms.setIsDefault(dto.getIsDefault()); materialSupplierMapper.insert(ms); } }逻辑说明Transactional保证删和插在同一个事务里中途失败会回滚不会出现关联被清空却没插入新数据的尴尬。先删后插是最简单的幂等方案适合关联数量不大的场景。如果关联数量上千可以改成差异更新但中小 WMS 没必要。参数说明supplyPrice用BigDecimal存别用double金额计算会有精度问题。isDefault用 0/1 表示一个物料最多一个默认供应商这个约束在应用层校验插入前检查是否已有默认。supplyCycle是供货周期天数采购计划可以参考它算到货时间。4. 微信小程序端扫码出入库接口联调与真机调试4.1 小程序扫码能力的接入方式小程序端核心是扫码。微信提供了wx.scanCode接口能扫二维码和条形码返回扫码内容。仓管扫物料上的条码拿到物料编码再调后端接口查库存或发起出入库。这里有个前提物料条码得先打印出来贴到货架上条码内容就是物料编码这样扫出来的字符串直接能当查询条件。小程序调用后端接口要注意域名白名单。开发阶段可以在微信开发者工具里勾选「不校验合法域名」但真机预览和上线必须在微信公众平台配置request合法域名且必须是 HTTPS。本地开发时后端是 HTTP真机就连不上常见做法是用内网穿透工具把本地服务映射成一个 HTTPS 域名或者直接部署到测试服务器。这一步卡住过很多人属于典型的「代码没问题但就是连不上」。4.2 扫码查询库存的完整调用链下面是小程序端扫码后查询库存的代码包含扫码、请求、结果处理。// pages/scan/scan.js Page({ data: { material: null, loading: false }, // 触发扫码 onScan() { wx.scanCode({ onlyFromCamera: true, // 只允许相机扫码防止从相册选图作弊 scanType: [barCode, qrCode], success: (res) { // res.result 就是条码内容这里约定为物料编码 this.queryMaterial(res.result); }, fail: () { wx.showToast({ title: 扫码取消, icon: none }); } }); }, // 根据编码查物料和库存 queryMaterial(code) { this.setData({ loading: true }); wx.request({ url: getApp().globalData.baseUrl /api/material/scan, method: GET, data: { code: code }, header: { Authorization: wx.getStorageSync(token) }, success: (res) { if (res.data.code 200) { this.setData({ material: res.data.data }); } else { wx.showToast({ title: res.data.msg || 未找到物料, icon: none }); } }, fail: () { wx.showToast({ title: 网络异常, icon: none }); }, complete: () { this.setData({ loading: false }); } }); } });逻辑说明onlyFromCamera: true强制用相机扫避免有人从相册选一张旧条码图来蒙混。scanType限定条码和二维码。扫码成功后拿res.result作为物料编码去请求后端。请求头带 token 做鉴权token 在登录后存到本地缓存。后端返回 200 且 data 有值就渲染物料信息否则提示未找到。参数说明baseUrl建议放在app.js的globalData里统一管理切换环境时只改一处。Authorization头的格式要和后端约定好常见是Bearer xxx或直接放 token前后端必须一致否则一直 401。wx.request默认超时 60 秒可以按需调整但扫码查询这种操作建议 10 秒内返回超时就提示重试。4.3 出入库提交与库存并发问题扫码查到物料后仓管输入数量、选择出入库类型提交到后端。这里有个必须处理的并发问题两个仓管同时对同一物料做出库如果都先读库存再减可能超卖。正确做法是在 SQL 层面用条件更新。-- 出库时扣减库存条件里带上库存充足判断 UPDATE wms_material SET stock_qty stock_qty - #{qty}, update_time NOW() WHERE id #{materialId} AND stock_qty #{qty};逻辑说明这条 SQL 把「库存是否充足」的判断和扣减放在同一条语句里数据库的行锁保证同一物料的并发更新串行执行。执行后检查影响行数如果为 0说明库存不足事务回滚并提示。这比先SELECT再UPDATE可靠得多后者在并发下必然出问题。参数说明#{qty}是出库数量必须大于 0在应用层校验。update_time用数据库时间而不是应用服务器时间避免多台服务器时钟不一致。如果业务允许负库存比如先出后补去掉stock_qty #{qty}条件即可但要评估是否真的需要。4.4 真机调试与缓存时间设置小程序真机调试时接口返回的数据有时会被缓存导致库存显示不更新。微信小程序的wx.request本身不缓存 GET 请求但如果你用了wx.setStorageSync缓存物料信息就要注意设置过期时间。常见做法是存的时候带上时间戳读的时候判断是否超过设定时长。// 带过期时间的缓存写入 function setCache(key, value, expireSeconds) { const data { value: value, expire: Date.now() expireSeconds * 1000 }; wx.setStorageSync(key, data); } // 读取时校验过期 function getCache(key) { const data wx.getStorageSync(key); if (!data) return null; if (Date.now() data.expire) { wx.removeStorageSync(key); return null; } return data.value; }逻辑说明把值和过期时间一起存读取时先判断是否过期过期就删掉返回 null调用方重新请求。这样既利用了缓存减少请求又不会拿到过期数据。参数说明expireSeconds按业务定物料基础信息可以缓存 300 秒库存这种变化频繁的字段建议不缓存或只缓存 10 秒。别把库存缓存太久否则仓管看到的是旧数据容易误操作。5. 部署上线与避坑那些让你加班到凌晨的细节5.1 后端打包部署的常见问题Springboot 打包成 jar 后部署最常见的问题是配置文件没带对。application.yml里如果写了数据库密码打包进 jar 后改密码要重新打包很麻烦。正确做法是把敏感配置外置启动时用--spring.config.location指定外部配置文件或者用环境变量覆盖。# 外部配置文件启动配置文件放在 jar 同级目录 java -jar wms-backend-1.0.0.jar \ --spring.config.locationfile:./application-prod.yml \ --server.port8080逻辑说明file:./表示从当前目录读配置文件这样运维改配置不用动 jar。--server.port可以在命令行覆盖方便同一台机器跑多个实例。参数说明生产环境记得关掉spring.devtools它只用于开发热部署上线带着会占资源。日志级别调成INFO或WARNDEBUG在生产会写爆磁盘。JVM 参数按服务器内存给比如-Xms512m -Xmx1024m别不设上限否则内存溢出会拖垮整台机器。5.2 前端打包与 Nginx 配置Vue 打包用npm run build产物在dist目录。部署到 Nginx 时要处理两件事静态资源路径和接口代理。前端路由用 history 模式的话还要配try_files防止刷新 404。server { listen 80; server_name your-domain.com; # 前端静态资源 location / { root /var/www/wms-web/dist; index index.html; try_files $uri $uri/ /index.html; # history 模式刷新不 404 } # 接口代理到后端 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让所有找不到的路径都回退到index.html由前端路由接管这是 history 模式的标配。proxy_pass把/api/开头的请求转发到后端注意结尾的斜杠/api/转发到http://127.0.0.1:8080/会去掉/api前缀要和后端接口路径对得上。参数说明proxy_set_header把真实 IP 传给后端方便记操作日志。如果后端接口本身带/api前缀proxy_pass就不要加结尾斜杠否则路径会错。这个斜杠问题坑过无数人联调时 404 先查这里。5.3 避坑清单物料与供应商模块的五个血泪教训现象一物料编码重复但系统没拦住。原因数据库唯一索引没建或者建了但应用层没捕获唯一键冲突异常直接抛 500。解决material_code加唯一索引插入和更新时捕获DuplicateKeyException返回友好提示「物料编码已存在」。现象二删除物料后历史出入库记录查不到物料名称。原因用了物理删除流水表关联的物料没了。解决改逻辑删除status置 0查询流水时关联物料表带出名称即使物料已停用也能显示。现象三供应商供货价显示成科学计数法或精度丢失。原因字段用了float或double。解决金额一律用DECIMAL(10,2)或 Java 的BigDecimal前端展示时格式化保留两位小数。现象四小程序扫码后查询很慢要等好几秒。原因material_code没索引或者接口里做了多次关联查询。解决编码字段加索引扫码查询接口只查必要字段别一次性把分类、供应商全带出来需要详情再单独请求。现象五并发出入库导致库存对不上。原因先查后改中间被其他请求插入。解决用第 4.3 节的条件更新 SQL或者给物料行加悲观锁SELECT ... FOR UPDATE前者性能更好。5.4 权限与操作日志不能省WMS 里谁改了库存、谁删了物料必须能追溯。操作日志表wms_operation_log记录用户 ID、操作模块、操作类型、请求参数、IP、时间。用 Springboot 的 AOP 切面统一拦截在注解标记的方法上自动记日志不用每个接口手写。Aspect Component public class LogAspect { Around(annotation(logAnnotation)) public Object around(ProceedingJoinPoint point, LogAnnotation logAnnotation) throws Throwable { long start System.currentTimeMillis(); Object result point.proceed(); // 执行原方法 long cost System.currentTimeMillis() - start; // 异步写日志不阻塞主流程 logService.asyncSave(point, logAnnotation, result, cost); return result; } }逻辑说明Around环绕通知能在方法执行前后都插入逻辑这里记录耗时和结果。日志写入用异步避免拖慢业务接口。参数说明point里能拿到方法名、参数logAnnotation是自定义注解上的模块和操作类型描述。异步写日志要用线程池别每次 new Thread池大小按日志量配一般 2 到 4 个线程够用。6. 让这套 WMS 真正跑顺的一个技巧库存对账任务怎么写才靠谱前面反复提到库存冗余存储有对不上的风险靠人工核对不现实得有个自动对账任务兜底。我的习惯是每天凌晨低峰期跑一次用出入库流水重算每个物料的库存和wms_material.stock_qty比对不一致就修正并记录差异日志。这个任务写好了能省掉大量「库存怎么又不对了」的扯皮。核心思路是流水表wms_stock_flow记录每一笔出入库入库为正、出库为负按物料分组求和就是理论库存。对账时用一条 SQL 算出所有物料的流水汇总再和主表比对。-- 找出流水汇总与主表库存不一致的物料 SELECT m.id, m.material_code, m.material_name, m.stock_qty AS current_qty, IFNULL(f.flow_qty, 0) AS calculated_qty FROM wms_material m LEFT JOIN ( SELECT material_id, SUM(change_qty) AS flow_qty FROM wms_stock_flow WHERE status 1 -- 只算有效流水 GROUP BY material_id ) f ON m.id f.material_id WHERE m.status 1 AND m.stock_qty IFNULL(f.flow_qty, 0);逻辑说明子查询按物料汇总流水变动量LEFT JOIN保证没有流水的物料也能查出来理论库存为 0。WHERE条件筛出主表库存和流水汇总不一致的记录。查出差异后用UPDATE把主表库存修正为流水汇总值同时往差异日志表插一条记录方便排查是哪里出的问题。参数说明change_qty入库为正、出库为负这个约定要在所有出入库代码里统一否则对账永远对不上。status 1过滤掉作废的流水。对账任务建议加分布式锁或数据库锁防止多实例同时跑导致重复修正。修正前先备份差异数据万一修错了还能回滚。对账任务跑顺之后还可以加一层预警差异条数超过阈值就发通知说明系统某处逻辑有 bug得去查。我自己的习惯是每周看一眼对账日志连续几天零差异才放心。这套东西不复杂但它是 WMS 能不能长期用下去的底线——库存不准再花哨的功能都是空中楼阁。希望帮到你。本文还有配套的精品资源点击获取
