
简介本资源是一套面向计算机专业本科生及Java初学者的毕业设计级实战项目聚焦校园竞赛全流程数字化管理解决高校竞赛信息发布滞后、报名分散、成绩统计低效等实际问题。压缩包为RAR格式共27.36MB包含Spring Boot后端源码、Vue前端工程、MySQL建表SQL脚本及配套毕业论文含需求分析、系统设计、实现细节与测试结果结构清晰、注释完整数据库设计符合范式且预留扩展字段前后端分离架构便于二次开发与功能迭代。目前已有53人学习下载适合作为课程设计、大作业或毕业设计选题参考——开箱即用支持本地快速部署与功能验证论文内容详实可直接用于答辩材料整理代码模块划分明确用户中心、竞赛发布、报名审核、成绩录入与图表统计有助于深入理解企业级Web应用开发规范与全栈协作逻辑。1. 项目概述与选题背景1.1 为什么选校园竞赛管理系统作为毕设每年到了毕业季选什么题目做毕业设计都能在技术论坛里看到大量求助帖。翻了十几个热门毕设选题后发现校园竞赛管理系统是一个被反复提及但依然不过时的项目。原因其实很简单第一它的业务边界非常清晰不像电商、社交系统那样业务庞大难以收尾第二竞赛报名、作品提交、成绩管理、公告发布这几个核心模块能完整覆盖一套Web系统最常见的CRUD、权限控制、文件处理、统计展示等开发场景第三前后端分离的技术栈跟目前企业招聘的主流要求完全对齐。这套系统的典型用户是学校教务处或二级学院竞赛管理老师。过去办一场校内编程竞赛从下发通知、收集报名表、汇总参赛作品到组织评委打分、统计排名、发布获奖公告全部靠Excel和邮件完成。信息分散、版本混乱、统计容易出错更麻烦的是参赛者无法实时了解审核进度和比赛安排。系统的价值就是把这些线下流程搬到线上学生在线报名、上传作品、查看进度老师在线审核、分配评委、录入成绩系统自动汇总排名并公示结果。如果你是计算机相关专业的学生拿这个题目做毕设无论后续是求职还是继续深造都能用这套项目完整展示SpringBoot和Vue的技术掌握程度。如果是指导老师或代跑代码的同学这套项目同样适合作为教学案例或快速交付模板——代码结构干净自带论文和数据库脚本改动成本低。1.2 项目整体架构选型技术选型这块是毕设答辩时老师必问的点先讲清楚为什么选这套组合。后端选择SpringBoot是因为它解决了传统SSH和SSM框架配置繁琐的问题——内置Tomcat、自动配置、起步依赖一个可运行的Web服务十几分钟就能搭起来对于学生党来说学习曲线友好出了问题社区解决方案也多。前端选择Vue是因为当前主流的前后端分离项目里Vue的普及率最高配合Vue Router做路由控制、Axios做请求封装整个前端工程组织起来很清晰。数据库是MySQL项目里附带SQL脚本导入即用。存储引擎用InnoDB事务提交和行级锁的配合在竞赛报名这种高频写入场景下比MyISAM稳得多。开发环境用Navicat或MySQL Workbench都可以个人更推荐后者免费且够用。Redis在这套系统里不是必须的但如果想让项目看起来更有亮点可以在公告缓存、登录Token存储这两个地方引入答辩时可以多讲一段缓存设计思路。整个项目的目录结构是标准的SpringBoot分层Controller接收请求、Service处理业务、Mapper操作数据库。前端是Vue-CLI构建的单页应用API层统一封装。前后端通过JSON交互用Nginx或开发环境的Proxy做跨域转发。这套架构最大的好处是——你答辩的时候能非常清晰地画出调用链路图而不用费劲解释各种花哨的设计模式。2. 系统功能拆解与数据库设计2.1 核心业务模块梳理竞赛管理系统的功能模块看上去多但归结起来就是三条主线竞赛生命周期管理、用户全流程参与、数据统计与展示。竞赛生命周期管理是管理员的核心工作区。从创建竞赛开始要填写竞赛名称、类别学科竞赛/技能竞赛/创新创业、参赛对象、报名起止时间、作品提交截止时间、比赛说明、奖项设置等信息。系统需要支持管理员对竞赛进行编辑、发布、下架等操作。报名截止后管理员要能批量导出报名信息方便线下确认。用户全流程参与这条线是开发量最大的部分。学生的操作路径是注册登录 — 浏览竞赛列表 — 查看竞赛详情 — 提交报名申请 — 上传参赛作品 — 查看审核结果 — 查看最终成绩。每一步在数据库里都有对应的状态流转。这里的难点不在单个接口的开发而在状态流转的严谨性比如报名结束后就不能再提交作品审核通过后不能再修改报名信息。这类规则逻辑如果分散写在各个接口里后期维护很痛苦。我建议在Service层做一个统一的状态校验方法每次操作前先检查当前竞赛所处阶段和用户记录状态类似于一个迷你状态机。数据统计与展示是麻雀虽小五脏俱全的模块。系统首页要展示竞赛数量、参赛人数、通过审核人数等实时数据这需要写聚合查询SQL竞赛列表页要支持按类别、状态筛选管理员后台还要有每个学生参赛记录的分类统计。这些内容都涉及复杂的SQL统计查询加分项是引入ECharts做可视化图表展示用柱状图展示各学院参赛人数、用折线图展示每日报名增量我实测下来效果非常出众答辩的时候直接加分。2.2 三套用户角色与权限设计权限设计是这套系统里最容易让老师追问的环节。系统涉及三种角色管理员、教师评委、学生三者对系统的使用深度和功能边界完全不同。如果只用前端路由做权限区分简单是简单但一旦有人绕过前端直接调接口数据就裸奔了。所以我的做法是前端路由控制加后端Java端拦截器双重校验。前端部分Vue Router里配置路由时加上meta信息标记每个路由需要的角色在路由守卫里判断当前登录用户的角色信息不匹配就重定向到登录页。后端部分写一个拦截器HandlerInterceptor拦截所有需要登录的请求通过请求头里携带的Token解析出用户角色再校验当前请求路径是否在角色允许的访问范围内。数据库层面用户表用role字段区分角色1表示管理员2表示教师3表示学生。这个设计很直接但要注意的一点是——不要为了展示技术水平而引入Shiro或Spring Security的完整权限框架。毕设项目里这种轻量级的方案足够而且代码简单清晰答辩容易讲明白。如果真的想用框架Sping Security也行但学习成本高代码量大幅增加如果熟练度不够反而容易被问住。2.3 数据库表结构设计要点数据库脚本是整个项目的地基表设计的好坏直接影响开发的顺畅程度。我对照这套系统核心流程整理出8张核心表用户表user、竞赛信息表competition、报名表registration、作品表work、评委分配表judge_assign、成绩表score、公告表announcement、系统日志表log。用户表是最基础的包含id、username、password、role、real_name、student_no、college、phone、email、create_time等字段。这里有几个细节需要注意密码存的是MD5或BCrypt加密后的密文绝对不允许明文存储学号字段要加唯一索引因为学号是学生用户的天然业务主键role字段建议用tinyint类型而非varchar查询效率更高。竞赛信息表是核心业务表字段包括id、competition_name、category、description、signup_start_time、signup_end_time、work_submit_deadline、max_team_members、status、create_by、create_timestatus字段用0-3表示不同阶段0是草稿、1是报名中、2是评审中、3是已结束。这里最关键的索引设计是联合索引status, signup_end_time因为后端所有查询都是先按状态过滤再按时间排序这个联合索引能极大提速。报名表需要设计成带有唯一约束competition_id, user_id防止同一个学生重复报名。成绩表是整个系统里关联关系最复杂的一张表关联了学生、竞赛、评委三方的信息。字段设计上除基础id、competition_id、user_id、judge_id、score、comment、create_time外还要加上一个final_score字段用来标记评委打分结束后的最终成绩。我在实际开发中发现一个坑多个评委给同一个参赛作品打分时如果只存一条记录后打的分数会覆盖先打的分数。正确做法是每个评委打分都独立存一条记录学生最终成绩在Service层用平均分或去掉最高最低后的均值来计算。由于同时存放了多张表的关联查询成绩统计的SQL复杂度和性能问题也在这里体现出来建议提前建好视图或写更精准的JOIN语句。3. 核心代码实现与关键业务逻辑3.1 后端SpringBoot架构分层与核心代码后端的工程结构必须工整包名用公司域名反写或者自己的域名类名和数据库字段名严格对应。项目包结构如下controller、service、mapper、entity、config、common、utils。controller层只负责参数接收和结果封装不写业务逻辑service层写业务逻辑接口和实现类分开mapper层是MyBatis的接口和XML文件。以竞赛列表分页查询为例Controller层代码非常薄RestController RequestMapping(/api/competition) public class CompetitionController { Autowired private CompetitionService competitionService; GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String category, RequestParam(required false) Integer status) { PageResultCompetition pageResult competitionService.queryPage(pageNum, pageSize, category, status); return Result.success(pageResult); } }Service实现类里分页查询使用MyBatis的PageHelper插件这是国内学术和商业项目中非常主流的方案。核心逻辑就是调用PageHelper.startPage()方法后紧接着执行查询PageHelper会拦截下一句SQL帮你完成分页。这里有一个非常容易踩的坑——startPage()之后必须紧跟查询语句中间不能有任何其他数据库操作否则分页会失效。很多同学调试一整天没查出原因最后发现是在这两行代码中间写了一个日志查询。业务逻辑层我把状态流转校验统一封装在一个方法里public void checkCompetitionStage(Competition competition, Integer expectStage) { Integer currentStatus competition.getStatus(); if (!currentStatus.equals(expectStage)) { throw new BusinessException(当前竞赛不在可操作的状态阶段); } Date now new Date(); if (now.after(competition.getSignupEndTime())) { throw new BusinessException(报名已截止); } }这个方法被报名、提交作品、评分等多个接口复用规则集中管理代码不散落后续改需求也很方便。这样在答辩时讲业务规则能清楚地说出哪个接口在哪个阶段被限制不是因为忘记写判断而是统一约束住了。3.2 前端Vue工程搭建与关键页面实现前端工程用Vue-CLI搭建进入项目的src目录后主要包含以下目录api封装Axios请求、router路由配置、storeVuex状态管理、views页面组件、components可复用的局部组件、utils工具类。Axios请求封装是前端工程质量的风向标。我的习惯是创建一个request.js文件统一设置baseURL、请求超时时间、请求拦截器和响应拦截器。请求拦截器里从localStorage中取出Token加在请求头里service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config })响应拦截器里统一处理后端返回的code值。后端约定返回格式是code为200表示成功其他为失败。业务错误统一弹出提示避免每个页面重复写错误处理。遇到401时自动清掉本地登录信息跳回登录页。这部分代码写好后整个前端开发效率能提升一大截不用每个页面重复处理错误提示。路由设计上注意前端路由要用history模式还是hash模式。开发环境随便但部署到服务器就要慎重了。hash模式兼容性最好URL里多个#号不太美观history模式URL漂亮但需要Nginx配置try_files否则刷新页面就404。实践建议是如果后端部署环境是Tomcat用hash模式省心如果前端单独用Nginx部署用history模式配置好rewrite就行。3.3 文件上传与M3U8视频播放的坑校园竞赛系统里有个非常常见的需求是作品提交。对设计类、视频类竞赛来说提交的往往不是简单的文档而是几MB甚至几百MB的视频文件。这就牵出热词里提到的vue播放m3u8的问题。后端接收文件上传用SpringBoot的MultipartFile接口在配置文件里设置上传大小限制。这里我踩过一个真实的坑SpringBoot 2.x的新版本里文件上传常用配置项已经和旧版本不一样了。老版本用spring.servlet.multipart.max-file-size这个配置有效但在某些SpringBoot 3.x的升级路径里配置项换成了spring.servlet.multipart.max-request-size和spring.servlet.multipart.max-file-size的组合并且默认大小限制是1MB不设置的话提交一个超过1MB的作品就会直接报错。视频转码方面我推荐的做法是不直接存用户上传的原视频而是在前端用ffmpeg.wasm或者后端调用FFmpeg把视频转成m3u8切片格式。转m3u8的主要好处是视频加载时可以边下边播不需要等整个文件下载完成对校园网络环境下的大视频播放非常友好。具体实施上后端执行FFmpeg命令是ffmpeg -i input.mp4 -profile:v baseline -level 3.0 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls output.m3u8这条命令把mp4切成10秒一个的ts切片生成的m3u8文件就是视频播放列表。前端Vue播放m3u8用video.js加videojs-contrib-hls插件或者直接用hls.js代码量很小import Hls from hls.js if (Hls.isSupported()) { const hls new Hls() hls.loadSource(/video/xxx.m3u8) hls.attachMedia(videoElement) }要注意的一点是m3u8文件生成后部署时后端必须放行ts和m3u8文件的访问权限否则前端拿到了播放列表地址却加载不了切片文件播放器就会一直转圈。另外切片文件多了之后要写定期清理脚本否则服务器磁盘很快被占满。这个细节在答辩时主动讲出来老师会觉得你想得比一般学生周到。4. 从0到1的部署与运行实操记录4.1 环境准备JDK、MySQL、Node.js这套系统的运行环境依赖我按版本兼容性整理了一个清单。后端需要JDK 8或JDK 11、Maven 3.6以上前端需要Node.js 14以上、npm 6以上数据库用MySQL 5.7或8.0都可以。这些版本组合是我测试过最稳定的注意SpringBoot 2.5以上版本对JDK 8的支持是完整的但SpringBoot 3.x就必须用JDK 17了——热词里那个“springboot版本太高”的问题就是这么来的很多同学下载了最新版本的SpringBoot却发现本地JDK版本太老直接编译失败。数据库准备最快的方式是直接导入项目附带的SQL脚本。用命令行导入mysql -u root -p competition_system.sql或者用Navicat图形界面导入。导入后检查一下表是否齐全、数据能不能正常查询。打开数据库我发现有的同学容易忽略的一个问题MySQL 8.0默认的字符集是utf8mb4但SQL脚本如果由旧版本MySQL导出可能还是utf8导致中文字符乱码。所以导入前最好先执行ALTER DATABASE competition_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;避免后期出现中文乱码这个低级但致命的问题。4.2 后端启动全过程后端项目的配置集中在application.yml文件里。核心配置项有数据源配置、端口配置、文件上传配置、MyBatis配置。数据源配置要改三处数据库地址、数据库用户名、数据库密码。我见过最多的启动失败原因就是这三个地方没改对本地数据库用户名密码跟配置不一致启动直接报Connection refused。配置好之后在项目根目录执行mvn spring-boot:run或者先用Maven打成jar包再运行mvn clean package -DskipTests java -jar target/competition-system-0.0.1.jar第一次启动会比较慢因为Maven要下载依赖jar包。如果网络状况不好建议在Maven的settings.xml里配置阿里云镜像下载速度能快好几倍。启动成功后控制台会打印SpringBoot的logo和Tomcat启动的端口号看到“StartedxxxApplication in x.xxx seconds”就代表后端起来了。用浏览器或Postman访问http://localhost:8080/api/competition/list能返回JSON数据就说明后端完全跑通了。4.3 前端搭建与部署细节前端部分先用npm安装依赖。这个过程中最痛苦的是等待需要有点心理准备。使用npm在国内网络环境下会经常超时我的做法是切换到淘宝镜像命令是npm config set registry https://registry.npmmirror.com/然后用npm install安装依赖。如果你看到下载了一会儿就报各种奇怪的错误大概率是镜像问题或node-sass安装失败。node-sass是前端项目里出了名的“坑王”常见的解决方案是切换Node版本或者改用sassdart-sass配置基本兼容。依赖安装完成后运行开发环境npm run serve默认端口是8080如果和后端端口冲突了需要在vue.config.js里修改devServer的port配置。更重要的配置是开发环境下前端如何访问后端接口。Vue-CLI提供了代理功能在vue.config.js里配置devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端代码里请求/api/xxx实际上会被转发到后端的8080端口解决了开发环境下的跨域问题。正式部署时前端执行npm run build生成dist目录把这个目录交给Nginx或者放入SpringBoot的static目录问题都不大。个人实测更推荐Nginx方式把dist目录内容复制到nginx的html目录下配置好location和反向代理并增加history模式的try_files配置。如果后端打包直接把dist复制到resources/static目录再重新打jar包也能正常出效果不过这种方式对前端修改不友好我想不出有多少同学毕设以后还会改前端。4.4 线上部署用宝塔还是Docker很多同学毕设演示的时候要开一堆软件今天可以把服务器环境用宝塔面板或Docker容器化整理干净。宝塔面板是国内最流行的服务器运维面板图形化界面装MySQL、Nginx、JDK都可以一键搞。对于毕设来说它的好处是上手快普通学生也能在半小时内把项目跑起来。Docker则是用配置文件把环境固定下来。把后端jar包、前端dist目录、MySQL数据库全部容器化任何一台新服务器上只需要装有DockerCompose一键docker-compose up -d就能拉起整个项目。这在项目的可移植性上做得更彻底不容易因为环境差异翻车。缺点是要学Docker的基本用法学习成本相对宝塔面板略高。准备二选一的情况下我更推荐Docker因为它能讲出一个很完整的“持续集成、自动化部署”的故事对评分有实打实的助益。5. 常见问题与排错大全5.1 SQL脚本导入报错与乱码问题从附带的SQL脚本导入数据库时最常见的报错有两类。第一类是SQL脚本里包含某些MySQL 8.0不支持的老语法比如TYPEInnoDB这种写法。遇到这种情况最简单的处理方式是用文本编辑器打开SQL文件全局搜索TYPE替换成ENGINE重新导入即可。第二类是因为版本间默认行为差异导致的导入报错比如sql_mode太严格导致某些文本字段默认值校验失败可以在MySQL的my.ini中修改sql_mode或者使用SET GLOBAL sql_mode 命令临时放松限制。乱码问题上面提到了根源多半是字符集不一致。导入前统一使用utf8mb4字符集导入时确认SQL文件的编码格式是UTF-8如果SQL文件是用Windows记事本保存的可能带了BOM头这会导致第一条SQL执行失败。解法是用VSCode这类编辑器另存为UTF-8无BOM格式。5.2 跨域问题排查前后端联调的经典坎前后端分离项目跨域问题是躲不掉的坎。开发模式下用代理解决生产模式下用Nginx反向代理解决。如果两种模式都出现了跨域报错需要从三个层面排查前端代理配置是否正确后端是否开启了CORS配置网络请求实际的Origin地址是什么。我在排查这个问题时的顺序是先看浏览器控制台的具体报错信息如果报的是Access-Control-Allow-Origin缺失就在后端加一个CORS过滤器或使用SpringBoot的CrossOrigin注解如果报的是404检查后端接口路径和前端请求路径是否一致很多时候是因为Controller的RequestMapping路径写错了被误以为跨域。调试时建议打开浏览器开发者工具的Network面板从请求的实际状态码去定位问题很多同学报错只看Console红色的字效果不佳。5.3 Vue项目运行时的常见报错与解决前端开发里最常见的错误简直可以总结成一本小册子。第一类是npm install时报依赖版本冲突错误通常指定legacy-peer-deps参数解决npm install --legacy-peer-deps这个参数会让npm忽略依赖之间的peer依赖冲突在使用Vue2生态的一些老包时特别有用。第二类是npm run serve启动时报端口被占用改一下vue.config.js里的port即可。第三类是编译时报语法错误比如在template里写了不存在的变量或filter这种通常是因为Vue2和Vue3的语法差异造成的。注意如果项目用的是Vue2就坚持用Vue2的Options API写法如果换成Vue3Composition API才是主流混着写容易把自己绕晕。5.4 SQL注入必须讲清楚的安全考点热词列表里有sql注入、sql注入万能密码绕过、ruoyi菜单sql这几个条目说明这类问题在开发中确实受到关注。答辩时老师问数据库安全你如果能主动说出SQL注入的原理和防御方案是大大的加分项。SQL注入的核心原因是拼接SQL字符串。比如查询登录用户的逻辑// 错误写法 String sql SELECT * FROM user WHERE username username AND password password ;当攻击者在用户名输入框填写or 11 --时拼接出来的SQL就变成了SELECT * FROM user WHERE username OR 11 -- AND password xxx后面的密码判断被注释符直接吃掉整个SQL条件恒为真攻击者无需密码就登录成功。这就是万能密码绕过的原理。防御方案有两个层次。第一层用MyBatis的#{}占位符不要用${}。#{}会生成预编译SQL参数作为预处理后的值传递不会被当成SQL语句执行${}是字符串替换直接在SQL中拼值有注入风险。第二层在Java后端加统一的SQL过滤校验比如使用Druid连接池来拦截可疑的恶意SQL。平时开发时我习惯在控制台打印执行的SQL语句用mybatis的日志配置开启SQL输出这样每次请求都能对执行的SQL做核对防止有低级拼接错误被忽略。5.5 版本不兼容与依赖冲突问题SpringBoot版本是新人最容易踩的坑。项目大版本更新后很多配置项、API都变了。比如SpringBoot 2.4版本里配置文件里的spring.redis配置从spring.redis改成了spring.data.redis如果导入的旧项目代码启动时Redis相关配置会报属性不匹配。除此之外SpringBoot版本和MyBatis Starter版本、Druid版本也有兼容性问题。如果拿到源码后遇到依赖冲突可以从Maven依赖树看冲突来源mvn dependency:tree查看哪些jar包被重复引入再通过exclusion标签排除掉旧版本。如果启动时报某类NoClassDefFoundError或NoSuchMethodError几乎是版本冲突的典型症状。说回热词里的springboot 4源码——SpringBoot至今的版本号还没到4.x如果看到“SpringBoot 4”的源码包大概率是自媒体误传或版本号写得随意。实操时还是用当前稳定的2.x或3.x版本就好别被来路不明的版本信息带偏。6. 答辩汇报与项目优化的加分经验核心功能全部跑通、项目能顺利演示之后其实还有非常重要的一步——如何把项目讲清楚如何让它在众多同质化的毕设项目里显得出众。答辩预览的演示路径要提前设计最好不要临时决定操作顺序。我的习惯是先展示登录功能分别用三种角色登录各看一眼首页然后以管理员身份创建一个竞赛设置好报名和提交作品的时间然后切换学生身份进行报名、上传作品、查看审核状态再切换教师身份打分最后用管理员身份查看成绩统计。整个流程走下来系统的基础功能全部覆盖逻辑连贯完整时间控制在十分钟左右最合适。项目优化方面我建议在基础功能之外选择一到两个亮点来展示。最容易在答辩中展开的是性能优化方案比如上面提到的Redis缓存公告数据、给频繁查询的字段加索引、用异步方式处理大量并发报名请求其次是安全性展示比如密码加密存储、SQL注入防御、XSS攻击过滤再次是工程化展示比如用Docker部署、用Git做版本管理、用Swagger生成接口文档。选择其中的一个深入展开比把三个都泛泛带过效果好得多。关于论文撰写毕设论文和项目代码的对应关系一定要清晰。如果论文里写了系统包含模块管理和成绩统计分析功能代码里就必须真能跑通这些功能。如果论文中提到的数据表、字段名和数据库脚本里的不一致答辩时被老师当场翻出来会很难看。所以在写论文时建议对照着数据库设计文档和接口文档边写边核实保证论文结构、代码实现、数据库实际状态三者高度一致。最后还有一个非常实用的建议把项目完整地在本地跑通之后录一个操作演示视频。答辩现场可能会出现各种意外比如网络断了、虚拟机打不开、数据库连接超时只依赖现场演示风险太高。提前录好一份从启动到全流程操作的视频保存在本地关键时刻能救急。平时也可以把这套项目打包成部署文档在个人博客或GitHub上做开源分享对简历和求职也会有实际帮助。本文还有配套的精品资源点击获取