首页 / 资讯中心 / 文章详情

SpringBoot+Vue+MyBatis车间管理系统开发实战

SpringBoot+Vue+MyBatis车间管理系统开发实战 ★ FEATURED ARTICLE
接手这个车间管理系统其实是被一张Excel表逼出来的。车间主任每天上午要把工单从ERP里导出来再手动填完成数量、合格率下午催物料月底还要对账光是维护这张表就占掉半天时间。交流了不到半小时我和同事就决定别再让人工台账拖后腿了直接做一个前后端分离的车间管理系统技术栈选 SpringBoot Vue MyBatis MySQL先把工单流转、物料领用、质检和设备状态这些核心场景管起来。这篇博文就把项目从需求拆解、数据库设计、后端接口、前端联调到部署上线的完整思路以及我们踩过的坑一次性写清楚给正在做或准备做类似系统的朋友一个可以照着走的参考。1. 系统整体设计与技术选型1.1 车间管理到底管什么从一张Excel表开始拆需求车间管理系统听起来是个很大的概念但落到实际生产环境真正高频的痛点就那么几个。第一是工单进度不透明车间主任每天要逐单问班长班长再跑去工位问操作工信息一层一层传拿到手往往已经是下午。第二是物料消耗对不上账领料单靠手写月底财务和生产对账时经常发现料废和超领没记录。第三是设备状态没人管机器停了半天才被发现维修记录散落在纸质本子上。第四是质检合格率统计滞后质量问题要等到月底汇总才暴露错过了及时纠偏的窗口。所以我们的需求拆解没有追求大而全而是围绕“工单”这条主线展开。一个车间工单从创建开始要经历排产、领料、加工、质检、完工入库几个环节每个环节都需要记录时间、人员、数量、状态和异常信息。系统因此被分成五个核心模块工单管理、物料管理、设备管理、质量管理、系统管理用户与权限。工单管理负责整个生产过程的进度跟踪物料管理管领用和退料设备管理记录设备运行和维修质量管理记录检验批次和结果系统管理则解决谁能看什么、能操作什么的问题。这套结构几乎可以平移到任何机械加工、电子装配、注塑车间的业务场景里后续扩展也方便。1.2 前后端分离架构到底怎么分层选用前后端分离并不是因为技术流行而是这个项目实际需要这样切。SpringBoot 后端只需要提供一套 Restful API不关心页面长什么样Vue 前端负责页面渲染、表单交互和数据展示通过 axios 调用后端接口MyBatis 在中间做 SQL 映射把数据库表记录转换成 Java 对象MySQL 负责最终的数据持久化。数据流转大概是前端用户点击查询 - 发起 HTTP 请求 - 后端 Controller 接收 - Service 处理业务逻辑 - Mapper 通过 MyBatis 执行 SQL - MySQL 返回结果 - 层层封装回传给前端 - Vue 渲染到页面。这种分层带来的直接好处是团队可以并行开发。我负责后端接口定义好返回格式前端同学完全可以拿着接口文档先把页面搭起来不必等服务端全部写完。第二个好处是部署灵活前端打包成静态文件放到 Nginx后端打成一个 jar 包单独运行任何一方需要升级都不影响另一方出问题也能快速定位是接口挂了还是页面报错。再往深一层讲这种架构天然适合未来做移动端看板或小程序端因为接口可以复用只需要新写前端壳子就行。1.3 为什么偏偏选 SpringBoot Vue MyBatis MySQL选这套组合不是拍脑袋而是反复对比后的结果。SpringBoot 最核心的价值是简化了Spring应用的配置和部署内嵌 Tomcat一个 java -jar 就能跑起来对一个中小型车间系统来说非常省事。MyBatis 的定位是轻量持久层框架不像 Hibernate 那样对实体关系做全自动映射SQL 完全由开发者控制正好适合车间系统里复杂的多表关联查询、动态条件筛选和报表统计。Vue 则在前端生态里最适合快速开发管理后台组件化和响应式数据绑定让工单列表、表单弹窗这类页面写起来很顺手。MySQL 自不用说成熟稳定社区资料丰富对工厂这种数据量级一天新增几千条工单记录完全够用还能省下商业数据库的授权成本。当然这套组合也有边界。如果你要做的是制造业大型 ERP 系统要考虑微服务拆分、分布式事务如果要做的是高并发设备数据采集可能需要时序数据库和消息队列。但就车间管理这种企业内部系统来说SpringBoot Vue MyBatis MySQL 是性价比最高的“标准答案”这也是很多同类项目选它的原因。2. 数据库设计车间系统核心表与关系2.1 从工单流转出发设计数据模型数据库设计是整个项目的地基这块没想清楚后面全得返工。我们当时不是一上来就画 ER 图而是先把工单的完整生命周期跑了一遍列出每个环节产生的数据和涉及的角色。新建工单阶段需要产品信息、计划数量、计划交付时间对应一张生产工单主表。排产阶段工单会被拆成多道工序比如下料、车床加工、铣床加工、表面处理、装配每道工序有顺序号、计划工时、负责班组这就有了工单工序表。领料阶段操作工凭工单去仓库领原材料记录物料编码、数量、领取人、用途正常领用、补料、超领所以有物料领用表。加工阶段每完成一道工序就上报数量、合格数、废品数操作工、设备、工时都要记录于是有生产报工表。质检阶段一批完工品进入检验记录抽检数、缺陷数、缺陷类型、检验结果对应质量检验表。再加上设备本身要建档、维修要记录以及用户、角色、部门、菜单权限等系统表整体模型就搭建起来了。这种“按业务阶段拆分成多张明细表”的设计比把所有信息堆在工单表里要合理得多。一是数据粒度细后续想做“每道工序的合格率”、“某台设备产出的废品率”都能直接从表里取数二是避免单表字段爆炸工单主表保持简洁明细数据通过外键关联三是并发操作更安全不同环节录入的数据互不阻塞。2.2 关键表结构与字段说明工单主表是整个系统的核心字段设计要从业务查询角度反推。比如车间主任最常看的是当前工单在哪个工序、进度多少因此状态字段必不可少。我们用 tinyint 存状态码0表示新建1表示排产中2表示生产中3表示已完工4表示已质检5表示已关闭。为什么不用字符串因为状态码占用空间小而且前端可以通过字典翻译成任意文案后续状态改名不用动数据库。数量字段统一用 decimal避免 float 精度问题。下面是工单主表的关键字段示例CREATE TABLE work_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 工单编号, product_name VARCHAR(128) NOT NULL COMMENT 产品名称, product_code VARCHAR(64) NOT NULL COMMENT 产品编码, plan_qty DECIMAL(12,2) NOT NULL COMMENT 计划数量, completed_qty DECIMAL(12,2) DEFAULT 0 COMMENT 已完成数量, qualified_qty DECIMAL(12,2) DEFAULT 0 COMMENT 合格数量, workshop_id BIGINT NOT NULL COMMENT 所属车间ID, status TINYINT NOT NULL DEFAULT 0 COMMENT 工单状态, priority TINYINT DEFAULT 1 COMMENT 优先级1普通 2紧急, plan_start_time DATETIME COMMENT 计划开始时间, plan_end_time DATETIME COMMENT 计划结束时间, actual_start_time DATETIME COMMENT 实际开始时间, actual_end_time DATETIME COMMENT 实际完工时间, remark VARCHAR(255) COMMENT 备注, deleted TINYINT NOT NULL DEFAULT 0 COMMENT 软删除标记, create_time DATETIME NOT NULL COMMENT 创建时间, update_time DATETIME NOT NULL COMMENT 更新时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT生产工单表;工单工序表相对更简单核心是记录工序顺序和实际执行情况CREATE TABLE work_order_process ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL COMMENT 工单ID, process_name VARCHAR(64) NOT NULL COMMENT 工序名称, sequence_no INT NOT NULL COMMENT 工序顺序号, plan_hours DECIMAL(6,2) DEFAULT 0 COMMENT 计划工时, actual_hours DECIMAL(6,2) DEFAULT 0 COMMENT 实际工时, process_status TINYINT DEFAULT 0 COMMENT 工序状态0待开始 1进行中 2已完成, operator_name VARCHAR(64) COMMENT 负责人, equipment_id BIGINT COMMENT 使用的设备ID, completed_qty DECIMAL(12,2) DEFAULT 0 COMMENT 该工序完成数量, qualified_qty DECIMAL(12,2) DEFAULT 0 COMMENT 该工序合格数量, create_time DATETIME NOT NULL, update_time DATETIME NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT工单工序表;物料领用表要特别设计领料类型因为在车间实际管理里正常领料、补料、超领是完全不同的业务动作月底成本核算全靠这个字段区分。设备表则需要记录设备编码、名称、状态运行、空闲、维修、停机、所在车间、下次保养日期维修记录和工单工序表关联。2.3 设计时的几个坑别急着建表先想这几个问题第一个坑是状态字段和枚举值没提前约定后端写死1、2、3前端又定义另一套数字代表不同含义联调时进度条怎么都对不上。所以建表之前就要把状态字典、类型字典、优先级字典整理成一份数据字典文档前后端共用。第二个坑是时间字段的时区问题。MySQL 的 timestamp 会随数据库时区变化datetime 不自动转换而我们的后端部署在云服务器上时区设置不一致容易导致前端显示的时间比实际差8个小时。统一方案是数据库连接串加上 serverTimezoneAsia/Shanghai实体类时间字段用 LocalDateTime接口层统一格式化为 yyyy-MM-dd HH:mm:ss一次到位。第三个坑是软删除和唯一约束冲突。工单编号通常要求唯一但如果用了 deleted 软删除一条记录被删除后只标记为1下次新增同样的单号再写入时唯一索引就冲突了。常用解决方法是单号带上当前日期或时间戳比如 WO20250626140001从业务上规避重复。如果没有唯一需求尽量别乱加索引约束。3. 后端 SpringBoot MyBatis 实现要点3.1 后端工程结构和统一响应体后端工程我习惯用标准的多层结构业务量大一点也方便扩展com.factory.mes ├── controller // 接收前端请求做参数校验 ├── service // 业务逻辑层事务控制 ├── mapper // MyBatis接口 ├── entity // 数据库实体类 ├── dto // 数据传输对象避免把实体直接暴露给前端 ├── vo // 返回给前端的视图对象 ├── common // 统一返回、异常处理、工具类 ├── config // 配置类比如MyBatis分页、CORS、JWT拦截器 └── utilsController 只做参数接收和结果包装不写业务代码Service 处理核心逻辑比如创建工单时需要生成单号、初始化工序明细、校验库存Mapper 只负责 SQL 操作。这里有一个很重要的原则前端需要什么字段就定义对应的 VO 返回不要把数据库实体直接返回出去。比如工单列表页要显示“工序名称和进度”直接返回实体类会导致前端拿到一堆用不上的字段同时还会把数据库内部信息比如创建者 ID 暴露出去。统一响应体是前后端联调效率的关键。我封装了一个 Result 类泛型结构包括 code、message、data成功状态码约定为 200业务异常码从 400 开始。所有接口返回 Result.success(data) 或 Result.error(500, 服务端异常)前端拿到后只需要判断 code 是否等于 200 即可不用每个接口单独处理 HTTP 状态码。public class ResultT { private Integer code; private String message; private T data; // 成功的静态方法 success失败的静态方法 error }配合全局异常处理器把业务异常统一转换成 Result 返回这样即使代码里漏了一层 try-catch前端也不会收到一堆看不懂的堆栈信息。我在项目里还额外定义了一个 BizException凡是库存不足、工单状态不允许编辑这类业务性问题都手动抛出异常由全局处理器兜底。3.2 登录与权限JWT 拦截器车间管理系统不需要对接复杂的统一认证我们用 JWT 拦截器做了一套轻量权限方案。用户登录成功后后端校验用户名密码密码用 BCrypt 加密存储认证通过后生成 token把用户 ID、角色编码、过期时间放进 token 里返回给前端。前端每次请求在 axios 拦截器里携带 token后端自定义一个拦截器对所有需要认证的路径校验 token 是否有效并解析出当前用户。为什么不用 Spring Security说实话这个项目涉及的权限模型没那么重用户表、角色表、菜单表三张表就能搞定Spring Security 的过滤器链和配置反而会增加理解成本。如果需要类似“车间主管可以审批操作工只能提交”这种按钮级权限还可以用自定义注解配合拦截器实现在需要控制权限的方法上标注一个 RequirePermission(work_order:approve)拦截器解析注解比较用户权限列表没权限直接返回 403。这种方法比引入一个完整框架要轻得多也更容易让团队成员上手。密码加密是很多人容易忽略的细节。数据库里绝不能存明文密码BCrypt 每次生成的哈希值都不同验证时用相同算法匹配安全性比 MD5 高一个量级。用户重置密码时我会设置一个一次性初始密码并要求首次登录强制修改这个逻辑虽然简单但能避免很多安全隐患。3.3 MyBatis 动态 SQL工单分页查询的实战写法车间系统查询工单时筛选项非常灵活按工单编号模糊搜、按状态筛选、按车间筛选、按计划时间范围筛选而且不同条件可以组合。这种场景 MyBatis 的动态 SQL 非常合适我在 XML 里写了一个核心查询配合 if 标签动态拼接条件select idselectOrderPage resultMapOrderResultMap SELECT wo.id, wo.order_no, wo.product_name, wo.plan_qty, wo.completed_qty, wo.status, wo.priority, ws.workshop_name, u.real_name AS creator_name, MAX(wop.process_name) AS current_process FROM work_order wo LEFT JOIN workshop ws ON wo.workshop_id ws.id LEFT JOIN sys_user u ON wo.create_by u.id LEFT JOIN work_order_process wop ON wop.order_id wo.id AND wop.process_status 1 where if testquery.orderNo ! null and query.orderNo ! AND wo.order_no LIKE CONCAT(%, #{query.orderNo}, %) /if if testquery.status ! null AND wo.status #{query.status} /if if testquery.workshopId ! null AND wo.workshop_id #{query.workshopId} /if if testquery.startTime ! null AND wo.plan_start_time gt; #{query.startTime} /if if testquery.endTime ! null AND wo.plan_end_time lt; #{query.endTime} /if /where GROUP BY wo.id ORDER BY wo.create_time DESC LIMIT #{query.offset}, #{query.pageSize} /select这里有三个细节值得说明。第一模糊查询用 CONCAT(%, #{query.orderNo}, %)而不是直接拼字符串因为 #{} 是预编译参数能防 SQL 注入如果用 ${} 拼 % 容易被注入风险。第二SQL 中大于小于号要转义成 和 否则 XML 解析会报错这个报错信息很长不注意看容易让人误判是 SQL 本身的问题。第三动态查询的 if 判断要注意空字符串和 null 都合法的情况尤其在 status 为 0 时不能用 if 判断空字符串导致条件丢失。MyBatis 的 resultMap 是另一个重点特别是当查询结果有多个表字段时建议显式定义列和属性的映射关系。项目里开启了下划线转驼峰配置map-underscore-to-camel-casetrue但遇到别名或者字段名不规范的情况还是需要写 resultMap。MyBatis 报“Invalid bound statement”是常见问题一般是 XML 里 namespace 和 Mapper 接口全限定名不一致或者 mapper XML 的 resource 路径没有配置到 Spring Boot 的 mybatis.mapper-locations 里。3.4 事务与并发控制车间数据易错点车间生产数据最怕出现重复扣料、工单重复完工。以物料领用出库为例操作工提交领料申请后端第一步查库存是否足够第二步扣减库存第三步写入领料单。如果这些操作不加事务第二步执行过程中突然抛异常库存已经扣了但领料单没生成第二天对账就会发现库存凭空少了。解决方法很简单在 Service 方法上标注 TransactionalSpring 会把这个方法里的数据库操作放进同一个事务任何一步失败就整体回滚。更隐蔽的坑是并发问题。两个操作工同时领同一批物料两个请求同时读到库存剩余 10 件第一个申请领 8 件第二个申请领 5 件如果数据库不加锁两个请求都发现库存够最后库存变成负数。项目里我给物料库存表加了版本号字段 version更新库存时用乐观锁先查版本号为 3更新时执行 UPDATE material_stock SET qty qty - 8, version 4 WHERE id 1 AND version 3如果更新行数为 0 则说明数据已被别人改过重新读取再提示用户。部分关键操作比如工单完工确认也可以用悲观锁 SELECT ... FOR UPDATE 来防止同一时刻两个人重复点击完工。4. 前端 Vue 实现与前后端联调4.1 Vue3 Vite 工程初始化别在环境配置上卡太久前端我用的是 Vue3 Vite Element Plus 这套组合。Vite 启动速度比 Webpack 快很多本地开发体验明显更顺滑。创建项目可以直接用命令npm create vitelatest mes-web -- --template vue cd mes-web npm install安装依赖后需要装两个核心包路由 vue-router以及 UI 组件库 element-plus。顺便还要装 axios 用来发请求以及用于解析 element-plus 图标库和状态管理的 pinia。网上很多 Vue2 老项目还在用 Vue2 Element UI如果你是新起项目我建议直接上 Vue3因为 Element Plus 组件质量更高而且生态已经稳定了。启动项目后第一步是配置环境变量。我会在项目根目录建立 .env.development 和 .env.production 两个文件开发环境的 VITE_API_BASE_URL 设置成 “/api”生产环境根据部署情况也设置成 “/api”。后端地址不直接写死到代码里因为本地联调要用代理生产环境要按服务器实际 Nginx 配置走把不同环境的后端地址统一映射到 /api 前缀前端代码里就只要写相对路径。4.2 axios 封装与跨域代理配置axios 如果不做封装每个页面都要重复写请求路径、token、错误提示代码很冗余而且容易出错。我在项目里建了一个 request.js 文件统一处理这几件事从 localStorage 读取 token 放到请求头响应拦截器里判断 code 是否为 200不为 200 就弹 ElMessage 错误提示并拦截到登录页。跨域问题也是前后端分离的必修课。本地开发时前端跑在 5173 端口后端跑在 8080 端口浏览器直连后端会报跨域错误。解决方案不是在后端写一堆 CORS 配置而是用 Vite 的 proxy 代理。在 vite.config.js 里加一段配置server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } }这样前端请求 /api/work-order/list开发服务器就会把请求转发到后端的 /work-order/list浏览器端整个请求过程看起来是同源的不触发跨域。这个配置还有一个好处生产环境 Nginx 也可以复用同样的 /api 规则后端接口路径不用改。4.3 工单列表页与表单页怎么拆组件工单列表页我拆成了三个主要组件顶部搜索表单、中间数据表格、底部分页器。搜索表单用 Element Plus 的 el-form 配合 el-input、el-select、el-date-picker 实现点击查询时把表单数据作为查询条件传给后端。表格用 el-table渲染工单编号、产品、数量、状态、进度等列状态列用 el-tag 根据状态码显示不同颜色。分页器绑定当前页和总数切换页码时重新请求接口。表单页则单独建一个组件新建工单和编辑工单共用同一套表单区别只是处理数据时是否带工单 ID。表单组件用 el-dialog 包起来打开时回显数据提交时调用不同的接口。这里有一个很实用的逻辑提交前做一个统一的字段校验必填项用 Element Plus 的 rules比如工单编号、计划数量必填数量必须大于 0。校验规则除了前端做后端 Controller 接收 DTO 时也要加 Validated 注解前后端双重校验才能减少脏数据。前端列表页最关键的一个点是后端返回的分页数据结构。我统一用 PageVO 返回数据包含 records 列表、total 总数、current 当前页码。前端拿到 total 后赋给分页器拿到 records 交给表格渲染整个联动就通了。4.4 前端路由 history 刷新 404 与打包部署Vue Router 默认有 hash 和 history 两种模式。hash 模式 URL 里带 #刷新不会出问题但不好看history 模式去掉了 #看起来更规范但直接刷新子路由页面时Nginx 找不到对应的物理文件会返回 404。这个问题在我们系统上线第一天就遇到过测试同事点进工单详情页按了一下刷新页面直接白屏。原因很简单Nginx 默认配置中 /work-order/detail 这个路径没有真实文件它不会自动转发到 index.html。解决方法是配置 Nginx 的 try_files把所有前端路由都回退到 index.htmllocation / { root /opt/mes-web/dist; index index.html; try_files $uri $uri/ /index.html; }这样刷新任何一个前端路由时Nginx 都会先找真实文件找不到就返回 index.html由 Vue Router 接管页面渲染。如果你不想用 Nginx也可以把 dist 文件夹打进 SpringBoot 的 static 目录但这时必须配置一个转发规则让所有非接口路径都返回 index.html否则同样会遇到 404 问题。5. 部署上线与常见问题排查5.1 本地从零部署MySQL 初始化、后端打 jar、前端 build真正部署时严格按照以下流程走一遍基本能一次通过。第一步安装 MySQL 并初始化数据库。登录 MySQL 后执行建库脚本设置字符集为 utf8mb4CREATE DATABASE mes_factory DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后导入项目的 init.sql里面包含建表语句和初始数据。注意 MySQL 8 默认认证插件是 caching_sha2_passwordSpringBoot 数据库驱动版本必须用 mysql-connector-j 8.x否则会提示认证插件不支持。我踩过一次坑连接字符串写成 5.x 的 com.mysql.jdbc.Driver直接 ClassNotFoundException后来改成 com.mysql.cj.jdbc.Driver 才正常。第二步后端打 jar 包。在项目根目录执行mvn clean package -DskipTests生成的 jar 在 target/ 目录下。启动前需要确认 application.yml 里的数据库配置、端口号、文件路径都符合生产环境。启动命令建议用 nohup 或者 systemd 开机自启避免终端关闭后进程退出。当时我图省事直接 java -jar 跑结果 SSH 断了应用就停了后来改成 systemd 服务才算稳定。第三步前端打包npm run build生成的 dist 目录是纯静态文件。我一般先放在 Nginx 的 web 目录下用前面说的 try_files 配置托管再单独配置接口转发server { listen 80; server_name your-domain.com; location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { root /opt/mes-web/dist; index index.html; try_files $uri $uri/ /index.html; } }注意 proxy_pass 末尾的斜杠如果上游地址是 http://127.0.0.1:8080/Nginx 会替换掉匹配到的 /api/ 前缀把 /api/work-order/list 转发成 /work-order/list这跟本地 Vite 代理的行为保持一致。5.2 常见问题清单我的排查经验这里整理一份我在项目中真实遇到过的问题按出现频率排序方便你直接对号入座。问题现象根本原因解决方法前端请求接口报 404Nginx 只托了静态文件没有转发 /api检查 server 块里是否配置 location /api/并确认转发地址登录接口报 401但密码正确JWT token 过期或拦截器没放行登录接口查看拦截器 excludePathPatterns 是否包含 /auth/login数据库时间比本地时间早 8 小时MySQL 连接串没指定时区连接串加 serverTimezoneAsia/ShanghaiMyBatis 报 Invalid bound statementmapper XML 路径或 namespace 不对检查 mybatis.mapper-locations 配置和 XML namespace页面刷新 404history 路由缺少 try_files按前面 Nginx 配置加上 try_files列表接口很慢表没走索引比如状态字段没建索引给查询频繁的条件字段加普通索引控制 JOIN 数量扣减库存出现负数并发导致乐观锁版本不一致更新时带版本号条件失败重新读取跨域报错但配置了 CORS前端用了 Vite 代理但生产又用了 Nginx明确开发环境走 Vite proxy生产走 Nginx /api 转发二选一排查思路有个顺序先看浏览器控制台请求返回的 HTTP 状态码再判断是前端路由问题、接口路径问题还是后端业务问题。如果网络请求没发出去大概率是前端代码如果状态码是 401 或者 403先检查 token 和权限如果是 500去后端看日志SpringBoot 默认控制台会打出异常堆栈定位很快。5.3 低配服务器上的优化让系统跑得更稳很多工厂内部服务器配置不高可能就是一台 2核4G 的旧机器运行 jar 包和 MySQL 再加 Nginx资源会比较紧张。这时候做几个低成本优化效果很明显。后端启动时指定 JVM 初始和最大堆内存一致避免动态扩容带来的停顿java -Xms512m -Xmx512m -jar mes.jar。MySQL 设置 innodb_buffer_pool_size 为物理内存的 50%~70%比如 2G 内存设置为 256M能明显提升查询和写入速度。前端打包时开启压缩Nginx 配置 gzip on对 js、css、json 等文件压缩传输体积能减少 60% 以上。数据库连接池参数也不能忽略。SpringBoot 默认的 HikariCP 已经把最大连接数设为 10如果并发访问量不大可以保持默认但必须设置 connection-timeout 和 idle-timeout避免异常情况下连接池耗尽。另外项目里我建议把分页查询用到的排序字段、筛选字段都加上索引尤其工单状态、计划开始时间这两个字段加索引前后查询速度差距非常明显。最后再分享两个实操建议这个系统上线后最直观的改变是车间主任每天早上的 Excel 流程消失了工单进度、物料领用、合格率全部可以在看板上实时查到月底对账也只需要一键导出。个人经验是做这类工厂内部系统业务需求优先级排序比技术选型更重要。不要一口吃成一个全流程 ERP先把工单、物料、质检这三件事做透车间用起来再逐步加设备对接、扫码报工、移动审批这些扩展功能。另一个建议是代码规范和数据字典一定要从第一天就建立好。前后端命名不一致、状态码各写各的越到后面越痛苦。我们后来专门花了一天时间梳理数据字典把工单状态、设备状态、领料类型、质检结果统一成一套枚举前后端落地后整个团队沟通成本下降了一大截。如果时间允许再补一套简单的接口文档或 Postman 集合后面接手的人不会骂你。
阅读完成 · 觉得有帮助?
咨询建站