☰
SpringBoot+Vue全栈实战:校园招聘求职平台毕业设计完整方案
2026/9/26 11:37:57 网站建设 项目流程

每年毕业季,我身边都有不少计算机专业的同学在选题和开发这一步反复纠结。今天要聊的这套“一网寻职”校园学生网,就是一个非常典型的Java毕业设计方向——基于SpringBoot+Vue的校园招聘求职一体化平台。它把大学生就业信息和企业人才对接整合在一个系统里,覆盖前端展示、后端业务、数据库设计、接口联调全链路,技术点很贴合主流Java岗位的日常开发,作为毕业设计选题既有实际应用场景,又能向面试官展示全栈能力。

这套系统适合谁参考?一是正在做类似选题、需要完整思路和技术方案的同学;二是想把SpringBoot和Vue真正串起来做一个完整项目的初学者;三是准备Java面试、需要项目经验来支撑简历的人。下面我就把整个项目的设计思路、核心实现、关键难点和踩坑记录完整拆一遍,你照着这条路线走,少走很多弯路。

1. 项目定位与需求拆解——搞清楚“一网寻职”到底在做什么

1.1 盘点校园招聘的三个痛点

动手写代码之前,先想清楚一个问题:校园招聘场景里,最大的痛点是什么?

我总结下来有三点。第一,信息分散。企业的招聘信息散落在各种群聊、海报、就业网站里,学生要反复切换渠道才知道哪里有岗位,很多机会因为信息不及时就错过了。第二,流程割裂。投递简历、查看进度、接收面试通知,每一步都是割裂的,学生不知道自己的简历到底有没有被查看。第三,数据不闭环。学校就业办想统计就业率,企业想筛选优质毕业生,双方没有统一的数据入口,汇总全靠人工。

所以这类校园招聘平台的核心价值,就是用一个系统把学生、企业、管理员三方的需求统一起来。在这个基础上,我再把功能拆成角色权限、简历管理、职位发布与检索、投递管理、消息通知、数据统计几大块,每一块都有明确的业务目标,而不是简单做一个“增删改查”的架子糊弄答辩。

1.2 三类用户角色与核心功能拆分

“一网寻职”这类平台最合理的角色拆分就是三方:学生、企业、管理员。三方权限边界清楚,功能各自独立又相互关联,数据模型也能串起来。

学生端功能相对核心。注册登录是基础,登录后需要能维护在线简历,简历内容不是简单的文本域,而是分块的:基本信息、教育经历、项目经历、实习经历、技能标签、自我评价,每一块都单独录入和修改。职位检索要有,支持按关键词、城市、岗位类型、薪资范围多条件过滤。投递和收藏是学生的日常操作,投递后要能看到投递记录和状态流转——企业未查看、已查看、已发送面试邀请、已录用、已拒绝,每一步学生都要有感知。

企业端的核心是职位和简历筛选。企业注册后要经过管理员审核才能发布职位,发布职位时要填写岗位名称、招聘人数、学历要求、薪资区间、工作地点、职位描述,这些字段后面会直接影响检索和推荐。企业还需要能浏览主动投递过来的学生简历,并且能对简历进行标记,比如“已查看”“已下载”。这两个操作看似简单,实际上对权限设计和数据表设计都有要求。

管理员端负责全局把控。用户管理涵盖学生和企业账号的启用与禁用;职位审核负责过滤掉重复、虚假或不合规的招聘信息;数据统计需要能按学院、按专业、按时间维度展示就业数据,这个模块在论文里特别好写,因为能体现系统分析的深度。

基于这三个角色,我还要在功能清单里加一个容易忽略的东西——消息中心。企业发送面试邀请,学生收到站内信;学生投递简历,企业收到投递提醒。这个功能虽然不起眼,但能显著提升系统完整度,答辩时也容易讲出彩。

1.3 技术选型:为什么是SpringBoot + Vue而不是别的组合

选题阶段很多人会纠结技术栈。我一贯的建议是:毕业设计不求技术最前沿,求的是体系完整、你能讲透原理、跑得通完整链路。SpringBoot + Vue这个组合恰好满足这些条件。

后端选SpringBoot,不选SSH或者SpringMVC老项目,理由很直接。SpringBoot解决了Spring配置地狱的问题,内嵌Tomcat,一个main方法就能启动,前后台分离开发的模式也更贴近企业实际。而且SpringBoot生态极其丰富,MyBatis-Plus、Spring Security、JWT认证、POI导出表格,全都是现成的starter和文档,对毕业设计这种周期固定的项目来说,能省下大量和环境做斗争的时间。

前端选Vue,核心原因是组件化开发和成熟的配套工具链。Vue的响应式数据绑定写起来直观,Element UI、Element Plus这些组件库能直接拿出表格、表单、下拉选择、日期选择器、分页组件,开发效率非常高。再加上Vue Router做路由管理、Axios做HTTP请求、Pinia或Vuex做状态管理,前后端联调体验非常顺滑。

数据库方面我推荐MySQL配MyBatis-Plus。MySQL是毕业设计标配,兼容性最好;MyBatis-Plus在MyBatis基础上升级了CRUD能力,内置分页插件和LambdaQueryWrapper,写复杂条件查询时不用手拼大量的XML,代码可读性高很多。这个组合对刚接触项目开发的同学非常友好,还能在论文里理直气壮地分析它的优势。

2. 系统设计与数据库建模——先把地基夯实

2.1 前后端分离架构与RESTful接口约定

架构上不搞微服务,卒业设计也没必要。我用的就是经典的前后端分离单体架构:前端Vue项目独立部署,后端SpringBoot提供RESTful接口,通过HTTP协议交互。数据格式统一使用JSON,前端通过Axios携带Token请求接口,后端通过拦截器校验Token,再根据角色做权限控制。

接口设计要遵守一个简单的约定:路径按资源命名,动词交给HTTP方法。比如查询职位列表就GET /api/positions,发布职位就POST /api/positions,修改简历就PUT /api/resumes/{id},删除投递记录就DELETE /api/deliveries/{id}。遵循这套规范,前后端联调时不用反复争论路径格式,接口文档也自然清爽。

这里有一个我特别想强调的经验:接口返回的数据结构必须统一。我项目里定义了一个全局响应类,包含code、message、data三个字段。后端所有接口都返回这个结构,前端Axios拦截器统一判断code,为200就放行,为401就跳转登录页并弹出提示。这样全局的错误处理就有了统一入口,而不是每个页面单独写try-catch,代码量直接砍掉三分之一。

2.2 核心数据表设计与关联关系

数据库设计这一步千万别马虎,表设计不合理,后面写业务代码会处处别扭。我按模块把核心表拆成六张:用户表、简历表、企业信息表、职位表、投递记录表、收藏表,再辅以消息表、系统通知表、职位类别字典表。

用户表是最基础的一张表,字段我建议这样设计:id主键、username登录名、password加密后的密码、real_name真实姓名、role角色(学生/企业/管理员)、status账号状态、avatar头像路径、mobile手机号、email邮箱、create_time创建时间。角色字段用字符串存储最简单,配合后端拦截器判断即可。

简历表要存学生的详细信息,字段包括id、user_id关联用户表、education最高学历、school_name学校名称、major专业、graduation_year毕业年份、phone联系电话、email联系邮箱、skill_tags技能标签、self_evaluation自我评价、file_path简历附件路径、update_time更新时间。这个表就成为一个学生的完整画像,企业查看简历时直接读取这里的字段,不需要额外拼表。

企业信息表单独建一张,字段包括id、user_id关联用户表、company_name企业名称、industry所属行业、scale公司规模、address办公地址、description企业简介、license_path营业执照图片路径。职位表关联企业信息表,字段包括id、company_id、title职位名称、category岗位类别、salary_min最低薪资、salary_max最高薪资、education_requirement学历要求、work_city工作城市、description职位描述、status状态(待审核/已上架/已下架)、create_time发布时间。

投递记录表和收藏表实现关联功能。投递记录表字段有id、student_id、position_id、company_id、status(待查看/已查看/已邀约/已录用/已拒绝)、create_time。收藏表字段有id、student_id、position_id、create_time。两张表都用联合字段定位一条记录,查询时用唯一索引保证同一个学生对同一个职位只能投递一次或收藏一次。

表与表之间的关系,用一个简单的话说:用户表是“人”的底座,企业信息表是企业用户的扩展,简历表是学生用户的扩展,职位表挂在企业信息表下面,投递记录表是学生和职位的多对多关联表,收藏表则是学生与职位的轻量级关联。理解了这层关系,数据库物理建模基本就清晰了。

2.3 统一响应结构与全局异常处理

后端项目里我最不看好的情况就是每个接口里各种try-catch、返回不同类型的Map或Object,这会导致前端拿到的数据结构参差不齐,联调起来非常痛苦。

我的做法是在项目启动阶段就定义好统一响应类Result,静态方法提供成功和失败两种构造方式。成功时返回Result.success(data),失败时返回Result.error(code, message),同时配合全局异常处理器@RestControllerAdvice,把业务异常、参数校验异常、未知异常全部拦下来,转换成统一错误结构返回给前端。

这里有一个很容易被忽视的细节:全局异常处理器的优先级和捕获范围。为了让参数校验异常返回更友好的提示,我会在校验异常处理器里读取BindingResult中的字段错误信息,拼成一句完整的话返回给前端。比如“学历要求不能为空”而不是一串堆栈信息。细节上再打磨一下,答辩时老师会觉得你的系统工程化水平不错。

3. 核心功能模块实现——从登录到投递的完整链路

3.1 基于JWT的登录认证与权限控制

登录认证是这个项目的第一个硬骨头,也往往是面试必问的点。我选的是JWT方案:用户登录成功后,后端生成一个包含用户ID、角色、过期时间的Token返回给前端,前端把Token存到本地,后续每次请求都在请求头带上Authorization: Bearer <token>。后端用一个自定义拦截器拦截需要认证的接口,解析Token并校验有效期,如果Token非法或过期就返回401。

JWT方案比起传统的Session方案,最大优势在于无状态。服务器不需要在内存或Redis里保存用户的会话信息,横向扩展时每个服务实例都能独立校验Token。毕业设计虽然用不到集群部署,但把这个优势写进论文里,能体现你对认证机制的理解深度。

权限控制方面,我用拦截器加角色判断的方式。自定义拦截器拦截所有/api/**请求,解析Token后从认证上下文拿到当前用户的角色,然后判断该角色是否有权限访问当前接口。比如发布职位的接口,只有企业角色能访问;职位审核接口,只有管理员角色能访问。前端再配合路由守卫控制页面跳转,Vue Router里用beforeEach判断当前用户的角色字段,没有权限就重定向到首页或403页面。这就是双层权限控制,前端限制“看不到”,后端限制“进不来”,两者缺一不可。

3.2 职位检索:关键词匹配 + 分页 + 筛选

职位检索是学生端的核心入口,也是后端查询逻辑的集中体现。需求看起来简单,就是输入关键词、选择条件、点击搜索,但实现起来牵扯到多条件动态查询和分页。

我用的MyBatis-Plus分页插件解决分页问题。集成方式是配置一个MybatisPlusInterceptor,添加PaginationInnerInterceptor,然后业务层调用Page对象配合LambdaQueryWrapper查询即可。分页插件会自动生成带LIMIT的SQL语句,返回的IPage对象里直接带着total总记录数、records当前页数据、current当前页码等字段,前端组件直接使用。

多条件动态查询用LambdaQueryWrapper来实现最合适。比如用户传了关键词、城市、学历要求三个条件,代码大概长这样:

LambdaQueryWrapper<Position> wrapper = new LambdaQueryWrapper<>(); if (StringUtils.isNotBlank(keyword)) { wrapper.and(q -> q.like(Position::getTitle, keyword) .or().like(Position::getDescription, keyword)); } if (StringUtils.isNotBlank(city)) { wrapper.eq(Position::getWorkCity, city); } if (StringUtils.isNotBlank(education)) { wrapper.eq(Position::getEducationRequirement, education); } wrapper.eq(Position::getStatus, 1); wrapper.orderByDesc(Position::getCreateTime); Page<Position> page = positionMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);

这段代码的逻辑核心有两个。第一,if判空后动态拼接查询条件,传入的参数为空就不拼该条件,避免了写N个固定SQL的尴尬;第二,用and包裹关键词的模糊匹配,确保关键词范围限定在标题和描述两个字段里,而不是把整个表的所有字段都like一遍。关键词搜索性能在数据量小的时候没有问题,后期要优化可以在title和description字段上加全文索引或改用Elasticsearch——这是扩展方向,论文里提一句就行。

3.3 简历在线编辑与文件上传

简历这个模块的难点在于它分块很多,而且可编辑性要求高。我的方案是前端把简历拆成一个多Tab表单组件,每个Tab对应一块内容,比如基本信息、教育经历、项目经历、技能标签等,用户填完当前Tab后自动保存到后端。这里要注意自动保存的防抖处理,用watch监听表单变更后延迟两秒发送请求,避免用户每敲一个字就触发一次接口调用。

技能标签用标签选择器组件处理,用户从预设标签里选取,也能手动输入自定义标签。后端存储用字符串拼接方式,比如“Java,SpringBoot,MySQL”,读取时用逗号分割。虽然不够规范,但上字的快、实现简单,对毕业设计完全够用。

文件上传这里要处理两类文件:学生上传简历附件和头像,企业上传营业执照。将文件保存到服务器的本地目录,而不是直接存数据库的BLOB字段,是实践中的常规做法。数据库只保存文件的访问路径,后端通过一个全局配置映射,把/upload/**路径映射到磁盘目录,这样浏览器直接用路径就能访问图片和文件,实现简单高效。

上传接口有一个坑值得注意:前端以multipart/form-data格式传文件,后端接收时用MultipartFile类型接参。本地存储时用UUID生成新文件名,并且带上原始扩展名,避免文件名重复覆盖和中文文件名乱码。具体逻辑是:

String originalFilename = file.getOriginalFilename(); String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); String newFileName = UUID.randomUUID() + extension; file.transferTo(new File(uploadDir + newFileName));

上传完成后返回/upload/xxx.pdf这样的访问路径,前端拼接完整地址展示预览。这样处理的最大好处是,前端HTML里直接<img :src="avatarUrl">就能显示图片,完全不需要写后端读文件流的接口。

3.4 投递、收藏与状态流转

投递和收藏逻辑不复杂,但容易踩到重复提交的坑。学生在同一个职位页面上快速点两次“投递”,后端第一次投递完成后第二次可能又插一条记录。我处理的办法是在投递记录表上建联合唯一索引(student_id, position_id),数据库中直接约束同一个学生对同一职位只能有一条有效投递记录,同时后端在插入前先查一次是否已存在。两层保险下,重复投递问题彻底解决。

投递状态流转是这里面的一个小亮点。我给每个状态定义一个整型值:0表示待查看,1表示已查看,2表示已邀约,3表示已录用,4表示已拒绝。学生投递成功时状态为0,企业打开简历详情时后端自动把状态改为1,企业点击邀请面试按钮后状态变为2,后续录用或拒绝根据企业操作对应修改。每次状态变更同步向消息表插入一条站内信记录,学生登录后就能在消息中心看到“企业已查看你的简历”“企业向你发送了面试邀请”等提示。

消息中心这个功能很多同学会忽略,但它恰恰能体现系统完整性。投递后状态更新、企业处理后反馈、管理员审核结果确认,全部通过消息中心推送,学生不用反复刷新页面去猜进展。这个模块用一张messages表就够了:id、user_id接收方、content消息内容、is_read是否已读、type消息类型、create_time创建时间。用户登录后,后端提供一个“未读消息数量”接口,前端在导航栏顶部的徽标组件上显示数字,实时提醒,交互感和真实产品非常接近。

4. 关键难点与优化方案——让答辩有亮点

4.1 文件上传与访问:本地存储方案详解

文件上传我已经写了基本方案,这里补充一个部署时的细节。开发阶段和后端部署阶段,本地上传路径我建议统一用绝对路径,并且把路径配在application.yml里,比如:

file: upload-dir: D:/upload/ # Windows环境示例,Linux环境一行改掉

业务代码里通过@Value("${file.upload-dir}")注入,上传时创建目录再保存文件。之所以强调配置文件管理,是因为开发时你用本机路径,部署到服务器后要改成/home/xxx/upload,如果不走配置文件,每次部署都要重新编译或者改代码,麻烦程度拉满。

文件访问映射也要配好。SpringBoot里实现WebMvcConfigurer接口,重写addResourceHandlers方法,把/upload/**映射到磁盘路径:

@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceResolver(new PathResourceResolver()) .addResourceLocations("file:" + uploadDir); }

配置完成后,浏览器直接访问http://localhost:8080/upload/abc.pdf就能打开文件。这块配置是很多同学忽略的,以为保存文件就结束了,结果前端图片404,排查半天才发现是资源映射没配。

4.2 安全防护:密码加密与XSS过滤

安全是毕业设计答辩时很容易被追问的点。我至少会做好两件事:密码加密和XSS过滤。

密码加密我用的BCrypt算法。Spring Security里自带BCryptPasswordEncoder,也可以单独引入spring-security-crypto包,调用encoder.encode(password)保存,校验时调用encoder.matches(rawPassword, hashedPassword)。BCrypt的优点是每次哈希值都带随机盐,同样的密码两次加密结果完全不同,即使数据库泄露,攻击者也无法通过彩虹表反推明文。这是业界标准做法,答辩时提出来非常加分。

XSS过滤方面,前端应对与后端防御要联动起来。尤其是允许用户输入富文本内容的页面,比如企业发布职位描述、学生填写自我评价,都不能把输入直接当HTML渲染。前端设置优先使用纯文本展示内容,特殊字符如<、>、&自动转义为HTML实体。后端在全局过滤器层面做一层拦截,把请求参数中的危险字符替换成安全字符。

我实现的思路是注册一个自定义Filter,继承OncePerRequestFilter,对request.getParameterMap()中的所有参数值执行消毒逻辑,把<script>、javascript:等危险片段替换为空或转义。这里有一个细节:直接修改request.getParameterMap()是做不到的,因为它是不可变的。实际做法是自定义一个HttpServletRequestWrapper,重写getParameter和相关方法,在返回前先执行消毒。这个wrapper在过滤器中包装原请求,再传递给后续链路。代码框架大致是这样:

public class XssFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { filterChain.doFilter(new XssRequestWrapper(request), response); } }

其中XssRequestWrapper负责在getParameter、getParameterValues、getInputStream、getReader等方法里统一进行HTML转义。注意,富文本字段和纯文本字段的过滤强度可能不同,所以要给富文本字段留一个白名单机制,避免把正常的业务描述给过滤掉了。这块需要结合你的实际字段来调整,建议先从纯文本开始,跑通后再考虑富文本放行。

4.3 性能优化:分页查询、缓存与索引

毕设项目数据量不大,性能问题不是主要矛盾,但论文里写一段优化分析会显得你考虑全面。

第一层优化是SQL层面的索引。职位表的status字段和work_city字段经常出现在查询条件里,简历表关联用户查询频繁用到user_id,投递记录表联合查询常用(student_id, position_id),这些字段建上普通索引就够了。注意一个原则:索引不是越多越好,每次写入都要同步更新索引,现在建索引要权衡查询频率和写入频率。

第二层优化是热点数据的本地缓存。比如职位类别字典数据,几乎没有变化,每次查询都打数据库完全是浪费。我用一个Map<String, List<String>>在系统初始化时加载到内存里,查询时直接从Map取。如果数据量再大,可以用Caffeine或Redis做缓存,给缓存设置过期时间,这个方案写进论文也是一个很好的扩展点。

第三层优化是前端层面的分页和懒加载。职位列表用分页组件,页大小设置为10,切页时重新请求接口。简历附件列表用懒加载图片组件,滚动到视口时才加载资源,减少首屏请求数量。这些前端优化方案虽然不起眼,但能展示你不仅有后端思维,也有用户视角的性能意识。

4.4 常用工具扩展:导出表格与统一数据统计

校园招聘平台还有一个高频需求:数据导出。管理员需要把某个时间段内的投递数据导出成Excel表格,企业需要把收到简历的列表导出备份。这里我用的Apache POI解析。

POI生成Excel表格的基本流程是:创建工作簿XSSFWorkbook,创建工作表Sheet,创建行Row,创建单元格Cell并填充数据,最后把工作簿写入HttpServletResponse的输出流。写一个通用导出工具类,传入表头和数据集就能生成表格。这里有一个细节:导出接口的请求类型是POST,因为查询条件可能比较复杂,参数放在请求体里传给后端,前端用一个Blob方式接收文件流并触发浏览器下载。需要设置响应头Content-Disposition: attachment; filename=xxx.xlsx,否则浏览器可能直接打开文件流而不是下载。

数据统计模块我也用了简单套路。管理端首页展示几张统计卡片:学生总数、企业总数、职位总数、投递总数,四个统计用四条SQL一查就完。再向下一层是近六个月的投递趋势折线图,后端按月份分组统计投递记录数,返回前端一组[月份, 数量]格式的数据,前端用ECharts画图,效果一目了然。这个模块在答辩演示时视觉效果很好,能直观展示系统的统计分析能力。

5. 常见问题与排查技巧实录——毕业设计踩坑合集

5.1 环境搭建阶段的经典坑位

我见过太多同学把时间浪费在装环境和启动报错上,这里把最高频的几个问题整理出来,你对照排查。

第一个是Node版本问题。Vue3项目一般要求Node.js 16以上,但很多电脑上装的是旧版Node,导致npm install报错或者npm run serve启动失败。解决方法是去Node官网下最新LTS版本重装,或者用nvm管理多版本Node。装完后在终端执行node -v和npm -v确认版本号。

第二个是SpringBoot版本和Java版本不匹配。很多同学用最新版SpringBoot 3.x,但本地JDK还是8,启动直接报UnsupportedClassVersionError。SpringBoot 3.x必须搭配JDK 17以上,如果你熟悉的是JDK 8,稳妥做法是用SpringBoot 2.7.x版本,这个版本支持JDK 8到JDK 17,兼容性最广。选版本时先确认自己电脑的JDK版本,用java -version查看后再决定。

第三个是Maven依赖下载过慢或失败。国内环境使用阿里云Maven镜像可解决:

<mirror> <id>aliyun</id> <mirrorOf>central</mirrorOf> <url>https://maven.aliyun.com/repository/public</url> </mirror>

配置完后再执行mvn clean install,依赖下载速度会有明显提升。这里特别提醒,不要在一个项目里混用多个Maven仓库源,否则容易出现依赖冲突。

第四个是npm安装依赖时卡在某个包上。我的经验是优先设置淘宝镜像源:

npm config set registry https://registry.npmmirror.com

装完之后再把镜像切回来也来得及,切回官方源执行npm config set registry https://registry.npmjs.org即可。如果某个特定包一直安装失败,可以单独尝试npm install 包名 --force来绕过缓存冲突,但先不要一上来就强制安装,优先清缓存。

5.2 联调阶段的跨域与接口问题

前端工程跑在localhost:8080,后端跑在localhost:9090,跨域问题不可避免。前后端分离项目最常见的就是这个错:

Access to XMLHttpRequest at 'http://localhost:9090/api/xxx' from origin 'http://localhost:8080' has been blocked by CORS policy

解决办法有两种。开发阶段,在后端配置CORS跨域策略,我用的WebMvcConfigurer加CorsRegistry:

@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:8080") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true); }

注意allowedOrigins要指定前端真实地址,不要直接写*,因为allowCredentials(true)模式下不能同时使用*。生产环境部署后,如果前后端同域或通过Nginx代理,CORS配置就不需要了,这块讲道理容易理解。

联调阶段的另一个经典问题,是前端拿到接口数据后页面不渲染。大部分人第一反应是后端接口有问题,但我排查过的案例里,百分之七十是前端写法问题。用Vue3的ref或reactive声明响应式数据时,赋值直接用response.data整体覆盖是没问题的,但如果你用Object.assign给一个深层对象加属性,或者直接给数组按索引赋值,Vue的响应式系统可能监听不到变化。遇到这种情况,先把JSON.stringify(response.data)打印出来,确认接口数据确实拿到了,再检查赋值方式是否响应式,一般问题立刻浮现。

另一个联调坑是接口返回JSON里包含时间格式的字符串,比如2024-01-15T08:30:00.000+08:00,前端直接显示会很难看。稳妥方案是后端在统一响应结构里使用@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")控制时间格式,前端再配合dayjs或moment格式化显示,两层配合确保界面干净。

5.3 部署上线:前后端分离打包与运行

如果你的毕设要求在服务器上部署演示,你需要掌握最基本的打包和部署流程。

后端打包用Maven执行mvn clean package -DskipTests,在target目录下得到xxx.jar,通过java -jar xxx.jar运行。重点提醒一下:打包前先把application.yml里的数据库连接、文件上传路径、端口号改成服务器上的真实配置,否则直接用本地配置启动会连不上数据库。数据库连接串用服务器IP或云数据库内网地址,账号密码也要独立配置,不要在代码里写死。

前端打包执行npm run build,生成dist目录。部署方式有几种,最简单的是把dist目录丢进Nginx的html目录,配置Nginx转发接口请求到后端服务。Nginx配置示例:

server { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:9090; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

这里有两个细节容易出问题。第一,try_files $uri $uri/ /index.html;这段配置必须写上,否则前端项目用的是Vue Router history模式,刷新页面时会出现404——因为前端路由是虚拟路径,实际文件系统里不存在。第二,/api/前缀的请求要转发给后端,否则所有接口请求都会落到Nginx找不到对应文件。

如果不想折腾Nginx,也可以选择更省事的方案:前端打包后由Nginx托管,后端直接java -jar启动,二者网络打通就完事。但答辩时讲到部署方案,能把Nginx反代、前后端分离下载这些点讲清楚,明显比只说“双击jar包运行”更有说服力。

在部署过程中,我还遇到过服务器上端口被占用的问题。排查方法是执行netstat -tlnp | grep 9090查看谁占用了端口,找到占用进程后kill掉,或者改端口重配Nginx。杀进程时注意确认一下是不是自己的误操作,别误杀了系统的关键进程。

5.4 面试追问里的高频问题准备

毕设做完接着就是找工作,答辩时老师或面试官针对这个项目的追问,我整理几个高频点,提前准备不慌。

第一个追问很经典:“你的JWT存在什么安全风险?”回答要点是:JWT只有签名没有加密,Payload部分用Base64编码,任何人拿到Token都能解码看到里面的信息,所以不要在Payload里存敏感数据。同时设置合理过期时间,比如2小时,过期后前端用刷新Token去重新换新Token。能把这个逻辑讲清楚,说明你真的用过,不是背概念。

第二个追问是“数据库为什么这么设计?”回答的思路是抓住表与表之间的引用关系。比如简历表为什么要单独建,因为一个用户可能有多份简历,后续也要支持按简历模板生成新简历,所以用户和简历是一对多关系;投递记录表为什么要加联合唯一索引,因为业务上同一个学生对同一职位只能投递一次。能讲清楚设计动机,比单纯背字段列表有用得多。

第三个追问是“如果用户量变大,你的系统瓶颈在哪?”这个问题你只要回答“当前是单体架构,后续可以拆分模块、加Redis缓存、引入消息队列解耦投递通知流程”就足够。重点是展现你有扩展思维,而不是真的要求你现在就搞微服务。把上面提到的缓存、索引、分页优化方案再系统地说一遍,面试官基本满意了。

写在最后的一点实操建议

“一网寻职”这套系统,我把从项目定位、数据库设计、前后端核心功能实现,到安全防护、性能优化、部署上线、面试追问全链路拆了一遍。套用我最近做项目时的一个习惯:先定好表和接口,再动手写前端页面;先把登录和权限的闭环跑通,再做简历和职位模块;先把投递和收藏跑通,再去细化消息通知。按这个顺序推进,你的毕设进度不会卡壳。

最后再分享一个小经验:项目演示时,提前准备两套账号,一个学生账号一个企业账号,把两条核心链路先走一遍,比如“学生发布简历 -> 企业搜索到学生 -> 企业发面试邀请 -> 学生收到消息”。这条链路能走通,整个系统的主体功能就全部验证过了。别等到了答辩现场才发现消息中心没通,台下老师等着看,你急着手忙脚乱去改代码,那体验实在太差。好好把上面的细节过一遍,你的毕业设计绝对不会差。

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

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

立即咨询