当前位置: 首页 > news >正文

SpringBoot+Vue人事管理系统:从源码拆解到实战部署

简介:这是一套面向Java与前端初学者、实习开发者及中小型项目实践者的人事管理系统完整源码,基于SpringBoot+Vue实现前后端分离架构,覆盖员工信息管理、部门维护、岗位配置、考勤统计等核心HR业务场景。资源包共189个文件,包含60个Java后端业务与控制器类、23个Vue组件及23个JS逻辑脚本、44个PNG图标与SVG/GIF资源,辅以XML配置、YML启动参数及SQL初始化脚本,结构清晰、模块职责分明,便于理解分层设计与接口联调流程。压缩包体积为6.98MB,轻量易导入,适合快速部署学习或二次开发。已有546人下载学习,配套代码具备完整登录鉴权、RESTful API设计、Element UI界面布局及基础权限控制逻辑,可直接运行查看前后端交互细节,是掌握企业级全栈开发流程的典型参考案例。 搞人事管理系统这个方向,说实话这些年经手过不少类似项目。很多朋友拿来一套"基于SpringBoot+Vue开发的前后端分离人事管理系统源码",第一反应是想跑起来看看效果,但真要往自己业务里套,就发现细节问题一个接一个。这套系统我拆过也重构过,从数据库设计到权限模型,从前端列表到部署上线,今天抽时间把整个思路和实现过程完整聊一遍,给正在做前后端分离项目实战、或者想拿源码改造自用的小伙伴一个参考。

先说结论:这套东西选型没问题,SpringBoot加Vue的组合在中小型企业内部系统里非常能打,源码本身也覆盖了人事管理最核心的闭环。但它不是开箱即用的产品,更像一个高质量脚手架。想真正落地,你得理解每个模块背后的设计意图,把业务状态流转理顺,把权限模型搞扎实,再把部署那一套走通。下面我按拆解源码的顺序,把关键环节逐层讲清楚。

1. 人事系统要管的不是"人",是一整套业务状态流转

很多初学者看到"人事管理系统"这六个字,脑子里浮现的就是增删改查:加个员工、改个信息、删个记录。真这么想,做出来的就是一个电子表格,不是系统。人事管理的核心是状态流转,一个人从进入公司到离开公司,他的状态是不断变化的,系统要管的其实是这个变化过程,以及伴随这个过程的各类业务数据。

1.1 员工生命周期的六个关键节点

我习惯把所有业务先拆成一条时间线,这样后面设计表结构、写接口、画页面都会变得特别清晰。一个员工从候选到归档,大致经过下面这些节点:

状态节点触发条件关键操作产出数据
入职登记通过面试、确认到岗录入基础档案、上传证件员工主数据
试用期入职当天开始设置试用期时长、工资标准试用期记录
转正试用期结束且考核通过更新员工状态、调整薪资转正记录
调岗调薪业务调整或个人申请变更部门、岗位、薪酬异动记录
离职主动辞职或公司辞退离职审批、工作交接、工资结算离职记录
离职归档离职手续办结封存档案、权限回收归档记录

源码里的员工状态字段基本就是按这条线设计的,通常是一个整数枚举:0试用、1正式、2调岗中、3离职,再往后就是归档状态。我在实际重构时更喜欢直接用字符串枚举或者Java枚举类,因为数字枚举在排查业务时看数据库根本看不明白,还得翻代码。

public enum EmployeeStatus { PROBATION("probation", "试用期"), FORMAL("formal", "正式"), TRANSFERRING("transferring", "调岗中"), RESIGNED("resigned", "已离职"), ARCHIVED("archived", "已归档"); private final String code; private final String description; EmployeeStatus(String code, String description) { this.code = code; this.description = description; } public String getCode() { return code; } public String getDescription() { return description; } }

别小看这个状态字段,它是整个系统的"中枢神经系统"。考勤、薪资、审批、报表,全部依赖这个状态做关联查询。你要是把状态设计成简单的字符串存在数据库里,后面做统计报表时SQL不知道要写多长。

1.2 人事系统的六大功能模块

围绕这条生命周期线,系统需要拆出六个功能域。这也是源码里Controller命名的主要依据:

  • 组织架构管理:部门树、岗位体系、编制管理
  • 员工档案管理:基础信息、证件信息、教育经历、工作经历、合同信息、紧急联系人
  • 考勤管理:排班、打卡记录、请假、加班、补卡审批
  • 薪资管理:薪资结构、核算规则、工资条、社保公积金
  • 合同管理:劳动合同签订、续签、解除提醒
  • 报表中心:人员编制报表、入离职统计、学历结构、年龄分布

我看过很多学习者拿到源码就急着跑前端,把后端启动起来,看了一会儿觉得"就这?",然后关掉。问题就出在只看到了页面,没看到模块之间的关联关系。真正值钱的不是某个员工列表页面,而是"部门、员工、考勤、薪资、合同"这五张主表之间的耦合方式。

1.3 为什么很多公司弃用SaaS选择自研

聊个现实问题。现在市面上一套SaaS人事系统一年几千块到几万不等,功能还挺全,为什么还要用SpringBoot自己搞一套?

我给出的答案是:人事系统是典型的"数据敏感型 + 流程定制型"业务。不同公司的试用期规则不同、薪资结构不同、审批链路不同,SaaS产品为了兼容所有客户,表单字段和流程编排都做得非常抽象。真用起来就会觉得,每个功能都有,每个功能都差一点。自研系统的好处是数据结构自己说了算,字段自己加,流程自己编排,还能跟内部其他系统打通。

源码项目最大的价值不是省掉买SaaS的钱,而是提供了一个可以完全掌控的数据底座。你在这个底座上改业务逻辑,改多狠都行。

2. 这套技术选型的合理性,以及它背后的分工逻辑

SpringBoot加Vue在前后端分离项目里已经快成标配了,但很多人说不清为什么是这两个。我站在改造项目的角度把分工逻辑说透。

2.1 SpringBoot层:负责一切"不该前端关心"的事

SpringBoot在这套系统里至少承担了以下五个职责,缺一个都不行:

  • 数据建模与数据访问:通过MyBatis-Plus或Spring Data JPA映射数据库表
  • 业务规则实现:计算薪资、校验考勤、判断合同到期,核心逻辑全在后端
  • 安全认证与鉴权:登录签发Token,接口校验权限,密码加密存储
  • 接口协议编排:把业务能力暴露成RESTful接口,前端只需要关心数据
  • 系统集成能力:对接邮件服务、消息通知、文件存储、导出工具

你发现没有,这套分工的核心逻辑是"前端不做任何业务判断"。我做项目时反复跟团队强调一个原则:前端只翻译用户操作、展示数据结果,比如"试用期员工能不能调薪"这种规则判断,绝不能出现在前端代码里。否则前端改一个条件,后端接口就被绕过,数据一致性完全失控。

SpringBoot在Java生态里的好处是自动化配置极其省心。一个spring-boot-starter-web依赖,内嵌Tomcat,java -jar跑起来就是完整应用。源码里如果看到pom.xml或者build.gradle,你会发现依赖管理非常清晰,业务模块按starter拆分,这是后期裁员维护成本比较低的关键。

2.2 Vue层:负责交互体验与状态管理

前端部分,源码里常见的是Vue 2 + Element UI或者Vue 3 + Element Plus的组合。Vue的响应式机制在处理表单数据、列表筛选、联动选择这些场景时非常顺手。

以员工档案录入为例,选择部门后岗位下拉要根据部门联动,选择婚育状态后某些字段要显示或隐藏。用Vue的computed属性加watch监听器,这些联动逻辑写起来非常清爽:

export default { computed: { // 根据当前选中的部门过滤岗位列表 filteredPositions() { if (!this.form.deptId) return [] return this.allPositions.filter(item => item.deptId === this.form.deptId) }, // 是否显示紧急联系人区域 showEmergencyContact() { return this.form.contractType === 'formal' } }, watch: { 'form.deptId'(newVal) { // 清空岗位重新选择,避免数据不一致 this.form.positionId = '' } } }

源码前端的路由设计一般会分成两类:一类是登录后根据角色动态生成的路由表,管理员和普通HR看到的菜单不同;另一类是公共页面,比如登录页、404页。动态路由这块是前端最需要吃透的部分,它跟后端的权限模型紧密绑定。

状态管理方面,Vue 3项目用Pinia,Vue 2项目用Vuex。源码里store模块通常包含三个:user模块存当前登录用户信息和Token,app模块管理侧边栏、标签页等界面状态,permission模块管理路由权限。注意这个permission模块千万不要自己造轮子去后端拉菜单表,最好是登录后由后端一次性返回该用户的菜单和按钮权限,前端store里直接保存。

2.3 开发协作模式的转变

前后端分离不只是技术架构,更是团队协作方式的变化。后端定好接口文档,前端拿Mock数据同步开发,两边不用互相等。接口文档这块,源码里一般会集成Swagger,也就是springfox或者springdoc,后端的Controller加了注解之后,打开swagger-ui就能在线调试每一个接口。

在前后端分离项目实战里,我最建议你先定死接口协议再动手写代码。协议里至少要包含三块:

  • 统一响应结构:比如{ code: 0, message: "success", data: {} }
  • 分页参数规范:pageNum、pageSize、total统一命名
  • 异常码规范:业务异常码要从10001开始定义,跟HTTP状态码区分开

这套东西开发前不定好,联调阶段天天扯皮。

3. 数据库设计与后端接口:这套系统的功底全在这里

人事管理系统最见功力的地方,不是前端界面多炫酷,而是数据库设计。表字段设计好了,接口写起来顺手;设计乱了,后期加一个查询条件都是噩梦。

3.1 核心表结构与它们之间的关系

源码里核心表一般是这几张,我建议你导入数据库之后,先用工具画出它们的关系图再动手改代码:

数据表核心字段关联关系
sys_dept 部门表id, parent_id, dept_name, sort自关联树形结构
sys_user 系统用户表id, username, password, employee_id关联员工表
emp_employee 员工表id, emp_no, name, dept_id, position, status关联部门表
sys_role 角色表id, role_name, role_code与用户多对多
sys_menu 菜单权限表id, parent_id, menu_name, perms, path与角色多对多
sal_salary 薪资表id, employee_id, base_salary, bonus, deduction关联员工表
att_attendance 考勤表id, employee_id, work_date, check_in, check_out关联员工表

员工表是绝对核心,其他表都用外键或者逻辑关联指向它的主键。这里注意一点,我不推荐数据库物理外键,全部用逻辑关联,理由很简单:物理外键在做分库分表或者数据归档时特别痛苦,而且删除顺序稍微不对就报外键约束错误。业务系统里,外键约束由应用层去保证。

员工表字段设计上,除了工号、姓名、性别、出生日期这些常规字段,有几个字段是最容易被忽略的:

  • 入职日期和转正日期,很多系统只存一个入职日期
  • 社保缴纳城市,影响社保公积金计算规则
  • 银行卡号和开户行,发工资要用
  • 紧急联系人姓名和电话,要允许一个人录多个,所以应该拆子表
  • 员工照片URL,注意本地存储还是对象存储,决定后续部署复杂度

我见过最头疼的一个客户,他们的组织架构有四级:集团、公司、事业部、部门。原来的系统只支持两级,硬生生往dept表里塞。做人事系统前,一定先跟业务确认组织层级最多几层,这会影响树形查询的写法。

3.2 一个完整的分页查询接口是怎么设计的

以员工列表查询为例,这个接口几乎是所有管理系统的"样板间",源码里最值得精读的就是它。我结合一个标准实现说明白:

@RestController @RequestMapping("/api/employee") public class EmployeeController { @Autowired private EmployeeService employeeService; @GetMapping("/page") public Result<PageResult<EmployeeVO>> page(@Validated EmployeeQuery query) { return Result.success(employeeService.pageQuery(query)); } }

EmployeeQuery继承了分页基类,包含了pageNum、pageSize,以及姓名、部门ID、状态、入职时间段等查询条件。Service层负责组合这些条件,MyBatis-Plus的LambdaQueryWrapper写动态SQL非常方便:

public PageResult<EmployeeVO> pageQuery(EmployeeQuery query) { LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(query.getName()), Employee::getName, query.getName()); wrapper.eq(query.getDeptId() != null, Employee::getDeptId, query.getDeptId()); wrapper.eq(query.getStatus() != null, Employee::getStatus, query.getStatus()); wrapper.between(query.getStartDate() != null && query.getEndDate() != null, Employee::getHireDate, query.getStartDate(), query.getEndDate()); wrapper.orderByDesc(Employee::getCreateTime); Page<Employee> page = new Page<>(query.getPageNum(), query.getPageSize()); Page<Employee> employeePage = employeeService.page(page, wrapper); // 转VO,将部门ID转化为部门名称 List<EmployeeVO> records = employeePage.getRecords().stream() .map(employee -> { EmployeeVO vo = new EmployeeVO(); BeanUtils.copyProperties(employee, vo); vo.setDeptName(deptService.getById(employee.getDeptId()).getDeptName()); return vo; }).collect(Collectors.toList()); return new PageResult<>(records, employeePage.getTotal()); }

这段代码看起来简单,里面对前端非常关键的一个设计是:返回给前端的对象叫VO,不是数据库实体。原因有两点,一是数据库字段不一定想全暴露给前端,比如员工表里的身份证号、银行卡号这种敏感字段,列表接口根本不该返回;二是VO可以自由组装前端需要的展示字段,比如部门名称、岗位名称,让前端少掉一次联查。

3.3 入职、转正、离职这几个状态接口的事务控制

人事系统里最容易出数据问题的是状态流转接口。假设执行入职操作,后端要做的事情包括:插入员工主表、插入用户账号、分配初始角色、记录操作日志。这四步任何一个失败,前面的操作都不能生效,必须在一个事务里。

@Transactional(rollbackFor = Exception.class) public Long createEmployee(EmployeeCreateDTO dto) { // 1. 校验工号唯一性 if (employeeService.count(new LambdaQueryWrapper<Employee>() .eq(Employee::getEmpNo, dto.getEmpNo())) > 0) { throw new BusinessException("工号已存在"); } // 2. 保存员工信息 Employee employee = new Employee(); BeanUtils.copyProperties(dto, employee); employee.setStatus(EmployeeStatus.PROBATION.getCode()); employeeService.save(employee); // 3. 创建系统账号 User user = new User(); user.setUsername(dto.getEmpNo()); user.setPassword(passwordEncoder.encode(initPassword)); user.setEmployeeId(employee.getId()); userService.save(user); // 4. 分配默认角色 userRoleService.assignDefaultRole(user.getId()); // 5. 写入操作日志 operationLogService.record("入职", "创建员工 " + dto.getName()); return employee.getId(); }

注意@Transactional只能保证Spring容器管理的异常回滚,如果你在方法内自己catch了异常没有抛出,事务就失效了。所以rollbackFor要显式声明,而且要保证所有业务异常都继承RuntimeException并抛出。源码里如果有统一的全局异常处理器@RestControllerAdvice,那么业务代码里只管thrown,Controller层不用try-catch,这个设计是非常值得保留的。

3.4 时间字段这个坑,前后端都得注意

Java后端用LocalDateTime,前端用JavaScript的Date,序列化格式如果不统一,查出来的时间是"2025-01-15T08:30:00",用户看得一脸发懵。源码里一般会配置全局的日期序列化格式:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

配置GMT+8这个细节尤为重要。我排查过不止一次"为什么提交的时间比我本地时间早了8小时"的bug,原因就是服务器部署环境时区是UTC,数据库连接串里没有指定时区。MySQL里连接URL建议加上:

jdbc:mysql://localhost:3306/hrms?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

4. 权限模型与多角色管理:源码里最值得借鉴的部分

人事系统的用户角色非常典型:超级管理员、HR管理员、部门经理、普通员工。四种角色能看的数据、能做的操作完全不同。源码中实现权限的部分,说它是整个项目的精华都不为过。

4.1 RBAC模型在三张表里的落地

这套权限模型叫RBAC,角色访问控制,具体落地就是五张表的关联关系:用户表、角色表、菜单表,加上用户角色关联表、角色菜单关联表。

它解决的问题是:不给用户直接授权,而是给角色授权,用户挂到某个角色上,就继承了该角色的所有权限。好处是权限调整只改角色,不用一个个用户去配。部门经理调岗了,换一个角色即可,他的所有权限跟着变。

菜单权限表的perms字段是关键,它存储权限标识字符串,比如employee:listemployee:exportsalary:update,后端接口用这个字符串做鉴权。

4.2 Spring Security + JWT的鉴权流程

源码里要是用的Spring Security,核心就两个部分:认证和授权。认证解决"你是谁",授权解决"你能干什么"。

登录流程是这样的:用户提交用户名密码,后端校验通过后签发一个JWT Token,这个Token包含了用户基础信息,之后前端所有请求都在请求头带Authorization: Bearer <token>,后端拦截器解析Token,拿到用户身份和权限集合。

关键配置在自定义的SecurityFilterChain里:

@Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests() .antMatchers("/api/auth/login", "/api/auth/captcha").permitAll() .antMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() .antMatchers("/api/employee/**").hasAuthority("employee:list") .antMatchers("/api/salary/**").hasAnyAuthority("salary:view", "salary:update") .anyRequest().authenticated() .and() .exceptionHandling() .authenticationEntryPoint(jwtAuthenticationEntryPoint) .accessDeniedHandler(customAccessDeniedHandler); http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }

这套接法在前后端分离项目里是标准范式。无状态会话让后端可以水平扩展,多实例部署不用考虑Session共享问题。JWT的密钥要放配置文件里用环境变量注入,不要硬编码在代码里。

4.3 前端路由守卫和按钮级权限

后端鉴权做完,前端还有一层控制。Vue Router的路由守卫里,每次跳转前检查本地有没有Token,没有就跳到登录页。

router.beforeEach(async (to, from, next) => { const token = useUserStore().token if (!token) { if (to.path === '/login') { next() } else { next('/login') } return } // 已登录且要去登录页,直接跳首页 if (to.path === '/login') { next('/') return } next() })

按钮级的控制一般用自定义指令v-permission实现。一个"导出员工"按钮,管理员能看到,普通HR看不到,这就是按钮级权限:

app.directive('permission', { mounted(el, binding) { const requiredPerms = binding.value const userPerms = useUserStore().perms if (requiredPerms && !userPerms.includes(requiredPerms)) { el.parentNode?.removeChild(el) } } })
<el-button v-permission="'employee:export'" @click="handleExport">导出</el-button>

注意一个要点:前端隐藏按钮只是提升体验,真正的安全防线在后端接口权限。前端可以绕过按钮直接调接口,后端必须做好二次校验。这是前后端分离架构最容易踩的认知坑。

5. 前端页面的几个关键实现,改源码前必看

前端页面多,但核心套路就几个。摸清这几类页面的实现模式,改任何后台管理系统都事半功倍。

5.1 员工列表页的分页、搜索、批量操作模式

列表页是所有管理系统的门面,也是使用频率最高的页面。源码里员工列表页基本遵循同一个模式:

  • 顶部搜索区:姓名、部门、状态的组合条件
  • 主表格区:分页表格,操作列有编辑、离职、分配账号等按钮
  • 批量操作栏:勾选后批量导出、批量调整部门

el-table的分页用法我就不多说了,只提醒一个细节:表格列的宽度和溢出处理。员工姓名、部门名称这些字段不长,固定宽度就好;但"入职时间"这种用YYYY-MM-DD格式的,最好用formatter或者插槽自定义展示。

5.2 多步骤入职表单的实现

入职登记页面我见过各种写法,最推荐的是el-steps加多步表单。第一步基础信息,第二步教育经历,第三步合同信息,第四步账号设置,每一步校验通过才能进下一步,最后提交时一次性调后端接口。

实现逻辑上要注意步骤条组件里每个step的内容不要平铺在一个大form里,而要拆成子组件,这样每步的校验规则互相独立,不会出现"填了第四步的错,把第一步的校验也带出来"的体验问题。

如果源码里用的是Vue 3 + Element Plus,el-form的rules校验规则要利用好trigger字段,区分blur(失焦校验)和change(值变化校验)。比如员工工号唯一的校验,应该用加debounce防抖的异步校验,用户输入完再发请求,过程中加loading提示。

5.3 人员报表与数据导出的两种方案

人事系统的报表模块,我归纳下来就两种实现路径:

  • 图形化报表:用ECharts绘制组织架构图、入离职趋势、学历分布饼图
  • 表格化导出:前端调后端导出接口,后端生成Excel或PDF文件返回给前端下载

这里重点聊导出。Excel导出一般用EasyExcel,配合自定义样式注解,导出的文件可以直接用Excel打开,不会乱码。PDF导出在热词里出现频率很高,前后端分离下实现步骤通常是:后端准备数据、填充PDF模板、返回文件流、前端接收blob后触发下载。

// 前端接收文件流的通用处理 download(res) { const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) const url = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.setAttribute('download', `员工花名册.xlsx`) document.body.appendChild(link) link.click() document.body.removeChild(link) window.URL.revokeObjectURL(url) }

注意axios请求必须设置responseType: 'blob',否则下载下来的文件打不开。这个坑我遇到不下十次。

5.4 统一请求封装

前端axios封装是必看的。一套规范的封装至少包含四件事:请求拦截器注入Token、响应拦截器解包统一数据、错误码统一提示、401时自动跳登录页。源码里如果这套封装做得好,你改造任何模块都会非常顺手。

service.interceptors.response.use( (response) => { const res = response.data if (res.code !== 0) { ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message || '请求失败')) } return res }, (error) => { if (error.response?.status === 401) { useUserStore().logout() router.push('/login') } else { ElMessage.error(error.message || '网络异常') } return Promise.reject(error) } )

6. 源码跑通到上线部署:我实测中踩过的坑和优化建议

这一步可能是标题党们最容易忽略的部分。"基于SpringBoot+Vue开发的前后端分离人事管理系统源码"跑起来容易,部署上线才是真正的考验。我把自己实测中遇到的高频问题整理成清单,每一条后面都跟着解决办法。

6.1 跨域问题的三种解决方式

前后端分离后,前端跑在8080端口,后端跑在8081端口,请求跨域,浏览器直接拦截。解决办法常用的有三种:

方式适用场景优点缺点
后端CORS配置开发调试配置简单生产环境暴露端口
前端proxy代理开发环境最常用,无跨域生产环境不生效
Nginx反向代理生产环境最规范、性能好需要额外配置

开发环境下,Vite或者Vue CLI的proxy配置是首选:

// vite.config.js server: { proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } }

生产环境用Nginx,配置方式是前端静态文件由Nginx托管,/api开头的请求反向代理到后端服务。注意加上proxy_set_header HostX-Real-IP,否则后端打日志拿不到真实客户IP。

6.2 后端打包与部署的完整步骤

后端的部署实际上不复杂,但版本环境和配置文件坑多。假设服务器是Linux CentOS 7,操作流程如下:

  1. 服务器安装JDK 8或JDK 17,取决于源码的spring-boot版本
  2. 安装MySQL,导入源码里的sql初始化脚本
  3. 修改MySQL账号密码为安全值,把数据库连接信息放入SpringBoot的application.yml
  4. 代码里用Maven打包:mvn clean package -DskipTests
  5. 把target目录下的jar包上传到服务器
  6. 启动命令:nohup java -jar hrms.jar --spring.profiles.active=prod > /opt/hrms/logs/hrms.out 2>&1 &

这里最容易被忽略的坑是数据库大小写敏感问题。Linux服务器上MySQL默认表名大小写敏感,Windows上不敏感。源码里的表名如果是小写下划线风格,导入Linux的MySQL没问题;但字段名如果用了驼峰,就得注意MyBatis-Plus的自动驼峰映射是否正常。配置里加上map-underscore-to-camel-case: true可以省掉一堆麻烦。

6.3 前端打包的清单

前端打包相对简单,但也要注意几个点:

  • .env.production文件里的VITE_API_BASE_URL要配置成生产环境的API域名,一般写/api由Nginx转发,或者写完整域名
  • 路由模式如果是history模式,Nginx需要配置try_files兜底,否则刷新页面就404
  • 打包产物dist目录上传到Nginx的html目录下

Nginx里history模式的核心配置:

location / { root /opt/hrms/dist; index index.html; try_files $uri $uri/ /index.html; }

6.4 Long类型精度丢失,前端ID变成科学计数法

这个坑我只说一次,但必须说:后端主键如果用了雪花算法,生成的ID是19位Long类型,前端JavaScript的Number类型只能精确表达2^53以内的整数,超过后精度丢失,表现为ID最后几位变成了0。查详情报404,问题就在这里。

解决方案是在后端统一对Long类型序列化时转成字符串:

spring: jackson: generator: write_numbers_as_strings: true

或者使用注解在特定字段上转:

@JsonSerialize(using = ToStringSerializer.class) private Long id;

6.5 项目上线前的安全检查

安全这块说几条最基础的,都是我在实际项目中遇到的:

  • 所有用户密码必须BCrypt加密,不能明文存储
  • 敏感接口,比如获取员工身份证信息,要做数据权限校验,不能只看登录没登录
  • 文件上传接口要限制文件类型和大小,防止恶意文件上传
  • 定时任务要防止重复执行,多实例部署时用分布式锁
  • 前端和后端日志里不要打印完整身份证号和银行卡号,做脱敏处理

热词里看到"springboot解决pdf xss攻击"这类搜索,本质就是用户上传的文件或者导出的PDF里包含了恶意脚本,处理方式是导出前对内容做转义,上传时校验MIME类型和内容嗅探。这套人事源码如果集成了文件上传功能,建议把这一块补上。

6.6 二次开发优先级到底怎么排

最后给正在拿这套源码做二次开发的朋友一个建议顺序:

  1. 先改数据库,加自己业务需要的字段,同步改实体类和前端表单
  2. 再改权限,把角色菜单配置理顺,让每个角色只看到该看的东西
  3. 然后改流程,把入职、离职、调岗这些审批链路跟自己的组织架构对齐
  4. 最后做报表,把统计口径确认好再写查询SQL

别上来就改前端样式。前端样式再好看,后台逻辑一堆问题,上线以后天天被业务部门轰炸。

我在实际使用中发现,很多人事系统的源码版本差异很大,有的用SpringBoot 2.7加Vue 2,有的用SpringBoot 3加Vue 3。选型时优先选SpringBoot 3之前用JDK 8的版本,生产环境跑起来兼容性最好。如果非要上SpringBoot 3,那JDK 17是底线,而且很多老一点的MyBatis-Plus版本不兼容,注意查一下依赖版本。

另外再分享一个小技巧:拿到源码后先不要急着运行,把接口文档Swagger打开浏览一遍,把所有接口的路径、参数过一遍,你在脑子里画出了系统的整体地图,后面改代码心里就有底了。如果源码里没有集成Swagger,花半小时补上,这个时间花得绝对值。

本文还有配套的精品资源,点击获取

http://www.cnnetsun.cn/news/4350372.html

相关文章:

  • Next AI Draw.io 部署指南:10 分钟跑通 AI 画图
  • Next AI Draw.io:一句话画出 draw.io 图表,从 Docker 部署到模型选型的完整上手指南
  • 用RTX 4090打造AI魔镜:本地大模型与多模态视觉实战
  • Nessus安装与使用教程
  • OpenVoice语音克隆实操指南:3分钟搭好环境,5秒语音样本完成克隆
  • 写论文英文AI率太高,怎么降低?先校对时态,再重组固定句式。
  • 如何实现天猫多店防关联管理自动化?无人值守订单处理,日发5000单零差错
  • 麻将实战总打错?从牌效率到防守,拆解“一看就会,一打就费”的真相
  • AI歌声生成全流程:从本地部署到未修音干声处理
  • Halo邮箱验证:注册即发验证码,把假邮箱挡在门外
  • IP地址与二进制转换全解析:从手算方法到Python实现
  • 为什么DNSHE免费DNS解析这么快?Anycast DNS技术原理深度剖析
  • 从零搭建RAG知识库问答系统:原理、代码与工程落地
  • Frigate 完整安装教程:30 分钟部署本地监控 AI NVR 与实时对象检测
  • 用Jetpack Compose从零实现安卓计时器:状态驱动UI与协程实战
  • AI搜索,你的企业还在“隐形”?北京GEO优化公司推荐,这三家助你提升可见度
  • CCR 配置备份与恢复的完整实战笔记
  • 如何为pm-skills贡献一个新技能?完整开发流程与验证脚本使用指南
  • 手术场景视觉-轨迹联合预测模型:从原理到工程部署
  • iFixAi新手完整教程:从干净机器到可引用审计报告只需4步
  • 基于Java+SpringBoot的船舶物料供应商交易平台的设计与实现(毕业设计项目源码+文档)
  • FreeRTOS 中优先级反转的解决方案-互斥量
  • Monorepo中管理多个DESIGN.md:多设计系统并行的完整指南
  • AI视频转场不靠运气:用Skill固化创作流程
  • 轮腿机器人离板面加速:5cm技术鸿沟的动力学原理与仿真实现
  • Abaqus热力耦合断裂仿真:UMAT/VUMAT子程序开发与工程实践
  • 如何用Feynman的rank命令给论文排优先级:PaperRank基于引用与复现证据的科学评分完整指南
  • Humanizer-zh 去 AI 痕迹实战 4 场景:营销文案、学术摘要、博客文章改写前后完整对比
  • 区块链智能合约详解:从原理到可运行Solidity源码实战
  • Hallmark Hero 标题长度与字号钳制关系:4 档自动降档规则完整指南