1. 项目概述与核心背景
做一个SpringBoot的工资信息管理系统作为毕业设计,是目前计算机专业学生里非常主流的选择。每年到毕设季,这东西的搜索量就会猛涨,为什么?因为它的业务边界足够清晰,不会像电商系统那样牵扯订单、库存、支付等一堆复杂的联动逻辑;同时又覆盖了绝大多数SpringBoot项目都会用到的核心技术点——用户认证、权限管理、CRUD、文件导入导出、复杂查询、报表统计等等。这个组合决定了它天然是个适合拿来展示技术功底的题材。
这套系统解决的核心问题,是传统Excel做工资管理的痛点:数据分散在多个工作表里难汇总、没有权限控制随便一个人都能改、发薪记录没有审计留痕、年度做统计时靠人工筛选极其容易出错。系统化之后,管理员的日常操作变成了录入或导入基础数据、核对个税社保、确认发放、留存记录,全流程都在一个系统里闭环,每一条数据变动都有迹可循。
这篇文章适合的读者是:正在做毕设选题还没有头绪的、已经选了类似题目不知道怎么下手设计数据库的、以及项目写完了准备写论文和准备答辩的同学。我会按实际开发顺序来拆解这个系统——从需求分析到数据库设计、从核心技术选型到关键代码实现,最后把毕设项目里最容易踩的坑和答辩时最常被问的问题一并整理出来。
先说明一下,这里讲的方案是我个人在多次带毕设项目过程中总结出的比较稳妥的实践路线,不是唯一标准。有些细节比如字段命名、权限粒度,你可以按自己学校的要求调整,但整体架构和设计思路是通用的。
2. 需求分析与功能模块细化
2.1 核心角色与典型业务场景
工资管理系统的角色不像OA系统那么复杂,绝大多数情况下三种角色就够了:系统管理员、财务/人事专员、普通员工。但在做需求分析的时候,不能只写一句“有三种角色”,要具体到每种角色每天面对什么场景、需要用到哪些功能。
系统管理员负责的是系统层面的维护——员工账号的开通与禁用、角色分配、基础数据字典维护(比如部门、岗位这类项)。财务专员是日常使用频率最高的角色,每个月固定的几个动作:核对员工基础工资信息、录入或导入当月的考勤和绩效数据、根据社保公积金规则计算应发工资、确认个税扣除、生成工资条、处理特殊员工的调薪申请。普通员工的需求就简单很多——查看自己的历史工资单、下载工资条、提交某些可申诉的异议。
我见过不少同学在设计上把角色功能写得很含糊,比如“财务可以管理所有数据”,这种描述在论文查重时容易过,但在系统实现的时候很容易漏功能点,因为你自己都没想清楚具体要操作什么。更建议的方式是做一张角色-功能对照矩阵表,把三个角色分别在哪些菜单上有哪些操作权限列得清清楚楚,这样后续写代码也好,画用例图也好,都会顺手很多。
2.2 功能拆解到菜单级别
把业务需求翻译成系统菜单,可以拆成下面这样的粒度:
- 系统管理:用户管理、角色管理、菜单权限管理
- 基础信息:员工档案管理、部门管理、岗位管理、社保公积金方案管理
- 薪资业务:薪资结构模板配置、月度工资核算、工资审批流、工资条发布、历史工资查询
- 报表统计:月度工资汇总、部门薪酬分析、年度人力成本趋势
- 个人自助:我的工资单、个人考勤确认
这套菜单结构基本覆盖了毕设评审老师关心的大部分功能点,又不会像真正的商业化人力资源系统那样把边界铺得特别大。做毕设最忌讳的就是功能太发散,导致三个月下来每个模块都是半成品。把边界收窄,每个模块都做得完整、能用,效果反而好很多。
2.3 非功能需求不要忽略
很多人在写需求分析时只关注功能需求,忽略了非功能需求,但答辩时老师几乎必问“系统的安全性怎么考虑”或者“并发访问怎么办”。这里有几点要在设计阶段就想清楚的:
数据安全性方面——工资数据属于敏感数据,接口层面不能直接把全量数据返回给前端,要进行脱敏处理和分页控制;操作日志上,谁在什么时间修改了某位员工的工资数据,要能追溯。系统健壮性方面——工资计算时如果出现五险一金比例配置缺失或者员工基础数据不完整,要有异常提示而不是直接报500错误。可维护性方面——代码分层清晰、命名规范,配置项不能乱撒。
3. 核心技术栈选择与版本设计
3.1 为什么是SpringBoot,版本怎么定
这个项目名字里就带着SpringBoot,但在选版本时不少同学犯了难。网上教程里既有2.x的写法又有3.x的写法,到底用哪个?
我的建议很直接:除非你的课题有特定要求,否则选SpringBoot 2.7.x系列。理由有这么几点:第一,2.7.x是目前稳定性获广泛验证的版本,大多数毕设相关的开源项目、网上教程、遇到的坑,在社区里都有现成的解决方案;第二,毕业设计的部署环境大概率是学校机房或者自己电脑,JDK版本可能停留在1.8,SpringBoot 2.7.x对JDK 1.8支持得很完整,不用额外折腾;第三,很多第三方集成组件(比如代码生成器、工作流引擎)对SpringBoot 3.x的支持跟进得并不及时,容易踩到兼容性的坑。
SpringBoot 3.x做了Jakarta EE的包名迁移,JDK最低要求17,虽然性能确实有提升,但为了这两个收益在毕设阶段额外处理一堆兼容性问题,我个人认为不划算。除非你的创新点本身就是“基于新版SpringBoot 3 + JDK 17的某某系统”,那另说。
3.2 配套组件的选择逻辑
ORM框架选择MyBatis-Plus而不是纯MyBatis,原因是工资管理系统里有大量常规的单表CRUD、分页查询、条件构造。MyBatis-Plus可以把这些重复度极高的代码简化到极致,极大节约开发时间。它的LambdaQueryWrapper在查询条件下能避免魔法字段名,后期改动字段名时IDE能直接帮你同步检查,这个体验比手写字符串字段名好太多了。
数据库选MySQL 5.7或8.0都可以,新项目直接上8.0,字符集默认utf8mb4,避免中文乱码的麻烦。如果学校机房只装了5.7,用也完全没问题,注意分页语法和函数兼容性差异就行。
安全框架这块,毕设级别用Shiro或者Spring Security都行。但考虑到SpringBoot 2.7x与Spring Security结合天然顺滑,且Spring Security在简历上含金量更高一些,推荐直接用Spring Security + JWT做无状态认证。前端每次请求在Header里带上Token,后端通过过滤器链校验身份权限。
前端的话,如果你的毕设定位是前后端分离——现在大多数学校都鼓励这种架构——那Vue 2 + Element UI和Vue 3 + Element Plus都是候选。Vue 2虽然是老技术了,但网上现成的后台管理模板极多,改起来快;Vue 3 + Element Plus更贴近企业现状。时间紧想求稳,Vue 2是务实的选择;时间充裕想写进简历,Vue 3会更好看。
3.3 项目初始化时的目录结构约定
项目要按职责划分包结构,这里是我建议的方式:
com.example.salary ├── common // 通用类:统一返回结果、异常处理、常量定义 ├── config // 配置类:跨域、安全配置、MyBatis-Plus配置 ├── controller // 控制层:接收前端请求 ├── service // 业务逻辑层:接口 + 实现 ├── mapper // 数据访问层 ├── entity // 数据库实体类 ├── dto // 数据传输对象:接收前端参数 ├── vo // 视图对象:返回给前端的数据 └── utils // 工具类:JWT工具、Excel工具等Controller层要做到只做参数接收和结果封装,不写业务逻辑。有同学图省事把工资计算的代码直接写在Controller里,临时跑通没问题,但一旦要加个事务或者复用逻辑就会非常痛苦。Service层才是业务逻辑的核心舞台,事务注解也加在这一层。Mapper层的接口方法名建议见名知意,复杂的SQL写XML,简单的用注解或者MyBatis-Plus内置方法。
4. 数据库设计与核心表关系
4.1 核心表清单和字段设计
工资信息管理系统的数据库是整个项目的灵魂。我见过不少同学代码写得没问题,就是数据库表设计不合理,导致后续统计查询的时候SQL写得又臭又长。这里给出一套经过验证的完整表设计方案,你可以直接参考使用。
第一张表是系统用户表(sys_user),存储登录账号相关信息,字段包括用户ID、用户名、密码(BCrypt加密后的密文)、真实姓名、手机号、邮箱、部门ID、状态(0禁用1启用)、创建时间、更新时间。这里要区分一个概念:员工(employee)和用户(user)不是一回事。员工档案是人力维度,记录的是工号、入职日期、薪资标准这些;用户是系统登录维度,记录账号和凭证。两者通过员工的用户ID字段关联,但不是所有员工都会有系统登录账号——比如某些生产岗位的员工可能不需要登录系统。
第二张表是员工档案表(employee),编号、姓名、性别、出生日期、身份证号、部门ID、岗位ID、入职日期、离职日期、学历、银行卡号、联系电话、社保起缴月份、状态(在职/离职)、创建人、创建时间、更新时间。身份证号和银行卡号虽然属于敏感字段,但在工资系统里是实际要用的字段,银行存款需要银行卡号,个税起征判断可能需要身份证号中的信息,所以按实际情况保留,只是返回前端时要脱敏。
第三张表是部门表(sys_dept),部门ID、部门名称、父级部门ID、排序号、负责人。岗位表类似:岗位ID、岗位名称、岗位编码、所属部门ID、备注。
接下来是薪资维度的表。薪酬结构通常包含基础工资、岗位工资、绩效工资、交通补贴、通讯补贴、住房补贴、餐补等。建议维护一张薪资项定义表(salary_item),定义这个企业有多少种薪资项目,以及每个项目是固定发放还是按公式计算的。然后员工通过关联确认自己拥有哪些薪资项,像基础工资这种,每个人的金额可能都不一样,怎么处理呢。工资标准表(employee_salary_standard),字段有主键ID、员工ID、薪资项目ID、金额、生效日期、失效日期。这样每次调薪不用改历史数据,只需要把当前标准的失效日期填上,再插入一条新生效日期的新记录,天然留下了历史追溯能力。
社保公积金配置表(social_security_config)按城市和年份存缴费基数上下限、企业和个人的养老/医疗/失业/工伤/生育比例以及公积金比例。这张表对计算的准确性影响巨大,很多同学前期嫌麻烦不建这张表,把比例硬编码到代码里,这是个大隐患——一旦评审老师问“不同城市的社保方案不同,怎么扩展”,当场就很被动。
最后是月度工资表(monthly_salary),这是系统的核心结果表,一次计算一个员工一个月一条记录,字段包括主键、员工ID、工资年月、应发工资、社保个人部分缴费基数、公积金缴费基数、个人养老金额、个人医保金额、个人失业保险金额、个人公积金金额、社保汇总、公积金汇总、累计应纳税所得额、累计已预扣税额、本期应纳个税、实发工资、发薪状态(0未发放1已发放)、发薪时间、审核状态、备注。另外还需要一张工资明细表(monthly_salary_detail)存每个工资项的金额,这样汇总表和明细表一对多,既能看总数也能看构成。
4.2 个税累计预扣法的表结构支撑
新个税法是按年度累计预扣预缴计算的,不是简单看单月工资。计算逻辑是:累计预扣预缴应纳税所得额 = 累计收入 - 累计免税收入 - 累计减除费用(5000元/月) - 累计专项扣除(三险一金) - 累计专项附加扣除 - 累计依法确定的其他扣除。
这里设计上要支持这个逻辑,是为了将来新入职员工处理起来也能算对。需要一张申报表(taxpayer_monthly_detail),字段包括:员工ID、税款所属期(比如2025-02)、累计收入额、累计减除费用、累计专项扣除(社保+公积金)、累计应纳税所得额、累计应纳税额、累计已预缴税额、本期应补退税额。这张表按月追加记录,计算本期个税的时候,先取上期累计数据,加上本期新增数据,再算出本期预扣额。
4.3 工资核算主流程
一个月的典型操作流程是这样的:
- 先在社保公积金配置界面确认当月的缴费基数和比例没有问题。
- 如果有员工入离职,通过“异动处理”把本月的新进和离职人员标记好,新进员工从入职月份开始有记录,离职员工工资算到离职当月。
- 录入或者导入考勤扣款、绩效得分、奖金等变动数据。
- 点击“工资核算”,系统按员工当月生效的工资标准跑一遍,生成草稿状态的月度工资记录。
- 财务人员对计算结果进行复核,尤其关注个税计算是否正确、实发是否为负数这种异常数据。
- 确认无误后提交审批,审批通过后完成封账,生成工资条。
- 员工登录系统能看到自己当月的工资单明细,也可以下载PDF工资条。
5. 核心代码实现与方案细节
5.1 项目骨架搭建要注意的配置
在实际创建项目的时候,为了方便管理基础数据与简化前后端对接,我建议做几件事:
在启动类上加上@MapperScan注解,不要依赖在每个Mapper接口上写@Mapper注解,少很多重复操作:
@SpringBootApplication @MapperScan("com.example.salary.mapper") public class SalaryApplication { public static void main(String[] args) { SpringApplication.run(SalaryApplication.class, args); } }MyBatis-Plus的分页插件要显式配置,很多同学引了依赖却忘了注册分页插件,结果分页查询完全失效,全部数据一次返回,页面一多就卡。配置大概长这样:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }跨域问题在前后端分离项目里几乎一定会遇到。Spring Boot的CORS配置要注意allowedOriginPatterns支持通配符,别用allowedOrigins("*")——至少在携带凭证的时候这种方式是不行的。如果用了Spring Security,还要留意跨域配置跟安全过滤链之间的处理顺序。
JWT工具类封装好生成和解析方法,要注意Token过期时间的设计。毕设系统一般场景是短时间内演示和答辩,但代码不能写得太随意,建议设定2小时的有效期,同时配合前端拦截器在Token即将过期时自动刷新。虽然刷新机制在毕设里不是必选项,但写上了在答辩时是个加分细节。
5.2 工资核算核心逻辑的数据流
工资核算不是一个简单的循环加减,它的数据流是这样的:
首先,按工资月份和员工状态查出所有需要核算的员工列表。然后,对每个员工循环执行:
第一步,读取员工的工资标准表,找到生效日期早于或等于当前工资月、失效日期晚于或等于当前工资月的所有记录,把金额装配为Map结构方便取值。
第二步,查询考勤表和绩效表中该员工当月的扣款和奖励数据,组装成变动项列表。
第三步,调用社保计算服务,根据员工参保城市和社保配置表算出个人缴纳部分。
第四步,调个税计算服务。如果截至上月的累计表里没有该员工记录,说明是首月或者不连续月份,需要做特殊处理。
第五步,把应发工资减去个人社保、个人公积金、个税,再加上或者扣除各项变动,得到实发金额。
这里每个员工的这几步之间没有任何依赖,天然适合并发处理。如果你的毕设想展示一下并发编程能力,可以用CompletableFuture把每个员工的计算任务丢到线程池异步跑,但要注意线程池大小和事务边界——因为要保证要么整个月都算完,要么都回滚,所以通常是先异步算好结果,再同步批量落库,不要在子线程里单独提交事务。
5.3 一个容易被问住的经典问题:事务失效
工资核算方法上加了@Transactional注解,测试的时候发现异常了数据没回滚,这个场景在毕设答辩时太经典了。原因通常是下面几种:
第一种是方法自调用。A方法没加事务,在同一个类里调用加了事务的B方法,Spring通过代理调用B的时候事务才会生效,自调用是绕过代理直接执行内部方法,所以失效。解决方案是把B方法移到另一个Service类里,或者自己注入自己,不建议用AopContext.currentProxy()这种方式,会让代码可读性变差。
第二种是注解加在了非public方法上。Spring默认用CGLIB代理,但事务切面对方法可见性有要求,private方法就算加了注解也不生效。
第三种是异常被吞掉了。try-catch捕获了异常但没有往外抛,事务管理器根本感知不到异常发生,自然就没法回滚。正确写法是捕获异常后要抛RuntimeException或者手动调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。
5.4 分页查询的三种常见写法
列表页在工资管理系统中占的比重非常大,员工列表、工资记录列表、操作日志列表都是分页的。结合MyBatis-Plus,最常用的三种写法:
最基础的方式是用Page和LambdaQueryWrapper:
Page<Employee> page = new Page<>(current, size); LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(name), Employee::getName, name) .eq(employeeDTO.getDeptId() != null, Employee::getDeptId, employeeDTO.getDeptId()) .orderByDesc(Employee::getCreateTime); Page<Employee> result = employeeMapper.selectPage(page, wrapper);如果分页查询后需要对某些字段做字典转换,比如把部门ID翻译成部门名称,一种做法是查出结果后再批量查一次部门表字典,在内存里翻译。如果关联表太多,也可以直接写分页SQL用LEFT JOIN查出翻译好的结果,MyBatis-Plus都可以支持自定义SQL分页。
第三种是多表复杂统计的场景,比如按部门汇总人力成本,写XML里的自定义分页查询。在这种场景中有个经验可以分享:如果SQL里要按某个组内顺序取最新一条记录之类的高级需求,可以先用子查询做ROW_NUMBER或窗口函数,再在外层进行汇总。
5.5 敏感数据脱敏防尴尬
列表页展示员工列表时,身份证号和银行卡号不能完整显示。最方便的做法是使用Jackson的序列化注解,在字段上标注@JsonSerialize,自定义一个脱敏序列化器,展示中间四位打星号,详情页如果有权限再提供查看完整号的接口。这样做的好处是底层查询不用改动,只在输出边界做控制,数据不会以完整形态轻易出现在前端响应里。
6. Excel导入导出的重难点处理
6.1 模板设计决定导入成功率
工资核算前的大量基础数据,如果让财务一条条手工录,体验是非常糟糕的,所以Excel导入导出是这个系统的硬需求。我用的是EasyExcel,相比Apache POI,它的内存占用更低,API设计也简单很多。
导入的关键在于模板设计。设计Excel模板时,每一列的标题要跟代码里定义的字段头匹配,不要用“姓名(必填)”之类的花哨写法,直接用标准字段名:员工工号、姓名、部门、岗位、基础工资、岗位工资、绩效工资、交通补贴、住房补贴、餐补、养老保险基数、公积金基数、考勤扣款、其他扣款、备注。代码里用@ExcelProperty注解匹配表头字符串。
每次导入的时候给出合理的校验错误提示,最好是错误信息能精确到行号和列名。比如“第3行 基础工资格式错误”。批量导入最怕的就是用户导入了1000行数据,100行格式有问题,如果不定位出来根本没法处理。用EasyExcel的Listener回调里的invoke方法逐行校验,把错误信息收集到List里,最后统一返回给前端。
6.2 大数据量导出时的内存优化
工资发放之后可能会需要批量导出整个部门的工资明细给财务做记账。如果直接查到内存里再写,数据量大时JVM内存很容易打满。EasyExcel支持从数据库流式读取并直接写入,不要汇聚全量数据,写多少读多少。在查询时配合游标,每次从数据库取一批数据然后马上写出。
6.3 导入和导出在答辩时可以这样讲解
如果评审老师问“这个导入导出解决了什么实际问题”,一个较好的回答思路是:传统录入模式下,财务人员录入一个50人的部门可能就需要1小时;有了批量导入,从Excel整理到系统数据落库只需要几秒钟,而且系统能检测出原有模板中常见的录入错误。另一个有价值的方向是把校验逻辑做成可配置的,比如哪一列是必填的,哪个字段的取值范围是什么,哪个字段要查重,把规则用注解配置到字段上。
7. 权限管理:不是只能做RBAC
7.1 基于RBAC模型的设计
工资管理系统的权限架构适合采用RBAC模型——用户关联角色,角色关联菜单权限。这种方式在毕设里展示效果均衡且易维护:新增一名财务人员时不需要为这个用户单独设置十几项权限,只需要给他绑定一个“财务专员”角色。调整某个岗位职责时,改角色权限就能生效,不必逐个用户操作。
具体落地为五张表:用户表、角色表、用户角色关联表、菜单表、角色菜单关联表。菜单表的字段包括:菜单ID、父级菜单ID、菜单名称、菜单类型(目录/菜单/按钮)、路由地址、权限标识,其中权限标识是一个比较重要的字段。前端按钮的显隐靠它去匹配,比如查看工资条的按钮要求有salary:employee:view这个权限标识,没有的话按钮直接不渲染。后端接口也会再次校验权限,每个接口方法上用@PreAuthorize("hasAuthority('salary:employee:view')"),数据安全性至少能得到保障。
7.2 数据权限的简单实现方案
RBAC解决了“能看哪个菜单”的问题,但还有一个问题:一个财务能看到哪些数据?是只能看自己部门的还是全部部门都能看?这就是数据权限。完整的数据权限体系在真实企业级系统里会做得很复杂——按组织架构、按数据归属人、按自定义规则。毕设里做一个简化的方案,在部门表上做文章。
基本思路是用户登录后,在Token里带上部门编码和角色信息,查询员工或工资数据时在SQL层面加上部门过滤。系统管理员和财务总监可以跨部门看全量数据,普通部门主管只能看本部门。在实现上可以自定义一个MyBatis-Plus的拦截器或者简单一点,在Service层判断当前用户可见的部门ID集合,然后拼进查询条件。
在答辩时把“为什么不能只靠前端路由隐藏来做权限控制”讲清楚也是加分项。前端的菜单和按钮隐藏只是用户体验层,后端的每个接口都要做独立鉴权,因为用户在浏览器控制台完全可以自己拼接请求来尝试越权操作。
7.3 密码安全存储不能只搞MD5
很多同学的源码里用户密码直接MD5后入库。这种做法在企业里已经不太被接受了。MD5属于快速摘要算法,暴力破解的成本很低。建议用BCrypt或BCrypt增强版,Spring Security自带BCryptPasswordEncoder,使用简单。
BCrypt的特点同一个人相同密码每次加密后的结果都不一样,因为内部自动引入随机盐。校验时把明文传给matches方法,它从密文里提取盐重新计算再比较,所以不用单独维护一个盐字段。安全性相对有保障,就算数据库泄露,攻击者想从BCrypt密文里逆推出明文,普通算力下会极其困难。
7.4 动态菜单的实现逻辑
用户登录成功后,前端拿到用户的Token去调用获取用户信息的接口。后端根据用户角色查出所有关联的菜单,构建成树形结构返回。前端用一个递归组件或者配合router.addRoute动态注册路由来实现渲染。以前不少人把菜单按角色全部写在路由表里,这种做法的问题在于:如果给某个用户临时加一个角色,他必须重新登录甚至重新编译前端才能生效,灵活性较差。后端动态下发菜单的方式改完权限刷新一下就能看到变化。
8. 常见问题排查与答辩避坑
8.1 一个月份字段类型引发的事故
工资表里月份字段,如果设计成String类型存储"2025-02",查询时MySQL会对该列做隐式类型转换,一旦有索引也派不上用场,数据量大的时候全表扫描跑不掉。而且"2025-2"和"2025-02"两个格式在字符串比较时不是一回事,数据录入不一致就会导致汇总时漏数据。
更合理的方案是用整数存年月,比如202502这样的格式,比较和排序直接走整数逻辑不用转换。如果要展示,格式化一下也不难。还有一种用DATE类型第一天代表所属月份,能直接调用MySQL的日期函数做区间查询,也算可行方案。
8.2 精度问题:千万别用Double存金额
Java开发里最经典也最严重的一个错误就是用Double类型存金额。二进制浮点数在表示0.1这类十进制小数时本身就有精度误差,多个金额累加后误差会越来越大。工资计算涉及减法、比例、阈值判断,失之毫厘谬以千里。
强烈建议数据库用DECIMAL(10, 2),Java实体用BigDecimal,所有金额计算都通过BigDecimal完成。要注意BigDecimal构造时尽量用字符串入参,new BigDecimal("0.1")而不是new BigDecimal(0.1),后者会把二进制浮点的完整精度带进来。
除法计算社保比例这类操作时,要指定保留小数的精度和舍入模式。比如:
BigDecimal personalRate = new BigDecimal("0.08"); BigDecimal personalAmount = base.multiply(personalRate).setScale(2, RoundingMode.HALF_UP);8.3 时间字段和JDBC时区问题
JDBC连接MySQL时如果URL里没有设置serverTimezone,新版驱动经常会报错或者时间偏差8小时。连接串建议写成这样:
jdbc:mysql://localhost:3306/salary_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=trueJava实体里日期时间字段用LocalDate和LocalDateTime配合MyBatis-Plus的自动填充功能。创建时间和更新时间可以统一在插入和更新时自动填充,不要在每个Service方法里手动set。
8.4 答辩时的几个必问点
做工资系统的同学答辩时基本会被问到这几类问题,提前做好准备:
“你系统设计上还有什么可以改进的地方?”这个问题不要回答没有。比较好的思路是:当前是单机部署方案,未来可以引入Redis缓存热点数据,工资核算模块可以进一步改为分布式任务调度,或者增加消息队列做异步通知。
“你的工资核算逻辑怎么保证准确性?”把个税累计预扣法、社保基数上下限、异常数据校验这套逻辑讲清楚,同时提到测试过程中准备了几组代表性数据验证计算正确性。如果你手头确实准备过边界测试数据,这里会很加分。
“权限控制怎么实现的?”把RBAC模型、后端接口级鉴权、数据权限范围过滤、前端菜单动态加载这条链路串起来讲,说明为什么不能只做前端控制。
“你的系统跟用Excel管理工资相比优势在哪里?”从效率、错误率、审计追溯、权限隔离几个方面讲。这个问题的关键不是技术炫技,而是展示你对业务价值的理解。
8.5 部署上线前要过一遍的自检清单
- 数据库脚本是否完整,能否在全新环境下从头执行成功
- 配置文件里有没有写死本机IP或密码,数据库连接信息是否改为环境变量或外部配置
- 前端构建产物是否正确放到后端静态资源目录或部署到Nginx
- 是否存在未授权就能访问的接口,遍历一遍Controller检查Security配置
- 工资核算的数据逻辑是否在跨月数据上有验证过
- 服务器系统时间是否准确,时区是否正确
9. 给准备复现或二次开发的人一些额外建议
如果你拿到的是一套别人写好的SpringBoot工资管理系统源码,不建议直接改个名字就当成自己的毕设提交。原因特别现实:毕业设计考察的是你解决问题的能力,答辩现场老师会问很多实现细节,如果你连项目结构都说不清楚,第一轮就会被问穿。正确做法是把源码当作一个参照物,自己动手把核心模块重写一遍。
具体来说,可以先画清楚系统的表关系图,理解每张表为什么存在,再对照源码把一条用户请求从Controller到Mapper的调用链走通,然后把工资核算这个核心模块自己实现一遍。这个过程里遇到问题没关系,解决问题后的经验才算真正沉淀下来了。
技术栈上如果想做一些轻量级扩展,可以考虑加Redis缓存热点员工的工资标准,避免每次核算都查一遍数据库;或者用定时任务在每个月底自动生成下个月的工资草稿数据;也可以增加一个简单的操作日志切面,用AOP记录所有修改操作的痕迹。这些方向都不会大幅增加开发量,但足以让系统在评委面前多几个谈资。
在我个人看来,工资信息管理系统这套毕设的价值在于它足够贴近真实业务,能够把一个开发者从单纯学习框架API变成思考业务建模的人,在编码之外还要梳理税务规则、理解企业角色分工,这种完整做透一个系统的经历,带来的收获会比单纯刷一个多模块电商Demo来得更扎实。