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

资讯详情

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

SpringBoot+Vue+MyBatis开源MES系统:完整源码与部署实战

SpringBoot+Vue+MyBatis开源MES系统:完整源码与部署实战 最近我整理了一套自己做过且跑通了的MES系统制造执行系统把完整源码和部署教程都开源了出来。技术栈就是标题里写的这一套SpringBoot Vue MyBatis MySQL前后端分离能直接部署上手改。陆陆续续有不少朋友问我是怎么设计的、数据库怎么建的、前后端怎么安全联调的我干脆把这套系统从设计到落地的完整思路写成一篇实战记录尽量说人话少讲虚的多讲能落地的。这套系统适合什么人如果你是做智能制造、工厂数字化相关开发的工程师或者公司要上MES但预算有限想先做个原型跑通流程再或者你是个学生想找一个完整的、前后端分离的企业级项目来练手那这篇内容基本就是照着抄作业的路子。1. 系统设计思路为什么用这套技术栈1.1 MES到底是什么核心解决什么问题先把这个概念讲透。MES全称是Manufacturing Execution System翻译过来是制造执行系统。它和ERP的区别很多人都没搞清楚一句话说明白ERP管的是“计划层”告诉你要生产什么、什么时候生产、需要多少料MES管的是“执行层”解决的是生产指令下达之后车间现场怎么把这件事做出来、做到什么程度、用了多少工时、有没有质量异常。我做的这套MES系统重点覆盖了这么几条核心业务链条从生产工单生产任务的创建开始经过工艺路线分配、物料准备、工序派工、现场报工、质量检验到最终的成品入库和产品追溯。中间任何一道工序出了问题系统里都能实时看到卡在哪里、是谁报的工、这批产品有没有质量风险。用一个场景化例子来说工厂接到一张2000件产品的订单计划员在ERP里下了一张生产订单。MES系统要做的事情就是把这2000件拆成若干个生产工单分配到不同的产线和工序上去每个工人都能通过电脑或PDA扫码领任务、提交完成数量、报质量异常。这个过程如果靠纸质流转单数据滞后不说还容易丢失和篡改。MES本质上就是把车间现场的人、机、料、法、环全都数字化让管理层随时能看到真实现场。1.2 前后端分离和这套技术选型的取舍现在做企业级系统前后端分离已经是绝对的主流这套MES也不例外。后端提供纯接口REST API前端用Vue框架写单页应用部署时Nginx接管静态资源然后通过反向代理把API请求转发给后端服务。这样做最直接的好处是前端能单独做负载均衡、单独发布后端不重启也能换页面配合上移动端扫码枪访问同一个API一个后端能养活多个前端入口。技术选型上我挨个说下理由SpringBoot做企业级后端Java是绕不开的选项SpringBoot的自动配置和Starter机制大幅降低了项目搭建成本。尤其适合MES这类业务逻辑密集、需要对接大量第三方硬件和系统的场景生态齐全遇到问题网上一搜一大把解决方案。VueMES系统的界面很特殊既有PC端功能复杂的表单和页面工单创建、参数配置又有车间大屏看板、产线平板这种追求实时刷新的界面。Vue的响应式特性和组件化设计对这类高频变动界面的开发效率非常高。如果只用一个老掉牙的JSP模板引擎前端代码会越写越乱。MyBatis为什么不用更”省事“的JPAHibernateMES系统的报表和统计查询SQL异常复杂经常出现多表联查子查询动态条件拼接。MyBatis允许我直接写原生SQL执行效率和调优空间完全在自己手里。对数据库比较熟的同学用起来会非常顺手这个选择在后期做性能优化时让我省了非常多力气。MySQL中小型工厂的数据量远没有互联网电商那么夸张MySQL在合理设计索引的前提下几千张表、几千万行的数据量也能扛得住。部署简单、运维成本低、免费对预算敏感的制造企业来说是最务实的选择。2. 数据库设计与核心模块拆解2.1 MES系统的核心业务模块我在设计这套系统时将功能拆成了六个核心模块。每一个模块都是根据真实工厂的业务场景提炼出来的不是凭空造的功能。基础数据模块。MES系统不能凭空跑起来它需要大量”白名单“数据支撑。比如物料档案、产品BOM物料清单、工艺路线、工序定义、工作中心和设备台账。这块是整个系统运转的地基基础数据不准确后面的工单和生产执行都会乱套。我在系统里专门做了一个数据导入的Excel模板让用户能批量导入物料和设备数据不然几百种物料全靠手工录入得录到天荒地老。生产计划模块。负责承接ERP或手动创建的生产工单把一个大的销售订单或者生产计划细化成车间可执行的任务。工单里会带上产品编号、计划数量、计划开工和完工日期、优先级等信息这是MES系统里最核心的数据载体。生产执行模块。这是MES的门面。工人登录系统之后能看到分配给自己的待办任务扫描产品或工单条码进行工序流转、报工和完工入库。报工的核心就是记录“哪道工序、谁、在什么时候、完成了多少合格品、多少不良品”。这些数据是后续所有统计报表的原始来源。质量管理模块。包括工序检验首检、巡检、完工检和不良品处理。检验记录会和工单绑定一旦某批产品出现质量问题可以通过产品条码追溯到每一道工序的检验数据这也是制造企业选择MES系统的核心动力。设备管理模块。记录设备的基础状态、点检保养计划和设备故障报修。这部分我做得相对轻量但对于有OEE设备综合效率统计需求的工厂这些是基础数据来源。报表看板模块。包括完工统计、不良率统计、工时统计、产线实时产量看板。这部分直接决定MES系统在车间和管理层那里的接受度数据清楚直观工人才愿意用管理层才愿意看。2.2 数据库表结构设计要点数据库表设计我遵循了几个核心原则也是在开发中踩过坑之后总结的经验首先所有的核心业务表都以工单状态为驱动来设计。在production_order表生产工单主表里我专门设计了一个状态字段order_status常见状态包括待下发、已下发、生产中、已完工、已关闭。所有的业务流程流转都是围绕这个状态值来推进的。这样做的好处是任何时候你查一个订单的状态只需要看一眼这个字段就够了而不用通过多个子表的关联数据倒推。第二条码追溯是MES系统的灵魂。我设计了一张product_trace表产品追溯表这张表记录了每个唯一产品序列号SN从第一道工序到完工入库的全过程。每一道工序报工的时候系统会自动往这张表里插入一条工序记录包括操作工、设备编号、工序名称、检验结果、时间戳。将来用户扫到这个SN码就能看到这个产品完整的一生。第三所有业务变更都要留痕。MES最忌讳的就是数据被静默修改。所以核心业务表我都加了一张流程记录表比如order_process_log表专门记录工单的每一次状态变化。这是很多商业MES产品都会做的事自研的时候也千万不能省。以一个例子来说工单状态流转的表设计思路是这样的CREATE TABLE production_order ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(64) NOT NULL UNIQUE COMMENT 工单编号, product_id BIGINT NOT NULL COMMENT 产品ID, plan_qty INT NOT NULL COMMENT 计划数量, completed_qty INT DEFAULT 0 COMMENT 完成数量, defected_qty INT DEFAULT 0 COMMENT 不良数量, order_status TINYINT NOT NULL COMMENT 1-待下发 2-已下发 3-生产中 4-已完工 5-已关闭, priority TINYINT DEFAULT 5 COMMENT 优先级 1-紧急 5-普通, plan_start_time DATETIME, plan_end_time DATETIME, create_by VARCHAR(32), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 生产工单表;状态字段必须加索引因为这是查询和统计最常用的条件字段。order_no也要唯一索引通过扫码枪查询工单速度才能快到无感。3. 后端核心实现SpringBoot与MyBatis的落地细节3.1 后端工程结构怎么分我采用的是标准的多模块单工程方式没有拆成微服务。原因是MES是典型的强一致性业务系统拆成微服务反而会引入分布式事务的复杂度对中小工厂来说属于过度设计。整个后端工程按业务边界拆包这个结构清晰且容易扩展src/main/java/com/example/mes/ ├── controller/ 控制器层只做参数接收和响应封装 ├── service/ 业务层核心业务逻辑都在这 │ └── impl/ ├── mapper/ MyBatis的Mapper接口 ├── entity/ 数据库实体 ├── dto/ 数据传输对象后端返回给前端的数据结构 ├── vo/ 视图对象特殊拼接的查询结果 ├── common/ 通用类统一返回结果、异常处理、常量 ├── config/ 配置类跨域、拦截器、MyBatis配置 └── utils/ 工具类条码生成、日期处理、JWT工具等3.2 工单状态驱动的核心业务逻辑MES系统里最麻烦的业务点就是不同状态下的工单能不能执行某个操作必须严格校验。我的解决方式是在Service层写了一个固定的“状态机校验逻辑”所有会改变工单状态的操作都必须先走这个方法校验。比如“生产报工”这个操作它要求工单必须是“已下发”或“生产中”状态而且操作人必须属于该工单绑定的车间。这些校验我用一个统一的方法做掉而不是在每个Service里各写各的public void checkOrderStatus(ProductionOrder order, Integer expectedStatus) { if (!order.getOrderStatus().equals(expectedStatus)) { throw new BusinessException(当前工单状态不支持该操作期望状态为[ OrderStatusEnum.getDesc(expectedStatus) ]实际状态为[ OrderStatusEnum.getDesc(order.getOrderStatus()) ]); } }每次报工完成之后根据完成数量、计划数量、不良数量系统自动计算下一步状态。如果完成数量加上不良数量已经超过计划数量就直接把工单标记为“已完工”否则保持“生产中”。这个逻辑保证了业务数据的自动流转也避开了人工改状态漏改的风险。3.3 条码生成与追溯实现追溯是MES的强需求没得商量。产品的唯一序列号SN我采用的编码规则是产品编码 年月日 四位流水号 校验位例如PRD001-20241201-0001-7。这样设计的好处是通过扫码就能知道这个产品是哪天生产的第几个产品方便排查。校验位用简单的奇偶校验即可用于快速判断条码是否在传输过程中扫描出错。生成条码之后每一道工序报工的时候都会调用一个报工方法同时把工序记录插入到追溯表中Transactional(rollbackFor Exception.class) public void reportWork(WorkReportDTO dto) { // 1. 校验工单状态合法 // 2. 更新工单完成数量 // 3. 插入工序报工记录 // 4. 插入追溯记录 // 5. 如果是最后一道工序自动触发入库逻辑 }这里注意报工这个方法必须加上Transactional因为涉及到了5张表以上的写操作。任何一个步骤失败整批数据都必须回滚否则会出现工单数量更新了但追溯记录没写上的问题。这种数据一致性问题在MES系统里是绝对不允许出现的。3.4 MyBatis的使用细节和优化MyBatis在这套系统里承担所有数据库读写操作。先说一个我强烈建议的做法开启下划线转驼峰的全局配置。mybatis: configuration: map-underscore-to-camel-case: true数据库字段是plan_qty实体属性写成planQty没有这个配置时你查出来的一堆字段全是null。这个配置项几乎90%以上的MyBatis初学者都会踩坑。其次是动态SQL的运用。MES系统的列表查询条件非常多工单列表至少有订单号、产品、状态、时间范围、车间五个条件而且每个条件都可选可不选。这种场景最适合MyBatis的where标签select idselectOrderPage resultTypecom.example.mes.vo.OrderVO SELECT * FROM production_order where if testorderNo ! null and orderNo ! AND order_no LIKE CONCAT(%, #{orderNo}, %) /if if testproductId ! null AND product_id #{productId} /if if testorderStatus ! null AND order_status #{orderStatus} /if /where ORDER BY create_time DESC /select用where标签不用写WHERE 11这种脏做法它会自动处理掉多余的AND关键字。这套代码在数据量上来之后依然跑得很稳。实际开发中我还遇到过批量插入的大量数据量问题。MES报工可能出现一次性录入几百条报工记录的极端场景我一开始用一个循环每次insert一条结果性能极差。后来改成MyBatis的批量插入标签性能提升上百倍insert idbatchInsertTrace INSERT INTO product_trace (sn_code, order_id, process_code, operator, device_code, status, create_time) VALUES foreach collectionlist itemitem separator, (#{item.snCode}, #{item.orderId}, #{item.processCode}, #{item.operator}, #{item.deviceCode}, #{item.status}, NOW()) /foreach /insert在连数据库的URL上必须加上allowMultiQueriestrue参数否则MySQL默认不支持这种多条INSERT合并的SQL语句。3.5 权限设计与登录鉴权MES系统涉及生产数据权限设计不能太随意。我没有用Shiro或Spring Security这种重量级安全框架原因是它们配置复杂且重对这个项目来说有点重武器打蚊子。我采用JWTJSON Web Token 拦截器实现无状态登录鉴权。具体逻辑是用户登录成功后后端生成一个有效期为8小时的JWT Token前端把它存在本地。后续每个请求在Header里带上Authorization: Bearer token后端通过拦截器验证Token的有效性解析出用户ID和角色。角色权限上我设了四种管理员、计划员、车间主管、操作工。每种角色能访问的接口通过在后端拦截器里配置一个URL和白名单集合来控制。比如操作工角色只能访问报工相关接口只有计划员才能创建和下发工单。从实际实施效果来看这套轻量级权限方案完全满足MES的权限控制需求部署简单也给读者减少一些学习成本。4. Vue前端实现与关键页面4.1 前端工程结构和脚手架前端是基于Vue 2配合Element UI组件库开发的用Vue CLI快速搭建。为什么选Vue 2而不是Vue 3主要是考虑到Element UI对Vue 2的生态支持最成熟、稳定对MES这种重表格、重表单的应用来说开箱即用的组件非常多能大幅加速开发。核心目录结构src/ ├── api/ 按模块封装的axios请求 ├── assets/ 静态资源 ├── components/ 公共组件表格、表单弹窗、二维码组件等 ├── router/ 前端路由配置 ├── store/ Vuex状态管理登录用户信息、菜单权限 ├── views/ 页面目录 │ ├── dashboard/ 数据看板 │ ├── order/ 工单管理 │ ├── production/ 生产执行 │ ├── quality/ 质量检验 │ └── system/ 系统管理 └── utils/ 工具类request封装、日期格式化、条码解析前端请求统一走封装好的request.js工具里面配置了基础的baseURL、超时时间、请求拦截器自动附加Token和响应拦截器统一处理HTTP错误码和业务错误码。4.2 生产报工页面怎么做工人每天使用频率最高的就是“报工”页面这个页面我设计的原则是越简单越好让工人不培训也能上手。页面上有一个大号输入框旁边配一个扫码枪的图标。工人或产线设备通过扫码枪把SN码或工序条码扫进去前端系统自动查找工单拉出该工单的所有工序然后工人只需要输入“完成数量”和“不良数量”点击提交即完成报工。这里有一个关键细节大量频繁的扫码提交前端必须防止重复提交。如果工人双击了提交按钮后端就会插入两条相同的报工记录造成数量翻倍。所以在提交方法里我加了防抖处理submitting: false, submitWork() { if (this.submitting) return; this.submitting true; reportWork(this.form).then(() { this.$message.success(报工成功); this.resetForm(); }).finally(() { this.submitting false; }); }仅靠前端的防抖还不够后端也需要做幂等性处理。我在工单表的设计里加了一个报工批次号的字段前端每次提交报工都生成一个UUID作为批次号后端在插入数据之前先去查询这个批次号是否已经存在存在则直接拒绝重复插入。这样前后端双层防护数据安全的可靠性才足够。4.3 产线看板如何实现实时刷新车间大屏看板是每个工厂老板最爱看的页面也是MES系统除了报工外第二大常用页面。它的核心需求是不能手动刷新页面要自动、准实时地显示产量、不良率、设备状态。实现方案有WebSocket和轮询两种。考虑到车间网络环境不一定稳定我用的是定时轮询数据轻量化的方案。在前端用一个setInterval每5秒钟调用一次后端接口把当前的完成数量和不良数量拉下来更新到页面mounted() { this.loadData(); this.timer setInterval(this.loadData, 5000); }, beforeDestroy() { clearInterval(this.timer); }这里注意一个Vue的坑组件销毁前必须清除定时器否则会因为在后台一直请求接口造成浏览器卡顿和无效的服务器压力。还有一点数据量大之后不要每次刷新都把原DOM全部替换应该用Object.assign或直接修改响应式对象的某个字段让Vue只更新变化的部分节点。4.4 路由守卫与登录跳转MES系统的角色权限不仅后端要控制前端也必须配合。我在router.beforeEach里加了全局前置守卫判断当前用户是否已登录以及是否有路由访问权限router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { next(); } else { if (!token) { next(/login); } else if (to.meta.roles !to.meta.roles.includes(store.getters.role)) { next(/403); } else { next(); } } });4.5 Vue打包部署的坑前端开发完成之后进入部署阶段npm run build打包生成dist目录这个目录部署到Nginx下即可运行。这里我踩过一个非常典型的坑打包之后刷新页面404。原因很简单Vue是单页应用路由切换是前端内部的history.pushState完成的Nginx没有对应的物理文件。用户访问/order/list这路由刷新时Nginx就去服务器上找/order/list这个路径的文件当然找不到。解决办法是在Nginx的location /配置块里加一段try_files指令让所有找不到的请求都回退到index.html由前端路由自己判断location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }如果不加这段部署上线后只要一刷新页面就是404用户会直接以为系统崩了。5. 完整部署教程从零到上线可运行5.1 环境准备需要装哪些软件在开始部署前先把所有依赖装好。我给出一份经过反复验证的、最稳妥的环境版本组合。版本这个东西很神奇Java和Node版本太高或太低都可能引发莫名其妙的问题所以一定要按这个版本来软件推荐版本说明JDK1.88u202Java后端运行环境1.8最稳Maven3.6.3后端构建工具MySQL5.7.40 或 8.0数据库5.7和8.0都支持Node.js14.x前端构建环境16会提示依赖不兼容npm6.14.x随Node自带Nginx1.20.x前端部署和反向代理如果你用的是IDEA开发环境直接在IDEA里装好Lombok插件然后导入后端工程即可。第一次导入时Maven会自动下载依赖建议配一下阿里云的Maven镜像源否则国内的网络环境下载依赖会慢到怀疑人生。5.2 后端启动步骤**第一步初始化数据库。**打开MySQL命令行执行项目里sql/init.sql文件。这个脚本会创建数据库、所有业务表和初始化数据包括默认管理员账号admin / admin123。执行完可以简单检验一下USE mes_db; SHOW TABLES;能看到一张张业务表说明数据库初始化成功。**第二步修改数据库连接配置。**后端工程的application.yml文件里配置了数据源你需要修改三个参数数据库地址、用户名、密码spring: datasource: url: jdbc:mysql://localhost:3306/mes_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword强烈建议在url后面加上serverTimezoneAsia/Shanghai参数。MySQL 8.0默认时区是UTC如果不指定时区Java和数据库之间交互时间数据会产生8个小时的时差所有的时间统计都会错位。**第三步启动后端服务。**在项目根目录执行mvn spring-boot:run或者用IDEA直接运行MesApplication.java的main方法。看到控制台输出Started MesApplication且Tomcat端口8080启动成功说明后端就绪。可以用浏览器访问http://localhost:8080/api/auth/login做一次接口验证。**第四步验证后端接口。**这里我提供一个最直接的接口调试方式用Postman或直接用浏览器访问测试接口地址。如果返回正常的JSON格式的数据说明前后端基础链路畅通。5.3 前端启动与联调配置**第一步安装依赖。**进入前端工程目录执行npm install这里有一个高频问题npm install报错常见原因是Node版本太高导致node-sass等依赖编译失败。解决办法是用Node 14版本重新安装或者改用npm install --legacy-peer-deps参数忽略依赖冲突。**第二步配置开发环境代理。**前端在开发模式下和后端不是同一个端口必然存在跨域问题。开发环境我用Vue CLI的devServer.proxy配置解决也就是前端请求统一走/api前缀代理到后端的8080端口// vue.config.js module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };这样在前端代码里调用接口时请求地址写成/api/order/list就可以了浏览器发出的请求都走8081端口的Nginx代理再由代理转发到8080的后端绕开了浏览器的跨域限制。**第三步启动前端开发服务。**执行npm run serve访问http://localhost:8081/login输入默认账号密码能正常登录并进入系统界面前后端联调就成功了。5.4 生产环境Nginx部署生产环境和开发环境有本质区别前端依然访问/api开头接口但这次不是本地代理而是Nginx反向代理到后端。安装好Nginx后修改/usr/local/nginx/conf/nginx.conf或/etc/nginx/nginx.confserver { listen 80; server_name localhost; # 前端静态资源 location / { root /opt/mes/dist; index index.html; try_files $uri $uri/ /index.html; } # API反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 大文件上传限制MES经常会有导入导出Excel操作 client_max_body_size 50m; }配置完成后执行nginx -t检查配置语法确认无误后nginx -s reload生效。这里涉及到一个部署细节生产环境后端接口地址是带/api/前缀的但SpringBoot的Controller里并没有定义这个前缀所以我需要给后端接口统一加一个server.servlet.context-path: /api配置server: port: 8080 servlet: context-path: /api这样后端所有接口都自动挂在/api路径下和Nginx的代理路径正好对应上了。这个细节在联调的时候最容易出幺蛾子接口一直报404查来查去是路径前缀的问题。5.5 部署后的冒烟测试部署完成后我一般会按下面的顺序做一遍冒烟测试全部通过才算是系统正式可用访问系统首页是否能正常跳转到登录页输入默认管理员账号登录是否成功创建一个新的产品信息确认下拉框能否加载出来创建一个生产工单并把状态流转从“待下发”改到“生产中”发起一次报工确认产线看板上的产量数字是否增加导出一次Excel的工单列表确认Nginx的client_max_body_size配置不影响下载刷新任意一个业务页面确认404问题已经解决。6. 常见问题与排查技巧6.1 前后端联调最典型的几个报错及处理前后端联调阶段我遇到并解决的问题按频率排个序供大家直接参考跨域报错。浏览器控制台显示Access to XMLHttpRequest at http://localhost:8080/... from origin http://localhost:8081 has been blocked by CORS policy。这个报错是联调第一道坎开发环境如果你没有配置代理或者走了错误的请求路径就会出现。排查思路先确认前端请求地址是不是以/api开头走代理如果请求直接写死了http://localhost:8080那就绕过了代理必须让后端开启CORS配置二选一。我这里的实践是开发环境全部走代理生产环境全部走Nginx反向代理所以后端默认不开启跨域配置。统一响应格式与前端解析不一致。我后端定义了一个统一的ResultT结构格式为{code: 200, message: success, data: {...}}。前端请求封装里也写了逻辑当code ! 200时弹出业务错误信息。但是如果后端有某个接口自己拼了个Map返回或者返回值结构不对前端的response.data.data解析就会拿到undefined。排查方式很简单打开浏览器F12的Network面板看接口响应体的JSON结构和后端Entity/DTO定义一一比对。时间格式错乱。后端返回给前端的时间是2024-12-01T10:30:00这种带T的格式前端直接显示会很别扭。我在后端全局配置了Jackson的日期格式统一返回yyyy-MM-dd HH:mm:ssspring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT86.2 数据库与MyBatis高频坑数据库这块是问题高发地我把每次排查出的典型问题记在小本本上现在直接公开。MySQL 8.0的驱动类名区别。如果你用的是MySQL 8.0驱动类要写com.mysql.cj.jdbc.Driver而MySQL 5.7的驱动类是com.mysql.jdbc.Driver。两者写错其中一个启动时报ClassNotFoundException。这个错位的坑肿么解决直接看自己pom里引的mysql-connector-java的版本8.x就用带.cj的5.x就用旧的全类名。表字段名是MySQL保留字。我早期设计表的时候给表加过一个字段叫order字段结果MyBatis执行SQL一直报语法错误。后来发现order是MySQL的保留关键字用于排序。解决方式是给字段起名加上前缀或使用反引号括起来order。我后来统一把所有字段名都用业务前缀区分如order_no、order_type彻底规避了保留字问题。MyBatis返回的Map字段大小写问题。有时候为了图方便查询SQL会用select直接返回MapString, Object结果发现有的字段名变成了大写ORDER_NO或者下划线命名没转成驼峰前端取不到数据。解决方式是在SQL里用别名显式指定返回字段名或者干脆定义对应的VO类不要用Map接收查询结果这样前后端的字段约定才能明确稳定。批量插入的SQL长度限制。用MyBatis批量插入时如果一次性插入10000条记录生成的SQL字符串会很长可能超过MySQL的max_allowed_packet大小限制报PacketTooBigException。解决方式有两个调整MySQL的max_allowed_packet配置参数比如调到64M或者在代码里给批量操作分批比如每500条一提交。我实际使用的是分批提交方案更可控也更稳妥。6.3 部署阶段的三大高频故障3306端口被防火墙拦截MySQL连不上。后端报Communications link failure请先检查服务器防火墙是否放行3306端口云服务器还要检查安全组。生产部署时数据库端口尽量限制只允许内网IP访问不要暴露到公网。Nginx配置反向代理后接口404或502。404要检查proxy_pass的地址是不是正确、后端context-path路径是不是/api、Nginx的location是否写了/api/502要检查后端服务有没有启动、端口是不是被占用、Java进程是否还活着。排查命令很固定# 检查后端进程 ps aux | grep java # 直接调后端接口 curl http://127.0.0.1:8080/api/order/list # 检查Nginx日志 tail -f /var/log/nginx/error.log静态资源加载失败页面白屏。前端打包后访问页面是白屏F12看到静态资源js、css返回404或403。大部分情况是Nginx的root路径配置错误dist目录下index.html的绝对路径和root参数拼起来得不到正确的文件。比如root /opt/mes; index index.html;实际访问时会去找/opt/mes/dist/...但真正的文件却在/opt/mes下两者就对不上了。检查时直接看Nginx日志里的实际访问路径就能定位。7. 这套系统后续还能怎么扩展系统上线之后我一直在思考制造业客户还会提什么需求。这里分享几个我从需求池里筛出来并觉得有价值的扩展方向给大家提供参考。第一是对接ERP系统。目前的工单是手动创建的但严格意义上来讲MES的工单应该来自ERP的生产订单。后续可以通过REST接口或者中间表的方式定时从ERP拉取订单数据自动生成MES工单全链路自动化就闭环了。第二是接入手持PDA和工业扫码枪。现在PC端流程已经完整下一步可以做一个移动端适配的页面或者对接安卓的PDA设备让工人不需要坐在电脑前在产线旁拿着PDA扫码就能报工、查询、处理异常。这是实际车间用得最多的交互方式。第三是增加设备数据采集模块。目前设备状态是人工标记的如果现场的设备有PLC或支持Modbus协议可以通过边缘网关直接采集设备运行状态和产量数据做真正的设备联网。这也是很多智能制造改造项目的核心诉求。我在实际开发和部署这套系统的过程中最深刻的感受是MES这个领域并没有很多高深的技术难点真正的难点在于要把制造业复杂的车间业务流程抽离清楚、设计成合理的表结构、再把生产中的每一个动作准确无误地记录成数据。技术是工具业务流程才是这个系统真正的灵魂。希望这篇记录能帮你少踩一些坑也欢迎在搭建过程中遇到问题时回来对照排查。
返回列表