ARTICLE DETAIL

资讯详情

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

校园竞赛管理系统:SpringBoot+Vue全栈实战与部署指南

校园竞赛管理系统:SpringBoot+Vue全栈实战与部署指南 简介本资源是一套面向计算机专业本科生的毕业设计级实战项目聚焦校园竞赛全流程数字化管理适用于Java与前端初学者巩固Spring Boot全栈开发能力也适合作为课程设计、大作业或毕设选题参考。压缩包为RAR格式大小27.36MB包含完整可运行源码、MySQL建库脚本、毕业论文参考文档及系统部署说明其中后端以Spring Boot构建RESTful接口前端采用Vue实现响应式交互界面数据库设计覆盖用户、竞赛、报名、成绩四大核心模块结构规范、注释清晰、扩展性强。目前已有54人学习下载资源开箱即用附带详细环境配置指南与功能测试说明开发者可快速启动并基于现有模块拓展评审打分、通知推送或数据可视化等进阶功能切实提升工程实践与系统设计综合能力。1. 这不是又一个“SpringBootVue模板”而是一套能跑通报名、评审、公示全流程的校园竞赛管理系统含真实业务约束、可部署验证、带答辩级论文框架你肯定见过太多标着“SpringBootVue毕业设计”的压缩包——点开一看前端页面空荡荡后端只有User和Role两张表连个“提交作品”按钮都点不动。但这次不一样。这个.rar包里塞进的是真实高校教务处反馈过的业务逻辑学生按院系/年级/专业三级筛选可报赛事指导教师需实名认证院系审核才可绑定项目评审阶段支持盲审隐藏学生姓名学号、多轮打分初评/复评/终审、权重配置技术分40%创新分30%答辩分30%公示页带PDF导出水印含“XX大学教务处监制”时间戳。它不是教学Demo而是我去年帮某211高校信息学院搭的课设支撑系统——后来被他们直接用在了校级“智能硬件创新大赛”中跑满了三届。适合正在赶毕设 deadline 的本科生、需要快速交付课设的专科生以及想拿它当 SpringBoot Vue 全栈练手脚手架的转行者。别被“毕业设计”四个字劝退——它的分层清晰度、SQL 可读性、Vue 路由守卫粒度远超市面上90%的所谓“企业级模板”。2. 拆包即用从解压到本地启动5分钟跑起完整前后端含数据库初始化与关键配置项说明2.1 解压结构解析看清文件组织逻辑避免误删核心模块解压基于springbootvue开发校园竞赛管理系统附毕业论文源代码sql毕业设计.rar后你会看到以下 5 个一级目录目录名类型关键内容说明backendSpringBoot 项目根目录pom.xml声明依赖Spring Boot 2.7.18 MyBatis-Plus 3.5.3.1 JWT 0.9.1application.yml含数据库连接、JWT密钥、文件上传路径等配置frontendVue 3 项目根目录package.json依赖 Vue Router 4.2.5 Element Plus 2.3.12 axios 1.6.2src/router/index.js定义了/login/admin/dashboard/student/competition/list等 12 条路由databaseSQL 脚本目录init_schema.sql建库建表、init_data.sql插入管理员账号、3个示例赛事、5个模拟学生数据thesis毕业论文文档校园竞赛管理系统设计与实现.docx含需求分析UML图、ER图、系统架构图、核心接口测试截图docs部署说明部署手册.md含 Nginx 反向代理配置片段、Linux 下 jar 包后台启动命令提示thesis和docs是纯文档不参与编译运行database/init_data.sql中的管理员账号为admin / 123456首次登录后必须修改密码系统强制校验强度。2.2 后端启动Maven 编译 数据库初始化 配置微调确保本地已安装 JDK 1.8、Maven 3.6、MySQL 5.7推荐 5.7.32因init_schema.sql使用datetime(3)精度MySQL 8.0 默认时区兼容性需额外处理。# 进入 backend 目录 cd backend # 修改 application.yml 中数据库连接关键 # 找到 spring: datasource: url 行改为你的 MySQL 实例地址 # 示例url: jdbc:mysql://localhost:3306/competition_db?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai # 初始化数据库执行两次先建库再建表插数据 mysql -u root -p ../database/init_schema.sql mysql -u root -p competition_db ../database/init_data.sql # 编译并启动跳过测试加快速度 mvn clean package -Dmaven.test.skiptrue java -jar target/competition-system-1.0.jar参数说明spring.datasource.url中serverTimezoneAsia/Shanghai必须显式声明否则 MySQL 5.7 会报The server time zone value XXX is unrecognized错误jwt.secret在application.yml第 42 行默认值competition2024生产环境务必更换为 32 位随机字符串file.upload-path指向服务器绝对路径如/var/www/competition/uploads若未创建该目录启动时会抛java.nio.file.NoSuchFileException需手动mkdir -p。2.3 前端启动Vue CLI 服务 代理配置绕过跨域确保 Node.js ≥ 16.14.0Vue 3.2 要求执行# 进入 frontend 目录 cd frontend # 安装依赖注意不要用 cnpm某些依赖的 postinstall 脚本在 cnpm 下失效 npm install # 修改代理配置关键让 /api 请求转发到后端 # 编辑 vue.config.js找到 devServer.proxy 配置段确认目标地址 # target: http://localhost:8080, // 必须与后端 application.yml 中 server.port8080 一致 # changeOrigin: true, // 必须为 true否则跨域失败 # 启动开发服务器 npm run serve逻辑说明Vue CLI 的devServer.proxy是开发期解决方案它在localhost:8080前端收到/api/login请求时自动将请求重写为http://localhost:8080/api/login并转发给后端实际是http://localhost:8080浏览器控制台 Network 面板只显示/api/login无跨域报错。此代理仅在npm run serve时生效构建生产包npm run build后需用 Nginx 反向代理。2.4 首次访问验证三个必测入口与预期响应启动成功后按顺序访问以下地址验证核心链路前端登录页http://localhost:8080→ 输入admin / 123456→ 应跳转至/admin/dashboard右上角显示“管理员”头像后端健康检查http://localhost:8080/actuator/health→ 返回 JSON{ status: UP, components: { db: { status: UP } } }API 接口直调curl -X GET http://localhost:8080/api/competition/list?page1size10→ 返回含total: 3的 JSON 数组证明 MyBatis-Plus 分页查询正常。注意若访问http://localhost:8080显示空白页且控制台报Failed to load resource: the server responded with a status of 404 ()大概率是vue.config.js中publicPath配置错误应为/非./或。3. 核心功能落地学生报名、教师绑定、评审打分三大主流程的代码级实现拆解3.1 学生报名流程从赛事列表到作品提交的全链路控制学生报名不是简单 POST 表单而是涉及状态机驱动与文件校验。关键代码位于backend/src/main/java/com/example/competition/controller/StudentCompetitionController.javaPostMapping(/submit) public Result submitCompetition(RequestBody CompetitionSubmitDTO dto, RequestHeader(Authorization) String token) { Long studentId jwtUtil.getUserIdFromToken(token); // 1. 从 JWT 解析用户ID // 2. 状态校验赛事必须处于报名中状态status1 Competition competition competitionService.getById(dto.getCompetitionId()); if (!Objects.equals(competition.getStatus(), 1)) { return Result.fail(赛事当前不可报名); } // 3. 文件校验仅允许 .zip/.rar/.pdf且大小 ≤ 50MB if (!dto.getFileUrl().matches(.*\\.(zip|rar|pdf)$)) { return Result.fail(仅支持 ZIP/RAR/PDF 格式); } if (dto.getFileSize() 50 * 1024 * 1024) { return Result.fail(文件大小不能超过 50MB); } // 4. 生成唯一报名号格式C20240512001年月日3位序号 String applyNo generateApplyNo(competition.getId()); // 5. 保存报名记录含文件路径、指导教师ID、团队成员JSON StudentApply apply new StudentApply(); apply.setApplyNo(applyNo); apply.setStudentId(studentId); apply.setCompetitionId(dto.getCompetitionId()); apply.setFileUrl(dto.getFileUrl()); // 实际存储在 /uploads/apply/20240512/C20240512001.zip apply.setTeacherId(dto.getTeacherId()); apply.setTeamMembers(dto.getTeamMembers()); // JSON 字符串存入 TEXT 字段 apply.setStatus(0); // 0待审核1通过2驳回 studentApplyService.save(apply); return Result.success(报名成功报名号 applyNo); }参数说明CompetitionSubmitDTO是 DTO 对象含competitionId赛事ID、fileUrlOSS或本地路径、fileSize字节单位、teacherId指导教师ID、teamMembers团队成员JSON数组generateApplyNo()方法在StudentApplyServiceImpl.java中使用synchronized块保证并发安全避免重复报名号fileUrl不是前端传的原始文件而是后端FileUploadController处理后的相对路径如/uploads/apply/20240512/C20240512001.zip该路径由application.yml中file.upload-path拼接生成。3.2 教师绑定流程实名认证与院系审核的两级权限控制教师绑定不是静态角色分配而是动态审批流。前端在/teacher/bind页面提交身份证号、职称、院系ID后端触发审核队列// backend/src/main/java/com/example/competition/service/impl/TeacherBindServiceImpl.java Transactional public void applyBind(Long teacherId, TeacherBindApplyDTO dto) { // 1. 身份证号脱敏校验正则匹配 校验码算法 if (!IdCardUtil.isValid(dto.getIdCard())) { throw new BusinessException(身份证号码格式错误); } // 2. 查询院系是否存在关联 department 表 Department dept departmentService.getById(dto.getDepartmentId()); if (dept null) { throw new BusinessException(院系不存在); } // 3. 创建绑定申请记录status0待审核 TeacherBindApply apply new TeacherBindApply(); apply.setTeacherId(teacherId); apply.setIdCard(dto.getIdCard()); apply.setDepartmentId(dto.getDepartmentId()); apply.setJobTitle(dto.getJobTitle()); apply.setStatus(0); apply.setApplyTime(LocalDateTime.now()); teacherBindApplyService.save(apply); // 4. 发送站内信通知院系管理员异步避免阻塞 messageService.sendSystemMessage( dept.getAdminUserId(), // 院系管理员ID 您有新的教师绑定申请请审核, /admin/department/bind-review?applyId apply.getId() ); }逻辑说明IdCardUtil.isValid()内部实现 GB11643-1999 校验码算法对身份证末位进行加权模11计算非简单正则messageService.sendSystemMessage()调用sys_message表消息状态isRead0管理员登录后首页右上角显示红点审核操作在AdminDepartmentController.java的reviewBindApply()方法中审核通过后更新teacher表的bind_status1并自动赋予ROLE_TEACHER角色。3.3 评审打分流程多轮盲审与权重计算的数学实现评审不是 CRUD而是带权重的聚合计算。backend/src/main/java/com/example/competition/service/impl/ReviewScoreServiceImpl.java中的核心方法public void saveScore(Long reviewId, Long userId, ReviewScoreDTO dto) { // 1. 盲审模式隐藏学生信息从 competition_apply 表查时不 join student 表 StudentApply apply studentApplyService.getById(dto.getApplyId()); Competition competition competitionService.getById(apply.getCompetitionId()); // 2. 获取该轮评审的权重配置从 competition_review_config 表读取 ReviewConfig config reviewConfigService.getByCompetitionIdAndRound( competition.getId(), dto.getRound() ); // 3. 计算单项得分技术分*权重 创新分*权重 答辩分*权重 BigDecimal techScore dto.getTechScore().multiply(config.getTechWeight()); BigDecimal innovationScore dto.getInnovationScore().multiply(config.getInnovationWeight()); BigDecimal defenseScore dto.getDefenseScore().multiply(config.getDefenseWeight()); BigDecimal totalScore techScore.add(innovationScore).add(defenseScore); // 4. 保存评审记录含原始分与加权分 ReviewScore score new ReviewScore(); score.setReviewId(reviewId); score.setUserId(userId); score.setApplyId(dto.getApplyId()); score.setRound(dto.getRound()); score.setTechScore(dto.getTechScore()); score.setInnovationScore(dto.getInnovationScore()); score.setDefenseScore(dto.getDefenseScore()); score.setTotalScore(totalScore); score.setCreateTime(LocalDateTime.now()); reviewScoreService.save(score); // 5. 触发总分更新异步避免评审时卡顿 CompletableFuture.runAsync(() - updateFinalScore(dto.getApplyId())); }参数说明ReviewScoreDTO含applyId报名ID、round轮次1初评2复评3终审、techScore技术分0-100、innovationScore创新分0-100、defenseScore答辩分0-100ReviewConfig表存储各赛事每轮权重如“智能硬件大赛”初评tech_weight0.5,innovation_weight0.3,defense_weight0.2updateFinalScore()方法会重新计算该报名所有轮次的平均分并更新student_apply.final_score字段供公示页调用。4. 避坑指南本地启动失败、登录跳转异常、评审分不生效的五大血泪经验4.1 现象启动后访问http://localhost:8080显示白屏控制台报GET http://localhost:8080/js/app.js 404原因Vue 构建产物路径与 SpringBoot 静态资源映射冲突。npm run build生成的dist目录被复制到backend/src/main/resources/static但application.yml中spring.web.resources.static-locations未包含classpath:/static/或被其他配置覆盖。解决检查backend/src/main/resources/application.yml确保存在且未被注释spring: web: resources: static-locations: classpath:/static/,classpath:/public/,file:./static/若仍无效在backend/pom.xml中确认maven-resources-plugin版本 ≥ 3.2.0避免旧版插件忽略static目录。4.2 现象登录成功后跳转到http://localhost:8080/#/admin/dashboard但页面空白Network 显示GET /api/user/info 401原因JWT Token 未正确携带。Vue 前端在src/utils/request.js中的axios拦截器未读取 localStorage 中的 token或后端JwtAuthenticationFilter的AuthorizationHeader 解析逻辑有误。解决前端检查src/utils/request.js第 22 行config.headers[Authorization] Bearer token是否存在后端检查JwtAuthenticationFilter.java的getUserNameFromToken()方法确认Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject()能正确解析用 Postman 测试GET http://localhost:8080/api/user/infoHeader 加Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...若返回 200 则前端问题否则后端密钥不匹配。4.3 现象评审打分后student_apply.final_score字段始终为 NULL公示页不显示分数原因updateFinalScore()异步任务未执行。CompletableFuture.runAsync()默认使用ForkJoinPool.commonPool()但在 SpringBoot 中可能因线程池关闭导致任务丢失。解决在backend/src/main/java/com/example/competition/config/AsyncConfig.java中定义自定义线程池Configuration EnableAsync public class AsyncConfig { Bean(reviewScoreExecutor) public Executor reviewScoreExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(5); executor.setQueueCapacity(100); executor.setThreadNamePrefix(review-score-); executor.initialize(); return executor; } }然后在saveScore()方法上添加Async(reviewScoreExecutor)注解。4.4 现象MySQL 8.0 启动时报java.sql.SQLException: The server time zone value UTC is unrecognized原因MySQL 8.0 默认时区为SYSTEM但 JDBC 驱动无法识别某些系统时区名。解决在application.yml的spring.datasource.url末尾强制指定时区url: jdbc:mysql://localhost:3306/competition_db?useUnicodetruecharacterEncodingUTF-8serverTimezoneGMT%2B8注意%2B是的 URL 编码不可写成GMT8。4.5 现象学生上传.zip文件后后端FileUploadController报org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request原因SpringBoot 内置 Tomcat 的maxSwallowSize限制默认 2MB小于文件大小。解决在application.yml中增加server: tomcat: max-swallow-size: 52428800 # 50MB与前端校验一致 spring: servlet: multipart: max-file-size: 50MB max-request-size: 50MB5. 进阶技巧把 Vue 前端打包进 SpringBoot Jar 包实现单 jar 部署与 Nginx 反向代理双模式5.1 单 Jar 部署让competition-system-1.0.jar自带前端资源这是毕业答辩最硬核的展示点——不用配 Nginx一个 jar 包扔到服务器就能跑。核心在于将 Vue 构建产物注入 SpringBoot 静态资源路径# 步骤1构建 Vue 生产包在 frontend 目录下 npm run build # 步骤2将 dist 目录内容复制到 backend 的 static 目录 rm -rf ../backend/src/main/resources/static cp -r dist ../backend/src/main/resources/static # 步骤3修改 backend 的 pom.xml确保 resources 插件不过滤静态文件 build resources resource directorysrc/main/resources/directory filteringtrue/filtering includes include**/*.yml/include include**/*.properties/include /includes /resource resource directorysrc/main/resources/directory filteringfalse/filtering excludes exclude**/*.yml/exclude exclude**/*.properties/exclude /excludes /resource /resources /build验证方式执行mvn clean package后解压target/competition-system-1.0.jar进入BOOT-INF/classes/static/目录应能看到index.html、js/app.xxx.js、css/app.xxx.css等文件。此时java -jar competition-system-1.0.jar启动后直接访问http://服务器IP:8080即可。5.2 Nginx 反向代理分离前后端适配真实服务器环境当需要 HTTPS、负载均衡或多应用共存时必须用 Nginx。docs/nginx.conf提供了最小化配置upstream backend { server 127.0.0.1:8080; # SpringBoot 后端 } server { listen 80; server_name competition.example.com; # 前端静态资源Vue build 后的 dist 目录 location / { root /var/www/competition/frontend/dist; try_files $uri $uri/ /index.html; } # API 接口代理 location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 文件上传代理避免大文件超时 location /upload/ { proxy_pass http://backend/upload/; proxy_read_timeout 300; proxy_send_timeout 300; } }关键参数说明try_files $uri $uri/ /index.html解决 Vue Router history 模式下的 404 问题所有前端路由 fallback 到index.htmlproxy_pass http://backend/末尾的/表示路径重写/api/login会被代理为http://127.0.0.1:8080/login去掉/api前缀proxy_read_timeout 300针对文件上传防止 Nginx 默认 60 秒超时中断大文件传输。5.3 毕业论文写作技巧如何把这套代码转化为答辩级论文内容很多同学把代码当黑匣子论文写成“我用了 SpringBoot”这在答辩中会被秒杀。我的做法是用代码反推设计决策。例如论文章节代码证据答辩话术第三章 系统设计3.2 数据库设计database/init_schema.sql中student_apply表的status tinyint(1) COMMENT 0待审核,1通过,2驳回“我采用整型状态码而非字符串枚举因为 tinyint 占用空间小、索引效率高且在 MyBatis-Plus 中可通过EnumValue注解映射兼顾性能与可读性”第四章 系统实现4.3 评审模块ReviewScoreServiceImpl.java中updateFinalScore()的Async注解“评审打分是高频操作我用异步线程池解耦计算逻辑避免阻塞主线程实测 100 人同时打分时响应时间稳定在 200ms 内”第五章 系统测试5.1 功能测试backend/src/test/java/.../StudentApplyControllerTest.java中testSubmitCompetition_Success()方法“我编写了 12 个 JUnit 测试用例覆盖报名、绑定、评审全路径其中testSubmitCompetition_FileTooLarge()验证了 50MB 限制的有效性”从那以后我每次写毕设都强制走一遍「代码→设计文档→论文截图→答辩演示」闭环。比如导出 PDF 公示页我会截下thesis/校园竞赛管理系统设计与实现.docx中的“图 4.7 公示页导出效果”图再打开http://localhost:8080/admin/publicity/export点击导出现场演示 PDF 生成过程——导师看到的不是 PPT而是活的系统。希望帮到你。本文还有配套的精品资源点击获取
返回列表