做后台管理系统这些年,我发现工厂车间的需求和企业办公完全不是一回事。办公系统管的是审批流、待办和通知,车间要的是工单走到哪了、物料够不够、设备是不是该保养、这批产品合格率是多少——事情看着不大,但每件都关系到生产的连续性。所以当有人拿着“Java SpringBoot+Vue3+MyBatis 工厂车间管理系统源码”来问我这套东西值不值得学的时候,我的回答很直接:如果你只是想要一个能跑的增删改查后台,那很多框架都能做到;但如果你想真正理解车间管理的业务闭环、前后端分离的工程组织方式,想搞懂 SpringBoot、MyBatis、MySQL 这些 Java 面试常客在真实项目里是怎么配在一起的,这套源码算是一个很典型的参考样本。
下面我把这套系统的设计思路、核心模块、后端实现、前端工程化以及部署排错过程完整拆开讲一遍,内容比较长,但都是实际开发中会踩到的地方,适合正在做毕业设计、转岗 Java 开发,或者想自己搭一套管理系统练手的读者参考。
1. 车间管理系统到底在管什么,技术选型凭什么这么定
1.1 车间管理的核心业务逻辑
工厂车间里的软件需求,绕来绕去其实就是几个关键词:工单、物料、设备、质量、人员。
车间主任早上关心的,是今天哪些工单开工、哪个产线还没排产;班组长盯的是领料单批了没有、上一道工序有没有延误;设备组要做的不是等坏了再修,而是按计划点检和保养。这些事看着零散,背后全指向一条主线——生产状态的透明化。纸质流转单会丢,Excel 表格各记各的,结果往往是“问谁谁都说在查,最后谁也不知道”。管理系统要解决的问题,就是把这些离散信息统一收拢到一张可以随时查询的状态表里,让每个角色看到自己关心的结果。
这也是我建议初学者别着急抄代码的原因。你去看源码之前,先用一句话回答:这套系统把车间的什么事数字化了?回答不上来,代码看再多也容易懵。
1.2 为什么是 SpringBoot + Vue3 + MyBatis + MySQL 这套组合
技术选型不是越新越好,而是要符合团队和业务的实际情况。
SpringBoot在 Java 后端已经稳了很长时间。它把配置、Web 容器、第三方框架集成做了大量自动化处理,以前需要折腾半天的环境搭建,现在几行配置就能起来。对时间成本敏感的车间管理系统来说,这个优势非常明显。Vue3是前端现在的主流方向,相比 Vue2,组合式 API 让业务逻辑的复用和可维护性明显更好,生态也成熟到可以放心用在生产环境。MyBatis是我个人比较偏爱的一环,因为车间场景里报表多、关联查询多,比如“统计本月各产线完工率”“按物料编码汇总领用数量”,这些 SQL 直接写在 XML 里,调优和排查都很直观。MySQL就更不用多说,开源、稳定、部署成本低,中小型工厂完全够用。
这里多说一句关于 MyBatis 和 MyBatis-Plus 的选择。很多新手纠结这个问题,我的看法是:源码用 MyBatis,其实更利于理解 SQL 和数据关系的本质,因为你要手写映射、手控 SQL;MyBatis-Plus 开发速度确实快,但“批量新增”“条件构造器”这些能力帮你包掉了,时间长了容易失去对 SQL 的手感。先吃透手写映射,再去用 Plus,你会发现顺手很多,Java 面试时被问到 MyBatis 底层原理也更有底气。
1.3 前后端分离到底解决什么问题
这套系统采用前后端分离,表面看是把前端页面和后端接口拆到两个工程里,深层的原因有三个:
第一,分工清晰。前端只负责交互和渲染,后端只负责业务逻辑和数据持久化,两边可以并行开发,互不阻塞。第二,部署灵活。前端打包成静态资源,交给 Nginx 托管即可;后端打成 jar 包独立运行,接口压力大的时候可以单独扩后端实例。第三,接口边界明确。分离架构迫使你把接口契约定义清楚,参数、返回结构、错误码都提前商量好,而不是页面和 SQL 混在一起写,后期根本没法维护。
| 架构方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 前后端分离 | 并行开发、部署灵活、接口复用 | 跨域处理、联调成本偏高 | 团队协作、中大型项目 |
| 服务端渲染 | 简单直接、SEO 友好 | 前后端耦合、维护成本高 | 小型内部工具、原型验证 |
2. 系统功能模块与数据库建模
2.1 模块怎么拆才贴近车间实际
车间管理系统一般绕不开这五个模块:用户与权限、工单管理、物料管理、设备管理、质量管理。这套源码也是从这几个维度展开的,结构上没有堆砌花哨功能,而是把每个模块做扎实。
工单模块是核心,承担着任务下发的入口。一个工单通常包含产品编码、数量、计划开始时间、计划结束时间、负责人、所属产线这些字段,状态从“待排产”流转到“生产中”“已完工”“已入库”。物料模块负责领料和退料,每次领用都要绑定到对应工单和企业编码,方便后续核算成本。设备模块管的是点检、保养和维修记录,设备状态直接影响到工单能不能按时开工。质量管理主要记录质检结果和不合格品数量,车间主任要能看到每张工单的抽检合格率。
模块划分的原则,不是按“增删改查”来切,而是按业务流程来切。比如物料领用一定是挂在工单下的,而不是单独做一堆孤立的物料的增删改查。这个逻辑想通了,系统的骨架就不会歪。
2.2 表设计实战与关键 DDL
数据库建模是这套系统最值得学习的地方之一。表设计得好,后面写 SQL、做统计都会很顺;设计得不好,后期全是 patch。我按“用户、工单、物料记录、设备点检、质检记录”各给一个精简示例,实际项目中表会更多,但核心思想一样。
CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', username VARCHAR(50) NOT NULL COMMENT '登录名', password VARCHAR(100) NOT NULL COMMENT '密码(BCrypt加密)', real_name VARCHAR(50) DEFAULT '' COMMENT '姓名', role INT NOT NULL DEFAULT 0 COMMENT '角色:0管理员 1车间主任 2操作工', phone VARCHAR(20) DEFAULT '' COMMENT '手机号', status TINYINT NOT NULL DEFAULT 1 COMMENT '1正常 0禁用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';CREATE TABLE work_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', order_no VARCHAR(40) NOT NULL COMMENT '工单编号', product_code VARCHAR(50) NOT NULL COMMENT '产品编码', product_name VARCHAR(100) DEFAULT '' COMMENT '产品名称', quantity INT NOT NULL DEFAULT 0 COMMENT '计划数量', completed_quantity INT NOT NULL DEFAULT 0 COMMENT '已完成数量', status TINYINT NOT NULL DEFAULT 0 COMMENT '0待排产 1生产中 2已完工 3已入库', line_name VARCHAR(50) DEFAULT '' COMMENT '产线名称', assignee_id BIGINT DEFAULT NULL COMMENT '负责人ID', plan_start DATETIME DEFAULT NULL COMMENT '计划开始', plan_end DATETIME DEFAULT NULL COMMENT '计划结束', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_status (status), KEY idx_order_no (order_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工单表';工单表里我没有用外键去强制关联负责人表,而是用assignee_id做逻辑外键。很多互联网公司都这么干,因为外键会引入锁竞争、影响写入性能,级联删除更是个隐患。这里业务上我们总是通过 Java 代码去保证引用关系,数据库只负责索引和约束,这是实践中更稳的做法。
2.3 字段设计里的几个经验
先说状态字段。我习惯用TINYINT表示状态,而不是字符串。 “0 待排产、1 生产中”这种写法代码里加一个常量类或者枚举就管住了,字符串在数据库里占空间、容易写错、还不好比较大小。数量字段这里用的是 INT,因为车间工单数量一般不会大到需要 BIGINT;但涉及金额的,比如物料单价、人工成本,一律用DECIMAL(10,2),切记别用 DOUBLE,浮点精度会坑人。
再强调一下默认值。建表时给字段一个合理的默认值是个好习惯,比如quantity INT NOT NULL DEFAULT 0、status TINYINT NOT NULL DEFAULT 0,这样 Java 实体类少写很多 null 判断。时间字段用DATETIME而不是TIMESTAMP,因为 TIMESTAMP 在 MySQL 5.6 和 8.0 的行为有差别,容易产生时区问题。最后,索引不是越多越好。这张工单表我给status和order_no各建了一个普通索引,足够满足列表筛选和编号查询。像create_time这种字段要看报表查询频率,如果经常按时间统计再加索引,否则纯粹浪费磁盘。
3. 后端实现:SpringBoot 与 MyBatis 的落地细节
3.1 工程结构:不要让 Controller 变成大杂烩
后端工程结构看起来很常规,但很多人分层分得不对。建议按业务模块分包,而不是只按 controller/service/dao 三层机械切。车间管理系统通常这样组织:
com.example.factory ├── controller # 接口层,只做参数接收和结果封装 ├── service # 业务逻辑层 │ └── impl ├── mapper # MyBatis 接口 ├── entity # 数据库实体 ├── dto # 接口传参对象 ├── vo # 返回对象 ├── config # 全局配置(WebMvc、MyBatis、事务等) ├── common # 公共结果类、异常类、工具类 └── interceptor # 登录拦截器、日志拦截器等Controller 里的代码量应该非常少,只负责“接参数、调服务、返回结果”。权限判断、SQL 拼接、数据校验这些都要下沉到对应的层去。我看到过不少“把所有业务逻辑写在 controller 里”的源码,那种项目看起来功能都做了,但一改需求就到处冒风险。判断分层好不好的标准很简单:加一个字段,你要动几个文件?越少越健康。
3.2 MyBatis 配置、XML 映射与缓存踩坑
先说配置。SpringBoot 工程里的application.yml至少要有这几段:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/factory?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true username: root password: root hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 30000 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.factory.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case如果不开,数据库的user_name就映射不到实体里的userName,结果会出现一堆查询字段为 null 的诡异问题。log-impl设成StdOutImpl,开发阶段可以直观看到 SQL 和参数,排查问题能省大量时间;生产环境建议改成Slf4jImpl或者直接关掉。
这里专门说一个新手常踩的坑:XML 映射文件要放在 classpath 下,不是 src/main/java 下。很多人手滑把 mapper xml 放进 Java 包目录,编译后 resources 里根本没有这个文件,启动就会出现Invalid bound statement (not found)。建议在src/main/resources下建一个mapper目录,和mapper-locations: classpath:mapper/*.xml对应起来。IDE 里顺便装一个 MyBatisX 插件,XML 里的标签高亮和跳转都会舒服很多,这个在写复杂 SQL 的时候尤其有用。
再说缓存。MyBatis 一级缓存是 SqlSession 级别的,默认开启,但 Spring 整合 MyBatis 之后每次操作基本都是新会话,一级缓存的实际作用远没有想象中那么大。二级缓存是 namespace 级别的,默认关闭,而且我明确建议不要在业务项目里开。为什么?因为二级缓存一旦失效更新,很容易出现“改了数据库,查出来还是旧数据”的情况,排查起来非常费劲。老实的做法是把缓存交给 Redis 统一管,热点数据手动控制失效时间,而不是依赖 MyBatis 内置缓存。分布式场景下数据一致性是另一个大话题,这里不展开,但至少不要图省事直接开二级缓存。
3.3 事务、鉴权与全局异常处理
车间系统的数据一致性通常靠事务保证。领料、扣减库存、生成记录这三步操作,任何一步失败都应该整体回滚,否则库存账和流水账就对不上了。在方法上加上@Transactional可以实现这个效果,但要记住几个前提:第一,方法必须是 public;第二,异常没有被捕获吞掉;第三,不要出现同类内部自调用,否则代理不生效,事务会失效。这几个点也是 Java 面试里高频出现的 @Transactional 失效场景,建议自己写 demo 验证一遍。
鉴权方面,这套源码一般会用到 JWT。流程很标准:用户登录成功后签发 token,前端每次请求带上 token,后端通过拦截器校验 token 合法性,并把用户信息放进上下文。拦截器只拦截/api/**路径,登录注册接口要放行。还有一种做法是用 Session,但对前后端分离项目来说,JWT 无状态、跨域友好,更适合接口化架构。
全局异常处理是我很看重的一个模块。如果没有统一异常处理器,数据库异常、空指针、自定义业务异常会直接以默认错误堆栈的形式返回给前端,既不安全也不友好。用@RestControllerAdvice兜底,业务异常返回固定的错误码,未知异常打印日志后统一返回“系统繁忙”,前端可以用同一个逻辑处理所有错误。这里有一个细节:错误信息要分类,比如参数校验错误是 400,未登录是 401,没有权限是 403,业务错误是 5002 这种自定义码,方便前端精确判断。
3.4 日志、SQL 打印与安全过滤
开发时 SQL 打印配合日志看请求链路,能解决大部分联调问题。但生产环境里如果每个 SQL 都打到控制台,性能有损耗。我的建议是开发环境用StdOutImpl打印完整 SQL,生产环境把日志级别设为 WARN,只保留必要的请求日志和异常日志。用 logback 时日志文件要按天滚动,保留最近 30 天,不然磁盘会被撑满。
安全方面,除了登录鉴权,还要防 XSS。车间系统里经常上传工艺卡片、检验报告,有些是 PDF,有些是图片。处理上传文件时的思路是这样的:文件内容和文件名要分开看待。文件内容要校验类型和后缀,禁止上传可执行文件;文件名和表单参数要经过 HTML 转义过滤,避免把<script>这类内容存进数据库。实现上可以写一个全局过滤器,把所有请求参数里的危险字符转义掉,再放行到 Controller。这个过滤器对上传的 PDF 不会去解析内容,那是文件服务的事,但会处理文件名和备注字段,从入口处挡住大部分脚本注入。
4. 前端 Vue3 工程化与联调实践
4.1 Vite + Vue3 环境搭建与组件库集成
前端部分我用 Vue3 + Vite + Element Plus 的组合,这是目前后台管理系统比较成熟的方案。Vue3 安装这一步比较简单,直接用 Vite 模板初始化:
npm create vite@latest factory-web -- --template vue cd factory-web npm install npm install element-plus vue-router pinia axios sass npm run dev这里要说一下为什么用 Vite 而不是直接用 Webpack。Vite 在开发环境下基于原生 ES Module,冷启动秒开,大幅提升了联调效率;生产构建走 Rollup,速度也快。不过 Vite 对 Node 版本有要求,建议用 Node 16 以上的版本,避免各种兼容问题。
SCSS 在 Vue3 项目里使用很简单,安装sass之后,在style标签上加lang="scss"就能用变量和嵌套。后台管理系统通常需要一个全局样式文件,把按钮间距、卡片阴影、页面背景这些统一定义成变量,组件里就不用来回写死颜色了。
4.2 Pinia、路由与 Axios 封装
Vue3 里状态管理用 Pinia,比 Vuex 更简洁。车间系统需要存全局状态的无非是用户信息、权限菜单和可选的工单筛选条件。我习惯建一个userStore存 token 和用户信息,登录成功后调用接口获取角色,然后根据角色渲染对应菜单。
路由要配合登录状态做守卫。router.beforeEach里判断有没有 token,没有就跳登录页;有 token 但访问的是登录页,就跳回首页。权限如果不复杂,直接用 meta 字段标记哪些路由需要管理员角色,在守卫里对比一下当前用户角色即可。这个模式对大多数管理系统都够用,不必一上来就用复杂的动态路由。
Axios 封装几乎是前端工程必备的。我通常是建一个request.js,统一设置 baseURL,请求拦截器里从 localStorage 取 token 并加到 header,响应拦截器里先判断 HTTP 状态码,再判断业务状态码。需要注意的点是:不要把业务数据直接返回给页面组件,而是解包成res.data,页面上调用时就不用每次.data.data了,代码会干净很多。统一处理 401 也很关键,token 过期后直接清掉登录状态并跳回登录页,而不是让用户对着报错页面发呆。
// request.js 精简示例 import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 10000 }) request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) request.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { return Promise.reject(new Error(res.msg)) } return res.data }, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') window.location.href = '/login' } return Promise.reject(error) } )4.3 车间看板、表单与列表的组件化实现
车间系统里最常写的两个界面是表格和表单。列表页建议统一封装SearchBar、Table、Pagination的组合:搜索表单负责条件筛选,表格展示带分页的数据,分页组件维护当前页和页码。组件内部不要写死字段,而是通过 props 传入列配置,这样每个页面只关心自己的查询逻辑和列定义,新增页面时能省很多重复代码。
车间看板相对特殊一点,它是给产线大屏幕用的,通常要轮询更新各产线的完工数和设备状态。最简单的方式是每隔 5 秒请求一次接口刷新数据,数据量小、实现简单;如果要做实时推送,可以上 WebSocket,但复杂度会明显上升。我做过几个这类系统,发现对大多数车间场景,轮询已经完全够用,实时推送只在设备监控这类高频场景里有意义。
表单校验是另一个容易出问题的点。比如工单数量必须大于 0、计划结束时间必须晚于计划开始时间、物料编码不能为空,这些都建议前后端双校验。前端校验提升交互体验,后端校验保证数据安全,两层都不能省。
4.4 联调阶段最常见的跨域与数据格式问题
前后端分离联调时,第一个卡住的就是跨域。
最省心的开发期方案是在 Vite 里配 proxy,让前端开发服务器把/api开头的请求转发到后端地址。这样浏览器看到的是同源请求,不需要后端开 CORS。
// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })生产环境如果前后端分别部署,通常由 Nginx 做反向代理,把/api转发到后端服务,同样不存在跨域问题。如果后端自己配了 CORS 拦截器,开发时也可以不用 Vite proxy,但我个人习惯统一用 proxy,因为后端少配一层安全入口,前端也能模拟生产环境的路径规则。
联调时还要注意前端传参格式和后端接收一致。POST 提交 JSON 时,后端要用@RequestBody接收;如果是表单提交,后端要用@RequestParam或普通对象接收。最常见的问题就是前端传的是 JSON,后端用@RequestParam去接,结果参数全是 null,然后查了半天查不到原因。这类问题其实在浏览器 NetWork 面板里看请求 payload 就能快速定位。
5. 源码部署、启动与常用问题排查
5.1 环境准备与数据库初始化
本地把系统跑起来,环境其实就四件事:JDK、Maven、Node、MySQL。
JDK 建议用 8 或 11。如果是 SpringBoot 2.7 系列,JDK 8 完全没问题;SpringBoot 3.x 必须 JDK 17,这个要看源码用的版本,别装错。Maven 装好之后记得把仓库镜像配成国内源,否则依赖下载会让人怀疑人生。Node 版本 Vite 项目要求 16 以上,低于这个版本会报各种语法错误。
MySQL 安装时有两个点特别容易踩。第一是字符集,初始化时一定要用utf8mb4,虽然 MySQL 8.0 默认就是 utf8mb4,但老版本建库时可能还是 latin1,导致中文乱码。第二是 8.0 的密码认证方式默认是caching_sha2_password,如果 pom.xml 里的 MySQL 驱动版本太老,连数据库会报Public Key Retrieval is not allowed。最好的做法是驱动使用com.mysql.cj.jdbc.Driver8.0 以上版本,并在连接串上加上allowPublicKeyRetrieval=true。
数据库初始化一般有 SQL 脚本,在 Navicat 或者命令行里执行即可。没有 SQL 脚本的话,可以按前面建表思路手动建几张基础表,重点是先把 user 和 work_order 建出来,登录模块才能跑通。
5.2 后端启动步骤
后端启动分三步:改配置、装依赖、启动。
先改application.yml里的数据库连接参数,用户名密码要和你本机 MySQL 一致,不要直接复制别人的配置就跑。然后在项目根目录执行mvn spring-boot:run,或者用 IDEA 打开项目直接运行主类。第一次启动会下载大量依赖,要耐心等。启动成功的标志是看到Started Application in ... seconds日志。如果启动立刻报错,大概率是数据库没连上、端口被占用或者驱动版本有问题。
排查端口占用很常见,直接告诉你在 Windows 下怎么查:
netstat -ano | findstr 8080 taskkill /PID <进程号> /FLinux 和 macOS 则用lsof -i :8080查端口。
5.3 前端运行与打包
前端启动之前先npm install,运行时npm run dev。Vite 默认端口是 5173,浏览器打开后如果页面能渲染出来,说明前端环境正常。登录时要先确认后端已经启动,然后看 Network 面板里请求是否走通。
生产部署时前端要打包:
npm run build构建产物会生成在dist目录,把这个目录丢到 Nginx 的 html 目录,再把/api路径反向代理到后端服务即可。一个简化配置可以这样写:
server { listen 80; server_name your-domain.com; location / { root /opt/factory-web/dist; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }注意try_files一定要配上,因为 Vue Router 是 history 模式,刷新页面时 Nginx 才能正确回退到 index.html,否则会出现“页面刷新后 404”的经典问题。
5.4 常见问题速查表
| 现象 | 排查思路 | 解决办法 |
|---|---|---|
| 后端启动报端口占用 | 确认 8080 是否有别的服务 | 杀掉占用进程或改server.port |
| 数据库连接失败 | 看用户名、密码、host 是否正确 | 对照application.yml逐项检查,测试连接 |
| SQL 执行报中文乱码 | 数据库字符集不对 | 库表统一用 utf8mb4,连接串加characterEncoding=utf8 |
| 前端请求跨域 | 看浏览器 Network 里的 CORS 报错 | 开发用 Vite proxy,生产用 Nginx 代理 |
MyBatis 报Invalid bound statement | XML 文件没被编译进 classpath | 把 XML 放到resources/mapper目录 |
| 接口返回 401 | token 没传或已过期 | 检查 Axios 请求拦截器是否正确带 token |
| 列表分页数据重复 | SQL 排序不稳定 | 分页查询加一个确定性的排序字段,如ORDER BY id DESC |
| 工单状态更新后列表不刷新 | 前端缓存或后端缓存 | 清浏览器缓存,检查是否误开了二级缓存 |
排查问题我有一条原则:先看请求,再看日志,最后看数据库,不要上来就改代码。前端 F12 打开 NetWork 面板,看到接口请求的 URL、参数、响应内容,问题解决一半;再看后端日志有没有异常堆栈;确认没问题才去连数据库验证数据。很多 Bug 都是配置或环境问题,而不是业务代码的问题,直接改代码反而会把问题搞复杂。
我的几个实操体会
最后再说几句心里话。车间管理系统这类项目,真正的难点从来不在框架本身,而在业务状态的闭环。工单状态、物料库存、设备状态、质检结果,这些数据之间有天然的先后依赖,你把表结构和状态流转设计清楚了,后端代码写起来就顺;反过来如果建表时少了字段,后面就得不停改接口改页面,成本会成倍放大。
我自己踩过最多的坑是时区问题。数据库和 JDK 的时区不一致,时间字段存进去和查出来差了 8 个小时,折腾了很久才发现是连接串里少配了serverTimezone=Asia/Shanghai。这种问题在开发机上偶尔会出现,在服务器上更容易发生,所以建议连接数据库的 JDBC 串统一把时区写死,不要在部署环境上赌默认值。
如果你正准备把这套系统跑起来,我的建议是别急着加功能,先照着流程把登录到工单创建、领料、质检报工的闭环走通,再考虑加仪表盘或者导出报表。真正跑通一个完整业务闭环之后,你回头再看 SpringBoot、MyBatis、Vue3 这些技术点,会发现自己对它们的理解完全不一样了。