这篇文章是《Web简易ERP进销存管理系统》系列的第 01 篇。系列目标是带着你从零搭建一个可用的 Web 进销存系统,而不是只贴概念图。先聊一个很直接的问题:市面上成熟的 ERP 系统非常多,为什么还要自己写一个简易 ERP?
答案通常是三个场景。第一,学习。Spring Boot、Vue、MySQL 这些技术,单独学容易,组合起来做一个完整业务系统很难,进销存是最适合练手的业务模型。第二,毕业设计。进销存几乎是计算机专业毕设里出现频率最高的题目之一,选一套清晰的系统设计比堆功能更重要。第三,小微企业轻量管理。很多小团队没有专门的 IT 人员,大型 ERP 太重、太贵,一个 Web 化的简易进销存系统,能解决商品资料、采购、销售、库存和基础报表的核心诉求就够了。
这篇文章是系列开篇,不写具体代码实现,先把整体规划讲清楚:系统解决什么问题、包含哪些模块、用什么技术栈、数据库怎么设计、后续怎么一步步开发。读完你会对整个项目有完整认知,后面每一期只需要照着路线图填充细节。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 基于 Web 的简易 ERP 进销存管理系统 |
| 核心业务 | 商品资料、供应商、客户、采购入库、销售出库、库存流水、库存结余、业务报表 |
| 系统形态 | B/S 架构,浏览器访问,无需安装客户端 |
| 常见角色 | 管理员、采购员、销售员、仓库管理员 |
| 技术栈方案 | 轻量方案:Flask + Vue + SQLite/MySQL;企业级方案:Spring Boot + MyBatis-Plus + MySQL + Vue |
| 运行环境 | Windows / Linux 均可,需安装对应运行时和数据库 |
| 部署方式 | 前后端分别部署,或打包后由 Nginx 托管静态文件并反向代理后端 |
| 是否支持 API | 后端按 RESTful 风格提供接口,方便后续对接小程序、扫码枪等 |
| 是否支持批量任务 | 采购单、销售单支持批量导入或批量录入(需后续实现) |
| 适合场景 | 技术学习、毕业设计、小微企业内部轻量管理 |
从当前系列定位来看,这是一个教学型项目。功能不会做成 SAP、Oracle ERP 那种大而全,而是把进销存最核心的链路跑通:商品档案 → 采购入库 → 库存增加 → 销售出库 → 库存减少 → 报表统计。这条链路清晰、完整,也最容易验证业务逻辑是否正确。
2. 适用场景与使用边界
2.1 适合解决的业务问题
进销存,本质上管的是三件事:进、销、存。
进:向供应商采购商品,生成采购订单,到货后入库。销:向客户销售商品,生成销售订单,发货后出库。存:实时记录每个商品在哪个仓库、剩余多少、出入库流水是否可追溯。
系统还要解决三个基础资料的问题:商品信息、供应商信息、客户信息。商品信息包括编码、名称、规格、单位、分类、价格等;供应商信息用于采购业务;客户信息用于销售业务。基础资料越规范,后面的库存和报表越准确。
2.2 不适合做什么
在设计简易 ERP 时,有几块复杂度很高,不建议一上来就做:
- 生产制造 BOM、工单、工序排程:这是专业 MES/ERP 的领域,不是简易系统能覆盖的。
- 财务总账、应收应付、固定资产、成本核算:这些需要会计逻辑和严格的凭证体系,适合对接专业财务软件。
- 多公司、多组织架构、多币种:涉及复杂的结算规则,不适合在入门项目中实现。
- 复杂审批流:例如多级审批、自定义流程引擎,会影响核心业务的建设优先级。
因此,这个系统的业务边界要提前划清楚:只做进销存,不做财务,不做生产,不做复杂审批。把核心链路做稳定,再逐步扩展。
2.3 数据安全与合规边界
进销存系统里会存商品成本、销售价格、客户资料、库存数量等经营数据。开发和使用时需要注意:
- 系统上线前要设置强密码和访问控制,不能把管理后台暴露到公网裸奔。
- 涉及客户隐私信息(电话、地址)时,必须获得对方授权,并按相关法规做好隐私保护。
- 数据要有定期备份机制,防止服务器故障导致商业数据丢失。
- 如果后续接入扫码枪、电子秤、小程序等设备,接口调用要加鉴权,不能开放任意写入。
3. 系统整体架构设计
简易 ERP 采用典型的前后端分离架构,也可以根据技术栈不同选择服务端渲染模式。这里推荐前后端分离,因为接口清晰、便于后续扩展。
整体架构分为三层:
- 展示层:Web 前端页面,包括登录、首页看板、基础资料管理、业务单据、库存查询、报表图表。
- 业务层:后端服务,处理权限、业务规则、事务控制、数据校验。
- 数据层:数据库存储,核心是基础资料表、业务单据表、库存流水表和报表统计表。
模块划分推荐如下:
| 模块 | 主要功能 |
|---|---|
| 系统管理 | 用户管理、角色管理、菜单权限、操作日志 |
| 基础资料 | 商品分类、商品信息、供应商、客户、仓库 |
| 采购管理 | 采购订单、采购入库、采购退货(可选) |
| 销售管理 | 销售订单、销售出库、销售退货(可选) |
| 库存管理 | 库存查询、库存流水、库存盘点、预警 |
| 报表统计 | 采购报表、销售报表、库存报表、利润统计(简化版) |
这个模块划分适合第一版迭代。后续如果需要,可以在报表模块增加图表展示,也可以把采购和销售模块的订单与入库/出库流程分离。
4. 技术选型建议
进销存系统的技术选型,主要看你当前的目的是什么。有两种路线比较常见。
4.1 轻量快速路线:Flask / FastAPI + Vue + SQLite
该组合适合 Python 技术栈学习、课程设计,或者快速做原型验证。后端使用 Flask 或 FastAPI,前端使用 Vue 3 + Element Plus,数据库先用 SQLite 起步,后期可以切换到 MySQL。
优点:
- 环境准备简单,Python 安装后即可启动。
- Flask 代码量少,适合快速理解业务逻辑。
- SQLite 文件数据库无需额外安装服务,方便本地开发和演示。
缺点:
- 高并发能力一般,适合小团队内部试用。
- SQLite 在多用户并发写入时会有锁竞争,正式环境建议切换 MySQL。
4.2 企业级学习路线:Spring Boot + MyBatis-Plus + MySQL + Vue
该组合适合 Java 技术栈学习、毕业设计,或者作为可扩展的系统底座。后端使用 Spring Boot,ORM 使用 MyBatis-Plus 减少 SQL 编写,前端使用 Vue 3 + Vite + Element Plus,数据库使用 MySQL 8.x。
优点:
- 生态成熟,岗位需求量大,学完直接能写企业级项目。
- Spring Boot 的事务管理可以很好地保证采购入库、销售出库的库存一致性。
- 后续对接小程序、APP 或者第三方系统都比较方便。
缺点:
- 环境安装和项目配置相对复杂,需要 JDK、Maven、MySQL。
做选择时不用太纠结。如果是 Java 方向,选方案二;如果是 Python 方向或快速验证,选方案一。系列后续文章会以 Spring Boot + Vue 为主路线讲解,但核心设计思路两种方案通用。
5. 数据库设计核心思路
进销存系统的数据库设计,最关键的是库存数据。不能只存一张“当前库存表”,一定要有流水表来记录每一次出入库变化。否则一旦出现数据错误,很难追溯。
推荐的表结构设计思路如下:
5.1 核心表清单
| 表名 | 用途 |
|---|---|
| sys_user | 用户表 |
| sys_role | 角色表 |
| sys_user_role | 用户角色关联表 |
| biz_category | 商品分类表 |
| biz_product | 商品信息表 |
| biz_supplier | 供应商表 |
| biz_customer | 客户表 |
| biz_warehouse | 仓库表 |
| biz_purchase_order | 采购订单主表 |
| biz_purchase_order_item | 采购订单明细表 |
| biz_purchase_inbound | 采购入库单主表 |
| biz_purchase_inbound_item | 采购入库单明细表 |
| biz_sale_order | 销售订单主表 |
| biz_sale_order_item | 销售订单明细表 |
| biz_sale_outbound | 销售出库单主表 |
| biz_sale_outbound_item | 销售出库单明细表 |
| biz_stock_balance | 库存结余表 |
| biz_stock_flow | 库存流水表 |
| biz_stock_check | 盘点记录表(可选) |
5.2 商品信息表示例
商品表不需要设计得太复杂,但编码、名称、规格、单位、分类、默认进价、默认售价都属于常规必填项。
CREATE TABLE biz_product ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', product_code VARCHAR(50) NOT NULL UNIQUE COMMENT '商品编码', product_name VARCHAR(200) NOT NULL COMMENT '商品名称', category_id BIGINT COMMENT '分类ID', spec VARCHAR(100) COMMENT '规格型号', unit VARCHAR(20) COMMENT '计量单位', default_purchase_price DECIMAL(10,2) DEFAULT 0 COMMENT '默认进价', default_sale_price DECIMAL(10,2) DEFAULT 0 COMMENT '默认售价', status TINYINT DEFAULT 1 COMMENT '状态:1启用 0停用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted TINYINT DEFAULT 0 COMMENT '逻辑删除' ) COMMENT '商品信息表';5.3 库存结余表与流水表
库存结余表保存当前仓库、当前商品的实时库存,用于查询。库存流水表记录每一次入库、出库、盘点、调整的操作,用于追溯。结余表可以冗余“最后更新时间”,但算法要统一。
CREATE TABLE biz_stock_balance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, product_id BIGINT NOT NULL COMMENT '商品ID', warehouse_id BIGINT NOT NULL COMMENT '仓库ID', quantity DECIMAL(12,2) DEFAULT 0 COMMENT '当前库存数量', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_product_warehouse (product_id, warehouse_id) ) COMMENT '库存结余表'; CREATE TABLE biz_stock_flow ( id BIGINT PRIMARY KEY AUTO_INCREMENT, product_id BIGINT NOT NULL COMMENT '商品ID', warehouse_id BIGINT NOT NULL COMMENT '仓库ID', change_type VARCHAR(20) NOT NULL COMMENT '类型:PURCHASE_IN SALE_OUT STOCK_IN STOCK_OUT CHECK', change_quantity DECIMAL(12,2) NOT NULL COMMENT '变动数量,入库正,出库负', before_quantity DECIMAL(12,2) COMMENT '变动前库存', after_quantity DECIMAL(12,2) COMMENT '变动后库存', biz_type VARCHAR(50) COMMENT '关联业务类型', biz_no VARCHAR(50) COMMENT '关联业务单号', remark VARCHAR(255) COMMENT '备注', create_by BIGINT COMMENT '操作人', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT '库存流水表';在业务实现中,采购入库、销售出库这些操作必须在同一个数据库事务里完成两件事:更新库存结余表,写入库存流水表。这样才能保证库存数据可追溯、不会凭空消失。
6. 项目目录结构与启动流程
不管是 Java 后端还是 Python 后端,项目目录结构都应该遵循分层设计:controller、service、mapper/repository、entity/model、dto、config 等。
6.1 后端目录结构示例(Spring Boot)
erp-backend/ ├── src/main/java/com/example/erp/ │ ├── ErpApplication.java │ ├── controller/ │ │ ├── AuthController.java │ │ ├── ProductController.java │ │ ├── PurchaseController.java │ │ ├── SaleController.java │ │ └── StockController.java │ ├── service/ │ │ ├── impl/ │ │ └── ... │ ├── mapper/ │ ├── entity/ │ ├── dto/ │ ├── config/ │ └── common/ ├── src/main/resources/ │ ├── application.yml │ ├── mapper/ │ └── db/ │ └── init.sql └── pom.xml6.2 前端目录结构示例(Vue 3 + Vite)
erp-frontend/ ├── src/ │ ├── api/ │ │ ├── product.js │ │ ├── purchase.js │ │ ├── sale.js │ │ └── stock.js │ ├── assets/ │ ├── components/ │ ├── router/ │ ├── store/ │ ├── views/ │ │ ├── Login.vue │ │ ├── dashboard/ │ │ ├── product/ │ │ ├── purchase/ │ │ ├── sale/ │ │ └── stock/ │ ├── App.vue │ └── main.js ├── package.json └── vite.config.js6.3 通用启动命令模板
实际启动命令需要按项目实际情况调整。下面给出通用模板:
# 后端启动(Spring Boot) cd erp-backend mvn spring-boot:run # 后端启动(Flask) cd erp-backend pip install -r requirements.txt python app.py # 前端启动 cd erp-frontend npm install npm run dev启动后,前端开发服务器一般会占用一个端口(例如 5173),后端接口服务占用另一个端口(例如 8080)。如果出现端口被占用,可以分别修改配置文件中的端口号。前后端联调时,需要在前端设置 API 代理地址,把/api请求转发到后端服务。
7. 功能流程演示与验证计划
虽然是系统整体介绍,但也需要把核心业务流程和验证方式讲清楚。后续每个功能模块都会按下面的流程开发。
7.1 主业务流
一个完整的进销存主流程如下:
- 管理员登录系统。
- 维护基础资料:新增商品分类、商品信息、供应商、客户、仓库。
- 采购员创建采购订单,选择供应商和商品,填写数量和进价。
- 采购订单审核通过后,仓库管理员执行入库操作。
- 系统自动增加库存结余,并记录一条采购入库流水。
- 销售员创建销售订单,选择客户和商品,填写数量和售价。
- 销售订单审核通过后,仓库管理员执行出库操作。
- 系统自动扣减库存,并记录一条销售出库流水。
- 库存报表和销售报表实时更新,管理者可以查看毛利和库存情况。
7.2 功能验证清单
| 验证点 | 输入 / 操作 | 预期结果 |
|---|---|---|
| 用户登录 | 输入正确账号密码 | 登录成功,进入首页;错误密码提示失败 |
| 商品新增 | 新增商品编码、名称、价格 | 商品列表展示新商品,重复编码被拦截 |
| 采购入库 | 创建采购单并入库 | 库存结余增加,库存流水生成 |
| 销售出库 | 创建销售单并出库 | 库存结余减少,库存流水生成 |
| 库存防负 | 出库数量大于当前库存 | 系统提示库存不足或不允许负数(可按业务开关控制) |
| 权限控制 | 普通用户访问用户管理菜单 | 返回无权限提示 |
| 数据一致性 | 并发执行多笔出库 | 库存最终结果正确,无超卖或负数 |
7.3 判断成功的标准
一套进销存系统能不能用,不只看页面是否好看,关键看两点:
第一,库存流水是否完整。每一次数量变动都能在流水表里找到对应的业务单号和时间。第二,库存结余是否准确。在任意时间点,库存结余等于所有入库流水数量减去所有出库流水数量。如果出现不一致,说明事务控制或业务逻辑有问题。
8. 接口 API 设计示例
前后端分离模式下,后端提供 RESTful API。以下是一组基础接口约定,实际开发中需要根据项目模块调整。
| 方法 | 路径 | 功能 |
|---|---|---|
| POST | /api/auth/login | 用户登录 |
| GET | /api/product/page | 商品分页查询 |
| POST | /api/product | 新增商品 |
| PUT | /api/product/{id} | 修改商品 |
| DELETE | /api/product/{id} | 删除商品 |
| GET | /api/stock/balance | 库存结余查询 |
| POST | /api/purchase/inbound | 创建采购入库单 |
| POST | /api/sale/outbound | 创建销售出库单 |
| GET | /api/report/sale | 销售报表查询 |
下面给出一个通用的 API 调用示例,使用 Python requests 模拟创建采购入库单。实际接口路径和字段需要按你的项目定义来调整:
import requests url = "http://127.0.0.1:8080/api/purchase/inbound" headers = { "Authorization": "Bearer <TOKEN>", "Content-Type": "application/json" } payload = { "supplierId": 1, "warehouseId": 1, "items": [ { "productId": 10, "quantity": 20, "purchasePrice": 15.5 } ], "remark": "第一批测试采购入库" } response = requests.post(url, json=payload, headers=headers, timeout=30) print(response.status_code) print(response.json())对应返回结果可以设计为:
{ "code": 200, "message": "success", "data": { "inboundNo": "RK202501010001", "stockFlows": 1 } }接口设计要注意几点:所有写操作都需要校验登录状态和操作权限;采购入库、销售出库这类影响库存的接口必须有事务控制;返回结果统一封装 code、message、data,方便前端处理。
9. 关键问题与排查思路
开发 Web 进销存系统过程中,遇到问题最多的集中在数据库、跨域、库存和部署几个方面。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 后端服务启动失败 | 端口被占用或配置错误 | 查看启动日志,检查端口占用 | 更换端口或关闭占用进程 |
| 数据库连接失败 | MySQL 未启动、账号密码错误、库不存在 | 检查 application.yml 配置 | 确认数据库服务和账号权限 |
| 前端请求接口跨域 | 前后端域名或端口不一致 | 打开浏览器控制台查看 CORS 报错 | 后端配置跨域或前端配置代理 |
| 库存出现负数 | 未做库存校验或并发问题 | 检查出库接口逻辑和事务 | 出库前查询库存,数量不足直接拦截;必要时加锁 |
| 采购入库后库存没变 | 事务未提交或流程未执行完 | 查看后端日志,检查库存流水 | 确认入库存方法事务生效,异常时回滚 |
| 页面刷新后 404 | 前端路由是 history 模式 | 检查 Nginx 部署配置 | Nginx 配置 try_files 指向 index.html |
| 数据异常但无日志 | 日志级别太低或异常被吞 | 调整日志级别为 DEBUG | 打印关键流水日志,记录操作人和时间 |
| 多人同时出库超卖 | 高并发下单行更新竞争 | 查看数据库锁和流水记录 | 对库存扣减使用条件更新WHERE quantity >= ? |
9.1 库存扣减的常见实现
库存扣减是进销存最容易出 Bug 的地方。推荐使用乐观更新加条件判断:
UPDATE biz_stock_balance SET quantity = quantity - #{quantity}, update_time = NOW() WHERE product_id = #{productId} AND warehouse_id = #{warehouseId} AND quantity >= #{quantity};如果更新影响行数为 0,说明库存不足,直接抛出业务异常,回滚事务。同时,无论成功失败,都写入一条库存流水记录。
10. 开发路线图与后续系列安排
为了让整套系统可落地,建议按下面的路线推进。每期解决一个明确问题,做完一期就能跑通一个阶段。
| 阶段 | 主题 | 产出 |
|---|---|---|
| 01 | 系统整体介绍 | 需求分析、模块划分、技术选型、数据库设计思路(本文) |
| 02 | 开发环境搭建 | JDK、MySQL、Vue 项目初始化,前后端运行成功 |
| 03 | 数据库建库与基础代码 | 建表 SQL、项目骨架、统一返回结果和异常处理 |
| 04 | 用户登录与权限管理 | 登录接口、JWT、角色权限、前端路由守卫 |
| 05 | 基础资料模块 | 产品分类、商品信息、供应商、客户、仓库管理 |
| 06 | 采购入库模块 | 采购订单、采购入库、库存更新 |
| 07 | 销售出库模块 | 销售订单、销售出库、库存扣减 |
| 08 | 库存查询与流水 | 库存结余、库存流水、库存预警 |
| 09 | 报表统计与首页看板 | 销售报表、采购报表、商品销售排行 |
| 10 | 部署上线与操作手册 | Linux 部署、Nginx 配置、数据备份、使用说明 |
第一期的重点是把业务边界和技术方向定下来。不要急着写代码,先把模块、表和流程想清楚,后面开发会顺畅很多。最容易踩的坑有两个:一是库存没有流水表,出问题只能手工改数据库;二是权限设计得过于复杂,导致迟迟做不完核心业务。建议第一次开发时,权限只做“管理员、采购员、销售员、仓库管理员”四种角色,权限控制到菜单级别即可。核心优先,先把采购入库、销售出库、库存流水跑通,再优化权限和报表。
下一期,也就是系列第 02 篇,会带你完整搭建开发环境:安装 JDK、Maven、MySQL,初始化 Spring Boot 后端项目和 Vue 3 前端项目,并让一个最简单的“Hello World”接口在前端页面上显示出来。这是整个系统开发的第一步,也是很多新手上手时最容易卡住的一步。到时候可以先把环境装好,下一篇直接跟着操作。