
简介这是一套面向场馆运营方与中小型开发团队的多场馆预约系统完整解决方案基于FastAdminThinkPHPUniapp技术栈构建专为体育场馆、共享办公、自助酒店等实体场所提供从在线预约、微信支付到现场核销的一站式数字化管理能力。资源包含后台管理系统、微信小程序前端及全套搭建教程支持私有化独立部署满足数据安全与业务自主需求。压缩包共2000个文件主体为1682个JavaScript逻辑文件、171个HTML页面模板、70个Vue组件及65个Markdown说明文档辅以SQL数据库脚本与配置文件总大小20.66MB结构清晰、模块解耦度高便于二次开发与场景适配。目前已有87人学习下载提供无加密源码、可运行的前后端工程、关键流程如短信登录、支付回调、核销扫码的完整实现以及install.html等部署引导页显著降低落地门槛。 做过多场馆预约系统的朋友应该都有同感真正麻烦的不是“预约”本身而是场馆、场地、场次、价格、核销这一整套状态模型。我之前帮客户搭过一套基于FastAdmin的场地预约支付核销小程序系统从后台权限到小程序端下单再到线下核销整套跑通之后才发现这类系统最难的不是代码量而是业务状态的设计和部署时那些藏在细节里的坑。这篇文章就把这套系统从核心设计、功能拆解到实际搭建过程完整梳理一遍。不管你是想自己搭一套预约系统还是准备接手类似的源码项目这篇内容都能帮你省掉不少试错的时间。1. 核心思路与方案选型1.1 为什么是FastAdmin先说选型。FastAdmin这个框架在国内PHP圈子里用得非常多它底层是ThinkPHP天然具备成熟的MVC结构、数据库操作、缓存机制和队列支持。但让我个人更看重的是它内置的那套后台管理系统AdminLTE风格的UI界面、Auth权限控制菜单、一键生成CRUD代码的脚手架这些对于做“预约系统”这类后台管理偏重的项目来说简直是量身定做的。举个例子正常从零写一个场馆管理模块你要处理路由、控制器、模型、验证器、视图模板、权限节点一套下来至少两三天。但FastAdmin的CRUD命令跑一遍基础增删改查和视图就全部生成好了你再往里面补业务逻辑就行。这个效率差距做过二次开发的人体会最深。另外FastAdmin本身自带插件机制和多语言支持虽然预约系统不一定用到复杂的多语言但插件机制对后期扩展很有价值。比如后期想增加会员卡、优惠券、消息通知等模块完全可以通过插件方式做增量开发不会污染主业务代码。1.2 多场馆系统的业务模型差异市面上很多预约系统只支持单场馆也就是一家店、多块场地管理起来相对简单。但标题里说的是“多场馆”这背后是一个维度更高的业务模型必须从设计上就处理好一个关键问题数据隔离与共享。多场馆系统里场馆数据是核心分类每块场地属于某个场馆场次模板按场馆独立设置甚至同一个场地的不同时间段价格都可独立配置。但用户侧的数据用户账号、微信OpenID又是全局共享的。这就意味着涉及场地、场次、订单、核销的数据表都必须带venue_id这类场馆标识而涉及用户的表则走全局维度。刚开始设计时容易犯的错是把“场馆”和“场地”混在一个表里处理。实际上这两个概念必须分开场馆是一个物理空间比如“XX体育中心”场地是场馆内可预约的资源比如“羽毛球1号场地”“篮球A场地”。场地必须挂在场馆下面场次则是“某场地在某天的某个时间段”。这种三层结构场馆-场地-场次加上订单、核销记录构成了整个预约系统的核心数据骨架。把这一层想清楚了后面写代码只是体力活。2. 核心功能拆解与实操要点2.1 小程序端的预约路径先从小程序端用户视角走一遍完整流程这样理解功能模块会更直观。用户打开小程序第一步是授权登录前端调wx.login拿到code后端用code去微信接口换openid然后静默注册账号。如果系统还要用户头像昵称可以通过wx.getUserProfile获取但这个接口的调起时机有限制需要放在用户主动操作事件的回调里否则会直接报错。登录之后是选择场馆这一步有两种常见方案一种是让用户通过列表或搜索来选择场馆另一种是基于地理位置的场馆推荐。后者需要在小程序里使用定位接口并且在后端根据经纬度计算距离。无论用哪种方案场馆列表封面图和营业状态要展示清楚用户才不会选错。选定场馆后进入场地选择页。这里需要按“场地类型”分组展示比如羽毛球、乒乓球、网球。用户选定具体场地后进入核心的“场次选择”页面。场次选择页是预约系统的核心交互界面。它需要展示可预约的日期通常是一个7天或14天的可滑动列表按日期展示该场地的时间段列表每个时间段需要标注状态可约、已约满、不可约、已过期不同时间段的定价可以不同比如周末价、晚场价这套交互的开发经验是不要在前端写死时间段的逻辑所有时间段数据都应该由后端接口根据场馆配置、场地配置、场次模板、已有订单实时计算出来。前端只负责渲染和提交选择这样后期调整价格、限制条件时不用频繁发版本。用户选择好时间段后进入确认订单页核对场馆、场地、日期、时长、金额提交后进入微信支付流程。支付流程走的是微信支付小程序支付JSAPI支付后端统一下单带回调地址前端通过wx.requestPayment拉起收银台。支付成功后后端在回调里更新订单状态为已支付并生成对应的核销凭证。用户在小程序里就能看到“待核销状态”的订单以及一个二维码或核销码。到店核销时前台人员在小程序或后台输入核销码、或直接扫码验证核销通过后订单状态变为已核销。到此一个完整的业务闭环才算结束。2.2 后台管理的核心模块后台是整个预约系统的控制台也是运营人员每天都要打交道的部分。功能上必须覆盖以下几个模块场馆管理维护场馆基础信息名称、地址、电话、营业时间段、经纬度、封面图、设施介绍并管理场馆的启用/停用状态。场地管理每个场馆下的具体场地资源字段包括场地名称、场地类型、容纳人数、单价基准和描述信息。场地还应该区分“独立计时场地”和“包时段场地”两种模式前者按小时计算后者按整场预约。场次模板管理这是预约系统里最灵活也最容易设计复杂的部分。场次模板的作用是定义“某场地在每周几的哪些时间段可以被预约”并针对不同时间段设置不同价格。比如周一至周五的9:00-18:00是普通时段单价50元/小时18:00-22:00是高峰时段单价80元/小时周六周日全部时段按60元/小时。通过模板运营人员一次配置长期生效。订单管理管理员可以查看所有订单筛选不同状态待支付、已支付、已取消、已核销、已过期并支持手动关闭异常订单。核销管理核销是预约系统区别于普通商城的关键功能。前台人员通过核销码验票核销成功后系统记录核销时间、核销人员并且核销操作不可撤销这在账目管理上很重要。数据统计按场馆维度统计预约量、核销量、支付金额、时段热力图。这些数据能帮运营优化场地资源配置比如发现每周五晚上羽毛球场地预约爆满就可以考虑增加场地或延长营业时间。2.3 支付与回调的状态处理预付订单系统的核心是状态流转预约系统的状态机要比普通商城更严格因为订单涉及时间资源。一个订单的状态至少要有待支付用户已提交但未付款需要设定一个有效支付时间比如15分钟超时后自动释放场次已支付微信支付回调到达后置为已支付同时锁定场次资源已核销用户到店后核销订单闭环已取消用户手动取消或后台关闭如果支付成功还需处理退款已过期超过预约时间后仍未核销系统自动标记为过期并释放场次资源这里有个容易踩坑的点支付回调的处理必须做幂等。微信支付回调可能因为网络原因多次推送同一个结果如果后端不做幂等处理就可能把订单状态从“已核销”覆盖回“已支付”或者重复加余额这种业务事故一旦发生非常难向客户交代。我习惯的处理方式是回调方法里先按商户订单号查订单判断当前状态只有当订单处于“待支付”时才执行后续状态更新。如果状态已经是“已支付”或更终态直接返回成功应答不做任何处理。另一个细节是支付金额的单位转换。微信支付金额单位是“分”系统数据库建议统一用“分”存储展示时再转成“元”。如果数据库用“元”但存了浮点数金额精度很容易出问题对账时更是一团乱麻。2.4 核销机制的两种主流实现核销功能的实体载体常见的有两种。第一种是二维码核销。用户在小程序端点击订单详情生成一个动态二维码后台核销员用专用的核销小程序或后台扫码枪扫描系统解码出核销码后进行验证验证通过则完成核销。这种方式用户体验好但需要端上有扫码能力如果后台是PC网页版需要外接扫码枪或者在小程序里做“扫一扫”入口。第二种是纯数字核销码。订单支付成功后系统生成6-8位数字作为核销码线下用户直接把码报给前台前台在后台输入确认。这种方式抗网络波动实现成本低很多场馆在信号不好的室内场地反而更实用。在实际商用场景里我推荐两种都保留用户端展示二维码同时把数字码显示在二维码下方后台核销页面同时支持输入核销码和扫码。这样既照顾了便利性又兼顾了无硬件条件下的核销需求。核销码的生成逻辑要保证唯一且不可预测。最简单的方式是生成随机数后与订单ID拼接做哈希取指定长度的数字部分。必须注意核销码不能直接用订单号本身否则用户可以通过规则推算其他订单的核销码带来安全风险。3. 部署搭建实战3.1 环境准备与版本选择这套系统的运行环境比较常规核心是PHP MySQL Web服务器。结合FastAdmin的历史版本兼容性环境建议如下PHP 7.4或8.0如果用ThinkPHP 8版本内核的FastAdmin建议直接上8.1MySQL 5.7以上建议8.0性能更好且原生JSON支持更完善Nginx 或 Apache生产环境我推荐Nginx配置清晰、性能好HTTPS证书微信小程序请求接口强制要求HTTPS这个必须有本地开发的话Windows用户可以直接用phpstudy或小皮面板一条命令装好NginxMySQLPHP还带数据库可视化管理工具非常方便。Linux服务器可以用宝塔面板做部署界面操作对新手特别友好运维成本很低。PHP扩展方面需要确认fileinfo、redis、swoole如果用队列等扩展是否已开启FastAdmin安装阶段的环境检测页面会明确列出哪些不满足跟着提示处理就行。3.2 FastAdmin后台安装步骤把源码上传到服务器Web目录后按以下步骤操作访问http://你的域名/install.php进入安装引导界面环境检测通过后填写数据库连接信息数据库名、用户名、密码和管理员账号信息安装完成后系统自动生成install.lock文件此时安装入口会失效防止重复安装访问http://你的域名/admin.php进入后台管理登录页用刚设置的管理员账号登录这里有几个经验点值得特意说一下第一安装之前先确认Web服务器根目录指向的是项目根目录/public。FastAdmin的入口文件在public目录下如果根目录指错了会出现访问白屏或者404的情况。Nginx配置里root指令需要指向public目录同时try_files要支持pathinfo模式否则ThinkPHP的路由解析会失效。第二安装完成后第一件事就是登录后台然后在“系统设置-常规”里修改默认的__DOMAIN__为你自己的域名并且把站点中的“快速开始”功能按需关闭。这些设置影响后台多数链接的生成别等业务跑起来后再改。第三后台的超级管理员账号密码务必设置成强密码并且开启登录验证码。FastAdmin默认支持后台登录验证码这个不要关。预约系统涉及资金流水后台安全等级怎么提升都不为过。3.3 小程序端配置与联调小程序端源码拿到手后需要修改的核心配置有这几处接口地址配置在小程序项目的公共配置文件中将请求基础URL改为你部署好的后端域名例如https://api.example.com。注意这个域名必须已配置HTTPS证书并且在小程序后台的“服务器域名”里添加为request合法域名。小程序AppID在project.config.json和app.js中替换成你自己的小程序AppID。这个不多说常识性的东西但确实见过上线前忘了替换导致真机调试报错的情况。微信支付配置商户号、API密钥、证书文件路径需要在后端配置中对应填好同时在小程序后台配置微信支付商户号关联。支付配置这步是最容易出问题的常见错误是证书格式不对或密钥长度不对导致统一下单接口调用失败。联调阶段建议用微信开发者工具的“不校验合法域名”功能来调试这样在开发期可以任意请求本地后端地址等联调完毕再打开合法性校验并进行真机预览。联调时有一个高频问题本地开发后端接口是HTTP协议而微信小程序的wx.request在正式环境强制要求HTTPS本地联调时如果不开“不校验合法域名”请求会被拦截。所以建议本地开发时就把开发者工具的“不校验合法域名、TLS版本以及HTTPS证书”打开这个开关在开发者工具右上角的“详情-本地设置”里。3.4 部署后的配置检查清单部署完成后不要急着上架小程序我建议按下面的清单逐项检查一遍后台能否正常登录权限菜单是否全部加载新增一个测试场馆、测试场地和场次模板小程序端能否拉到场馆列表和场地数据提交一个预约订单看能否正常生成待支付订单用测试微信支付或模拟支付完成支付检查回调是否触发、订单是否变为已支付在后台尝试核销该订单确认核销记录生成检查用户角度订单列表的状态显示是否正确整套流程跑通一遍确认没有状态错乱或数据缺失的问题才说明这个系统是真正可以交给用户使用的。4. 核心逻辑实现细节与原理4.1 数据库表结构设计思路这里把几张核心表的设计逻辑梳理一下理解了这张表的关联关系你就明白整个系统是怎么串起来的。场馆表venueid、name、address、lat、lng、business_hours_start、business_hours_end、status、cover_image、description。场地表siteid、venue_id关联场馆、name、type场地类型如羽毛球/篮球/乒乓球、capacity、base_price、status。这里要注意base_price是默认参考价最终的定价是按场次模板中的不同时段来确定的基地价格只做列表展示和兜底。场次模板表session_templateid、venue_id、site_id、week_day、start_time、end_time、price、status。一张场馆内的每块场地、每周7天的每个时段都可能对应一条模板记录。这样设计的好处是运营人员可以针对任意一个场地灵活配置周期性的预约时段和差异化价格而不是只能套用一种固定规则。订单表orderid、order_no业务唯一单号、user_id、venue_id、site_id、date、start_time、end_time、amount单位分、status、pay_time、verify_code、verified_time、verified_by。这张表是业务的核心每个字段在前后端交互中都会用到任何字段的缺失都会导致某个流程走不通。核销记录表verify_logid、order_id、verifier_id、venue_id、verify_time、remark。核销记录单独建表的好处是可以审计每一笔核销操作方便财务对账和纠纷处理。这几张表的关系是级联的场馆下有多个场地场地配置了场次模板用户根据模板生成订单订单完成后生成核销记录。整个过程数据是单向流动的避免了很多循环依赖的坑。4.2 场次模板与日期生成算法预约系统后台的核心难点在于“时间段”。用户看到的是某天某个场地哪些时间能约但系统内部实际上要经过三层处理。第一层场地规则层。场次模板里配置了“每周几的哪些时间段开放预约”这是无限循环的基础规则。第二层日期实例层。系统在用户选择日期后需要根据模板规则生成“当天该场地的可预约时间段列表”。这个列表就是最终展示给用户的数据。第三层状态过滤层。列表生成后还需要过滤掉“已约满”的时间段。判断逻辑是查询该场地、该日期、该时间段内状态为待支付或已支付的订单如果存在就标记为已约满否则为可约。这里有一个细节问题需要处理场次模板里定义的时间段是“预排”的但每天都会产生新的订单这也意味着状态过滤必须实时计算。实际项目中我建议在查询接口里用一条SQL同时完成“查模板生成时间查是否存在冲突订单”的过程减少前端二次处理。如果后续要扩展到“库存”概念比如某块场地在同个时间段可以被预约多次如多人团课场景那就需要再引入“库存数量”字段冲突检测的逻辑也要随之调整。预约系统的核心复杂度就在这个位置这里处理好了系统就稳定了一大半。4.3 订单状态机的设计状态机设计是预约系统里最容易出错的地方很多人写代码时只考虑“正常路径”忽略异常路径结果上线后各种状态错乱。我自己在项目中常用的状态机设计如下状态值含义可流转状态created待支付paid, cancelledpaid已支付used, refunding, expiredused已核销无终态cancelled已取消无终态expired已过期无终态refunding退款中refundedrefunded已退款无终态为什么“已核销”和“已取消”要设计为终态因为预约涉及时间资源一旦核销或取消意味着这个场次的资源已经不在可预约池里了必须保持不可逆避免财务纠纷。这里特别提醒一个常见问题超时未支付自动取消的时间设置。如果用户下单后不及时支付场次资源会被“锁住”其他用户想约也约不了。不同业务对这个时长的容忍度不同运动场馆一般15-30分钟医疗预约则可能需要更长的支付宽限期。这个参数应该做成系统配置项让运营人员可以随时调整而不是把死值写在代码里。4.4 并发预约的防超卖处理多场馆预约系统在上线后会遇到一个技术难点高并发场景下的预约资源防超卖。假设某天晚上7点一个热门场地的20:00-21:00时间段同时被10个用户提交预约请求如果没有并发控制系统可能把同一个时间段卖给了多个人。解决这个问题的思路具体实现上依赖于MySQL的事务和行锁。核心SQL大致是BEGIN; SELECT id FROM site_time_slot WHERE id ? AND status open FOR UPDATE; -- 如果查询无结果说明已被占用直接回滚 UPDATE site_time_slot SET status locked WHERE id ? AND status open; COMMIT;FOR UPDATE会对命中的行加排他锁第二个事务再执行同样查询会阻塞等待直到第一个事务完成后才能继续。在FastAdmin和ThinkPHP框架中可以直接使用事务闭包加上lock(true)方法来实现这个逻辑。另外还要给订单号生成设置唯一索引并在支付回调时校验订单归属用户避免用户越权操作他人订单。这些都是实际项目中必须考虑的细节。5. 常见问题与排查技巧实录5.1 安装阶段的典型报错安装时提示目录无权限FastAdmin的runtime目录、public/uploads目录需要写入权限。Linux服务器上执行chmod -R 777 runtime和chmod -R 777 public/uploads或者按更严谨的方式把目录所有者改为Web服务运行用户www或nginx并赋予对应写权限。安装时提示PHP函数被禁用通常需要开启putenv、proc_open等函数这些是Composer依赖时需要用到的。如果用的是宝塔面板可以在PHP设置中把禁用函数列表里的相关函数移除。访问后台白屏一般是public目录配置问题或PHP版本不兼容。先检查访问根目录是否正确再检查PHP错误日志FastAdmin的后台入口是admin.php不是index.php。5.2 小程序端接口报错排查nete::ERR_ABORTED或request:fail这类报错通常是域名配置问题。排查顺序为确认后端接口能正常访问、确认HTTPS证书有效、确认微信小程序后台已配置request合法域名、确认开发者工具已开启不校验合法域名。接口返回401一般是登录态失效或token问题。FastAdmin小程序端登录通常通过token鉴权检查请求头是否携带了正确的token登录态过期后需要重新调用登录接口换取新token。支付调不起来优先检查商户号、证书路径、回调地址配置同时确认小程序是个人主体还是企业主体。个人主体小程序无法使用微信支付这一点在需求确认阶段就要和客户沟通清楚。5.3 预约与支付的高频业务问题用户支付成功但订单状态未更新绝大多数是微信支付回调没配置好或者回调处理里逻辑有误。建议先模拟微信回调请求到回调接口检查日志中的响应输出确认返回了成功的应答。同时检查数据库订单表状态是否被意外覆盖。支付成功后场地仍显示可预约说明冲突检测和支付回调之间存在时间差或者状态过滤时只查询了“已支付”状态漏掉了“待支付”状态的订单。正确做法是冲突检测时把“待支付”和“已支付”都计算在内避免超卖。核销时提示核销码不存在先确认核销码是否来自正确订单排除用户截屏或输入错误的情况。再检查后端核销码生成规则是否和小程序端展示的一致如果两端逻辑不一致说明版本更新时出现了遗漏。5.4 经验技巧上线前一定要做的安全检查预约系统带支付和核销涉及资金和线下服务上线前有几项安全配置必须确认后台入口/admin.php要配置强密码或通过IP白名单限制访问微信支付证书文件不要放在Web可访问目录下避免被下载用户敏感信息手机号、OpenID在数据库里最好加密存储核销操作需要预留操作日志方便后期对账溯源关闭FastAdmin的调试模式避免错误信息泄露服务器路径和数据库配置这些配置虽然不能带来直接的业务增长但能在真实事故发生时帮你和客户免掉大量不必要的麻烦。我处理过几次客户服务器被扫描攻击的情况基本都是靠这些基础安全措施守住底线的。从核心表结构设计、小程序端预约支付流程到后台核销和部署上线的链路这套多场馆预约系统覆盖了一个典型交易闭环的所有核心环节。如果你正准备在自己项目中落地类似的系统我的建议是先把状态机和并发处理这两个底层设计想透再去搭建页面和接口至少能省下后面一半的返工时间。本文还有配套的精品资源点击获取