☰
Spring Boot招生与就业管理系统:表结构设计与部署避坑
2026/10/6 13:24:23 网站建设 项目流程

简介:一套基于Spring Boot的招生与就业信息管理系统完整工程,面向高校招生办、就业指导中心及教育信息化开发人员,覆盖在线申请、资格审核、招生计划,以及职位发布、简历投递、就业跟踪等流程,也可作为Java学习者理解业务系统设计与工程搭建的实践参考。整个zip压缩包共1806个文件,体积约69.97MB,主要包含Java控制层与工具类源码、编译后的class文件、FreeMarker页面模板与前端静态资源、数据库脚本以及Spring Boot环境配置文件,后端、前端、部署配置一应俱全。目前已有566人学习下载,便于直接导入开发工具开展运行调试。内容预览显示项目中封装了考生管理、定时任务调度、Excel导出、文件处理等通用模块,结合招生与就业两大子系统功能,可帮助使用者快速掌握系统搭建思路、数据表设计及核心业务实现方法,适合项目实战、二次开发和毕业设计参考。

1. 招生与就业信息管理系统:这套 Spring Boot 项目到底管了什么

招生季和就业季是学校信息部门最忙的两个时段。新生报名、录取确认、企业招聘、学生推荐、就业率统计,每一环的数据都在多个部门之间传来传去,靠表格和人肉对账,不出错才是反常。这套“招生与就业信息管理系统”基于 Spring Boot 搭建,核心价值是把招生主线(计划、报名、审核、录取)和就业主线(企业、招聘、推荐、统计)放进同一个后端服务,让管理员、招生人员、辅导员、学生、企业用户各自操作同一套数据源。它适合两类人:一类是毕业设计需要完整项目参考的学生,另一类是刚接手校园信息管理系统、想直接拿现成代码改业务的开发。接下来按我实际拆解这类项目的顺序,从表结构、核心接口、部署、踩坑一路讲到底层细节。

2. 需求拆解与表结构设计:角色、模块、数据表怎么对齐

2.1 从角色反推功能:四种账号分别对应哪几条业务线

我接手这类系统时,先不看代码,而是看角色和模块能不能对得上。这个系统的四个核心使用方是:招生就业处工作人员、院系辅导员、学生和企业招聘负责人。招生就业处要维护招生计划、审核报名、导出录取名单,还要维护企业库和招聘信息;辅导员要查看学生就业意向,录入就业状态;学生在网页或手机上报名招生计划、完善简历、查看招聘职位;企业用户负责发布职位、接收学生简历投递。

把这些需求列出来,模块边界就清楚了:系统管理管账号和权限,招生管理管计划、报名、录取,就业管理管企业、职位、推荐、统计,学生档案管基础数据和状态流转。设计阶段把边界定好,后面写 controller 和 service 都会省力很多。很多刚起步的人喜欢把学生和企业的字段全塞进用户表,短期看方便,但需求一扩展,比如要加“校外导师”角色,整个用户表就面临大改。

2.2 核心表结构:从账号到业务数据的一整套建表思路

信息管理类系统最忌讳上来就写大宽表。我在拆这套项目时,把表按“静态基础数据”和“动态业务数据”两条线分:用户、学生档案、企业信息属于前者,招生计划、报名记录、招聘信息、就业记录属于后者。基础数据表不频繁变更,业务表通过 user_id、plan_id 这类字段去关联它;业务表记录的则是“什么时候、谁、做了什么动作”。下面是一份和这个项目场景匹配的核心 DDL,按 MySQL 8 语法书写。

-- 系统用户表:所有角色统一走这张表登录 CREATE TABLE sys_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录账号', password VARCHAR(100) NOT NULL COMMENT 'BCrypt加密后的密码', role_code VARCHAR(20) NOT NULL COMMENT 'admin/teacher/student/company', real_name VARCHAR(50) COMMENT '姓名', status TINYINT DEFAULT 1 COMMENT '1启用 0禁用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 学生档案表:招生录取后由管理人员批量导入 CREATE TABLE student_profile ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL COMMENT '关联sys_user.id', student_no VARCHAR(30) NOT NULL COMMENT '学号', student_name VARCHAR(50) NOT NULL, gender TINYINT COMMENT '0男 1女', major_name VARCHAR(100) COMMENT '专业名称', class_name VARCHAR(50) COMMENT '班级', enroll_year VARCHAR(10) COMMENT '入学年份', phone VARCHAR(20), status TINYINT DEFAULT 0 COMMENT '0在读 1已毕业' ); -- 招生计划表:每年每专业一条记录 CREATE TABLE enrollment_plan ( id BIGINT AUTO_INCREMENT PRIMARY KEY, plan_name VARCHAR(100) NOT NULL, major_name VARCHAR(100), plan_count INT DEFAULT 0 COMMENT '计划招生人数', enroll_year VARCHAR(10), start_time DATETIME, end_time DATETIME, status TINYINT DEFAULT 0 COMMENT '0未开放 1报名中 2已结束' ); -- 报名记录表:学生报名后进入待审核状态 CREATE TABLE enrollment_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, plan_id BIGINT NOT NULL, student_user_id BIGINT NOT NULL, score DECIMAL(5,2) COMMENT '高考或入学成绩', source_region VARCHAR(50) COMMENT '生源地', status TINYINT DEFAULT 0 COMMENT '0待审核 1通过 2不通过', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, audit_time DATETIME NULL ); -- 招聘信息表:企业用户发布职位 CREATE TABLE recruitment_info ( id BIGINT AUTO_INCREMENT PRIMARY KEY, company_id BIGINT NOT NULL, position_name VARCHAR(100), salary_range VARCHAR(30), requirement TEXT, status TINYINT DEFAULT 0 COMMENT '0待审核 1已发布 2已下架', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 就业记录表:辅导员或学生本人确认就业状态 CREATE TABLE employment_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_user_id BIGINT NOT NULL, company_id BIGINT, job_title VARCHAR(100), salary VARCHAR(30), status TINYINT DEFAULT 0 COMMENT '0待确认 1已就业 2升学暂缓', confirm_time DATETIME NULL );

这段 DDL 里有两个细节值得说明。第一,id 统一用 BIGINT 自增,不在代码里手动生成 id,MyBatis-Plus 主键策略配 auto,insert 后能拿到回填的 id,后续保存父子关联记录很方便。第二,状态字段统一用 TINYINT 加注释,不单独建枚举表,统计分析时用SUM(status=1)就能算指标,对这个量级的校园系统来说,比维护一张关联字典表性价比更高。字段名统一用小写下划线,MyBatis-Plus 开启 map-underscore-to-camel-case 后自动和 Java 的 camelCase 对应,不需要手工维护 resultMap。

2.3 权限模型:为什么不给每个角色建独立账号表

登录是这类系统最容易改崩的地方。很多初学者喜欢给每个角色单独建账号表,学生表带登录密码,企业表也带登录密码,看起来直观,但需求变成“支持手机号登录”“找回密码”“记录登录日志”的时候,要改好几个 Mapper,极其痛苦。更稳妥的做法是统一用 sys_user 作为账号主体,学生、企业信息当作扩展资料,通过 user_id 关联。

这个模型在 Spring Boot 里的落地方式是:登录认证只查 sys_user,拿到 userId 和 roleCode 后,再按角色查扩展资料。前端的 Vue 路由根据 roleCode 生成不同菜单;后端接口用 Spring Security 的拦截器或 @PreAuthorize 校验权限。功能按钮可以精确到“仅招生处可审核”,而不是靠前端隐藏按钮来防越权。为了项目启动时能种入管理员账号,我一般会在 CommandLineRunner 里做一次初始化,检测 sys_user 为空时自动创建 admin 账号,初始密码做成固定值并提醒用户第一时间修改。这样既避免了手写 SQL 插入账号,也解决了不同开发者本地环境不一致的问题。

3. 核心业务落地:从报名接口到就业统计的实现过程

3.1 项目骨架与 pom 依赖:版本组合选得对,后面少踩坑

拿到源码后第一件事不是急着启动,而是确认依赖版本。这类管理系统最常见的组合是 Spring Boot 2.7.x + MyBatis-Plus 3.5.x + MySQL 8.x。这个组合我跑过不少校园信息类项目,稳定性足够,网上能搜到的踩坑资料也最全。如果下游拿到的是 Spring Boot 3.x 的版本,要注意 Spring Security 的写法已经完全变了,不再继承 WebSecurityConfigurerAdapter,而是直接声明 SecurityFilterChain Bean。下面这份 pom 给出的是 Spring Boot 2.7 的稳态组合:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.25</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

我的建议是版本写死,不要全写 latest。MyBatis-Plus 3.5.x 的分页插件在 3.5.9 之后包名有调整,很多人的报错“PaginationInterceptor 不存在”就是版本太新导致照抄旧配置。Spring Boot 2.7.18 是 2.x 收尾版本,安全补丁完整,跑本地和毕业设计完全够。mysql-connector-j 8.0.33 对应 MySQL 8 稳定驱动,如果数据库是 MySQL 5.7,可以换 5.1.49,连接串参数略有差异。

3.2 学生报名接口:Service 层校验是关键,Controller 只做参数传递

拿招生报名当例子,说说三层结构怎么分工。Controller 只做两件事:接收请求参数、从安全上下文取当前用户,然后交给 Service,不写业务判断。Service 负责校验、逻辑和事务,Mapper 负责单表 CRUD 和复杂查询 SQL。这样分工的好处是接口可以复用,管理员代报名、Excel 批量导入最终都调用同一份 Service 逻辑。

@Service @RequiredArgsConstructor public class EnrollmentServiceImpl implements EnrollmentService { private final EnrollmentRecordMapper recordMapper; private final EnrollmentPlanMapper planMapper; @Override @Transactional(rollbackFor = Exception.class) public Long createRecord(EnrollmentSignUpRequest req, Long userId) { // 1. 校验计划是否在报名期内 EnrollmentPlan plan = planMapper.selectById(req.getPlanId()); if (plan == null || plan.getStatus() == null || plan.getStatus() != 1) { throw new BizException("招生计划未开放或不存在"); } // 2. 防止重复报名:同一计划同一学生只能报一次 Long exists = recordMapper.selectCount( new LambdaQueryWrapper<EnrollmentRecord>() .eq(EnrollmentRecord::getPlanId, req.getPlanId()) .eq(EnrollmentRecord::getStudentUserId, userId) ); if (exists != null && exists > 0) { throw new BizException("你已报名过该专业,请勿重复提交"); } // 3. 构造记录并落库,初始状态为待审核 EnrollmentRecord record = new EnrollmentRecord(); record.setPlanId(req.getPlanId()); record.setStudentUserId(userId); record.setScore(req.getScore()); record.setSourceRegion(req.getSourceRegion()); record.setStatus(0); recordMapper.insert(record); return record.getId(); } }

这段逻辑有三个关键点。第一,@Transactional(rollbackFor = Exception.class)的 rollbackFor 必须显式指定,Spring 默认只在运行时异常时回滚,如果业务中抛出的是受检异常,数据不会回滚;第二,重复检查用 LambdaQueryWrapper 做条件计数,这是在代码层做唯一约束,更硬的双保险是给 enrollment_record 表加UNIQUE KEY uk_plan_user (plan_id, student_user_id);第三,学生身份不从前端传参,而是从登录态拿 userId,防止用户改参数冒充他人操作。

Controller 和统一返回体配合,大约长这样:

@RestController @RequestMapping("/api/enrollment") @RequiredArgsConstructor public class EnrollmentController { private final EnrollmentService enrollmentService; @PostMapping("/signup") public Result<Long> signUp(@RequestBody @Valid EnrollmentSignUpRequest req) { // 从登录态获取当前用户ID,不信任前端传值 Long userId = LoginUtil.getCurrentUserId(); return Result.ok(enrollmentService.createRecord(req, userId)); } }

LoginUtil.getCurrentUserId() 在 Spring Security 场景里,通常是从 SecurityContextHolder 拿 Authentication,再取出 UserDetails 里存放的自定义 userId。注意不要在 Controller 方法里写@RequestParam Long userId这种从外部接收用户 ID 的做法,普通用户传另一个人的 ID 就能操作别人的报名记录,越权漏洞就是这么来的。

3.3 就业统计看板:能用一条 SQL 算完,就别把数据拉到内存算

就业率统计是这类系统期末被查看最多的功能。很多新手习惯把大量记录SELECT *到 Java 内存,再用 for 循环统计。数据量小的时候无所谓,几千条也能跑,到上万条的时候接口响应时间就会很难看。可行做法是让 SQL 完成聚合,Java 只接收结果集。

以“按月统计报名通过率”为例,SQL 可以这样写:

SELECT DATE_FORMAT(create_time, '%Y-%m') AS stat_month, COUNT(*) AS total_count, SUM(IF(status = 1, 1, 0)) AS success_count, ROUND(SUM(IF(status = 1, 1, 0)) / COUNT(*) * 100, 2) AS pass_rate FROM enrollment_record WHERE create_time >= DATE_SUB(NOW(), INTERVAL 12 MONTH) GROUP BY DATE_FORMAT(create_time, '%Y-%m') ORDER BY stat_month;

COUNT(*) 统计每月报名数,SUM(IF(status = 1, 1, 0)) 统计审核通过数,两者一除就是通过率。DATE_FORMAT 按月份归组,跨年份看趋势时也能直接复用;WHERE 里的 DATE_SUB 控制最近 12 个月,既避免全表扫描,也让看板不需要在 Java 代码里二次过滤。如果统计维度换成“专业 + 就业状态”,把 GROUP BY 改成CONCAT(enroll_year, '-', major_name)就能实现复合维度。

在 MyBatis-Plus 里,这种复杂 SQL 我用 @Select 注解写在 Mapper 接口中,不硬套 QueryWrapper。查询结果映射成一个 StatResultVO,统计结果再套一层缓存。缓存方案在第 6 章展开。

4. 部署运行配置:本地跑通项目的完整步骤

4.1 环境准备:JDK、Maven、MySQL、Node 最少备几样

把项目跑起来之前,先确认环境。这套 Spring Boot 系统在本机运行最少需要:JDK 8 或 17、Maven 3.8+、MySQL 8.x。如果工程里包含src/main/frontend或 vite.config.js,还需要 Node.js 14.18+。Redis 不是必须,如果 application.yml 里没配置 redis 连接、pom 里没有引入 spring-boot-starter-data-redis,就别额外安装,免得白增加一个服务依赖。

有一个容易忽略的点:JDK 版本要跟 Maven 编译配置匹配。如果 pom.xml 的编译插件指定 source 是 1.8,本机却只装了 JDK 17,构建虽然能过,但运行时某些反射框架可能报模块访问异常。我的习惯是开跑前先确认java -version、mvn -version、mysql --version三个版本输出,再继续操作,避免启动失败后到处找原因。

4.2 application.yml 配置解析:时区、字符集、SQL 日志

配置文件是“看起来一样但跑不起来”的重灾区。重点看 datasource 连接串和 MyBatis-Plus 两段配置:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/school_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: banner: false db-config: id-type: auto

url 里四个参数能不动就不动。characterEncoding=utf8 保证中文存取不乱码;serverTimezone=Asia/Shanghai 避免时间差 8 小时;useSSL=false 消除 MySQL 8 的连接警告;useUnicode=true 建议保留。jackson 的 date-format 和 time-zone 控制 LocalDateTime 直接返回时的序列化格式,不配的话前端会看到一串2025-01-01T00:00:00这种 ISO 格式。mybatis-plus 的 log-impl 开发环境建议开启 StdOutImpl,直接在控制台看 SQL;上线前改成 NoLoggingImpl,否则日志文件增长很快。id-type: auto 让 MP 用数据库自增主键,insert 后实体 id 自动回填。

4.3 init.sql 导入与启动步骤:四步走完本机联调

拿到源码包后按顺序执行,不要跳步骤:

第一步,在 MySQL 里建库并指定字符集,用CREATE DATABASE school_system DEFAULT CHARACTER SET utf8mb4;。第二步,导入 init.sql,用 Navicat 或命令行 source 都行,导入后确认 sys_user、enrollment_plan 这些核心表已经生成。第三步,改 application.yml 里的数据库密码,改成自己本地 MySQL 的 root 密码。第四步,启动后端,项目根目录执行mvn spring-boot:run,看到 Tomcat started on port(s) 8080 说明启动成功。

如果是前后端分离项目,还要进入前端目录执行 npm install、npm run dev,浏览器打开前端端口。jar 包部署方式则执行mvn clean package -DskipTests,target 目录下生成 xxx.jar,再用java -jar xxx.jar --spring.profiles.active=prod指定生产环境配置。这里有个细节:Spring Boot 会优先读取 jar 同级的 config/application.yml,我习惯把生产环境的数据库密码放在 jar 外面,避免每次改密码都要重新打包。

4.4 前端联调:Vite 代理解决开发跨域,Nginx 反代解决部署跨域

开发环境里前端端口是 5173,后端是 8080,浏览器同源策略会拦截接口请求。通过 Vite 的 server.proxy 配置,把 /api 开头的请求转发到后端,是最省事的做法。

// vite.config.js import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } });

changeOrigin 改成 true 之后,后端拿到的请求头 Host 会变成 target 的地址。大多数后端框架不校验这个字段,但有些网关会做 Host 白名单,没加就会被拦。如果接口路径不带 /api 前缀,把 proxy 的 key 改成实际前缀就行。生产环境一般用 Nginx 把前端静态资源和后端接口合并成一个域名,避免二次跨域。我见过最折腾的情况是后端部署在跳板机,Nginx 代理地址写反,页面秒崩但 curl 后端接口又能通,最后发现是 location /api/ 的 rewrite 规则写错。遇到 404 先检查代理路径是否正确转发。

5. 常见问题与避坑记录:最容易绊倒本地运行的五个坑

5.1 中文变问号:建库字符集没指定

现象:把 init.sql 导入后启动项目,录入中文名称,查询出来全是 ??。

原因:CREATE DATABASE 没指定字符集,MySQL 默认用了 latin1,CHAR/VARCHAR 只能存单字节,中文被截断成问号。

解决:建库时统一加DEFAULT CHARACTER SET utf8mb4。如果库已经建好,用ALTER DATABASE school_system CHARACTER SET utf8mb4;转一次,同时把相关表也 ALTER 掉。字符集务必在建库阶段就做对,后期转换存在历史数据兼容风险。

5.2 事务不回滚:@Transactional 的生效边界

现象:报名记录插入成功后,后续更新剩余名额的代码抛异常,但报名记录还是留在表里。

原因:最常见的是同类内部调用this.createRecord()绕过了 Spring AOP 代理;或者是方法不是 public;或者是异常被 catch 后没抛出去,Spring 感知不到失败。

解决:事务方法必须是 public,且由另一个 Spring Bean 调用;异常必须抛到代理边界外。同类调用时,可以在 Service 内@Autowired自身代理,再通过代理调用目标方法。这个方法虽然不优雅,但让事务切面真正生效。

提示:事务不是加个注解就完事。rollbackFor=Exception.class 必须显式声明,否则受检异常不会触发回滚。

5.3 时间少 8 小时:连接串和 Jackson 都要指定时区

现象:接口返回的创建时间是 2025-01-05 06:00:00,数据库里实际是 14:00:00。

原因:MySQL 连接串没设置 serverTimezone,驱动用了 UTC;Jackson 序列化又沿用 JVM 默认时区,两边一叠加就是 8 小时偏差。

解决:url 后面加 serverTimezone=Asia/Shanghai,yml 里给 jackson.time-zone 也指定 Asia/Shanghai。两边都对了才不会差。改完连接串要重启应用。另外注意 MySQL 的 NOW() 取的是数据库服务器时区,如果数据库部署在海外机器,也要同步确认。

5.4 登录成功但其他接口 401:Security 白名单和 Token 传递问题

现象:登录接口正常返回 token,跳转页面后菜单加载接口一直报 401。

原因:两个原因常一起出现。一是 Spring Security 配置把业务接口拦截了,但前端请求没带 Authorization 头;二是前端 request 拦截器没有把 token 拼进请求。

解决:先看前端 axios 拦截器是否设置config.headers['Authorization'] = 'Bearer ' + token;再看后端 SecurityConfig 放行路径,常见白名单是/api/auth/login和验证码接口。判断方法很简单:用 Postman 带上 token 请求一次接口,通了说明问题在前端,还是 401 就查 Security 配置。

// Spring Boot 2.7 + Security 5.x 配置写法 @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/login", "/api/auth/captcha").permitAll() .anyRequest().authenticated() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }

注意这是 Boot 2.7 的写法,Boot 3.x 已废弃 WebSecurityConfigurerAdapter,要用 SecurityFilterChain 的 Bean 配置。很多人从网上复制代码不看版本,把 3.x 的写法抄进 2.7 的项目,编译直接报错。

5.5 CORS 报错:跨域是前后端分离最常见的拦截兵

现象:前端页面登录,浏览器 console 报blocked by CORS policy。

原因:前端是 localhost:5173,后端是 localhost:8080,端口不同即跨域,后端没加 CORS 响应头。

解决:后端配置全局跨域。注意allowedOrigins("*")和allowCredentials(true)不能共存,浏览器会直接拒绝;用 allowedOriginPatterns 写允许的来源。

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("http://localhost:*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

开发环境把来源写成http://localhost:*已经足够;生产环境就把线上域名替换进去。如果拿不准当前项目有没有 CORS 配置,看后端控制台有没有 OPTIONS 预检日志,完全没有预检日志通常说明后端没有拦截,问题多半在前端代理或浏览器缓存。

6. 进阶用法:把招生就业系统从能跑推向能交付

6.1 统计接口加一层本地缓存,看板刷新不卡库

就业率看板的数据几周才变一次,但页面每次刷新都在跑 COUNT 聚合,数据量大之后会卡。可行做法是引入 Caffeine 做本地缓存,过期时间设 30 分钟,或者直接复用项目里已经有的 Redis。这个改动把接口响应从秒级拉到毫秒级,业务上没副作用,统计看板允许一定的数据延迟。

6.2 统一返回体和全局异常,省掉一半重复代码

先让所有接口返回统一结构 Result ,再用 @RestControllerAdvice 做全局异常捕获。BizException 抛出时前端能拿到明确原因,其他未预期异常统一返回“系统繁忙,请稍后重试”。这两处都是前置工作,不做的话,后期三四十个接口挨个改返回结构,成本会成倍放大。

6.3 演示交付前最后一遍检查

无论毕业设计还是给学校交付,演示前一夜我固定走一遍五件事:重建库并导入最新 init.sql;确认初始账号可用;检查统计看板数据不是空的;把前端 API 地址改成实际部署域名;用无痕浏览器从登录页走到看板,完整走一遍主流程。这套检查改变不了代码质量,却能挡掉演示现场绝大多数尴尬。从那以后我接手这类管理系统,第一件事永远是先看账号体系和权限配置,再决定从哪个模块动手;希望这个习惯能帮到你。

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

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

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

立即咨询