
这套.NET源码搭建的BS版MES生产制造管理系统最初是给一家做汽车零部件的厂子做的。车间里排产数据全靠人工从ERP导出Excel再传到产线质检单都是纸质手填出了问题要翻半天纸箱才能定位到某台设备某个批次。所谓MES就是把这个断层补上让计划下发到工位让报工和质检数据实时回流让每个零件都能做到正向和反向追溯。而源码搭建意味着不是买一套闭源产品回来点配置而是把整个系统的设计权、改造权握在自己手里。这篇就站在实际项目的角度把架构、核心模块、部署上线和踩过的坑一次聊透。1. 项目定位为什么是.NET源码为什么是BS版1.1 业务痛点计划与执行之间的断层制造企业上了ERP不等于车间管理透明化。我们在项目初期整理需求时发现最痛的不是缺软件而是ERP和车间现场之间隔着一道人工墙计划员每天把生产计划从ERP导出成Excel再通过微信群发到各个班组长班组长手写排程完工后由统计员汇总录入系统数据往往滞后半天甚至一天。质量问题更是头疼客户投诉某个批次产品只能靠批次号去翻纸质流程卡一个人翻半天还容易漏。MES要解决的就是把“计划-执行-反馈”这条链路打通。从ERP或手工创建工单系统自动下发到对应产线和工位操作工在终端上点击开工、报工、送检质量数据实时进系统管理层随时能看到在制品、产量、合格率和设备状态。车间数据一旦实时化排产、计件工资、设备OEE这些管理动作都有了数据支撑。这类需求在汽车零部件、电子装配、机械加工行业特别集中特点是工序多、批量小、批次追溯要求高。如果只有几十个人的小作坊Excel够用一旦多车间、多产线、多班组协同纸质和Excel就扛不住了。这时一套能贴近业务定制、能持续改造的BS版MES就成了刚需。1.2 从CS到BS一次架构升级很多老牌MES是CS架构每个客户端都要安装程序车间电脑系统一重装、一升级IT就要跑断腿。我们接手的老系统就是典型的C/S结构现场有十几台工控机每次升级都要逐台部署常常出现部分电脑版本不一致数据格式对不上。BS版最大优势就是部署完全集中在服务器端客户端只要有浏览器就能用升级一次、全员生效远程分厂访问也方便。但BS版并不是把CS界面塞进浏览器就完事。车间网络环境复杂终端设备可能是Win7老工控机也可能是安卓PDA浏览器兼容性和断网处理都要重新设计。我们用BS版后报表和大屏监控直接在办公室浏览器打开车间工位用瘦客户端或平板权限按角色控制多工厂数据归集到同一套代码库后续扩展新车间只需要加组织和设备数据不用再单独部署一套程序。至于为什么选.NET核心原因是客户现有IT资产和团队技术栈都在微软体系内Windows Server IIS SQL Server是现成的开发人员对新版本.NET的上手成本也低。更重要是源码在手遇到问题可以自己调试不会被服务商卡脖子。如果你所在环境以Linux为主那Java可能更合适但在这个项目里.NET就是性价比最高的选择。1.3 源码搭建与“拿来即用”的差别市面上现成的MES产品很多但真正能拿来就贴合工厂流程的很少。所谓源码搭建不是从零写一套轮子而是基于一套可用的源码或开发框架根据工厂具体流程做二次开发。这样既保留了成熟产品的骨架又能把排产规则、报工逻辑、追溯粒度这些核心部分写成自己的。我见过不少类似“基于若依框架的MES”这类快速开发衍生项目开发框架只是把权限、菜单、日志这些通用部分搭好真正体现行业know-how的还是业务表结构和事件处理流程。.NET这边虽然没有那么多现成的MES开源项目但源码搭建的路径是通的拿到基础框架后优先梳理车间流程再把主数据、工单、报工、质量模块逐个落地最后用接口把设备数据接进来。源码搭建也要付出代价企业必须有一个能读懂代码、能维护的小团队。没有这个前提不如老老实实买商业产品。因为系统跑起来后每天有大量业务数据产生任何问题都要有人能定位、能改、能发版。这个项目能成除了技术方案客户的IT团队也参与了全部需求评审和测试否则光靠实施方单方面交付上线后很难平稳。2. 整体架构设计源码级的工程怎么组织2.1 技术栈怎么定基础框架和取舍技术选型决定了后面几个月开发是否顺畅我给这套系统定下的技术栈比较务实不追新但能应对业务发展和新人接手。后端使用.NET 8ASP.NET Core Web API承载REST接口数据访问层以Dapper为主部分简单增删改用EF Core前端使用Vue.js和Element Plus构建成独立的前端工程通过Nginx或IIS部署数据库用SQL Server 2019缓存用Redis异步消息用RabbitMQ定时任务用Hangfire。为什么Dapper和EF Core混用因为MES很多查询是复杂报表和多表关联Dapper写SQL更直观性能也稳而基础数据维护这类CRUDEF Core的变更跟踪能省不少模板代码。混用要注意事务一致性和Session管理我们在架构里统一用UnitOfWork把两类仓储纳入同一事务边界避免“一个接口里Dapper提交了、EF Core回滚了”这种低级事故。模块技术选型使用场景备注服务端框架ASP.NET Core 8 Web API工单、报工、质量、追溯接口支持跨平台部署前端Vue 3 Element Plus生产控制台、报表、系统管理前后端分离数据库SQL Server 2019主业务库、历史库事务性强报表方便缓存Redis 7字典、权限、实时产量缓存必须开启密码和持久化策略消息队列RabbitMQ报工异步、设备事件、打印任务解耦高并发写入任务调度Hangfire日结任务、设备停机告警、数据归档自带面板方便运维选了前后端分离就要考虑跨域、Token认证和部署复杂度。我们最开始也犹豫过是否直接用Razor Pages后来看到车间需要大量的自定义组件、看板、拖拽排产还是分开更合适。开发阶段前端用Vite代理上线后把前端静态文件放IIS或独立NginxAPI走独立域名或子路径就没有跨域问题了。2.2 解决方案的目录结构实战一个解决方案的组织方式决定了新同事加入时能否快速找到入口。下面这个目录结构是我们实际跑起来的看着层多但每个项目职责非常单一依赖方向也清晰。src/ ├─ MES.Api/ // Web API 入口控制器、过滤器、认证配置 ├─ MES.Application/ // 应用服务层业务用例、DTO、校验逻辑 ├─ MES.Domain/ // 领域层实体、枚举、领域服务接口 ├─ MES.Infrastructure/ // 基础设施层仓储实现、EF DbContext、Redis封装 ├─ MES.Jobs/ // Hangfire任务宿主定时任务和后台处理 ├─ MES.Common/ // 通用工具日志、加密、Excel导入导出 └─ MES.Web/ // 前端Vue工程独立package.json核心原则是Domain层不引用任何基础设施所有业务实体只描述数据和规则Application层编排业务用例Infrastructure层实现数据库和外部服务Api层只做参数绑定和返回结果封装。这样做的最大好处是当车间要求从SQL Server换成国产数据库或者Redis换成其他缓存中间件时只需要替换Infrastructure层里的实现上层业务代码基本不动。在MES.Api里还要统一鉴权和异常处理。我们加了全局异常过滤器把未捕获异常统一转换成标准错误码例如40001代表参数错误、50001代表数据库超时、50002代表重复报工。车间客户端拿到错误码弹窗直接显示中文提示不会出现“System.NullReferenceException”这种吓人的信息。这也是源码搭建比买产品更灵活的地方一切交互都可以按现场习惯打磨。2.3 权限模型与基础主数据BS版系统最怕权限失控尤其是MES这种直接绑定生产数据的系统。我们采用经典的RBAC模型用户关联角色角色绑定菜单和按钮权限再加一层数据权限用来限制只能看到本车间或本班组的订单和数据。比如设备操作工登录后只看到当前产线分配给他的待开工任务质检员能看到全车间的报工记录但改不了数量车间主任能看到整个车间报表但不能动工艺参数。基础主数据是整个MES的“字典”没有干净的主数据后面所有功能都是沙子。我们整理了物料表、产品表、工序表、工艺路线表、设备资源表、班组班次表。特别注意物料和产品的区别物料指原材料和半成品产品指最终交付物一个产品可以有多个版本工艺路线工单在创建时要锁定当时的工艺版本避免后续修改工艺导致历史工单追溯错乱。权限之外还有一套完整的操作日志谁在什么时间改了什么数据、登录了哪个IP全部落库。上线初期客户觉得这功能多余后来发生一次“有人误删了排程数据”的事故靠日志半小时内定位到操作人他们才意识到这是源码级系统必须留的后门。这些日志也成了后续追溯审计的重要依据。3. 核心业务模块拆解与实现细节3.1 生产工单和排产先把状态机管好工单是MES的主线所有报工、质检、追溯都围绕工单展开。我们设计工单时先把状态机定死状态包括待排产、已排产、生产中、完工、挂起、关闭六种。界面上的按钮按状态灰度不允许从待排产直接跳到完工所有状态变更都记录了操作人和时间这样流程清晰也不会出现“工单都完工了还有报工数据进来”的脏数据。状态机不是简单的一个字段而是配合数据库里的版本号做乐观锁。更新状态时用WHERE Status oldStatus AND Version version影响行数为0就说明有人并发操作了直接抛异常提示“请刷新后重试”。这个机制在排产调整的时候特别有用计划员和生产主管同时在改一份排程表再也不用担心谁覆盖谁。创建工单时还会做一道防呆校验检查物料是否停用、工单数量是否大于零、工艺路线是否完整、计划交期是否合理。这些校验放在Application层统一执行而不是分散在控制器里保证从API、定时导入、手工创建三个入口进的数据都走同一套规则。工单号生成规则是前缀年月流水号比如MO-202503-00123方便现场人员看一眼就知道是哪年哪月的单。3.2 报工接口事务、锁和防重复报工是高频操作也是最容易出并发问题的地方。一个产线几十个工位同时提交如果每个操作都直接裸更新工单表数据库很快会形成阻塞。我们最终的做法是每个工单的同一道工序用Redis分布式锁串行锁的粒度是工单号加工序号不锁整单这样不同工序还能并行报工同一个工序不会出现两次同时累加数量的情况。下面是一段核心报工接口的实测示例去掉了大量校验代码只看并发控制public async TaskResult ReportWorkAsync(ReportWorkRequest request) { var lockKey $mes:workorder:{request.WorkOrderNo}:process:{request.ProcessCode}; using var redisLock await _distributedLock.AcquireAsync(lockKey, TimeSpan.FromSeconds(30)); if (redisLock null) { return Result.Fail(当前工序正在报工请勿重复提交); } var report new WorkReport { WorkOrderNo request.WorkOrderNo, ProcessCode request.ProcessCode, Operator request.Operator, EquipmentCode request.EquipmentCode, Quantity request.Quantity, Shift request.Shift, ReportTime DateTime.Now }; await _workReportRepository.InsertAsync(report); var order await _workOrderRepository.GetByNoAsync(request.WorkOrderNo); order.CompleteQuantity request.Quantity; if (order.CompleteQuantity order.PlannedQuantity) { order.Status WorkOrderStatus.Completed; } await _workOrderRepository.UpdateAsync(order); await _messageBus.PublishAsync(mes.report.completed, report); return Result.Success(); }这个方案上线后99%的重复提交问题都解决了。剩下的1%是客户端连点两次但网络闪断前一次请求已经落库后一次的锁获取不到直接返回“请勿重复提交”操作工刷新页面就能看到新数量不会把产量报重。报工记录表还有一个唯一索引设计我建议根据业务控制粒度建索引比如工单工序操作工班次报工类型如果有重复数据进来数据库直接拒绝。但索引要克制不要为了防重把所有字段加进去不然插入性能和锁开销都会变大。我们用Redis锁做并发访问控制用唯一索引做最后一道兜底两层保险已经足够了。3.3 质量追溯一码到底的链路查询汽车零部件客户最看重的就是追溯。他们的要求是给一个入厂件编号半小时内还原出它经历了哪些工序、哪台设备、哪个操作工、哪一批原材料、检测了哪些项目。我们用的方案是“批次条码序列号”双轨制原材料按来料批次管理加工过程按批次流转产成品打印序列号条码从装配线开始扫描序列号向前关联所有工序报工记录和物料批次。追溯表的核心结构是一个扁平大宽表而不是递归树。每做完一道工序就插入一条追溯记录字段包含序列号、工序号、设备号、操作工、物料批次号、报工时间、检验结论、工艺参数快照。查询时只需要SELECT * FROM WorkTrace WHERE SerialOrBatchNo code ORDER BY OpTime一条SQL就能拉出整个生产履历而不是递归查多张表性能完全扛得住。SELECT wt.SerialOrBatchNo, wt.ProcessCode, wt.EquipmentCode, wt.Operator, wt.MaterialBatchNo, wt.InspectionResult, wt.OpTime FROM WorkTrace wt WHERE wt.SerialOrBatchNo SerialNo ORDER BY wt.OpTime, wt.SeqNo;这套设计的核心是“参数快照”。设备温度、压力、扭矩这些关键工艺参数在报工时一并写入追溯表后续查质量原因时可以对比同一批次不同设备的数据差异。我们遇到过一批产品平面度超标排查时对比追溯表里的压装参数发现某台压机的实际压力比设定值低了15%一下就定位到设备保养没有跟上。这就是MES追溯比扫码枪记流水账有价值的地方。3.4 设备数据采集和OEE计算只靠人工报工无法统计设备的真实利用率所以这套系统还接了一部分设备数据采集。通过OPC UA或Modbus TCP从PLC读设备状态包括运行、待机、故障、停机。采集服务是一个.NET BackgroundService每个采集周期从配置中心读取设备列表轮询设备点位再推送到RabbitMQ后台写入时序采集表。设备状态不能直接用来算OEE因为“运行中”不一定在产出合格品。我们结合报工数量来计算实际性能效率OEE的公式是可用率×性能率×合格率。可用率设备实际运行时间/计划运行时间性能率实际产出数量/理论可生产数量合格率合格品数量/总产出数量。这样计算出来的OEE才有管理意义不然天天显示设备在跑却看不到产出指标就是骗自己。设备采集最容易踩的坑是高频写库一台设备每秒采集一次几十台设备就是每秒几十条记录直接用EF Core插入一定死。我们的做法是采集服务先把数据批量写入Redis队列或内存缓冲区每满500条或每5秒批量写一次SQL Server写入失败也不影响现场采集因为原始数据还在队列里可以重放。上报到系统里用于实时看板的只取最后状态历史趋势数据走聚合表。4. 高并发与大数据量场景下的源码优化4.1 数据库索引和汇总表优化MES上线三个月后报工表和追溯表的数据量增长非常快日增几万行很正常。刚开始报表页面还能秒开后面越来越慢最典型的是按时间范围查工时产量全表扫描经常把CPU拉满。排查下来发现是索引没有跟上查询习惯业务人员习惯按日期范围车间产线过滤但原表索引只建了主键和工单号。优化时我们重新梳理了高频查询把联合索引按“查询条件中最常见的等值列放在前面范围列放在后面”的顺序建。比如报工查产量最常用车间ID班次日期范围索引字段顺序就建成(WorkshopId, Shift, ReportTime)。千万不要把日期建在最前面因为过滤不了多少数据索引效果反而不如按车间切分。另外专门建了一批“产量汇总表”由Hangfire每10分钟根据报工明细做一次聚合写入车间、产线、产品、班次的产量汇总。报表和看板只查汇总表明细表留给追溯和审计。这样既保证了实时性不至于滞后太多又避免了每次打开报表都全表跑一遍明细页面从十几秒秒变成了500毫秒以内。4.2 缓存和异步消息队列BS版系统大多数请求都是读取比如登录后要拉取工单列表、工艺路线、物料字典如果每次都查数据库数据库压力会成倍增长。我们用Redis缓存了高频且变化较少的数据字典项、用户权限树、物料属性、设备列表。权限变更和主数据维护后发布一个缓存更新消息即可不用等缓存过期保证了数据一致性。报工、设备事件这类写多读少的操作不再同步做所有下游处理。报工完成后发布一条mes.report.completed消息到RabbitMQ由后台任务异步更新生产看板、计件工资、物料消耗和在制品数量。这样接口响应时间从原来的300毫秒降到了100毫秒以内而且下游某个环节挂了不会阻塞主线报工。用消息队列最需要注意的是消息幂等因为网络波动可能让消费端重复处理。我们的惯例是每条消息带一个全局唯一MessageId消费端先查去重表已处理过就直接跳过。这个机制在刚开始容易被忽略但生产环境下重试机制一开重复消费问题就暴露了提前把幂等做了后面省很多心。4.3 报表和历史数据归集MES的报表需求极其碎片化日周月产量、工单报表、质检汇总、设备运行报表、人员绩效每个车间想要的维度都不一样。我们做了可配置的报表引擎业务人员能在后台选择表头、筛选条件、汇总方式保存成自己的数据视图而不是每提一个需求就开发一个新页面。这个功能很受车间主管欢迎因为他们终于可以自己拉想要的数据了。历史数据超过半年的明细表会按月份归档到独立的历史库或表分区在线库只保留最近六个月需要查更早的数据走归档库查询。这样既控制在线数据量又保留完整审计链路。我们用的是SQL Server表分区按月自动切换归档对业务透明查询视图自动跨分区命令行脚本定时执行省心很多。5. 部署实施与上线从测试环境到生产环境的实战5.1 环境准备清单如果服务器没准备好就上线后面会一直为环境问题焦头烂额。我们整理过一份部署环境清单照着检查基本能绕开大半初期故障。服务器我们用了一台16核32G的物理机跑应用和RedisSQL Server单独放另一台物理机8核16GSSD盘。这套配置支撑两三百个活跃用户、同时在线一百左右没有问题。组件版本要求用途重点检查Windows Server2016以上部署环境启用IIS功能.NET Hosting Bundle8.0.xASP.NET Core运行安装后重启IISSQL Server2019以上主数据库开启SQL Server AgentRedis7.x缓存、分布式锁设置密码、禁用高危命令RabbitMQ3.12以上消息队列配置erlang版本匹配Node/Nginx选装前端静态资源可复用到IIS很多部署问题出在应用池没配对。API站点必须选择“无托管代码”还是“.NET CLR v4.0”很多人直接在IIS里盲选。ASP.NET Core应用的应用程序池建议设置为“无托管代码”集成模式因为托管代码不由IIS引擎处理。项目.NET Framework版本则选v4.0。搞混了就会出现HTTP 500.19或者直接502。5.2 发布和配置的完整步骤发布过程看似简单操作细节却多。前端工程运行npm run build生成dist目录放到IIS或Nginx静态站后端在VS里执行dotnet publish -c Release -o ./publish然后把publish文件夹映射到IIS站点。API和前端建议分两个站点部署API站点绑定内网域名或IP端口前端站点通过代理把/api转发到API站。appsettings.json里最需要关注的是连接字符串和各种中间件地址。连接字符串要开启加密连接或最小权限账号Redis必须配置密码RabbitMQ虚拟主机和账号不能使用默认guest。敏感配置不要放明文配置文件我们用生产环境的appsettings.Production.json覆盖并让运维统一管理密钥文件防止源码仓库泄露密码。发布上线最容易被忽略的是IIS的URL重写。前端路由如果使用history模式直接刷新子页面会404必须在站点web.config里加一条重写规则把非文件请求全部指向index.html。如果图省事也可以用hash路由但正式地址会有个#号不太美观。我们在上线前就加了重写规则不然上线第一天就会被“刷新页面白屏”投诉淹没。5.3 上线前的并发测试与数据迁移上线前一定要做并发测试而不是只在测试环境点几遍。我们用了JMeter模拟50个操作工同时报工连续跑10分钟观察接口响应时间、数据库连接数、Redis内存占用。第一次测就发现问题SQL Server连接池耗尽界面大量超时。因为每个接口都开了新的DbContext排查后把数据库连接字符串里的最大池大小设置为200并把短查询接口改为异步才恢复正常。数据迁移是另一个大坑。老系统导出的数据经常有脏的同一个物料编码对应两个名称工时保留三位小数而新系统只有两位旧工单状态命名混乱没法直接映射。我们的建议是迁移前先出一份数据质量报告把空值、重复、超长字段列出来和业务方逐个确认再定映射规则。别指望一晚上写个迁移SQL就能搞定制造业几十万条主数据和报表数据需要反复对账。上线那周还有一道保障所有系统自动生成的报告都要人工抽查。我们每天早会前人工核对前一天的产量报表和纸质交接单连续对了一周确认没有误差后才让统计员停用老系统的录入功能。这个过程虽然繁琐但能让全体员工对系统数据建立信任后面推广阻力小很多。6. 踩坑实录那些文档上查不到的细节6.1 技术问题排查速查表下面这个表里的问题都是我们项目过程中实实在在遇到过的也代表了BS版MES最容易翻车的几个点。整理成速查表方便你踩坑时直接对照。问题现象根因分析解决方案IIS站点提示500.19站点目录缺少IIS_IUSRS读取权限给发布目录加IIS_IUSRS和IUSR读取权限.NET8接口返回500.30Hosting Bundle未安装或版本过低安装对应Hosting Bundle并重启IIS不重启等于没装前端刷新404history路由缺少重写规则web.config加URL Rewrite指向index.htmlRedis连接超时连接池耗尽或安全组未放通使用ConnectionMultiplexer限制连接数检查网络策略报工接口偶发死锁多个事务同时更新工单表分布式锁减小粒度避免长事务中做远程调用报表统计结果不一致汇总表和明细表时间片不同步统一按班次时间片聚合避免跨班次日期混淆追溯查询慢缺少联合索引扫描全表按追溯查询条件建立联合索引并考虑按月份分区页面顶部出现异常堆栈未配置全局异常过滤器加全局异常中间件统一错误码和日志排查问题有个技巧先看IIS日志再看应用日志最后看数据库Session和阻塞链。不要一上来就重启服务否则问题现场没了很难定位。我们的日志体系是Serilog写文件加结构化输出再通过日志平台集中检索出了问题能按工单号、设备号、操作人快速过滤出上下文省了很多扯皮时间。6.2 业务和现场容易忽略的坑技术问题可以靠排查解决业务问题的坑往往更隐蔽。第一个坑是班次归属。车间通常是早班8点到16点中班16点到24点夜班0点到8点但如果有人晚上11点报工按日期算会归到中班按业务规则其实应该归到夜班。我们最后做了一张班次时间配置表所有产量统计和计件工资都按照“启动时间所在的班次区间”计算而不是简单取系统日期。这个规则必须在开发前就是和车间确认清楚否则上线后工资报表全是错的。第二个坑是条码补打。很多产线原始条码在流转过程中磨损、丢失需要补打标签。如果补打条码时不做控制同一序列号可能被打印多次后期扫描到同一序列号对应不同工单追溯链路就断了。我们的处理是补打必须走申请流程系统记录补打原因、原打印时间、补打人同一序列号在有效期内只允许补打一次并在追溯页面上打上“此为补打条码”的标识。第三个坑是车间网络不稳定。虽然工厂内部有覆盖但总有一些角落的网络很弱直接让操作工在BS页面上操作非常痛苦。我们给PDA增加了离线缓存和队列重发机制操作工在断网时也能报工数据存在本地SQLite网络恢复后自动上传。这个功能我们本来觉得用户量不大结果上线后每天都有人在用说明车间环境远没有办公室那么理想。最后一个是培训难度。很多操作工不习惯鼠标键盘操作更别说理解ERP、工序这些概念。我们的工位界面设计成“大字、按钮式、两步内完成”第一步扫描工单条码第二步点击报工。中间所有可选信息都自动带出不允许手工输入再配合两三天的车间现场培训基本能做到不误操作。系统的易用性最终比功能清单更影响成败端到端流程走得顺操作工才愿意天天用。7. 二次开发建议源码在手怎么持续迭代7.1 如何快速读懂一套.NET MES源码由源搭建的项目后续可能由不同的人接手快速读懂源码是团队必须啃的骨头。我的经验是不要从代码第一行开始读而是先摸数据库。打开数据库看表结构表名、字段注释、外键关系业务建模的意图基本都写在表里。先看主数据表再看工单、报工、质量、追溯这四类核心表把数据流向串起来代码里的服务类就豁然开朗了。下一步是跑起来。把解决方案克隆到本地修改连接字符串指到开发库运行API看到Swagger页面后挨个调用接口观察返回结果。再配合断点调试从Controller进入Application再到Repository几次下来就能掌握调用链。遇到看不懂的地方不要硬啃先在代码里搜异常消息或日志关键字顺着错误信息找到对应的处理逻辑比从头到尾读代码高效得多。团队里要约定好扩展规范。新增一个MES业务模块时不允许直接在控制器里写SQL必须先定义DTO和接口再在Application层实现业务规则最后补仓储实现。这样源码框架才能保持稳定不至于三个月后变成无人能维护的“大泥球”。同时业务逻辑尽量写在领域服务或应用服务里不要在控制器和数据库之间到处撒代码这条规则我们吃过大亏才定下来。7.2 持续集成和版本管理源码已经有了下一步就是用流程把改动管起来。我们用Git做版本管理主分支master存可发布版本develop是日常开发集成分支每个功能一个feature分支验收后合入develop发版时从develop合到master并打Tag。这套分支策略不复杂但能保证线上代码永远是稳定版本开发同学也不会互相覆盖。数据库结构变更必须和代码同步。我们用了一组数据库脚本目录每个脚本文件名带上版本号和日期比如V20250310001_AddWorkReportIndex.sql发布时按顺序执行。FluentMigrator或者手动脚本都行关键是让“数据库结构版本”和“应用版本”一一对应否则新旧代码跑在不同库结构上故障发生时很难定位。在CI/CD上我们用Azure DevOps或Jenkins自动跑构建、单元测试和发布。前端编译、后端发布、数据库脚本执行全部串成一条流水线发版从半小时缩短到十分钟而且没人能跳过测试步骤。MES这类系统不能只靠业务人员手工回归自动化测试覆盖率至少要覆盖核心模块的接口和定时任务重点保障报工、追溯、权限这三块后面改代码就有底了。7.3 个人经验与扩展方向这套BS版MES上线稳定后我们又陆续加了手机端报工和车间App基础还是同一套API前端多了一个Vue移动端工程。核心教训是MES不是一次性项目只要工厂还有新产线、新产品、新设备就会持续有新的流程要配置和改动。源码搭建最大的价值不是首次上线省了多少钱而是后续每次变更都能自己掌控节奏不用被供应商排期绑架。给正在做同类项目的朋友一个建议第一套MES先不要追求功能大而全先把工单、报工、追溯这三根柱子立住再逐步叠加设备、质量、绩效、移动端。先把流程跑通数据准确了后面每个新模块都顺手很多。如果一开始就铺开十几个模块团队会被需求淹死上线日期也永远是个谜。最后说一个我自己的实操感受MES的核心从来不是代码而是把业务流程理清楚代码只是流程的固化。源码搭建给了企业一个很宝贵的缓冲带业务变了可以在自己的代码里跟着调整。还有从第一天上线就把车间操作日志完整打开用户的每一步操作都有记录真出了问题的时候能少吵很多架这比任何功能都值钱。