ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

餐饮预订小程序开发实战:Python后端状态机与uniapp跨端实现

餐饮预订小程序开发实战:Python后端状态机与uniapp跨端实现 做餐饮SaaS或者小程序外包的朋友对这类需求应该都不陌生一家做早茶和下午茶的茶楼每天电话订座、手写登记、口头转达高峰期漏单、错桌、超卖是家常便饭。老板想上一个预订系统又不想像连锁酒店那样搞太重最好顾客扫码就能看到今天还有没有位选中时段和包间提交一条预订记录前台能收到提醒超时没确认还能自动释放座位。这个项目说白了就是一套预订状态机 跨端展示的小型业务系统。我做完之后的体感是后端用Python写业务和状态管理非常顺手前端用uniapp一次编译出微信小程序后面如果想加App端或者H5端做商家版都留了口子。这篇我就把这个项目的完整拆解、核心实现、踩坑过程都写出来给正在做小程序预订类项目的朋友一个可直接参考的模板。1. 需求拆解与方案选型早茶下午茶预订到底要做什么1.1 业务场景的核心需求早茶和下午茶在餐饮行业里是典型的高峰期高翻台场景。早茶集中在07:30到11:00下午茶集中在14:00到17:00客人的消费特点是时间敏感、桌型敏感、包房需求高、等位容忍度低。这就导致预订系统的核心需求跟普通正餐预约有区别。我先跟茶楼老板过了一遍日常运营流程把需求收敛成四个核心点余座可见顾客最关心的是现在订不订得到所以首页和预订页要把每个时段的余座数、余桌数展示出来这直接决定顾客是下单还是流失。桌台和包房分级管理大厅二人桌、四人桌靠窗卡座包房这些桌型在预订时是强区分的。包房需要提前预占大厅普通桌可以弱化锁定逻辑到店后再分配这个差异要体现在数据设计里。时段模板可配置周末早茶可能提前到07:00开市下午茶在节假日会加场所以营业时段不能写死在代码里必须做成可配置的时段模板。订单状态闭环顾客提交预订后茶楼前台要能确认或取消顾客要能看到订单处于什么状态。预订不是提交完就结束了状态机是这个系统的灵魂。记住一个原则餐饮预订系统不是电商下单系统支付-发货-完成这种线性流程根本走不通它更像一个带时间约束的资源预约系统核心是桌台资源在某个时间片内的占用与释放。1.2 技术选型为什么是Python uniapp 微信小程序选型是我在这个项目里最先定的因为后端语言和前端框架直接决定了后面所有开发节奏。后端用Python理由很实际第一这类预订系统的业务复杂度中等不需要高并发架构Python足够第二团队日常用Python写数据处理脚本Flask/Django的经验存量直接复用第三餐饮后期大概率要做经营数据统计、菜品销量分析Python的数据生态往后接pandas、定时报表都非常方便等于提前铺路。我这次用的是Flask轻量、灵活配合SQLAlchemy和JWT半天时间就把接口骨架搭出来了。如果用DjangoAdmin后台对商家管理端来说倒是很有吸引力但项目节奏偏快、接口以小程序调用为主Flask更顺手。前端用uniapp核心原因是一套代码多端复用。微信小程序目前是落地首选但茶楼老板明确说以后想让顾客用H5页面在自己公众号里订还想给店员做一个简易安卓App当管理端。如果用原生小程序写这三端就是三套代码维护成本直接爆炸。uniapp基于Vue语法写完一套编译到微信小程序、App、H5我实际测试下来90%的页面代码都能复用只有支付、订阅消息这些涉及原生能力的地方需要写条件编译。微信小程序作为C端载体这没什么好纠结的茶楼的客群是本地食客扫码即用、不用装App、微信订阅消息天然触达获客成本最低。为什么不直接用其他方案我也简单对比过原生微信小程序写前端踩坑少但多端复用没戏Java/Node后端都能做但团队没有对应经验存量为了一个中型项目引进新语言不划算。总体上这套组合是团队能力、业务复杂度、扩展需求三者之间的平衡点。1.3 整体架构与模块划分系统按端 模块划分总共四部分微信小程序端uniapp顾客入口包含首页门店与余座展示、预订操作、订单列表、订单详情、个人中心。商家管理端后台页面或同款uniapp处理预订确认/取消维护桌台、时段模板查看当日预订汇总。这部分可以用uniapp的H5编译出一个商家页也可以单独做web后台我这次直接用了Flask提供一组管理接口商家管理端复用同一套uniapp代码编译为H5并增加角色权限控制。Python API服务Flask统一提供业务接口负责用户鉴权、预订状态流转、桌台资源锁、订阅消息推送。数据库MySQL核心数据落库用表结构支撑状态机和事务。部署结构上前端小程序走微信平台后端API部署到一台云服务器域名配置SSL证书后在小程序后台配好request合法域名。这一步很多人会漏小程序上线前必须确认request域名必须为HTTPS且已备案否则开发工具里调试正常真机和体验版全都请求失败。2. 后端设计与数据模型先把状态机想清楚2.1 核心表结构与字段预订类项目的数据库设计核心不在订单表本身而在资源和时间的关系上。我设计了六张核心表用户表user存储微信用户信息。id, openid, nickname, avatar_url, phone, create_timeopenid是微信生态里的用户唯一标识由微信登录接口返回后端要建立唯一索引。手机号一般不会在登录时强制获取等用户下单时再填这样合规压力小用户流失也少。门店表store支持一家茶楼多分店的场景字段包括门店名称、地址、营业时间、电话、经纬度。即便现在只有一家店我也建议预留这个表因为餐饮行业扩张速度很快后面增加分店时不用改表结构。桌台表table_info这是预订系统的资源核心。id, store_id, table_code, table_type, capacity, status, create_timetable_type区分大厅桌、卡座、包房capacity是座位数status是当前状态空闲/占用/锁定。我特意保留了一个锁定状态因为包房预订的时间跨度长需要提前锁定而大厅桌通常是到店分配锁定时间短。时段模板表time_slot_template定义营业批次。id, store_id, slot_name, start_time, end_time, sort_order早茶是一个slot下午茶是另一个slot如果周末加开夜茶时段直接在后台插入一条记录就行。时间不要存成datetime用time类型日期维度由预订记录自己的date字段承担。预订订单表reservation_order整个系统的主表。id, order_no, user_id, store_id, table_id, slot_id, reserve_date, party_size, contact_name, contact_phone, remark, status, expire_time, confirm_time, create_time, cancel_time注意字段设计意图order_no用时间戳随机数生成展示给顾客用reserve_date和slot_id联合决定了时间片expire_time是桌台锁定的过期时间用于超时自动释放status字段就是状态机的载体。2.2 订单状态机设计餐饮预订最怕的是状态混乱导致的超卖和漏单。我定义的状态集合如下状态含义触发事件PENDING待确认用户提交预订成功CONFIRMED已确认商家端确认SEATED已入座用户到店扫码/前台操作COMPLETED已结束用餐结束离店CANCELLED已取消用户主动取消EXPIRED已过期超过确认时限自动释放状态流转的核心规则只有四条PENDING 只能流向 CONFIRMED、CANCELLED、EXPIRED不能直接跳到 SEATED防止商家未确认就先入座。只有 CONFIRMED 才能变 SEATED保证座位分配是受控的。用户只允许对 PENDING 和 CONFIRMED 发起取消且 PENDING 态取消不产生任何补偿逻辑CONFIRMED 态取消需要释放桌台同时通知商家。EXPIRED 是定时任务扫出来的终态进入后桌台资源自动解锁同时把PENDING的预留标记清掉。手动在业务代码里写if-else做状态流转迟早会漏我用了一个状态机字典来集中管理STATUS_TRANSITIONS { PENDING: {CONFIRMED, CANCELLED, EXPIRED}, CONFIRMED: {SEATED, CANCELLED, COMPLETED}, SEATED: {COMPLETED}, CANCELLED: set(), EXPIRED: set(), COMPLETED: set(), } def transition_order(order, target_status): allowed STATUS_TRANSITIONS[order.status] if target_status not in allowed: raise ValueError(f非法状态流转: {order.status} - {target_status}) order.status target_status这样改状态前先查字典非法流转直接报错后端的健壮性一下子就有了。实测中这个设计帮我拦住了很多边界情况的bug尤其是有一次商家后台连点两次确认状态机直接拒绝避免了重复确认的脏数据。2.3 并发与桌台资源冲突处理预订系统上线后遇到的最大技术问题就是同一张桌同一时刻被两个人同时预订。解决思路很简单用数据库事务 原子更新锁住资源而不是在Python代码层面先查后改。# 原子锁定桌台affected_rows 1 表示抢占成功 affected_rows db.session.execute( text( UPDATE table_info SET status LOCKED, locked_until :expire WHERE id :table_id AND status AVAILABLE AND (locked_until IS NULL OR locked_until :now) ), {table_id: table_id, expire: expire_time, now: now} ).rowcount if affected_rows ! 1: return jsonify({code: 400, msg: 手速慢了这个时间段座位已被预订})这段SQL的核心是status AVAILABLE这个条件——用一条UPDATE语句同时完成判断空闲和改为锁定数据库行锁天然保证并发安全两个人同时提交也只有一个人能成功。实测在高并发测试100个并发请求抢同一张桌下只有1个成功其余全部返回已被预订没有出现超卖。另外锁必须带过期时间。用户提交预订但没付款或没被确认时桌台不能永远锁着。PENDING状态默认锁15分钟超时后台定时任务把状态改为EXPIRED并释放桌台。这里的定时任务我用的是APScheduler的cron触发每5分钟扫一次简单可靠。2.4 API设计与接口清单后端接口按资源维度划分加一个统一前缀/api/v1。核心接口如下接口路径方法说明/api/v1/auth/loginPOST微信code换登录token/api/v1/store/listGET门店列表/api/v1/table/availableGET按日期时段查询可用桌台/api/v1/slot/listGET时段模板列表/api/v1/reservation/createPOST提交预订/api/v1/reservation/cancelPOST取消预订/api/v1/reservation/listGET我的订单列表/api/v1/reservation/detailGET订单详情/api/v1/admin/reservation/confirmPOST商家确认/api/v1/admin/reservation/rejectPOST商家拒绝鉴权方面C端接口统一用JWT方式微信登录后后端返回一个带用户信息和过期时间的token小程序每次请求放在header的Authorization字段里。商家管理端接口我在JWT基础上增加了一个role字段做权限区分只有管理员角色才能调admin接口一套代码端到端控制。3. uniapp端搭建与页面实现从创建项目到能下单3.1 环境准备与项目创建前端我用HBuilderX创建了uniapp项目创建时直接勾选启用TypeScript项目模板选uni-app默认模板。微信小程序端开发前需要准备三样东西注册好的小程序AppID、微信开发者工具、HBuilderX。manifest.json里的配置是第一个容易翻车的地方。需要修改的点小程序AppID填写自己注册的不是测试号mp-weixin节点下设置usingComponents: true如果要用订阅消息和定位需要在permission里声明对应字段基础库最低版本建议设到3.0以上太低有些组件API用不了。组件库我引入了uview-plus这个组件库对uniapp的适配性很好尤其是它的u-calendar日历组件和u-datetime-picker时间选择器预订页面直接复用省了很多事。导入方式是从插件市场一键导入HBuilderX项目注意如果项目启用了TS需要确认插件市场的组件包是TS兼容版本。3.2 页面结构与底部导航设计小程序页面结构规划如下pages/ ├─ index/index # 首页门店列表 余座概览 ├─ booking/index # 预订页选店、选日期、选时段、选桌台、填信息 ├─ orders/index # 订单列表展示我的预订记录 ├─ orders/detail # 订单详情状态流展示 取消操作 └─ mine/index # 我的个人信息、关于、客服电话tabBar我配置了三个首页、订单、我的。预订页作为业务核心不放进tabBar而是从首页的门店卡片点击进入这样操作路径短视觉上也突出重点。pages.json里tabBar的icon需要准备81px规格的png图标未选中和选中两种状态。这里建议直接用纯文本tabBar或者简单图标不要为了好看上传大图否则影响包体积。3.3 核心页面的实现细节首页的逻辑是门店列表加今日余座标签。小程序端调用/store/list接口拿门店数据再往/table/available传当天日期和早茶/下午茶两个默认时段拿到余座数渲染到卡片上。这里要注意余座数不是实时刷新的小程序端做了每60秒轮询一次用户下拉刷新也会触发重新请求。对茶楼这种低频预订场景这个刷新频率完全够用不用上WebSocket。预订页是核心拆成四步操作第一步选日期。用uview-plus的日历组件限制最早可选今天、最晚可选未来7天。这里有个逻辑坑超过7天的预订茶楼是不受理的因为供应商备货和人员排班都有限我在后端也做了同样的7天校验前端后端双重拦截。第二步选时段。时段数据从/slot/list拉取。这里我处理了一个细节过期时段的禁用逻辑。如果现在是下午16:00下午茶时段14:00-17:00虽然还没结束但已经不适合接受新预订了我配置时段的时候额外存了一个latest_book_time字段用来控制前端时段可点选截止时间。没有这个字段会出现顾客16:30提交下午茶预订结果茶楼17:00打烊的尴尬情况。第三步选桌台。这是预订页交互最重的部分。我把桌台数据可视化成一排卡片每张卡片显示桌号、类型、容纳人数不可订的置灰并加上已满标签。const buildTableCards (tables) { return tables.map(t ({ ...t, disabled: t.status ! AVAILABLE || t.capacity partySize, capacityText: ${t.capacity}人, typeText: t.table_type ROOM ? 包房 : (t.table_type BOOTH ? 卡座 : 大厅桌) })) }注意disabled判断用了两个条件桌台状态不可用、或者桌台容纳人数小于当前选择人数。容量不足的桌台直接置灰避免用户选完才发现坐不下。第四步填信息。联系人姓名、手机号、备注手机号做正则校验。提交前一次性调用uni.requestSubscribeMessage申请订阅消息授权这样后面商家确认时才能发送模板消息通知。这里有一个UX细节订阅授权最好放在用户提交预订动作的同时触发单独提前弹授权框通过率会低很多。3.4 与后端联调的关键配置联调阶段最容易出问题的是域名和代理配置。我的做法是在项目根目录新建config.js集中管理环境变量const ENV development const API_BASE { development: https://your-test-domain.com/api/v1, production: https://your-prod-domain.com/api/v1 }[ENV]然后把uni.request封装成公共请求函数统一处理baseURL拼接、token注入、错误提示、loading状态const request (options) { return new Promise((resolve, reject) { uni.request({ url: API_BASE options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: Bearer ${uni.getStorageSync(token)} }, success: (res) { if (res.data.code 0) { resolve(res.data.data) } else { uni.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail: (err) { uni.showToast({ title: 网络异常请重试, icon: none }) reject(err) } }) }) }微信开发者工具里联调时要确认两点第一在详情 - 本地设置里勾选不校验合法域名开发期跳过域名校验第二测试环境域名必须配置SSL证书否则真机预览会挂。上线前再把ENV切到production删除本地设置里的勾选。4. 预订流程与订单状态的完整闭环4.1 用户端下单全流程用户从首页进入预订页完整操作链路如下选择门店 - 选择日期 - 选择时段 - 查看实时余座 - 选择桌台/包房 - 填写联系信息 - 提交预订 - 授权订阅消息 - 跳转订单列表查看状态提交预订这个动作对应的前端代码const submitReservation async () { if (!formData.contactName || !validPhone(formData.contactPhone)) { uni.showToast({ title: 请填写正确的联系人信息, icon: none }) return } uni.requestSubscribeMessage({ tmplIds: [TEMPLATE_ID], success: () uni.showToast({ title: 订阅成功, icon: none }), fail: () console.log(用户拒绝订阅) }) const res await request({ url: /reservation/create, method: POST, data: { storeId, tableId: selectedTable.id, slotId, reserveDate: dateStr, partySize, contactName, contactPhone, remark } }) if (res.orderId) { uni.redirectTo({ url: /pages/orders/detail?id${res.orderId} }) } }后端create接口的执行逻辑顺序是校验日期在可订范围内 - 校验时段可订 -原子锁定桌台- 创建PENDING订单 - 返回订单号。整个过程包裹在一个数据库事务里任一步失败整体回滚。4.2 商家确认与订阅消息通知商家端我在后期用同一套uniapp源码编译成了H5页面店员登录后进入一个简洁的今日待确认列表每条预订右侧有确认/拒绝按钮。确认操作调/admin/reservation/confirm接口后端把订单状态从PENDING改为CONFIRMED同时调用微信订阅消息接口通知用户。服务端发订阅消息是很多人第一次做会比较懵的环节。先获取小程序全局access_tokendef get_access_token(appid, secret): url fhttps://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credentialappid{appid}secret{secret} resp requests.get(url).json() return resp[access_token]然后调用subscribeMessage.send接口传入用户openid、模板ID、跳转页面和模板数据def send_subscribe_message(openid, template_id, page, data): access_token get_access_token(APPID, SECRET) url fhttps://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token{access_token} payload { touser: openid, template_id: template_id, page: page, data: { thing1: {value: 您的预订已确认}, time2: {value: confirm_time}, thing3: {value: table_name} } } requests.post(url, jsonpayload, timeout5)这里有个关键限制要提前跟需求方沟通微信订阅消息是一次性订阅用户授权一次只能发一条消息如果商家确认后又发生取消、改期第二条是发不出去的。我给的方案是下单时同时申请两条订阅模板预订确认、预订取消诱导用户一次性授权两个模板被拒绝订阅的用户订单状态变化只靠主动刷新订单列表获取首页顶部加一个待处理订单数的角标提醒重要订单包房、多人宴请在确认时同时触发送短信通知保证触达率。实际运营中茶楼老板自己基本都会给VIP客户打电话确认订阅消息对散客来说已经够用。4.3 状态流转与防呆设计状态不光是后端的事前端展示也要同步配合。订单详情页我用了一个时间轴组件展示状态流每个状态显示对应的时间戳顾客能清楚看到自己的预订走到了哪一步。前端订单状态标签的映射后端状态前端文案标签颜色PENDING待确认橙色CONFIRMED已确认按时到店绿色SEATED已入座蓝色COMPLETED已结束灰色CANCELLED已取消灰色EXPIRED已过期灰色后端定期任务扫PENDING超时单时除了改状态外还必须做两张表的数据同步一是把桌台状态从LOCKED改回AVAILABLE二是清掉桌台的locked_until字段。否则会出现顾客看座位空着但系统层面还在锁定的诡异情况。另一个防呆设计是订单取消时的桌台释放。用户取消CONFIRMED订单后商家端当日预订列表里桌台会少一个但如果商家已经提前把桌台分配出去了这个释放动作要谨慎处理。我的方案是取消操作不会自动改桌台状态而是把桌台状态置为待释放RELEASING由商家端决定是重新开放预订还是保留给到店客户。这样比较符合真实运营逻辑避免线上取消但线下已经安排给其他客人的冲突。5. 实际开发中那些绕不开的坑踩坑实录5.1 小程序包体积超限2MB的紧箍咒这个项目开发到中后期HBuilderX编译微信小程序时突然报错source size 2612kb exceed max limit 2mb。微信小程序主包限制2MB而我的代码和组件库已经到了2.6MB。解决方案是分包加载。把不属于主流程的页面拆到subPackages里例如订单详情页、个人中心页、商家页。配置pages.json{ pages: [ pages/index/index, pages/booking/index, pages/orders/index ], subPackages: [ { root: pages/ordersDetail, pages: [ { path: index, style: { navigationBarTitleText: 订单详情 } } ] }, { root: pages/mine, pages: [ { path: index, style: { navigationBarTitleText: 我的 } } ] } ] }分包后还要注意tabBar页面必须在主包里所以首页、订单列表、我的这三个入口页留主包分包之间的跳转使用uni.navigateTo的url时要带分包路径前缀/pages/ordersDetail/indexuview-plus这种大组件库建议按需引入不要用全量模式的uni_modules/uview-plus/index否则光是组件库就占掉几百KB。我把预订流程相关的页面放主包其余全拆出去后主包体积从2.6MB降到1.4MB编译通过。图片资源也全部压缩了一轮能用云存储URL的就不放本地静态资源这对后续迭代很重要。5.2 顶部导航栏高度适配到吐血的细节微信小程序的自定义导航栏是前端适配里最琐碎的坑茶楼老板要求预订页头部用品牌色大标题的沉浸式设计所以不能直接用默认导航栏。自定义导航栏需要动态计算状态栏高度和胶囊按钮位置我封装了一个工具函数export const getNavBarInfo () { const systemInfo uni.getSystemInfoSync() const menuButton uni.getMenuButtonBoundingClientRect() const statusBarHeight systemInfo.statusBarHeight || 20 const navBarHeight (menuButton.top - statusBarHeight) * 2 menuButton.height return { statusBarHeight, navBarHeight, menuButtonRight: systemInfo.windowWidth - menuButton.left, menuButtonTop: menuButton.top } }注意这个uni.getMenuButtonBoundingClientRect()是胶囊按钮的坐标信息api不同机型的返回值差异很大必须在页面onLoad时实时获取而不是写死。设计稿通常按375px宽度做rpx在小程序里会自动换算但自定义导航栏高度如果写死rpx在iPhone 14 Pro Max这种长屏手机上就会出现按钮错位。另外我在页面样式中统一加了safe-area的处理底部用env(safe-area-inset-bottom)适配全面屏避免预订页提交按钮被底部小黑条遮挡。5.3 调试过程中遇到的网络与日志问题联调阶段微信开发者工具经常出现请求发出去但控制台不打印任何日志的情况。这不是代码问题是工具的网络面板有时候不刷新。我的排查习惯分三步第一步在uni.request的success/fail回调里加console.log确认请求是否发出第二步看工具Network面板的请求状态是pending还是failed第三步打开微信开发者工具的vConsole在真机上跑一遍看Console面板。抓包调试我用的是Charles配合本地代理主要用来查看小程序发到后端的完整请求体和响应体。Mac上配置好Charles的SSL代理后手机WiFi代理指向开发机IP:8888安装信任证书就能在Charles里看到codelogin、reservation/create这些接口的明文报文。这里要提醒一个我的真实经历Charles抓包时如果有域名自动拦截配置小程序requests始终连不上测试服务器。我当时排查了很久才发现是Charles的Access Control Settings里把测试域名给过滤掉了在Proxy - Access Control Settings里加上测试域名就恢复正常。另外真机调试时手机和电脑必须在同一WiFi下而且手机代理指向的IP必须是电脑的局域网IP不是公网IP。还有一类高频报错是小程序端的10002错误。这个一般是wx.login的code过期或者appid密钥不匹配导致的。排查顺序确认manifest里的小程序AppID和微信公众平台一致确认后端请求微信接口用的appsecret没有换过。特别注意如果前端调试用的是测试号后端appid也必须是同一个测试号的混合使用必报10002。5.4 页面交互细节单选、离开监听与状态更新预订页桌台选择我用的是radio-group组件包桌台卡片每个卡片的点击区足够大避免小按钮误触。这里有个交互优化选择了人数后桌台卡片会重新渲染一次容量小于人数的卡片即时置灰反馈非常直观。小程序监听用户离开我用来处理一个场景用户在预订页填了一半退出或提交订单后立刻切到微信聊天如果订单一直停在PENDING状态前端显示待确认没问题但用户如果再也不回来商家那边会一直积压未处理单。我的做法是onHide时记录离开时间onShow时如果发现当前有超过15分钟未处理的PENDING订单自动刷新一次订单状态并提示您的预订已超时请重新选择时段。这样把残留在PENDING状态的脏数据引导用户主动清理减轻商家负担。5.5 上线前的收尾清单项目交付前我整理了一份必查清单这些项全是实际踩过的坑小程序后台配置request合法域名、uploadFile合法域名缺一个都会导致线上环境请求失败发布体验版前用真机预览完整走一遍预订流程开发者工具里正常不代表真机正常检查首页余座轮询是否在页面onHide时被关闭不然用户退出后后台还在跑定时器会触发小程序性能告警后端定时任务跑一遍确认EXPIRED状态订单和桌台释放的数据正确性测试多人同时预订同一张桌验证并发锁是否生效商家端至少模拟一次确认订单 - 发送订阅消息的完整链路确认模板消息能正常收到。写在最后的一点经验这个项目整体做下来最大的体会是餐饮预订系统的核心其实不在代码本身而在于业务状态的边界是否想清楚。一个顾客提交预订茶楼要考虑的不只是座位锁住没有还有商家有没有时间确认、用户会不会爽约、桌台资源什么时候释放。这些边界条件理清了后端用Python写状态机加原子更新前端用uniapp做交互展示整个开发流程会非常顺。如果再让我重做一遍我会在第一天就把桌台锁的过期时间和商家确认时限的配置做进后台而不是写死在代码里。茶楼在不同季节的响应速度差异很大旺季前台忙不过来15分钟确认时限太紧淡季又太松。这类业务参数放到配置表里后续运营调优只需要在后台改数字不需要麻烦开发重新发版。这个系统后续还有不少扩展空间扫码入座把SEATED状态的确认动作交给顾客自助完成、在线支付锁位、排队取号接住高峰期溢出客流、多门店区域化的桌台共享调度技术架构都已经留了口子。真做到那一步这套Python后端加uniapp前端的底子是完全扛得住的。希望这篇拆解能帮到正在做预订类小程序的朋友少踩几个坑。
返回列表