FEATURED · 精选文章

进销存ERP系统源码实践:Spring Boot + Vue3 + 小程序全栈落地

发布时间 / 2026/9/8 5:25:48
来源 / 创域科博编辑部
栏目 / 资讯中心
进销存ERP系统源码实践:Spring Boot + Vue3 + 小程序全栈落地 简介一套基于 .NET 与 MsSQL 的完整进销存 ERP 管理系统源码内置小程序端适合中小型企业及开发者进行库存、销售、采购等核心业务管理也可作为 .NET 企业级项目二次开发的参考。源码包共 7267 个文件总大小约 46.71MB主要包含后端 C# 相关文件cs、aspx、ashx、前端页面与脚本htm、html、js、css、gif、png、jpg 等以及小程序相关文件wxml、wxss、json目录结构完整便于定位模块。系统自带电商管理功能可与公众号、小程序对接覆盖微信订单、小程序订单、公众号订单及参数设置等场景销售、采购模块同样齐备。该项目已有多家企业实际使用成熟稳定下载后可直接获取全套源码和必要配置在此基础上进行扩展或定制开发十分高效。目前已有 9655 人学习下载适合需要部署 ERP/进销存系统或希望研究整套业务源码的开发者参考。 完整可跑的进销存ERP管理系统从来都不是什么遥不可及的大厂项目。我自己手头这套“源码小程序”的全套系统就是从一个真实的中小商贸公司需求里长出来的。仓库要管货、财务要看账、老板要随时在手机上看销售额和毛利还要能开单、能收款、能打印小票。今天我把这套系统的整体设计思路、源码关键实现、小程序端对接经验以及我在实盘上线过程中踩过的坑一并整理出来。不管你是准备拿源码二次开发的技术同学还是正打算给自家公司或客户搭一套进销存系统的实施者这篇文章应该都能让你少走不少弯路。这套系统的代码量不算夸张但五脏俱全后端用 Spring Boot 做接口服务管理后台是 Vue3 的单页应用移动端配套微信小程序数据库用 MySQL缓存和并发控制交给 Redis。三个端全打通数据从商品建档、采购入库、销售出库、库存盘点、往来对账到利润报表一整条链路都在闭环跑。1. 项目定位一套能直接落地的进销存ERP源码1.1 这套系统到底解决什么问题进销存翻译成大白话就是进货、销售、库存三件事。但实际做业务的时候这三件事牵扯出来的东西特别多先要有商品资料、客户档案、供应商档案然后采购要下单、入库要有审核销售要开单、要收款库存要变动、要盘点财务要算毛利、要管应收应付。任何一个环节断掉月底对账就是灾难。我最初拿到这个需求时客户明确说不要那种“只登记单据”的小打小闹系统而是要一套覆盖“采购-入库-销售-出库-库存-财务”全流程的管理工具。而且仓库人员普遍年纪偏大电脑操作不熟练所以移动端必须能用小程序扫码开单。基于这些约束系统被拆成了三个端管理后台负责基础资料、审核、报表小程序端负责仓库扫码、移动开单、老板看数后端统一处理业务规则和数据存储。这套源码的价值在于它不是教学意义的半成品而是可以直接部署上线、并且能支撑几十万级商品数的实际系统。新手用它学企业级项目结构老手拿它做二次开发底座都合适。1.2 三个端的分工与真实的用户场景先看管理后台它是给管理员、财务、采购和销售主管用的。职责分别是商品分类维护、供应商和客户管理、采购单审核、销售单审核、入库单确认、退款退货处理、毛利率报表、应收应付账龄分析。PC端屏幕大适合批量操作和复杂查询。小程序端是我个人认为这套系统里最有价值的部分它面对的是仓管员和老板两种截然不同的角色。仓管员在小程序上打开“扫码入库”对着商品的条码扫一下系统自动带出商品名称、规格和上次采购价录入数量就能生成入库单老板打开小程序看的是今日销售额、回款额、低库存商品预警以及按客户维度排列的应收账款排行。后端服务层是一套 RESTful API负责鉴权、参数校验、业务事务、库存流水、报表聚合。设计上做了几个关键约定所有单据必须走审核流程所有库存变动必须写流水所有对外接口统一返回 Result 包装体。这样的好处是前端三个端的行为完全一致不会出现“后台改了库存小程序上却没变”的情况。2. 技术选型与源码架构思路2.1 后端为什么选 Spring Boot 而不是别的现在新手做管理系统Python 有 FastAPI/DjangoNode 有 NestJSJava 这边其实也有 Spring Boot 和 Solon 等选择。但我最终还是选了 Spring Boot 3.x原因很简单这套系统要承载完整的进销存业务里面涉及复杂的库存流水、多表单据事务、定时任务如自动生成月度报表、Excel 导入导出、权限精细控制这些场景 Spring 生态的组件最成熟出问题的概率最小招人也好招。配合的组件我拉了这几样MyBatis-Plus 做 ORM负责单表 CRUD 几乎不用写 SQLFlyway 管理数据库版本避免多人开发时字段不一致Redis 做登录 Token 和库存扣减的分布式锁Sa-Token 或者 Spring Security 做权限我实际用的是 Sa-Token配置轻、注解式权限控制方便。这里有一点值得多说库存操作不能光靠数据库事务还需要在代码层加锁。我的做法是扣减库存时用 Redis 锁住当前商品 SKU再在事务里执行“查库存-校验-扣减-写流水”。这样就算两个仓管同时扫同一件商品也不会出现超卖。2.2 管理端和小程序端的技术搭配管理后台用了 Vue3 Element Plus Vite Pinia Vue Router。选这个组合理由很直白Element Plus 的表单组件和表格组件对后台管理场景覆盖得特别好像商品多规格录入、单据明细行的动态增删这类交互用现成组件改改就能上开发效率很高。小程序端我用了原生微信小程序框架没去套 uni-app 或者 Taro。原因是这个项目里小程序端功能相对聚焦扫码、开单、看报表原生语法完全够用而且一旦遇到微信特有的 API比如扫普通条码、蓝牙打印小票原生调试比跨端框架舒服得多。你要是打算以后同时发支付宝小程序或抖音小程序那可以换 uni-app但当前场景原生更稳。前后端接口这块我没有用 OpenAPI 生成 SDK而是手写 TypeScript 接口定义配了 axios 封装。原因挺实际项目节奏快后端接口字段经常微调手写类型反而灵活不会因为重新生成代码导致空白扫除。接口统一走/api前缀JWT 格式的 Token 放在 Header 里小程序端则用 wx.request 封装一层。2.3 数据库设计里的关键细节进销存系统的表不少但核心表其实就几张商品表product、SKU 表sku、仓库表warehouse、采购单purchase_order、采购单明细purchase_order_item、销售单sale_order、销售单明细sale_order_item、库存表stock、库存流水表stock_flow、客户表、供应商表。我重点想讲两个设计约定。第一个关于 SKU 和库存。商品和 SKU 必须拆成两张表因为同一个商品可能有多个规格不同规格的库存是独立计算的。库存表则按“SKU 仓库”维度存储当前可用库存和锁定库存。所有库存的变动一律只通过库存流水表追加记录任何业务表都不得直接 UPDATE 库存数字这从根本上保证了数据可追溯。第二个关于单据编号。手工录单系统最忌讳编号杂乱。我用了一套“前缀 日期 序列”的生成器销售单 SALE202401010001采购单 PURCHASE202401010001入库单 STKIN202401010001出库单 STKOUT202401010001。编号不在数据库自增而是由服务端统一生成避免并发重复。我自己在数据库设计上踩过最大的坑是一开始把“供应商”和“客户”各建了一张表结果后续做“既是供应商又是客户”的往来单位时完全没法处理对账。后来重构时加了一张partner伙伴表用“类型字段供应商/客户/二者都是”来区分再关联联系人、地址、账户信息。如果你也是从零设计进销存我强烈建议一开始就做这个统一建模否则后面对接财务应收应付会非常痛苦。3. 实操过程把这套源码跑到生产环境3.1 快速搭建本地开发环境拿到源码以后第一步不是急着看代码而是把环境跑起来。我习惯的流程是这样安装 JDK 17、MySQL 8.0、Redis 6.x、Node.js 18。导入数据库脚本项目里有个sql/init.sql直接 source 进去。修改application.yml把数据库账号密码、Redis 地址改成自己的。启动后端服务端口默认 8080看到 “ERP Server Started” 就说明起来了。管理后台前端npm install然后npm run dev浏览器打开 5173 端口。小程序用微信开发者工具导入miniapp目录appid 先用测试号后面再换正式。这里面最容易出的问题就是“报表服务器连接不上”或者“数据库连接失败”。我之前帮同事排查过一次现象是前端登录正常但一点报表模块就报连接失败。最后发现是因为报表模块里有独立的数据源配置默认指向了application-report.yml里写死的数据库地址没有跟着主配置走。改成一个外部化的动态数据源路由就好。3.2 初始化基础资料的正确顺序系统启动后不要急着录单据。基础资料的维护顺序有讲究先建计量单位、商品分类再建仓库、往来单位供应商和客户然后建商品SKU录入初始库存最后建操作员并分配权限。有个常见误区是上来就导商品Excel结果供应商还没建商品表里只能留空供应商字段后面做采购入库时对不上账。我的建议是先用 Excel 模板把往来单位导进去再去导商品顺序不能反。系统中我专门写了导入校验逻辑如果商品引用了不存在的供应商编码那一行会被标红跳过并生成错误报告下载。初始化库存这件事也值得多说一句。不要直接在库存表里 insert 初始数据而是用“期初库存入库单”来导入每行写明商品、仓库、数量和单价审核后系统自动生成一条入库流水。这样做的好处是你的账面上有一张实实在在的期初单以后对账时能直接追溯。3.3 管理端和小程序端联调的关键配置前后端联调最烦的是跨域和地址配置。管理端 Vite 开发服务器配了代理/api前缀的请求全部转发到后端生产环境则用 Nginx 做反向代理同时托管前端静态文件。小程序端不一样它没有浏览器跨域的概念但它要求请求的域名必须是 HTTPS并且在小程序后台配置 request 合法域名。我联调时的固定操作是先用 ngrok 或者内网穿透工具把本地后端暴露成 HTTPS 临时域名然后在小程序开发者工具里把“不校验合法域名”打开直接用临时域名调本地接口。真正上线前再换成正式域名同时把后端的 Nginx 配好证书。整个过程如果你跳过直接拿http://localhost:8080去小程序里请求半天都连不上。4. 核心业务代码实现哪些地方最值得细看4.1 出入库和库存流水的事务实现在进销存里最核心的方法就是“库存变更”。我写的StockService.changeStock()是这个系统的命脉方法它负责接收 skuId、warehouseId、变更数量、关联单据号、业务类型然后执行一段事务逻辑。核心伪代码大概是Transactional(rollbackFor Exception.class) public void changeStock(StockChangeDTO dto) { String lockKey stock:sku: dto.getSkuId(); boolean locked redisLock.tryLock(lockKey, 3, TimeUnit.SECONDS); if (!locked) { throw new BizException(库存操作繁忙请重试); } try { Stock stock stockMapper.selectForUpdate(dto.getSkuId(), dto.getWarehouseId()); if (dto.getChangeType() OUTBOUND) { if (stock.getAvailableStock() dto.getQuantity()) { throw new BizException(可用库存不足); } stock.setAvailableStock(stock.getAvailableStock() - dto.getQuantity()); stock.setFrozenStock(stock.getFrozenStock() - dto.getQuantity()); } else { stock.setAvailableStock(stock.getAvailableStock() dto.getQuantity()); stock.setTotalStock(stock.getTotalStock() dto.getQuantity()); } stockMapper.updateById(stock); stockFlowMapper.insert(buildFlow(dto)); } finally { redisLock.unlock(lockKey); } }这里有两个细节必须做到位一是selectForUpdate要加数据库行锁二是 Redis 锁的 key 必须和数据库行锁的粒度一致。我试过一次只加 Redis 锁不加行锁两个服务实例同时操作时虽然 Redis 锁挡住了大部分冲突但极端情况下数据库层面的更新丢失还是会发生。两边都加才算是双保险。4.2 单据“审核-反审核”的状态机处理单据不能删只能作废单据状态通过审核流转。这是我给这套系统定的铁律。采购单的状态有待审核、已审核、已入库、已作废。销售单的状态有待审核、已审核、部分出库、已出库、已作废。每一张单据的审核动作都会触发业务逻辑。采购单审核通过后系统会自动生成一张待入库的入库单入库确认后增加库存。销售单审核通过后系统会把对应商品加入“锁定库存”直到出库完成后再扣减可用库存。这个“锁定库存”的设计极其重要它解决了多人同时抢单时“都以为有货”的假象。反审核是所有进销存系统里最容易出 bug 的地方。我的处理方法是反审核不能跳过已发生的下级流程。比如销售单已经出库了你想反审核系统会提示“请先删除出库单”。哪怕是管理员角色也需要在权限设置里单独开启“强制反审核”才会放行。这个限制看似繁琐但避免了大量账实不符的问题。4.3 报表统计用不对方式就会卡死进销存的报表往往跑在几万条单据明细上如果用 MyBatis-Plus 的list()把所有明细拉到内存再聚合系统基本就废了。我在统计模块里全部改成了 SQL 级别的聚合。以“销售毛利报表”为例核心 SQL 类似SELECT DATE_FORMAT(s.pay_time, %Y-%m-%d) AS day, s.partner_id, p.name AS partner_name, SUM(i.sale_amount) AS sale_amount, SUM(i.sale_cost) AS sale_cost, SUM(i.sale_amount - i.sale_cost) AS gross_profit FROM sale_order s LEFT JOIN sale_order_item i ON s.id i.order_id LEFT JOIN partner p ON s.partner_id p.id WHERE s.status IN (SHIPPED, DONE) AND s.pay_time #{startTime} AND s.pay_time #{endTime} GROUP BY day, s.partner_id ORDER BY day DESC这种聚合查询在数据量大时也有短板所以我又加了一层 Redis 缓存把按天、按月的统计结果缓存 10 分钟。老板看日报时命中缓存速度几乎秒开后台的流水明细查询则走 MySQL 索引不混在一起。5. 小程序端实现和微信支付对接实录5.1 小程序登录鉴权和扫码开单的细节小程序端的登录流程和 Web 端不一样不能用账号密码登录而是走wx.login()获取 code后端拿 code 去微信接口换 openid再用 openid 关联系统用户。如果在系统里已经绑定过员工账号就直接返回 Token如果没绑定则弹出绑定页面要求输入手机号和管理密码。首次扫码开单时我建议在商品基础数据里就把条码字段维护全。入库和出库的扫码动作打通了微信的wx.scanCode接口扫到的条码直接作为 sku 编码去模糊查询商品。这里有个细节市面上的商品条码格式很多有些是纯数字的 EAN-13有些是企业自编的二维码所以后端查询的时候不要做死板相等匹配要用“完全匹配-包含匹配-模糊匹配”的三级策略否则很容易出现“扫不出来”的情况。5.2 小程序微信支付 V3 对接的踩坑记录微信支付这块我前后折腾了将近一天主要问题集中在签名和证书上。现在小程序支付对接的是 V3 版本接口它在请求头里要求带上Authorization: WECHATPAY2-SHA256-RSA2048的签名信息这个签名格式比 V2 单纯 MD5 复杂得多。关键的坑在于商户私钥要用商户API私钥而不是APIv3密钥。两者不是一回事。前者是商户平台生成的 RSA 私钥用来给请求做签名后者是 AES 密钥用来解密支付回调的报文。签名时需要先拼接请求方法 请求路径 时间戳 随机串 请求体的字符串再用 SHA256WithRSA 算法签名然后把签名结果和商户号、证书序列号一起放进请求头。我把自己用的签名工具类封装好了之后测试支付一次性通过。但第二天发现线上联调时支付回调的验签又报错了当时排查了很久才发现是因为微信的证书序列号要从证书里动态读而不是写死。后来我写了一个证书加载器定时从微信平台拉取最新证书问题才彻底解决。要是你也在对接支付建议直接从源码里的WxPayService看起签名、加密、回调解密、退款逻辑都在里面。5.3 移动端的消息通知和待办提醒小程序端还有一块容易被忽略但实际使用体验提升很大的功能那就是消息通知。我在系统里引入了微信订阅消息配置的是“待办事项提醒”模板。采购主管在 PC 端新建了采购单仓管员的小程序就会收到“您有一笔新的入库任务待处理”的通知销售出库单审核通过后销售员也会收到提醒。订阅消息有个特性用户必须在小程序里主动点击“允许订阅”后下一次才能推送一条。也就是说你不能靠它做连续轰炸只能做关键节点的提醒。我的方案是在用户每次进入待办列表时弹窗申请订阅拿到一次权限就发一条用户体验相对自然。如果你在项目里也用到订阅消息记得把模板 ID 放到服务端统一配置别写死在小程序前端否则后期想换模板很麻烦。6. 常见问题与排查技巧实录6.1 库存对不上先查流水再查锁定位问题时有个优先级铁律先查库存流水表再看业务表。我遇到过“销售出库之后库存反而增加了”的诡异问题排查到最后发现是某个接口重复调用了changeStock()一次是出库扣减另一次是作废单时回补库存两个逻辑在并发下交错执行流水账面上数据都对但净库存变了。解决办法也简单给流水表加一个“业务单据号类型”的唯一索引同一张销售单明细只允许产生一条出库流水。这样一来重复调用直接报唯一键冲突问题自然浮出水面。这也是为什么我一直强调源码里的create_order流程必须经过代码审查不能让写单和改库存分成两个没有强关联的独立事务。6.2 报表数据加载慢优化思路要分层报表变慢是每个老系统都躲不过的问题。我的优化顺序是先看数据库索引再看查询方式最后上缓存。具体到这个项目最有效的优化是把sale_order_item表里的order_time字段改成二级索引并且在大结果集查询时强制走索引避免 type 变成 ALL。其次是停掉所有前端懒加载导致的多请求并发统计把日报和周报接口串行化减少瞬时数据库压力。最后才是上 Redis 缓存并且给缓存设置合理的过期时间。如果你拿到的源码里没有这些优化建议照着这个顺序补一遍。个人实测索引补上之后报表接口从 3 秒变成 0.8 秒缓存加上之后基本稳定在 0.2 秒内。6.3 权限管理混乱需要按角色重新梳理接口源码里内置了超级管理员、老板、采购、销售、仓管、财务六种角色但不同业务场景下角色权限会重叠。比如有的客户要求仓管员也能看成本价格有的要求财务也可以录入采购单。这种需求不应该靠改数据库的 role 字段硬编码而应该在权限模块里用“菜单权限 数据权限 按钮权限”三层模型来配置。我在实际过程中把后端的接口按 Restful 风格重新做了一遍权限标注例如SaCheckPermission(sale:order:audit)这种形式管理后台的按钮则用v-permission指令控制显隐。维护起来比一开始写死 if-else 轻松得多。7. 二次开发建议这套源码要落地还需要补什么如果你打算把这套源码拿去给客户部署或者直接自己运营我觉得最需要补的是三块内容。第一块是打印模板的定制。进销存系统百分百要对接打印机销售小票、采购单、价签模板都每个客户要求不一样。源码里用的是通用 80mm 热敏打印模板换客户时要能在后台编辑模板变量。第二块是移动端重新打包发布。小程序正式上线需要注册企业主体、申请 AppID、配置服务类目。个人开发者在测试阶段可以用测试号但正式发布不建议挂个人号否则支付功能很容易被限制。我在对接微信支付时看到过“由于小程序违规支付功能暂时无法使用”的提示基本上都是和主体资质、类目选择有关系务必在上线前把资质准备齐全。第三块是订单导入的容错。真实业务里Excel 导入是每天都会发生的操作但客户粘贴的数据总是千奇百怪。源码里已经有导入模板和错误回显但建议再补一层“重复数据校验”和“批量更新库存”的功能尤其是老客户想要从旧系统迁移数据的时候一个好的导入模块能帮你节省大量实施时间。最后再分享一个我个人的习惯接手任何一套 ERP 源码都先通读一遍数据库表结构然后画一张“单据流转图”贴在工位上。等你看懂采购单如何变成入库单、销售单如何影响库存和应收款整套源码的骨架也就真正装进脑子里了。后面无论改什么功能都不容易跑偏。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻