温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片!
1. 项目背景与意义
随着高校及企事业单位体育活动的日益丰富,传统的人工记录、纸质通知、电话协调的运动会管理模式已难以满足高效、准确、透明的管理需求。运动会管理系统应运而生,旨在通过信息化手段,对运动会从报名、赛程编排、成绩录入、公告发布到数据统计的全流程进行数字化管理。
项目意义:
- 提升效率:自动化处理报名、分组、成绩统计等繁琐工作,大幅减少人工操作和错误。
- 增强透明度:实时发布赛程、成绩和排名,所有参与者可通过系统随时查看,过程公开公正。
- 优化体验:为运动员提供便捷的在线报名与成绩查询,为裁判员提供高效的成绩录入工具,为管理员提供全面的数据看板。
- 数据驱动决策:系统积累的报名、成绩等数据可用于分析参赛趋势、项目热度,为未来活动策划提供依据。
本系统采用前后端分离架构,后端使用Spring Boot提供稳定高效的 RESTful API 服务,前端使用Vue.js构建交互友好、响应迅速的用户界面,是现代化 Web 应用开发的典型实践。
2. 技术栈详解
2.1 后端技术栈 (Spring Boot)
- 核心框架:Spring Boot 2.x
- Web 框架:Spring MVC
- 数据持久层:MyBatis-Plus / Spring Data JPA
- 数据库:MySQL 8.0
- 连接池:HikariCP
- 权限控制:Spring Security + JWT (JSON Web Token)
- 接口文档:Swagger2 / Knife4j
- 项目构建:Maven
- 其他工具:Lombok (简化代码), Hutool (工具类库)
2.2 前端技术栈 (Vue.js)
- 核心框架:Vue 3 (Composition API) 或 Vue 2
- 构建工具:Vite 或 Vue CLI
- UI 组件库:Element Plus (Vue 3) 或 Element UI (Vue 2)
- 状态管理:Pinia (Vue 3) 或 Vuex (Vue 2)
- 路由管理:Vue Router
- HTTP 客户端:Axios
- 可视化图表:ECharts (用于数据统计看板)
2.3 开发与部署
- 版本控制:Git
- 后端部署:可打包为 Jar 包,使用 Java 环境运行,或使用 Docker 容器化部署。
- 前端部署:构建为静态资源,部署至 Nginx、Tomcat 或对象存储 (如 OSS)。
3. 系统核心功能模块设计
系统主要分为三大角色:系统管理员、裁判员、运动员/观众。
3.1 管理员端
- 用户管理:对裁判员、运动员账号进行增删改查、权限分配。
- 项目管理:创建、编辑运动会项目(如100米跑、跳高),设置项目规则、名额限制。
- 报名管理:审核运动员的线上报名申请。
- 赛程编排:根据项目、场地、时间自动或手动生成比赛日程表。
- 成绩总览:查看所有项目的最终成绩与排名。
- 公告管理:发布运动会相关通知、规则变更等信息。
- 数据统计:可视化图表展示报名人数趋势、项目热度等。
3.2 裁判员端
- 我的任务:查看被分配负责的比赛项目及赛程。
- 成绩录入:在比赛结束后,录入或修改所负责项目的运动员成绩。
- 成绩确认:提交成绩前进行二次确认,确保数据准确。
3.3 运动员/观众端
- 个人信息:查看和修改个人资料。
- 项目浏览与报名:查看所有可报名的项目,在线提交报名申请。
- 我的赛程:查看个人已报名项目的比赛时间、地点。
- 成绩查询:实时查询个人或所有项目的比赛成绩与排名。
- 公告查看:浏览系统发布的最新公告。
4. 数据库核心表设计
以下为部分核心数据表结构:
- 用户表 (sys_user):id, username, password, real_name, role (admin, referee, athlete), phone, status...
- 运动会项目表 (sport_event):id, event_name, category, limit_participants, rule_description, status...
- 运动员报名表 (registration):id, user_id, event_id, registration_time, status (pending, approved, rejected)...
- 赛程表 (schedule):id, event_id, start_time, end_time, venue, referee_id, status...
- 成绩表 (score):id, schedule_id, athlete_id, score_value, ranking, record_time, referee_id...
- 公告表 (announcement):id, title, content, publisher_id, publish_time, status...
5. 核心代码示例
5.1 后端核心:Spring Boot 统一响应封装与JWT验证
统一响应体 (Result.java):
import lombok.Data; import java.io.Serializable; @Data public class Result<T> implements Serializable { private Integer code; // 状态码,如200成功,500失败 private String msg; // 提示信息 private T data; // 响应数据 public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMsg("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(String msg) { Result<T> result = new Result<>(); result.setCode(500); result.setMsg(msg); return result; } }JWT工具类 (JwtUtil.java) 核心方法:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtUtil { private static final String SECRET_KEY = "your-secret-key"; // 应配置在application.yml中 private static final long EXPIRATION = 86400000L; // 24小时 // 生成Token public static String generateToken(String username, String role) { Map<String, Object> claims = new HashMap<>(); claims.put("username", username); claims.put("role", role); return Jwts.builder() .setClaims(claims) .setSubject(username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } // 解析并验证Token public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } }5.2 后端核心:Spring Security 配置与登录接口
Spring Security 配置类核心片段:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Override protected void configure(HttpSecurity http) throws Exception { http // 关闭csrf,因为使用JWT无状态认证 .csrf().disable() // 允许跨域 .cors().and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() // 登录接口允许匿名访问 .antMatchers("/api/auth/login").anonymous() // 静态资源、Swagger文档等放行 .antMatchers("/swagger-ui.html", "/webjars/", "/swagger-resources/", "/v2/api-docs").permitAll() // 其他所有请求都需要认证 .anyRequest().authenticated(); // 添加JWT过滤器 http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } // 密码编码器 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }登录控制器 (AuthController.java):
@RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private UserService userService; @PostMapping("/login") public Result<String> login(@RequestBody LoginRequest request) { // 1. 验证用户名密码 User user = userService.findByUsername(request.getUsername()); if (user == null || !passwordEncoder.matches(request.getPassword(), user.getPassword())) { return Result.error("用户名或密码错误"); } // 2. 生成JWT Token String token = JwtUtil.generateToken(user.getUsername(), user.getRole()); // 3. 返回Token return Result.success(token); } }5.3 前端核心:Vue3 + Pinia 状态管理与API调用
Pinia Store (userStore.js) 用于管理用户登录状态:
import { defineStore } from 'pinia' import { ref } from 'vue' import { loginApi, getUserInfo } from '@/api/user' export const useUserStore = defineStore('user', () => { const token = ref(localStorage.getItem('token') || '') const userInfo = ref({}) // 登录动作 const login = async (loginForm) => { const res = await loginApi(loginForm) if (res.code === 200) { token.value = res.data localStorage.setItem('token', token.value) // 登录成功后获取用户信息 await getUserInfo() return Promise.resolve() } else { return Promise.reject(new Error(res.msg)) } } // 获取用户信息 const getUserInfo = async () => { const res = await getUserInfoApi() if (res.code === 200) { userInfo.value = res.data } } // 退出登录 const logout = () => { token.value = '' userInfo.value = {} localStorage.removeItem('token') } return { token, userInfo, login, logout, getUserInfo } })Axios 请求拦截器 (设置JWT Token):
import axios from 'axios' import { useUserStore } from '@/stores/user' import { ElMessage } from 'element-plus' const service = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, timeout: 5000 }) // 请求拦截器 service.interceptors.request.use( (config) => { const userStore = useUserStore() if (userStore.token) { config.headers['Authorization'] = Bearer ${userStore.token} } return config }, (error) => { return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( (response) => { const res = response.data if (res.code !== 200) { ElMessage.error(res.msg || 'Error') return Promise.reject(new Error(res.msg || 'Error')) } else { return res } }, (error) => { ElMessage.error(error.message || '请求失败') return Promise.reject(error) } ) export default service5.4 前端核心:运动员报名页面组件示例 (Vue3 + Element Plus)
<template> <div class="registration-page"> <el-card> <template #header> <span>运动会项目报名</span> </template> <el-table :data="eventList" style="width: 100%"> <el-table-column prop="eventName" label="项目名称" /> <el-table-column prop="category" label="类别" /> <el-table-column prop="limitParticipants" label="名额限制" /> <el-table-column prop="currentParticipants" label="已报名" /> <el-table-column label="操作"> <template #default="scope"> <el-button type="primary" size="small" @click="handleRegistration(scope.row)" :disabled="scope.row.status !== 'OPEN' || scope.row.currentParticipants >= scope.row.limitParticipants" > {{ scope.row.status === 'OPEN' ? '立即报名' : '已截止' }} </el-button> </template> </el-table-column> </el-table> </el-card> </div> </template> <script setup> import { ref, onMounted } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import { getEventListApi, submitRegistrationApi } from '@/api/event' const eventList = ref([]) // 获取项目列表 const fetchEventList = async () => { const res = await getEventListApi() if (res.code === 200) { eventList.value = res.data } } // 处理报名 const handleRegistration = async (row) => { try { await ElMessageBox.confirm(确认报名【${row.eventName}】项目吗?, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) const res = await submitRegistrationApi({ eventId: row.id }) if (res.code === 200) { ElMessage.success('报名成功,等待管理员审核!') fetchEventList() // 刷新列表 } } catch (error) { console.log('报名取消或失败', error) } } onMounted(() => { fetchEventList() }) </script>6. 总结与展望
本文详细阐述了基于 Spring Boot 和 Vue.js 的运动会管理系统的设计与实现,涵盖了项目背景、技术栈选型、功能模块、数据库设计以及前后端核心代码示例。该系统通过现代化的技术栈,实现了运动会管理的信息化、自动化,具有良好的可扩展性和维护性。
未来可扩展方向:
- 移动端适配:开发微信小程序或 Uni-app 跨端应用,方便运动员随时查看。
- 实时通知:集成 WebSocket 或消息队列,实现成绩发布、赛程变更的实时推送。
- 智能编排:引入算法优化赛程,避免运动员项目冲突,合理分配场地资源。
- 数据分析大屏:使用更强大的可视化工具,为管理者提供实时数据决策支持。