由于员工信息管理系统是计算机类毕业设计中出现频率最高的一类选题很多同学拿到题目之后第一反应是“功能太简单、没啥可写”但真正动手之后才发现处处是坑框架版本不兼容、Mapper扫描不到、JSON序列化死循环、部署到服务器之后数据库连不上……我今年刚带完几个做这个题目的同学前后花了大概三周帮他们梳理完整套流程这里把整个项目的搭建思路、核心代码、数据库设计以及最容易踩的坑一次性说清楚。先说结论Spring Boot MySQL 做员工管理系统技术栈选得没问题但如果你能在这个基础上把细节做扎实比如权限控制、日志记录、分页查询异常处理答辩的时候老师基本挑不出大问题。这篇文章不会只给你贴代码我会把每一步为什么这么做的原因也讲清楚方便你写到论文的“需求分析”和“系统设计”章节里同时也会把我在实际指导过程中遇到过的高频报错整理成排查清单希望能帮正在赶工的你少走点弯路。1. 项目整体设计与技术选型思路1.1 为什么只有Spring Boot还不够很多同学拿到“基于Java Spring Boot MySQL的员工信息管理系统”这个题目第一反应是直接用Spring Boot写个增删改查就完了。但实际情况是毕业设计答辩时老师一定会问你这个系统怎么保证安全性多用户同时访问数据库会不会出问题为什么用MyBatis而不是直接用JDBC所以我在指导时一般建议给系统加两个最基本的模块一是登录认证与权限控制至少区分管理员和普通员工二是统一异常处理与日志记录。这套组合在本科毕设里属于“安全边际很高”的方案——功能完整度足够代码量可控每个模块也都有得写。技术选型方面我建议的搭配是后端框架Spring Boot 2.7.x不要一上来追新版本后面讲原因ORM框架MyBatis Plus 3.5.x比纯MyBatis省掉大量XML配置数据库MySQL 5.7或8.0都能用但驱动和连接串写法有区别前端Thymeleaf模板引擎 Bootstrap能少写大量JS代码适合赶工权限控制Spring Security或者自定义拦截器二选一即可抛开技术栈你在论文里一定要写清楚这个系统面对的用户角色和使用场景。员工信息管理系统通常有两类用户管理员和普通员工。管理员负责维护整个公司的员工档案包括新增入职员工、编辑资料、调整部门、删除离职员工等操作普通员工只能查看个人信息、修改部分字段甚至只能看不能改。这种设定意味着你的数据库设计从一开始就要考虑权限问题而不是等到写代码的时候才补。1.2 功能模块划分与工作量预估我在给同学规划任务时一般把系统拆成以下模块用户登录与注销验证用户名密码、Session保存登录态员工信息管理增删改查查询支持姓名、部门、入职时间等条件组合部门信息管理用于员工表关联查询同时减轻数据冗余个人信息查看与修改普通员工登录后的权限范围操作日志记录谁在什么时间操作了哪条数据答辩加分项数据统计各部门人数统计用图表展示可选我遇到过不少同学一开始想把功能设计得很丰富什么考勤、薪资、请假审批全都塞进去结果写到一半发现数据库表关系越来越复杂代码Bug越改越多最后临近提交才草草收尾。如果你的目标是“顺利通过答辩并能讲清楚自己的代码”上面这六个模块就是比较合适的体量。再多的功能不是不行而是你要考虑自己是否有足够的时间去调试。1.3 目录结构与包命名规范包结构这块我要求同学统一按照Controller - Service - Mapper - Entity 四层结构分包不要图省事把什么都写在Controller里。这里给你一个可以直接照搬的包结构com.example.employee ├── controller // 接收前端请求返回页面或JSON数据 ├── service // 业务逻辑层处理具体业务规则 │ └── impl // 业务逻辑实现类 ├── mapper // 数据访问层定义数据库操作方法 ├── entity // 实体类对应数据库表结构 ├── config // 配置类拦截器、跨域等 ├── common // 通用返回结果、异常处理类 ├── interceptor // 登录拦截器 └── EmployeeApplication.java // 启动类为什么一定要这样拆有三个原因第一答辩时候老师问你项目分层了没有你直接指着项目结构就能讲清楚第二Spring容器帮你管理Bean的前提是类上标注明确分包混淆会导致扫描不到Mapper或者Service第三后面排查Bug时你能很快定位问题是出在数据层、业务层还是控制层。2. 数据库设计与建表实操2.1 表结构设计背后的核心思路员工信息管理系统虽然名字叫“员工信息”但实际开发时最少也要三张表员工表、部门表、用户表。如果只建一张员工表然后所有信息全都堆进去数据库设计这关就过不了。员工表employee是核心表需要存储姓名、性别、手机号、邮箱、入职日期、部门ID、岗位、薪资状态等字段。这里有个关键设计决策部门ID是以外键关联的方式存在员工表里还是直接存部门名称字符串我在一开始就定了方案存部门ID然后通过SQL联表查询查出部门名称。这样做的好处非常明显。假如有一天部门改名了员工表里存的是“技术一部”你就要写一个批量更新的SQL把所有员工的部门字段都改一遍如果存的是部门ID只需要修改部门表里那一条记录员工表不用动。另外存ID也方便后面按部门统计人数时直接用group by操作。2.2 核心建表SQL参考下面是我给同学整理过的一套可以直接执行的建表SQL注释信息也符合毕设论文里“数据库设计”章节的截图要求-- 部门表 CREATE TABLE department ( id int NOT NULL AUTO_INCREMENT COMMENT 部门ID, name varchar(50) NOT NULL COMMENT 部门名称, location varchar(100) DEFAULT NULL COMMENT 办公地点, created_time datetime DEFAULT NULL COMMENT 创建时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT部门信息表; -- 员工表 CREATE TABLE employee ( id int NOT NULL AUTO_INCREMENT COMMENT 员工ID, emp_no varchar(20) NOT NULL COMMENT 工号, name varchar(50) NOT NULL COMMENT 姓名, gender tinyint DEFAULT NULL COMMENT 性别 0未知 1男 2女, phone varchar(20) DEFAULT NULL COMMENT 手机号, email varchar(100) DEFAULT NULL COMMENT 邮箱, department_id int DEFAULT NULL COMMENT 部门ID, position varchar(50) DEFAULT NULL COMMENT 岗位, entry_date date DEFAULT NULL COMMENT 入职日期, status tinyint DEFAULT 1 COMMENT 在职状态 1在职 0离职, created_time datetime DEFAULT NULL COMMENT 创建时间, updated_time datetime DEFAULT NULL COMMENT 更新时间, PRIMARY KEY (id), KEY idx_department_id (department_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT员工信息表; -- 用户表用于登录认证 CREATE TABLE sys_user ( id int NOT NULL AUTO_INCREMENT COMMENT 用户ID, username varchar(50) NOT NULL COMMENT 登录用户名, password varchar(100) NOT NULL COMMENT 密码(BCrypt加密), role varchar(20) DEFAULT EMPLOYEE COMMENT 角色 ADMIN/EMPLOYEE, employee_id int DEFAULT NULL COMMENT 关联员工ID, status tinyint DEFAULT 1 COMMENT 账号状态, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;建表的时候要注意两个细节。第一个是字符集直接指定utf8mb4而不是utf8因为utf8在MySQL里最多只能存3字节遇到生僻字或者特殊符号会报错。第二个是工号emp_no建议加唯一索引因为工号是员工在公司的唯一标识如果重号会导致后续数据混乱我在实际操作里吃过这个亏后面在问题排查部分会详细讲。2.3 数据库连接配置与驱动版本选择配置application.yml的时候我见过太多同学在这里栽跟头。Spring Boot 2.7.x默认使用JDBC驱动是com.mysql.cj.jdbc.Driver而旧教程里常见的com.mysql.jdbc.Driver在MySQL 8.0的驱动包中已经标记为过时虽然还能跑但会一直输出警告日志。推荐直接使用下面的配置spring: datasource: url: jdbc:mysql://localhost:3306/employee_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.employee.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这里重点讲一下serverTimezoneAsia/Shanghai这一项。MySQL 5.7版本默认时区不是UTC但8.0版本安装时如果选的是系统默认时区Java连接时会出现“The server time zone value”的异常。加上serverTimezone参数可以统一时区避免数据库时间和程序时间相差8小时。另外allowPublicKeyRetrievaltrue是MySQL 8.0在非SSL连接下经常需要加的参数有时候漏掉这个会报Public Key Retrieval is not allowed权限错误。3. 后端核心功能实现与踩坑记录3.1 项目骨架搭建与依赖配置创建一个Spring Boot项目我建议直接使用Spring Initializr或者IDE自带的创建向导手动在pom.xml里加依赖也完全没毛病但要注意版本匹配。核心依赖如下dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies我在指导同学做项目时发现很多初次接触Spring Boot的人会忽略一个关键点spring-boot-starter-parent父工程版本。用IDEA创建项目时如果没有指定Spring Boot版本默认可能会生成3.x的最新版本而网上能找到的教程大部分是2.x。Spring Boot 3.x要求JDK 17以上且javax.servlet包名改成了jakarta.servlet很多旧代码直接迁移会报“找不到符号”错误。如果你的电脑装的是JDK 8建议创建项目时直接选择Spring Boot 2.7.16或相近的2.7.x版本否则连编译器这关都过不了。3.2 员工信息增删改查的实现要点员工信息CRUD是整个系统最核心的业务很多同学会以为只是简单的调用MyBatis Plus自带的insert、selectById、updateById、deleteById方法但在真实场景里需要考虑更多的边界情况。以新增员工为例业务逻辑至少包含先校验工号是否重复再确认部门ID是否真实存在最后才执行插入操作。直接用employeeService.save(employee)确实能插入数据但如果工号重复数据库就会爆出唯一索引冲突异常最终返回给前端的是默认的错误页面。我更推荐在Service层写一个明确的保存方法示例代码Service public class EmployeeServiceImpl implements EmployeeService { Override public Result saveEmployee(Employee employee) { LambdaQueryWrapperEmployee queryWrapper Wrappers.lambdaQuery(); queryWrapper.eq(Employee::getEmpNo, employee.getEmpNo()); long count this.count(queryWrapper); if (count 0) { return Result.error(工号已存在请检查后重试); } employee.setCreatedTime(LocalDateTime.now()); this.save(employee); return Result.success(新增成功); } }这种做法的好处是逻辑一目了然答辩时老师问“新增员工时怎么防止重复数据”你可以直接指着这段代码解释。另外删除员工时不要用物理删除deleteById而是更新状态字段把status置为0也就是逻辑删除。员工离职了档案还需要保留哪天需要查历史记录时逻辑删除的数据还能查出来。这点在你写论文时也可以当作系统设计的一个亮点来写。3.3 分页查询最容易忽略的条件封装员工管理系统里的员工列表通常不是一次性加载全部数据的数据量不大时可以全查但几十条以上就会出现页面打开变慢、浏览器渲染卡顿的问题。分页查询是必做的一个功能。MyBatis Plus提供了内置分页插件需要在配置类里注册否则调用分页方法不生效Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInnerInterceptor new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setMaxLimit(100L); interceptor.addInnerInterceptor(paginationInnerInterceptor); return interceptor; } }分页参数里边有几个点容易踩坑current页码从1开始前端传0的话查询结果会偏移需要做空值校验size每页条数如果不限制用户传一个100000就能把整个表全查出来性能极其拉胯条件查询按姓名模糊匹配、按部门筛选需要把条件封装进LambdaQueryWrapper再传给分页方法顺序不能反查询代码参考public PageEmployeeVO getEmployeePage(int pageNum, int pageSize, String name, Integer departmentId) { PageEmployee page new Page(pageNum, pageSize); LambdaQueryWrapperEmployee wrapper Wrappers.lambdaQuery(); wrapper.like(StringUtils.hasText(name), Employee::getName, name) .eq(departmentId ! null, Employee::getDepartmentId, departmentId) .orderByDesc(Employee::getEntryDate); PageEmployee employeePage this.page(page, wrapper); // 接着把部门名称查询出来封装成VO返回给前端 }这段看起来简单但藏着两个容易犯错的位置。StringUtils.hasText(name)是条件判断如果name是null或者空串like条件不追加而departmentId ! null则避免了eq条件把数据库里的null值字段全部过滤掉。我见过有同学没用这种条件写法直接在拼接SQL时判断结果要么忘记了判空导致空指针要么条件全加进去导致查询结果为空。3.4 登录认证与权限拦截的轻量实现很多教程教你用Spring Security做权限控制但配置类复杂且对新手不友好。在指导毕设的时候我更推荐用自定义拦截器的方式实现登录认证代码量小、逻辑清楚、答辩也好讲明白。核心思路是前端登录成功后把用户信息存入Session写一个拦截器拦截所有页面请求如果Session里没有登录用户就跳转到登录页。登录Controller核心代码PostMapping(/login) public String login(String username, String password, HttpSession session, Model model) { LambdaQueryWrapperSysUser wrapper Wrappers.lambdaQuery(); wrapper.eq(SysUser::getUsername, username); SysUser user userService.getOne(wrapper); if (user null) { model.addAttribute(error, 用户不存在); return login; } if (!BCrypt.checkpw(password, user.getPassword())) { model.addAttribute(error, 密码错误); return login; } session.setAttribute(loginUser, user); return redirect:/employee/list; }密码存储使用BCrypt加密而不是明文这也是答辩加分项。明文密码一旦数据库泄露用户的习惯性密码在其他平台也容易被撞库。Spring Security单独作为依赖引入有点重其实spring-security-crypto这个轻量依赖里就有BCrypt工具类不用启动整套Security过滤器链。拦截器的实现也比较简单public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); if (session.getAttribute(loginUser) ! null) { return true; } response.sendRedirect(/login); return false; } }写完拦截器之后还要在配置类里注册拦截路径很多同学写了拦截器却忘记注册导致拦截器完全没生效Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /loginPage, /css/**, /js/**, /images/**); } }这里有个非常容易被忽略的问题静态资源路径如果也被拦截登录页的CSS和JS全部加载不出来页面就会变成纯HTML的丑样子。所以我反复强调要把/css/**、/js/**等路径加进排除列表不然排查半天都不知道问题出在哪。4. 前端页面渲染与数据交互4.1 Thymeleaf模板还是前后端分离我见过不少同学在毕设里选择了前后端完全分离的方案即Spring Boot只提供JSON接口前端用Vue或者React单独写一套页面。这个方案对前端功底要求比较高而且需要在服务器上额外部署Nginx处理静态文件如果你时间紧或者前端基础一般我不太建议。Thymeleaf Bootstrap组合的好处是Spring Boot内置支持不用额外配置跨域数据在服务端渲染好后直接输出HTML代码量少对毕设来说完全够用。使用Thymeleaf时还要注意一个小坑Spring Boot 2.x默认的模板前缀是classpath:/templates/页面文件要放在resources/templates目录下否则会报Error resolving template异常。之前指导的同学经常把HTML文件放在static目录下面折腾半天报500错误其实就是目录放错了。4.2 列表页展示与表单校验要点员工列表页是系统的门面做得清楚会直接影响答辩时老师的第一印象。我的做法是表格上方放条件查询区域工号/姓名输入框、部门下拉框、查询按钮下方表格展示员工信息最后一列放操作按钮编辑、删除再下边分页导航。分页导航这部分如果你用MyBatis Plus的Page对象Thymeleaf页面里可以直接循环生成页码div classpagination a th:href{/employee/list?pageNum1size ${page.size}}首页/a a th:if${page.current 1} th:href{/employee/list?pageNum ${page.current - 1} size ${page.size}}上一页/a span th:text${page.current} / ${page.pages}/span a th:if${page.current page.pages} th:href{/employee/list?pageNum ${page.current 1} size ${page.size}}下一页/a a th:href{/employee/list?pageNum ${page.pages} size ${page.size}}末页/a /div表单校验我给同学的建议是前后端都做。前端用Bootstrap的required属性或者JS做基本非空校验提升用户体验后端在Service层再校验业务规则比如工号重复、手机号格式。只做前端校验的后端接口用Postman直接传脏数据就能绕过老师如果懂行一眼就看出来系统的健壮性不够。4.3 删除操作前的二次确认直接点删除按钮就删数据这个交互太鲁莽了。实际开发中删除前必须弹窗确认防止误触。最简单的实现是在删除按钮上绑定一个JS确认事件a th:href{/employee/delete/ ${emp.id}} classbtn btn-danger btn-sm onclickreturn confirm(确定要删除该员工吗此操作不可恢复。)删除/a这种实现方式虽然简单但效果好。线下答辩演示时老师会点删除按钮你弹个确认框既专业又能防止演示过程中误删数据导致尴尬。我遇到过同学演示时手滑删错了一条数据又没有备份结果在那手动还原数据库画面非常混乱。如果你提前做了二次确认这个坎基本能避开。5. 项目测试与打包部署实录5.1 本地功能测试清单开发完成之后我建议按照下面的测试清单过一遍每一项都确认通过再进行打包部署登录成功/失败场景正确密码能进入系统错误密码有提示未登录访问首页会跳登录员工新增必填字段为空能拦截工号重复能提示新增成功后列表页出现新数据员工编辑修改姓名、部门后再列表页刷新能看到新数据员工删除弹窗取消不删确认后在列表页消失分页查询第二页数据与第一页不重复条件查询结果与表格数据匹配权限控制普通员工账号看不到“删除”按钮或者删除接口返回无权限数据库备份操作完后再看一眼数据库表数据是否和页面显示一致这个清单的关键价值在于防患于未然。很多同学开发时只测了自己写的“快乐路径”比如登录成功进去能看见列表就觉得万事大吉结果一测试删除就发现404一查原因才发现路由映射写错了。答辩演示的时候老师往往会敲几个边界条件比如你输入一个不存在的人名来查如果你没做空结果处理页面直接白屏或者报错印象分会掉一截。5.2 项目打包部署的两种方式毕设常见的部署方式有两种一种是直接在IDEA里运行另一种是打包成JAR放到服务器上运行。如果你只需要本地演示方式一完全够。但答辩时如果电脑突然出问题或者老师想看一下远端访问效果方式二会更稳妥。打包命令mvn clean package -DskipTests打包成功后target目录下会生成一个employee-system-0.0.1-SNAPSHOT.jar文件。如果部署在服务器上直接用java -jar employee-system-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod这里要提前规划好生产环境的配置。我一般建议单独写一个application-prod.yml把数据库地址改成服务器的IP然后端口使用80或者8080。如果你部署到某云服务器记得在安全组或者防火墙规则里放行对应端口否则外部访问不通。曾经有个同学的部署答辩演示环节挂了就是他以为把端口号改成8080就行结果安全组默认只开了80和3389半天都连不上。6. 常见问题与排查技巧速查表6.1 高频报错及解决方案这部分我整理成了表格都是我实际带毕设时高频出现的问题遇到对应报错可以直接按表格排查场景常见报错信息解决方案启动项目Consider defining a bean of type Mapper in your configuration检查启动类有没有MapperScan注解或者Mapper接口上有没有加Mapper注解启动项目Failed to configure a DataSource检查application.yml里的数据库连接串有没有写对驱动类有没有引入登录时报错Public Key Retrieval is not allowed在jdbc连接串后面加allowPublicKeyRetrievaltrue查询时报错Unknown column xxx in field list实体类属性名和数据库字段名不一致检查map-underscore-to-camel-case是否开启页面上显示500Error resolving templateHTML文件没有放在templates目录下检查文件名拼写列表页加载慢无日志但接口响应超过几秒检查是不是没有分页一次性查询了所有数据删除操作后刷新又有数据无报错数据库外键约束或者逻辑删除写成了update失败检查SQL语句部署到服务器Communications link failure数据库连接地址是不是本机地址防火墙是否放行3306端口6.2 容易被忽略的版本兼容问题Spring Boot与MyBatis Plus以及MySQL驱动之间的版本匹配是“毕设十大隐形杀手”之首。前端时间某同学在pom.xml里加了Spring Boot 3.0.5的最新版本结果MyBatis Plus 3.4.x不支持JDK 17的新特性项目启动时直接报了一堆反射相关的错误。事实上MyBatis Plus官方适配Spring Boot 3是从3.5.3版本开始的很多老版本3.3.x根本不兼容。我给你的建议是尽量不要追新版本直接用Spring Boot 2.7.16版本打底搭配MyBatis Plus 3.5.3.1这种组合我实测过很多台机器稳定性很高。如果非得用Spring Boot 3.x那你要做好心理准备可能需要在网上翻更多的适配文档。6.3 数据一致性维护的实操心得这个点我一定要单独拿出来说因为太多人做毕设时完全不考虑数据一致性问题。什么叫数据一致性问题举个例子员工表里有个部门ID为1的员工你在部门管理页里把这个部门删了员工那边就变成“无部门”状态。如果你编程时没处理这个逻辑数据库里就是一堆脏数据。我在这个项目的表结构设计中把员工表和部门表用外键关联但没有物理外键约束。为什么不建物理外键因为很多老师会拿“数据库设计合理性”来问问题物理外键会锁表插入和更新数据时性能开销比较大更重要的是如果建了物理外键删除部门时MySQL会直接抛外键约束异常而异常消息不友好用户看到的是一堆乱码。所以更合适的做法是在部门删除的Service方法里先查一下有没有员工还归属这个部门有的话就拦截并提示“该部门下还有员工请先调整员工部门”。这算是一个比较符合实际业务经验的设计也容易在答辩时展开讲是一个不错的加分点。6.4 关于实习和简历项目的延展这个员工信息管理系统虽然叫“员工信息”但如果你把它往简历上写尽量不要太直接了事我建议在技术点上做些延展引入Redis缓存热点员工数据并说明缓存穿透和缓存击穿的解决方案使用Spring AOP记录操作日志并持久化到数据库通过Layui或Bootstrap完成响应式页面布局说明自己对前端组件库有了解给查询接口加上参数校验和统一返回结果处理体现接口设计的规范思维如果时间充裕还可以把登录流程图和数据表ER图画出来排版放进论文中比单纯的文字描述直观很多。很多同学的项目答辩表上写的“熟练使用Spring Boot”只是嘴上说说你把技术点真正摆出来老师追问几句一样能答上这才算扎实。回到我自己的经验上今年带过的某位同学就是按上面这套方案完成的从需求分析到数据库设计到编码大概花了十来个工作日的晚上最后在答辩时演示了员工分页查询、部门联动下拉、操作权限控制以及异常提示几个场景整个流程大约七分钟老师问了两个问题他都顺利答上来了最终评了个不错的分数。最关键的是这套体系不是靠“背代码”硬撑起来的每块代码自己都理解透彻碰上提问不会慌。如果你正在做这个题目希望这篇文章能减少你查资料的时间。最后再提醒一句动手写代码前先把数据库表结构设计好这是整个项目的地基。地基歪了后面写再多代码都要返工。