简介:生产管理系统源代码是一套面向制造型企业与ASP开发者的完整业务系统,覆盖生产计划、物料需求、库存管理、生产进度跟踪、质量控制等模块,可用于提高生产效率、优化资源分配,也是学习或二次开发的基础。资源包内含165个文件,以ASP动态页面为主承载业务逻辑,配合JavaScript、CSS实现前端交互与样式,GIF/JPG图片提供界面素材,数据库文件保存业务数据,压缩包仅1.33MB。源码中可见MRP物料需求计算、出入库与盘点、不合格品处理、订单状态流转及报表分析的具体实现,并有员工、销售、物料等页面入口,便于按模块对照学习。已有2002人学习下载,适合具备ASP与数据库基础、熟悉制造流程的开发者使用。可在此基础上调整流程、增补功能,为企业信息整合与降本增效提供可落地参考。
1. 生产管理系统源代码:是把车间规则写进代码,不是装上去就能用的软件
车间还在靠 Excel 排计划、拿纸单报工的时候,很多工厂 IT 会想到找一套生产管理系统源代码回来自己部署。这个念头很实际:有源代码意味着能改流程、能接自己的打印模板、能跟现有 ERP 对账,不再被商业软件的黑匣子卡住。但它也不是装好就能用的成品。一套生产管理系统源代码,本质是把物料、工单、工序、报工、库存、质检这些车间日常,做成一组数据库表和一组前后端页面。接手的人得先看懂订单怎么变成工单、工单怎么变成工序,再去改规则才不容易改崩。这篇笔记写给准备接手 MES 二次开发的工厂 IT 和软件公司同事,按我落地这类系统的路数,从拆结构、跑通环境、调参数,一路讲到坑在哪、上线前怎么验证。
2. 拿到源码先拆结构:生产管理系统的五个模块和一条主流程
拿到生产管理系统源代码,第一件事不是启动,而是先把仓库结构、数据字典、状态枚举过一遍。这类系统是典型业务系统,核心在流程。先弄清楚哪些文件是前端、哪些是后端、哪些是数据库脚本,再开始改,能少走很多弯路。
2.1 前端、后端、数据库脚本:源代码包里这三种东西各管什么
生产管理系统源代码,通常由三块组成:后端服务、前端页面、数据库脚本。前端负责让人在浏览器里填工单、扫码报工、看统计;后端负责校验数据、推进状态、控制权限、跟数据库打交道;数据库脚本则决定了第一版表结构、字典数据和初始账号。
| 组成部分 | 常见目录 | 职责 |
|---|---|---|
| 后端服务 | prodms-server 这类目录 | 接口、权限、状态流转、与 MySQL 交互 |
| 前端页面 | prodms-ui 这类目录 | 页面、菜单、表单、条码报工界面 |
| 数据库脚本 | sql/ 目录 | 建库建表、初始字典、管理员账号 |
做生产管理系统源代码管理的第一件事,我建议在 Git 里建一个本地开发分支,比如dev-local,不要直接在主干上改。这些系统的 SQL 脚本通常只有一份初始化脚本,后续靠增量脚本升级;分支出问题可以随时回退,等于给自己留后悔药。
读代码的顺序也有讲究:先看 README,再看 SQL 脚本,然后看后端依赖文件pom.xml和前端依赖文件package.json。从依赖文件确认技术栈组合,常见是 MySQL 8 + Spring Boot 3 + Vue 3,老一些的源码是 Spring Boot 2 + Vue 2。如果拿到手的压缩包里只有编译后的dist目录和混淆过的 JS 文件,没有源码和数据库脚本,那只能叫部署包,不叫源代码。
2.2 从销售订单到完工入库:一条工单在源码里的完整流转
生产管理系统源码里,业务对象绕不开这几张表:生产工单、工艺路线、工序任务、报工记录、库存流水。名字可能叫work_order、process_route、process_task、work_report、inventory_ledger,核心字段类似。
| 业务对象 | 常见表名 | 核心字段 |
|---|---|---|
| 生产工单 | work_order | order_no, product_code, plan_qty, status |
| 工艺路线 | process_route | product_code, process_seq, workcenter |
| 工序任务 | process_task | order_id, process_seq, plan_qty, done_qty |
| 报工记录 | work_report | order_id, process_id, user_id, qualified_qty |
| 库存流水 | inventory_ledger | item_code, direction, qty, bill_type |
一条工单在源代码里的流转顺序很固定:销售订单或者手工创建工单,计划员把产品拆成生产工单,这是第一层;系统按工艺路线把工单拆成多个工序任务,这是第二层;车间工人报工时,把合格数写进报工记录,同时更新当前工序的完成数量;最后一道工序报工完成后,工单自动进入完工状态,生成入库单回写库存;之后所有报表从这些明细表里聚合数据。
这个流程看起来简单,真正决定系统是否好用的,是每一步的状态怎么码。生产管理系统不是普通的增删改查系统,它内部有一个状态机。工单状态和工序任务状态要分开看:工单状态通常有草稿、已下达、生产中、已完工、已关闭;工序任务状态有待报工、报工中、已质检、已完工。后面改流程时,最先要动的就是这块状态机。
2.3 为什么大量生产管理系统长在通用后台框架上
为什么很多生产管理系统源代码看起来都有一层“通用后台”的影子?因为车间系统天然需要组织架构、用户角色、菜单权限、操作日志、数据字典这些能力,从零造一遍成本很高。大量源码选择长在若依这类通用后台管理框架上,这不是缺陷,反而是二次开发的基础。
这类基座通常有清晰的模块分层:系统模块管权限,业务模块管生产,定时任务模块管排产和库存预警,通用模块放工具类。看代码时先找到业务模块,生产管理系统的核心改动基本都集中在那里。
prodms-server/ ├── prodms-admin # 启动入口,Controller 层 ├── prodms-system # 用户、角色、菜单权限 ├── prodms-quartz # 定时任务 ├── prodms-mes # 生产业务模块:工单、报工、BOM └── prodms-common # 通用工具类这个分层结构里,业务模块的代码路径通常会带mes、produce、wip之类的前缀,取决于代码生成器的配置。改流程主要看 service 层,改页面主要看前端views目录。框架层的东西能不动就不动,生产管理系统的价值在业务模块里,不在登录和权限里。
3. 把源代码跑起来:建库、改配置、启动后端和前端的具体操作
跑一套生产管理系统,不是照着 README 敲几行命令就完事。有三个参数几乎每次都会改:数据库名、数据库密码、后端接口地址。这三个参数没对齐,后面全是看似奇怪的环境报错。
3.1 先建库再改连接配置:启动报错八成出在这一步
拿到 SQL 脚本,先不要急着执行。用编辑器打开脚本前 20 行,看有没有CREATE DATABASE。有些脚本作者会把建库语句写进去,有些只建表。没有建库语句的话,先手动建库。
CREATE DATABASE prodms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;建完库再导入脚本,字符集要指定为 utf8mb4,避免中文表注释和字典数据变成乱码。
mysql -uroot -p --default-character-set=utf8mb4 < sql/prodms.sql然后改后端数据库连接配置。配置文件常见位置是src/main/resources/application.yml,注意多环境配置时可能在application-dev.yml里。核心就三个参数:连接地址、用户名、密码。
spring: datasource: url: jdbc:mysql://localhost:3306/prodms?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai username: root password: '这里改成你的MySQL密码'连接参数里,prodms必须和刚才建库的库名一致;characterEncoding=utf8mb4解决中文乱码;serverTimezone=Asia/Shanghai解决时间字段差 8 小时的问题。如果 MySQL 驱动版本比较新,还需要在 URL 后面加allowPublicKeyRetrieval=true,否则连接时可能报公钥检索错误。密码为空时,注意 YAML 里不要用裸字符串,加单引号更安全。
提示:导入脚本前先确认 MySQL 版本。现在多数生产管理系统要求 MySQL 8.0 以上,MySQL 5.7 跑新版脚本容易在 JSON 字段和索引长度上报错。
3.2 启动后端:读一下启动类的端口再执行
后端启动前,先确认本机装了 JDK 17 和 Maven 3.8+。执行打包命令,跳过单元测试可以省时间。
cd prodms-server mvn -DskipTests clean package java -jar prodms-admin/target/prodms-admin.jar --server.port=8080开发调试阶段也可以直接用mvn spring-boot:run,好处是不用每次都打包。启动日志里看到Tomcat started on port(s): 8080 (http),说明后端已经起来了。如果 8080 端口被占用,先用命令排查。
lsof -i :8080 kill -9 <PID>或者换一个端口启动,比如--server.port=8081,但前端代理也要跟着改。生产管理系统经常同时开着多个服务,端口冲突是家常便饭,习惯在启动参数里显式指定端口,比去翻配置文件更快。
java -jar prodms-admin/target/prodms-admin.jar --server.port=8080 --spring.profiles.active=prod--spring.profiles.active=prod会切到生产环境配置。很多工厂环境同时有测试库和生产库,环境切换靠这个参数。如果 jar 包没有生成,多半是pom.xml里漏了 Spring Boot 的打包插件,检查<packaging>jar</packaging>和spring-boot-maven-plugin。
3.3 启动前端与验证登录:看到菜单不算通,跑一张工单才算
前端主要确认 Node 版本,生产管理系统源码的 package.json 里一般会写清楚要求,常见是 Node 18 以上。安装依赖并启动开发服务。
cd prodms-ui npm install npm run dev启动后,Vite 会打印一个本地访问地址。如果登录页能打开但接口一直转圈,优先查前端的代理配置。开发环境常用/dev-api前缀把请求转发到后端,代理配置写在vite.config.js里。
server: { port: 80, proxy: { '/dev-api': { target: 'http://localhost:8080', changeOrigin: true } } }changeOrigin: true的意思是后端看到的请求 Host 是后端地址,避免接口跨域报错。改后端端口后,这里的target要同步改,否则前端报 502 或Network Error。登录账号和初始密码一般在初始化 SQL 脚本里,常见是 admin/admin123 这类组合,以脚本内容为准。
关键在于,登录成功、看到菜单,只代表环境通了,不代表系统能用。生产管理系统真正跑通的标准是:能新建一张工单、能把这个工单状态从草稿推到完工。这个验证流程留到最后一章细说。
4. 把参数调到车间能用:报工状态、工作日历与物料齐套怎么落库
环境跑起来之后,新系统还不能直接给车间用,因为车间有班次、有不良原因、有物料替代关系,这些规则系统一概不知。生产管理系统里所谓的“调参数”,调的不是配置文件里的数字,而是数据库里的字典、基础资料和状态流转规则。
4.1 最小基础参数:班次、工作日历和工位为什么不能拍脑袋
生产管理系统的基础参数分三类:数据字典、基础资料、工作日历。数据字典管不良原因、工单类型这类枚举值;基础资料管物料编码、计量单位、默认工艺路线;工作日历决定哪天开工、哪天休息,直接影响排产结果。
以班次为例,很多工厂是两班倒或者三班倒,报工记录要能区分早班、中班、夜班,工时统计才有意义。在源代码里,这类枚举值通常已经预留了字典配置位置,初始化脚本里可能只放了一个默认值。上线前可以根据实际班次补数据。
INSERT INTO sys_dict_data (dict_type, dict_label, dict_value, sort) VALUES ('shift', '早班', '1', 1), ('shift', '中班', '2', 2), ('shift', '夜班', '3', 3);不同源代码里字典表可能叫sys_dict_data或base_dict_item,表名前缀取决于框架,导入前先看现有数据。最小参数集建议至少覆盖这几项:
| 参数类型 | 建议最小配置 | 影响范围 |
|---|---|---|
| 班次 | 每个班次的起止时间 | 报工归属、工时统计 |
| 工作日历 | 未来三个月的开工日与休息日 | 排产、交期倒排 |
| 不良原因 | 常见十种原因 | 质检报表 |
| 工序工时 | 每道工序标准工时 | 效率统计、绩效 |
这些参数看着不起眼,一旦报工记录进去了再改,历史数据的班次归属就乱了,报表对不上。所以正式数据导入前必须把基础参数确认完,不要等车间开机后再补。
4.2 工序报工的状态机:一条工单走到哪一步、代码怎么改
生产管理系统最核心的状态流转,在报工这一步。一次报工不只是“插一条记录”,它要做四件事:校验工单状态、找到当前待报工的工序、写入合格数、决定工单下一步往哪走。源码里这段逻辑通常在 service 层,看代码时优先找report、workReport这类方法。
public void report(ReportDTO dto) { // 1. 根据工单号找到工单,工单不存在直接抛异常 WorkOrder order = workOrderMapper.selectByNo(dto.getOrderNo()); if (order == null) { throw new BizException("工单不存在"); } // 2. 只有已下达的工单才能报工,草稿状态不允许 if (order.getStatus() != ORDER_RELEASED) { throw new BizException("工单未下达,不能报工"); } // 3. 找到当前正在执行的工序任务 ProcessTask task = processTaskMapper.selectCurrentTask(order.getId()); if (task == null) { throw new BizException("当前没有待报工的工序"); } // 4. 合格数量不能超过该工序的计划数量 if (dto.getQualifiedQty().compareTo(task.getPlanQty()) > 0) { throw new BizException("报工数量超过计划数量"); } // 5. 写入报工记录 workReportMapper.insert(dto); // 6. 累加已完成数量,并判断是否最后一道工序 processTaskMapper.addDoneQty(task.getId(), dto.getQualifiedQty()); if (task.getProcessSeq() >= task.getTotalProcessSeq()) { workOrderMapper.updateStatus(order.getId(), ORDER_COMPLETED); } else { processTaskMapper.updateStatus(task.getId(), TASK_DONE); processTaskMapper.updateReadyBySeq(order.getId(), task.getProcessSeq() + 1); } }这段逻辑的顺序很重要:必须先校验,再写报工记录,最后改状态。如果顺序反了,报工记录插进去了但状态校验失败,事务回滚还好;如果没加事务,就会出现“库存有数、工单没动”的脏数据。qualifiedQty是合格数量,报工接口通常还会接收不良数量、报工人、设备号和工时字段。状态比较用的ORDER_RELEASED、ORDER_COMPLETED这类常量,在真实源码里可能叫WorkOrderStatus.RELEASED,不同项目命名不同,但思路一致。
注意:这一段代码必须加事务,Spring 体系中就是
@Transactional。报工记录写成功、状态更新失败时,没有事务就会出现车间报工了但工单永远停在上一步的现象。
4.3 物料齐套检查:BOM 表和库存表现在怎么对账
生产管理系统里另一个高频需求是齐套检查:一张工单要开工了,物料到底够不够。简单场景可以查一级 BOM 平铺的需求量和现有库存做对比。
SELECT wo.order_no, bi.item_code, bi.item_name, bi.need_qty, IFNULL(stock.avail_qty, 0) AS avail_qty, bi.need_qty - IFNULL(stock.avail_qty, 0) AS shortage_qty FROM work_order wo JOIN bom_item bi ON bi.product_code = wo.product_code LEFT JOIN ( SELECT item_code, SUM(onhand_qty - blocked_qty) AS avail_qty FROM inventory_current GROUP BY item_code ) stock ON stock.item_code = bi.item_code WHERE wo.order_no = 'MO20250601001' AND bi.bom_level = 1;这段 SQL 算出一级物料的短缺量:onhand_qty是账面库存,blocked_qty是冻结库存,相减得到可用量。IFNULL把没有库存记录的物料当成 0,短缺量才不会被空值吃成负数。真实车间里一张工单往往要展开多级 BOM,一级不够,要用递归查询把组件一级级乘下去。
WITH RECURSIVE bom_flat AS ( SELECT product_code AS parent, component_code AS item, need_qty, bom_level FROM bom_item WHERE product_code = 'P-10001' UNION ALL SELECT b.product_code, b.component_code, b.need_qty * f.need_qty, b.bom_level FROM bom_item b JOIN bom_flat f ON b.product_code = f.item ) SELECT item, SUM(need_qty) AS total_need_qty FROM bom_flat GROUP BY item;递归 CTE 里b.need_qty * f.need_qty是把父级需求逐层乘到子件上。跑这个查询前,必须先做 BOM 回路校验,也就是检查有没有物料在自己的子级里出现,否则递归会死循环。齐套检验在源码里通常做成一个按钮或定时任务,点击后生成一张齐套明细表,把短缺物料列出来,让计划员去催料。现场还要考虑替代料和在途采购量,只看库存表是不够的,但最小闭环先从这里开始。
5. 生产管理系统二次开发的三个高频改点与一次避坑排查
生产管理系统拿回来后,几乎都要改三样东西:标签打印、报表导出、工序流程。前两个看着简单,坑全在后端;第三个是真正的业务核心,一改就可能翻车。这一章先说怎么改,再给一份按现象排查的清单。
5.1 改标签打印和报表:前端模板做展示,再用后端 POI 导出
车间最常见的需求是把成品标签改成自己的格式,或者把完工报表导出成 Excel。标签打印这块,我的建议是不要在后端 Controller 里拼 HTML 字符串返回给浏览器打。正确做法是后端只提供 JSON 数据,前端维护一套标签模板,用浏览器的打印能力配合条码字体渲染。这样改标签样式,只发前端包,不动后端,省去很多上线窗口。
报表导出的常见做法是用 Apache POI 在后端生成 Excel 文件,接口返回二进制流,前端触发下载。
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=report.xlsx"); Workbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet("完工日报"); Row header = sheet.createRow(0); header.createCell(0).setCellValue("工单号"); header.createCell(1).setCellValue("完工数量"); // 数据行按查询结果逐条写入,注意日期列要设置单元格样式 try (OutputStream os = response.getOutputStream()) { wb.write(os); }实现时有两个细节容易踩坑。第一,先设置Content-Type和Content-Disposition,再获取输出流,顺序反了下载文件名会乱;第二,日期字段不要直接setCellValue(new Date()),要建一个日期格式的CellStyle,否则导出的 Excel 里日期会变成一串数字。生产管理系统报表导出最隐蔽的问题是“只导出当前页”。很多源码把报表页面的分页查询接口直接复用到导出功能,页面只显示前十页,导出也只导前十页。接手后检查导出接口,单独写一个不分页的查询方法,别复用列表查询。
5.2 改动工序流程:先改状态机设计,再改枚举和数据库表
工厂说“帮我们在质检后面加一道返工工序”,这是生产管理系统二次开发里最危险的需求。危险不在代码量,而在把流程想简单了。正确顺序是先设计状态机,再动代码。
第一步,把流程画出来。现在的状态是“待报工 → 已质检 → 完工”,加返工之后变成“待报工 → 已质检 → 返工 → 重新报工 → 完工”。关键要确定从哪个状态能进返工,返工完后回到哪个状态。第二步,加状态枚举。历史数据已经存了 0、1、2、3,新增返工状态就追加一个值,比如 4,千万不要重排旧值。一旦重排,所有历史报工记录的含义全乱了,查报表全是错数据。第三步,数据库表字段要克制。返工次数、返工原因这些信息,放报工记录表比堆在工单表里更合适,一张报工记录可以有多条返工记录。第四步,前端按钮和后端校验一起改。我见过只加了前端“返工”按钮、后台 service 状态校验没放行的情况,车间一点按钮就报“当前状态不允许操作”,这就是典型的前后端没同步。
这类改动建议写一个单元测试,造一张工单,人工把状态推到质检完成,再调用返工接口,断言状态和数据的变化。生产管理系统迭代快,靠手工点页面验证,早晚漏一次。
5.3 生产管理系统上线前后的常见故障排查:按现象、原因、解决来定位
这一节列五条我在生产管理系统上见过的真实故障,全部按现象、原因、解决三步走,排查时照着定位。
现象:后端启动直接报Unknown database 'prodms'。
原因:SQL 脚本只建表,没有建库,连接配置里却写了一个不存在的库。
解决:先执行建库语句再导脚本,或者把脚本里手工补上CREATE DATABASE,并确认连接 URL 中的库名完全一致。
现象:页面能登录,但登录进去业务菜单是空的,只剩首页。
原因:初始化账号没有绑定角色,或者角色没有分配业务菜单权限。
解决:进入系统管理-角色管理,给该角色勾选生产管理相关菜单,重新登录。生产管理系统的框架性菜单权限管得很严,这不是 bug,是权限没配全。
现象:报工提示保存成功,但工单状态一直没变化。
原因:报工记录插入和工单状态更新不在同一个事务里;或者代码只写了报工记录,状态流转逻辑被漏掉了。
解决:给报工 service 方法加@Transactional,然后顺着代码日志往下看,确认状态更新是否执行、是否被异常中断。
现象:导出的 Excel 里日期显示成一串数字。
原因:POI 写入日期时没有设置日期格式单元格样式。
解决:给日期列单独建CellStyle,设置yyyy-mm-dd格式,再写入。
现象:两个车间同时给同一道工序报工,数据出现重复或工单状态错乱。
原因:报工逻辑没有做并发控制,多个请求同时读到同一条任务数据并更新。
解决:在生产管理系统的状态更新 SQL 里加乐观锁版本号,where 条件带version,更新影响行数为 0 时提示“数据已被其他人更新”。不要依赖应用层锁,车间扫码枪并发量不高但打工人多,数据库行锁才是兜底。
6. 上线前这样验证:让一条真实生产工单走完四个节点
系统上线前,我最不信任的验证方式是“打开登录页点几下,看到列表有数据就算通过”。生产管理系统是状态机驱动的,只测界面不通业务,上线第二天就会接到车间电话。我固定用一条真实工单做四步验证。
第一步,选一个真实产品,维护好它的 BOM、工艺路线和库存,手工建一张生产工单。第二步,下达工单,执行齐套检查,确认短缺量跟实际库存对得上。第三步,按工艺路线逐道工序报工,最后一道工序完成后,确认系统自动生成完工入库动作,库存余额发生变化。第四步,用对账 SQL 把工单数据拉出来核对。
SELECT wo.order_no, wo.plan_qty, wo.status, (SELECT SUM(r.qualified_qty) FROM work_report r WHERE r.order_id = wo.id) AS report_qty, (SELECT SUM(l.qty) FROM inventory_ledger l WHERE l.bill_type = '完工入库' AND l.order_id = wo.id) AS in_qty FROM work_order wo WHERE wo.order_no = 'MO20250601001';这张工单如果满足“计划数量等于报工合格数合计、等于完工入库数量”,而且工单状态是完工,才算闭环。字段名以你手上的表结构为准,没有bill_type就换成库存流水里的来源单号字段。我习惯每个月挑一张真实工单走一遍全流程,每次改动代码后也跑一遍这条最小回归。早期吃过亏,只测了登录和列表,结果报工后状态没流转,车间隔天就拿回 Excel 顶着用,系统再没人碰。后来定了这个规矩,生产管理系统才真正在车间站住脚。希望帮到你。
本文还有配套的精品资源,点击获取