公司动态
SpringBoot+Vue校企实习管理系统毕设实战:从环境搭建到模块开发
1. 先搞清楚这个毕设项目到底要解决什么问题如果你正在为计算机专业的毕业设计发愁特别是不知道从哪里找需求、怎么把技术栈串起来、如何做出一个能跑通且结构清晰的系统那么直接找一个成熟的“校企实习管理系统”作为蓝本是最高效的起点。这个选题的核心价值在于需求明确、业务闭环、技术栈主流、文档和参考资源丰富。它不是一个让你从零构思天马行空创意的项目而是提供了一个现成的、符合实际业务逻辑的“骨架”。你的任务不是发明需求而是理解这个骨架并用 SpringBoot Vue 这套主流技术把它“搭建”和“丰满”起来。这恰恰是大多数企业对新人的实际要求在给定需求和架构下完成高质量的工程实现。这个系统通常涵盖学生、企业、学校导师三类角色包含用户管理、实习岗位发布与申请、过程材料提交、成绩评定、数据统计等核心模块。采用前后端分离架构前端用 Vue 构建交互界面后端用 SpringBoot 提供 RESTful API数据库用 MySQL。整个过程你会完整经历环境搭建、技术选型、数据库设计、接口开发、前后端联调、部署测试这一整套企业级开发流程。这比做一个功能炫酷但技术栈单一或业务逻辑薄弱的项目对你的能力提升和面试帮助要大得多。2. 环境与工具准备别在第一步就卡住动手之前先把“战场”布置好。很多同学项目跑不起来八成是环境问题。下面是我建议的、经过大量实测的稳定组合能避开很多版本兼容的坑。2.1 后端开发环境 (SpringBoot 侧)JDK选择JDK 8 或 JDK 11。这是 SpringBoot 2.x 系列最兼容的版本。不建议一上来就用最新的 JDK 17 或 21虽然新版本支持但某些依赖库可能有隐藏的兼容性问题。用java -version确认。开发工具 (IDE)IntelliJ IDEA Ultimate 版学生可免费申请或 Community 版。它对 SpringBoot 的支持是最好的自动补全、项目创建、Maven 依赖管理、一键运行和调试都非常顺畅。Eclipse 也可以但配置起来稍麻烦。项目管理与构建Maven。确保你的 IDEA 里 Maven 配置正确能正常从中央仓库下载依赖。配置文件pom.xml是项目的核心。数据库MySQL 5.7 或 8.0。两者都可以8.0 功能更多但可能稍有差异。务必记住你安装时设置的root密码。同时安装一个数据库可视化工具如Navicat、DBeaver或MySQL Workbench用于执行 SQL 脚本和查看数据。2.2 前端开发环境 (Vue 侧)Node.js去官网下载LTS长期支持版本如 18.x 或 20.x。安装后在命令行用node -v和npm -v检查版本。Node.js 自带了 npm 包管理器。包管理工具虽然 npm 是自带的但国内更推荐使用cnpm或yarn速度更快。安装 cnpmnpm install -g cnpm --registryhttps://registry.npmmirror.com。Vue 脚手架使用 Vue CLI 快速创建项目。安装命令npm install -g vue/cli或cnpm install -g vue/cli。安装后用vue --version验证。前端 IDEVisual Studio Code (VS Code)是首选。轻量、插件丰富尤其推荐 Vetur 或 Volar、ESLint、Prettier对 Vue 开发支持极好。2.3 协同与版本控制Git是必须的。在本地安装 Git并注册一个Gitee码云或GitHub账号。即使是一个人开发也养成提交代码的习惯。这不仅是毕设要求更是工程师的基本素养。注意环境安装时所有路径不要包含中文和空格。特别是 Java 安装路径、项目存放路径。这是无数血泪教训换来的经验。3. 项目创建与骨架搭建从“Hello World”到业务模块不要一拿到代码就想全部看懂。正确的顺序是先让项目在本地跑起来看到登录页面然后再去分析代码结构。3.1 后端 SpringBoot 项目初始化创建项目打开 IDEA选择Spring Initializr。关键配置如下Project SDK选择你安装的 JDK 8 或 11。TypeMaven Project。Java Version8 或 11。PackagingJar。Groupcom.yourname(例如com.school)Artifactinternship-systemDependencies这是核心勾选Spring Web(用于构建 RESTful API)Spring Data JPA或MyBatis Plus(持久层框架推荐 MyBatis Plus更灵活)MySQL Driver(数据库驱动)Lombok(简化实体类代码强烈推荐)项目结构创建完成后标准的 Maven 项目结构如下internship-system ├── src/main/java/com/school/internship │ ├── controller // 控制器接收前端请求 │ ├── service // 业务逻辑层 │ │ └── impl // 业务逻辑实现类 │ ├── mapper // MyBatis Plus 的 Mapper 接口 │ ├── entity // 实体类对应数据库表 │ └── config // 配置类如跨域配置 ├── src/main/resources │ ├── application.yml // 主配置文件 │ └── static templates // 静态资源前后端分离项目通常不用 └── pom.xml // Maven 依赖管理文件配置文件打开resources/application.yml配置数据库连接和服务器端口。server: port: 8080 # 后端服务启动端口 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/internship_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password # 换成你的MySQL密码 jpa: hibernate: ddl-auto: update # 初次启动可设为update自动建表。生产环境用none或validate。 show-sql: true # 控制台打印SQL调试用启动测试找到主类InternshipSystemApplication通常由 Spring Initializr 生成右键Run。控制台看到Tomcat started on port(s): 8080且没有报错说明后端基础环境成功。3.2 前端 Vue 项目初始化创建项目打开终端CMD 或 PowerShell进入你的工作目录运行vue create internship-web在交互界面中选择Manually select features。勾选Babel,Router,Vuex,CSS Pre-processors,Linter / Formatter。Router 和 Vuex 对于管理系统是必须的。选择 Vue 版本3.x。路由模式Yes(使用 history 模式)。CSS 预处理器Sass/SCSS。ESLint 配置选一个标准配置即可如ESLint Standard config。后续选项按回车选择默认。安装必要依赖项目创建后进入项目目录并安装 UI 库和请求库。对于管理系统Element Plus 是很好的选择。cd internship-web npm install element-plus axios --save # 或使用 cnpm cnpm install element-plus axios --save配置与测试在src/main.js中引入 Element Plus 和 axios。import { createApp } from vue import App from ./App.vue import router from ./router import store from ./store import ElementPlus from element-plus import element-plus/dist/index.css import axios from axios const app createApp(App) app.use(store) app.use(router) app.use(ElementPlus) // 全局配置 axios设置后端 API 基础地址 axios.defaults.baseURL http://localhost:8080 app.config.globalProperties.$axios axios // 可选挂载到全局 app.mount(#app)运行npm run serve。浏览器打开http://localhost:8081看到 Vue 欢迎页说明前端项目成功。3.3 前后端连接测试解决跨域此时前端8081端口请求后端8080端口会遇到跨域问题。需要在 SpringBoot 后端配置跨域支持。在com.school.internship.config包下创建CorsConfig.javaimport org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); // 允许所有源生产环境应指定具体前端地址 config.setAllowCredentials(true); // 允许发送Cookie config.addAllowedMethod(*); // 允许所有方法GET, POST等 config.addAllowedHeader(*); // 允许所有头 UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); // 对所有接口生效 return new CorsFilter(source); } }重启后端服务。然后在前端写一个测试接口例如在src/views/Home.vue的mounted生命周期里mounted() { this.$axios.get(/test).then(res { console.log(后端连接成功, res.data) }).catch(err { console.error(连接失败, err) }) }在后端创建一个简单的TestController返回 “Hello from Backend”。打开浏览器控制台看到成功消息证明前后端通信链路打通。这是最关键的一步。4. 核心业务模块开发实战以“实习岗位管理”为例我们以“实习岗位管理”这个核心模块为例拆解前后端如何协作。理解了这一个其他模块如学生管理、申请管理、成绩评定都是类似的模式。4.1 数据库设计与实体类Entity首先设计internship_position表。CREATE TABLE internship_position ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, company_id bigint(20) NOT NULL COMMENT 发布企业ID, title varchar(100) NOT NULL COMMENT 岗位标题, description text COMMENT 岗位描述, requirement text COMMENT 任职要求, location varchar(200) COMMENT 工作地点, number int(11) DEFAULT 1 COMMENT 招聘人数, start_date date COMMENT 实习开始日期, end_date date COMMENT 实习结束日期, status tinyint(4) DEFAULT 1 COMMENT 状态1开放中0已关闭, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_company (company_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT实习岗位表;在 Java 后端创建对应的实体类InternshipPosition.javapackage com.school.internship.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDate; import java.util.Date; Data TableName(internship_position) // MyBatis Plus 注解指定表名 public class InternshipPosition { TableId(type IdType.AUTO) // 主键自增 private Long id; private Long companyId; private String title; private String description; private String requirement; private String location; private Integer number; private LocalDate startDate; private LocalDate endDate; private Integer status; // 1开放0关闭 TableField(fill FieldFill.INSERT) // 自动填充创建时间 private Date createTime; TableField(fill FieldFill.INSERT_UPDATE) // 自动填充更新时间 private Date updateTime; }使用 Lombok 的Data注解自动生成 getter、setter、toString 等方法极大简化代码。4.2 数据持久层Mapper创建 Mapper 接口InternshipPositionMapper.javapackage com.school.internship.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.school.internship.entity.InternshipPosition; import org.apache.ibatis.annotations.Mapper; Mapper // 声明为 MyBatis Mapper public interface InternshipPositionMapper extends BaseMapperInternshipPosition { // 继承 BaseMapper 后基础的 CRUD 方法insert, selectById, updateById, deleteById, selectList等都已具备 // 如果需要复杂查询可以在这里定义方法并在 resources/mapper/ 下编写对应的 XML }MyBatis Plus 的强大之处在于你不需要写任何 SQL 就能完成单表操作。4.3 业务逻辑层Service创建 Service 接口和实现类。InternshipPositionService.java(接口):package com.school.internship.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.school.internship.entity.InternshipPosition; public interface InternshipPositionService extends IServiceInternshipPosition { // 可以定义一些特殊的业务方法比如带条件的分页查询 PageInternshipPosition getPositionPage(PageInternshipPosition page, String keyword, Integer status); }InternshipPositionServiceImpl.java(实现类):package com.school.internship.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.school.internship.entity.InternshipPosition; import com.school.internship.mapper.InternshipPositionMapper; import com.school.internship.service.InternshipPositionService; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; Service public class InternshipPositionServiceImpl extends ServiceImplInternshipPositionMapper, InternshipPosition implements InternshipPositionService { Override public PageInternshipPosition getPositionPage(PageInternshipPosition page, String keyword, Integer status) { LambdaQueryWrapperInternshipPosition wrapper new LambdaQueryWrapper(); // 按标题或描述模糊查询 if (StringUtils.hasText(keyword)) { wrapper.like(InternshipPosition::getTitle, keyword) .or().like(InternshipPosition::getDescription, keyword); } // 状态筛选 if (status ! null) { wrapper.eq(InternshipPosition::getStatus, status); } // 按创建时间倒序 wrapper.orderByDesc(InternshipPosition::getCreateTime); return this.page(page, wrapper); } }4.4 控制器层Controller与 API 设计创建InternshipPositionController.java提供 RESTful API。package com.school.internship.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.school.internship.common.Result; import com.school.internship.entity.InternshipPosition; import com.school.internship.service.InternshipPositionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/position) public class InternshipPositionController { Autowired private InternshipPositionService positionService; // 新增岗位 PostMapping public Result save(RequestBody InternshipPosition position) { // 这里可以加入权限校验比如只有企业用户才能发布 boolean saved positionService.save(position); return saved ? Result.success(发布成功) : Result.error(发布失败); } // 分页查询岗位列表带搜索 GetMapping(/page) public Result findPage(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword, RequestParam(required false) Integer status) { PageInternshipPosition page new Page(pageNum, pageSize); PageInternshipPosition resultPage positionService.getPositionPage(page, keyword, status); return Result.success(resultPage); } // 根据ID获取岗位详情 GetMapping(/{id}) public Result getById(PathVariable Long id) { InternshipPosition position positionService.getById(id); return position ! null ? Result.success(position) : Result.error(未找到该岗位); } // 更新岗位信息 PutMapping public Result update(RequestBody InternshipPosition position) { boolean updated positionService.updateById(position); return updated ? Result.success(更新成功) : Result.error(更新失败); } // 删除岗位逻辑删除或物理删除 DeleteMapping(/{id}) public Result delete(PathVariable Long id) { boolean removed positionService.removeById(id); return removed ? Result.success(删除成功) : Result.error(删除失败); } }这里的Result是一个自定义的通用返回结果类用于统一 API 响应格式。package com.school.internship.common; import lombok.Data; import java.io.Serializable; Data public class ResultT implements Serializable { private Integer code; // 状态码如 200成功500失败 private String msg; // 提示信息 private T data; // 返回的数据 public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMsg(操作成功); result.setData(data); return result; } public static T ResultT success(String msg) { ResultT result new Result(); result.setCode(200); result.setMsg(msg); return result; } public static T ResultT error(String msg) { ResultT result new Result(); result.setCode(500); result.setMsg(msg); return result; } }4.5 前端页面与接口调用Vue 3 Element Plus在前端src/views目录下创建position/PositionList.vue组件。模板部分使用 Element Plus 的表格、分页、表单组件。template div classposition-container el-card !-- 搜索和操作栏 -- div stylemargin-bottom: 20px; el-input v-modelsearchForm.keyword placeholder请输入岗位标题或描述 stylewidth: 300px; keyup.enterloadData / el-select v-modelsearchForm.status placeholder状态 stylewidth: 120px; margin-left: 10px; el-option label全部 :valuenull / el-option label开放中 :value1 / el-option label已关闭 :value0 / /el-select el-button typeprimary stylemargin-left: 10px; clickloadData搜索/el-button el-button clickresetSearch重置/el-button el-button typesuccess stylefloat: right; clickhandleAdd发布新岗位/el-button /div !-- 数据表格 -- el-table :datatableData border stripe el-table-column propid labelID width80 / el-table-column proptitle label岗位标题 min-width180 / el-table-column propcompanyId label企业ID width100 / el-table-column proplocation label工作地点 width120 / el-table-column propnumber label招聘人数 width100 / el-table-column propstatus label状态 width100 template #defaultscope el-tag :typescope.row.status 1 ? success : info {{ scope.row.status 1 ? 开放中 : 已关闭 }} /el-tag /template /el-table-column el-table-column propcreateTime label发布时间 width180 / el-table-column label操作 width200 fixedright template #defaultscope el-button sizesmall clickhandleView(scope.row)查看/el-button el-button sizesmall typeprimary clickhandleEdit(scope.row)编辑/el-button el-button sizesmall typedanger clickhandleDelete(scope.row.id)删除/el-button /template /el-table-column /el-table !-- 分页组件 -- div stylemargin-top: 20px; text-align: center; el-pagination v-model:current-pagepageParams.pageNum v-model:page-sizepageParams.pageSize :page-sizes[5, 10, 20, 50] :totaltotal layouttotal, sizes, prev, pager, next, jumper size-changehandleSizeChange current-changehandleCurrentChange / /div /el-card !-- 新增/编辑对话框 -- el-dialog v-modeldialogVisible :titledialogTitle width50% el-form refformRef :modelformData :rulesrules label-width100px el-form-item label岗位标题 proptitle el-input v-modelformData.title placeholder请输入岗位标题 / /el-form-item el-form-item label岗位描述 propdescription el-input v-modelformData.description typetextarea :rows4 placeholder请输入岗位描述 / /el-form-item !-- 其他表单项... -- el-form-item label状态 propstatus el-radio-group v-modelformData.status el-radio :label1开放中/el-radio el-radio :label0已关闭/el-radio /el-radio-group /el-form-item /el-form template #footer span classdialog-footer el-button clickdialogVisible false取消/el-button el-button typeprimary clicksubmitForm确定/el-button /span /template /el-dialog /div /template脚本部分处理数据、调用 API。script setup import { ref, reactive, onMounted } from vue import { ElMessage, ElMessageBox } from element-plus import axios from axios // 搜索表单 const searchForm reactive({ keyword: , status: null }) // 分页参数 const pageParams reactive({ pageNum: 1, pageSize: 10 }) const total ref(0) const tableData ref([]) // 加载数据函数 const loadData async () { try { const params { pageNum: pageParams.pageNum, pageSize: pageParams.pageSize, ...searchForm // 展开搜索条件 } // 移除值为 null 或 undefined 的参数 Object.keys(params).forEach(key { if (params[key] null || params[key] undefined || params[key] ) { delete params[key] } }) const response await axios.get(/api/position/page, { params }) if (response.data.code 200) { tableData.value response.data.data.records total.value response.data.data.total } else { ElMessage.error(response.data.msg || 数据加载失败) } } catch (error) { console.error(请求失败:, error) ElMessage.error(网络请求失败请检查后端服务) } } // 分页事件 const handleSizeChange (val) { pageParams.pageSize val pageParams.pageNum 1 // 切换每页条数时回到第一页 loadData() } const handleCurrentChange (val) { pageParams.pageNum val loadData() } // 对话框相关逻辑新增、编辑、删除 const dialogVisible ref(false) const dialogTitle ref() const formData reactive({ id: null, title: , description: , status: 1, // ... 其他字段 }) const formRef ref() const handleAdd () { dialogTitle.value 发布新岗位 Object.keys(formData).forEach(key { if (key status) { formData[key] 1 } else { formData[key] null } }) dialogVisible.value true } const handleEdit (row) { dialogTitle.value 编辑岗位信息 Object.assign(formData, row) // 将行数据复制到表单 dialogVisible.value true } const submitForm async () { // 表单验证... try { const url formData.id ? /api/position : /api/position const method formData.id ? put : post const response await axios({ method, url, data: formData }) if (response.data.code 200) { ElMessage.success(response.data.msg) dialogVisible.value false loadData() // 刷新列表 } else { ElMessage.error(response.data.msg) } } catch (error) { ElMessage.error(操作失败) } } const handleDelete (id) { ElMessageBox.confirm(确定要删除此岗位吗, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, type: warning }).then(async () { try { const response await axios.delete(/api/position/${id}) if (response.data.code 200) { ElMessage.success(删除成功) loadData() } } catch (error) { ElMessage.error(删除失败) } }) } // 页面加载时获取数据 onMounted(() { loadData() }) /script路由配置在src/router/index.js中添加这个页面的路由。import { createRouter, createWebHistory } from vue-router import PositionList from /views/position/PositionList.vue const routes [ // ... 其他路由 { path: /position, name: Position, component: PositionList } ]至此一个完整的“实习岗位”增删改查CRUD功能就完成了。你通过这个例子可以清晰地看到前端负责页面渲染、用户交互、收集数据、调用后端 API。后端负责接收请求、处理业务逻辑、操作数据库、返回 JSON 数据。数据流Vue 组件 - Axios 请求 - SpringBoot Controller - Service - Mapper - MySQL。5. 项目深化与毕设亮点打造完成基础 CRUD 只是及格线。要让你的毕设脱颖而出需要在这些基础上增加深度和复杂度。5.1 引入权限控制RBAC管理系统必须有权限。最常用的是基于角色的访问控制RBAC。数据库表新增sys_user用户、sys_role角色、sys_menu菜单/权限、sys_user_role用户-角色关联、sys_role_menu角色-菜单关联表。后端实现用户登录后后端生成一个 JWT (JSON Web Token) 返回给前端。前端将 JWT 存储在localStorage或Vuex/Pinia中并在每次请求时通过请求头Authorization: Bearer token传给后端。后端通过拦截器Interceptor或过滤器Filter验证 JWT 的有效性并解析出用户角色和权限。在 Controller 方法上使用注解如PreAuthorize(hasRole(ADMIN))或PreAuthorize(hasAuthority(position:add))进行权限校验。前端实现登录后前端根据用户角色动态生成路由router.addRoute和侧边栏菜单。在按钮级别可以使用自定义指令v-permission来控制显示/隐藏。5.2 文件上传与材料管理实习系统必然涉及简历、报告等文件上传。后端使用 SpringBoot 的MultipartFile接收文件。建议将文件存储到对象存储如阿里云 OSS、腾讯云 COS或服务器本地特定目录需考虑备份和访问路径。在数据库中只存储文件的访问 URL。前端使用 Element Plus 的el-upload组件配置上传地址、文件类型限制、大小限制、多文件上传等。5.3 工作流与状态机实习过程有状态流转如岗位申请待审核 - 已通过/已拒绝 - 实习中 - 待评分 - 已完成。设计在相关实体如internship_application中设计status字段使用枚举定义状态值。实现在 Service 层编写状态变更的逻辑确保状态转换符合业务规则例如不能从“已拒绝”直接跳到“已完成”。可以引入简单的状态模式。5.4 数据统计与报表使用 ECharts 或 AntV 等图表库为管理员提供数据看板。统计内容各专业实习人数分布、企业发布岗位数 Top 10、实习完成率、平均评分等。后端编写复杂的统计查询 SQL或使用 MyBatis Plus 的查询构造器组装。前端调用统计接口用图表组件渲染。5.5 系统部署与文档后端打包使用 Maven 的package命令生成可执行的jar包。java -jar your-project.jar即可运行。前端打包运行npm run build生成静态文件在dist目录。部署方式传统部署将后端jar包放到云服务器用nohup或 systemd 运行。前端dist目录放到 Nginx 中并配置代理转发 API 请求到后端。容器化部署加分项为前后端分别编写Dockerfile使用docker-compose.yml编排一键部署。这能极大提升项目的工程化水平。文档编写清晰的README.md包括项目介绍、技术栈、本地运行指南、部署指南、接口文档可以使用 Swagger 或 Knife4j 自动生成。6. 常见问题与排查清单开发过程中你几乎一定会遇到下面这些问题。按照这个顺序排查能解决 90% 的麻烦。6.1 后端启动失败端口占用Address already in use。改application.yml中的server.port或杀死占用端口的进程。数据库连接失败检查application.yml中的数据库 URL、用户名、密码。确认 MySQL 服务已启动且数据库internship_db已创建。依赖下载失败检查 Maven 的settings.xml是否配置了国内镜像阿里云镜像。IDEA 中刷新 Maven。主类找不到确认SpringBootApplication注解的主类位置正确在包的最外层。6.2 前端运行或打包失败npm install失败网络问题。使用cnpm或配置 npm 淘宝镜像。删除node_modules和package-lock.json后重试。npm run serve失败检查package.json中的脚本和依赖版本冲突。Vue CLI 创建的项目一般没问题。页面空白控制台报错检查浏览器控制台Console和网络Network标签。常见原因是API 请求 404后端服务没启动或请求路径写错。API 请求 500后端代码报错看后端控制台日志。CORS 错误后端跨域配置没生效检查CorsConfig是否被加载。6.3 前后端联调问题前端收不到数据Network 里看请求是否成功状态码 200响应体是否正确。后端用RestController和Result统一封装返回。后端收不到参数GET 请求参数使用RequestParam。POST JSON 数据使用RequestBody且前端请求头Content-Type必须是application/json。POST 表单数据使用RequestParam或封装成对象且不要加RequestBody。日期类型传输错误前后端日期格式不一致。后端实体类中使用LocalDate或JsonFormat注解定义格式。前端传递标准格式字符串如YYYY-MM-DD。6.4 数据库相关问题字段值不对检查实体类字段名是否与数据库列名一致驼峰转下划线默认支持。或者使用TableField注解指定。查询结果不符合预期打开show-sql: true查看 MyBatis Plus 生成的 SQL 语句直接在数据库客户端执行看结果。插入或更新失败检查是否有非空字段未赋值或字段长度超限。6.5 部署问题前端访问后端 API 跨域生产环境Nginx 配置中需要添加代理。或者在后端配置具体的生产环境前端地址而不是*。静态资源 404Nginx 配置的 root 路径是否正确指向了前端dist目录。后端 Jar 包运行报错在服务器上用java -jar your.jar运行查看日志。常见问题是生产环境数据库配置与本地不同需要在application.yml中使用spring.profiles.activeprod并配置application-prod.yml。7. 从“做完”到“做好”给毕设加分的建议不要只满足于功能实现。答辩老师或面试官更看重你的工程化思维和解决问题的能力。代码质量遵循一定的编码规范。使用 IDEA 的代码格式化。关键方法写注释。保持 Controller 层简洁业务逻辑放在 Service 层。Git 提交规范不要一次提交所有代码。按功能点拆分提交提交信息写清楚例如feat: 完成实习岗位管理模块、fix: 修复分页查询总数错误。接口文档集成Swagger或Knife4j。启动项目后访问http://localhost:8080/doc.html就能看到所有 API 的详细说明和测试界面非常专业。单元测试为 Service 层的主要方法编写 JUnit 单元测试。这不仅能验证逻辑也体现了你的测试意识。日志记录使用 SLF4J Logback在关键业务节点如新增、删除、状态变更记录操作日志便于排查问题。简单安全除了权限控制密码存储一定要用 BCrypt 加密绝对不要明文存。对用户输入做基本的校验和过滤防止 SQL 注入和 XSS前端框架如 Vue 默认有一定防护但后端也要注意。设计模式在合适的场景尝试使用简单的设计模式。例如使用工厂模式创建不同的报表生成器使用策略模式处理不同的成绩计算规则。这会是很大的亮点。最后也是最重要的理解你写的每一行代码。不要只是复制粘贴。搞清楚为什么这里要用Autowired为什么那里要用LambdaQueryWrapperVue 的响应式数据ref和reactive有什么区别。在答辩时老师随便指一个地方问你原理你能答上来比任何华丽的功能都更有说服力。这个“校企实习管理系统”的骨架已经给你了从环境搭建到模块开发再到深度优化和问题排查一条完整的路径清晰可见。接下来要做的就是动手把它实现出来并在过程中不断思考、调试和优化。当你把这个项目完整地跑通、部署并清晰地讲出它的设计和技术细节时你的毕设焦虑自然会烟消云散。