1. 项目背景与意义
随着全民健身意识的提升,网球运动逐渐走进大众生活,场地预约需求快速增长。然而,传统网球场管理仍普遍依赖电话预约、前台登记或人工排班,存在预约效率低、场地利用率不均、信息不透明、爽约率高等问题。尤其在高峰时段,用户往往需要反复电话确认,体验不佳。
微信小程序凭借「无需下载、即用即走、社交裂变」等优势,成为连接线下场馆与线上用户的理想载体。基于微信小程序的网球场智能预约管理平台,能够实现场地实时查询、在线预约、智能排期、订单管理、消息提醒等功能,既提升了场馆运营效率,也改善了用户预约体验,具有明确的实用价值和社会意义。
2. 系统技术栈
本平台采用前后端分离架构,整体技术栈如下:
| 层次 | 技术选型 | 说明 |
|---|---|---|
| 前端(小程序端) | 微信小程序原生框架 + WXML / WXSS / JavaScript | 面向用户和管理员的移动端界面 |
| 后端服务 | Spring Boot 2.x + MyBatis-Plus | 提供 RESTful API,处理业务逻辑 |
| 数据库 | MySQL 8.0 + Redis | MySQL 存储业务数据,Redis 缓存热点数据与分布式锁 |
| 接口鉴权 | 微信登录 code2session + JWT | 实现用户身份认证与状态保持 |
| 部署环境 | Nginx + 云服务器(Linux) | 反向代理与静态资源托管 |
其中,Spring Boot 负责核心业务接口,MyBatis-Plus 简化数据持久层开发,Redis 用于缓存场地状态和防止并发重复预约。
3. 系统核心功能模块
平台主要包含以下功能模块:
- 用户模块:微信授权登录、个人信息维护、历史订单查询。
- 场地模块:场地列表展示、场地详情、实时状态(空闲/占用/维护)。
- 预约模块:按日期和时段选择场地、提交预约、取消预约、爽约记录。
- 订单模块:订单生成、支付状态管理、预约核销。
- 管理后台:场地管理、时段配置、订单审核、数据统计。
4. 核心代码实现
4.1 微信小程序端:场地预约页面
小程序端通过 wx.request 调用后端接口,获取场地列表并渲染到页面。以下为预约页面的核心代码片段:
// pages/booking/booking.js Page({ data: { courtList: [], selectedDate: '', selectedTime: '' }, onLoad() { this.loadCourts(); }, // 获取可预约场地列表 loadCourts() { wx.request({ url: 'https://api.example.com/court/list', method: 'GET', header: { 'Authorization': wx.getStorageSync('token') }, success: (res) => { if (res.data.code === 200) { this.setData({ courtList: res.data.data }); } } }); }, // 提交预约 submitBooking(e) { const { courtId, date, time } = e.detail.value; wx.request({ url: 'https://api.example.com/booking/create', method: 'POST', data: { courtId, date, time }, header: { 'Content-Type': 'application/json', 'Authorization': wx.getStorageSync('token') }, success: (res) => { if (res.data.code === 200) { wx.showToast({ title: '预约成功', icon: 'success' }); } else { wx.showToast({ title: res.data.msg, icon: 'none' }); } } }); } });4.2 后端:预约接口与防并发处理
后端预约接口是系统的核心。为防止同一时段被多人同时预约,这里使用 Redis 分布式锁保证并发安全。核心代码如下:
// BookingServiceImpl.java @Service public class BookingServiceImpl implements BookingService { @Autowired private StringRedisTemplate redisTemplate; @Autowired private BookingMapper bookingMapper; @Override @Transactional(rollbackFor = Exception.class) public Result createBooking(BookingDTO dto) { String lockKey = "court:lock:" + dto.getCourtId() + ":" + dto.getDate() + ":" + dto.getTime(); // 尝试获取分布式锁,防止同一时段并发预约 Boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "1", Duration.ofSeconds(10)); if (Boolean.FALSE.equals(locked)) { return Result.error("该时段正在被预约,请稍后重试"); } try { // 再次校验该时段是否已被占用 int count = bookingMapper.countByCourtAndTime( dto.getCourtId(), dto.getDate(), dto.getTime()); if (count > 0) { return Result.error("该时段已被预约"); } Booking booking = new Booking(); booking.setUserId(dto.getUserId()); booking.setCourtId(dto.getCourtId()); booking.setDate(dto.getDate()); booking.setTime(dto.getTime()); booking.setStatus(1); // 1-已预约 bookingMapper.insert(booking); return Result.success("预约成功"); } finally { redisTemplate.delete(lockKey); } } }4.3 微信登录与 JWT 鉴权
用户首次进入小程序时,通过 wx.login 获取 code,后端调用微信接口换取 openid,并签发 JWT 令牌。核心代码如下:
// WechatAuthController.java @RestController @RequestMapping("/api/auth") public class WechatAuthController { @Autowired private RestTemplate restTemplate; @Autowired private UserMapper userMapper; @Autowired private JwtUtil jwtUtil; @PostMapping("/login") public Result login(@RequestBody LoginDTO dto) { // 调用微信 code2session 接口 String url = "https://api.weixin.qq.com/sns/jscode2session" + "?appid=" + dto.getAppId() + "&secret=" + dto.getSecret() + "&js_code=" + dto.getCode() + "&grant_type=authorization_code"; String response = restTemplate.getForObject(url, String.class); JSONObject json = JSONObject.parseObject(response); String openid = json.getString("openid"); if (openid == null) { return Result.error("微信登录失败"); } // 查询或创建用户 User user = userMapper.selectByOpenid(openid); if (user == null) { user = new User(); user.setOpenid(openid); user.setNickname("微信用户"); userMapper.insert(user); } // 签发 JWT String token = jwtUtil.generateToken(user.getId()); return Result.success(token); } }5. 数据库设计要点
核心数据表包括用户表、场地表、预约订单表。预约订单表设计如下:
CREATE TABLE `booking_order` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` BIGINT NOT NULL COMMENT '用户ID', `court_id` BIGINT NOT NULL COMMENT '场地ID', `booking_date` DATE NOT NULL COMMENT '预约日期', `time_slot` VARCHAR(20) NOT NULL COMMENT '预约时段,如 18:00-19:00', `status` TINYINT DEFAULT 1 COMMENT '状态:1-已预约 2-已取消 3-已完成', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_court_time` (`court_id`, `booking_date`, `time_slot`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预约订单表';其中,idx_court_time联合索引用于加速「按场地、日期、时段」的冲突校验查询,是保证预约准确性的关键设计。
6. 总结
基于微信小程序的网球场智能预约管理平台,通过小程序端与 Spring Boot 后端的协同,实现了场地查询、在线预约、订单管理和并发控制等核心能力。项目在技术选型上兼顾了开发效率与系统稳定性,尤其通过 Redis 分布式锁解决了高峰时段重复预约的痛点,具备良好的实用性和扩展性,可作为同类场馆预约系统的参考实现。