拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于RuoYi框架的MES系统开发实战:架构设计、权限控制与踩坑记录

基于RuoYi框架的MES系统开发实战:架构设计、权限控制与踩坑记录

1. 为什么MES项目选型绕不开RuoYi这套组合拳

我大概在两年前开始做制造业数字化的项目,接触过不少做MES的团队。早期大家的能力参差不齐,有人用纯手工JavaWeb写,有人用Python搭个小系统,也有人直接在Excel上建模型硬撑。后来发现,真正能在生产环境稳定运行、能扛住车间高频操作和复杂工单流转的,还得回到SpringBoot这套成熟生态上,而前端用Vue2配合RuoYi框架做底座,几乎是中小型MES项目里最稳妥的起手式。

先说MES本身。MES是制造执行系统,主要解决“计划层”和“控制层”之间的信息断层问题。ERP管的是订单、物料需求计划,DCS/PLC管的是设备执行,MES夹在中间,负责工单下发、工序流转、报工采集、质量检验、返工返修、设备状态汇总、产量与不良品统计。简单说,ERP告诉车间“这周要做什么”,MES负责盯着“每一步做得怎么样、做完了没有、合格不合格”。

这就决定了MES天然是个“流程重、状态多、数据量大、权限细”的业务系统。如果从零开始搭,用户管理、角色权限、菜单管理、操作日志、定时任务、代码生成、文件上传这些基础能力都得重写,周期会拖得很长。RuoYi的价值就在于把这些通用能力全部沉淀好了,我们只需要把精力集中在MES的业务领域上。

我自己的选型经验是:不是所有企业都需要RuoYi,但做MES用RuoYi底座是性价比最高的选择。MES的大量页面其实就是“主从表结构”——一个工单对应多个工序,一个工序对应多次报工记录,一个质检批次对应若干检验项。RuoYi的代码生成功能天然适配这种结构,生成控制器、Service、Mapper、实体类、Vue页面,再手工调整业务逻辑,七八成的基础CRUD工作直接省掉。

还有一个关键点:技术栈的一致性。RuoYi是基于SpringBoot + Vue2的前后端分离架构,企业招聘Java开发上手几乎没有门槛。MySQL作为默认数据库,Redis做缓存,Shiro做权限管理,这些都是国内团队最熟悉的技术组合。对比国外那些MES套件,实施成本动辄几百万,而且报表逻辑都是写死的,想改一个字段往往要联系原厂。开源RuoYi版MES虽然不能直接商用,但作为二次开发基底,灵活性和可控性完全不在一个量级。

2. MES系统整体架构与领域模型:先理解信息流再写代码

2.1 以工单为主线体的数据流转链路

写MES系统最怕一上来就设计数据库表,因为MES的业务数据不是孤立的,而是一条完整的信息链。标准的流转链路是:

  • 创建生产工单(来源于ERP同步、手工录入或计划排产系统)
  • 绑定工艺路线,生成工序计划
  • 工序计划下达到车间班组,形成派工任务
  • 工人在终端(PC端、PDA、工位机)进行报工,填报加工数量、设备编号、操作员
  • 检验人员根据工序质检方案执行检验,记录合格数、不良数、不良原因
  • 不合格品进入返工返修流程,重新排入对应工序
  • 生产完成后进行工序转移,流转到下一工序
  • 最终完工后关闭工单,生成统计报表

我经常跟团队强调:MES的领域模型核心是“工单-工序-操作记录-质检记录”这四张表,其他所有表都是围着它们转的。工单表记录主数据,工序表记录工艺路径的具体节点,操作记录表(也叫报工记录)是每道工序的执行历史,质检记录是质量维度的平行数据流。理解了这四者的关系,MES的表设计就做好了七八成。

2.2 后端工程结构与核心模块拆解

实际开发时,后端包结构我在RuoYi基础上做了一层延伸,建议按业务域分包,而不是全部堆在system模块里:

com.ruoyi.web // 控制层入口,RuoYi原始结构 com.ruoyi.framework // 框架配置、安全、切面 com.ruoyi.system // 系统管理基础模块 com.ruoyi.mes.domain // 工单、工序、报工、质检等实体对象 com.ruoyi.mes.mapper // MyBatis数据访问层 com.ruoyi.mes.service // 业务逻辑层 com.ruoyi.mes.controller // MES模块的控制器

MES模块内部按业务职能分成几个子域:

  • 基础数据域:产品、物料、工艺路线、工序字典、设备台账、班组与人员
  • 计划域:生产工单、工单拆分、工单变更
  • 执行域:派工、报工、工序转移、暂停/恢复
  • 质量域:检验任务、检验项、不合格品登记、返工返修流程
  • 报表域:产量汇总、工时统计、不良率统计、工序在制

每个子域内部CRUD逻辑相对独立,但跨子域的状态联动是MES最复杂的地方。比如工单状态从“已下发”到“生产完成”,需要检查所有工序是否都已完工;报工数量超过工单数量时需要重新校验是否触发超额提醒。

2.3 前端Vue2页面如何组织才能扛住车间使用

RuoYi前端默认是Vue2 + Element UI,页面组织方式基本是“一模块一目录”,MES模块我会额外拆分:

src/views/mes/workorder // 工单列表、工单新增、工单详情 src/views/mes/process // 工序计划、工序派工、工序转移 src/views/mes/feedback // 报工录入、报工记录查询 src/views/mes/quality // 质检任务、检验记录、不合格品管理 src/views/mes/rework // 返工返修登记与处理 src/views/mes/device // 设备台账与设备状态看板

这里有个实操经验:车间现场的页面和办公室的页面要差异化设计。办公室人员习惯表格列表+筛选,车间工人则倾向于大按钮、大字号、少字段的卡片式操作界面。MES的报工页面我最终做成了“选择工单-显示工序-录入合格数/不良数-提交”的三步卡片流,而不是传统表格,车间反馈好用很多。

Vue2的生命周期这块也是前后端联调时的高频问题。RuoYi生成的列表页通常用created()去调加载列表接口,但如果页面带了Tab页签切换,组件的mounted和activated要分清。比如工单详情页里有“基本信息”“工序记录”“报工记录”“质检记录”四个Tab,如果每个Tab都用created加载,切换到第二个Tab时数据并不会刷新,需要把请求挪到Tab切换事件或子组件内部的生命周期钩子里去触发。这个坑我在做MES的质量追溯页面时踩过,导致工单状态更新后详情页数据一直显示旧值。

3. 从RuoYi框架到MES业务落地:权限设计与核心功能实现

3.1 功能权限和数据权限要分开设计

RuoYi自带了比较完整的权限控制体系:菜单权限、按钮权限、数据权限(通过@DataScope注解实现,按照部门/用户做数据隔离)。但在MES里,这两种权限的重要性排序会发生变化。

功能权限解决“谁能看到这个菜单、谁能点这个按钮”的问题。比如只有质量主管能看到“检验规则配置”菜单,只有车间主任能执行“工单下发”操作。这些直接用RuoYi的@PreAuthorize("@ss.hasPermi('mes:workorder:dispatch')")注解即可,配置在Controller方法上,权限标识符在菜单管理中维护。

数据权限才是MES的重头戏。同一个系统里,车间主任要看全部产线的工单,班组长只能看自己班组的工单,操作工只能看自己名下或本工位的报工记录。RuoYi的默认数据权限是“按部门划分”,MES里直接套用部门往往不够,因为班组的组织关系并不等同于系统里的部门树。

我的做法是在工单表、报工记录表上都冗余一个dept_id和team_id字段,然后在Service层拼接数据范围条件。具体实现是写一个MES专用的数据权限工具类,从当前登录用户读取部门信息,再拼接查询条件。RuoYi框架里登录用户信息存在SecurityUtils.getLoginUser()中,可以拿到SysUser对象的deptId和userId,配合角色里配置的数据范围枚举值(全部、本部门、本部门及以下、仅本人),基本能满足MES多级权限隔离需求。

这里要特别提醒:不要把数据权限完全依赖在SQL注解上。MES业务中很多查询不是单一表的查询,而是多表关联,比如“查工单时显示工艺路线名称和产品名称”,这时候RuoYi的@DataScope注解可以加到方法上,但要求SQL里的别名约定必须一致,否则会拼出错误的SQL。

3.2 工单状态机设计与工序流转的实现要点

工单是MES系统的状态中枢,最忌讳在代码里随意写if (status == 1) { status = 2; }这种硬编码状态流转。我的做法是设计一个独立的状态枚举类,把所有合法流转路径集中管理:

public enum WorkOrderStatus { CREATED(0, "已创建"), RELEASED(1, "已下发"), IN_PROGRESS(2, "生产中"), FINISHED(3, "已完工"), CLOSED(4, "已关闭"), CANCELED(5, "已取消"); private final int code; private final String label; // 构造函数、getter省略 }

在Service层统一封装transitionStatus(workOrderId, fromStatus, toStatus)方法,用数据库乐观锁机制防止并发提交导致状态错乱——更新时加上WHERE status = #{fromStatus}条件,影响行数为0则说明状态已被其他操作变更,直接抛异常提示“工单状态已变化,请刷新后重试”。

工序流转是另一块核心逻辑。每道工序都有状态:待开始、进行中、已完工。工件在工序间转移,本质是上一道工序的完工确认触发下一道工序的可开工状态。实现上我设计了mes_process_task表,记录每道工序的执行状态、开始时间、完成时间、操作班组,并通过previous_process_id关联前一道工序。当前一道工序报工且检验合格后,自动打开下一道工序的“可开工”标志。

工序流转最容易被忽略的是“并行工序”和“返工回路”这两种特殊情况。并行工序指同一个工单的某两个工序可以同时开工(比如不同零件分别加工),这时候不能用单纯的链式状态机,需要用前置工序集合来判断能否开工。返工回路更麻烦,不合格品会重新进入前面的工序,如果不加“是否返工”标记区分,后面的在制品统计和产量统计都会错。

3.3 报工防重复、质检与返工返修模块的设计思路

报工是车间使用频率最高的操作,也是最容易出现脏数据的环节。车间工人一天报工几十次,网络抖动、页面卡顿都可能导致同一笔报工重复提交。我的方案是在报工表上建立业务唯一索引,索引字段为:work_order_id + process_id + user_id + report_time,时间精确到秒。同时在Service层用Redis的setIfAbsent做分布式锁,key为mes:report:unique:{workOrderId}:{processId},锁过期时间设5秒,防止同一工位重复点击。

质检模块与报工是孪生关系。MES的质检通常是工序完工后才能检验,检验结果分为合格、不合格、待判定。不合格品登记时不仅记录数量,还要记录不良代码(如划伤、尺寸超差、漏加工),不良代码用数据字典维护,方便后续做不良分析。这里建议用RuoYi自带的字典管理功能,不要写死枚举在前端,因为制造企业的不良分类经常调整。

返工返修模块我单独说一下。这个模块最容易做成“只是登记一下”,但实际业务上它意味着一次完整的生产子流程:不合格品拆出来建返工单,返工单绑定原始工单,工艺路线可以指定为返工专用路线,返工完成后再检验,检验合格后回到正常流程。RuoYi版MES里通常需要建两张表:mes_rework_order(返工单主表)和mes_rework_process(返工工序记录),同时需要在原工单上增加“返工数量”字段,避免返工数量混入正常产量统计。

3.4 看板与报表的SQL优化实践

MES项目上线三个月后,车间产量看板、不良率趋势图、设备稼动率报表这些需求会集中涌来。这些页面本质上都是聚合查询,最容易写出性能极差的SQL。我的经验是:报表接口不要直接查询业务明细表,而是查询预先汇总好的统计表。

具体做法是设计mes_production_daily表,按“日期+产线+班组+工序+产品”维度每天凌晨由定时任务汇总一次产量、工时、不良数。看板接口只查这张汇总表,秒级出结果。如果业务要求实时性强一些(比如当日看板要看到上午的数据),汇总任务可以每小时跑一次,最多15分钟延迟,车间完全能接受。

定时任务在RuoYi里可以直接用框架自带的任务调度功能,在数据库中配置任务的cron表达式和调用目标类。需要注意:任务类里不要写太多查询逻辑,尽量把汇总SQL封装在单独的Mapper中,避免定时任务串行执行时拖垮主库。我在生产环境遇到过一个问题,某个汇总任务跑了30秒,而车间的报工接口依赖同一张表,导致短暂阻塞,后来通过优化SQL(原来查明细再程序汇总,改成一条SQL用CASE WHEN分组统计)直接把耗时降到2秒以内。

4. 开发中的高频问题与排查修复记录

4.1 Vue2组件的更新与缓存问题

RuoYi前端框架是Vue2,版本比较老,但企业项目里用得稳。开发MES过程中,有几个Vue2典型问题值得记录。

第一个是路由缓存导致的列表刷新失效。MES的工单列表页,在详情页提交操作后返回列表,发现列表数据还是旧的。原因是RuoYi的标签页导航默认开启了keep-alive缓存,组件在第二次进入时不会重新执行created,而是走activated。解决办法是在列表页补充activated钩子,里面调用加载列表的方法。因为created只在组件首次创建时执行,activated每次激活都会执行,这是Vue2生命周期最关键的差异点。

第二个是ECharts图表在Tab页签中的尺寸问题。MES的产量看板放在Tab页里,第一次切到该Tab时图表宽度渲染为0,因为G2/ECharts在容器隐藏时初始化拿不到真实宽度。标准解法是在Tab切换事件中调用chart.resize(),我一般会把图表实例挂到组件的this上,在activated钩子里统一处理。

第三个是与文件相关的场景——质检报告附件、工艺图纸上传。RuoYi默认文件上传使用的是MultipartFile,配合SysFileUtils.upload方法。MES场景里多文件批量上传很常见,我建议直接封装一个FileUpload组件,用Element UI的el-upload搭配:on-success回调,把返回的附件ID存入业务表的附件ID字段(字符串逗号分隔),详情页再根据ID列表回渲染。列表里展示附件用缩略图,点击时用el-image-viewer或video.js直接预览,注意视频文件格式如果是m3u8流,前端需要引入hls.js或video.js插件,工厂监控视频经常是这个格式。

4.2 事务失效与配置错位的排查过程

有段时间车间反馈“报工后产量没有增加,有时候增加两次”,排查后发现是事务配置问题。RuoYi的Service实现类上默认有@Service注解,但没有强制所有方法加@Transactional。报工方法的逻辑是:插入报工记录、更新工单已完成数量、更新工序状态、记录产量缓存。这里必须一个事务,否则插入成功但更新失败时,报工记录就会残留。

事务还有一个应用场景是工单下发操作:修改工单状态、生成工序派工单、给相关人员发送通知消息,三步需要原子性。RuoYi框架里开启事务很简单,直接在方法上加@Transactional(rollbackFor = Exception.class),但要注意两点:第一,方法必须被Spring的代理对象调用,同类内部调用会失效;第二,自定义异常要回滚时rollbackFor必须指定,默认只处理RuntimeException。

另外一个配置方面的坑是RuoYi的application-druid.yml。多数据源配置时,如果主从库的驱动类名和URL写错,启动时不一定报错,但运行中会偶发连接异常。MES场景我建议主库和报表库分开:业务数据在主库,报表数据在备用库,通过RuoYi的@DataSource(DataSourceType.SLAVE)注解指定查询走从库。这样报表统计不影响工单写入性能。

4.3 SpringBoot版本与依赖冲突的避坑清单

RuoYi官方基础版通常基于SpringBoot 2.x的早期版本,比如2.5.x或者2.7.x。有些团队拿到老项目后,习惯性把SpringBoot版本升级到2.7.18,结果遇到各种问题。

我自己遇到过最典型的是:升级SpringBoot后,validation注解(如@NotBlank)不生效。原因是从SpringBoot 2.3开始,spring-boot-starter-validation不再包含在spring-boot-starter-web中,需要单独引入。RuoYi老版本里如果用了Hibernate Validator做参数校验,升级版本后必须检查pom.xml里有没有显式依赖。

还有分页插件的问题。RuoYi使用PageHelper做分页,升级SpringBoot后可能遇到PageHelper版本和MyBatis版本不兼容,报PageHelper无法注册Interceptor的错误。解决方案是固定PageHelper版本5.3.1以上,并且在MyBatis配置中显式声明插件,同时确认mybatis.configuration.map-underscore-to-camel-case配置没被覆盖,否则数据库字段的下划线命名无法映射到实体类驼峰属性,列表查询全部返回null。

最后是文件上传配置,SpringBoot默认单文件上传大小是1MB,MES车间上传设备照片、图纸附件经常几十MB。需要在application.yml中配置:

spring: servlet: multipart: max-file-size: 100MB max-request-size: 200MB

这类问题一般不会在开发阶段发现,都是部署测试环境后试出来的。建议做MES项目时第一时间把上传限制改掉,同时在后端Controller里加文件类型和白名单校验。

4.4 生产环境的高可用配置与上线注意事项

MES一旦上线就是7x24小时不能断的系统。车间停线等系统恢复,损失远超服务器成本。所以上线前一定要做几个基础配置。

第一,MySQL必须开启binlog,并配置每日全量备份+每两小时增量备份。RuoYi的数据库脚本会创建所有表,但表数据量增长到百万级后,没有索引的查询会拖垮数据库。建议提前给报工记录表加联合索引(work_order_id、process_id、report_time),给工序表加(work_order_id、current_status)索引。

第二,Redis必须配置持久化,至少开启AOF。MES里的分布式锁、验证码缓存、登录token都放在Redis中,重启丢失会导致用户全部掉线。RuoYi的token默认有效期30分钟,建议配置Redis哨兵或集群模式,单机Redis在高并发报工场景下可能会有瞬时连接数超限。

第三,前端部署建议使用Nginx,配置gzip压缩和静态资源缓存。RuoYi的Vue项目前端打包产物中有大量chunk文件,首次加载在车间内网的PC上可能很慢,本地局域网还好,如果存在远程分厂访问的情况,建议开启gzip后Vue2页面加载体积能减少60%以上。

5. 从开源版到生产环境:改造RuoYi-MES的四个关键动作

5.1 验证码、接口鉴权与二次认证的定制

RuoYi框架默认登录需要输入验证码,在工厂内网的MES工位机上,工人频繁登录很不友好。很多团队都会去掉验证码,我的方案是保留验证码但做成可配置项,通过application.yml中的配置决定是否启用,而不是直接删除代码,这样外网环境还能开启验证码保护。

mes: captcha-enabled: false

RuoYi的验证码逻辑在SysLoginController中,改造时保留原有校验代码块,用条件判断跳过即可。

接口鉴权方面,RuoYi默认使用Shiro做登录认证,/login接口放行,其余接口需要token。由于MES报工终端可能涉及多个厂区PC,建议对报工提交接口额外增加签名校验(比如在Header中传sign,后端用密钥+时间戳生成签名比对),防止内部接口被扫描工具恶意调用。

5.2 用户存量导入与组织架构初始化

企业上线MES时,最大的工作量不是写代码,而是整理基础数据。几百个工人、十几个班组、多套工艺路线,都要在系统上线前初始化完成。RuoYi的用户管理支持Excel导入,但默认的导入模板字段有限,我建议扩展为MES专用导入接口,包含工号、姓名、所属班组、岗位、工种、手机号等字段,同时在导入时自动创建对应的班组角色并分配数据权限。

组织架构方面,制造企业的“部门-班组-工位”层级与RuoYi默认的“部门-子部门”模型有些出入。我一般建议把班组建模为部门树下的节点(如部门编码MES-WC01),然后用数据字典维护工位列表。工位和用户的关系通过岗位关联,报工界面按工位过滤任务时,直接查询该工位绑定的排班和任务即可。

5.3 与ERP和设备的接口对接

MES不可能独立存在,至少要和ERP系统做数据交互。标准做法是中间表模式:ERP创建生产订单后写入中间表,MES定时同步,MES完工后写回中间表,ERP读取更新订单状态。用RuoYi的定时任务功能实现同步逻辑,注意同步失败要有重试机制,中间表增加sync_status字段(0未同步,1已同步,2失败),失败时在系统管理-定时任务日志中排查原因。

设备对接方面,现代车间设备大多支持OPC UA或者Modbus TCP协议,采集设备状态和工件计数,MES直接对接设备数据源。如果设备老旧不支持,只能靠工人手工报工,那就要把报工节点做得很简单:一键完成、自动带出当前时间,尽量减少操作次数。

5.4 数据追溯与生产批次的闭环管理

MES上线最直接的收益就是追溯能力。客户投诉某个批次的产品有质量问题时,需要反查出这批产品用了哪批原料、经过了哪些设备、由谁在什么时间加工、检验结果如何。实现质量追溯的前提是基础数据齐全:物料批次信息在工单创建时录入,报工时记录物料批次号,检验表记录检验员和检验设备。RuoYi的通用查询加筛选可以基本满足追溯需求,但更好的做法是单独做一个“批次追溯”页面,输入产品条码或批次号,展示树形结果:产品批次 -> 生产工单 -> 各工序报工记录 -> 原料批次 -> 质检报告。这个页面前端用Vue2的树形表格组件实现,后端用一次查询所有关联数据,避免多次联查导致性能过慢。

6. 实战心得:这套系统的定位、边界与后续演进

做了几个MES项目后,我对RuoYi版MES系统的定位越来越清晰:它不是拿来即用的成品商业软件,而是最适合制造企业做数字化转型起步的技术底座。

企业MES选型通常面临三个选择:买商业套件、找外包定制、自己组建团队开发。商业套件功能成熟但价格高,而且柔性制造场景下定制成本极高;外包定制交付快但维护难,业务逻辑一变就要重新找人;自己用RuoYi+SpringBoot+Vue2开发,初期周期会稍长,但系统完全掌控在自己手里,后续加设备对接、加报表看板、调整工艺流程都方便。

我个人的建议是,如果企业内没有全职的开发团队,至少要有一个懂Java的骨干,再加上一个有车间管理经验的生产主管,两者配合才能把MES做好。RuoYi解决了通用底层问题,但MES的大量定制逻辑必须深入了解生产业务才能设计合理。

这里的边界也很重要:MES系统解决的是生产执行层面的数字化,不要试图把财务核算、供应链计划这些ERP的核心职责塞进MES。有些需求方想让MES系统直接生成计件工资,那不是不能做,而是要做的话就要和考勤、请假、加班规则打通,复杂度完全超出MES范围。我一般会建议这类需求走独立模块或让ERP处理,MES只负责提供准确的工序工时和报工数量数据,系统集成食反而更清晰。

从技术演进角度看,这套架构在后续扩容上也有明确的路线:前端Vue2如果要迁移Vue3,RuoYi生态有对应的版本,但迁移成本主要在自定义组件和第三方库的兼容性上,如果当前运行稳定,不急着动;后端SpringBoot升级到3.x需要JDK17,但MES这种业务系统对Java版本不敏感,稳定优先。

我另一个深刻的体会是,MES系统的成功最终取决于车间工人愿不愿意用、用得好不好。技术选型再先进,如果报工界面要翻三页才能找到按钮,工人很快就会抵制。我在部署时给工位机的浏览器做了精简配置,默认打开的首页就是当日报工任务列表,大字号、少输入、单选多,尽量减少键盘操作。这套交互上的投入,比后端功能的复杂度更值得。

最后,如果你正打算用RuoYi框架做MES系统,建议先花一周时间在车间里待着,看一遍真正的生产流程再动手建表。技术架构两三周就能搭好,但业务流程梳理不清晰,后面返工改代码的时间会翻好几倍。生产执行系统的灵魂不在代码里,在对现场的理解里。

返回列表