1. 项目概述与需求拆解去年年中接了一个企业内部人力资源系统简称HR系统的开发需求技术栈被业务方明确锁死为后端Spring Boot、前端Vue。表面看这是一个很常见的CRUD管理类项目但真正做起来才发现工资计算、考勤规则、组织架构调整、权限审计这些业务点每一个都能踩出不少坑。这篇文章就围绕这套“Spring Boot Vue”的完整落地过程梳理一下从需求拆分到部署上线的核心思路、关键代码和实战中遇到的问题希望能给正在做类似管理系统或者打算拿HR项目练手的朋友一点参考。先说下这套系统到底干什么。企业内部HR系统听起来只是“员工信息增删改查”其实拆开看至少包含六大模块组织架构管理、员工档案、考勤排班、请假审批、薪资核算、招聘简历管理。除了这些主线功能还要有角色权限、操作日志、Excel导入导出、消息提醒这类公共能力。如果公司还有多部门、多子公司数据权限又会变成另一个复杂度来源。因此不要一上来就写页面先花一周时间把业务边界理清楚比急着编码重要得多。本文适合哪些人看如果你已经会用Spring Boot写REST API会Vue组件化开发但还没有完整做过一个前后端分离的中后台项目这篇文章可以帮助你把零散的知识串起来。如果你正在准备简历上的项目也可以参考这套系统的模块拆法和权限设计至少能在面试时把“RBAC权限模型”“JWT认证”“动态路由”这些点讲出实际落地细节。当然如果你是零基础建议先补完Java基础、Spring Boot入门和Vue基础语法再来读实操部分。为什么选Spring Boot Vue这套组合从我实际开发经验看Spring Boot的生态太成熟了无论是MyBatis-Plus还是JPA社区资料扎堆遇上问题基本都能查到前端Vue的组件化、响应式、自定义指令在管理后台这种表单表格密集型场景里非常顺手。更重要的是招人会比较容易这套技术栈的候选人基数大后续维护才不至于一个人扛所有事。2. 技术选型与整体架构设计2.1 技术栈版本搭配与选择理由前后端分离已经是中后台项目的默认形态。后端我用了Spring Boot 2.7.18这个版本兼容性好相关依赖也都稳定没有急着上Spring Boot 3。原因很简单很多企业级项目里旧代码、老依赖迁移成本高2.7依然是可以安全选择的生产版本。MyBatis-Plus 3.5.x做数据访问少写了大量单表CRUD代码分页插件也很顺手。数据库用MySQL 8.0缓存用Redis认证授权用Spring Security JWT导入导出用EasyExcel工作流这版没有引入Activiti而是用状态机自己实现了请假审批毕竟业务场景比较简单没必要把流程引擎这种重武器塞进来。前端选了Vue 3 Vite Pinia Element Plus。Vue 3的组合式API在中后台场景下代码组织特别清晰Pinia替代Vuex后状态管理简洁很多Vite的冷启动速度比Webpack快了好几个量级。生产构建时Vite会做依赖预打包前端部署包体积控制得也不错。Element Plus的表格、表单、树形控件、上传组件基本覆盖了HR系统的全部界面需求不需要自己造轮子。这里还要明确一下前后端分离的交互约定。后端只提供JSON数据接口不做页面跳转前端用Axios统一发送请求携带JWT令牌。跨域问题在开发环境用Vite代理解决生产环境用Nginx反向代理处理后面部署章节会细说。2.2 系统模块划分与架构分层HR系统的物理架构并不复杂浏览器 - Nginx - Vue静态资源 后端接口 - MySQL / Redis。但逻辑上一定要保持清晰我按业务模块把后端拆成了几个独立的包employee、organization、attendance、salary、recruit、system。每个包内部都遵循Controller - Service - Mapper三层结构实体类、VO、DTO、枚举、异常也做了区分。这样分层的最大好处是当考勤模块出现问题时不会波及员工模块当薪资计算逻辑调整时只需要改salary包。代码维护边界清晰以后多人协同开发才不容易互相踩脚。前端也按模块划分了目录views/employee放员工档案页面views/attendance放考勤页面views/salary放薪资页面对应的路由和API文件一一对应。架构层面还有一个容易被忽略但极其重要的点统一返回结果。我用一个ResultT对象包含code、message、data三个字段。所有接口都返回这个对象前端拦截器统一判断code是否为200如果不是就弹出错误提示。这样做之后业务异常和系统异常都走统一出口排错时能快速定位是前端的锅还是后端的锅强烈建议以后再做项目时保持这个习惯。2.3 RBAC权限模型与数据库设计权限设计是所有内部系统的灵魂。HR系统里不同角色看到的菜单和数据范围必须严格控制。比如普通HR只能维护本部门员工档案部门主管只能审批自己部门的请假系统管理员才能配置角色和菜单。这个模型就是RBAC用户关联角色角色关联权限菜单权限 操作权限。数据库表结构相对固定用户表、角色表、用户角色关联表、权限表、角色权限关联表。权限表里我用type区分目录和按钮例如“员工管理”菜单和“员工导入”按钮是两条权限记录。员工管理核心表的设计我放在下面这张表里只列关键字段避免一开始就陷入冗余表名关键字段说明employeeid, name, id_card, phone, dept_id, position_id, hire_date, status员工档案表身份证加密存储deptid, parent_id, name, leader_id, sort部门表树形结构用parent_id关联positionid, name, dept_id, level职位表可配置多个attendance_recordid, employee_id, work_date, check_in_time, check_out_time, status考勤记录表每个员工每天一条记录salary_detailid, employee_id, salary_month, base_salary, bonus, deduction, net_salary薪资明细表recharge请假id, employee_id, start_time, end_time, type, days, status请假申请表数据库设计时有三条经验第一员工表的主键不要用自增ID而是用员工编号比如“EMP0001”这样在外部对接、Excel导入时不会乱序。第二所有涉及金额的字段都用decimal不要用double和float否则薪资计算会出现精度问题。第三逻辑删除字段deleted统一加上一是避免误删数据二是保留审计痕迹。3. 后端Spring Boot核心模块实现3.1 项目初始化与公共代码封装后端项目我直接通过Spring Initializr生成依赖选择web、security、mysql-connector-java、redis、lombok、validation。然后手动加入MyBatis-Plus和EasyExcel依赖。如果你的项目涉及操作日志再引入Spring AOP即可。这里我贴一下pom.xml中的关键依赖片段dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.2.0/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency要注意的是jjwt的0.9.1版本依赖javax.xml.bindJDK8不需要额外处理但如果用JDK17需要手动引入jaxb-api否则启动时会报ClassNotFoundException。另外JDK版本建议统一用8或11生产环境兼容性最高。公共代码封装是后端开发的第一步。我统一封装了返回结果对象ResultT、全局异常处理器、分页请求参数PageQuery、当前登录用户工具类SecurityUtils。全局异常处理尤其重要如果没有它业务代码里到处try-catch代码会显得很乱。我的做法是自定义一个BizException业务出错时直接throw new BizException(员工编号不能为空)然后RestControllerAdvice里统一捕获并转成Result返回。前端拿到code为500时弹窗展示后端给的message体验和开发效率都会大幅提升。3.2 JWT登录认证与Spring Security配置登录认证是权限系统的地基。流程是这样的用户提交账号密码后端校验通过后生成一个JWT令牌返回给前端前端保存到localStorage每次请求在Authorization头带上Bearer token后端过滤器解析token并校验用户身份。token不存session服务端天然无状态水平扩展时不需要考虑session同步。Spring Security的配置有好几种写法我用的是自定义过滤器链。核心配置代码如下http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/api/auth/login, /api/auth/captcha).permitAll() .antMatchers(/api/**).authenticated() .anyRequest().permitAll(); http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);JWT工具类里我用SecretKey签发生成token有效期设置为8小时并加入了用户ID、用户名和角色编码。解析token时如果过期或非法直接返回401。这里有一个非常容易踩的坑token中不要放入过多用户信息否则令牌会很长每次请求都会占用额外带宽。只要放用户ID和角色编码就够了其他信息需要时再查数据库。关于密码加密必须使用BCryptPasswordEncoder不要自己写MD5加盐逻辑。Spring Security自带的BCryptPasswordEncoder已经经过了大量安全验证我直接拿它做密码hash登录时调用matches方法比对。实际操作中发现如果历史数据里存过其他算法加密的密码登录比对方式需要做兼容适配比如老用户第一次登录强制走一次加解密转换流程。3.3 员工管理接口开发实战员工管理是HR系统里最基础的模块但实现起来也有不少门道。接口设计上我提供了分页查询、新增、修改、详情、删除、导入、导出、批量调整部门等接口。这里重点讲讲分页查询和Excel导入。分页查询是管理后台使用最频繁的接口。MyBatis-Plus自带分页插件配置好之后只需在Controller接收pageNum和pageSizeService层用lambdaQuery包装条件例如Override public PageEmployeeVO pageEmployees(EmployeeQuery query) { PageEmployee page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperEmployee wrapper Wrappers.lambdaQuery(); wrapper.like(StringUtils.isNotBlank(query.getName()), Employee::getName, query.getName()) .eq(query.getDeptId() ! null, Employee::getDeptId, query.getDeptId()) .eq(query.getStatus() ! null, Employee::getStatus, query.getStatus()) .orderByDesc(Employee::getCreateTime); PageEmployee result employeeMapper.selectPage(page, wrapper); // 转VO并填充部门名称、职位名称 return convertToVO(result); }这里要特别提醒如果查询条件里有多个字段一定要注意条件构造器的and/or逻辑否则会出现“部门条件明明选了却不过滤”的怪问题。MyBatis-Plus的eq(condition, column, value)只有在condition为true时才拼接SQL这个特性用好了代码会非常干净。Excel导入这块我用EasyExcel监听器解析上传文件逐行校验身份证格式、手机号格式、所属部门是否存在。如果某一行数据有问题不能直接中断全部导入而是要把错误信息收集起来在导入结果中返回给用户具体行号和失败原因。生产实践中我还为导入过程加了事务控制全部校验通过后才统一保存避免出现半成功状态。导出则相对简单查询出数据后用EasyExcel.write()写出注意导出列顺序要和模板对齐日期格式尽量用字符串免得Excel打开后显示成数字。3.4 考勤与薪资模块的关键实现考勤模块是整个HR系统里最容易出业务分歧的地方。最常见的一种模式是员工每天打两次卡上班打卡和下班打卡后端根据考勤规则计算是否迟到早退。数据表上用attendance_record记录每天的打卡时间和状态。我在实现时用了Redis来缓存当天打卡记录员工打卡时先写Redis然后异步批量落库。这样做的原因是为了防止打卡高峰期的数据库压力。如果公司人数不多直接落库也没问题。迟到判断的逻辑要考虑公司规则。我使用策略模式每种考勤规则抽取一个AttendanceStrategy接口比如“固定班制”和“弹性班制”各有一个实现类。固定班制判断打卡时间是否晚于规定上班时间弹性班制则允许晚到但需要补足工时通过计算当日工作总时长判断。这套设计看起来多写了一些类但在规则调整时能避免大面积改动已有代码值得一试。薪资模块更敏感我设计的基础逻辑是每月根据员工基础工资、岗位工资、绩效奖金、社保扣款、个税等字段组合成薪资明细。用salary_detail表存储每个员工每个月的完整薪资快照而不是实时计算因为历史薪资不能因为后来规则改变而变动。批量计算薪资时我用CompletableFuture并发处理提升计算速度但要注意并发写MySQL时的事务隔离级别。计算完以后还可以一键导出工资条Excel按模板生成后通过前端下载。4. 前端Vue3实现要点4.1 前端工程搭建与Axios请求封装前端使用npm create vitelatest初始化Vue3工程模板选择vue-ts因为TypeScript在管理后台项目里真的太有用了接口定义一清晰字段拼错的情况直接少一半。然后安装element-plus、pinia、vue-router、axios。Element Plus按需导入会麻烦一些我图省事直接全量引入虽然首屏包体积大点但开发效率高后续优化空间也够。Axios请求封装是前端项目的基础。我在src/utils/request.ts里创建了axios实例设置baseURL、超时时间、请求拦截器带上token和响应拦截器统一处理code非200的情况和HTTP 401跳转登录页。拦截器里有一个容易被忽略的细节后端返回的下载文件流的接口通常返回的是二进制Blob不是JSON如果统一响应拦截器强行走result.code判断会发现拿不到data。所以对下载接口要单独设置responseType: blob并在拦截器里判断响应头类型再决定是否解析JSON。工作当中我还会给Axios错误信息做一层人性化提示。比如网络超时提示“网络开小差了”后端业务异常直接弹出后端返回的message。如果用户token快过期可以提前在响应里识别状态码弹出“登录已过期请重新登录”后清掉本地状态并跳转登录页。这个流程不处理好用户经常会在不恰当的时机丢失正在填写的数据。4.2 动态路由与权限菜单的实现动态路由是权限控制在前端的体现。后端登录后返回两个重要东西token和当前用户的权限码列表或者菜单树。前端根据权限码动态生成可访问的路由。具体做法前端路由表拆成两部分一部分是constantRoutes登录页、404、首页等所有人可访问另一部分是asyncRoutes所有需要权限的页面路由。用户登录后调用后端接口获取菜单列表前端遍历菜单数据动态添加对应路由到Vue Router中。菜单和数据源的关系我建议后端直接返回树形结构的菜单数据包括菜单名称、路由路径、图标、子菜单、按钮权限码。前端用递归组件渲染侧边栏。按钮权限则用自定义指令v-permission控制比如只有拥有“employee:add”这个权限码的人才显示“新增员工”按钮。注意事项动态路由添加后如果用户退出登录必须重置Router实例否则再次登录另一个账号时旧路由还残留在路由表里可能出现越权访问页面片段的问题。我用一个resetRouter()方法配合location.reload()做判断保证每次登录都是干净的上下文。还有一个血泪教训异步路由添加时机必须在路由守卫里完成并且要配合next({ ...to, replace: true })重新进入一次否则页面刷新后会出现白屏。4.3 Element Plus表格表单开发技巧HR系统90%的页面都是表格 表单 弹窗的组合。Element Plus的el-table非常强大但在开发时要特别注意第一表格列宽要给出min-width否则数据太多时会把操作列挤没第二表格数据更新后要重新调用查询接口或者手动更新table数据不要只改弹窗里的表单数据第三多选列要留一列typeselection并在分页切换时清空选中的行否则不满足业务预期。员工管理页面是最典型的具体例子。查询区放姓名、部门、录用状态使用el-form的inline属性表格区展示员工编号、姓名、部门、职位、手机、入职日期、状态操作区放编辑、离职、调整部门等按钮。新增/编辑弹窗用el-dialog内部放一个el-form提交前用rules做必填校验。如果字段很多可以分步骤展示用el-steps把基本信息、工作信息、合同信息分成三步。我这里还有一个强烈建议前端所有列表页的分页控件不要每个页面都复制粘贴一套分页代码而是封装一个ProTable组件。把查询参数、分页参数、表格列配置、请求方法都收敛到组件内部使用方只需要传一个配置项。团队里后端都还忙的时候前端先做组件封装后面接接口就能很快。5. 核心业务场景难点与解决实录5.1 考勤排班数据如何高效处理考勤模块最开始我设计得比较简单每个部门绑定一个班次员工每天按部门班次执行。结果上线后发现公司允许员工自己调休不同员工当天可能对应不同班次如果部门班次一改历史考勤记录就会被影响。后来把班次设计成attendance_class表新增employee_schedule表存员工某一天的班次ID排班后生成未来一个月的考勤计划记录。这样员工请假或调休时只需要更新对应日期的排班记录考勤计算会根据排班表重新生成结果。批量排班的接口是前端选择部门、日期范围、班次后端循环员工生成排班记录。这里要注意性能如果有1000个员工排一个月班直接循环插入1万条数据会有点慢。我用MyBatis-Plus的saveBatch一次性批量插入并做了表索引优化在employee_id和work_date上建立了联合唯一索引避免重复排班。考勤计算时最麻烦的边界情况包括请假半天叠加迟到、外勤打卡无法定位、凌晨下班打卡日期归属等问题。我的经验是不要试图把逻辑全部写在一条SQL里而是先按员工ID分组按天读取原始打卡数据然后用Java代码统一计算。这样虽然多几步但逻辑可读性、可维护性都更好。5.2 薪资核算模块的灵活扩展薪资核算模块最初只支持固定薪资后来客户要求增加计件工资、提成工资、绩效系数等场景。如果每个场景写一套if-else代码会越来越臃肿。我引入了策略模式SalaryCalculator接口负责计算实发工资每个计算策略实现一个calculate(EmployeeSalaryContext context)方法。例如固定薪资策略读取基本工资直接得出应发金额计件策略读取本月产量和单价算出应发工资。同时用工厂模式根据员工薪资类型返回具体策略类。计算过程中还会涉及社保公积金、个税、专项附加扣除等参数。我将这些参数维护在数据库配置表里而不是写死在代码这样每年政策调整时只需要在管理后台修改参数即可。计算完成后生成salary_detail记录并保留计算快照避免后续岗位或社保调整导致历史数据变化。薪资模块最容易出错的是金额精度。我的处理方式是所有金额用BigDecimal除法指定保留两位小数和舍入模式。Excel导出时也要统一格式用DecimalFormat格式化数字防止精度的坑。5.3 多条件组合查询与数据权限过滤HR系统列表页基本都需要组合查询姓名、部门、入职时间范围、状态、学历等。如果用MyBatis-Plus拼接条件倒是简单但数据权限就要额外小心。普通HR不能查全公司所有员工只能看他管辖部门及其子部门的员工。这个需求我在Service层统一处理从SecurityUtils拿到当前用户的部门ID然后查出该部门的所有子部门ID集合拼成一个IN条件放到查询wrapper里。实现时要注意如果部门层级很深递归查询子部门会导致SQL里IN条件非常长。我的优化方案是先查出整棵部门树在内存中通过递归计算出当前部门的所有子孙节点再一次性放入treeDeptIds集合。这个量级通常不大性能可接受。如果是上百个部门的集团型企业就需要引入部门权限表来维护用户的可见范围。除了数据可见范围还要防止越权操作。例如普通HR不能修改其他部门员工的信息。我在后端修改接口的Service层里同样先校验当前用户是否对该员工有操作权限如果没有权限直接抛业务异常不依赖前端按钮隐藏来保证安全。安全永远得以后端为主前端隐藏只是交互简化。6. 常见问题与性能优化实践6.1 开发中遇到的典型问题与排查开发这套系统时我统计了一下踩得最多的坑基本集中在这几个方面问题现象根本原因解决方式前端请求接口报跨域错误开发环境后端端口和前端端口不一致Vite配置proxy代理将/api转发到后端服务登录成功后刷新页面白屏动态路由没有在刷新时重新加载在路由守卫中判断路由是否已注册未注册则重新加载Excel导入时中文乱码文件名编码或文件流编码处理不当使用MultipartFile.getOriginalFilename()解析文件名时统一UTF-8解码JWT解析报JWT signature does not match签名密钥不一致或算法设置错误前后端确认SecretKey使用相同的签名算法密钥长度不少于32字符MyBatis-Plus分页查询不过滤条件Wrapper条件使用错误比如or导致条件漂移检查SQL日志必要时用括号明确and优先上传文件提示超出大小限制Spring Boot默认单次请求上传大小只有1MB在application.yml中调整spring.servlet.multipart.max-file-size和max-request-size每一个问题在刚遇到时都挺闹心但解决以后复盘会发现多是配置或边界逻辑的疏忽。这里特别说一下排查前后端问题最好把浏览器Network和Console同时打开大部分问题在Network里的状态码和响应体中就有提示。后端问题就去看日志尤其是全局异常处理器打印的堆栈信息能定位到具体类和行号效率非常高。6.2 数据库与接口性能优化经验这套系统上线后随着员工数据量增长一些接口响应时长开始变慢。我逐步做了几轮优化效果比较明显。数据库层面的优化主要是索引。分页查询的where条件字段如dept_id、status、hire_date都加了二级索引员工表的employee_no和id_card建立唯一索引既保证唯一性又加快查询考勤记录表按employee_id work_date建立联合索引这个索引让打卡记录查询速度翻了好几倍。缓存层面我把部门树、字典数据、职位列表这类变动少、读取频繁的数据放进了Redis。员工详情页第一次查询完后缓存一份显示用数据员工信息变更时主动删除缓存保证数据一致性。这里有缓存穿透和击穿风险我用了空值缓存和逻辑过期避免大量请求直接打到数据库。接口层面前端表格页的搜索按钮统一做防抖避免用户点击太快触发多次查询后端接口的查询结果转VO时避免在循环中查询数据库。比如查询员工列表后展示部门名称我是一次性查出相关部门再在内存中映射绝对不会在循环里查一次库。这条规则我已经在很多项目里验证过了表现效果立竿见影。7. 部署上线与运维实践7.1 前后端Docker化部署系统开发完成后进入部署阶段。我使用Docker来打包前后端前后端各构建一个镜像。后端Dockerfile基于openjdk:8-jre-alpine将项目打成Jar包后运行前端则是基于node镜像构建静态资源再把产物复制到nginx:alpine镜像中由Nginx托管静态文件并反向代理后端接口。Nginx配置有几个关键点一是将/api路径代理到后端容器二是配置client_max_body_size方便文件上传三是开启Gzip压缩静态资源四是SPA路由history模式时需要try_files配置否则前端路由在刷新时会出现404。配置片段如下server { listen 80; server_name localhost; client_max_body_size 10M; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend-service:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里尤其要注意proxy_pass末尾的/api/是否携带斜杠。如果后端接口路径本身包含/api后端服务和Nginx的路径拼接很容易配错接口请求到后端就是404。我在第一次部署时就因为多带了一个/api排查了半天。7.2 上线后的监控与备份策略HR系统上线后我第一时间加了基础监控。后端使用Spring Boot Actuator暴露健康检查和指标端点前端则通过定时上报接口监控页面访问情况。数据库方面每天凌晨用脚本定时备份MySQL数据至少保留最近七天的备份文件。考勤数据和薪资数据是核心资产备份这事千万不能省。除了监控系统本身日常运维还要关注定时任务的执行情况。比如考勤数据汇总、薪资月结等定时任务需要在日志里记录任务执行结果。我开发了一个简单任务中心模块把定时任务统一管理任务执行失败后发送通知给管理员。这样即使半夜发生异常也不会到第二天上班时才发现问题。还有一点系统要预留版本回滚方案。前端镜像和前端静态资源我在发布时都保留了上一版本的镜像标签后端Jar包也保留了上一版本。一旦发布后发现严重问题可以快速一键回滚到上一个版本。这个操作在关键时刻能救命绝不只是在文档里做做样子。8. 一些项目复盘后的心里话整套系统从需求确认到上线大概花了三个多月后面又迭代了一个多月。现在回头看技术上踩过的坑基本都能在本文里找到对应点但更值得记录的是项目过程中的几个体会。第一不要迷信“类越多越好”也不要觉得“能用就行”。设计模式要结合业务场景比如考勤、薪资确实适合策略模式但员工查询这种简单逻辑就老老实实写一个Service方法没必要为了架构而架构。第二前后端接口约定一定要提前统一。字段命名习惯、日期格式、分页参数、错误码定义这些如果不在一开始定清楚后面联调时到处都在“对字段”效率会特别低。我在项目中直接用了开源的API规范范式把公共参数和响应结构写成文档前后端保持同步。第三数据权限和操作日志这一类“非功能需求”必须提前规划。很多系统最开始不做操作日志后面要补就非常痛苦。我在员工信息修改、薪资调整、角色配置这些接口上加了AOP日志记录操作人、操作时间、变更内容。操作日志表设计得很简单就五六个字段但审计时的价值非常大。第四如果条件允许尽量在项目初期就引入自动化部署和基础监控。人工部署一次两次还好天天人工部署迟早会有一天因为漏了一个配置文件而出问题。把这部分自动化之后开发体验会好非常多。这套系统后续扩展空间也很大比如绩效模块、考勤设备和钉钉/企业微信的集成以及数据分析报表。但地基已经打得比较稳后面每加一个模块基本不需要改动原有代码结构。如果让我重新做一遍我可能会在前端工程化和数据库设计层面更早做长期规划避免早期临时设计带来的返工成本。开发这类管理系统工具和技术只是基本面真正拉开差距的是对业务的理解和细节的耐心。希望这篇总结能给你带来一些参考少走一些我走过的弯路。