ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue高校实习管理系统毕设项目实战解析

SpringBoot+Vue高校实习管理系统毕设项目实战解析 每年到毕业季总有一批做Java Web毕设的同学被高校实习管理系统这种题目折腾得够呛——需求方其实是导师觉得就是几个页面加增删改查真做起来才发现里面塞着学生、教师、企业、学院、实习计划、过程记录、报告评定好几条业务线前后端联调时更是被跨域、权限、日期格式各种问题轮番轰炸。我前阵子正好把一整套 SpringBootVue 的高校实习管理系统完整源码、SQL脚本和接口文档整理归档了这套东西覆盖了从数据库设计到前端路由守卫的完整链路适合正在做毕设、想快速搭一个拿得出手的Web项目的同学直接参考。我先把整体做一遍拆解再把表结构、后端分层、前端工程化、接口文档规范这些核心环节逐个讲透最后把我在实际开发里踩过的坑和排查技巧一并整理出来。1. 项目整体设计与技术选型思路1.1 高校实习管理系统的业务痛点和功能拆解先聊业务。很多同学一拿到实习管理系统这个题目就开始建表结果建了七八张表把系统做成了学生档案管理根本不符合实际实习管理的场景。高校实习管理真正要做的是什么是把这个链条管起来实习前岗位发布、学生申报、教师审核、实习中签到打卡、过程记录、指导答疑、实习后实习报告提交、成绩评定、数据统计。不同角色的诉求完全不一样学生关心我能看到哪些岗位、我的申请批了没、报告怎么交教师关心哪些学生还没落实实习、过程记录是否正常、最终成绩怎么评企业关心我发布的岗位有没有人申请、学生表现怎么样管理员关心全院/全校的实习覆盖率是多少、各单位接收情况如何。所以我在设计功能模块时是按四种角色、三大阶段拆的学生端实习岗位浏览与申报、我的实习计划、过程记录填报、实习报告提交、成绩查询。教师端含辅导员/指导教师实习计划审核、学生过程记录批阅、实习报告批阅与打分、所带学生列表。企业端岗位发布与下架、在线接收学生申请、对学生实习表现进行评价。管理员端基础数据管理学院、专业、班级、用户、统计报表实习率、对口率、单位性质分布。一个完整的实习管理系统光是权限设计就够写一大段但没有必要在一开始的标题里堆功能清单——关键是让读者看到这个项目不是只有CRUD而是有清晰业务主线的系统。1.2 技术栈选型为什么是SpringBootVue而不是SSH或原生JSP这个项目的标题是SpringBootVue两个词拆开都有理由。先说SpringBoot。很多教材还在教SSHStruts2SpringHibernate甚至纯ServletJSP但放到2025年的就业市场SpringBoot已经是Java后端的事实标准。它解决了传统SSH最痛的配置地狱问题——不用再写一堆XML内嵌Tomcat直接java -jar就能跑。对毕设来说SpringBoot明显更容易出成果。也许有人会杠毕设用JSP不更简单吗但JSP的前后端耦合到后期改起来是真的痛苦页面逻辑混在一起导师让你加个字段你得翻一整天JSP根本不划算。再说Vue。Java后端同学普遍对前端的心理预期是能出页面就行但Vue恰好是上手成本最低的现代化前端框架之一组件化开发、数据响应式、官方脚手架Vue CLI或Vite一条龙模板语法接近HTML比React的函数式组件和Hooks对新手友好得多。特别是这个项目的核心交互是列表、表单、状态流转Vue的双向绑定可以省掉大量手动操作DOM的代码。另外Vue的生态在国内社区非常活跃遇到问题随便一搜就是中文解决方案对毕设党来说这比技术先进性更重要。技术上锁定SpringBootVue之后数据库我用的是MySQL 8.x而不是SQL Server。不是说SQL Server不好而是在SpringBoot生态里MySQL的整合资料更全连接驱动、默认配置、云数据库兼容都更方便毕设部署也省事。SQL脚本文件我同时给了一份里面包含建库、建表、初始数据导入即可运行。2. 数据库表结构设计与SQL脚本编写要点2.1 核心表设计从用户角色到实习业务闭环数据库设计是这个项目里最值得细讲的部分。我见过不少同学把用户表设计成一个万能表角色用字符串拼接结果后面写权限判断时疯狂if(user.getRoles().contains(teacher))代码又丑又容易错。我这里用了经典的用户-角色-用户扩展信息分离设计sys_user账号表存登录名、密码BCrypt加密、手机号、状态不存业务字段。tb_student学生扩展表user_id关联sys_user存学号、姓名、学院ID、专业、班级、入学年份。tb_teacher教师扩展表user_id关联sys_user存工号、姓名、职称、所属学院ID。tb_company企业扩展表user_id关联sys_user存企业名称、统一社会信用代码、行业类型、简介、联系人。sys_rolesys_user_role角色表、用户角色关联表支持一个用户多角色比如老师同时是院系管理员。这五张表解决了谁登录、是什么身份的问题再往下就是实习业务表。业务表是这条链tb_job实习岗位→tb_internship_apply实习申请→tb_internship_plan实习计划申请通过后生成→tb_daily_record过程记录→tb_report实习报告→tb_score成绩评定。这一步特别核心很多新手把申请和计划做成一张表看起来省事但后续要加一个学生申请多个岗位、被一家通过后生成计划的场景就懵了。拆开表之后每个环节都有独立的审核状态字段比如申请表的status有PENDING、APPROVED、REJECTED三种状态计划的status有IN_PROGRESS、COMPLETED、TERMINATED状态机清晰前后端联调时永远不会对不上。另外必须建一个sys_dict_data字典表存实习类型分散实习/集中实习、单位性质国企/民企/事业单位、成绩等级优/良/中/及格/不及格这些下拉选项。我在这里吃过亏一开始把枚举硬编码在Java类里后来导师说要改及格为合格前端页面加后端的if-else改了一圈改成字典表之后维护人员直接在后台改数据就行了。2.2 表的关联与外键策略别让外键成为性能和迁移的负担外键用不用是个老话题。我的建议是设计时画清楚ER图展示表关联但物理建表时不建外键约束只建索引。为什么外键约束会在插入、更新时触发额外的完整性检查对高并发场景是性能杀手更重要的是毕设项目后期经常要批量导入测试数据外键导致插入顺序稍有不对就报错排查起来很麻烦。正确做法是在tb_internship_apply表里保留student_id、job_id这些关联字段建普通索引加速查询然后在代码里维护关联逻辑。这样既保证了查询效率也不会被数据库层的约束卡脖子。2.3 SQL脚本编写字符集、时区、初始化数据一个都不能少SQL脚本是整个项目能不能拿到就运行的关键。我看过太多所谓的完整源码SQL脚本里连CREATE DATABASE都没有或者建表语句里不指定字符集导入中文全变问号。我这里直接给出可执行的完整脚本几个核心点建库语句指定字符集CREATE DATABASE IF NOT EXISTS internship_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;一定要用utf8mb4而不是utf8因为utf8在MySQL里最多存3字节存不了emoji和一些生僻字utf8mb4才是完整的Unicode支持。时间字段统一用datetime不要用timestamp。timestamp有2038年问题而且受时区影响在系统里容易平白多出8个小时的偏移。Java后端只要在连接串上加上serverTimezoneAsia/Shanghai时间就能对得上。初始化数据要够用。我给了三套一套是管理员、教师、学生、企业的账号密码统一是123456的BCrypt加密串一套是几家公司、几十个岗位的演示数据还有一套是几个典型的实习流程数据从申请到报告完成。三套数据对应三阶段的自测登录跑通、前后端联调、流程全通。没有数据的新系统测试起来寸步难行而给数据又恰好能倒逼你把系统各状态都摸一遍。每条INSERT语句前建议加上字段列表比如INSERT INTO tb_student (user_id, student_no, student_name, college_id, major, class_name) VALUES (...)这样即使后面表加了字段老脚本也不会崩。2.4 SQL索引设计与慢查询预防索引是毕设里最容易被忽略、但答辩时最容易被追问的东西。我的设计原则是主键索引必有外键关联字段建普通索引常用查询条件建联合索引。比如tb_daily_record表经常按student_id record_date查询某学生某段时间的打卡记录我就建了idx_student_date(student_id, record_date)联合索引。另外我强烈建议在SQL脚本里给status字段加索引——实习申请表最核心的查询是某个教师待审核的申请列表WHERE teacher_id ? AND status PENDING就是一次典型的索引查找。答辩时导师问你这系统并发高怎么办你就可以从索引设计、分页查询、Redis缓存三个角度来答而不是只能干瞪眼。3. 后端SpringBoot核心实现与实战要点3.1 Java Web项目标准目录结构从分层混乱到规范统一很多人毕设项目一打开controller、service、dao三个包下面堆了几十个类命名全靠拼音缩写谁看谁头疼。我这个项目的目录结构是经过多轮实战沉淀的直接展示最有参考价值的段落com.example.internship ├── common │ ├── constant │ ├── exception │ ├── result // 统一响应体 ResultT │ └── utils // JwtUtil, DateUtil, ExcelUtil ├── config // WebMvcConfig, CorsConfig, SecurityConfig(若有) ├── controller │ ├── admin │ ├── student │ ├── teacher │ └── company ├── service │ ├── impl ├── mapper // MyBatis-Plus 的 Mapper 接口 ├── entity // 数据库实体 ├── dto // 接收前端参数的DTO不等于Entity ├── vo // 返回给前端的VO不等于Entity └── Interceptor // 登录拦截器、权限拦截器这里说几个容易被忽视的细节。第一dto和vo必须和entity分离千万不要把数据库实体直接返回给前端。数据库实体里有密码字段直接返回意味着你把用户的BCrypt哈希发给浏览器了虽然理论上不可逆但这是非常糟糕的习惯。第二controller层只做参数接收和结果封装业务逻辑全部下沉到service层这样代码可读性和可测试性都强很多。第三接口路径要有模块前缀/api/student/job/list、/api/teacher/apply/audit管理等后端和前端联调时看着路径就知道是哪个功能。3.2 SpringBoot版本选择不要盲目踩新版本热词里有一条springboot版本太高这真是血泪教训。很多同学在Spring Initializr上直接选最新版一创建项目引入MyBatis-Plus、Shiro等第三方依赖就各种版本冲突。我的建议是稳定优先选Spring Boot 2.7.x比如2.7.18不要无脑上Spring Boot 3.x。原因有三一是大量网上教程、毕设参考资料基于Spring Boot 2.x遇到问题你能搜到最多前人的解决方案二是Spring Boot 3.x强制要求JDK 17以上很多学校机房或者学生自己电脑装的是JDK 8直接编译都不通过三是部分老牌第三方库如某些代码生成器对Spring Boot 3的Jakarta命名空间迁移不完整配置起来多一步踩坑。JDK 8 Spring Boot 2.7.x MyBatis-Plus 3.5.x这套组合已经被无数生产项目验证过作为毕设足够稳妥。pom.xml核心依赖如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency /dependencies3.3 统一响应体与JSON接口文档模板前后端联调最大的痛点之一就是响应格式不统一有的接口返回{code:0, data:...}有的接口直接返回裸数组前端处理逻辑写得像打地鼠。好的做法是全项目统一一个响应体{ code: 200, message: 操作成功, data: {}, timestamp: 2025-04-15 10:30:00 }对应的Java类很简单Data public class ResultT { private Integer code; private String message; private T data; private String timestamp; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); result.setTimestamp(DateUtil.now()); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); result.setTimestamp(DateUtil.now()); return result; } }配合全局异常处理器RestControllerAdvice凡是业务异常、参数校验失败统一包装成这个响应体返回。前端拿到响应后只需要判断res.code 200异常分支全部交给拦截器统一提示代码干净不少。这个统一响应体本身就是接口文档模板的最佳注释——不管用Apifox还是Postman你都可以把这个JSON体作为所有接口的响应模板。3.4 JWT登录鉴权与拦截器设计登录鉴权是Java Web毕设的必考点。我用的是JWT方案核心逻辑用户登录成功后后端用用户ID、角色、过期时间生成一个Token返回给前端前端把Token存在localStorage每次请求在Authorization请求头里带上后端写一个JwtInterceptor拦截器在HandlerInterceptor的preHandle里解析Token、放行或返回401。JwtUtil的核心代码不复杂但有几个点要注意public class JwtUtil { private static final String SECRET_KEY your-secret-key-please-change; private static final long EXPIRE_TIME 7 * 24 * 60 * 60 * 1000; public static String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } }踩坑提醒jjwt 0.9.1在解析Token时会依赖javax.xml.bind.DatatypeConverterJDK 8以上需要手动引入jaxb-api依赖否则会报NoClassDefFoundError。所以pom.xml里最好加上dependency groupIdjavax.xml.bind/groupId artifactIdjaxb-api/artifactId version2.3.1/version /dependency或者干脆用io.jsonwebtoken:jjwt-api:0.11.5jjwt-impljjwt-jackson这套新API加密方式改成Keys.hmacShaKeyFor(secret.getBytes())没有JDK兼容问题。我两种都试过后者更省心。拦截器注册到WebMvcConfigurer时要注意放行路径。登录接口、验证码接口、Swagger接口文档路径必须放行其余接口全部拦截Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/login, /api/auth/captcha); }3.5 SpringBoot配置文件中的隐藏坑application.yml是SpringBoot项目里配置密度最高也最容易出错的地方。我这里给出一个实测可用的核心配置并解释几个关键项server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/internship_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 123456 servlet: multipart: max-file-size: 10MB max-request-size: 50MB mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0allowPublicKeyRetrievaltrue是MySQL 8.0的坑不加它连接时可能报Public Key Retrieval is not allowed。useSSLfalse是避免本机开发时SSL握手警告。MyBatis-Plus的log-impl配置成StdOutImplSQL语句会直接打印在控制台联调时排查问题效率翻倍——但上线前务必注释掉否则每个查询都打印SQL日志量爆炸。logic-delete-field配置逻辑删除后所有delete操作自动变成update可以防止误删数据这在毕设答辩时也是一个可以拿出来讲的亮点。4. 前端Vue工程化实现与联调技巧4.1 Vue环境安装与项目初始化从Node.js到Vue CLI前端环境配置是很多后端同学的噩梦实际上按步骤走并不难。先装Node.js建议装16.x或18.x LTS版本不要装最新的偶数版比如20.x——部分老项目依赖和最新Node的兼容性不好。装完验证版本node -v npm -v然后设置npm淘宝镜像这一步强烈建议做。国内直连npm官方源下载依赖很容易超时npm config set registry https://registry.npmmirror.com创建Vue项目有两种主流方式# 方式一Vue CLI稳定适合大多数毕设 npm install -g vue/cli vue create internship-ui # 方式二Vite更快但需要Node 14.18 npm create vitelatest internship-ui -- --template vue我个人建议用Vue CLI因为它的vue.config.js配置方案非常成熟配置反向代理只需几行代码对前后端联调极其友好。项目初始化过程中会问你选哪些功能建议勾选Router、Vuex或之后改用PiniaCSS预处理器选Less或Sass都行。4.2 Vue Router路由与页面权限控制实习管理系统有四种角色前端路由必须能根据登录用户的角色动态控制访问权限。我的方案是静态路由只保留登录页、404页和首页框架Layout然后根据角色动态添加路由。// router/index.js const routes [ { path: /login, component: () import(/views/login/index.vue) }, { path: /, component: Layout, redirect: /dashboard, children: [ { path: dashboard, name: Dashboard, component: () import(/views/dashboard/index.vue), meta: { title: 首页, roles: [admin, student, teacher, company] } } ] } ]路由守卫的核心逻辑是每次路由跳转前检查localStorage里的Token和用户角色如果没有Token就重定向到登录页如果有Token但当前路由的meta.roles不包含用户角色就重定向到首页。这里有一个关键细节角色是直接放在JWT里解出来的不要单独发请求查角色——登录时后端把Token和角色一起返回前端存到本地路由守卫直接读本地避免每次刷新页面都要向后端请求用户信息。vue.config.js里的代理配置也是非常关键的一环不配代理的话前端请求/api会直接打到前端开发服务器的8080端口跨域报错配完代理后module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端axios请求/api/student/job/list开发服务器会自动把请求转发到http://localhost:8080/api/student/job/list完美绕过跨域而且上线时只需把baseURL换成后端地址即可。4.3 Pinia vs Vuex状态管理到底选哪个热词里提到了vue pinia vs vuex这是现在Vue生态里一个不可避免的选择题。Vuex是Vue 2时代的官方状态管理库Pinia是Vue 3时代的新官方推荐。两者的核心区别对比项VuexPinia设计理念单一全局StoreMutationsActions多Store按业务模块拆去掉了MutationsAPI风格需要写state/getters/mutations/actions四件套只需state/getters/actions更简洁TypeScript支持需要额外类型体操原生支持类型推断友好学习成本概念多新手容易混淆接近Composition API上手快对于这个实习管理系统我建议如果用Vue 2 Vue CLI用Vuex并采用modules拆成user、app两个模块如果用Vue 3 Vite直接上Pinia按useUserStore、useAppStore拆分。这个项目里状态管理主要用来存用户信息、角色、侧边栏折叠状态数据量不大选哪个都不会错。但答辩时如果被问到为什么选Pinia你可以答Pinia移除了Mutation的概念Action直接修改状态代码更简洁同时它天然具备模块化拆分能力每个Store自带命名空间不会出现Vuex那种模块多了以后getter和state重名的问题这个回答就很有深度。4.4 axios请求封装与拦截器前端请求层的封装质量直接决定联调效率。我封装了一个request.jsimport axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: /api, timeout: 15000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.message || 请求失败) if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.message)) } return res }, error { Message.error(error.response?.data?.message || 网络异常) return Promise.reject(error) } )这套封装的精髓在于请求拦截器统一加Token响应拦截器统一处理业务错误码、HTTP错误码、401跳转。后续每个页面的请求代码只需要关心业务数据不需要重复写错误处理逻辑。同时timeout: 15000是实测后的值——实习报告上传等大接口偶尔会比较慢15秒是妥协后的合理值。5. 接口文档设计与开发工具推荐5.1 接口文档模板与规范从零开始设计一份能直接用的文档接口文档是毕设项目中看起来容易、做起来全是细节的部分。很多同学把接口文档写成了数据库字段翻译完全没有请求/响应示例前端看到文档根本不知道该传什么、会返回什么。我这份项目的接口文档是按这个模板写的你可以直接套用5.1.1 接口基本信息项目内容接口名称学生端-获取实习岗位列表请求URL/api/student/job/list请求方式GET是否需要认证是Header携带Token5.1.2 请求参数参数名类型是否必填说明pageNumInteger是页码从1开始pageSizeInteger是每页条数keywordString否岗位名称/企业名称关键字typeString否实习类型集中实习/分散实习5.1.3 响应示例{ code: 200, message: 操作成功, data: { total: 25, list: [ { id: 1, jobName: Java开发实习生, companyName: 某某科技有限公司, jobType: 集中实习, salary: 200元/天, city: 上海, publishTime: 2025-04-01 10:00:00 } ] }, timestamp: 2025-04-15 10:30:00 }5.1.4 错误响应示例{ code: 500, message: 参数错误pageNum不能为空, data: null, timestamp: 2025-04-15 10:30:00 }接口文档的编写要点总结成一句话每一个字段都要有说明每一个成功响应都要有示例每一个失败响应都要有原因。这样前端同学拿到文档就能直接开工不用反复来问这个字段是什么意思。5.2 接口文档工具选型Apifox还是Postman说到接口文档工具热词里有一条写接口文档的工具可以记录响应成功、响应失败的json体并能保存和发布到浏览器这个需求指向的正是Apifox或Postman这类工具。我在这个项目中用的是Apifox它比Postman更适合国内团队协作一是Apifox把接口文档、接口调试、Mock数据、自动化测试整合在一个工具里天然支持在线文档分享把链接发给任何人就能在浏览器里查看二是它对中文团队和国内网络环境优化更好三是它支持从Swagger/OpenAPI一键导入也可以直接从IDEA插件同步。用Apifox保存响应成功/失败JSON体的操作很直观调试接口时点击保存为用例工具会把当前请求参数和响应体整体保存下来然后在接口文档的响应示例里你可以针对200和500分别添加示例生成文档时前端就能看到不同状态码对应的完整响应结构。这个能力对毕设的好处是你调试通一个接口顺手就把文档示例生成好了不用写文档时再手动复制粘贴。5.3 接口文档与后端代码的一致性维护接口文档最容易出现的问题是写的和代码不一致——代码改了接口参数文档忘了更新。我的做法是用Swagger注解给接口补充描述启动项目后访问http://localhost:8080/doc.htmlknife4j就能直接看到接口文档然后定期从Apifox的导入Swagger功能同步到团队空间。这样即使代码迭代文档也不会严重滞后。具体来说在controller层的每个方法上加ApiOperation(学生端-获取实习岗位列表)在dto字段上加ApiModelProperty(岗位名称)就能生成高质量的自维护文档。6. 常见问题与排查技巧实录6.1 前后端联调的经典三座大山跨域、404、401联调阶段最常遇到的就是这三个报错我一个个说。跨域CORS问题在第一轮联调中几乎必现。浏览器控制台报Access-Control-Allow-Origin错误时先确认前端开发服务器的代理是否配置正确。如果你用的是Vue CLI脚手架vue.config.js的proxy配置里changeOrigin必须为true它表示将请求头的Host字段替换为目标的Host否则后端通过request.getServerName()获取域名时会拿到前端域名导致某些场景下的逻辑错误。如果你不是用脚手架启动而是把前端打包后丢进Nginx那就需要在Nginx层配置proxy_pass http://后端地址;。如果实在要直接跨域请求比如临时测试后端可以加一个CorsConfigConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .maxAge(3600); } }注意allowedOriginPatterns(*)是Spring Boot 2.4以上版本的新写法allowedOrigins(*)在搭配withCredentials(true)时会被浏览器拒绝。404问题大概率不是接口不存在而是路径对不上。先看后端RequestMapping的类级路径和方法级路径拼接后是否与前端请求完全一致再看拦截器是否误拦截了接口。排查最快的办法在后端加logging.level.org.springframework.webDEBUG启动日志里会打印每个请求匹配到的Handler匹配不到就直接显示404原因。401问题基本都是Token校验失败。可能原因前端请求头名称写错我要求用Authorization有人写成tokenToken过期过期时间建议7天毕设演示期间频繁重新登录很烦人后端解密时密钥不一致部署到服务器换了环境但密钥没同步。排查思路用Apifox手动复制Token发一次请求看后端日志报的是token无效还是token过期再做针对性修复。6.2 数据库相关的坑时区、SQL注入、数据导入失败时区问题我前面提过这里再强调一次终极排查清单MySQL连接串必须有serverTimezoneAsia/ShanghaiMySQL服务器时区用SHOW VARIABLES LIKE %time_zone%检查必要时设置SET GLOBAL time_zone 8:00Java实体类日期字段用LocalDateTime不要用java.util.DateMyBatis对LocalDateTime的支持已经非常成熟。SQL注入是答辩时老师一定会问的安全问题。这个项目的后端全程使用MyBatis-Plus的条件构造器QueryWrapper和参数占位符彻底避免了字符串拼接SQL的问题。但在动态排序场景比如按发布时间排序如果直接把前端传的排序字段拼进SQL就存在注入风险。我的做法是做一个白名单映射前端传sortpublishTime后端映射到ORDER BY publish_time DESC而不是直接拼接。你可以在接口文档的安全说明里注明这一点答辩时非常加分。数据导入失败最常见的问题有三个一是Excel模板的列顺序与数据库字段顺序不一致我建议在导入工具类中明确按列头名称取值而不是按索引二是日期格式五花八门2025/1/1、2025-01-01、2025.1.1统一用EasyExcel的DateTimeFormat注解指定三是重复数据导致唯一键冲突批量导入前用SELECT查重或者用INSERT IGNORE。6.3 SpringBoot项目部署常见问题端口被占用、内存不足、静态资源404本地开发最烦的是端口被占用。启动SpringBoot时报Port 8080 was already in use处理方式Windowsnetstat -ano | findstr :8080然后taskkill /PID 进程号 /FLinux/Maclsof -i :8080然后kill -9 进程号如果换端口记得同时改后端application.yml的server.port和前端vue.config.js的代理目标端口两个地方的端口不一致是最容易踩的坑。内存不足问题在打包运行时比较常见。如果是用java -jar方式运行建议指定初始堆和最大堆java -Xms256m -Xmx512m -jar internship-0.0.1-SNAPSHOT.jar毕设项目一般512MB堆内存完全够用。如果是在IDEA里运行可以在VM options里设置-Xmx512m。静态资源404问题通常出现在前端路由使用了history模式时。vue-router开启history模式后页面刷新会向后端发起真实请求如果后端没做history fallback就会404。解决方案有两个一是修改vue-router为createWebHashHistory()URL会带#号刷新不会404二是后端做默认跳转把未知路径都转回前端页面。对毕设来说直接用hash模式最省事虽然URL不够美观但完全够用。6.4 Vue安装依赖时的高频报错与解决方案前端同学经常在npm install阶段就被卡住。我整理几个高频报错和解决方式# 报错1ERR_SOCKET_TIMEOUT 或 ETIMEDOUT # 原因npm源慢或网络不稳定 # 方案切换镜像源 npm config set registry https://registry.npmmirror.com # 报错2ERESOLVE unable to resolve dependency tree # 原因依赖版本冲突常见于vue/cli与eslint版本 # 方案用legacy-peer-deps绕过 npm install --legacy-peer-deps # 报错3Node Sass does not yet support your current environment # 原因node-sass不支持当前Node版本 # 方案卸载node-sass安装sassdart-sass npm uninstall node-sass npm install -D sass sass-loader10sass-loader版本也要注意sass-loader12以上需要Node 12但和Vue CLI的vue.config.js配置有兼容性问题时回退到sass-loader10.x是业界通用解法。这个版本兼容问题在Java和前端两套生态里都存在记住一个原则遇到诡异报错先怀疑版本再怀疑代码逻辑。6.5 实习管理系统的典型场景测试清单除了技术排错我再分享一个业务层面的测试清单这是我反复跑过N轮总结出来的。做这类管理系统光验证能增删改查远远不够要按业务主线走一遍你会发现大量隐藏的关联逻辑问题学生A先申请岗位X待审核再申请岗位Y此时两个申请的状态是否正确独立教师B审核通过A的申请后系统是否自动生成了A的实习计划计划的状态是否自动置为IN_PROGRESS学生A提交过程记录时如果计划已被TERMINATED系统是否还能提交实习报告提交后教师评分完毕学生看到的成绩是否立即更新成绩变化是否有留痕管理员把某企业账号停用后该企业已发布的岗位在学生端是否还显示这些场景测试看着琐碎但每一个都对应着真实的业务约束。答辩时你能主动说出我测试过申请通过后自动生成实习计划、计划终止后不能再提交过程记录这类细节评委自然会觉得你是认真做过系统的而不是拿个半成品糊弄。写在最后做这个项目时我最大的体会是类似实习管理系统这种Java Web毕设功能模块可以列出一大串但真正拉开差距的不是做了多少个页面而是业务闭环是否完整、权限和状态是否严谨、联调交互是否顺畅。表结构设计时多花半小时梳理角色和状态流转后端先把统一响应体、全局异常、JWT拦截器这些基础设施搭好前端把axios封装和路由守卫一次配到位后面每一页的新功能都只是复制粘贴再加改开发效率能提升好几倍。如果你正在做这个题目建议按数据库设计→统一响应体→登录鉴权→核心业务流程→前端页面→联调→部署这个顺序来每一步都验证通再进入下一步别急着堆页面。这些经验和踩坑记录都是实打实换来的希望对你有所参考。
返回列表