ARTICLE DETAIL

资讯详情

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

Flask+uniapp微信小程序餐饮预约系统开发实战:早茶下午茶预订全解析

Flask+uniapp微信小程序餐饮预约系统开发实战:早茶下午茶预订全解析 早茶要排位一个小时下午茶每天只出二十份点心这种场景很多餐饮老板都头疼。我去年帮本地一家粤式茶楼做了一套预定系统后端用Python前端用uniapp统一编译到微信小程序。整套系统从需求梳理到上线跑了大半年踩了不少坑也沉淀出一些能直接抄作业的方案。这篇文章就把这套早茶下午茶预定系统的设计思路、核心技术链路、微信小程序端的实现细节和常见的打包调试问题完整拆一遍给正在做类似餐饮预约项目、或者在准备毕业设计的同学一个参考。项目本身不复杂但涉及的点很杂用户端要有菜品浏览、时段预约、点单提交、订单跟踪商家端要有桌台管理、时段库存、订单确认。用户拿起手机打开小程序就能预约商家在后台看到一张清晰的排班表。我把这套系统按模块拆开说说每一步为什么这么做、数据怎么设计、接口怎么定义再把我实际遇到的那些坑一个个列出来。1. 项目全景拆解这套预定系统到底在做什么1.1 业务需求与功能模块划分早茶下午茶预定系统和普通的餐厅外卖点餐有一个本质区别核心不是“配送”而是“到店时段”。所以整个系统的数据模型必须围绕“时段-桌台-人数-菜品”的关联来建而不是简单搞一个购物车。我最初和茶楼老板聊需求时他提了三个核心痛点第一周末早上九点到十一点散客排队严重老客不愿意等第二下午茶的限量点心每天只做固定的量卖完就没了客户经常白跑第三后厨反馈下单和实际到店人数对不上备料不准。基于这些问题系统拆成四个模块。用户端的小程序负责展示菜单、选择到店日期和时段、填写人数、选桌台类型、提交预订商家管理端负责维护菜品、设置每个时段的桌台数量和点心配额、查看和确认订单服务层负责时段库存的实时扣减、订单状态的流转、消息提醒基础层负责用户登录、权限控制、数据存储。这里有一个关键的设计决策把“预约”和“点单”分开。用户先预约时段提交后商家会预留桌台到店前半小时用户可以提前点菜也可以到店后扫码现场点。这样后厨可以根据预约单做初步备料再根据提前点单做精准备餐。老板觉得这个逻辑比较合理不会出现预约了但人没到导致食材浪费的情况。1.2 技术选型背后的几个考量技术栈锁定为Python uniapp 微信小程序其实不是拍脑袋选的。后端用Python我推荐用Flask而不是Django。Flask轻、灵活一个餐饮预定系统的接口量不大如果用Django自带Admin、ORM、中间件等一大套东西反而把简单项目搞得沉重。我实际用的Flask SQLAlchemy MySQLRedis用来做时段库存的原子扣减。小红书上很多教程喜欢推荐Django但这类小程序项目最怕的是后端太重Flask能把业务逻辑写得更清楚尤其适合接口权限、状态机这类需要逐行可控的场景。前端选uniapp而非微信原生小程序最直接的原因是后续要扩展。uniapp一套代码可以编译成微信小程序、支付宝小程序、H5和App茶楼老板说以后可能要出App或者网页端预约用uniapp就不用从零再写一遍。虽然uniapp在微信小程序上会有一些兼容问题但总体收益大于成本。利用HBuilderX创建项目非常顺手直接在插件市场导入uview-plus做UI组件库列表加载、表单、弹窗这些常用交互基本不用自己造轮子。这里有个容易忽略的点微信小程序官方对个人主体和企业主体开放的能力不同。我们这个项目需要用户手机号登录和消息订阅所以必须用企业主体的小程序账号。如果你的毕业设计只是做功能演示用测试号就够但如果是真实商用记得提前准备营业执照。2. 后端核心实现从数据库表到预约状态机2.1 数据库表结构设计不要用一个订单表走天下很多新手做这类系统习惯把预约信息、菜品信息、桌台信息全塞进一张大表里后面改需求时想死的心都有。我按业务边界拆了六张核心表。用户表保存微信小程序的openid、昵称、手机号、会员等级菜品表保存菜品名称、分类早茶/下午茶、价格、图片、每日供应量桌台表保存桌台类型大厅桌、包间、卡座、可容纳人数、每个时段的最大订单数预约单表是主表记录用户ID、日期、时段、桌台类型、人数、状态预约明细表记录预约单里包含的菜品和数量时段表是配置表定义早茶时段、下午茶时段的具体起止时间。时段库存的实现我单独说明。比如某个大厅桌在周六上午十点这个时段一共能接五单这个数字不能直接存在预约单表里否则并发时容易超卖。我用Redis的“时段桌台类型”作为key值保存剩余可接单数量用户提交预约时通过decr操作原子扣减如果返回负值就说明库存不足。数据库里的预约单只记录最终结果不参与实时库存计算。这个思路借鉴了电商秒杀的做法在餐厅预约场景里同样适用。2.2 菜品、桌台与时段的关联逻辑早茶下午茶有很强的时效性同一道菜可能只在下午茶时段供应。菜品表里加了“供应时段”字段用逗号分隔的时段ID表示比如“1,3”表示早茶和下午茶时段都供应。前端在用户选择时段后菜单列表会自动过滤掉当前时段不供应的菜品这样就避免了用户点了菜但后厨根本不做的情况。桌台数量和时段也要挂钩。茶楼大厅有十张四人桌周末早茶时段翻台两次实际上午时段能接二十单。这个数字不是在桌台表里写死的而是在时段配置表里维护一个“最大可预订桌台数”。老板说周末要加桌直接在管理后台把数字调一下就行不用改代码。我有一个比较实用的经验预约单里不要只保存“桌台类型”还要保存“用户备注”。很多老人喝早茶会要求靠窗位置、或者需要宝宝椅这些信息后厨和楼面都要看到。我在预约单表加了remark字段并且push到商家端的订单列表首屏展示老板看一眼就知道要预留什么。2.3 关键API设计与状态流转规则后端接口遵循RESTful风格所有接口都挂在/api/v1下面。用户端核心接口有这些获取可预约时段返回当日各时段剩余桌台数量前端按时间轴展示获取菜单列表支持按时段和分类过滤同时支持分页提交预约接收用户ID、日期、时段ID、桌台类型、人数、菜品明细后端先校验库存再创建订单查询我的订单返回订单状态列表支持分页取消预约在商家未确认前允许取消释放库存。订单状态我用一个数字来表示0待确认、1已确认、2已到达、3已取消、4已完成。状态流转的规则很严用户提交后是0商家确认后置为1用户到店后由商家扫码操作置为2消费结束置为4。只有在0和1的状态下用户才能取消2以后就不能取消了。库存的释放只在用户取消且状态为0或1时发生避免重复释放。这里我用了一个小技巧状态字段用int而不是string因为小程序端做条件渲染、后端做if判断都比字符串高效。而且int状态可以配合枚举类在代码里给每个数字一个命名常量可读性和维护性比写死阿拉伯数字好很多。Flask里的接口实现我习惯用蓝图划分模块auth、menu、order、admin各一个文件。返回格式统一为{code: 0, msg: success, data: {...}}code为0表示成功非0表示业务错误码。这样小程序前端封装request后只需要判断code不需要每个接口单独处理异常。3. 微信小程序端uniapp的实现细节3.1 uniapp工程搭建与页面结构用HBuilderX创建uniapp项目时建议直接选择“默认模板”或者“uview-plus模板”。我一开始手动装了uview-plus走了不少弯路后来直接在插件市场搜索uview-plus导入它会自动把组件和easycom规则配好省了一大堆时间。页面结构上小程序端我规划了五个底部Tab首页、菜单、预约、订单、我的。首页放的是推荐菜品和营销banner菜单页是完整的菜品列表支持分类切换和搜索预约页是核心页面包含日期、时段、桌台类型、人数选择以及一个简易的菜品加购区订单页展示我的预约历史和状态我的页面放用户信息、会员等级、联系客服等。这里要注意一个细节tabBar页面在小程序里是固定的但预约流程中用户从菜单页加购后跳转到预约页这个跳转不应该刷新tabBar页面状态。我在菜单页加购时把数据存到了全局store里预约页从store读取而不是通过URL参数传递避免因为页面栈限制导致数据丢失。uview-plus的列表组件用u-list自带分页加载的插槽但真正做起来还是要自己写触底加载的逻辑。小程序里页面滚动到底部会触发onReachBottom生命周期我在每个需要分页的页面里都实现了一个loadMore函数配合一个loading状态和pageNum变量避免重复请求。3.2 菜单页实现分类切换、列表加载更多菜单页的列表加载更多是个高频需求。我的逻辑是这样页面加载时请求第一页数据每页返回20条和total总数用户下拉触底时pageNum加1继续请求下一页如果当前已加载数量大于等于total就显示“没有更多了”并停止请求。实际操作里我遇到一个问题微信小程序的原生scroll-view和页面级滚动在uniapp里的触发方式不一样。如果用页面的onReachBottom需要在pages.json里配置enablePullDownRefresh为true而且触底距离默认是50px。如果页面结构是自定义的scroll-view就要监听scrolltolower事件。我统一用了页面级滚动因为tabBar页面默认就是页面级滚动配合u-list的custom属性可以正常工作。还有一个细节是请求的loading。加载第一页时要显示骨架屏加载下一页时只在底部显示“加载中”的小菊花。uview-plus没有默认的骨架屏组件我用u-skeleton配合v-if实现代码量不大但体验提升非常明显。之前用统一的loading遮罩用户每次翻页都看到全屏转圈体验太差。菜单数据在store里做了缓存。用户第一次进入菜单页时请求一次之后切换分类或回到该页面时先显示缓存再请求新数据这样页面切换不会有白屏感。缓存使用的时间戳是五分钟超过五分钟再做强制刷新这个时间足够覆盖早茶时段内的常规浏览。3.3 早茶时段选择与预约单提交流程预约页是整个系统的核心我把这个页面的交互逻辑研究得很细。日期选择我用u-calendar组件支持禁用今天之前的日期日期选定后系统根据后端返回的时段数据渲染早茶时段和下午茶时段。时段的数据结构是对象数组每个对象包含startTime、endTime、remaining和isActive。时段展示有个坑微信小程序的顶部导航栏高度在不同机型上不一样如果页面顶部被遮挡会特别难看。我在pages.json里给预约页配置了自定义导航栏然后通过getSystemInfo获取状态栏高度和胶囊按钮位置动态计算页面内容区的padding-top。这个经验也适用于其他有自定义导航栏的页面。用户选好日期时段后还要选桌台类型和人数。我这里做了一张映射表比如两人桌最多坐四人、包间最多坐十人前端选中桌台类型后就限制人数选择器的最大值避免用户选了包间只坐两个人浪费商家桌台资源。这个规则我也会在后端校验一次双保险。提交预约时前端先把整个预约单对象传给后端包括日期、时段、桌台类型、人数、备注、加购的菜品明细。后端在生成订单前会对菜品明细里的每个菜品做库存校验因为限量点心可能在你选菜的过程中被别人订完了。如果某个菜品库存不足后端会返回该菜品的ID和剩余数量前端用u-toast提示用户调整。提交成功的回调里有订单号和一个“订阅消息授权”的弹窗。小程序端的订阅消息是每次用户点击允许才算一次如果能拿到用户授权订单状态变化时商家可以给用户推送。我们给用户推送的场景是“订单确认”和“距离预约开始前半小时提醒”这两个模板在微信公众平台后台申请后前端用uni.requestSubscribeMessage拉起授权。3.4 我的订单列表与状态更新逻辑订单页的列表同样要做分页加载。每张订单卡片展示订单号、日期、时段、状态文案和操作按钮。订单状态我用颜色区分待确认橙色、已确认蓝色、已完成灰色、已取消红色。uview-plus的u-tag组件可以直接传入color属性状态值映射成颜色和文案我抽成一个字典每次便利。状态更新不能靠轮询。小程序里没有通用的长连接方案微信官方推荐用订阅消息被动通知。但用户在小程序内停留时还是需要订单状态刷新。我在order页的onShow生命周期里会重新请求接口同时用uni.$emit在用户从其他页面返回时通知订单页刷新。这里用了uniapp的全局事件总线注意在页面onUnload时移除监听否则会重复请求。用户取消订单的接口要传订单号和取消原因后端收到请求后会先判断状态是否允许取消再调用Redis释放库存。有一个边界情况如果预约时间是明天用户取消后库存立即释放别人马上能订这个时段但如果预约时间是今天下午商家已经备料了就不能取消。我在后端做了一个简单的时间判断距离预约开始不足两小时的订单不支持线上取消必须电话联系商家。4. 打包上架与微信小程序适配实战4.1 manifest配置与微信小程序打包流程开发完uniapp项目后在HBuilderX里选择“运行到小程序模拟器”首次需要填微信开发者工具的安装路径。这里有个值得注意的地方manifest.json里微信小程序部分的配置包括AppID、权限声明、组件配置一定不要用默认值尤其是AppID如果用测试号很多接口会受到限制。小程序项目里用到的一些接口需要在小程序后台配置域名白名单。本地调试时可以在微信开发者工具里勾选“不校验合法域名”但真机预览时必须保证请求的域名是HTTPS且已经在后台备案。我开发时后端跑在本地小程序端请求http://localhost:5000预览时改成线上服务器域名。这个域名配置是我在项目交付时操作最多的一个环节。uniapp的打包其实分两步先在HBuilderX点“发行-小程序-微信”生成dist/build/mp-weixin目录然后用微信开发者工具导入这个目录确认代码能正常编译后再上传版本并提交审核。如果你使用的是uniapp CLI创建的vue3项目可以在根目录执行npm run build:mp-weixin效果一样。微信开发者工具导入项目时会把dist目录当作项目根目录所以如果能编译成功就直接点击右上角“上传”按钮版本号建议每次递增。4.2 解决小程序包体积超限和分包加载问题很多人在打包时会看到“source size 2612kb exceed max limit 2mb”这样的错误这就是主包体积超过了2MB的限制。我第一次打包时也是2612KB超了不少。但要明确一点微信小程序总包上限是2M分包后每个分包上限是2M所有分包总和上限是20M。所以我们不需要硬把所有东西塞进主包用分包策略就能解决。我的做法是把“预约页”、“订单列表页”、“我的页面”这些核心业务页面放进主包因为它们是tabBar页面必须留在主包而“商家详情页”、“优惠券页”、“历史订单详情页”等非核心页面放到“subPackages子包”里用到了才加载。操作很简单在pages.json里加一个subPackages数组配置root和pages路径然后在页面跳转时用uni.navigateTo跳转到分包路径微信会自动分包加载。静态资源压缩是另一个大头。小程序端的图片对体积影响巨大我从后端接口返回的图片URL直接走CDN同时把本地的静态图片尽量压缩到几十KB。页面用到的图标全部从uview-plus组件库里取避免自己引入iconfont文件。做完这些优化主包体积降到了1.4MB顺利通过编译。还有个小技巧在uniapp项目里pages.json的condition字段可以配置分包预下载比如用户进入首页后就提前下载预约页所在的分包这样点击跳转时不出现白屏等待。这个配置很简单但能明显提升体验。4.3 微信小程序顶部导航栏与页面布局适配顶部导航栏高度是所有uniapp开发者绕不开的一个坑。微信小程序的导航栏默认是全局配置但为了沉浸式体验我选择自定义导航栏。查高度的方法很简单通过uni.getSystemInfoSync拿statusBarHeight和menuButton的boundingClientRect导航栏高度就是menuButton.bottom - statusBarHeight (menuButton.top - statusBarHeight)再加上状态栏高度。实际上很多项目直接用公式navigationBarHeight statusBarHeight 44因为微信官方设计稿里胶囊高度就是44。不过在部分安卓机型上胶囊高度不是44所以我做了动态计算。我在项目里写了一个mixin来处理导航栏高度所有需要自定义导航栏的页面都混入这个mixin在computed里返回contentTop值。对应页面根节点的padding-top绑定这个值这样内容就不会顶到状态栏上。这个方法在Android和iOS上都测试过比较稳。还有一个细节是页面底部安全区。iPhone X以后的机型底部有Home条预约页面的“立即预约”按钮要避开这个区域。我在按钮底部加了safe-area-inset-bottom的适配uniapp里可以直接用代码块。除了高度我还要提醒一个非常容易忽略的问题微信小程序在页面滚动时自定义导航栏的背景会默认是透明的内容会穿透过来。我在滚动事件里对导航栏背景做动态处理当scrollTop大于一定数值时把背景色从透明变为白色并加上box-shadow让导航栏始终有层次感。4.4 真机调试与日志打印的几个常见问题真机调试时最烦的问题是日志不打印。在微信开发者工具里console能看到日志但真机预览时经常什么都看不到。这时候在代码里用uni.showToast或者console.log都不行因为真机上的控制台不会默认输出。我建议用vConsole这个工具库在调试模式下动态加载真机页面上会悬浮一个调试按钮点开就能看console、网络请求和页面数据。上线前记得把vConsole的引入关掉避免用户看到调试信息。另一个常见问题是开发调试时页面白屏。这种白屏多半是因为请求接口失败但看不到报错。我排查时先在network面板看request是否发出如果发出但没返回检查后端日志如果没有发出检查接口地址是否写对、域名是否在小程序后台配置过。还有一个坑小程序开发时默认不允许请求http接口但本地开发用localhost是允许的前提是在工具栏里勾选了“不校验合法域名”如果忘了勾选就会一直白屏。真机调试时还要关注定位权限的申请。我们这个项目不需要定位但如果你自己扩展了“附近门店”功能就要在manifest里配置权限。uniapp在微信小程序端调用uni.authorize前需要隐私协议弹窗。如果用户拒绝对话授权需要引导用户到设置页打开权限这个流程一定要处理好否则审核容易被拒。5. 常见问题排查与优化技巧速查5.1 预约流程相关疑难杂症问用户提交预约后一直转圈没有反应。 答优先看网络请求是否收到response。如果收到但code不为0把msg取出来用toast显示如果没收到大概率是请求被拦截检查域名白名单和HTTPS证书。问时段库存明明有剩余但提交时提示已经订满。 答这是并发问题。多人同时点击提交后端Redis的decr操作只有一个能成功。可以查一下当前时段的可用数量如果变成负数就说明库存扣减了但订单创建失败需要做回滚。问小程序端显示“当前时段不可选”但后端口径一致。 答这就是前端没有获取到该时段的剩余量。我建议后端在返回时段列表时把剩余量和该时段的开放状态都返回前端先判断isOpen再判断remaining这样某个时段售罄时前端会置灰显示“已订满”不会出现用户非要选择这个时段的尴尬。问用户提前点了菜但到店后菜品已经下架。 答这是早晚数据不同步。我设计时在预约明细里加了菜品快照字段包含菜品名称和单价即使菜品表改了价或下架预约单里的记录不变对用户和商家都公平。这条经验在做账单打印的时候特别有用。5.2 性能优化与体验提升的五个方向第一接口数据做缓存。菜单列表、时段配置这类不常变的数据在小程序端用storage缓存设置过期时间。每次进入页面先读缓存再在后台更新能大大减少用户等待时间。第二图片懒加载。uview-plus的u-image组件自带了lazy-load属性开启后图片只在出现在视口时加载。菜单页会有几十张菜品图不加懒加载真机能明显感到卡。第三接口合并。预约页需要同时拿时段库存、桌台类型、菜品列表原本三个请求。我把这三个接口合并成一个聚合接口数据一次性返回前端渲染快很多。合并接口的原则是同一业务场景、同一页面交互需要的数据尽量由一个接口返回。第四骨架屏优先。用u-skeleton模拟页面结构数据返回后再替换成真实内容视觉上不会有空页面跳变。第五分页的请求防抖。列表触底时网络慢的情况下用户反复上拉会并发触发多次请求。我在loadMore函数里加了一个isLoading标志如果上一次请求还没完成直接return从根上避免重复请求。5.3 微信审核被拒的常见原因与规避小程序提交审核时除了明显的功能性问题最容易被打回的是没有明显的用户隐私保护提示类目选择与实际服务不符涉及餐饮预订但未提交相关资质。针对隐私保护我在登录和提交预约时都弹出了隐私政策弹窗并明确说明收集用户手机号、位置信息的用途。类目选择上我选择了“餐饮-食品”类目。如果做的是校园食堂预定类目也要对应好。资质问题取决于小程序的主体类型如果是企业主体且有餐饮服务资质在后台提交相关证书即可。这里还有一个很多新手会踩的坑提交审核的小程序不能有强制分享、诱导分享的行为。我在预约成功后没有做“分享给好友得优惠”之类的引导虽然老板一直想加但我告诉他等审核过了再考虑不然审核期容易被拒。6. 从零部署到线上运营的经验总结6.1 环境准备Python安装、依赖管理与服务器部署虽然开发阶段买一台云服务器不是必须的但上线运营肯定是。服务器我选了2核4G的配置操作系统Ubuntu 22.04。先安装Python 3.10和pip用virtualenv建一个独立环境把项目依赖用pip freeze导出到requirements.txt服务器上直接pip install -r requirements.txt。后端服务用Gunicorn跑配合Nginx反向代理。Nginx配置里最关键的是把/api/路径转发到后端端口同时配好静态文件的别名。HTTPS证书用Lets Encrypt免费申请一个月内自动续期这个配置务必在后台重建一次。数据库用MySQL 8.0字符集设为utf8mb4否则中文会乱码。服务器上要提前建好数据库和账号Python代码里的数据库连接串通过环境变量读取不要把密码写死在代码里。Redis也需要在服务器上装好并且设置访问密码因为预约库存的关键操作都依赖它。6.2 小程序前端从模拟器到真机自动发布开发阶段我在微信开发者工具里反复运行调试上线时用HBuilderX发行。实际交付中我推荐在项目根目录创建一个build:mp-weixin脚本自动化构建输出到dist目录。然后微信开发者工具里有一个“自动化测试与预览”的命令行工具可以配置CI流程但项目小的话手动操作就够。版本号管理建议每次发布对应一个小程序版本号同时在微信公众平台后台设置体验版把二维码发给店长和店员测试。测试通过后提交审核。审核通过后不要立即全量发布先发布为“分阶段发布”给10%的用户试用几天收集反馈再全量开放。这一步在餐饮店实际运营中很重要因为一旦出现线上问题影响面可以控制得很小。6.3 运营期间的数据统计与功能迭代方向系统上线后茶楼老板最关心的数据是各时段预约转化率、热门菜品点单率、用户取消率。我在后端加了一个简单的统计接口按照日期、时段、菜品维度输出报表商家管理后台用ECharts图表展示。小程序端也有一个简单的数据看板老板手机上就能看。后续迭代的方向我目前考虑的有三个。一是接入小程序的订阅消息在用户预约前一天晚上推送次日预约提醒二是增加会员充值功能用户在小程序内储值到店消费可以打折三是根据历史点单数据做智能推荐用户进入预约页时直接推荐他常点的几道菜。这三个方向老板都觉得很实用我也在逐步开发里验证可行性。我个人做下来最深的体会是这类预定系统的核心不是代码写得多漂亮而是要把业务方说的每一句话都转化成系统里的一个字段或者一条规则。比如“下午茶每天只出二十份”这种很口语的需求对应的就是Redis里的一个库存key和数据库里的一张限量时段表。思维转了项目就顺了。最后再分享一个小技巧上线前一定要把“取消预约释放库存”的代码写一个单元测试用并发压测工具跑一下不然活动日库存超卖会让你哭都来不及。
返回列表