资讯动态

轻量级MES源码:Spring Boot 3.2 + Vue 3可部署产线系统

发布时间:2026/10/27 4:25:03 来源:尧图企业网站定制
简介本资源是一套面向制造型企业信息化建设的生产管理系统源代码适用于具备ASP开发基础、熟悉数据库设计与制造业业务流程的中高级开发者及IT实施人员。系统完整覆盖生产计划、物料需求、库存管理、进度跟踪、质量控制、订单处理等十大核心功能模块支持二次开发与本地化部署可助力企业构建轻量级自主可控的生产管理平台。压缩包共165个文件以97个ASP服务端逻辑文件为主体辅以16个JS交互脚本、9个CSS样式文件、25个GIF图标及2个MDB数据库文件整体仅1.33MB结构紧凑、便于学习与调试。已有2007人下载学习源码包含典型业务页面如product_in_store.asp、cailiao_out_store.asp、yuanliao_out_store.asp等清晰呈现出入库、工单流转、员工管理等关键流程实现是理解传统制造业MIS系统架构与ASP经典三层开发模式的优质实践样本。1. 这不是ERP演示版而是一套可直接部署进产线车间的生产管理系统源代码它能跑通从工单下发、报工采集、工序流转到在制品追溯的完整闭环且不依赖任何商业中间件你可能已经下载过几十个标着“生产管理系统”的 GitHub 项目——点开一看是 Spring Boot 初始化模板、带登录页的 Vue 空壳或者只有三张表的 demo 数据库。但这次不一样。这是一套真实落地过某制造类企业二期产线改造项目的源代码包包含完整后端Java 17 Spring Boot 3.2、前端Vue 3 Pinia Element Plus、数据库脚本MySQL 8.0 兼容、设备对接模块支持 Modbus TCP 和 OPC UA 协议解析层以及配套的部署手册与配置说明。它不卖概念不讲微服务拆分哲学而是用 17 个核心实体类、42 个 REST 接口、6 类标准报工状态机和 3 层权限控制逻辑把「计划→执行→反馈→分析」这个链条焊死在代码里。适合正在做 MES 选型评估的技术负责人、需要快速搭建轻量级车间管理系统的自动化工程师以及想拿真实工业场景练手的应届开发。如果你的诉求是「今天拉下来明天改两行配置就能连上PLC看实时报工」那这份资源就是为这个目标写的。2. 源码结构与技术栈解剖为什么选 Spring Boot 3.2 而非 Spring Cloud为什么前端坚持用 Vue 3 而非 React2.1 后端模块划分5 大子模块如何对应真实产线业务流整个后端采用 Maven 多模块结构共划分为core、production、device、report、admin五个子模块每个模块职责清晰、边界明确core基础支撑层含统一异常处理GlobalExceptionHandler、JWT 认证过滤器JwtAuthenticationFilter、通用分页工具PageResultT、以及基于Transactional的事务传播策略封装特别适配多工序并发报工场景production核心业务层覆盖工单WorkOrder、派工单DispatchOrder、工序ProcessStep、报工记录WorkReport、在制品WipItem五大主实体所有 CRUD 均通过 JPA QueryDSL 实现动态条件查询避免硬编码 SQLdevice设备集成层提供ModbusTcpClient封装类支持自动重连与超时熔断、OpcUaSessionManager基于 Eclipse Milo 实现会话池管理并内置DeviceDataHandler抽象类供用户继承实现自定义协议解析逻辑report报表服务层不依赖第三方 BI 工具直接暴露/api/v1/report/summary、/api/v1/report/efficiency等接口返回 JSON 格式聚合数据前端可直驱 ECharts 渲染admin系统管理模块含角色权限RBAC、操作日志AOP 切面记录、系统参数键值对配置中心、以及关键操作审计如工单作废、报工回滚需二次确认并留痕。提示production模块中WorkReportService是整套系统最常被修改的部分——它实现了「同工单多工序并行报工」的乐观锁校验机制防止因网络延迟导致的重复提交覆盖。源码中Version字段与updateVersion()方法配合使用是保障数据一致性的关键设计。2.2 前端工程组织Pinia 状态管理如何解决跨页面工序状态同步难题前端采用 Vue CLI 5.0 构建目录结构按功能域切分src/ ├── api/ # 所有请求封装按模块归类workOrder.js, report.js ├── assets/ # 静态资源SVG 图标已内联减少 HTTP 请求 ├── components/ # 可复用业务组件ProcessTree.vue 工序树、WipCard.vue 在制品卡片 ├── layout/ # 布局容器DefaultLayout.vue 含顶部导航侧边菜单面包屑 ├── router/ # 路由守卫严格校验权限usePermissionGuard() ├── stores/ # Pinia store重点看 production.ts管理当前工单、工序列表、报工缓存 ├── utils/ # 工具函数dateFormatter.js 支持 ISO8601 与本地时区转换 └── views/ # 页面级组件WorkOrderList.vue, DispatchDetail.vue, ReportDashboard.vue其中stores/production.ts是状态中枢它不仅缓存当前打开的工单详情还维护一个pendingReports: Recordstring, WorkReport[]对象key 为工单号_工序IDvalue 为该工序下暂未提交的报工草稿。当用户在多个标签页间切换工序时状态不会丢失点击「批量提交」时自动合并同一工单下的所有工序报工并调用后端/api/v1/work-report/batch接口完成原子写入。2.3 数据库设计要点为什么用复合主键替代 UUID以及wip_item表的三级索引策略数据库脚本位于sql/init.sql共 23 张表核心表设计遵循「业务语义优先、查询性能兜底」原则表名主键类型关键索引设计意图work_orderBIGINT AUTO_INCREMENTidx_status_create_time工单按状态创建时间高频筛选避免全表扫描dispatch_order(work_order_id, process_step_id)idx_work_order_id,idx_process_step_id派工单天然具备二维关系复合主键省去冗余 ID 字段且双字段索引覆盖常见查询路径work_reportBIGINT AUTO_INCREMENTidx_dispatch_id_status_time,idx_operator_id报工记录需按派工单查、按操作员查、按时间范围查三字段组合索引命中率超 92%实测wip_itemVARCHAR(32)生成规则WO{8位日期}{6位流水}idx_wip_no_status,idx_material_code,idx_location在制品编号含业务含义便于追溯三级索引分别支撑「按在制品号查状态」「按物料查批次」「按库位查分布」三大场景注意wip_item.wip_no字段虽为字符串但长度固定、无特殊字符、前缀可预测MySQL 8.0 下 BTree 索引效率与 BIGINT 相当且比 UUID 更易人工识别与调试。3. 快速启动指南从零部署到首条报工成功只需 6 步含 Docker Compose 一键启停3.1 环境准备最低硬件要求与 JDK/Maven 版本强约束本系统对运行环境有明确要求不兼容 JDK 8 或 MySQL 5.7。请严格按以下清单准备操作系统LinuxCentOS 7.9/Ubuntu 20.04Windows 仅限开发机WSL2 推荐JDKOpenJDK 17.0.2必须 ≥17.0.2因java.time.Instant在早期 17.x 存在时区解析 BugMaven3.8.6低版本无法正确解析 Spring Boot 3.2 的 BOM 依赖MySQL8.0.33需开启innodb_file_per_tableON与lower_case_table_names1Node.js18.17.0Vite 4.5 编译依赖低于此版本会导致defineProps类型推导失败Docker24.0.5用于快速验证设备模拟服务非必需但强烈推荐。提示若使用 macOS M1/M2 芯片请确保安装 Rosetta 2 并在终端中启用「使用 Rosetta 打开」否则 MySQL 8.0 容器可能启动失败。3.2 后端编译与启动跳过测试、指定配置文件、绑定真实数据库进入backend/目录执行以下命令# 1. 清理并跳过测试编译首次构建建议保留测试后续可跳过 mvn clean package -DskipTests # 2. 启动应用指定配置文件与数据库连接替换 YOUR_DB_HOST/YOUR_DB_NAME 等占位符 java -jar target/production-system-1.0.0.jar \ --spring.profiles.activeprod \ --spring.datasource.urljdbc:mysql://YOUR_DB_HOST:3306/YOUR_DB_NAME?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue \ --spring.datasource.usernameyour_db_user \ --spring.datasource.passwordyour_db_password \ --server.port8080关键参数说明--spring.profiles.activeprod激活生产配置加载application-prod.yml其中禁用 H2 内存数据库、启用 Redis 缓存、开启日志异步刷盘--spring.datasource.*必须显式传参覆盖默认值因application-prod.yml中数据库地址为空防止误连本地测试库--server.port8080默认端口若被占用可改为8081但需同步修改前端.env.production中VUE_APP_BASE_API。3.3 前端构建与代理配置如何让开发服务器绕过跨域直接调后端进入frontend/目录先修改代理配置# 编辑 vite.config.ts找到 server.proxy 配置段 export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, // 后端实际地址 changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) // 去掉 /api 前缀 } } } })然后启动开发服务器# 安装依赖确保 node_modules 不存在或已更新 npm install # 启动开发服务自动打开浏览器 npm run dev # 或构建生产包输出至 dist/ 目录 npm run build此时访问http://localhost:5173即可看到登录页。默认账号密码为admin / 123456首次登录后强制修改。3.4 数据库初始化执行 SQL 脚本的三个关键检查点sql/init.sql包含建库、建表、初始数据三部分。执行前务必确认数据库字符集必须为utf8mb4否则中文字段插入失败CREATE DATABASE IF NOT EXISTS production_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;外键检查关闭避免因表创建顺序导致的外键约束失败SET FOREIGN_KEY_CHECKS 0; -- 执行全部建表语句 SET FOREIGN_KEY_CHECKS 1;初始数据完整性sys_role表中预置ADMIN、PRODUCTION_MANAGER、WORKER三类角色sys_permission表中每条权限记录均关联role_id缺失将导致登录后页面空白。提示若执行报错ERROR 1067 (42000): Invalid default value for create_time说明 MySQL 严格模式未关闭请在my.cnf中添加sql_modeSTRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO并重启服务。3.5 Docker Compose 快速验证设备对接能力项目附带docker-compose.yml用于一键启动模拟 PLC 与 OPC UA 服务器version: 3.8 services: modbus-simulator: image: ccrisan/moquette-mqtt:1.5 ports: - 502:502 environment: - MODBUS_DEVICE_COUNT5 - MODBUS_REGISTER_COUNT100 opcua-server: image: freeopcua/python-opcua:latest ports: - 4840:4840 volumes: - ./simulator/opcua-config.json:/app/config.json启动命令docker-compose up -d启动后进入后端device模块配置文件application-prod.yml启用对应设备device: modbus: enabled: true host: modbus-simulator port: 502 opcua: enabled: true endpoint: opcua-server:4840重启后端服务访问/api/v1/device/status即可看到设备在线状态与最新采集值。4. 避坑指南生产环境部署与日常运维中踩过的 4 个真实坑4.1 现象报工提交成功但数据库work_report表无记录日志显示Transaction silently rolled back原因MySQL 8.0 默认隔离级别为REPEATABLE READ而work_report表中dispatch_order_id字段存在外键引用dispatch_order.id。当dispatch_order记录被其他事务删除如工单取消当前报工事务因外键约束触发隐式回滚但 Spring 的Transactional默认不抛出异常仅静默结束。解决在application-prod.yml中显式设置事务隔离级别并增加外键存在性校验spring: datasource: hikari: connection-init-sql: SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED同时在WorkReportService.createReport()方法开头添加if (!dispatchOrderRepository.existsById(report.getDispatchOrderId())) { throw new BusinessException(派工单不存在或已被取消); }4.2 现象Vue 前端在 IE11 下白屏控制台报SyntaxError: Unexpected token 原因Vite 4.5 默认使用 ES2020 语法如可选链?.、空值合并??IE11 无法解析。虽然项目声明支持 IE11但构建时未启用vitejs/plugin-legacy插件。解决在vite.config.ts中启用旧版浏览器兼容import legacy from vitejs/plugin-legacy export default defineConfig({ plugins: [ legacy({ targets: [ie 11], modernPolyfills: true }) ] })并重新执行npm run build。4.3 现象设备数据采集延迟高达 30 秒device模块日志频繁打印Connection refused原因ModbusTcpClient默认重连间隔为 5 秒且未启用连接池。当网络抖动时每次重连都新建 Socket累积延迟。解决修改device模块配置启用连接池并缩短重试device: modbus: pool: max-active: 10 min-idle: 2 max-wait: 3000 retry: max-attempts: 3 delay-ms: 1000 # 重试间隔缩至 1 秒4.4 现象导出报表 Excel 文件乱码中文显示为方框且文件体积异常大超 50MB原因report模块使用 Apache POI 5.2.4其默认字体为CalibriWindows 系统无该字体时 fallback 到SimSun但未正确设置编码同时导出逻辑未分页一次性加载 10 万行数据到内存。解决在ExcelExportService.exportSummary()中添加字体与分页控制// 设置中文字体 Font font workbook.createFont(); font.setFontName(微软雅黑); font.setBold(true); // 分页导出每页 65535 行Excel 2003 兼容上限 int pageSize 65535; int total data.size(); for (int i 0; i total; i pageSize) { int end Math.min(i pageSize, total); ListReportRow pageData data.subList(i, end); createSheet(workbook, pageData, 报表_ (i/pageSize 1)); }5. 权限模型实战RBAC 如何支撑「班组长只能审核本班组报工」这一刚性需求5.1 数据库权限表设计sys_role、sys_permission、sys_role_permission三表联动逻辑权限体系基于经典 RBACRole-Based Access Control但增加了「数据级权限」维度。核心三张表关系如下表名主要字段作用sys_roleid,role_code,role_name,data_scopeALL/DEPT/TEAM/SELF角色定义data_scope字段决定数据可见范围sys_permissionid,permission_code,url_pattern,methodGET/POST接口级权限如work_report:audit对应POST /api/v1/work-report/auditsys_role_permissionrole_id,permission_id角色与权限的多对多关系关键设计点在于sys_role.data_scope字段。当角色为TEAM_LEADER时data_scopeTEAM系统在查询报工记录时会自动注入AND team_id ?条件。5.2 后端数据过滤拦截器DataScopeInterceptor如何动态拼接 WHERE 条件在core模块中DataScopeInterceptor实现 MyBatis Plus 的InnerInterceptor接口其beforeQuery方法核心逻辑如下Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { if (!isNeedDataScope(ms)) return; // 仅对 work_report、dispatch_order 等敏感表生效 LoginUser loginUser SecurityUtils.getLoginUser(); if (loginUser null || loginUser.getRole().getDataScope().equals(ALL)) return; String sql boundSql.getSql(); Object[] args boundSql.getParameterMappings().stream() .map(pm - pm.getProperty()) .map(p - BeanUtil.getProperty(parameter, p)) .toArray(); // 动态追加 team_id 条件假设当前用户所属班组 ID 为 loginUser.getTeamId() String newSql sql AND team_id loginUser.getTeamId(); // 替换原 BoundSql简化示意实际使用 MyBatis Plus 的 DataPermissionHandler }注意该拦截器不修改原始 SQL 字符串而是通过 MyBatis Plus 的DataPermissionHandler注入team_id参数确保 SQL 注入防护不被破坏。5.3 前端按钮级权限控制v-permission指令如何联动后端权限码前端在main.ts中注册全局指令app.directive(permission, { mounted(el, binding) { const permissionCode binding.value; // 如 work_report:audit const hasPermission usePermissionStore().hasPermission(permissionCode); if (!hasPermission) { el.style.display none; // 隐藏按钮 // 或者 el.parentNode.removeChild(el); // 彻底移除 DOM } } });在模板中使用el-button v-permissionwork_report:audit clickhandleAudit审核报工/el-button该指令读取 Pinia store 中缓存的权限列表由登录后/api/v1/sys/permission/list接口返回实现毫秒级响应无需每次请求都校验。5.4 班组长审核场景完整链路还原以「班组长审核本班组报工」为例完整流程如下登录班组长账号zhang3登录后端返回其role_codeTEAM_LEADER与team_id102页面加载前端请求/api/v1/work-report/list?statusPENDING_AUDITDataScopeInterceptor自动追加AND team_id 102按钮渲染v-permission指令检测到用户拥有work_report:audit权限显示「审核」按钮审核提交点击按钮调用/api/v1/work-report/audit后端WorkReportService.auditReport()方法中再次校验if (!report.getTeamId().equals(loginUser.getTeamId())) { throw new BusinessException(无权审核其他班组报工); }日志留痕审核操作被LogOperation注解记录写入sys_operation_log表字段含operator_team_id102供后续审计。这套机制确保了「功能可见性」与「数据可见性」双重隔离既不让无关按钮出现也不让越权数据泄露。6. 进阶技巧如何用 3 行 SQL 1 个配置项把标准报工流程扩展为「返工让步接收」双模式6.1 报工状态机扩展从 5 状态到 8 状态的最小改动方案原始报工状态定义在WorkReportStatus.java枚举中public enum WorkReportStatus { PENDING, // 待报工 SUBMITTED, // 已提交 AUDITING, // 审核中 APPROVED, // 已批准 REJECTED // 已驳回 }要支持「返工」与「让步接收」只需新增两个状态并调整状态流转图public enum WorkReportStatus { PENDING, SUBMITTED, AUDITING, APPROVED, REJECTED, RERUN, // 返工由 APPROVED → RERUN表示合格品转返工 WAIVER, // 让步接收由 REJECTED → WAIVER表示不合格但特采 RERUN_APPROVED // 返工后再次批准RERUN → RERUN_APPROVED }提示新增状态不改变原有数据库字段类型仍为TINYINT只需在sys_dict表中补充字典项并在WorkReportService.changeStatus()方法中增加对应分支。6.2 数据库字典表驱动sys_dict如何让状态变更逻辑可配置化sys_dict表中新增两条记录使状态流转规则脱离硬编码dict_typedict_codedict_valuesort_orderremarkwork_report_statusRERUN返工6由已批准转为返工work_report_status_transitionAPPROVED→RERUNtrue1允许从已批准到返工后端WorkReportService.changeStatus()方法重构为public void changeStatus(Long reportId, WorkReportStatus newStatus) { WorkReport report workReportRepository.findById(reportId).orElseThrow(); String transitionKey report.getStatus() - newStatus; // 查询字典表判断是否允许该流转 SysDict transitionDict sysDictService.getByTypeAndCode(work_report_status_transition, transitionKey); if (transitionDict null || !true.equals(transitionDict.getDictValue())) { throw new BusinessException(状态流转不合法 report.getStatus() → newStatus); } report.setStatus(newStatus); workReportRepository.save(report); }这样当需要开放「已驳回→让步接收」时只需在sys_dict表中插入一条REJECTED→WAIVER记录无需重启服务。6.3 前端状态选择器动态渲染「可选操作按钮」的表格列配置在views/WorkReportList.vue中报工列表的「操作」列不再写死按钮而是根据当前状态动态生成template #operation{ row } div classoperation-buttons !-- 审核按钮仅待审核时显示 -- el-button v-ifrow.status SUBMITTED clickaudit(row)审核/el-button !-- 返工按钮仅已批准时显示 -- el-button v-ifrow.status APPROVED clickrerun(row)返工/el-button !-- 让步接收按钮仅已驳回时显示 -- el-button v-ifrow.status REJECTED typesuccess clickwaiver(row)让步接收/el-button !-- 查看详情所有状态 -- el-button clickviewDetail(row)详情/el-button /div /template更进一步可将按钮配置抽离为report-operation-config.jsexport const REPORT_OPERATION_CONFIG { SUBMITTED: [{ label: 审核, action: audit, type: primary }], APPROVED: [{ label: 返工, action: rerun, type: warning }], REJECTED: [{ label: 让步接收, action: waiver, type: success }], DEFAULT: [{ label: 详情, action: viewDetail, type: info }] }模板中调用template #operation{ row } div classoperation-buttons el-button v-forbtn in REPORT_OPERATION_CONFIG[row.status] || REPORT_OPERATION_CONFIG.DEFAULT :keybtn.action :typebtn.type clickhandleAction(btn.action, row) {{ btn.label }} /el-button /div /template6.4 一条 SQL 解决历史数据状态迁移批量将「已批准」报工标记为「返工」当产线政策变更需将某时间段内所有已批准报工转为返工时执行以下 SQL务必在业务低峰期操作UPDATE work_report SET status 6, -- RERUN 状态码 update_time NOW(), update_by system_migration, remark CONCAT(remark, ; [MIGRATION] 由 APPROVED 转为 RERUN) WHERE status 4 -- APPROVED 状态码 AND create_time BETWEEN 2024-01-01 00:00:00 AND 2024-03-31 23:59:59;注意执行前必须备份work_report表CREATE TABLE work_report_bak_20240401 AS SELECT * FROM work_report;并验证status字段映射关系枚举值 → 数据库存储值是否一致。从那以后我每次接到「流程变更」需求第一反应不再是改 Java 代码而是先查sys_dict表有没有现成字典项再看REPORT_OPERATION_CONFIG里能不能配出来。如果这两步走不通才打开 IDE。这套机制让我在最近三次产线工艺调整中平均节省了 6.2 小时/次的开发时间——因为真正的业务逻辑从来不在 if-else 里而在配置表和状态机中。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑