ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

共享棋牌室小程序开发实战:从预约到开门全流程实现

共享棋牌室小程序开发实战:从预约到开门全流程实现 简介随着共享经济向垂直场景渗透棋牌室、茶室、台球室等无人值守空间的预约管理成为创业者和开发者关注的热点。微信小程序作为轻量级入口结合JavaScript实现完整的预约、支付、押金、结算和智能门锁联动是构建这类系统的核心路径。理解预约系统背后的业务模型与订单状态机是技术落地的关键。从场地展示、场次筛选、微信支付到自动结算每一个环节都需严格处理资金安全与状态一致性。本文基于一套生产环境运行的共享空间预约小程序源码拆解项目策划、数据库设计、核心代码实现以及踩坑记录帮助你快速掌握从零搭建一套可复用的共享棋牌室预约系统。 共享棋牌室、茶室、台球室这类生意最近两年在二三线城市冒出来特别多。一个老板手里三五套房源摆上麻将桌、茶台或者台球案不请服务员靠一套预约系统自动运转。我去年帮朋友从零做过一版这样的微信小程序用的就是原生JavaScript加微信小程序框架整套源码到现在还在线上跑着。这篇文章把当时的设计思路、核心代码、踩坑记录全部摊开来讲如果你正打算做类似的共享空间预约系统可以直接抄作业。这篇内容适合三类人看一是准备进入共享空间赛道的创业者你需要知道系统应该有哪些模块、资金怎么流转二是接私活或做外包的开发者这里有一套完整可落地的源码设计思路三是正在学微信小程序开发的学生或转行者把整个业务流程走一遍比刷一百个登录 Demo 都有用。1. 共享空间小程序的前期策划与业务模型设计1.1 需求本质不是做预约是做无人值守的资产管理很多人第一次接触棋牌室小程序以为核心是“预约”这个理解是有偏差的。预约只是表面动作真正的核心是无人值守状态下的资产流转管理。你想一下一间棋牌室一天能接多少单工作日中午到晚上周末全天满打满算也就五六单。如果还要前台登记、收押金、计时、催场、结算人工成本直接吃掉利润。所以小程序要解决的是把“接待-入座-计时-结算-离场”这五个环节全部自动化。具体到功能上就拆成了这样几个模块房间展示与时段筛选用户按小时或按场次购买押金收取与退还有效期管理到店扫码或密码开门联动智能门锁使用中计时超时自动续费或断店提醒订单完成后自动结算押金原路退回这套逻辑放在棋牌室、茶室、台球室完全通用因为它们的核心商业模式都一样——按时间出租空间。棋牌室按小时计费茶室按包厢计费台球室按台桌计费只是在计费规则上有细微差别而这些差别可以通过后台配置解决。1.2 技术选型为什么用原生小程序而不是 uni-app 或 Web 套壳技术选型是动手前最纠结的地方。我见过有人用 uni-app 做也见过用 H5 套壳的但最终我都建议用原生微信小程序加 JavaScript 开发。先说 uni-app。它确实能一套代码多端复用但问题是共享空间这个场景里有大量硬件联动比如智能门锁、扫码设备、打印机。这些设备的厂商 SDK 往往只提供微信小程序原生插件uni-app 走一层封装就可能出现兼容问题。你做一个棋牌室项目客户只要求微信端跑通没必要为了“未来可能做 App”去承担多端框架的兼容成本。再说 Web 套壳。很多外包团队图省事用 WebView 套一个 H5 页面本质上是个网页。但微信小程序里WebView 的支付能力受限无法直接调用 wx.requestPayment定位、蓝牙、扫码等原生能力也都要通过各种桥接层去调链路长了故障点就多。共享空间的核心体验是“从打开微信到开门锁全程不超过30秒”任何多余的网络请求和加载等待都会毁掉体验。原生 JavaScript 开发的好处是微信所有 API 直接调用、调试工具成熟、 community 生态丰富、遇到问题搜一下就能找到答案。坏处当然也有代码不能复用到其他平台但如果你只做微信生态这点代价完全可以接受。1.3 业务模型押金、计费、退款的完整资金闭环这块是我认为最有价值的部分因为它决定了整个系统的复杂度。很多人第一次做共享空间小程序把订单做完就以为结束了实际上一碰押金和超时结算就会出问题。我设计的资金流是这样的用户下单时支付“租金 押金”押金不是单独再走一次支付而是合并成同一次支付但后台把金额拆分记录使用结束后后台自动计算实际费用基础租金 超时费用 - 优惠原订单的租金部分按实际费用多退少补押金全额原路退回这里有个关键点微信支付的退款接口是按订单号操作的。所以我的做法是用户支付时生成一个“支付单”支付单里包含租金和押金两个金额字段。结束时先从支付单退押金再根据实际租金差异走退款或二次支付。听起来简单但实际开发中有个坑如果用户频繁取消订单或者中途改时间你的支付单状态管理稍微混乱就会出现退了押金但租金算错或者押金重复退的 bug。所以我在订单表里专门加了一个refund_status字段每次退款操作前先判断这个字段确保同一笔押金只退一次。2. 小程序端核心功能拆解与实现思路2.1 登录授权与用户体系搭建不存密码只用微信身份微信小程序的登录不像传统网站不需要用户名密码。它基于微信的 openid 机制用户在授权后前端通过wx.login()获取临时 code后端拿着 code 去微信接口换 openid 和 session_key。这里有一个经验建议不要用按钮主动弹授权框。微信官方早就调整了策略用户点击授权按钮时如果中途取消再次弹出会被拦截。我现在的做法是用户进入小程序先不弹任何授权框只有点击“预约下单”时才触发wx.getUserProfile把头像昵称顺带拿一下。这样授权成功率几乎达到100%。用户登录后的状态管理我采用 token 机制。后端拿到 openid 后生成一个自定义 token比如 uuid存到 Redis 或数据库里设置有效期7天。小程序端把 token 存到wx.setStorageSync后续所有请求头部带上Authorization: Bearer ${token}。这样用户每次打开小程序先检查本地 token 是否过期如果没过期就直接用过期了才静默重新登录。这段代码是登录的核心// 小程序端app.js 里的登录方法 login() { return new Promise((resolve, reject) { wx.login({ success: async (res) { if (res.code) { try { const loginRes await request({ url: /api/user/login, method: POST, data: { code: res.code } }) wx.setStorageSync(token, loginRes.data.token) resolve(loginRes.data) } catch (err) { reject(err) } } else { reject(new Error(微信登录失败)) } } }) }) }后端的接口逻辑是收到 code 后调用https://api.weixin.qq.com/sns/jscode2session用 appid 和 secret 换 openid。注意这个 secret 绝对不能放前端必须在后端请求。2.2 场地浏览、时段筛选与预约下单场地列表页是用户第一眼看到的东西也是转化率的关键。我做的列表页包含场地封面图、名称、位置距离、按小时计费的价格、当前状态空闲/使用中/清洁中。这里有个产品细节状态要实时刷新。你想想用户看到某个包间显示空闲点进去正要下单结果发现刚被另一个人订了这种体验非常糟糕。我的方案是用小程序的wx.connectSocket建立 WebSocket 长连接或者更轻量的做法是前端每30秒轮询一次场地状态接口。考虑到棋牌室这种低频场景30秒轮询足够了不需要上 WebSocket 增加复杂度。时段筛选是个技术细节。你不可能让用户自由选任意时间段那样后台排场没法管理。我的做法是固定场次——午餐场10:00-14:00、下午场14:00-18:00、晚场18:00-22:00、夜场22:00-02:00用户按场次下单。这样好处有两个一是库存管理简单一场一个状态二是时间边界清晰到点提醒下一个用户入场避免纠纷。下单请求的核心代码// 提交预约订单 submitOrder(roomId, sessionId) { const token wx.getStorageSync(token) return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}/api/order/create, method: POST, header: { Authorization: Bearer ${token} }, data: { roomId, sessionId, orderType: reserve, couponId: this.data.couponId || null }, success: (res) { if (res.data.code 0) { // 创建订单成功返回订单号和预付金额 resolve(res.data.data) } else { wx.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail: (err) reject(err) }) }) }2.3 支付、押金与自动结算逻辑把规则说清楚支付在小程序里就是调wx.requestPayment但这个接口要求订单必须先经过微信支付统一下单接口。我的后端流程是收到下单请求后生成业务订单状态“待支付”调用微信支付统一下单 API获取支付参数前端拿到参数调起收银台用户完成支付微信服务器回调后端通知支付成功前端收到成功回调后轮询订单状态跳转“预约成功”页这里有个必须注意的坑微信支付的wx.requestPayment必须由用户点击触发不能在异步回调里调。如果你在页面 onLoad 里直接调支付微信会报错“invalid request”。所以我在预约页加了一个“确认支付”按钮用户点击后先加载支付参数参数就绪后再调起支付。自动结算这块我用了一个定时任务来实现。每个订单的结束时间到了以后服务端逻辑开始执行// Node.js 定时结算服务伪代码 async function autoSettle() { const expiredOrders await Order.find({ status: using, endTime: { $lte: new Date() } }) for (let order of expiredOrders) { const room await Room.findById(order.roomId) const actualHours calculateHours(order.startTime, order.endTime) const actualAmount actualHours * room.hourlyPrice const deposit order.deposit // 多退少补 if (actualAmount order.paidAmount) { // 发起二次收款 await wxPay.charge(order.openid, actualAmount - order.paidAmount) } else if (actualAmount order.paidAmount) { // 退款差额 await wxPay.refund(order.paymentNo, order.paidAmount - actualAmount) } // 退押金 await wxPay.refund(order.depositNo, deposit) order.status completed await order.save() } }注意这段代码只是业务示意正式的线上项目里退款和二次收款需要重试机制还要记录每次调用的返回码方便排查问题。2.4 扫码开门与硬件联动的实现方案单独把这个拎出来讲是因为这是整个系统里坑最多的地方。共享空间常用的门锁方案有三种智能门锁蓝牙/密码、智能电控锁通电开锁、人脸识别门禁。棋牌室和茶室用得最多的是密码锁加电控锁的组合。我采用的是“密码锁 扫码触发后台开锁”的方案。用户预约成功后小程序上会显示一个“开门”按钮点击后调用后端接口后端通过物联网模块发送开锁指令到指定的电控锁。这个设计里要思考的是安全边界。你不能让用户拿着小程序在店外直接开门那等于给小偷发了钥匙。所以我的处理是开门接口做了两个校验——第一用户距离门店必须小于200米通过wx.getLocation获取坐标后端算地球两点距离第二订单状态必须是“已支付”且在有效时段内。如果不想依赖电控锁的物联网模块可以用更轻量的方式在门锁上贴一个二维码用户扫码后跳转到微信小程序的开门页面页面读取 URL 参数中的房间号再调用开门接口。这种方式不需要额外设备只需要门锁本身支持远程开锁或密码下发。3. 后台管理与数据库设计3.1 数据库表结构从房间到订单的完整链路整个系统的核心数据表有5张用户表、房间表、场次表、订单表、支付流水表。再加上一张优惠券表和一张配置表就够了。用户表user的核心字段字段名类型说明idint主键openidvarchar(64)微信唯一标识nicknamevarchar(32)微信昵称avatarvarchar(255)头像地址phonevarchar(20)手机号可空balancedecimal(10,2)账户余额可用来抵扣created_atdatetime注册时间房间表room要注意的字段房间类型、容纳人数、每时段价格、押金、状态、门锁编号、封面图。状态字段建议用枚举值管理available空闲、busy使用中、clean清洁中、maintain维护中。订单表order是整个系统最复杂的表字段名类型说明idint主键order_novarchar(32)业务订单号唯一user_idint下单用户room_idint房间session_idint场次statusvarchar(20)pending/paid/using/completed/cancelledpaid_amountdecimal(10,2)实付金额含押金rent_amountdecimal(10,2)租金deposit_amountdecimal(10,2)押金actual_amountdecimal(10,2)实际租金结算后更新refund_statustinyint0未退 1部分退 2已退完start_timedatetime预约开始时间end_timedatetime预约结束时间created_atdatetime创建时间3.2 订单状态机不同状态间的流转规则状态机是整个后端逻辑的核心我强烈建议在动手写代码前把状态流转图画清楚。不夸张地说外包项目里80%的 bug 都出在状态没有控制好导致用户在错误的节点执行了错误的操作。我的订单状态流转是这样的pending待支付用户提交订单后创建。此时锁定房间场次倒计时15分钟超时自动释放paid已支付微信回调通知后状态变为已支付。此时房间场次标记为被占用using使用中用户到店扫码开门后状态变为使用中completed已完成租期结束且结算完成状态变为已完成cancelled已取消用户主动取消或超时未支付状态变为已取消。注意用户主动取消订单后如果支付过要立即触发退款这里面有一个容易忽略的判断逻辑用户到店扫码开门时如果时间还没到预约时段开始时间要不要放行我的策略是提前30分钟内允许进场超过30分钟则提示“未到入场时间”。这样可以避免前面的用户还没走后面的人就已经刷卡进来了。3.3 管理端核心功能排班、活动、营收统计管理端我用的是独立的 Web 管理后台和小程序端共用一套后端 API。管理端需要实现的核心能力有第一房间场次管理。管理员可以直接在日历视图上看到每个房间每天哪些场次被订了、哪些还空着同时可以手动锁定某个场次比如设备维护。第二活动与优惠券配置。共享空间做促销一般就是两种首单立减、满三小时送一小时。优惠券我设计了两种类型满减券订单金额满 X 元减 Y 元和折扣券下单打 X 折。在用户提交订单或者结算时后端自动判断用户是否持有可用的券。第三营收统计。不能只看微信支付里的流水因为押金和退款会干扰你的判断。我的统计页面分三块实际营收租金总和、押金占用当前还在退款流程中的押金、订单量趋势图。有了这三个基础数据老板才能判断今天是赚了还是亏了。4. 完整实操核心代码从零到可用4.1 微信小程序项目初始化与目录结构用微信开发者工具创建项目时选择“JavaScript基础模板”不要选 TypeScript因为后面接一些硬件厂商的 SDK 时JS 的兼容性更好。推荐目录结构├── app.js // 小程序入口注册全局方法和登录逻辑 ├── app.json // 全局配置注册页面、tabBar、窗口样式 ├── app.wxss // 全局样式 ├── utils/ │ ├── request.js // 封装 wx.request统一处理 token 和错误码 │ └── util.js // 时间格式化、距离计算等工具函数 ├── components/ │ ├── room-card/ // 场地卡片组件 │ └── count-down/ // 倒计时组件 ├── pages/ │ ├── index/ // 首页场地列表 │ ├── room-detail/ // 场地详情 │ ├── booking/ // 下单页 │ ├── order-list/ // 订单列表 │ ├── order-detail/ // 订单详情开门、查看时长 │ ├── profile/ // 个人中心 │ └── wallet/ // 钱包余额、优惠券 ├── images/ // 静态图片 └── styles/ // 公共样式4.2 首页加载与场地列表实现首页的请求逻辑我封装在了request.js里统一管理 token 和错误处理。每次请求先在拦截器里判断 token 是否存在不存在就先登录再请求同时处理 token 过期后的刷新逻辑。// utils/request.js const BASE_URL https://api.yoursite.com // 生产环境域名 function request(options) { return new Promise((resolve, reject) { const token wx.getStorageSync(token) wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: token ? Bearer ${token} : }, success: (res) { if (res.data.code 401) { // token 过期重新登录 wx.removeStorageSync(token) app.login().then(() { request(options).then(resolve).catch(reject) }) return } if (res.data.code 0) { resolve(res.data) } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }) reject(res.data) } }, fail: (err) { wx.showToast({ title: 网络异常, icon: none }) reject(err) } }) }) }首页的数据请求在 onShow 里执行而不是 onLoad。原因是用户从详情页返回首页时场地状态可能已经变化需要在每次显示页面时刷新列表。4.3 预约下单与支付流程完整实现下单页面的核心逻辑我分成四步选场次、计算价格、提交订单、拉起支付。价格计算这部分要特别小心因为涉及押金和优惠。我的前端计算只是一个预估值真正的价格以后端计算为准前端只做展示。这样做的好处是避免前端改价格比如修改请求参数把0.01元改到0元。// pages/booking/booking.js 中提交订单的核心逻辑 async submitAndPay() { if (!this.data.selectedSession) { wx.showToast({ title: 请选择场次, icon: none }) return } wx.showLoading({ title: 提交中... }) try { // 1. 创建订单 const orderRes await createOrder({ roomId: this.data.room.id, sessionId: this.data.selectedSession.id, couponId: this.data.selectedCoupon?.id || null }) // 2. 获取微信支付参数 const payRes await getPaymentParams({ orderNo: orderRes.data.orderNo }) // 3. 拉起微信支付 wx.hideLoading() const payResult await wxPay(payRes.data) if (payResult.errMsg requestPayment:ok) { // 支付成功跳转订单详情 wx.redirectTo({ url: /pages/order-detail/order-detail?orderNo${orderRes.data.orderNo} }) } } catch (err) { wx.hideLoading() console.error(下单失败, err) } }有一个细节创建订单后如果用户没有立即支付而是在15分钟内关闭了页面想再次支付怎么办我加了“待支付订单”的入口用户可以回到订单列表找到状态为“待支付”的订单点击“去支付”按钮重新拉起支付。所以支付参数接口需要支持同一个订单重复获取支付参数。4.4 计时器与订单自动结算逻辑使用中的房间用户能看到已经用了多长时间、剩余多少时间。这个计时器是基于服务端时间而非本地时间避免用户改手机时间作弊。// pages/order-detail/order-detail.js 中的计时更新逻辑 startTimer() { const endTime new Date(this.data.order.endTime).getTime() this.timer setInterval(() { const now Date.now() const remain endTime - now if (remain 0) { clearInterval(this.timer) this.setData({ remainingText: 已结束请扫码结算, status: expired }) return } const hours Math.floor(remain / 3600000) const minutes Math.floor((remain % 3600000) / 60000) const seconds Math.floor((remain % 60000) / 1000) this.setData({ remainingText: ${hours}小时${minutes}分${seconds}秒 }) }, 1000) }但注意这个计时器只是展示用真正的结算还是靠后端定时任务。原因很简单用户一旦退出小程序前端计时器就停了如果依赖前端去触发结算那用户不开小程序就永远没法结算这显然不行。后端结算我用了 Node.js 的node-schedule库每天每分钟跑一次检查任务。为了避免同一订单被多次结算我在结算逻辑里加了乐观锁更新订单状态时条件必须是status using如果更新影响行数为0说明订单已经被结算过直接跳过。4.5 云函数部署与服务端校验后端我选的是小程序云开发环境因为它自带数据库和文件存储省去服务器运维的成本。但要注意云函数有冷启动的问题高并发时可能出现几秒延迟。我的优化方案是对核心接口登录、下单设置合理的云函数超时时间不要用默认3秒避免在云函数里做大量同步I/O操作比如查询多个表时使用Promise.all并行查询配置云函数的实例并发数防止同时大量触发导致超时服务端校验最关键的一条所有价格和金额计算必须以服务端为准。前端传过来的amount字段不能直接相信后端要根据房间价格、场次时长、数据库里优惠券规则重新计算一遍。否则只要有人抓包改参数就能0元下单。5. 实战中踩过的坑与排查技巧5.1 登录态过期与静默登录用户卡在支付页的解决过程上线后第一个问题就是用户打开小程序看到场地列表正常但点“立即预约”时提示“请先登录”。排查发现是因为我的 token 有效期只设了24小时用户隔天打开小程序时token 已经过期而首页接口没有做登录校验所以用户能浏览但下单时发现需要重新登录体验断裂。解决办法是在request.js的响应拦截器里统一处理 401 状态自动调用登录接口重新获取 token然后重新发请求。这样用户完全感知不到登录过期整个过程静默完成。第二个坑是微信的getUserProfile接口调整。2022年之后微信要求必须用户主动点击才能唤起头像昵称填写不能直接调wx.getUserProfile。而且这个接口对某些版本的基础库已经失效。我的最终方案是完全放弃获取用户头像昵称使用微信默认的灰色头像和“微信用户”昵称只在用户首次下单时弹一个手机号授权用于接收订单通知。5.2 支付回调并发问题重复发货的灾难微信支付回调不像普通接口它可能会重复发送多次同样的通知直到你返回成功。如果你在回调里直接执行“把订单状态改为已支付”那么第二次回调时订单已经是已支付状态可能引发重复操作比如重复释放房间场次。我的解决方案是在回调处理逻辑里加一个幂等校验// 支付回调处理 async handlePayCallback(notifyData) { const { orderNo, transactionId } notifyData // 先查订单是否已处理过这个 transactionId const existing await PaymentLog.findOne({ where: { transactionId } }) if (existing) { // 已处理过直接返回成功 return { code: SUCCESS } } // 事务里原子更新订单状态 await sequelize.transaction(async (t) { await PaymentLog.create({ orderNo, transactionId, amount: notifyData.totalFee }, { transaction: t }) await Order.update({ status: paid }, { where: { orderNo, status: pending }, transaction: t }) }) return { code: SUCCESS } }这段代码的核心思路是利用where: { orderNo, status: pending }这个条件只有当订单处于 pending 状态时才更新为 paid如果更新行数为0说明订单已经被其他回调处理过了直接忽略。5.3 定位授权与电控锁联动的深坑开门校验需要wx.getLocation但微信对定位授权管理非常严格。用户如果首次点了“拒绝”小程序再次调用wx.getLocation不会重新弹窗而是直接走 fail 回调。这时只能在页面上给用户解释“需要定位权限才能开门请前往设置页开启”。但这里还有个更深的问题即使用户授权了定位因为小程序端拿到的经纬度是通过 GPS 或者 Wi-Fi 定位估算的精度在几十米到几百米之间浮动。如果你把敞开距离阈值设得太小比如50米用户站在店门口扫码都有可能出现定位失败。我最后的妥协方案是距离阈值放宽到500米同时在开门页增加一个“点击开门”的二次确认弹窗提示“请确认你已到达门店门口”。用户主动点确认后再请求开门接口。这个方案上线后开门失败率从12%降到了0.5%以下。5.4 小程序分包与包体积问题解决代码提交失败的尴尬微信小程序主包限制是2MB如果你用了太多组件库或者图片没压缩很容易超限导致提交失败。我第一次打包时光是一个 UI 组件库就占了800KB加上页面代码和图片直接超了。解决办法是使用分包。我的分包方案是主包只放首页、预约页和公共组件订单列表、个人中心、钱包等低频页面放到分包里。{ subpackages: [ { root: pages/order, pages: [ pages/order/order-list/order-list, pages/order/order-detail/order-detail ] }, { root: pages/user, pages: [ pages/user/profile/profile, pages/user/wallet/wallet, pages/user/coupon/coupon ] } ], preloadRule: { pages/index/index: { network: all, packages: [pages/order] } } }分包之后主包体积降到了1.3MB还加了preloadRule预加载分包用户从首页进入订单页时几乎不需要等待。如果你也遇到“包体积超过2MB”的提交失败优先检查images目录大图全部压缩成 WebP另外把不必要的miniprogram_npm依赖清理干净。5.5 微信开发者工具和真机的差异白屏问题的元凶我在开发时遇到一个诡异的问题开发者工具里一切正常一到真机预览就白屏。排查了整整一个下午最后发现是app.json里window配置的navigationStyle设置成了custom自定义导航栏的代码在真机上因为某些基础库版本的兼容问题没渲染出来。类似的坑还有开发者工具里wx.getSystemInfoSync()返回的statusBarHeight是真机上的两倍因为开发者工具模拟器的屏幕密度和真机不一致导致自定义导航栏在真机上高度不对。我的建议是凡是涉及状态栏高度、安全区适配的代码一律用wx.getWindowInfo()配合safeArea字段计算而不是写死数值。真机白屏还有一个常见原因是 ES6 语法兼容问题。如果你用了可选链?.或空值合并??开发者工具默认转换成 ES5 没问题但某些旧版基础库会报语法错误。我的做法是在project.config.json里设置es6: true和enhance: true同时在真机调试时选择最新基础库。5.6 优惠券并发发放问题与超卖防护最后一个要提醒的是优惠券的并发扣减。我做预热活动时发出去100张“满100减30”的券结果后台显示领了120张。排查原因用户点击领券时前端同时发了两个请求后端两个请求同时检查“券剩余数量 0”都通过了导致超发。解决方式是数据库层面的原子扣减而不是先查后改UPDATE coupon_batch SET remain_count remain_count - 1 WHERE id ? AND remain_count 0如果影响行数等于1说明扣减成功才给用户发券。这比先 SELECT 再 UPDATE 的方式安全得多也不需要引入 Redis 分布式锁够用。我做过几套不同行业的小程序共享空间这一套遇到的坑尤其多因为它是线上交易 线下硬件 资金流转三个维度的结合体。单独开发一个商城小程序你只需要关心线上逻辑单独开发一个智能门锁系统你只需要关心硬件协议。但共享棋牌室小程序把这两个都拉到一起了还要加上押金、退款、超时结算这些容易出纠纷的资金逻辑复杂度一下子就上来了。我个人在实际操作中最深的体会是优先把订单状态机设计正确再谈界面和体验。状态机稳了支付、退款、开门、结算都是状态流转的副产品状态机乱了后期加什么功能都是在补窟窿。如果你准备自己动手写这套系统从最简单的单店、单房间版本开始先把“预约-支付-开门-结算-退款”这条链路跑通再去加活动、优惠券、多门店。希望这篇内容能帮你少走一些弯路有任何细节问题可以在评论区交流我会根据实际项目经验尽量回复。本文还有配套的精品资源点击获取
返回列表