资讯详情

台球室预约小程序开发实战:从业务拆解到上线运营指南

📅 2026/10/9 11:07:00 | 华诺云谱 👁 阅读
台球室预约小程序开发实战:从业务拆解到上线运营指南
春节前一位开台球室的朋友找我说要做一个台球室预约小程序。他那边35张球桌高峰期电话不断手写登记本翻得稀烂散客到店经常要等一两个小时。我花了两周把整套台球室预约平台搭起来小程序端给顾客选球台、选时段、在线付押金下单后台给他自己管理球台、时段、会员和订单。这条路走下来有收获也有不少坑我用这套台球室管理系统的完整过程写下来从业务拆解到上线运营都讲清楚同行或者准备做预约类小程序的都可以参考。1. 为什么台球室需要一套预约系统1.1 球房运营的真实痛点先拆业务。台球室和其他休闲业态不太一样它有一个很特殊的地方球台是一种“按时段独占”的资源。一张球台在某个小时被人占了其他顾客就没法用。高峰期大家都在门口等着散客等一小时是常事而电话预约又经常被高峰期的铃声淹没忙起来根本记不住。老顾客打电话订台服务员抄在本子上抄错、漏记、重复订台都发生过。到了现场发现台被人占了顾客当场翻脸口碑一下就没了。从顾客角度看痛感也很直接电话经常打不通就算打通了也没法直观看到现在哪张台空着、接下来两个小时能不能订。到了店才发现要排队半天时间就耗在等待上。这类消费本身就是到店前的“计划型消费”顾客愿意提前预约只是没有靠谱渠道。所以当时我给朋友做的第一件事不是写代码而是把预约业务链路上所有人、所有节点理清楚。一套合格的台球室预约系统至少要考虑三类角色顾客、前台店员、老板管理员。顾客要能快速选台、支付、查看订单店员要能核销、换台、处理超时老板要能设置球台价格、不同时段的不同计费策略、查看每天的满台率、非高峰期的空台率。只有这三块都打通系统才算真正用起来。1.2 预约业务全链路拆解我把整个预约链路拆成八步顾客打开小程序看到球房今天可用球台的实时状态。选择一个球台再选择预约日期和时间段。系统检测这个球台在该时间段是否空闲如果被占则提示换时段。顾客支付定金或全额费用生成预约订单。到店后店员用管理端确认核销订单状态变为“使用中”。顾客结束打球系统按实际时长/局数结算返还剩余押金或发起补扣。订单转为“已完成”进入数据统计。老板在后台看每天的营收、球台使用率、高峰时段。这个链路里最容易出问题的点是第3步和第6步。第3步涉及并发冲突两个顾客同时看到一张空闲台同时下单处理不好就会出现“超卖”。第6步涉及计费规则预约按小时计费实际打球超时了怎么办必须在一开始就想清楚。后面的章节我会顺着这条链路把技术实现和踩坑细节一个个展开。先把技术选型和页面结构定下来再讲核心逻辑最后讲调试和上线。2. 小程序端整体设计与技术选型2.1 技术栈选择uniapp还是原生这个项目用的是uniapp Vue3的组合。当时在原生微信小程序和uniapp之间权衡了很久最终选uniapp主要有两个原因一是后续客户可能还要抖音小程序、支付宝小程序一套代码多端编译能省不少事二是我自己更熟悉Vue的写法模板语法、组件化、状态管理都顺手开发效率明显高于原生小程序的wxml js。但uniapp不是没有坑。最明显的就是打包后的代码体积比原生大因为框架本身要带运行时。后面做分包加载的时候比原生小程序要更注意静态资源和页面拆分。还有一个细节uniapp里很多内置组件的样式在不同端表现不一致比如单选框radio在微信和H5上的视觉差异就很大。我们的球台选择页面用了自定义卡片式单选纯靠CSS控制避免依赖原生radio组件的差异。如果你不想引入框架直接用微信开发者工具写原生小程序也完全可行。这个项目的核心是逻辑和业务设计不是框架。原生小程序在真机调试、调试器报错信息上反而更直接。选哪个不重要重要的是先把页面结构定下来。2.2 核心页面与路由设计小程序端我规划了四个主页面首页球台列表展示球台编号、位置、类型中式八球/斯诺克/九球、当前状态空闲/使用中/已预约、接下来的可约时段。预约页从首页点进某张球台后进入选择日期、开始时间、时长系统实时显示“可约”还是“冲突”确认后生成订单。订单页我的预约列表状态分待支付、已预约、使用中、已完成、已取消。列表用分页加载滚动到底部加载下一页。我的页面用户信息、会员卡、优惠券、联系客服、设置。路由上采用微信原生的tabBar模式。首页和订单页放tabBar预约页作为普通页面压栈进入。订单页用到了微信小程序“页面列表加载更多”的经典玩法onReachBottom触发下一页加载配合isLoading锁和hasMore标记避免重复请求。有一点要提前说tabBar页面在微信里最多5个我们四个正好。如果后面要加分销、积分商城就得考虑分页跳转或者把tabBar替换掉。2.3 状态管理与球台实时状态同步台球室预约是一个强状态系统。球台的“空闲/预约/使用中/维护中”状态白天的每一分钟都在变化。如果只靠前端本地状态两个手机打开同一个页面看到的球台状态会不一致。我的做法是球台状态尽量由服务端实时计算不在前端存副本。前端拉取球台列表时服务端根据当前时间和所有有效订单实时计算每张球台的状态返回给前端。比如现在18:30球台3号有一笔17:00-19:00的预约单服务端就返回“使用中”如果下一笔预约从19:30开始前端会在19:30到来后主动刷新一次。前端的状态管理用的Pinia只存三样东西用户登录态、当前选中的球台和时间段、全局门店配置。不要把所有球台状态都塞进store里因为那部分数据本身是动态的缓存反而容易变成脏数据。3. 预约的核心逻辑与计费规则3.1 时间段冲突检测这是整个预约系统最核心的算法没有之一。一张球台在某个日期下所有有效预约记录组成若干个时间段。新来的预约要判断[newStart, newEnd)是否和已有时间段重叠。判断重叠的逻辑很简单两个区间[a, b)和[c, d)不重叠当且仅当b c或者d a。反过来只要newStart existingEnd并且newEnd existingStart就是冲突。我用一个例子说明。球台5号已经有两条预约10:00-11:00、14:30-16:00。现在要抢的是13:30-15:00。用代码走一遍const existing [ { start: 10:00, end: 11:00 }, { start: 14:30, end: 16:00 } ]; const newStart 13:30; const newEnd 15:00; const hasConflict existing.some(slot { return newStart slot.end newEnd slot.start; }); // true因为 13:30 16:00 且 15:00 14:30前端用这个逻辑在用户拖动时间时实时提示冲突。但真正防超卖要靠后端。后端生成订单时不能只靠查询判断因为两个请求可能同时查都认为没冲突。稳妥做法是两张表配合预约订单表里加一个联合唯一索引(table_id, date, start_time, end_time)插入时数据库直接拒绝重复。同时还可以在事务里给该球台当天记录加锁或者用Redis的分布式锁锁的key设为table:{id}:{date}。对于小店比如一天预约单不超过300条数据库唯一索引就够了。我实际用的是“先查后插唯一索引兜底”简单可靠没有引入Redis维护成本也低。3.2 计时计费与会员储值设计预约收费我设计成两种模式预付定金和全额预付。定金模式适合散客预约时先付10元定金占位到店后按实际时长再结算。全额预付适合节假日高峰比如春节期间球台非常抢手先用全额锁定放鸽子的成本由顾客承担。计费单位我统一用“半小时一档”。比如标准台20元/小时那么0.5小时就是10元。用户选时间时时间选择器的步长就是30分钟避免出现“1小时17分钟”这种没法计价的情况。会员储值这块做了一个简单的余额体系。会员卡本质就是用户表加一个balance字段充值100送20、充300送80这类规则配置在管理端。用户用余额支付时前端展示余额和应付金额下单时后端做一次余额扣减。这里有个容易忽略的坑余额扣减和订单创建必须在同一个事务里否则可能订单创建成功但钱没扣或者钱扣了但订单没生成。我用的事务顺序是开启事务 → 锁用户余额 → 判断余额充足 → 扣减 → 创建订单 → 提交事务。如果中途失败全部回滚。3.3 预约状态流转与超时释放订单状态我用数字表示0待支付、1已预约、2使用中、3已完成、4已取消、5已过期。超时释放是预约项目的生命线。散客预约了19:00-20:00但19:15还没到店这张台一直空着其他想玩的顾客被耽误了。我的规则是预付定金的订单超过预约开始时间15分钟未核销自动标记为“已过期”释放球台如果顾客后来到店可以用未消费定金顺延预约其他空档。全额预付的订单不自动释放因为钱已经收了除非顾客主动取消。自动释放不能只靠小程序端定时器小程序在用户退出后就不会运行了。我在服务端写了一个每分钟执行的定时任务扫描所有“已预约”且“开始时间早于当前时间-15分钟”的订单批量改成“已过期”。这个任务用Node.js的cron表达式调度部署到服务器后我实测了几天凌晨的释放任务也没有误伤过正常订单。4. 微信登录、手机号获取与后台管理4.1 微信登录换取openid的完整流程每个微信小程序用户都对应一个openid这是用户在小程序内的唯一标识。流程不复杂前端调用 wx.login 获取临时code。前端把code传给后端接口 /auth/login。后端用code调用微信的 jscode2session 接口换取 openid 和 session_key。后端用openid查用户表新用户自动注册老用户直接返回。后端生成自己的登录态token我用的JWT有效期7天返回给前端。前端把token存到uni.setStorageSync后续请求都带上。有个细节必须注意openid和session_key绝不能让前端拿去做判断。session_key是微信端解密的密钥一旦泄露前端就可以自己解密用户敏感数据风险很大。我后端拿到session_key后直接丢弃只在需要解密手机号时临时使用。自建token的过期和刷新也建议做我们的token是7天有效期过期后前端拦截器检测到401自动调用refresh接口重新登录用户无感续期。登录态是接口安全的根基。很多新手在这步图省事把openid直接作为登录凭证存本地这是非常危险的。4.2 手机号快速验证组件接入微信小程序的“获取手机号”以前可以用wx.getUserProfile拿到手机号但后来微信把玩法改了现在主流做法是用手机号快速验证组件。接入步骤很简单页面上放一个button加上open-typegetPhoneNumberbutton open-typegetPhoneNumber getphonenumbergetPhoneNumber微信一键登录/button用户点击授权后bindgetphonenumber事件回调里会返回一个code拿着这个code去后端调微信接口换取真实手机号。注意这里的code是动态的一次性使用有效期只有5分钟而且只能用一次。这里有一个很关键的坑这个组件不是所有小程序都能用要求小程序已完成微信认证主体是个人开发者不行并且在微信公众平台后台开通“手机号快速验证组件”权限。如果你接的时候发现按钮点了没反应先检查这两项。开发调试阶段微信开发者工具里可以用测试号模拟但拿到真机微信上跑没有权限就是不行。我当时卡在这里一个多小时后来才发现是后台权限没开。这个权限申请审核很快一般当天就能下来。4.3 后台管理端的页面规划与接口设计管理端我做成一个H5后台管理员直接扫二维码打开网页登录不用装APP。页面就五块工作台、球台管理、订单管理、会员管理、设置。工作台放今日数据营收、订单数、满台率、正在使用的球台。球台管理负责增删球台给每张台设置类型、时价、照片。订单管理支持按日期/状态筛选是前台客诉和后台对账的核心页面。会员管理查储值余额、充值记录。设置里配置营业时间、预约提前量和最小预约时长。接口设计遵循一个原则前端小程序和管理端共用一套后端API但用不同的权限控制。小程序端用的是用户token只能操作自己的订单管理端用的是管理员token可以操作全局数据。接口鉴权用中间件统一处理比如创建订单的接口校验用户身份核销接口校验管理员身份。预约列表这种接口我用了最简单的分页格式请求参数带page和pageSize返回数据带list和hasMore。列表页在小程序端用onReachBottom加载更多管理端用按钮点击加载更多。两个端共用同一套分页约定后端代码只写一次。5. 开发调试实战抓包、体积与导航栏适配5.1 用代理工具排查登录接口问题小程序真机调试时看不见网络请求是很多人的痛点。电脑上可以用微信开发者工具直接看Network面板但真机预览时开发者工具里模拟的请求和真机上跑的不完全一样。卡在登录、支付这类接口问题时抓包是最管用的排查手段。我用的是Charles一套很成熟的HTTP代理抓包工具。基本流程是电脑上安装并打开Charles记下电脑的局域网IP和默认的8888代理端口手机和电脑连同一个WiFi在手机WiFi的高级设置里找到HTTP代理填上电脑IP和8888端口。手机第一次连代理时需要下载并信任Charles的根证书这样才能解密HTTPS请求。完成之后手机上打开小程序Charles里就能看到这个小程序发出的每个请求包括请求URL、请求头、请求体和返回体。登录接口返回401、token失效、参数格式不对看一眼返回体就清楚了。排查完记得把手机的代理关掉不然手机上其他App的网络请求会走电脑代理容易误伤别的服务。这里补充一个微信小程序的限制正式环境下小程序请求的域名必须在小程序后台配置为合法域名且必须是HTTPS。开发调试阶段可以直接在工具里勾选“不校验合法域名”但真机调试阶段如果域名没配好抓包看到的第一个问题就是request fail这个也属于常见坑放后面问题清单一起说。5.2 导航栏动态高度适配自定义导航栏是台球室预约小程序的外观刚需。默认导航栏的标题只能是固定的文案顶多动态改个标题但很多球房想让顶部栏和自己的品牌色、店招图片融为一体那就得自定义导航栏。自定义导航栏第一个问题就是顶部高度怎么算。不同手机的刘海屏、灵动岛、状态栏高度都不一样不能写死。我用的是微信官方推荐算法const { statusBarHeight } uni.getSystemInfoSync(); const menuButton uni.getMenuButtonBoundingClientRect(); const navBarHeight (menuButton.top - statusBarHeight) * 2 menuButton.height;简单解释一下statusBarHeight是手机顶部状态栏高度menuButton是右上角胶囊按钮的位置信息。自定义导航栏的高度等于胶囊按钮所在区域的高度这个公式计算的是胶囊按钮的垂直居中位置偏移实测在绝大多数手机上都很准iPhone和小米、华为我都验证过。做自定义导航栏时还有个隐藏问题页面滚动时自定义标题栏要固定在顶部不能跟着页面滚走。实现上用position: fixed 占位div占位div高度等于导航栏高度否则页面内容会被导航栏遮住。我在这上面吃过亏第一次做的时候忘了加占位内容从顶部直接钻到导航栏下面去了。5.3 分包加载解决2MB体积限制微信小程序的代码包有2MB的主包限制。uniapp打包出来的工程很容易超我的项目第一次打包就报错提示source size 2612kb exceed max limit 2mb。解决思路就是分包小程序允许把页面拆分到分包里主包只保留启动页、tabBar页面和公共资源分包里的页面在用户访问时按需加载。在uniapp里分包很简单pages.json按官方结构配置{ pages: [ pages/index/index, pages/order/order, pages/mine/mine ], subPackages: [ { root: pagesSub/book, pages: [ book, pay-success ] }, { root: pagesSub/ticket, pages: [ my-coupon ] } ] }把预约下单、支付成功页、优惠券页这些不常直接打开的页面拆分到分包里主包体积立刻降到1MB左右。除了分包体积瘦身还能做三件事图片不要放本地全部传CDN换URL公共组件尽量复用不要每个页面复制一份第三方UI库能不用就不用uniapp自身的组件够用UI库全量引入通常是最占体积的元凶。我项目里没用任何第三方UI库所有列表、卡片、弹窗都是自己写的样式视觉统一还省体积。但分包也有坑tabBar页面不能放分包里分包之间不能互相跳转跳转要写全路径分包里不能有公共的全局自定义组件除非重复引用。还有包内的静态资源也会算体积本地图片是整个分包最大的拖累。5.4 门店信息动态配置与接口错误排查球房老板经常会改门店名称小程序端标题不可能跟着每个版本发。我在小程序首页onLoad时先调用门店配置接口拿到店名后调用uni.setNavigationBarTitle({ title: name })动态设置导航栏标题这样后台改一下名称用户端刷新后就变新名字不用重新提审。同样需要动态控制的是页面的分享文案用onShareAppMessage动态返回title和imageUrl把“XX台球俱乐部·在线预约”作为默认分享文案地推和裂变都靠这个。接口报错排查我总结了固定套路。先看小程序的Network面板或Charles里响应状态码401是token过期先检查登录态500优先看后端日志4xx一般参数问题多比对请求参数和后端路由参数名。微信相关的错误码比如登录换取token时返回的code无效那就是wx.login返回的code过期了有效期5分钟或者被使用了两次。遇到这类问题别急着改代码先把微信返回的原始报错信息完整贴出来再定位。6. 上线后的运营迭代与复盘6.1 数据统计维度设计系统上线不是终点运营数据才是检验系统好坏的标尺。后台我加了几个关注维度每日/每周的营收曲线、球台使用率、高峰期分布、取消率和过期率。取消率是最值得盯的指标。如果每天取消率超过15%说明预约门槛太低顾客把预约当免费占位应该提高定金或者缩短免费取消时间。过期率则反映了到店履约情况过期率高说明前台核销不及时顾客可能已经到店被店员手工安排了但没在小程序里操作超过15%就要给店员做培训。这些统计在后端一张日汇总表里每晚定时任务算好存进去第二天打开管理端直接看不用实时聚合。实时聚合数据量大了以后会拖慢数据库夜间离线计算更稳妥。6.2 微信开发者工具的分发试用与反馈迭代开发完第一版后我没有直接发布到线上而是先在微信开发者工具里把小程序工程导出成体验版二维码发给店长、店员和一个老顾客群试用。微信开发者工具支持“上传体验版”功能上传后生成二维码最多可以配置几十个体验成员扫码即可进入真实环境操作方便收集试用反馈。这一周收集到的反馈真实有效。店员提得最多的是核销操作太繁琐要找到订单再点核销后来我把首页直接放了一个“扫码核销”入口顾客出示预约二维码店员扫码就能定位到订单操作从三步变成一步。顾客反馈最集中的是找不到球台编号因为台球室灯光偏暗首页列表的球台位置信息不清楚后来在球台列表加了“近门口/靠窗/最里面”的位置说明反馈明显减少。用体验版的真实反馈迭代两个版本再提交审核正式发布。这个流程走得越稳上线后翻车概率越低。6.3 避坑清单最后整理一份从开发到上线完整踩过的坑每一条都是真金白银换的预约冲突检测一定要“前端提示 后端唯一索引”双重保障只靠前端判断一定会出超卖。手机号组件需要企业认证小程序个人主体项目在接手机号接口前先确认主体类型别等打包上线才发现。自定义导航栏高度不写死用胶囊按钮位置动态计算fixed定位别忘了配占位元素。预约自动释放用服务端定时任务不要指望小程序端定时器。主包超2MB直接分包不要在压缩图片上死磕图片本身就该走CDN。抓包排查完记得关手机代理不然其他App请求全走电脑轻则卡顿重则误耗流量。体验版测试一定要用真机开发者工具模拟器和真机在手机号授权、支付回调上的表现差异很大。我个人在这套台球室预约平台上最大的体会是预约类系统的核心不在界面多炫而在状态一致性。球台空闲、订单冲突、余额扣款每一处都牵扯到并发和数据正确性。把业务链路拆透把并发兜底做扎实上线后才能真正稳得住。后面如果要做多人拼场、分段AA计费或者接入台球计费系统的智能灯控这套预约骨架也还能继续往上长。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑