基于Spring Boot的马拉松赛事报名系统的设计与实现
2026/8/24 4:42:43 网站建设 项目流程

一、 项目背景与意义

随着全民健身热潮的兴起和城市品牌营销需求的增长,马拉松赛事已成为一项广受欢迎的群众性体育活动和城市名片。传统的线下报名方式存在效率低下、信息易错、数据统计困难、支付不便等问题,难以应对大规模、高并发的报名需求。因此,开发一个功能完善、稳定可靠、用户体验良好的在线马拉松赛事报名系统具有重要的现实意义。

本系统的设计与实现旨在:

  • 提升报名效率:实现7x24小时在线报名,打破时空限制,简化流程。
  • 优化管理体验:为赛事组织者提供可视化的后台管理界面,实现选手信息、订单、赛事的集中管理。
  • 保障数据安全与一致性:通过数据库统一管理,避免人工录入错误,确保数据准确。
  • 增强用户体验:提供清晰的赛事信息展示、便捷的在线支付、实时的报名状态查询等功能。
  • 技术实践价值:作为Spring Boot技术栈的典型应用案例,涵盖了Web开发的核心流程,具有学习和参考价值。

二、 系统技术栈

本项目采用前后端分离的架构模式,后端基于Spring Boot构建,前端使用主流的Vue.js框架。

2.1 后端技术栈 (Backend)

  • 核心框架:Spring Boot 2.7.x / 3.x
  • Web框架: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 或 Webpack
  • UI组件库:Element Plus (Vue 3) 或 Element UI (Vue 2)
  • 状态管理:Pinia (Vue 3) 或 Vuex (Vue 2)
  • 路由:Vue Router
  • HTTP客户端: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 RedisTemplate<String, Object> 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 支付回调处理Controller

@RestController @RequestMapping("/api/pay/callback") @Slf4j public class PayCallbackController { @Autowired private RegistrationService registrationService; @PostMapping("/alipay") public String alipayCallback(HttpServletRequest request) { Map<String, String> 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实现报名人数实时播报;开发微信小程序端以覆盖更广泛的用户群体。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询