☰
SpringBoot+Vue+MySQL人事管理系统:从部署到避坑全指南
2026/10/15 18:21:17 网站建设 项目流程

直接在开始之前,我先说个结论:一套 SpringBoot 后端 + Vue 前端 + MySQL 的中小企业人事管理信息管理系统,只要你的电脑环境没有严重的历史包袱,从拿到源码到登录进首页,二十分钟以内完全够用。这套组合现在的成熟度已经很高了,依赖、配置、启动方式全都摆在明面上,唯一容易出问题的反而是一些看起来不起眼的小地方,比如 MySQL 版本、Node 版本、端口占用、跨域配置这些。这篇文章我打算把人话讲透,把整套系统的功能边界、源码结构、启动步骤,以及我实际跑过之后踩到的高频坑全部梳理一遍。

适合看这篇文章的人大概分三类:一是公司里的人力或者 IT 负责人,想花最小成本弄一套自己能控制的人事系统,告别 Excel 和微信群统计;二是刚接触全栈开发的开发者,需要一个结构清晰、能跑通、能二次改的参考项目;三是做毕设或者个人作品集的朋友,需要一套可以直接演示的后台管理系统。目标很明确:让你用最短时间把系统跑起来,并且知道它为什么这么设计、后续怎么改。

1. 为什么这套组合恰好在中小企业的舒适区里

1.1 人事系统选型,先看三点

中小企业选人事管理系统,先别急着追求功能全,先看三件事:多少钱、谁维护、能不能改。软件采购的年费对小公司是一笔隐形负担,市面上常见的 SaaS 人事系统按人头计费,一百个人一年下来也是一笔不小开销,而且数据放在别人那里,想导出来做定制分析往往还要额外付费。自己搭一套部署在内部服务器上的系统,一次性搞定,长期用下来性价比确实高很多。

然后是谁维护。很多中小企业没有专职的后端工程师,可能只有一个懂点技术的行政或者外包团队。这就决定了技术栈不能太冷门,出了问题在社区里问不到答案就很痛苦。SpringBoot、Vue、MySQL 这三样,几乎算是国内 Java 全栈开发者默认会的组合,网上资料多、教程多、出问题能找到的解决方案也多,维护门槛相对低。

最后是能不能改。人事管理的业务逻辑每家都不一样,有的公司考勤复杂,有的公司薪资结构特殊,有的还有很强的审批流程需求。SaaS 产品的定制能力通常有限,想改个字段、加个报表都要提工单。而自己掌握的源码,想加模块、改逻辑、接企业微信通知,都完全可以自己动手,这就是“可掌控”的价值。

1.2 三件套各自的角色和取舍逻辑

这套系统里,SpringBoot 负责后端接口和业务逻辑,Vue 负责前端页面和交互,MySQL 负责数据持久化。三者的分工很清楚:前端收集用户操作,调用后端接口;后端处理业务、校验权限、读写数据库;MySQL 存储员工、部门、薪资、考勤等所有结构化数据。

为什么是 SpringBoot 而不是 PHP 或者 Node?SpringBoot 在 Java 生态里已经成了事实上的标准,内嵌 Tomcat,不需要单独装容器,打包成 jar 就能跑,对部署环境要求极低。而且 SpringBoot 的生态太全了,安全框架、持久层框架、缓存、消息队列都有成熟方案,哪怕以后系统要做大,也能平滑扩展。PHP 虽然上手快,但是后期代码规范性和工程化差一些;Node 做前端友好,但后端一些复杂事务和生态积累还是不如 Java 稳。

前端为什么是 Vue 而不是 React?Vue 的上手曲线更平缓,模板语法接近 HTML,对一个以“快速交付”为目标的系统来说,开发效率很高。而且 Element UI 或 Element Plus 这类组件库让后台管理页面的开发几乎变成搭积木,表格、表单、弹窗、树形控件全都是现成的。React 也很好,但相对 VUE 来说,中小团队的学习成本会高一点点。

MySQL 的选择更不需要纠结了,免费、稳定、运维资料多,InnoDB 引擎的事务能力对薪资、考勤这种需要数据一致性的场景完全够用。有些项目喜欢上 PostgreSQL 或者 MongoDB,PostgreSQL 确实功能更强,但多数做 Java 开发的老手对 MySQL 更熟,遇到问题好排查;MongoDB 这种文档型数据库用在人事数据上反而不合适,关联查询和事务一致性都别扭。

1.3 这套系统的边界:什么该做,什么不该做

我很反对一上来就把系统设计得很重。中小企业人事系统最常见的使用场景是:员工档案、部门岗位、考勤记录、请假审批、薪资登记、公告通知、权限管理。这套系统把这些核心流程覆盖到,就已经能解决 80% 的日常问题了。

但有几件事,我不建议在初期版本里碰。一是复杂的薪酬核算逻辑,比如社保公积金的地区差异、个税专项附加扣除、绩效奖金的多级加权,这些规则太容易变,每个地方都不一样,硬写到代码里后续维护会非常痛苦。初期可以做“薪资项 + 月度录入 + 工资条展示”,把计算的重任先留给 Excel 或者财务软件,系统只做登记和下发工资条。二是复杂的审批流引擎,比如多级会签、条件分支、超时自动转交,这些如果要做得完整,工作量比整个员工管理模块还要大。初期用固定的“申请人—直属上级—HR”三层审批就够了。三是移动端 App,小公司没必要一上来就做 App,一个手机浏览器能访问的响应式页面就很够用了。

划清楚边界,才能让这套系统真正“可直接运行”。很多项目跑不起来,不是因为代码有问题,而是因为功能设计得太满,一堆没做完的按钮和模块在那里,用户一点就报错。好系统是做完的模块都能用,没做的功能干脆不显示。

2. 人事系统到底管哪些事:模块边界与数据模型

2.1 核心模块:从员工档案到工资条

打开系统之后,左侧菜单一般会分成几个主要区域。第一个是系统管理,包含用户管理、角色管理和菜单权限,这部分是后台系统的地基,控制谁能登录、谁能看什么。第二个是组织架构,包含部门管理和岗位管理,树形结构能直观看到公司的层级。第三部分是员工管理,这是使用频率最高的模块,包括员工列表、新增员工、员工详情、入职转正调岗离职等状态流转,列表页一般支持按部门筛选、按姓名或工号搜索、分页展示。

考勤管理是这个系统的重点模块。日常操作包括每日打卡记录、请假单提交与审批、加班登记和月度考勤汇总。打卡数据可以给员工一个简单的上下班打卡按钮,也可以在后台直接录入,小公司一般没有严格的闸机接口需求,人工录入加简单统计就够了。请假流程通常是员工提交、直属上级审批、HR 最终确认,审批通过后状态自动流转。

薪资管理模块承担的是“工资条下发”的职责。管理员每个月录入或者导入每个员工的应发工资、社保扣款、个税、实发工资,员工登录系统后可以查看自己的历史工资条。这块的权限要格外小心,薪资数据只能本人和 HR 角色看到,绝对不能做成所有人可见。招聘管理在部分系统里也有,主要是职位发布、简历入库、面试记录这些轻量功能,有就比没有强,但优先级不高。

公告通知模块是提升系统存在感的关键。公司制度、放假安排、培训通知都可以通过公告发出去,用户登录首页就能看到。这个模块实现起来简单,但实际使用中它的价值比想象中高,能让员工养成“有事上系统看”的习惯。

2.2 权限模型:RBAC 是怎么落地的

这套系统在权限上通常采用 RBAC 模型,也就是用户关联角色、角色关联权限的经典模式。实际落地时,用户表里不直接存权限,而是存角色 ID;角色表里可以通过菜单表配置自己能访问哪些功能。这样设计的好处是:新增一个员工只需要给他分配角色,不需要一个个去勾权限;调整一批人的权限,只需要改角色。

菜单权限通常分两级控制。第一级是路由权限,程序员在 Vue Router 里根据用户角色过滤出他能访问的页面,没权限的菜单直接不显示。第二级是按钮权限,比如“新增员工”按钮、比如“删除员工”按钮,在一个列表页面里,普通 HR 可能只能查看,管理员才能删除。后端的接口也要做同样的校验,不能只靠前端隐藏按钮,因为懂技术的人完全可以直接调接口。

RBAC 的实现过程中有一个很容易被忽略的点:超级管理员角色一般不走常规的权限判断逻辑,而是直接放行所有接口。这不是偷懒,而是为了避免万一权限配置错了,连管理员自己都被锁在系统外面。首次启动系统时默认账号通常也是超级管理员,方便初始化数据。

2.3 数据库表结构:关键表与设计细节

数据库设计决定了这个系统的天花板。核心表至少有这几张:系统用户表、角色表、用户角色关联表、菜单权限表、部门表、员工信息表、考勤记录表、请假申请表、薪资记录表、公告表。

员工信息表是关键中的关键。一般会包含工号、姓名、性别、出生日期、手机号、邮箱、入职日期、转正日期、离职日期、所属部门、岗位、学历、紧急联系人、住址等字段。有几个细节要注意:工号要建唯一索引,这是员工在系统里的业务主键;离职状态不要直接删记录,而是用状态字段标记,同时保留一个离职日期;所有表最好都有创建时间和更新时间字段,方便日后排查数据问题。

部门表建议做成树形结构,通过父级部门 ID 关联,而不是把所有层级都拍平。树形结构虽然查询时要用递归,但对组织架构这种天然层级的数据来说是最合理的。考勤记录表要记录考勤日期、员工 ID、上班打卡时间、下班打卡时间、考勤状态,状态一般有正常、迟到、早退、缺卡、请假等枚举值。薪资表不要直接覆盖历史数据,每个月生成一条新的月度薪资记录,这样工资条天然就带月份维度,查历史记录很容易。

物理外键我不太建议在表结构里大量使用,更多是用逻辑外键,也就是字段关联但不在数据库层面强制约束。原因是实际开发中,分页查询、批量导入、数据归档都对物理外键不友好,而且一旦外键约束写错,插入数据时经常出现莫名其妙的失败。逻辑外键配合代码层面的校验,对运维和管理都更灵活。

3. 源码长什么样:后端分层、前端组织与关键接口

3.1 后端包结构与统一返回格式

拿到后端源码后,先看包结构。标准的分层大概是 controller、service、mapper、entity、config、common、utils 这七个包。controller 层只做参数接收和结果返回,不写业务;service 层写业务逻辑,比如校验、计算、状态流转;mapper 层通过 MyBatis-Plus 操作数据库;entity 是数据库表对应的实体类。

后端代码里我觉得最值得先看的是 common 包下的统一返回类。所有接口返回的数据结构都统一是 code、message、data 三段式,例如登录成功后返回 code 200、message 成功、data 里带着 token 和用户信息。这样前后端联调非常省心,前端只需要在统一的拦截器里判断 code 是不是 200,不需要每个接口单独处理异常。

配置方面,SpringBoot 的配置文件通常叫 application.yml,里面主要配置端口、MySQL 连接地址、JWT 密钥、文件上传路径等。我第一次跑的时候最容易漏掉的是 MySQL 连接串里的时区参数,不加上去启动连接时会报时区错误。这个问题后面专门再说。

3.2 登录鉴权:JWT 拦截器怎么处理

登录模块的常见实现是 JWT + 拦截器。用户提交用户名密码后,后端校验通过返回一个带签名的 token,前端把 token 存到本地存储或者 Cookie 里,之后每次请求都在 Header 里带上。后端写一个拦截器,统一从请求头里取 token,解析失败就返回 401,要求前端重新登录。

核心代码逻辑大致是:

// JWT 工具类,负责生成和解析 token public String generateToken(User user) { return Jwts.builder() .setSubject(user.getUsername()) .claim("role", user.getRoleId()) .setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 3600 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }

这里有一个实际经验:token 有效期不要设太长,一天到七天比较合理,过期后前端用 401 状态码引导用户重新登录。这样即使 token 泄露,风险窗口也可控。拦截器里除了登录接口和验证码接口,其他接口都要经过校验。有的项目会在这里再加一层 IP 白名单或者接口访问频率限制,小公司初期可以不加,但代码里留着扩展位是好的。

3.3 前端目录与接口联调

再来看前端。Vue 项目的结构通常包括 views 目录存放页面组件、router 目录配置路由、api 目录封装接口请求、utils 目录放 axios 实例和 token 操作、store 目录(如果用 Vuex)放全局状态。

axios 封装是我看前端代码时第一个检查的地方。一个规范的封装应该做到:请求发出前自动从本地存储读取 token 并加到请求头;收到响应后拦截器里统一判断 code,code 非 200 弹出错误提示;遇到 401 自动跳转登录页并清理本地状态。这套逻辑写好之后,每个业务页面只需要写简单的请求方法,不用重复考虑异常处理。

前端开发环境联调后端时,常用的办法是在 vue.config.js 里配一个代理。开发时前端跑在 9528 端口,后端在 8080 端口,直接跨域请求会被浏览器拦截,通过代理把 /api 前缀的请求转发到本地 8080,是开发阶段最轻量的方案。配置大致是这样:

module.exports = { devServer: { port: 9528, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

生产环境就不靠这个代理了,一般是把后端跑在服务器某个端口,前端构建后的静态文件交给 Nginx 托管,再用 Nginx 反向代理把 /api 转发到后端。这个细节我在部署那一节再展开。

4. 半小时跑通全流程:环境、初始化与启动步骤

4.1 环境清单与版本对照

先把环境准备这一关当成最重要的。很多系统跑不起来,都是因为版本不自洽。我整理了一个经过验证的版本对照表,照着准备基本不会出大问题:

软件推荐版本说明
JDK8 或 11SpringBoot 2.x 用 JDK8 稳妥,3.x 需要 JDK17
Maven3.6+后端依赖管理工具,IDEA 自带也可
MySQL5.7 或 8.0注意两者驱动配置不同,8.0 需带 cj 驱动
Node.js14 或 16Vue CLI 项目对过新 Node 敏感,18 以上可能有兼容问题
IDEA / VSCode任意较新版本后端建议 IDEA,前端 VSCode 即可

开始之前先在命令行确认环境。Windows 下分别执行java -version、mvn -v、node -v、npm -v、mysql --version,哪一条报错就先解决哪一条。这一步花五分钟,比后面启动时反复失败高效得多。

4.2 数据库初始化与后端配置

数据库初始化是整个流程里最容易被忽略却最关键的一步。先打开 MySQL 客户端,执行建库语句,注意字符集一定要用 utf8mb4,不然中文数据写入会乱码。建库和导入脚本一般长这样:

CREATE DATABASE IF NOT EXISTS hrms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hrms; SOURCE /你的路径/hrms.sql;

导入成功后,用SHOW TABLES;看下有哪些表,再随便查一条员工表数据,确认表结构和数据都进来了。接下来打开后端的 application.yml,把数据库账号密码改成你自己的。这里最容易踩坑的是 MySQL 8.0 的连接驱动和时区配置,推荐配置如下:

spring: datasource: url: jdbc:mysql://localhost:3306/hrms?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver

注意 driver-class-name 如果是 MySQL 8.0 要用com.mysql.cj.jdbc.Driver,5.7 则用老驱动。allowPublicKeyRetrieval=true 这条在 MySQL 8.0 下经常被忽略,不加上去会在连接时报公钥检索错误。

4.3 启动后端与接口自测

用 IDEA 打开后端工程后,第一步是等 Maven 把依赖下载完。国内网络环境下这一步可能比较慢,打开 Maven 的 settings.xml,配置一下国内公共镜像仓库,速度能快很多。依赖下载完成后,找到主类运行。看到类似Started HrmsApplication in 10.432 seconds的日志,就说明后端启动成功了。

这个时候先别急着打开前端,先用接口自测工具验证几个关键接口。POST 请求登录接口,传{"username":"admin","password":"123456"},看返回包里是否带 token。再带 token 请求一个分页查询员工接口,确认能正常返回数据。接口通了再启动前端,排查范围会小很多。如果这一步就报错,绝大多数情况都是数据库连接问题,去检查账号密码、数据库名、端口、驱动。

4.4 启动前端与首次登录

进入前端目录,先执行npm install。如果项目里自带 node_modules 目录,可以跳过这一步,但保险起见还是执行一次,避免本地环境和自带的依赖不一致。安装完成后执行npm run dev,看到类似App running at: http://localhost:9528的输出就可以打开了。

打开浏览器访问前端地址,用默认账号登录。登录成功后先看几个核心页面:员工管理列表能不能搜到数据、部门树是否正常展示、新增员工能不能保存。通常到这里,这套系统就已经完全跑起来了。整个过程如果顺利,真的不会超过半小时。

如果启动过程中报错,别慌,下一节我整理了我自己实测遇到的高频坑,绝大多数情况你都会在那里找到答案。

5. 我实测遇到的高频坑:跨域、时区、版本与端口

5.1 跨域:开发环境靠代理,生产环境靠反代

第一次跑前后端系统,最常遇到的就是跨域问题。开发环境浏览器访问的是 9528 端口的前端,后端在 8080,端口不同就触发了浏览器的同源策略。解决方案就是前面说的 vue.config.js 里配置 proxy,让前端请求通过代理转发到后端,对浏览器来说所有请求都是同域。

有一种情况是前后端配置都看起来没问题,但接口还是报跨域。检查一下请求路径,看代理前缀是否对得上。如果后端接口路径是 /api/user/list,前端请求路径也必须是 /api/user/list,代理只是转发,不会自动加前缀。还有一种情况是后端自己开了 CORS 配置,和前端代理同时存在,也可能产生诡异问题,建议开发环境只用代理、生产环境只用反向代理,不要两种方案混着来。

5.2 时区、驱动和 MySQL 版本引起的连锁反应

MySQL 相关的坑在我看来是最多也最容易迷惑的。先说时区问题,旧版本 MySQL 连接串里如果没有 serverTimezone 参数,新版驱动会直接报异常,即使不报异常,日期类型的数据存进去也会和实际时间差 8 小时。解决方案就是在连接串里固定写serverTimezone=Asia/Shanghai。

MySQL 5.7 和 8.0 的差异体现在驱动类名上,老驱动在 8.0 下会报找不到类。如果你的项目是从网上找的,可能需要改一下配置。另外 MySQL 8.0 默认使用 caching_sha2_password 认证插件,而一些老客户端不支持,连接时报错时可以去 MySQL 里执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码';解决。

还有个容易被忽略的问题是大小写敏感。Linux 服务器上的 MySQL 默认对数据库名和表名区分大小写,Windows 不区分。如果你在 Windows 上开发没问题,部署到 Linux 却报表不存在,大概率就是表名大小写不一致,解决办法是建表时统一用小写,并且别改表名大小写。

5.3 端口、Node 版本与依赖安装的幺蛾子

后端默认跑在 8080 端口,前端默认跑在 9528 或者 5173 端口,如果本机有其他服务占用,启动时会提示端口被占用。Windows 下用netstat -ano | findstr 8080找到占用进程,要么换端口,要么把占用服务停掉。我个人建议遇到端口冲突直接换端口,修改 application.yml 里的 server.port,把前端代理 target 同步改一下就行。

Node 版本的坑更隐蔽。Vue CLI 构建的旧项目,用 Node 17 以上的版本启动时,经常出现Error: error:0308010C:digital envelope routines::unsupported之类的报错,原因是新版 Node 改了 OpenSSL 的默认算法。我自己试过两个解法,一是用 nvm 把 Node 切到 14 或 16 版本再启动;二是在 package.json 里给启动脚本加NODE_OPTIONS=--openssl-legacy-provider,但这个方法在新版本上不一定生效,最稳妥的还是切换 Node 版本。

依赖安装慢或者失败也很常见。npm install 执行到一半卡住,多半是网络问题,把 npm 源切换到国内镜像会快很多。如果安装完成后启动时报模块找不到,先删掉 node_modules 和 package-lock.json,重新npm install一次,百分之八十的问题能解决。这些都不是代码问题,纯粹是环境问题,习惯了之后五分钟能定位完。

我把这些高频坑整理成一张表格,方便你遇到问题时快速对照:

症状根因标准解法
前端请求接口报跨域开发环境没配代理或前缀不匹配检查 vue.config.js proxy 配置
后端启动报时区异常连接串缺 serverTimezone追加 Asia/Shanghai 参数
连接数据库报公钥错误MySQL8 未开 allowPublicKeyRetrieval连接串加 allowPublicKeyRetrieval=true
登录能进,列表查不到数据数据库未初始化或表数据为空重新导入 SQL 脚本
启动报端口被占用8080/9528 被其他进程占用换端口或杀占用进程
Node17 启动前端报 OpenSSL 错误Node 版本与 Vue CLI 不兼容用 nvm 切到 Node14/16
部署到 Linux 报表不存在MySQL 大小写敏感统一建表用小写表名

6. 从本机走向服务器:打包部署和后续还能怎么改

6.1 生产构建:jar 包与 dist 目录

开发环境跑通只算第一步,真正交给公司用还得部署到服务器。后端打包很简单,在项目根目录执行mvn clean package -DskipTests,构建完成后 target 目录下会生成一个 jar 包。这个 jar 包就是整个后端,里面内嵌了 Tomcat,放到装了 JDK 的机器上就能跑。

前端构建执行npm run build,完成后生成一个 dist 目录,里面全是静态文件:html、js、css。这套静态文件本身已经能直接打开,但真正生产环境建议用 Nginx 托管,而不是直接双击 index.html。原因是路由和接口代理都需要 Web 服务器配合,直接打开文件的方式会导致刷新页面时路由变成 404。

6.2 服务器部署的简易方案

服务器上首先得装好 JDK 和 MySQL,然后上传 jar 包,执行启动命令:

nohup java -jar hrms.jar --server.port=8080 > system.log 2>&1 &

前端把 dist 目录里的文件上传到服务器某个目录,比如 /usr/share/nginx/hrms,然后配置 Nginx。一个典型的配置思路是:根路径指向静态文件目录,/api 路径反向代理到本机 8080 端口。这样用户访问域名就能进入系统,所有接口请求都由 Nginx 转发给后端,不需要在前端代码里暴露后端端口。

部署过程中还有一个经常被忽略的点:数据库和服务器是不是同一台机器。如果是分开的,数据库连接串里的 localhost 要改成数据库服务器的内网地址,并且确保数据库开了远程访问权限,防火墙放行对应端口。我遇到过很多次,数据库配置没问题,但是连不上,查到最后都是安全组或者防火墙没放行。

6.3 后续迭代:我的三个优先建议

系统跑起来之后,下一步最该做的是选对迭代方向。我自己的经验,按性价比排序,第一个建议做员工自助功能,让员工自己登录系统查看工资条、更新个人信息、提交请假申请。这个改动能把 HR 从大量重复沟通里解放出来,是投入产出比最高的功能。

第二个建议是完善消息通知。考勤异常、审批待处理、公告发布,这些都能通过站内信或者企业微信类的工具推给对应的人。不用一开始就做很复杂的推送系统,先把站内消息这个最简单的场景打通,让用户打开系统能看到待办事项,就已经比微信群通知可靠得多了。

第三个建议是数据报表。员工花名册、部门人数统计、月度考勤汇总、工资总额趋势,这些都是管理层经常问的数据。初期不需要做复杂的数据可视化大屏,先实现几个核心报表导出 Excel 的功能就够用。管理层把系统当成数据来源,这个系统才算真正扎根在业务流程里。

最后说一个我的个人体会:这套系统的价值不完全在于代码本身,而是它把一个公司最基础的人力数据从 Excel 里搬到了一个可以查询、可以统计、带权限控制的平台里。我在实际使用中的感受是,跑通只是开始,真正让系统“活”起来的是后续持续的数据维护和流程优化。每次看到一个公司从手工表变成系统化运营,我都会觉得这一步带来的改变比想象中更大。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询