简介:这是一款基于J2EE的B/S架构MES生产执行管理系统,定位专业、通用且开源免费,主要面向国内离散制造业中小企业,旨在用较低成本实现生产过程的可视化与精细化管理。系统整合了多年离散智造与J2EE项目经验,围绕车间排产、工单执行、质量追溯、物料协同等典型场景,帮助企业打通计划与执行环节,缓解生产进度不透明、异常响应慢等常见痛点。资源包为zip压缩格式,整体大小约49.05MB,内部配有覆盖售前、实施、用户培训、运维等多个阶段的成套指导文档和教学视频,能够为非IT背景的选型或实施人员提供从需求梳理到上线运维的全流程参考。目前已有356人学习下载,对正在评估MES方案或计划轻量化落地的制造企业而言,是一份兼顾业务理解与操作路径的实用资料。
1. 从车间混乱到透明化,开源MES到底解决什么
车间最典型的问题不是设备启动不了,而是工单明明下了,产品却不知道流转到哪道工序。MES系统要解决的就是这种透明化,但十几万起步的商业MES license常比一整年的设备维护预算还高。开源MES走的是另一条路:把工单、工艺、报工、质检、追溯做成一套免费可部署的web底座,你只需针对不同工厂做配置和少量二次开发。
这篇内容面向两类人。一类是工厂里的IT或数字化负责人,想在买商业软件之前先验证MES的业务边界到底在哪;另一类是给制造企业做集成的实施顾问,需要一套能快速演示、能改源码、能接ERP和PLC的底座。我不打算把MES吹成什么工业大脑,而是从可落地的角度讲清楚:这类系统通常长什么样、Docker怎么拉起一套、数据库怎么设计才能支撑追溯、上线前要用什么手段验证它扛不扛得住车间现场的并发。
2. 开源MES的产品边界与架构拆解:为什么"通用"是配置出来的
2.1 模块边界:标准MES的六张表,决定通用性
MES看似业务复杂,撕开来看核心对象并不多。工单、工序执行、报工记录、质检单、条码序列、设备参数,这六类对象基本能演化出绝大多数车间现场业务。所以判断一个开源项目是否"专业",不要只看界面漂不漂亮,而要看这些对象是否都有独立的数据结构,以及状态迁移是不是可控的。
| 功能域 | 核心对象 | 通用性做法 |
|---|---|---|
| 工单管理 | 工单号、产品编码、计划数量、状态 | 支持拆单、合并、插单,状态机按流程配置 |
| 工艺路线 | 工序、工位、标准工时、替代物料 | 工序级BOM和工单级BOM解耦,允许按订单重排 |
| 报工 | 操作工、设备、数量、工时 | 手工报工、扫码报工、设备自动报工三种模式并存 |
| 质量 | 检验单、不良代码、判定结论 | 质检项目按抽样方案配置,判定规则可扩展 |
| 追溯 | 批次号、序列号、返工记录 | 以批次为中心做正/反向追溯,预留ERP/PLC对接 |
| 设备 | 设备编号、状态、参数、维修记录 | 设备数据走采集服务,不直接写业务库 |
通用性不是靠堆功能实现的,而是靠三件事:状态机可配置、对象可扩展、接口不过度绑定。比如不合格品处理,不同行业差异很大,但底层都是"不良代码 + 责任工序 + 处理方式"的组合,只要这三个维度开放维护,就能支撑电子行业的加修和服装行业的降级处理。反过来,如果一个MES把"不良原因"写死在代码的switch语句里,那它越专业越难复制,换一个行业就要动源码。
MES处在ERP和车间设备之间,它不是ERP的订单管理,也不是PLC的组态软件。开源项目里常见的错误是贪多,把排产算法、设备运维、人员绩效考核全塞进一个系统,结果每个模块都做不深。我一般会建议先守住工单到追溯这条主线,其他属于周边能力,可以靠接口集成。
2.2 架构拆解:前后端分离、消息队列、插件点
目前常见的开源MES技术栈是Spring Boot加Vue3加PostgreSQL,部分项目用Java + Vue3开源框架,少数用.NET Core加MySQL。选型时我坚持一条:核心业务对象必须落在关系型数据库,因为工单、报工、质检都是强事务,不要让主单据漂在NoSQL上。
架构上常见做法是前后端分离。后端把业务拆成工单服务、质量服务、追溯服务和基础数据服务,对外暴露统一REST API;前端用Vue3和动态路由做菜单权限,标签页这种交互基本成了标配。服务间通信会用到RabbitMQ或Kafka,用来解决设备采集和ERP同步的削峰。如果设备数量不大,比如几十台注塑机,直接用Redis当缓冲也够用。
插件点一般出现在三个位置:报工时的合格率判定规则、物料批次号生成规则、工单状态推进时的校验逻辑。成熟的MES不会让你为每种异常都改流程代码,而是把判断条件配置化,把动作回调化。例如工单从"加工中"推进到"待检验"时,触发一个事件,事件消费方可以对接质检,也可以对接自动入库。
架构里最容易翻车的不是微服务,而是把前端和后端放在同一个war包里输出。车间的网络环境经常有老旧防火墙和奇怪的代理,前后端分离的好处是静态资源走CDN或NGINX,API只在对等网络内开放,出问题也好定位。生产环境里,我通常会再加一层网关做统一鉴权和限流,不然扫码枪连续工作时会把登录接口打挂。
2.3 开源许可证与商用边界
在gitee上选开源许可证时,最常见的错误是选错"传染性"条款。MES项目若以GPL方式发布,内部使用没有问题,但修改后以SaaS方式对外运营,就必须把修改部分开源。很多工厂IT没意识到这一点,以为下载了就能封版改成自己的产品出售,实际会带来合规风险。
| 许可证 | 是否能闭源SaaS | 二次开发作品是否必须开源 | 适用建议 |
|---|---|---|---|
| MIT | 可以 | 不需要 | 直接做商业集成项目首选 |
| Apache 2.0 | 可以 | 不需要,但保留声明 | 开源贡献友好的生态 |
| GPL v3 | 不可以 | 必须开源 | 社区类项目常用,商用需谨慎 |
如果你准备在多个项目间长期复用这套MES,我一般推荐MIT或Apache 2.0。做集成项目时,也不要只看社区版和商业版的功能隔离,重点看核心工单流程是否完整,数据库迁移脚本是否随社区版发布。有些开源项目把报表模块单独收费,这不算问题,但如果连基础报工都要额外付费,那它就不是免费MES。
3. 本地化部署:用Docker Compose跑通最小MES集群
3.1 环境准备与镜像选择
落地开源MES的第一步,是把它跑在一个可复制的环境里。测试机8C16G就够演示,真正车间生产建议至少16C32G,具体还要看报工并发量和设备采集频率。镜像版本上,优先选择官方稳定标签,比如postgres:15-alpine,不要用latest,否则两个月后重新部署时镜像内容已经变了。
操作系统建议用Ubuntu 22.04或Debian 12,装好Docker和Docker Compose v2插件。工厂内网经常没有外网,所以环境准备阶段要把镜像包、离线安装包都准备好。常见做法是在有网机器上先写好docker-compose.yml和一个.env文件,再统一拷贝过去。
.env文件里至少要管理这几个变量:
POSTGRES_DB=mes POSTGRES_USER=mes_user POSTGRES_PASSWORD=change_me_2019 REDIS_PASSWORD=redis_pass_9527 MES_API_PORT=18080 MES_WEB_PORT=18081这里把默认的8080改成18080,主要是避免和其他业务系统冲突。密码不要出现特殊字符,因为连接串里一旦包含@或#,解析时会非常痛苦。实际实施时,我遇到过现场人员在.env里写错数据库名,花了两个小时才定位到。
3.2 docker-compose.yml 服务编排
下面是一份最小可用的编排文件,包含数据库、缓存、后端API和前端Web四个服务。
version: "3.8" services: db: image: postgres:15-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine command: ["sh", "-c", "exec redis-server --requirepass $$REDIS_PASSWORD"] environment: REDIS_PASSWORD: ${REDIS_PASSWORD} volumes: - ./redisdata:/data api: build: ./mes-backend depends_on: db: condition: service_healthy redis: condition: service_started environment: SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/${POSTGRES_DB} SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER} SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD} SPRING_REDIS_HOST: redis SPRING_REDIS_PASSWORD: ${REDIS_PASSWORD} ports: - "${MES_API_PORT}:8080" web: build: ./mes-frontend depends_on: - api ports: - "${MES_WEB_PORT}:80"这份配置里有几个关键点要解释清楚。SPRING_DATASOURCE_URL里的主机名写db而不是127.0.0.1,因为不同容器在Docker网络里通过服务名互访。数据库的healthcheck非常重要,没有它,后端容器可能在数据库尚未就绪时就开始启动,导致一连串连接超时。Redis的启动命令用了$$REDIS_PASSWORD,原因是在Compose文件里$会被解析,$$才能转义成容器内的环境变量引用。
数据卷挂载了./pgdata和./redisdata。生产环境要把这两个目录放到独立的数据盘,不要放在系统盘,不然系统盘写满后整个服务都会宕掉。另外,如果现场有多台MES实例,不要同时让它们挂载同一个NFS目录里的pgdata,PostgreSQL多写者模式在这种场景下会产生数据损坏。
3.3 首次启动、初始化数据与验证最小流程
环境准备完成后,实际操作顺序是这样的:
cp .env.example .env # 修改.env里的密码和端口 docker compose build docker compose up -d docker compose exec api ./entrypoint.sh init-db docker compose psinit-db脚本只应在首次部署时执行,它负责建表、写入默认工厂、默认班次、一个管理员账号和一套演示工艺路线。执行完后,打开浏览器访问http://服务器IP:18081,用管理员账号登录。如果页面能打开但接口超时,先执行docker compose logs api | grep ERROR,多半是数据库连接失败或Redis密码不一致。
如果是在无外网的工厂内网部署,不要在现场docker compose build,因为要拉取基础镜像。正确做法是在办公网环境执行docker compose pull,然后用docker save导出镜像包,到现场用docker load导入。这个步骤看起来简单,但真有不少项目当天到现场才发现镜像没带全,只能干等。
初始化数据后,建议立即验证一个完整流程:建工单、释放工单、报工、做质检判定。如果这套流程中的每一个动作都能在页面操作,并且后台数据库对应表有记录,说明基础环境正常。否则不要急着进入后续二次开发,先把网络和时区设置问题排查完。
4. 核心数据模型与二次开发:从工单创建到批次追溯
4.1 数据表关系:工单、工序执行表与序列号追溯
MES的追溯能力最终要看数据库怎么设计。我先给一张简化工单表和工序执行表:
create table work_order ( order_no varchar(30) primary key, product_code varchar(64) not null, qty_required numeric(12,2), qty_completed numeric(12,2) default 0, status varchar(20) not null, plan_started_at timestamp, plan_finished_at timestamp, created_by varchar(30) ); create table work_order_process ( id bigserial primary key, order_no varchar(30) references work_order(order_no), process_seq int not null, process_code varchar(40) not null, qty_in numeric(12,2), qty_out numeric(12,2), bad_qty numeric(12,2) default 0, reporter varchar(30), report_time timestamp default now() );order_no用字符串做主键,而不是自增数字,是因为它在ERP同步、纸质工单和条码场景下都需要可读性。status字段用varchar保存状态编码,比如CREATED、RELEASED、PROCESSING、FINISHED,可读性好,也不容易因为枚举类型变更导致迁移麻烦。数量和不良数都用numeric,因为有些行业按重量或面积报工,integer会丢掉精度。
要支持单件追溯,常见做法是增加一张序列号表和工序过程绑定表。每次报工时生成一批序列号,或扫描一个序列号后记录到work_order_process的扩展表。不要把追溯信息全部塞在工单表的备注里,那将导致无法查询。
4.2 用REST API完成报工与不良登记
报工是MES里频率最高的操作,现场用扫码枪或PDA提交。下面是一个标准报工请求:
curl -X POST http://localhost:18080/api/v1/wo/WO-20250401-001/process-report \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "processSeq": 20, "reportedQty": 500, "scrappedQty": 3, "badReasonCode": "NC-102", "operator": "zhang_san", "machineNo": "MC-05" }'这个接口的业务逻辑是:先校验工单状态是否为PROCESSING或RELEASED,再校验当前工序序号必须等于上一工序序号加10。processSeq用10的倍数是为了在后续插入新工序时不必重排所有序号。报工成功后,接口会在同一事务里更新work_order.qty_completed,并把不良数量写入质量缺陷表。如果写入一半失败,整个事务回滚,不会出现数量对不上的情况。
如果报工返回409,说明指定工序跳号了;返回422,说明报工数量超过工单剩余量。这些业务错误码需要在接口文档里写清楚,不然PDA端的开发人员会把这些当成系统故障。每台设备每隔几秒就报一次的场景,设备采集服务不要直接调这个接口,应该先把数据放到Redis队列,再由后端批量消费,否则高并发下会拖慢正常的人工报工。
4.3 扩展自定义字段与前端菜单
不同行业在工单上要挂的额外字段差别很大,比如电子行业要加客户型号、组装行业要加包装规范。常见的做法是预留一个扩展字段列:
alter table work_order add column if not exists extended_fields jsonb;前端用JSON Schema渲染动态表单,工单详情页上就能动态出现"客户型号""特殊工艺要求"这些配置项。使用jsonb的好处是加字段不用再做一次建表迁移,坏处是字段不可作为查询条件。如果某个扩展字段后续频繁参与检索,比如客户型号要作为报表筛选条件,那就把它提升为正式列,并建立合适的索引。
前端部分,现在的Java + Vue3开源框架一般都有代码生成器和菜单管理。你在数据库加一张表后,可以在管理端注册菜单和按钮权限,再利用代码生成模板生成列表页和编辑页。这一套流程和若依这类脚手架很像,好处是权限模型直接复用,不需要自己再开发一套用户和角色体系。需要注意,不要为了省事把所有扩展字段都放进jsonb,否则半年后你根本不知道里面存了什么,维护成本会迅速上升。
4.4 与ERP和PLC的集成点
ERP和MES之间的数据流通常是订单下达和生产完工回报。PLC和MES之间的数据流是设备状态和产量计数。协议选型上,我整理了一张常见的集成方式表:
| 外部系统 | 协议 | 数据方向 | 实现建议 |
|---|---|---|---|
| ERP | REST或WebService | 订单到MES,完工回ERP | 通过API推送,MQ做失败重试 |
| PLC | OPC UA或Modbus TCP | 设备状态和质量参数到MES | 采集服务先写时序数据,再聚合入业务库 |
| WMS | REST或RabbitMQ | 物料配送和成品入库 | 绑定批次号,接口幂等 |
PLC采集是最容易出问题的环节。很多实施方让采集程序直接把每分钟的数据写入MES业务库,导致数据库连接池被占满,工单报工页面打不开。正确做法是采集服务先用高频写Redis或时序数据库,再以10秒或30秒的窗口聚合成设备运行记录,最后写入业务库。这样即使设备数量从30台涨到200台,MES核心库的压力也不会成倍增长。
5. 落地前的压测与验证:一小时快速验收清单
5.1 用并发请求验证事务一致性
上线前我最担心的是工单超发和报工丢数据。用Apache Bench简单打一下报工接口:
ab -n 200 -c 20 \ -H "Authorization: Bearer ${TOKEN}" \ -p report.json -T application/json \ http://localhost:18080/api/v1/wo/WO-20250401-001/process-report-c 20模拟20个扫码终端同时报工,-n 200是总请求数。跑完后查看工单已完成数量,如果大于计划数量,说明接口没有对工单行锁做正确控制,大概率是在事务里缺少select for update。这种并发问题在功能测试阶段几乎测不出来,因为手工点击不可能同时完成。
5.2 看日志和慢查询,定位问题
压测后,第一时间检查日志:
docker compose logs api --since 10m | grep ERROR如果日志不明确,再进数据库看慢查询:
SELECT query, calls, total_exec_time FROM pg_stat_statements WHERE query LIKE '%work_order_process%' ORDER BY total_exec_time DESC LIMIT 5;慢查询里如果出现对work_order_process的全表扫描,通常是因为order_no字段没有索引,或者报表统计时跨表关联太多。MES的业务表并不大,但频繁查询的字段一定要有索引,尤其status和order_no的组合查询,在生产上会遇到明显卡顿。
5.3 升级前把定制放进增量迁移
开源MES项目迭代很快,自己改过的代码越多,升级越痛苦。我一般会保持核心源码尽量不动,所有行业化逻辑放到扩展包里,数据库变更用增量迁移脚本管理。规范的迁移文件命名类似V20250401__add_customer_field.sql,每台服务器执行顺序一致。给上游项目提交修改时,最好把通用字段提交给官方作为"开源文档贡献"或PR,把偏门的行业逻辑留在自己的迁移目录里,不要全部塞进同一个分支。
上线前必须做一次恢复演练。先用pg_dump导出数据,再用pg_restore导入到一个全新的数据库实例,确认导入后页面可登录、工单数据可查询。备份恢复流程如果能在一小时内跑通,生产环境即使遇到磁盘故障,也不会造成长时间停产。把这条命令跑通,再谈版本升级。
本文还有配套的精品资源,点击获取