1. 项目全景:毕业生信息招聘平台到底在解决什么问题
1.1 这个选题为什么值得做
每年到了大四下学期,很多计算机相关专业的同学就开始为毕业设计发愁。选什么题目、做什么技术栈、写多少代码、答辩怎么讲,一环扣一环。而我今天要聊的“毕业生信息招聘平台”这个题目,属于典型的管理信息系统(MIS)类毕业设计,它最大的优势是:业务场景极其清晰、用户角色明确、数据处理流程完整,既有足够的代码量支撑论文字数,又不会复杂到让人在两个月内赶不完。
从实际需求来看,每年高校毕业季,就业信息分散在各个渠道——学校就业网、企业官网、辅导员转发的群消息、各类招聘App,学生找起来零散,企业招人也缺乏一个能直接触达应届生的窗口。毕业生信息招聘平台的出现,本质上就是把“学生—企业—学校管理者”三方角色放到同一个系统里,让职位发布、简历投递、信息审核、数据统计形成闭环。这个场景不虚拟、不悬浮,甚至很多高校确实需要这样一个内部平台,所以无论是开题答辩还是评审老师提问,它都站得住脚。
1.2 三方角色与核心业务流程拆解
任何以“平台”为后缀的系统,第一件事就是要理清角色边界,否则后面代码做到一半就容易面目全非。按照毕业设计的常规设计,这个系统分成三种登录身份:
- 学生用户:注册账号、完善个人信息、填写或上传简历、浏览职位、按条件搜索职位、投递简历、收藏职位、查看投递反馈状态。
- 企业用户:注册并提交企业资质信息、等待管理员审核、审核通过后发布职位、管理已发布职位(下架/重新发布)、查看收到的简历、对学生简历进行标记处理。
- 管理员用户:学生信息管理、企业注册审核、职位审核(过滤违规或无效信息)、岗位分类管理、系统数据统计(注册量、职位量、投递量等)。
这样梳理下来,核心业务流就很清晰了:企业发布职位 → 管理员审核通过 → 学生浏览搜索投递 → 企业查看简历并反馈 → 学生查看反馈结果。整条链路对应了数据库里的各类单据表和状态字段,后续设计表结构的时候,就是沿着这条链路展开的。
1.3 功能清单与交付物的对应关系
很多同学做一个毕设只盯着“代码写完就行”,忽略了最后交付物之间的耦合关系。实际上“毕业论文+PPT+源代码+演示视频”这几样东西,应该是互相咬合的整体,不是各做各的。以我辅导过的此类项目为例,它们的对应关系大概是这样的:
| 交付物 | 对应内容 | 演示/阐述要点 |
|---|---|---|
| 毕业论文 | 需求分析、概要设计、数据库设计、系统实现、测试 | 论文里的架构图对应系统实际模块 |
| 源代码 | 前端工程、后端工程、SQL脚本、配置文件 | 关键代码要能对应论文中的核心算法或设计 |
| 演示视频 | 按业务流程录制的系统实操 | 最好对应论文的“系统实现”章节逐模块操作 |
| PPT | 背景、技术栈、功能演示、创新点、总结 | 每页不超过3个要点,图表优先 |
这样理解之后,你做每一个交付物的时候都有了“锚点”,不会出现论文里画了三层架构图而实际代码只有一张学生表的情况。我在实际带项目的过程中见过太多“代码与论文严重分离”的例子,结果答辩时一问就露馅。所以做这个项目,第一建议就是先把业务流程和模块边界钉死,再动手写代码。
2. 技术选型:毕设系统的架构该怎么定
2.1 后端技术栈的选择逻辑
对于毕业生信息招聘平台这种典型 CRUD 系统,技术选型不需要追求花哨,但一定要能体现“工程化”的思维。目前这类毕设最主流的方案是:Spring Boot + MyBatis/MyBatis-Plus + MySQL + Redis + Vue + Element UI。我来分别说下为什么要这样组合,以及它们各自承担什么职责。
后端框架用 Spring Boot,核心原因是它简化了 Spring 家族的大量配置。Spring Boot 内置的自动装配机制,意味着你不需要写繁琐的 XML 配置文件,一个@SpringBootApplication注解就能启动整个服务。这对毕设节奏来说非常友好—毕竟我们的重点是业务实现,而不是花大量时间在环境配置上。
持久层框架方面,我建议用 MyBatis-Plus 而不是原生 MyBatis。MyBatis-Plus 提供BaseMapper接口,像selectById、selectList、insert这些常规操作直接继承就能用,能大幅减少重复的 SQL 编写。但是需要注意,不要因为有了 MyBatis-Plus 就完全放弃手写 SQL,像多表关联查询(比如“职位表 join 企业表 join 投递表”)还是需要手写 XML 映射,这部分也是论文里“复杂查询优化”可以展示的内容。
Redis 在系统里的定位是缓存和会话管理。应届生招聘平台的访问特点是:职位列表与某类热门职位的浏览量大,但数据变更频率低,非常适合缓存到 Redis 中。另外图片验证码的存储、高频访问的分页数据缓存,都可以由 Redis 承担。这样设计之后,论文里就能写“基于缓存机制优化系统访问性能”的具体实践,而不是空谈性能。
2.2 数据库设计:从ER模型到建表细节
数据库设计是整个系统的命门。很多同学一上来就建表,结果建到一半发现外键关系对不上,或者字段冗余严重。我这里把核心表结构列出来,大家可以对照自己的系统裁剪。
第一张表是t_user(用户表),这是所有角色的底座。设计上我建议用role字段区分三种身份(student/company/admin),而不是拆成三张表分别存学生、企业和管理员。原因在于:三种角色在登录认证和基础信息上有高度共性,拆表会导致每次登录都要判断走哪张表,增加冗余逻辑。但需要注意的是,学生的扩展信息(学号、毕业年份、学历、专业)和企业扩展信息(公司名称、统一社会信用代码、公司简介)差异很大,所以还需要关联独立的扩展表。
以学生用户表为例,合理的结构包含:id、username、password、email、phone、role、status(是否锁定)、create_time等基础字段。扩展的信息可以放在t_student_info表,关联user_id,字段包括real_name、student_no、school、major、education、graduation_year等。这样既保证了用户认证的通用性,又保留了角色差异化数据的空间。
第二张核心表是t_job(职位表),它承载了系统里最关键的业务数据。字段至少包括:id、company_id(关联企业)、job_name、job_type(岗位分类)、salary_min、salary_max、city、job_description(职位描述)、requirement(任职要求)、publish_status(草稿/待审核/已发布/已下架)、view_count(浏览量)、create_time、update_time。这个publish_status字段非常重要,它配合管理端的审核功能,构成了完整的职位生命周期管理流程。
第三张表是t_resume(简历表),建议采用“表单式简历为主,附件上传为辅”的设计思路。表单字段包括user_id、real_name、phone、email、education、work_experience、project_experience、skill_tags等,同时提供一个attachment_url字段存 PDF/Word 版本简历的路径。这样既方便学生在系统里快速投递结构化简历,也照顾到企业查看原始简历的需求。
第四张表t_delivery(投递记录表)负责整个投递行为的状态流转。关键字段有:id、student_id、job_id、company_id、status、create_time、update_time。status字段建议用整型数字枚举:0表示待处理、1表示已被查看、2表示已通过初筛、3表示已发面试邀请、4表示不合适。这样学生端和企业端都可以通过状态码轻松筛选投递记录,界面上用 Tag 显示不同颜色。
剩下的辅助表还包括t_collection(职位收藏表)、t_job_category(职位分类表)、t_company(企业信息表)、t_audit(审核记录表)等。设计表的整体原则是:能通过字段区分的不要拆表,能用逻辑删除的不要物理删除,能建联合索引的尽量建联合索引(比如投递表的student_id + job_id联合索引)。
2.3 前后端分离与项目目录规划
毕业生信息招聘平台这种毕设规模,工程的规范化程度直接决定了后期开发效率和代码可读性。做过实际项目的同学都清楚,最痛苦的不是写功能,而是功能写完想改的时候找不到代码在哪。所以项目目录从一开始就要规划好。
一个后端工程的标准结构大致是:
src/main/java/com/example/recruit/ ├── config/ // 配置类:拦截器、跨域、Redis序列化等 ├── controller/ // 控制层:接收前端请求 ├── service/ // 业务层:接口定义 + 实现类 │ └── impl/ ├── mapper/ // MyBatis数据访问层 ├── entity/ // 数据库实体类 ├── vo/ // 视图对象:封装响应给前端的数据 ├── dto/ // 数据传输对象:接收前端请求参数 ├── common/ // 通用工具:统一返回结果、异常处理、分页对象 └── RecruitApplication.javavo和dto这两个包非常容易被人忽略,但对毕设论文而言,它们恰恰是体现代码规范性的亮点。比如学生在前端提交职位搜索表单时,可能带有keyword、city、salaryRange等多个参数,用一个JobSearchDTO去接收就比散装参数上浏览器传参干净得多。而在响应端,你也不需要把整个t_company表的所有字段暴露给前端,用一个CompanyVO只返回需要的字段即可。
前端工程使用 Vue CLI 或 Vite 搭建,目录上按views(页面组件)、components(通用组件)、router(路由配置)、api(接口请求封装)、store(Vuex/Pinia状态管理)、utils(工具函数)等模块划分。api目录建议按业务模块拆文件,比如user.js、job.js、resume.js、admin.js,每个文件统一封装对应模块的 axios 请求方法。这样前后端接口一旦约定好,前端开发完全就是填文件调函数的事情。
3. 核心模块实现:从登录鉴权到数据统计
3.1 登录认证与JWT令牌机制
毕业生信息招聘平台里有三种角色,所以登录认证的核心是:登录接口必须能识别用户角色,并在后续请求中持续保持权限范围。
实现思路是这样的:用户输入账号密码后,后端通过BCryptPasswordEncoder对密码进行校验——数据库里存的是 BCrypt 加密后的密文,而不是明文,这是基本安全底线。校验成功后,生成一个 JWT(JSON Web Token)令牌,令牌中包含userId、username、role三个关键信息,并以签名保证不可篡改。之后前端每次请求都在请求头加上Authorization: Bearer <token>,后端通过拦截器解析令牌、获取当前用户身份。
这里要特别强调的是 Redis 与 Token 的配合。我在实际项目中习惯于把 JWT 的过期时间设置得较短(比如2小时),同时在 Redis 中维护一个login:token:<userId>的键,值为当前有效 token。一旦用户退出登录或管理员强制下线,直接删除 Redis 中对应的键,就能让 token 立即失效。这个机制对比单纯的 JWT 有一个明显的优势:JWT 本身是无法撤销的,如果不引入 Redis,你很难做到“服务端主动让一个已登录用户下线”。
在权限控制层面,先做一个实现HandlerInterceptor接口的AuthInterceptor,在preHandle方法中解析 token、校验角色。然后通过注册类将该拦截器加入到 WebMvcConfigurer 中,并按照接口路径前缀配置白名单。比如/api/auth/login、/api/auth/register以及/api/job/**(查看职位公开接口)放行,而/api/admin/**只允许role=admin的令牌访问。这样系统的安全边界就清楚了。
3.2 职位发布与检索模块的实现细节
职位发布与检索是招聘平台的核心业务,也是最容易出 bug 的地方。先看企业端发布职位的流程:企业用户在前端表单填入职位信息,点击提交后,后端JobService首先校验企业资质是否完整(企业名称、统一社会信用代码等是否已填写),然后构造Job实体,将publish_status置为1(待审核),插入数据库。管理员审核通过后,状态变为2(已发布),此时该职位才能被学生端检索到。
职位检索模块需要实现多条件组合查询。这里我用 MyBatis-Plus 的LambdaQueryWrapper来实现动态条件拼接:
LambdaQueryWrapper<Job> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Job::getPublishStatus, 2); if (StringUtils.hasText(searchDTO.getKeyword())) { wrapper.and(w -> w.like(Job::getJobName, searchDTO.getKeyword()) .or().like(Job::getJobDescription, searchDTO.getKeyword())); } if (StringUtils.hasText(searchDTO.getCity())) { wrapper.eq(Job::getCity, searchDTO.getCity()); } if (searchDTO.getMinSalary() != null) { wrapper.ge(Job::getSalaryMin, searchDTO.getMinSalary()); } if (searchDTO.getMaxSalary() != null) { wrapper.le(Job::getSalaryMax, searchDTO.getMaxSalary()); } wrapper.orderByDesc(Job::getCreateTime);这段代码的中心思想就是:有参数才拼接条件,避免了多条件查询时常见的if-else多层嵌套地狱。检索结果使用分页插件PageHelper或 MyBatis-Plus 内置的分页功能,返回统一的分页对象(总条数、当前页数据、总页数等),前端即可轻松渲染长列表。
另外一个细节值得在论文里写:热门职位的列表数据可以设置 Redis 缓存。缓存 key 形如hotJob:list:0:10(第0页每页10条),缓存失效时间设为5分钟。因为职位信息的更新频率较低,而列表浏览频率很高,缓存命中后能明显降低数据库的压力。需要留意的是,企业端一旦修改了职位信息,必须主动删除对应的缓存 key,保证数据一致性。
3.3 简历管理与投递状态流转逻辑
学生端的简历填写是整个系统中“面向学生的核心功能”。我的建议是:简历表单做成多区块分段编辑模式,比如“基本信息”“教育经历”“项目经历”“技能标签”这几个 Tab。后端提供保存草稿和提交完整版两个接口。在用户投递职位时,系统校验简历完整度——比如是否有联系方式、是否有教育经历、是否有项目描述,若缺失则提示学生完善后方可投递。
投递操作本身是典型的“先校验,后写记录,再更新计数”流程:
@Transactional public Result<?> deliver(DeliverDTO dto) { Job job = jobMapper.selectById(dto.getJobId()); if (job == null || job.getPublishStatus() != 2) { return Result.error("职位不存在或已下架"); } // 防止重复投递 Long count = deliveryMapper.selectCount( new LambdaQueryWrapper<Delivery>() .eq(Delivery::getStudentId, dto.getStudentId()) .eq(Delivery::getJobId, dto.getJobId())); if (count > 0) { return Result.error("您已投递过该职位,请勿重复投递"); } Delivery delivery = new Delivery(); delivery.setStudentId(dto.getStudentId()); delivery.setJobId(dto.getJobId()); delivery.setCompanyId(job.getCompanyId()); delivery.setStatus(0); deliveryMapper.insert(delivery); // 职位浏览量/投递量+1 jobMapper.increaseApplyCount(dto.getJobId()); return Result.success(); }注意这里@Transactional注解标明了事务边界。一旦后续代码出现异常,会触发事务回滚,不会出现“投递记录写入成功但职位投递量没加”的数据不一致问题。这个是论文写“数据一致性保证”的直接素材,也是答辩时老师爱问的一个点。
企业端查看简历列表时,同样用delivery表和resume表做关联查询。企业每查看一条简历,建议将对应delivery.status从0更新为1(已被查看),这样学生在端上看到的投递进度就会变成“企业已查看待筛选”,信息透明度提升了不少。
3.4 管理员审核模块与数据统计报表
管理员模块的核心价值是平台治理,它让系统不再是直连的“学生—企业”双边市场,而是有监督和审核机制的可管可控平台。
企业注册审核和企业发布职位审核,我在设计上统一走一个t_audit审核日志表。管理员登录后看到待办列表,点进详情查看企业资质图片或职位描述,选择通过/驳回并填写驳回原因。审核结果通过一个定时机制或实时接口更新到对应业务表中。这套流程在论文中对应“业务流程再造”或者“闭环管理设计”,也是系统特色里可以写的一笔:很多同类毕设只是做了简单的 CRUD,但它不是,它有完整的审核链。
数据统计报表使用 ECharts 在前端做可视化图表展示。后端提供四个统计接口:
- 近12个月学生注册量趋势(按月份分组统计
t_user表的创建时间) - 职位发布量 Top 5 企业排行(
t_job按company_id分组统计) - 各岗位类别投递占比(联表
t_job_category与t_delivery) - 网站数据总览(总学生数、总企业数、总职位数、总投递数)
这些统计接口需要注意 SQL 的写法效率。比如按月份分组统计,可以使用 MySQL 的DATE_FORMAT(create_time, '%Y-%m')函数格式化日期,再配合GROUP BY分组。不要傻乎乎地在 Java 里把所有数据查出来再遍历算时间,又慢又难看。写 SQL 时加个EXPLAIN观察执行计划,确保统计查询走了索引。
4. 毕业论文、PPT与演示视频的打磨经验
4.1 论文各章节该怎么布局才不容易被挑刺
毕业论文本质上是对你整个毕业设计工作的书面呈现,所以它的章节顺序和系统开发流程是一一对应的。很多同学写论文喜欢从网上找个模板直接往上套内容,这种做法最大的问题是内在逻辑断裂。我的建议是严格按照下面的结构来:
- 第一章 绪论:写项目背景、研究意义、国内外招聘平台发展现状综述。注意这部分不要长篇大论堆概念,重点突出“针对普通高校毕业生的招聘管理与数据服务存在哪些不足”,从而引出本系统的必要性。
- 第二章 相关技术介绍:逐项说明 Spring Boot、MyBatis、MySQL、Redis、Vue.js 等技术的核心特点和选型理由。在这一章里加入“技术选型对比表”是非常加分的设计——比如用表格对比 Spring Boot 与 SSH/SSM 在自动化配置、开发效率、生态支持方面的差异。
- 第三章 系统需求分析:画用例图、功能模块图、业务流程图。用例图建议用 PlantUML 或者 Draw.io 来画,尽量规范;非功能需求部分包括系统安全性、可靠性、易用性、可维护性,每一小点要有对应设计支撑。
- 第四章 系统设计:重点是架构图、总体功能模块设计、数据库ER图和数据表设计说明。表结构描述要用规范的表格列出字段名、类型、约束和说明,不要截图数据库可视化工具的界面。
- 第五章 系统实现:分角色展示核心功能的实现界面和关键代码。注意这里展示的代码一定要是真实项目代码的精简版,并加上注释。页面截图要处理干净,不要留浏览器的书签栏或者其他个人信息。
- 第六章 系统测试:写测试方法和测试用例结果。分别列出功能测试用例表、性能测试结果(如接口响应时间压缩前后的比较)、兼容性测试说明。
论文里的图表统一编号也很重要,插图用“图4-1 系统总体架构图”这样的格式,表格用“表4-2 用户表设计”这样的格式,每一个图表在正文中必须有引用。这是高校论文格式规范的硬性要求,提前对照本校规范做才能避免后期返工。
4.2 PPT的展示逻辑与答辩节奏控制
做答辩 PPT 的时候,最容易出现的毛病就是“把论文粘贴到 PPT 上”。实际上,答辩 PPT 的核心作用不是罗列细节,而是在最短时间内向评委证明你做了什么、怎么做出来的、结果是什么样的。
我建议 PPT 控制在 15-20 页以内,按这样的节奏推进:封面页(题目、姓名、导师)→ 目录页 → 项目背景与意义(1-2 页)→ 核心功能展示(3-5 页,每页展示一个模块的截图+简述)→ 系统架构与技术栈(1-2 页)→ 数据库设计(1 页 ER 图+核心表结构说明)→ 创新点与难点解决(2 页)→ 系统测试结果(1 页)→ 总结与展望(1 页)。其中“创新点与难点解决”是答辩提分的核心页面,需要重点打磨。比如你可以写“为了提升投递效率,设计了基于 Redis 的职位缓存机制,接口响应时间平均下降 60%”,这样具体的数字远比空洞的“系统性能良好”有说服力。
答辩的时候还有一个细节:老师特别喜欢打断演示、追问细节。所以 PPT 上任何一张截图,你自己都要提前准备好它的“背景故事”——这段代码是怎么实现的、这个状态是如何转换的、这个异常是怎么处理的。我接触过不少学生,PPT 做得非常漂亮,但到了提问环节就开始含糊其辞。如果时间允许,建议准备一份儿“答辩自问自答清单”,把老师最可能问的 10 个问题写下来逐条演练。
4.3 演示视频录制:别让细节毁掉整体印象
演示视频是今年很多学校线上答辩或提交毕设材料的必备材料,它看似只是一个“录屏”,但录得好不好,直接影响老师对你项目完成度的第一印象。我用过几款录屏工具,这里做个简单对比参考:
| 工具 | 适用场景 | 注意事项 |
|---|---|---|
| OBS Studio | 免费、开源、画质调节灵活 | 需要自行设置输出分辨率(建议 1080p) |
| Bandicam | 操作简单、自带鼠标特效 | 免费版会挂水印,需要激活 |
| Screen Recorder(系统自带) | Win11/Mac 自带录制 | 画质固定,无额外调参选项 |
录制内容建议按业务流程走三条主线:第一条是学生端流程——注册登录、完善简历、搜索职位、投递简历、查看进度;第二条是企业端流程——企业注册、等待审核、发布职位、查看收到的简历;第三条是管理端流程——登录后台、审核企业、审核职位、查看数据统计图表。三条线正好覆盖系统的全部核心功能,每一段都配合旁白讲解操作意图。
录视频时有几个非常关键的经验:
- 录制前把浏览器窗口调整好,不要出现重叠窗口或多余标签页。提前清理浏览器的无关书签和插件,显得整洁专业。
- 旁白的语速要平缓,每做一个操作前先说出“我要做什么”,然后再操作。比如“现在我以学生身份登录系统,进入简历管理页面”,这样录出来的视频逻辑节奏感会更强。
- 操作过程中如果点错了不要慌张,不要一错就切掉重录,尽量在同一个视频片段里自然地纠正错误。万一录到一半发现出现关键问题,宁可重新录制该段,也不要留给后期剪辑——大多数同学没有时间精剪视频。
另外,演示视频的分辨率至少设置为 1920×1080,视频时间控制在8-12分钟比较合适。太长显得拖沓,太短则可能遗漏功能展示的完整性。视频末尾还可以放一个简单的字幕总结,比如“以上就是本系统的完整演示,谢谢观看”,观感会专业很多。
5. 开发调试中容易踩的坑与排查经验
5.1 前后端分离联调过程的经典问题
前后端分离模式下,最常见的坑就是接口请求跨域问题。浏览器默认会拦截跨源请求,所以你需要在后端配置跨域规则。做法是在后端写一个CorsConfig配置类,使用CorsRegistry注册允许跨域的路径规则:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }配置好后,前端 axios 里还要确保携带了withCredentials: true,否则 cookie 等凭证信息不会随请求发送。我见过一个项目,前端登录成功后回调里说“欢迎您”,但后续所有请求都报 401,起初以为是 token 没存对,排查了大半天才发现是跨域配置中allowCredentials(true)跟allowedOriginPatterns("*")的兼容性问题。
另一个频繁出现的坑是:前端拿到的数据格式和后端返回的统一结构不一致。建议后端设计一个统一的Result<T>响应类,包含code、message、data三个字段。所有接口都返回这个结构,不要有的接口直接返回裸对象、有的返回 Page 对象。这样前端 axios 拦截器里就可以统一处理错误提示和 token 过期跳转,省去了每个页面单独写 try-catch 的麻烦。
5.2 业务逻辑中的边界问题
边界问题在招聘平台里非常典型。举几个我自己辅导项目时遇到过的例子:
第一个问题是重复投递。学生手一抖,连续点了两次投递按钮,系统就生成了两条投递记录。除了在接口代码里加重复性校验(也就是前面的selectCount逻辑),前端按钮在提交后也应该立刻置灰(禁用)并显示“已投递”。前后端双层防护才能万无一失。
第二个问题是职位下架后已有投递记录的处理。企业将职位下架后,学生端应该看不到该职位,但历史投递记录依然需要保留给企业和学生查询。这就要求列表查询时使用job.publish_status关联过滤,但详情页查询投递记录时不能过滤,否则学生会在“我投递的职位”里看到一片空白,造成困惑。解决方式是投递记录的列表接口返回job的快照信息(职位名称、薪资范围),即使职位已下架,快照仍然能正常展示。
第三个问题是时间字段的时区处理。数据库使用 MySQL 时,create_time默认是数据库服务器的当前时间,如果你本机服务器时区设置不对,插入的时间记录可能和实际时间差8个小时。解决办法是在数据库连接 URL 中加上serverTimezone=Asia/Shanghai参数,并在 JDBC 连接串里显式指定characterEncoding=utf8,避免中文乱码和时间错位。
5.3 接口响应速度与安全性细节
代码写完后,我习惯对整个系统做一轮简单的性能体检。核心关注两个指标:普通列表接口的响应时间(应该控制在 200ms 以内)、涉及复杂多表查询的接口响应时间(应该控制在 1s 以内)。如果发现某个接口响应特别慢,优先排查两件事:SQL 是否命中索引、是否因为 N+1 问题导致一条记录一次查询。
N+1 问题在 MyBatis-Plus 里特别容易发生。比如你查出一页 10 条投递记录,然后遍历每条记录去查对应职位的名称,这样就是 1 次查询 + 10 次子查询。优化的办法是用 SQL 联表查询一次取出所需的数据,或者使用 MyBatis 的collection标签进行关联映射。SQL 优化这个点我在论文里特地写了一段,因为它是体现系统设计深度的有力证据。
在安全层面,密码必须 BCrypt 加密存储,这是底线中的底线,千万不要用 MD5 加一个固定盐值就完事,MD5 在今天的算力下存在彩虹表碰撞风险。前端登录页面加入人机验证比较好(简单图片验证码即可),防止刷接口暴力破解。管理员接口一律要求 token 中role=admin,不能只靠前端路由隐藏页面来“保护”。
5.4 配置与部署的经验备忘
项目收尾阶段要将系统在答辩现场运行或录制视频,这时环境一致性就成了最现实的痛点。建议所有依赖都固定在明确版本:pom.xml或package.json里的依赖版本不要用SNAPSHOT,MySQL 使用 8.0 以下或以上的版本要注意驱动差异。把整个项目的环境搭建过程(数据库建库脚本、Redis 启动、后端启动命令、前端启动命令)整理成一个README.md文件。这不仅是交付物的专业度体现,更重要的是万一你换了电脑演示,照着 README 操作几分钟就能把环境恢复起来。
部署方面推荐一个低成本方案:本地开发用 IDEA 启动 Spring Boot 服务 +npm run serve启动前端开发服务器,联调完成后,将前端的 API 请求地址从http://localhost:8080改为相对路径或通过开发服务器的代理转发,演示时会更加稳定。整个部署不要上云服务器——除非你有多余的时间去处理公网安全组、域名备案等额外事务,否则会拖累答辩进度。
我在实际带项目的过程中还有一个私藏经验:每次改完代码或跑完一轮测试,都用 git 提交一次。哪怕只有一个人开发,Git 也相当于一个“后悔药”存档点。答辩前万一哪次改动把系统搞坏了,一条git revert就能回退到之前稳定运行的版本,这种安全感能让你的答辩准备从容很多。这也算除了代码本身之外,毕设让你提前养成的最有价值的职业习惯了。