ARTICLE DETAIL

资讯详情

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

从订单状态机到实时推送:餐饮点单系统开发实践

从订单状态机到实时推送:餐饮点单系统开发实践 1. 项目概述与整体架构设计做这个项目起因是一次朋友开的餐饮店跟我吐槽店里高峰期服务员来回跑顾客催单、后厨漏单、结账对不上号纸质小票满天飞。当时市面上现成的餐饮SaaS系统不是不好而是对他们这种社区店来说功能太重、费用不便宜而且数据都在别人手里。聊了几次后决定自己动手做一套够用、可控、能改的店内点单系统。这套系统的定位很明确不碰外卖平台不做本地生活推广就专注解决店内这一个场景——顾客进店扫码或者服务员帮着点单订单实时到后厨后厨做完出菜服务员端和收银端同步状态。整个链路覆盖前厅服务员、后厨厨师、顾客三个角色。1.1 业务场景分析与核心需求拆解做项目之前我把整个点单流程走了好几遍最终拆成几个核心场景。场景一顾客自助点餐。顾客进店坐下扫桌台码进入小程序浏览菜单、加购物车、提交订单。支付环节我选择了先下单后支付模式因为店内场景顾客信任度高而且允许服务员现场协助确认订单避免线上支付失败导致订单悬挂的问题。场景二服务员协助操作。不是所有顾客都会用小程序尤其是中老年顾客。所以服务员端必须支持代替顾客下单、加菜、退菜、催菜、叫起暂缓制作、结账这些操作。服务员看到的界面和顾客端要有区别核心是多一个桌台管理视图。场景三后厨制作与出菜。后厨不需要管钱、不需要管桌台状态只看待制作和制作中的订单。为了减少厨师操作成本后厨端只提供两个动作接单、出菜。这个交互必须足够简单后厨忙起来的时候根本没时间滑动屏幕。场景四订单状态全局同步。一个订单顾客端显示制作中服务员端显示已下单、未上菜后厨端显示队列和进度收银端要看到完整的流水记录。状态不能各端各算各的必须以服务端订单状态机为准客户端只做展示和动作触发。1.2 技术选型为什么是ThinkPHP加Laravel双框架很多朋友看到这个项目名会问一个系统为什么要用两个PHP框架这不是给自己找麻烦吗实际不是。我是把系统拆成两套部署的各管各的事。ThinkPHP部分负责管理端。包括菜品管理、分类管理、桌台管理、员工账号、订单流水查询、营业数据统计。这套管理后台的特点是CRUD密集、页面多、逻辑直白开发速度要快。ThinkPHP 3.2虽然老但对PHP 8做了兼容处理后配合我熟悉的模板渲染方式做后台效率极高。而且管理后台访问量低不需要极致的性能调优稳定就够。Laravel部分负责API服务。微信小程序端、后厨大屏端、服务员端都通过这套API交互。订单创建、状态流转、消息推送、队列任务这些逻辑放Laravel里主要看中它的中间件机制、队列系统和更规范的服务化能力。Laravel的Eloquent模型做关联查询比ThinkPHP的模型直观尤其是订单和订单明细这种一对多的场景写起来顺手很多。两套服务共用同一个MySQL数据库ThinkPHP管写入和维护数据Laravel管业务处理各自读取需要的表。实际跑下来这套架构没有出现数据冲突因为关键数据表的写入都收敛在Laravel API这一侧管理后台的修改操作频率低冲突概率可以忽略。1.3 系统整体架构与核心模块划分整体架构分三层客户端层微信小程序顾客端、服务员端共用一套代码通过登录角色区分、后厨KDS大屏端用小程序WebView模式嵌入H5页面、PC管理后台ThinkPHP渲染。服务层Laravel 8 API接口包含鉴权中间件、订单模块、菜品模块、桌台模块、消息模块ThinkPHP管理后台独立部署承担数据维护和报表。数据层MySQL 5.7核心表包括菜品表、菜品分类表、桌台表、订单表、订单明细表、制作日志表、操作员日志表。模块划分上核心模块就四个菜品与菜单模块、订单与支付模块、桌台与服务员模块、后厨制作模块。这个系统的重点不在功能数量多而在状态清晰、链条完整。我把80%的精力放在订单状态机和消息实时性上因为这才是餐饮点单系统的骨架。2. 数据库设计与订单状态机设计数据库是整个系统的地基。餐饮点单系统的数据库设计核心不复杂但对关系的准确性要求很高。一个订单从创建到完成涉及的关联数据要能完整还原谁点的、点了什么、谁做的、什么时候上菜的整个过程。2.1 核心数据表设计与字段说明建表我坚持一个原则订单一旦落了库任何一条明细都不能物理删除只能通过状态字段作废。这样做的好处是后续做营业分析、菜品销量排行、员工绩效统计的时候数据永远齐全。菜品表menu字段名类型说明idint主键category_idint所属分类namevarchar(50)菜品名称pricedecimal(10,2)售价imagevarchar(255)图片路径statustinyint1上架 0下架sortint排序权重is_sold_outtinyint估清状态这里要提一下估清售罄和上下架是两个概念。上下架是管理员操作控制菜品是否出现在菜单里估清是后厨实时操作比如某道菜食材剩最后三份厨师在后厨端点一下估清这道菜在前端立即显示已售罄。我当时单独设计了is_sold_out字段而不是复用status就是因为这两个状态的操作者不同、频率也不同。订单表orders字段名类型说明idint主键order_novarchar(32)订单号table_idint桌台IDstatustinyint订单状态total_amountdecimal(10,2)订单总金额pay_statustinyint支付状态remarkvarchar(255)订单备注creator_typetinyint1顾客 2服务员created_atdatetime下单时间订单明细表order_items记录每一道菜包含菜品名、数量、单价、小计、状态待制作、制作中、已出菜、已退菜、制作人、出菜时间。为什么明细里要冗余一份菜品名称和单价因为菜品后来可能改价、改名订单详情必须保留下单时的快照不能关联查询实时菜单表。桌台表tables记录桌号、座位数、状态状态有空闲、使用中、待清洁三类。操作日志表operation_logs记录所有关键动作包括谁在什么时间操作了哪个订单修改了什么字段。出问题时这是排查的第一手数据。2.2 订单状态机这单子到底走到哪一步了我见过不少点单系统订单状态字段设计得很随意今天写个数字明天写个字符串最后代码里一堆if判断乱得没法维护。这个项目我把状态机单独提出来设计状态流转关系明确任何节点不可跳转。待支付(0) - 待制作(1) - 制作中(2) - 已出菜(3) - 已完成(4) \- 已退菜(5, 作废)订单从待支付变成待制作是顾客确认下单或者服务员代客下单后触发。订单进入待制作时后厨大屏立刻收到这条消息这是整条链路里最核心的推送。制作中状态表示厨师接单开始做了。这里有个细节一张订单包含多道菜后厨接单是整单接还是分菜接我实际跑下来总结小餐饮店用整单接最简单。一碗面加一个卤蛋你让厨师分别接两次单他反而觉得烦。整单接的意思是厨师看到这个订单后点击开始制作订单整体进入制作中然后出菜时逐道菜标记完成。出菜和上菜是两个动作。厨师做好菜在后厨端点出菜这道菜标记为已出菜同时通知服务员端某某桌有菜出锅了。服务员端收到通知操作上菜后这才算真正走完一道菜的流程。支付状态独立于订单状态。客人可以吃完再结账也可以下单就结账。订单状态走完不一定代表支付完成两个状态机并行管理报表统计时分别汇总。2.3 并发问题的预防订单号怎么生成才不重复餐饮高峰期同一秒内可能会创建几十个订单。订单号如果自增主键直接暴露给前端不合适用时间戳加随机数又可能撞号。我用了日期自增序列的方式在Laravel里通过Redis的原子自增操作生成每日序列号。$date date(Ymd); $seq Redis::incr(order_seq_ . $date); $orderNo $date . str_pad($seq, 4, 0, STR_PAD_LEFT);这个方案的好处是订单号短、可读性强服务员报单号催菜的时候报202403150032比报订单ID 3321884顺口得多。Redis天然支持原子自增不存在并发撞号的问题。如果店铺没有Redis也可以用数据库表的自增ID配合日期拼接但并发高的时候性能会打折扣。3. 后端API设计与核心代码实现API层是这套系统最核心的部分。小程序端、后厨大屏端、服务员端拿到的所有数据都靠这一层输出。API设计我是按照资源动作的思路拆的每个控制器只负责一类资源的操作不做越界的事情。3.1 Laravel API整体设计与中间件鉴权Laravel这边我用的是api路由所有接口统一走/api前缀。接口整体分三类登录鉴权类、业务操作类、数据查询类。登录鉴权类接口包括小程序登录微信登录code换openid、服务员PIN码登录、刷新令牌。业务操作类接口包括创建订单、确认订单、取消订单、退菜、叫起、催菜、出菜、上菜、结账。数据查询类接口包括菜单列表、订单详情、今日订单列表、桌台状态列表、后厨待制作列表。权限控制用中间件实现。顾客只能查自己的订单服务员能操作自己负责的桌台订单后厨只能看到待制作和制作中的订单角色权限在登录时写入JWT令牌的claims里每次请求在中间件里校验角色和资源归属。public function handle($request, Closure $next, $role) { $user $request-user(); if ($user-role ! $role) { return response()-json([code 403, message 无权限操作], 403); } return $next($request); }这里要重点提醒一下光校验角色不够必须做资源归属校验。我吃过亏的场景是一个服务员A通过抓包把订单ID改成服务员B负责的订单ID然后对这条订单执行了退菜操作。虽然是小概率情况但在接口设计时就应该堵住。所以凡是涉及订单操作的接口都要额外校验操作员是否属于订单桌台的服务员。3.2 ThinkPHP管理后台菜单与桌台的CRUD设计ThinkPHP这边没有太多花哨的东西主要是把管理后台做扎实。菜品管理、分类管理、桌台管理、订单查询、员工账号管理、营业数据统计六块功能。菜品管理界面我做了批量编辑功能菜品多的时候很实用比如门店调价不需要一个一个打开编辑页直接列表里逐行修改价格然后批量保存。ThinkPHP的Db类和模型配合写入操作非常顺滑。// ThinkPHP 3.2 菜品批量更新 public function batchUpdate() { $data $_POST[items]; // [[id1,price18],[id2,price22]] $model D(Menu); $model-startTrans(); try { foreach ($data as $item) { $model-where([id $item[id]])-save([price $item[price]]); } $model-commit(); $this-ajaxReturn([code 0, msg 更新成功]); } catch (\Exception $e) { $model-rollback(); $this-ajaxReturn([code 1, msg 更新失败]); } }为什么批量更新要用事务因为中间只要有一条更新失败前面更新成功的菜品价格就和服务端不一致了。餐饮店改价往往是统一调价必须保证要么全改要么全不改。管理后台的数据统计模块我用的是MySQL的GROUP BY按天聚合。这里有个优化经验不要直接在订单表上跑复杂统计SQL。订单量大了以后聚合查询会拖慢整个库。我的做法是每天凌晨用一个定时任务把当天的营业数据汇总写入一张报表表报表页面只查汇总表速度飞快。3.3 后厨实时通知是如何做出来的后厨大屏的实时性是整个系统体验好坏的关键。厨师不可能一直手动刷新页面必须做到来单自动响、新单自动出现。实时方案我对比过几类方案一小程序端轮询。最简单粗暴小程序每隔5秒请求一次待制作订单列表。缺点是延迟高、费流量而且小程序端频繁setData对性能不友好。方案二WebSocket长连接。实时性好但后端需要单独跑一个WebSocket服务不管是Workerman还是Swoole部署复杂度都会上升。考虑到这个项目是社区店场景客流峰值有限上WebSocket有点杀鸡用牛刀。方案三SSEServer-Sent Events。服务端单向推送正好符合后厨的需求——后厨不需要往服务端推消息只看订单变化。我实际用的是轮询加上SSE降级的混合方案Laravel端用响应流式输出实现SSE推送新订单事件。public function kitchenStream() { return response()-stream(function () { while (true) { $newOrders Order::where(status, 1) -where(is_pushed, 0) -get(); if (!$newOrders-isEmpty()) { echo data: . $newOrders-toJson() . \n\n; ob_flush(); flush(); } sleep(2); } }, 200, [ Content-Type text/event-stream, Cache-Control no-cache, X-Accel-Buffering no, ]); }这里有个坑必须提醒如果用Nginx默认会缓冲响应内容导致SSE数据不能实时到达浏览器。必须在Nginx配置里关闭缓冲proxy_buffering off;或者像我这样在响应头里加上X-Accel-Buffering: no。这个参数很多新手不知道效果立竿见影不加的话SSE会被Nginx吃住好几秒才推送一次和轮询没区别。后厨端还有一个语音播报需求。小餐饮店后厨环境嘈杂光靠屏幕闪烁提醒不够。我用了小程序的wx.createInnerAudioContext接口播放一段短促的您有新的订单提示音声音文件放在服务器上小程序端直接播放远程音频地址。4. 微信小程序端核心实现小程序端是整个系统的门面顾客、服务员、后厨三个角色都通过小程序触达。虽然角色不同但底层网络层、登录层是复用的一套代码。4.1 小程序端整体页面结构与登录设计小程序端用了原生微信小程序语法没有引入uni-app等跨端框架。原因很简单这个项目只服务微信小程序单端没必要为了未来可能做App的想象去增加一层依赖。原生小程序语法熟悉的人多排查问题也方便。页面结构上顾客端包括首页菜单分类页、菜品列表页、购物车页、订单确认页、订单详情页、订单列表页。服务员端包括桌台管理页、订单操作页、催菜通知页。后厨端包括待制作订单列表页、订单详情与制作操作页。登录设计上顾客首次进入小程序先走wx.login换取code后端用code调微信接口拿openid然后生成自定义登录态返回给小程序。小程序端把登录态存到storage里后续请求带上token。服务员登录我特意做成了扫码登录。每张桌台上有一个专属二维码服务员扫描后可以选择身份我是服务员输入PIN码绑定。这样服务员登录不需要记复杂的账号密码每家店的管理员在管理后台给服务员分配一个4位数PIN码就行操作门槛低。4.2 点单主流程顾客扫码到下单的完整链路顾客扫码的二维码里包含桌台ID参数通过wx.scanCode得到场景值或者直接用小程序码的scene参数传递。// app.js 中处理小程序码参数 onLaunch(options) { if (options.scene) { const scene decodeURIComponent(options.scene); const params this.parseScene(scene); // 解析出 table_id this.globalData.tableId params.table_id; } }解析到桌台ID后顾客进入菜单页。菜单页左侧是分类列表右侧是菜品列表点击菜品加入购物车。购物车逻辑我用的是一个全局变量加storage持久化避免页面切换后购物车状态丢失。顾客提交订单时小程序端把桌台ID、购物车明细、备注一并提交到后端。这里有个交互细节下单按钮点击后要做防止重复提交处理。实际操作中我见过不少顾客连点两次提交结果生成了两笔一模一样的订单。小程序端在按钮点击后立即置灰显示提交中…后端也做了幂等校验——同一个桌台、同一组明细、5秒内不允许重复创建。4.3 服务员端和后厨端的交互逻辑服务员端和小程序顾客端复用同一套代码包首次加载时如果检测到当前用户角色是服务员底部tab切换为桌台管理视图。服务员端最关键的操作是代客下单和订单催菜。代客下单的流程和顾客自助下单一样只是点单前要先选择桌台。催菜功能实现很简单——点击催菜按钮后端在订单上标记一个催菜状态同时向对应的后厨推送一条消息如果有接入硬件还可以联动后厨屏闪烁提醒。// 服务员端催菜 confirmHurry(orderId) { wx.request({ url: ${apiBase}/api/order/hurry, method: POST, data: { order_id: orderId }, header: { Authorization: Bearer ${token} }, success: (res) { wx.showToast({ title: 已通知后厨, icon: success }); } }); }后厨端的大屏页面设计成一个只读时序表单式的滚动列表。订单按时间倒序排列新来的单子在最上面卡片用超大字体显示桌号、菜品名、数量、备注。厨师点击开始制作卡片变蓝色置顶显示点击出菜卡片标记为已完成移入历史区域。整个页面没有任何多余的按钮避免厨师戴着手套操作时误触。后厨大屏的容错设计也要考虑屏幕常亮是必须的小程序端用wx.setKeepScreenOn设置保持亮度。另外后厨现场经常会关掉屏幕盖防尘布但程序不能崩溃所以我把后厨端做成了断网自动恢复——断网时提示网络异常但不停止轮询网络恢复后自动拉取最新数据。4.4 小程序端性能优化与装机踩坑记录小程序端性能优化的核心点在于减少不必要的setData。菜品列表页一页有几十个菜品每次setData传整个数组会导致渲染卡顿。我的做法是分页加载每页12个菜品滚动到底部时再加载下一页。购物车数据量小用全局变量管理只有购物车角标变更时才触发一次轻量setData。这里说一个我调试时踩的坑wx.request的并发限制是10个小程序端如果同时发起超过10个请求后面的请求会排队等待。点单高峰期菜单请求、购物车结算请求同时发出再加上登录态刷新很容易顶到限制。解决办法是做一个简单的请求队列或者在后端把多个查询聚合到一个接口。我最终把菜单数据和桌台信息合并成一个getInitData接口小程序进入页面只发一次请求问题就解决了。5. 关键代码实现与流程串联前几个部分讲了不少设计思路这一部分把核心代码串起来从订单创建到后厨接单出菜走一个完整的闭环。这样大家看代码的时候就清楚每一步是在干什么。5.1 订单创建与状态流转的Laravel实现订单创建是整套系统的核心入口包含订单头、订单明细、桌台状态变更三个动作必须用事务保证一致性。public function create(Request $request) { $user $request-user(); $tableId $request-input(table_id); $items $request-input(items); // [[menu_id1,quantity2,remark不要辣]] $remark $request-input(remark, ); DB::beginTransaction(); try { // 1. 校验桌台状态 $table Table::where(id, $tableId)-lockForUpdate()-first(); if (!$table || $table-status Table::STATUS_CLEANING) { throw new \Exception(桌台不可用); } // 2. 创建订单 $order Order::create([ order_no $this-generateOrderNo(), table_id $tableId, status Order::STATUS_PENDING_PAY, total_amount 0, remark $remark, creator_type $user-role waiter ? 2 : 1, ]); // 3. 创建订单明细并计算总价 $totalAmount 0; foreach ($items as $item) { $menu Menu::where(id, $item[menu_id])-first(); $subtotal $menu-price * $item[quantity]; $totalAmount $subtotal; OrderItem::create([ order_id $order-id, menu_id $menu-id, name $menu-name, price $menu-price, quantity $item[quantity], subtotal $subtotal, status OrderItem::STATUS_PENDING, ]); } $order-update([total_amount $totalAmount]); // 4. 桌台状态设为使用中 $table-update([status Table::STATUS_IN_USE]); DB::commit(); return response()-json([code 0, data [order_id $order-id, order_no $order-order_no]]); } catch (\Exception $e) { DB::rollBack(); return response()-json([code 1, message $e-getMessage()]); } }这里有个非常重要的细节查询菜品价格不能从前端传值里读取。虽然前端购物车里已经显示了价格但后端必须重新从数据库取价格来计算订单金额。原因不用多说前端传值容易被篡改这是基本的安全意识。另一个细节是用lockForUpdate锁住桌台记录防止两个顾客同时扫同一张桌子提交订单产生数据竞争。5.2 后厨接单与出菜逻辑实现后厨接单的逻辑不复杂但要注意操作幂等性。厨师可能因为网络卡顿重复点击开始制作后端要通过状态判断来阻止非法流转。public function kitchenHandle(Request $request) { $itemId $request-input(item_id); $action $request-input(action); // start / finish $item OrderItem::where(id, $itemId)-first(); if (!$item) { return response()-json([code 1, message 订单明细不存在]); } if ($action start) { // 只有待制作状态的明细才能开始制作 if ($item-status ! OrderItem::STATUS_PENDING) { return response()-json([code 1, message 当前状态不允许该操作]); } $item-update([ status OrderItem::STATUS_COOKING, cooker_id $request-user()-id ]); } if ($action finish) { if ($item-status ! OrderItem::STATUS_COOKING) { return response()-json([code 1, message 当前状态不允许该操作]); } $item-update([ status OrderItem::STATUS_DONE, finish_time now() ]); // 通知服务员端有菜出锅了 $this-notifyWaiter($item-order_id); } return response()-json([code 0, message 操作成功]); }我始终控制状态停留时间最长的步骤。一道菜如果同时点了十份厨师需要做很久那么这十份怎么管理我的做法是允许厨师修改制作数量——比如点了10份做了3份先出菜剩余7份继续制作。订单明细表里加了done_quantity字段出菜时更新已完成数量全部完成才把明细状态改为已出菜。5.3 小程序端购物车与订单确认页实现小程序端的购物车实现用的是全局变量加事件通知。因为菜品列表页和购物车弹层不是同一个页面跨页面状态同步是小程序开发的常见痛点我没有用复杂的状态管理库而是用了一个简单的全局存储加wx.eventCenter模式。// cart.js 购物车全局管理 const cart { items: [], add(menuItem) { const idx this.items.findIndex(i i.id menuItem.id); if (idx -1) { this.items[idx].quantity 1; } else { this.items.push({ ...menuItem, quantity: 1 }); } this.notifyChange(); }, remove(id) { this.items this.items.filter(i i.id ! id); this.notifyChange(); }, getTotal() { return this.items.reduce((sum, i) sum i.price * i.quantity, 0); }, notifyChange() { wx.setStorageSync(cart_items, this.items); // 触发全局事件这里简化为调用页面刷新方法 if (this.listener) this.listener(); } };订单确认页的展示逻辑不复杂但有几个体验细节值得注意桌号要放大显示避免顾客看错桌号去错桌位菜品备注要支持整单备注和单菜品备注两种顾客在购物车里可以对某一项写少盐不要香菜也可以整单备注尽快上菜金额展示要区分菜品金额和服务费如果店铺设置了服务费必须明码标出这是市场监管的要求。6. 常见问题与排查技巧实录项目上线后在店里跑了三个月积累了大量的真实问题。这一部分挑几类有代表性的总结一下很多问题是只有在真实环境里才会暴露的。6.1 微信小程序登录态与接口鉴权问题小程序端最容易出的问题就是登录态失效。微信的wx.login拿到的code有效期只有5分钟而且一个code只能用一次。最初我把登录态的获取放在onLaunch里后面发现小程序冷启动的时候onLaunch执行顺序和页面onLoad顺序不固定导致有时候请求先于登录态生成发出拿到401。解决思路是做一个请求前置拦截在发送业务请求前检查token是否存在且未过期如果不存在先执行登录流程再发请求。// request.js 简易封装 function request(url, data, method GET) { return new Promise((resolve, reject) { const token wx.getStorageSync(token); if (!token) { login().then(() { resolve(doRequest(url, data, method)); }).catch(reject); } else { resolve(doRequest(url, data, method)); } }); }还要注意一个细节微信开发者工具里登录态正常真机上偶发失效很大概率是wx.login在特定手机环境下没走回调。调试时在fail回调里打日志确认code获取失败的原因。6.2 Laravel跨域与后端配置问题小程序请求后端接口不需要配置跨域因为小程序的wx.request不受浏览器同源策略限制。但如果你用H5页面调试后厨大屏就会遇到跨域问题。我的后厨端是用H5放在小程序WebView里打开的H5调试阶段必须解决跨域。Laravel这边我用了一个最简洁的全局中间件设置CORS头public function handle($request, Closure $next) { if ($request-isMethod(OPTIONS)) { return response(, 204) -header(Access-Control-Allow-Origin, *) -header(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS) -header(Access-Control-Allow-Headers, Content-Type, Authorization); } $response $next($request); $response-headers-set(Access-Control-Allow-Origin, *); return $response; }调试H5时最坑的是明明后端加了CORS浏览器还是报错。排查下面几个点请求头是否带了非简单请求字段比如Authorization后端有没有处理OPTIONS预检请求Nginx有没有拦截OPTIONS请求。6.3 点单高峰期接口性能优化店铺午市高峰期从11:30到13:30这两个小时内订单量是全天最高峰。项目上线第一个星期就遇到一个性能瓶颈——后厨大屏轮询接口在高峰期响应变慢原来是轮询接口每次都查询全部待制作订单并查关联明细菜品数据量大以后SQL执行时间变长。排查过程先在MySQL里用EXPLAIN看执行计划发现order_items表查询没有走索引。原因是订单明细表的order_id字段没有建索引数据量上来后全表扫描。补了索引后查询速度提升明显。另一个优化点是轮询接口添加时间戳参数只返回增量数据。具体做法是前端记录上一次请求的时间后端只返回该时间之后变更的订单ID列表前端再通过ID去拉取详情。高峰期减少了大量无效数据传输。性能优化总结表问题原因解决方案后厨轮询慢关联查询未走索引给外键字段建索引优化SQL高峰期接口响应慢每次全量返回订单数据增加时间戳增量拉取小程序setData卡顿数据量过大频繁渲染分页加载、减少全量setData大量订单同时创建没有锁表导致竞争使用Redis自增序列6.4 部署与运维中的实战经验部署环境是两台阿里云轻量应用服务器一台跑Laravel API一台跑ThinkPHP管理后台共用一台云数据库。域名和HTTPS证书是必须的小程序要求所有请求域名必须是HTTPS。这里说一个很多人忽略的小细节微信公众平台的小程序后台配置request合法域名时一天只能修改5次。上线前一定要把环境域名确认好不然临时想改发现次数用完了只能干等第二天。我就在这个坑里浪费过一天时间。日志系统我用了最简单的文件日志Laravel和ThinkPHP各写各的。Linux上部署时务必确认storage和runtime目录有写权限不然报错都写在系统日志里业务日志一片空白排查问题像大海捞针。数据库备份我设置的是每天凌晨3点自动备份到OSS保留30天。这个项目规模小一台数据库服务器就够了但备份一定要做。餐饮数据出了问题要是靠人工补录一天的营业流水想死的心都有。最后再说一个杯具的教训项目上线前要测试低电量模式。有一回店里平板后厨大屏用平板挂墙电量低于20%系统自动进入省电模式SSE连接被系统切断了后厨没收到新订单通知结果一张桌的单子压了20分钟没做。后来解决方案是给后厨平板设置充电常连并在代码里加了音量提醒和轮询兜底双保险。整个项目从设计到落地回头来看最有价值的不是用了多高级的技术栈而是把一条点单链路理顺了。ThinkPHP快速支撑管理后台Laravel专注业务API小程序端做交互各得其所。如果你也要做类似的店内点单系统不用纠结框架选型先把状态机设计清楚、实时链路打通再考虑界面和细节这个顺序不能反。动手做的话建议第一步先把数据库建好然后写一个最简单的创建订单接口再用小程序通一遍购物车提交流程。这条主链路通了后面加桌台管理、服务员端、后厨大屏都只是往上叠房子而已。
返回列表