基于Spring Boot的马拉松赛事报名系统的设计与实现

📅 2026/8/24 4:42:51
基于Spring Boot的马拉松赛事报名系统的设计与实现
一、 项目背景与意义随着全民健身热潮的兴起和城市品牌营销需求的增长马拉松赛事已成为一项广受欢迎的群众性体育活动和城市名片。传统的线下报名方式存在效率低下、信息易错、数据统计困难、支付不便等问题难以应对大规模、高并发的报名需求。因此开发一个功能完善、稳定可靠、用户体验良好的在线马拉松赛事报名系统具有重要的现实意义。本系统的设计与实现旨在提升报名效率实现7x24小时在线报名打破时空限制简化流程。优化管理体验为赛事组织者提供可视化的后台管理界面实现选手信息、订单、赛事的集中管理。保障数据安全与一致性通过数据库统一管理避免人工录入错误确保数据准确。增强用户体验提供清晰的赛事信息展示、便捷的在线支付、实时的报名状态查询等功能。技术实践价值作为Spring Boot技术栈的典型应用案例涵盖了Web开发的核心流程具有学习和参考价值。二、 系统技术栈本项目采用前后端分离的架构模式后端基于Spring Boot构建前端使用主流的Vue.js框架。2.1 后端技术栈 (Backend)核心框架Spring Boot 2.7.x / 3.xWeb框架Spring MVC数据持久层ORM框架MyBatis-Plus (简化CRUD操作)数据库MySQL 8.0连接池HikariCP安全与权限Spring Security JWT (JSON Web Token) 实现认证与授权缓存Redis (用于缓存热点数据、存储验证码、会话管理等)消息队列RabbitMQ / Kafka (可选用于异步处理报名成功通知、订单超时关闭等场景)文件存储本地存储 或 集成阿里云OSS/腾讯云COS (用于存储选手上传的体检报告、身份证明等)API文档SpringDoc OpenAPI (Swagger UI) 或 Knife4j其他工具Lombok (简化Java Bean代码)Hutool (Java工具库)MapStruct (对象映射)2.2 前端技术栈 (Frontend)框架Vue 3 (Composition API) 或 Vue 2构建工具Vite 或 WebpackUI组件库Element Plus (Vue 3) 或 Element UI (Vue 2)状态管理Pinia (Vue 3) 或 Vuex (Vue 2)路由Vue RouterHTTP客户端Axios可视化ECharts (用于后台数据统计图表)2.3 开发与部署版本控制Git项目管理Maven 或 Gradle容器化Docker, Docker Compose持续集成/部署 (CI/CD)Jenkins 或 GitLab CI三、 系统核心功能模块设计3.1 用户端 (选手端)用户注册与登录手机号/邮箱注册JWT令牌认证。赛事浏览与查询按城市、日期、赛事类型筛选查看赛事详情规则、路线、奖励等。在线报名选择赛事、组别全程/半程/迷你填写个人信息、紧急联系人上传所需材料。订单与支付生成报名订单集成支付宝/微信支付支持退款申请。个人中心查看报名历史、订单状态、下载电子参赛包、修改个人信息。3.2 管理端 (运营端)管理员登录RBAC权限控制。赛事管理 (CRUD)发布、编辑、上/下线赛事设置报名时间、名额、费用。选手管理审核报名信息查看选手列表导出数据。订单管理查看所有订单处理退款手动修改订单状态。数据统计报名人数趋势、各赛事热度、收入统计等可视化图表。系统设置支付配置、短信/邮件模板管理。四、 核心代码实现示例4.1 实体类设计 (Entity)import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; Data TableName(t_race) public class Race { TableId(type IdType.AUTO) private Long id; private String name; // 赛事名称 private String city; private LocalDateTime startTime; // 开赛时间 private LocalDateTime regStartTime; // 报名开始时间 private LocalDateTime regEndTime; // 报名结束时间 private Integer totalQuota; // 总名额 private Integer registeredCount; // 已报名人数 private BigDecimal fee; // 报名费 private Integer status; // 状态0-未开始1-报名中2-已截止3-已结束 TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; }4.2 报名业务核心Service层import org.springframework.transaction.annotation.Transactional; Service Slf4j public class RegistrationService { Autowired private RaceMapper raceMapper; Autowired private RegistrationMapper registrationMapper; Autowired private RedisTemplatelt;String, Objectgt; redisTemplate; /** 提交报名申请 param dto 报名请求数据 return 订单ID */ Transactional(rollbackFor Exception.class) public Long submitRegistration(RegistrationDTO dto) { // 1. 校验赛事状态与名额 Race race raceMapper.selectById(dto.getRaceId()); if (race null || race.getStatus() ! 1) { throw new BusinessException(赛事不存在或不在报名期); } if (race.getRegisteredCount() race.getTotalQuota()) { throw new BusinessException(该赛事名额已满); } // 2. 防止重复提交 (分布式锁或Redis Key) String lockKey reg:lock: dto.getUserId() : dto.getRaceId(); Boolean lockAcquired redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 10, TimeUnit.SECONDS); if (Boolean.FALSE.equals(lockAcquired)) { throw new BusinessException(请勿重复提交); } try { // 3. 创建报名记录 Registration reg new Registration(); BeanUtils.copyProperties(dto, reg); reg.setOrderNo(IdUtil.getSnowflakeNextIdStr()); // 生成订单号 reg.setStatus(0); // 待支付 registrationMapper.insert(reg); // 4. 占用名额 (乐观锁) int updateCount raceMapper.updateRegisteredCount(race.getId(), race.getVersion()); if (updateCount 0) { throw new BusinessException(名额更新失败请重试); } // 5. 发送创建订单事件 (可选异步处理) // applicationEventPublisher.publishEvent(new OrderCreatedEvent(reg.getId())); return reg.getId(); } finally { // 释放锁 redisTemplate.delete(lockKey); } } }4.3 支付回调处理ControllerRestController RequestMapping(/api/pay/callback) Slf4j public class PayCallbackController { Autowired private RegistrationService registrationService; PostMapping(/alipay) public String alipayCallback(HttpServletRequest request) { Maplt;String, Stringgt; params convertRequestToMap(request); // 1. 验证签名 (调用支付宝SDK验证) boolean signVerified AlipaySignature.rsaCheckV1(...); if (!signVerified) { return failure; } // 2. 处理业务 String outTradeNo params.get(out_trade_no); String tradeStatus params.get(trade_status); if (TRADE_SUCCESS.equals(tradeStatus)) { registrationService.handlePaySuccess(outTradeNo); } return success; } private void handlePaySuccess(String orderNo) { // 更新订单状态为已支付更新报名状态发送报名成功短信/邮件 // ... } }4.4 基于JWT的认证过滤器Component public class JwtAuthenticationFilter extends OncePerRequestFilter { Autowired private JwtUtil jwtUtil; Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader request.getHeader(Authorization); if (authHeader ! null authHeader.startsWith(Bearer )) { String token authHeader.substring(7); try { String username jwtUtil.extractUsername(token); if (username ! null SecurityContextHolder.getContext().getAuthentication() null) { if (jwtUtil.validateToken(token, username)) { UsernamePasswordAuthenticationToken authToken new UsernamePasswordAuthenticationToken(username, null, new ArrayList()); SecurityContextHolder.getContext().setAuthentication(authToken); } } } catch (Exception e) { log.error(JWT token验证失败, e); } } filterChain.doFilter(request, response); } }五、 总结与展望本文介绍了基于Spring Boot的马拉松赛事报名系统的整体设计与核心技术实现。系统采用微服务思想模块化设计运用了Spring Boot、MyBatis-Plus、Redis、JWT等主流技术确保了系统的高并发处理能力、数据一致性和安全性。核心代码示例展示了报名、支付、认证等关键业务流程。未来可扩展的方向包括引入分布式事务如Seata保障数据最终一致性使用Elasticsearch实现赛事智能搜索与推荐通过WebSocket实现报名人数实时播报开发微信小程序端以覆盖更广泛的用户群体。