ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从毕业设计管理系统看Spring Boot全流程开发与答辩实践

2026/8/31 20:28:34 拓冰建站 浏览量
从毕业设计管理系统看Spring Boot全流程开发与答辩实践 简介本资源是面向高校计算机专业师生的毕业设计全流程数字化管理平台聚焦选题分配、任务书审核、开题评审、中期检查、论文查重与答辩组织等核心环节解决传统人工管理效率低、流程脱节、过程难追溯等痛点适用于本科毕业设计教学管理与信息化系统开发实践。压缩包共2005个文件涵盖1198个JavaScript前端逻辑、198个HTML页面结构、161个JSON配置与接口数据、155个Markdown文档说明及140个CSS样式文件辅以PHP后端脚本、SQL数据库脚本和PDF/DOCX规范文档整体23.57MB结构完整、模块清晰便于二次开发与教学部署。已有67人学习下载提供含‘附赠资源’与详细说明文件的完整工程包包含FastAdmin框架定制后台、Bootstrap响应式前端及Pimple轻量依赖注入实现可直接运行并深入理解高校教务类系统的典型分层架构与业务闭环设计。 每年到三四月份我这边就会集中收到一批计算机专业学生的求助问题几乎都长一个样毕设做什么题目才能既体现工作量又不至于答辩现场被老师两句话问住说实话“毕业设计管理系统”这个选题放在前几年不算稀奇但最近两年反而成了香饽饽原因是它天然长在真实业务上——选题、任务书、开题报告、中期检查、论文查重、答辩评分一整套流程全是硬场景。做电商你绕不开商品订单做管理系统你也绕不开角色权限和多流程状态流转但它比博客、商城多了一个优势每一块功能都能在答辩时讲出“为什么这么设计”。这篇文章我就基于这个计算机系毕业设计管理系统把从需求分析、数据库设计到核心代码实现、部署演示的全过程完整拆一遍争取让你看完之后不仅能复现还能在答辩时讲得头头是道。1. 项目背景与核心需求拆解1.1 高校毕业设计管理中的真实痛点在系统立项之前一定要先搞清楚线下流程到底痛在哪。很多高校的毕业设计管理还停留在“Excel表格 微信群通知 邮箱收文件”的阶段我见过最夸张的情况是选题汇总表在导师、教学秘书、系主任三个人手里来回传了五版最终版本到底是谁改的已经没人能说清。学生提交开题报告用的是 Word 文档文件名从“开题报告_最终版.docx”改到“开题报告_最终版2_真的不改了.docx”导师下载之后还得自己手动改文件名才能区分。这种模式下有四个核心痛点选题阶段靠抢学生盯着群消息“拼手速”导师这边却不知道谁抢到了、哪个课题被选满了经常出现一个课题报了十几个人另一个课题无人问津。过程文件分散在各个私聊记录和邮箱附件里导师想整体查看学生的进度根本无从下手。审核没有留痕导师口头说“可以”过几天学生改了内容到底哪版通过过双方都记不清。中期检查和答辩环节需要大量人工统计教学秘书熬夜汇总成绩、算加权平均还容易算错。这个系统要解决的核心问题就是把这些无序的线下流程变成“每个环节都有记录、每个步骤都可追溯、每个角色都能看到自己关心的数据”的线上闭环。对计算机专业的学生来说这个场景的价值还在于业务需求足够复杂可以自然引出权限管理、状态机、并发控制、文件存储、定时任务这些硬核技术点全做下来简历和答辩都有东西可讲。1.2 角色梳理与功能边界做管理系统最忌讳一上来就写代码先把角色画清楚后面的路才会顺。这个系统我拆成了三个大角色外加一个“隐形角色”角色核心诉求主要操作学生快速选题、按时提交材料、随时看审核结果浏览课题、选择课题、填写任务书回执、提交开题报告、填写周报、提交论文、查看查重结果导师少做重复劳动、高效审批、掌握学生动态发布课题、审核选题、审核开题/任务书、查看学生进度、评阅论文、参与答辩评分管理员教学秘书/系主任掌控整个流程、配置时间窗口、处理异常维护师生账号、配置选题开放时间、管理答辩分组、导入导出统计报表督导/评阅老师只关注评阅和答辩环节评阅论文、录入答辩分数功能边界的划分原则是学生管“提交”导师管“审核”管理员管“规则”。不要在学生的页面里放任何审核功能也不要在导师的页面里放账号管理功能。角色功能一旦越界后面做权限控制会非常痛苦。对系统设计而言角色梳理还直接决定了数据库表结构和接口权限方案。我的建议是权限模型采用经典的 RBAC基于角色的访问控制先建用户表、角色表、用户角色关联表再在接口层用注解控制访问权限这样后面加一个“督导”角色只需要往角色表里插数据、给接口配权限完全不用改主体代码。2. 技术选型与整体架构设计2.1 后端与前端的技术栈这套系统我推荐采用前后端分离架构后端用 Spring Boot 2.7.x MyBatis-Plus MySQL 8.0前端用 Vue 3 Element Plus Axios Vite。选这个组合不是因为它最“高级”而是因为它最稳。Spring Boot 是 Java 系做毕设和中小型管理系统的事实标准资料多遇到问题随便搜都有答案MyBatis-Plus 把单表 CRUD 封装得非常舒服内置分页插件可以直接用不用手写一堆 XMLVue 3 Element Plus 组件齐全做后台管理界面基本就是“搭积木”表格、表单、弹窗、上传组件都有现成的能省大量时间。如果前端基础比较弱不想碰 Node.js 那套工具链也可以退一步用 Thymeleaf 做服务端渲染。但是我从实际答辩效果来说还是建议做前后端分离因为现在高校老师对“前后端分离”这个概念非常买账答辩时你可以理直气壮地说“前端部署在 Nginx、后端打包成 jar两边通过 RESTful API 交互”这句话本身就是加分项。整个系统的架构可以概括为浏览器访问 Vue 页面Vue 通过 Axios 调后端 RESTful 接口后端按业务模块分包处理数据存到 MySQL上传的文件落本地磁盘并通过 Nginx 做静态映射。视频演示的时候这套流程可以当成“系统总体架构”直接画在白板上。2.2 数据库设计核心表结构和字段说明数据库设计是这个系统能不能撑起“全流程管理”说法的关键。我列一下完整的数据表清单和核心用途表名中文含义核心用途sys_user用户表存所有人的登录账号、密码、姓名、角色标识sys_role角色表角色基础信息如学生/导师/管理员sys_user_role用户角色关联表用户和角色多对多关系student学生扩展表学号、班级、方向、指导老师IDteacher导师扩展表工号、职称、研究方向、可带学生数上限topic课题表课题名称、简介、要求、创建人、开放人数selection_record选题记录表谁选了哪个课题、状态待审核/通过/驳回task_book任务书表任务内容、提交文件路径、审核状态proposal_report开题报告表报告内容、提交文件路径、评审意见weekly_report周报表每周进展、存在问题、撰写日期midterm_check中期检查表总体进度、是否延期、检查结果thesis论文表论文文件、版本号、提交状态plagiarism_record查重记录表查重报告、相似度、查重时间defense_group答辩分组表组名、组长、组员、房间、时间defense_score答辩评分表学生ID、评委ID、各维度分数、总分notice通知公告表系统内公告、流程节点提醒几个关键的字段设计思路每张业务表都建议带status字段用 int 类型存状态码比如 0草稿、1待审核、2通过、3驳回。为什么不用 varchar 直接存中文因为状态码可扩展、可比较、数据库体积小翻译成中文这件事交给前端枚举或者后端字典就好。选题记录表必须加唯一约束(student_id)一个学生同一时间只能有一条有效选题记录同时加一个topic_id status的组合索引用来快速查某个课题当前有多少人处于“已通过”状态。论文表单独加一个version字段学生每次重新提交自动加一既保历史版本又能在答辩时展示“我迭代了三次”的过程。2.3 文件存储与上传方案学生提交任务书、开题报告、论文本质上都是文件上传。我见过有人在这个环节引入 FastDFS、MinIO 甚至阿里云 OSS对毕设项目来说其实没必要。最务实的方案是文件直接存到服务器本地磁盘的一个约定目录比如/data/graduation/upload/然后按模块分子目录再通过 Nginx 把这个目录映射成静态资源路径。好处有三个零额外成本不需要申请云存储服务也不用配对象存储的密钥。方便打包演示整个项目压缩之后数据文件跟着走答辩现场不会出现“文件打不开”的尴尬。下载预览简单后端只需要返回文件的相对路径前端拼上静态资源前缀就能在浏览器里打开。要特别注意文件名的处理。用户上传的文件名中文居多而且可能重名落盘时必须重命名。我的习惯是用UUID 时间戳 原文件后缀作为新文件名原始文件名单独存在数据库字段里下载时通过接口把原始文件名作为Content-Disposition返回。这样既避免文件名冲突也避免中文文件名在 URL 传输过程中的编码问题。3. 核心功能模块的落地实现3.1 选题管理并发与去重设计选题环节是整个系统第一个容易翻车的地方。业务流程是导师发布课题管理员审核通过后上线学生在规定时间内选择一个课题提交申请导师确认后选题生效。这里有两层并发问题两个学生同时选同一个课题而该课题只剩 1 个名额怎么保证不会超选同一个学生重复提交选题请求怎么保证不会生成两条记录第一个问题的最优解是加唯一约束加乐观锁。selection_record表给student_id加唯一索引可以防重复提交防超选要在更新课题剩余名额时加条件判断比如UPDATE topic SET selected_count selected_count 1 WHERE id #{topicId} AND selected_count max_student这样“读-改-写”三步变成了一条原子 SQL只要更新影响行数为 1就说明抢选成功影响行数为 0说明名额已经被占满直接返回“课题已满员”。我建议后端 Service 层在事务里先执行这条更新再插入选题记录很多并发问题都能被数据库本身拦掉。第二个问题除了唯一索引之外前端也要做交互层提示比如学生一旦选过课题对应的按钮就置灰。但前端限制只是体验优化真正兜底的还是数据库的唯一约束。3.2 任务书与开题报告的状态流转任务书和开题报告这两个模块本质是同一个模式“学生提交文件 - 导师审核 - 通过或驳回”。这个模式最应该做的事是把状态转换的规则固化下来而不是放任每个接口自己改状态。我强烈建议定义一个状态机。拿开题报告举例状态有DRAFT(0)、SUBMITTED(1)、APPROVED(2)、REJECTED(3)。合法的流转路径是DRAFT - SUBMITTED学生提交SUBMITTED - APPROVED导师通过SUBMITTED - REJECTED导师驳回REJECTED - SUBMITTED学生修改后重新提交如果代码里没有状态校验学生把一份驳回的报告反复提交、导师把已通过的状态改回待审核这种操作就会出现最后数据全乱。实现上可以在 Service 层写一个状态转换校验方法每次更新前先装载旧记录再校验当前状态和目标状态是否在允许的转换矩阵里。我实际写代码时喜欢用一个枚举类把所有状态转换规则表描述出来这样不仅校验逻辑清晰答辩时也可以说“我用了状态机的思想管理流程”这比单纯堆 CRUD 高级不少。3.3 中期检查与进度预警中期检查模块要解决的问题是导师怎么知道学生到底有没有在推进很多学生交了开题报告就人间蒸发直到最后一周才开始写论文。这里我设计了两个机制第一个是周报机制。学生每周提交一篇简单的工作记录内容包括本周进展、遇到的问题、下周计划。导师在首页就能看到所有指导学生的最近周报时间超过 7 天没有提交的学生系统自动标记成“进度预警”。第二个是中期检查节点。管理员可以配置中期检查的起止时间在这个时间窗口内学生提交中期检查表导师审核后给出“按计划进行/存在风险/严重滞后”三个结论。这里需要引入 Spring 的定时任务Scheduled每天凌晨扫描一次把超过预警阈值的学生统计出来插入到通知表里导师一登录就能看到。定时任务的写法很简单核心逻辑是查weekly_report表里每个学生最新的提交时间再和学生表里的指导老师 ID 关联最后生成提醒数据。注意定时任务要加分布式锁或者至少保证单实例执行否则部署多副本的时候会因为重复执行产生重复通知。3.4 论文提交、查重与版本管理论文提交模块最容易被忽略的是“版本管理”。学生不是一次性交终稿的很多人会反复修改如果系统只有一个“上传论文”按钮每次上传都覆盖旧文件学生想找上一版回来就没戏了。我在thesis表里设计了一个version字段每次提交时后端读取当前最大版本号并加一所有历史文件都保留在磁盘里数据库里每条记录对应一个版本。查重模块是答辩提问的高发区。学生自己很难接触学校官方的查重系统所以要做一个查重模块的合理方案是设计一个plagiarism_record表前端提供一个“提交查重”按钮后端调用一个查重接口。如果做模拟后端可以生成一个随机相似度并返回一份模拟报告帮助演示流程如果对接真实API可以在代码里预留PlagiarismService接口用 HTTP 客户端调用第三方服务存储返回的报告编号和相似度等学校提供接口时再替换实现。务必注意查重是敏感操作真实场景中论文内容不能随便传给没有资质的外部服务。在系统的交互文案里要把查重结果标注为“模拟演示数据”避免导师误判。3.5 答辩分组与评分统计答辩模块可以做成两部分分组管理和评分录入。分组管理由管理员操作根据学生所在班级、课题方向把学生随机分到若干个答辩组每个组关联一个组长、若干评委、答辩时间、答辩地点。这个功能可以用一个简单的分页列表加一个“自动分组”按钮实现自动分组的算法甚至可以就是按学生 ID 取模真实业务里更复杂的分组算法反而没必要在毕设阶段做。评分表的字段要体现“维度化”。比如总分 100 分拆成“论文规范性 20 分”“表达能力 20 分”“技术创新性 30 分”“回答准确性 30 分”。评委登录系统后只看到分配给自己的学生列表录入每个维度的分数。最终成绩由后端统一计算加权和再汇总到成绩统计页面。一个小的经验评分之前要注意去重校验一个评委对同一个学生只能评分一次否则改来改去会出现多次评分记录。实现方式就是defense_score表加联合唯一索引(student_id, teacher_id)。4. 实操过程与关键代码示例4.1 项目初始化与依赖配置假设你拿到的压缩包解压之后是这个结构前端一个目录后端一个目录外加一个 readme。先看后端。使用 IDEA 打开项目之后首先要确认pom.xml里的依赖是否齐全。我这里给一个核心依赖清单dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdcn.dev33/groupId artifactIdsa-token-spring-boot-starter/artifactId version1.34.0/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependenciesapplication.yml里最需要注意的几项是数据库连接、MyBatis-Plus 配置和文件上传大小限制spring: datasource: url: jdbc:mysql://localhost:3306/graduation_manage?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 servlet: multipart: max-file-size: 100MB max-request-size: 200MB mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 sa-token: token-name: satoken timeout: 2592000 is-concurrent: true is-share: true这里有两个细节数据库连接串里必须加characterEncodingutf8和serverTimezoneAsia/Shanghai否则中文乱码、时间差 8 小时的问题会让你查一个晚上文件上传大小限制必须调大因为学生开放的论文往往有几十兆Spring Boot 默认只允许 1MB。4.2 基于 Sa-Token 的登录鉴权与角色控制登录鉴权我推荐用 Sa-Token而不是 Spring Security。原因很简单Sa-Token 开箱即用API 非常直白不需要写一堆 SecurityConfig 的过滤器链对毕设这种体量的项目完全够用而且答辩时你能说清每个注解是干嘛的。登录接口的核心逻辑是根据用户名查出用户校验密码登录成功后调用StpUtil.login(userId)再把用户角色信息写入会话PostMapping(/login) public ApiResult login(RequestBody Valid LoginDTO dto) { SysUser user userService.findByUsername(dto.getUsername()); if (user null || !PasswordUtil.matches(dto.getPassword(), user.getPassword())) { return ApiResult.error(用户名或密码错误); } StpUtil.login(user.getId()); StpUtil.getSession().set(role, user.getRole()); return ApiResult.success(StpUtil.getTokenInfo()); }接口权限控制直接用注解SaCheckRole(teacher) PostMapping(/topic) public ApiResult createTopic(RequestBody Topic topic) { topicService.save(topic); return ApiResult.success(); }这里有一个很实用的小知识Sa-Token 的SaCheckRole注解走的是拦截器配置你需要在启动类或配置类里注册SaInterceptor否则注解不生效。我见过很多人把依赖加上之后发现怎么鉴权不生效就是漏了这一步。另外前端拿到 token 之后每次请求要在 header 里带satoken这个字段前后端对应的字段名必须一致。4.3 选题接口的完整实现选题接口是整个系统里最需要小心写的接口。我直接给一个可落地的版本。先定义 Service 接口public interface TopicSelectionService { /** * 学生选择课题 */ void selectTopic(Long studentId, Long topicId); }实现类里做三步校验、占名额、插入记录Override Transactional(rollbackFor Exception.class) public void selectTopic(Long studentId, Long topicId) { // 1. 校验学生是否已有有效选题 Long count selectionRecordMapper.selectCount( new LambdaQueryWrapperSelectionRecord() .eq(SelectionRecord::getStudentId, studentId) .ne(SelectionRecord::getStatus, 3)); if (count 0) { throw new BizException(你已存在一条待处理或已通过的选题申请); } // 2. 占住名额通过 update 的条件判断保证不超选 int rows topicMapper.occupySeat(topicId); if (rows 0) { throw new BizException(该课题名额已满); } // 3. 插入选题记录 SelectionRecord record new SelectionRecord(); record.setStudentId(studentId); record.setTopicId(topicId); record.setStatus(1); // 待导师审核 selectionRecordMapper.insert(record); }topicMapper.occupySeat对应的 XML 是update idoccupySeat UPDATE topic SET selected_count selected_count 1 WHERE id #{topicId} AND selected_count lt; max_student /update注意这里rollbackFor Exception.class必须写Spring 默认只回滚 RuntimeException如果你抛的是自定义Exception而不指定类型占住的名额就回滚不掉会出现“没有选上但名额少了”的诡异 bug。这一步我踩过坑印象特别深。4.4 文件上传与在线预览文件上传的后端代码不复杂核心是落盘路径和返回 URL。我习惯把文件保存到可配置目录通过配置文件里的upload.path指定PostMapping(/upload) public ApiResult upload(RequestParam(file) MultipartFile file, RequestParam(module) String module) { if (file.isEmpty()) { return ApiResult.error(上传文件不能为空); } String originalFilename file.getOriginalFilename(); String ext StringUtils.getFilenameExtension(originalFilename); String newFileName UUID.randomUUID().toString().replace(-, ) . ext; String datePath LocalDate.now().format(DateTimeFormatter.ofPattern(yyyy/MM/dd)); File dir new File(uploadPath / module / datePath); if (!dir.exists()) { dir.mkdirs(); } File dest new File(dir, newFileName); try { file.transferTo(dest); } catch (IOException e) { throw new BizException(文件保存失败); } String relativePath / module / datePath / newFileName; return ApiResult.success(relativePath); }文件预览有个麻烦点如果是 PDF 可以浏览器直接打开但如果是 Word 文档直接在浏览器里没法看。两个思路一是把上传入口限制成“上转 PDF”在系统提示里写明“请将 Word 转成 PDF 后上传”二是引入 LibreOffice 做在线转换但这对毕设来说太重了。我推荐第一种简单省事答辩演示也不会出问题。前端接入 Element Plus 的el-upload组件时注意在请求头里带上 tokenel-upload :actionuploadUrl :headers{ satoken: getToken() } namefile :data{ module: proposal } el-button typeprimary上传开题报告/el-button /el-upload4.5 查重模块的模拟与对接查重模块我建议做成接口抽象 默认模拟实现。定义接口public interface PlagiarismCheckService { /** * 提交查重返回一个任务id */ String submitCheck(Long thesisId); /** * 查询结果 */ PlagiarismResult queryResult(String taskId); }模拟实现里可以让线程睡几秒钟模拟查重需要时间再生成一个 5% 到 30% 之间的相似度Service public class MockPlagiarismCheckServiceImpl implements PlagiarismCheckService { Override public String submitCheck(Long thesisId) { String taskId UUID.randomUUID().toString(); // 实际开发中可以存入数据库 return taskId; } Override public PlagiarismResult queryResult(String taskId) { int similarity 5 (int) (Math.random() * 25); PlagiarismResult result new PlagiarismResult(); result.setTaskId(taskId); result.setSimilarity(similarity); result.setStatus(1); return result; } }前端拿到 taskId 之后用轮询方式每隔几秒调一次queryResult直到 status 变成完成。把这段逻辑讲清楚答辩时可以说“我了解文本查重的异步处理模型模拟了提交任务和轮询结果的完整链路”这个回答方向很加分。5. 常见问题与排查实录5.1 并发选题导致超选现象两个学生同时点击选题数据库里同一个课题通过的人数超过了max_student。排查思路先看 SQL 日志确认更新名额的 SQL 是否带上了selected_count max_student条件。我遇到过有人把这条条件写错了大于小于号方向搞反或者直接在 Java 代码里做了 if 判断而不是用数据库原子更新并发下必出问题。解决方案就是前面讲过的把“检查 更新”合并成一条带条件 UPDATE把并发控制交给数据库。另外要在selection_record表加上唯一索引兜底双保险。5.2 文件上传超限与预览失败现象上传大一点的文件就报FileSizeLimitExceededException。排查思路先看 Spring 配置的max-file-size再看 Nginx 的client_max_body_size。这两个都要调大任何一个默认限制都会被卡住。预览失败的另一个常见原因是文件路径不对。Nginx 映射静态目录时location /upload/的 alias 路径必须和实际落盘路径对应。举个例子如果文件存在/data/graduation/upload/proposal/2024/05/task.pdfNginx 配置应该是location /upload/ { alias /data/graduation/upload/; }少了末尾斜杠或者路径写错浏览器访问就是 404。5.3 流程状态跳级问题现象学生提交的任务书还没审核通过就能直接提交开题报告或者开题报告被驳回了学生还能进入中期检查。排查思路系统缺少状态流转的强校验。每个流程的接口入口处都要先查上一环节的状态是否满足条件。我在做流程控制时喜欢写一个“流程校验工具类”比如FlowCheckUtil.checkProposalCanSubmit(studentId)里面查任务书状态和开题报告当前状态不满足就抛异常。这样在多个接口里复用不会出现同样的校验逻辑写两遍、最后改了一处漏了另一处的情况。5.4 数据库中文乱码与时间时区现象页面上提交的中文说明库里存成了问号或者时间字段差了 8 小时。排查思路先看数据库连接串是否带characterEncodingutf8再看 MySQL 建表时的默认字符集是不是utf8mb4最后看服务器和数据库所在的时区。连接串带serverTimezoneAsia/Shanghai之后绝大多数时区问题都能解决。建表时我建议统一在application.yml里配上CREATE DATABASE graduation_manage DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;如果已经建了表也可以执行ALTER TABLE xxx CONVERT TO CHARACTER SET utf8mb4;修复旧数据。5.5 演示环境准备不充分现象答辩现场演示的时候登录不进去或者列表是空的或者某个按钮报了“系统错误”。排查思路这不是代码问题是演示环境没准备好。不要现场新建账号、现场走全流程一定要提前准备好“预制数据”。我的建议是准备三个演示账号分别对应学生、导师、管理员提前在库里造好完整过程数据学生 A 已选题、任务书已提交、开题报告已通过、周报有 3 条、中期检查已完成、论文已提交、查重结果有相似度、答辩组已分配。演示的时候按这个精心设计好的顺序走每一个页面都有数据可看。这样导师会觉得这个系统是真在跑业务而不是个空壳子。6. 部署上线与答辩演示技巧6.1 本地打包含部署项目完成后要能在 Linux 服务器上跑起来。后端先 Maven 打包mvn clean package -DskipTests打包完成后target目录下会生成一个 jar 文件直接启动nohup java -jar graduation-system-1.0.0.jar --spring.profiles.activeprod app.log 21 前端先构建生产包npm run build构建产物在dist目录把这个目录拷贝到 Nginx 的 html 目录下然后配置反向代理。如果前后端都在同一台服务器前端请求 API 时要把/api前缀代理到后端的 8080 端口location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }6.2 使用 Docker Compose 一键启动如果你的毕设要体现“工程化能力”Docker Compose 是加分项。至少可以把 MySQL 和 Redis 用容器跑起来后端 jar 包也可以打成镜像。一个简单的docker-compose.yml长这样version: 3 services: mysql: image: mysql:8.0 container_name: graduation-mysql environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: graduation_manage ports: - 3306:3306 volumes: - ./mysql/data:/var/lib/mysql - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql backend: build: ./backend container_name: graduation-backend depends_on: - mysql ports: - 8080:8080 frontend: image: nginx:stable container_name: graduation-frontend ports: - 80:80 volumes: - ./frontend/dist:/usr/share/nginx/html - ./frontend/nginx.conf:/etc/nginx/conf.d/default.conf depends_on: - backend把init.sql放到 mysql 初始化目录里MySQL 容器第一次启动时会自动建表、导数据这一点非常方便。写进文档里评委老师如果现场想看环境部署可以直接敲docker compose up -d对工程能力的评价会高很多。6.3 答辩演示的准备工作答辩演示这个环节准备得好不好效果差别非常大。我总结了一个清单照着准备基本不会冷场准备三套演示账号提前截图记录密码不要现场输入。完整走一遍学生提交材料、导师审核、管理员分组的流程把每一步可能出现的弹窗和异常都提前过一遍。电脑充好电浏览器提前把系统首页开好收藏好登录地址。打开开发者工具也没关系但不要现场改代码、不要现场重启项目。如果演示中途报错不要慌先看一眼是否是网络或环境问题如果是代码 bug直接说“这个边界情况我下来再处理”不要在现场调试。有一个技巧一定要用上把“选题并发控制”“文件版本管理”“状态机流转”“定时任务预警”这四个点做成能力清单答辩的时候导师不论问到哪个模块你都能往这四件事上靠。这四件事代表着数据库设计能力、并发控制能力、流程建模能力和工程化能力正好覆盖了计算机专业毕业设计最看重的几个维度。这套系统我前后带学生做过好几个版本最深的体会是不要一上来就写代码先把状态流转图和角色权限图画明白。你要是把流程梳理清楚了后面的代码其实就是往框架里填逻辑。最后再分享一个小技巧答辩前把演示账号的首页数据、选题记录、各环节审核都提前造好别现场临时注册新账号跑流程一旦某个状态没对上冷场五分钟分数就直接往下掉了。祝你的毕设顺利过审。本文还有配套的精品资源点击获取