城市运动预约小程序源码拆解:排期、并发与支付核心逻辑
2026/9/8 23:14:45 网站建设 项目流程

简介:开源城市运动预约小程序源码是一套面向20-45岁运动爱好者、适用于大型综合文体中心及单一运动场馆的轻量级预约工具。它覆盖羽毛球、足球、篮球、健身房、乒乓球、游泳、网球等场馆预约,并包含场馆动态、运动常识等模块;后台支持预约时间/人数灵活配置、自定义客户填写项,以及线下签到、核销、二维码自助签到等凭证核验方式,还可将预约名单导出Excel并打印,方便运营管理。资源包共494个文件,以JavaScript(184个)、WXSS(105个)、WXML(82个)、JSON(70个)等小程序前后端源码文件为主,另有PNG图片与少量HTML/TXT说明文档,压缩包仅2.68MB。采用腾讯云开发方案,无需自备服务器和域名,已有196人浏览/学习,适合开发者直接改装或学习小程序云开发实战技巧。 做运动预约类小程序,尤其是面向一个城市级别的多场馆场景,坑远比想象中多。最近我把一套开源的城市运动预约工具类小程序源码完整跑了一遍,从前端页面到后端接口,从场地排期到支付回调,算是把这类型项目的骨架彻底摸清了。这套源码定位很直接:不是做商城,不做社交,就专注搞定“用户找场地—选时段—付钱—到场核销”这条链路。如果你正准备上手类似项目,或者想找一套能直接二次开发的小程序底座,这篇东西应该能帮你省不少时间。

1. 这个项目到底是做什么的

1.1 一句话说透项目定位

城市运动预约小程序,本质上是一个连接“运动场馆”和“用户”的撮合工具。用户打开小程序,根据地理位置或运动类型找到附近的篮球馆、羽毛球馆、网球馆、游泳馆,查看某个场地的空闲时段,选定时间后完成支付,到店出示核销码,商家端确认销账。整套流程我用一个词总结:工具属性极强

这和商城类小程序有本质区别。商城小程序的逻辑是“逛—加购—下单”,重点是商品展示和营销转化;而运动预约小程序的逻辑是“查—约—付—核销”,重点是资源排期和时段冲突处理。我见过不少团队直接用商城模板改预约功能,结果一到多人抢同一时段就出问题,根源就是底层的数据模型没按预约场景设计。开源的这套源码在这点上做得比较老实,场地、时段、订单、核销四张核心表的关系清晰,值得作为参考。

1.2 解决的核心痛点

传统运动场馆的预约方式有多痛,我自己深有体会。大部分小型场馆还在用“微信群报名+线下转账”,场地管理员每天手动排期,用户要反复确认有没有空场,遇到爽约更是扯皮。稍微大一点的连锁场馆,会买一套SaaS系统,但年费不低,数据还在别人手里。

这套开源小程序解决的痛点很明确:

  • 场地状态实时可见:用户自己就能看到今天、明天、未来一周每个场地的占用情况。
  • 预约流程标准化:选场地、选时段、支付、拿核销码,全流程线上化,不需要人工介入。
  • 爽约成本可控:先付费后使用,配合超时取消规则,场馆方的空场损失明显减少。
  • 数据沉淀:谁在什么时间用了哪个场地、复购率如何,这些数据对场馆运营非常有用。

2. 整体设计思路与功能拆解

2.1 核心功能模块划分

运动预约小程序虽然看起来功能不多,但每个模块拆开都有讲究。我按源码里的实际实现,把核心功能分成五块:

场地管理模块:后台维护场馆信息,包括场地名称、类型(篮球/羽毛球/网球等)、位置、图片、可预约的时段规则、价格规则。这里有个细节需要注意:不同运动的计费方式差异很大,羽毛球按小时计费,篮球有时按半场计费,游泳按人次计费。源码里的价格模型是“场地+时段”组合定价,比较灵活,基本能覆盖大多数场景。

时段排期模块:根据管理员设置的规则,自动生成未来N天的可预约时段。比如一个羽毛球馆有4片场地,运营时间是9:00-22:00,每1小时一个时段,那系统每天自动生成4片场地×13个时段,也就是52个可预约格子。生成后用户可以按日期查看,已经被预约的格子置灰。

预约下单模块:用户选择场地和时段后,创建订单并锁定时段。这个模块是并发问题的高发区,必须做好防重复预约。后面我会单独讲这部分的实现细节。

支付模块:对接微信支付,包括下单、支付回调、退款三部分。微信支付v3接口的签名机制和回调验签逻辑,是很多新手容易踩坑的地方。

核销模块:用户到店后出示核销码,场馆管理员在管理员端(可以是小程序内嵌的管理页面,也可以是一个独立的管理后台)扫一扫或手动输入核销码完成核销。核销后订单状态变为“已完成”,对应时段彻底释放。

2.2 周边辅助功能

除了上述核心链路,这套源码还带了一些提升体验的辅助功能。比如场馆搜索和筛选,支持按距离、按运动类型、按价格区间筛选;订单中心,用户能查看待支付、已预约、已完成、已取消的订单;优惠券功能,后台可以创建满减券、折扣券,用于拉新和促活。

这些辅助功能里,我觉得订阅消息通知值得特别说一句。用户预约成功后,小程序通过微信订阅消息推送预约成功提醒;距离预约开始前30分钟,再推送一条临期提醒。别小看这个功能,它直接关系到到场率。实测下来,有临期提醒的场地爽约率能降不少。

3. 技术架构与选型

3.1 前端:原生小程序还是跨端框架

这套源码的前端用的是微信小程序原生语法(WXML+WXSS+JS)。我最初以为现在应该都用uni-app或者Taro了,但看完代码发现原生实现也有它的合理性。

原生小程序最大的好处是没有框架层损耗,页面渲染性能更可控,对微信API的调用也更直接。比如预约需要获取用户地理位置,原生实现只需要wx.getLocation,配合腾讯地图的逆地址解析即可;如果用了uni-app,还得考虑不同端的兼容封装层是否覆盖完整。

对于运动预约这种工具类小程序,页面结构并不复杂,主要就是列表页、详情页、订单页、个人中心几个页面,交互逻辑也不算重。原生语法完全够用,而且源码里针对长列表渲染做了优化——场馆列表用分页加载,每页10条,配合onReachBottom触发下一页,避免了数据量大了之后页面卡顿的问题。如果你团队里全是React或Vue背景的开发者,用Taro或uni-app重写一版也不是不行,但就这套源码来说,原生代码的可读性已经不错了。

3.2 后端与数据库设计

后端源码选的是Spring Boot 2.x + MyBatis Plus + MySQL + Redis的组合。这个选型在开源项目里非常主流,生态成熟、资料多、招人容易。

数据库表设计是这套源码比较出彩的地方,核心表我梳理了一下:

  • 场地表:记录场馆信息、场地名称、所属场馆ID、运动类型、价格基准。
  • 时段表:记录某个场地的某一天、某个具体时间段(如“2025-06-15 14:00-15:00”),包含状态字段(可预约/已被锁定/已预约/已核销)。
  • 订单表:记录用户ID、场地ID、时段ID、金额、支付状态、订单状态。
  • 核销记录表:记录核销时间、核销人、关联订单ID。

这里最关键的设计是:“时段”被独立成了一张表,而不是在订单里去校验时间交叉。这么做的好处是,判断“这个时间段还空不空”只需要查时段表的状态字段,性能和代码复杂度都低很多。如果用订单表做时间区间交叉校验,SQL写起来费劲不说,索引还不好建。

Redis在这个项目里承担了分布式锁热点缓存两个职责。预约时会先尝试获取Redis锁,key设计成“场地ID+日期+时段编号”,这样并发请求同时抢同一个时段时,只有一个线程能拿到锁并进入下单流程,其余请求直接返回“该时段已被预约”。后面我会细讲这个逻辑。

4. 核心逻辑的实现细节

4.1 时段自动排期怎么生成

我自己最感兴趣的一个模块,是排期生成。你想想,如果靠后台管理员手动去创建未来一周每个场地每一天的每个时段,那工作量得有多大。这套源码里用了一个定时任务来解决。

具体思路是在每天凌晨执行一个定时任务,自动生成“从今天开始未来7天”的时段数据。比如一个篮球馆有2片场地、每天营业时间为10:00-22:00、每2小时一个时段,那么系统会为明天生成2片×6个时段=12条记录,后天也是12条,以此类推,生成完7天共84条记录。生成时根据场地表里的时段规则,逐一场地、逐一天、逐一规则循环插入。

这里有个值得参考的点:为什么不实时生成而是提前生成?因为预约场景下,用户查询某个日期的时段列表是一个高频操作。如果每次查询都现场计算时段,势必要把所有场地规则加载到内存、循环拼接日期和时段,响应时间就会变长。而提前生成后,时段表里的数据已经是实体记录,用户查询就是简单的SQL查表,加上日期索引,毫秒级返回。这就是典型的用空间换时间

补充一下,源码里生成时段时,会先把即将过期的旧时段标记为“过期不可约”,同时对已经过了开始时间但还没被核销的时段做异常状态清理。这个清理逻辑很重要,否则会出现一个诡异场景:用户看到某个时段显示“可预约”,但时间其实已经过了。

4.2 并发预约的防超卖处理

这是整个项目里最核心、也最容易写崩的部分。我实操的时候反复测过并发场景,比如50个用户同时抢同一个场地的同一个热门时段,系统必须保证最终只生成一个订单。

源码里的方案分两步:

第一步,Redis预占锁。用户点击“立即预约”时,前端先调用后端接口,后端在Redis里执行SETNX操作,key为reserve:{场地ID}:{日期}:{时段编号},value为userId,同时设置过期时间(比如5分钟)。SETNX成功了,说明当前没有人在预约这个时段,可以继续往下走;SETNX失败,说明别人正在抢或者已经被抢走,直接返回“手慢了,该时段已被约”。

第二步,数据库乐观锁占位。拿到Redis锁之后,执行更新时段表状态的SQL,类似:

UPDATE time_slot SET status = 1, lock_user_id = #{userId} WHERE id = #{slotId} AND status = 0

注意这个SQL的核心是WHERE status = 0,这就是乐观锁的体现——只有当这个时段当前还是“可预约”状态时,才能更新为“锁定”状态。如果更新的影响行数为0,说明在Redis锁竞争到SQL执行之间,时段状态已经被改变了,这时候要释放Redis锁并向用户返回失败。

做完这两步,才开始创建订单、调起微信支付。这里有个时序上的小细节:先锁时段,再创建订单。如果反过来的话,极端情况下会出现订单创建成功但时段被别人抢走,订单变成了无资源可用的脏数据。

我还发现这套源码在支付回调的处理上比较稳妥。微信支付成功回调后,后端会更新订单状态为“已支付”,同时把时段状态更新为“已预约”。回调接口做了幂等处理——即使微信因为网络原因重复推送回调,后端也能通过订单号判断状态,避免重复更新。

4.3 支付对接中的一个容易忽略的坑

支付回调验签是最容易出问题的地方。微信支付v3的回调,头部有Wechatpay-SignatureWechatpay-TimestampWechatpay-Nonce,body是加密的报文。实操中我发现不少人的问题是直接用语言自带的JSON库去解析请求体,而平台要求使用AES-256-GCM解密,容易把密文当成明文存库,导致回调成功了,但库里订单金额是乱码。

正确的流程是先验签,再解密:

  • 用平台证书验签,确认回调确实来自微信。
  • 拿到resource里的密文,用APIv3密钥做AES-256-GCM解密。
  • 解出来之后,再从明文JSON里取订单号和金额,核对金额是否与订单一致。
  • 核对通过后再更新订单状态。

另外一个小坑是回调地址必须配置在微信支付商户平台的APIv3回调通知里,而且必须是HTTPS公网地址。我在本地调试的时候就因为回调地址填了内网IP,导致支付成功但订单迟迟不变状态,排查了半天才发现是这个问题。

5. 部署与二次开发实操

5.1 你需要准备哪些环境和配置

把源码跑起来之前,你需要准备好以下几样东西。这里我列一个清单,按准备顺序排:

  1. 微信小程序AppID和AppSecret,从微信公众平台申请。
  2. 微信支付商户号,以及APIv3密钥、商户API证书。这里注意,小程序和商户号要做关联绑定。
  3. 一台能跑Spring Boot的服务器,推荐2核4G及以上配置,操作系统最好选CentOS 7.x或Ubuntu 20.04。
  4. MySQL 5.7+,Redis 5.0+。
  5. 一个HTTPS域名,并完成ICP备案。同时要在小程序后台配置request合法域名、uploadFile合法域名。

配置项主要集中在后端项目的application.yml里。我把关键配置项整理成一个表,方便你对照修改:

配置项说明示例值
wx.appid小程序AppIDwx1234567890abcdef
wx.secret小程序AppSecret32位字符串
pay.mchId微信支付商户号1230000109
pay.apiV3KeyAPIv3密钥32位字符串
pay.serialNo商户证书序列号从证书中读取
pay.privateKeyPath商户私钥文件路径/etc/certs/apiclient_key.pem
db.urlMySQL连接地址jdbc:mysql://localhost:3306/sports_reserve

配置好之后,导入源码里的sql目录下的数据库脚本,然后启动后端服务。如果一切正常,日志里会打印“server started successfully”,并且定时任务开始自动生成明天的时段数据。

5.2 前端编译和对接

前端部分用微信开发者工具打开,修改app.js里的baseUrl为你的后端HTTPS地址,然后编译预览。这里有一个我在调试时发现的小窍门:在微信开发者工具里勾选“不校验合法域名”选项,可以在本地用HTTP的IP地址调试,能省去很多来回配置域名的麻烦。但上线前一定要记得关闭这个选项,并配置好真实合法域名。

另外,源码里用到了wx.login静默登录换取openid的流程。如果你之前只做过普通的网页端登录,第一次看这段逻辑可能会有一点头疼。简单说就是:前端调用wx.login获取一个临时code,把code传给后端;后端拿code加上AppID、AppSecret,请求微信的接口换取openid和session_key;拿到openid后在后端生成一个自定义的登录态token返回给前端;后续所有需要登录的接口都带着这个token即可。这是微信小程序标准的登录链路,源码里已经封装好了,直接调用就行。

6. 典型问题与排查技巧

6.1 支付成功但订单状态未更新

这是我在测试中遇到的第一个让人头大的问题:微信支付页显示扣款成功,但小程序订单中心里还是“待支付”。排查路径式很清晰:

  • 第一步,看微信支付商户平台后台,确认回调通知是否发送成功。如果回调通知列表里有记录但返回码不是200,说明后端处理回调时报错了。
  • 第二步,看后端日志,重点搜“notify”关键词,确认是否进到了回调接口。如果根本没进接口,检查回调地址配置是否正确、HTTPS证书是否受信任。
  • 第三步,如果进了接口但处理失败,多半是验签失败。常见原因是服务器时间不准,导致回调请求的时间戳校验不通过,用NTP服务同步一下系统时间就好。

当商户没有配置回调地址或回调异常时,微信支付会按照一定频率重复通知(通常15秒/15秒/30秒/3分钟/10分钟…),所以这类问题最终会被重试掩盖。但用户可能已经等不及了,所以很多项目会额外做一个主动查询支付状态的定时任务,每5分钟扫描一遍待支付超时的订单,向微信发起订单查询,作为回调的兜底。这个逻辑源码里没有,建议你自己加上。

6.2 并发测试时出现超卖

我在本地用JMeter做了50并发同时抢同一个时段的压测,第一次跑就有几个请求成功创建了订单,但时段只有一个——这显然超卖了。排查后发现原因是我在测试时没有先初始化Redis,Redis锁没有生效,只剩SQL乐观锁兜底,理论上也不会超卖。但看代码发现,SQL的UPDATE语句里,status = 0的判断条件被写成了status != 2(2代表已预约),这就导致状态为1(锁定中)的时段也能被重复更新,出现了漏网之鱼。

把条件改回严格的status = 0之后,再跑压测,50并发下只有1个请求成功,其余返回失败,表现正常。所以在这里提醒一句:锁时段的条件要严格判断等于“可预约”状态,不能用不等于排除法。虽然这属于源码里的一处小缺陷,但恰恰是这类问题的典型代表——很多超卖问题不是没加锁,而是锁的判断条件写得太宽松。

6.3 预约成功后时段被释放

还有一个场景值得注意。用户创建订单但一直没有支付,如果这个时段一直被锁住,对场馆方来说就是浪费。源码里提供了“超时自动释放”方案:定时任务扫描已锁定超过15分钟且未支付的订单,将时段状态重新置为“可预约”,同时把订单标记为“已取消”。这个逻辑是必须的,否则每天会产生大量无效锁定,真正想约的人约不上。

实现上要特别注意:自动释放时段和用户继续支付之间会有竞争。用户可能在15分钟临界点点支付,而定时任务同时释放了时段,这时候支付回调回来订单还在但时段已经被释放给其他人了。处理方案是在支付回调里再做一次校验:如果订单对应的时段状态已经变成别的用户的预约,说明该订单已经失效,需要走退款流程。这种边界情况在并发场景下一定要考虑到。

最后的实操心得

把全套代码跑通、再改掉几个坑之后,我对这种城市级运动预约项目的技术难度有了更准确的判断:它不算难,但足够琐碎。真正考验功夫的不是某个单点技术,而是预约状态机的正确性——从可预约、锁定中、已支付、已核销到过期释放,每个状态转换都要考虑并发和异常场景。如果你准备基于这套源码做二次开发,我建议先不要着急加功能,而是把时段状态流转画一张状态图(自己画就行,不需要给谁看),把所有可能的分支列出来,再对着源码看是否都覆盖了。

另外,授权协议也要提前看清楚。这类的开源项目一般用Apache 2.0或MIT协议,允许商用但要求保留版权声明;如果用了GPL协议,那你想闭源发布就会比较麻烦。动手改之前先确认协议,免得后期商务对接时出岔子。总的来说,这套源码作为学习样本或者业务原型,都值得花几天时间仔细过一遍。

本文还有配套的精品资源,点击获取

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

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

立即咨询