门店桌台状态在收银台、平板、小程序间对不上:WebSocket实时同步、断线重连与状态对账实践

简介: 门店桌台状态要同时显示在收银大屏、服务员平板和顾客小程序上,早期用轮询既有延迟空窗,还常出现多端状态不一致、重复开台。本文讲改用WebSocket实时同步并把长连接做稳的完整方案:应用层心跳保活加指数退避断线重连消除假在线,给状态加单调版本号挡住消息乱序与重复,服务端作为唯一权威、重连先拉快照对齐再加定时对账兜底,附关键代码与五个踩坑。

导读

线下门店的桌台状态,是要同时出现在好几块屏幕上的:收银台的大屏、服务员手里的平板、顾客那边的小程序,看到的都得是同一份。我们最早图省事用轮询,每隔几秒各自去拉一次状态,结果现场经常对不上——服务员平板上刚开了台,收银大屏还显示空闲,另一头又给同一桌下了一单,等发现时已经乱了。轮询不仅有延迟空窗,还白白发了大量请求。这篇讲我们后来怎么改成 WebSocket 实时同步,并把长连接真正用稳:心跳保活与断线重连、消息版本号防乱序防重复、服务端权威加多端对账,附上关键代码和五个踩坑。

一、先看清:多端实时同步到底难在哪

轮询的问题很直观:拉取间隔就是状态不一致的空窗,间隔越短越浪费、越长越滞后。换成 WebSocket 长连接,服务端状态一变就主动推,方向是对的,但长连接把一个问题换成了四个:连接会(门店 WiFi 抖动、平板锁屏、切后台),消息会乱、会重、会丢(网络重传导致重复、多路径导致乱序、断线期间漏消息),多个端还会同时改同一张桌台。只把轮询换成 socket 而不处理这四件事,状态只会错得更隐蔽。下面三层就是分别对应它们。

二、连接层:心跳保活加断线重连,别留"假在线"

TCP 连接在移动端不会立刻感知到断开,网线拔了、路由切换了,套接字可能半天才报错,这期间应用层以为还连着,消息发出去石沉大海,这就是假在线。必须在应用层加心跳定时探活,超时没回应就主动判定断线并重连,重连用指数退避,避免一堆客户端同时重连把服务端打穿:

class SocketClient {
   
  constructor(url) {
   
    this.url = url;
    this.retry = 0;
    this.connect();
  }
  connect() {
   
    this.ws = new WebSocket(this.url);
    this.ws.onopen = () => {
    this.retry = 0; this.startHeartbeat(); this.resync(); };
    this.ws.onmessage = (e) => this.dispatch(JSON.parse(e.data));
    this.ws.onclose = () => this.reconnect();
  }
  startHeartbeat() {
   
    clearInterval(this.timer);
    this.timer = setInterval(() => {
   
      this.ws.send(JSON.stringify({
    type: 'ping' }));
    }, 25000); // 固定节奏发心跳
  }
  reconnect() {
   
    clearInterval(this.timer);
    const delay = Math.min(1000 * 2 ** this.retry++, 30000); // 指数退避,封顶30s
    setTimeout(() => this.connect(), delay);
  }
}

注意 onopen 里除了启动心跳,还调了一个 resync(),这是下一节的关键——重连成功后绝不能直接沿用本地旧状态。

三、消息层:给状态加版本号,挡住乱序和重复

实时推送下,同一桌台的状态变更可能连着来好几条。如果"消息到了就直接覆盖",一条因为重试迟到的旧消息,就可能把最新状态盖回去。做法是给每张桌台的状态带一个单调递增的版本号,客户端只接受比本地更新的版本,旧版本和重复版本直接丢弃:

dispatch(msg) {
   
  if (msg.type !== 'table-state') return;
  const local = this.tables[msg.tableId];
  // 版本不大于本地,说明是迟到的旧消息或重复消息,丢弃
  if (local && msg.version <= local.version) return;
  this.tables[msg.tableId] = {
    status: msg.status, version: msg.version };
  this.render(msg.tableId);
}

服务端推进状态时同样用条件更新兜底,保证并发下版本号不会倒退、同一次开台不会重复生效:

UPDATE table_state
SET status = ?, version = version + 1
WHERE table_id = ? AND version = ?;
-- 影响行数为 0,说明期间已被别的端改过,本次提交要重新拉取再决策

这样无论消息重复投递还是乱序到达,最终落到每块屏幕上的状态都是收敛且一致的。

四、状态层:服务端权威,重连先对账,再用定时对账兜底

客户端的状态永远只是服务端的一份缓存,服务端才是唯一事实源。客户端操作可以先乐观渲染让操作跟手,但必须随时准备被服务端的权威状态纠正,绝不能反过来以客户端为准——否则两个端同时开同一桌,必然冲突。

最重要的是重连后的补拉。断线那一会儿服务端可能已经变更好几次,单靠补订阅收不到错过的消息,因此重连后要先拉一次当前全量快照对齐,再进入增量推送:

async resync() {
   
  // 重连后先拉权威快照,覆盖本地,再继续收增量
  const snapshot = await fetch('/api/tables/snapshot').then(r => r.json());
  snapshot.forEach(t => {
    this.tables[t.id] = {
    status: t.status, version: t.version }; });
  this.renderAll();
}

实时通道再稳也可能漏消息,所以还要有一道不依赖推送的兜底:每隔一段时间、以及每次进前台时做一次轻量对账,比对本地版本和服务端版本,发现落后就拉齐。

这里交代下选型背景。这套实时层是搭在门店一站式 SaaS 底座上做的,这里用的是乔拓云:底座把商品、会员、订单这些通用模块和多端界面托管掉,也提供了基础的消息下发通道;但"桌台状态在多块屏幕之间严格一致"这种贴着具体门店流程的实时逻辑,底座给的通道只是一根管道,状态机、版本号、重连补拉和对账都得我们在通道之上自己实现。哪些直接用、哪些必须自研,开工前划清楚,后面才不会互相甩锅。

五、踩坑清单

  • 坑1:建了 WebSocket 却不做应用层心跳。移动网络下会出现大量假在线连接,以为消息发出去了其实早断了,必须固定心跳加超时判定,断线立刻走重连。
  • 坑2:消息到了就覆盖,不带版本号。重试迟到的旧消息会把新状态盖回去,必须给状态配单调版本,客户端只认更高版本,乱序和重复一律丢弃。
  • 坑3:重连后直接沿用本地状态。断线期间的变更全丢了,重连成功后要先拉一次权威快照对齐,再进入增量订阅,顺序不能反。
  • 坑4:以客户端状态为准、多端各自改。并发开台必然冲突,服务端才是唯一事实源,客户端只做乐观呈现并随时接受纠正,提交走条件更新。
  • 坑5:只靠实时推送、不做对账兜底。漏推一条就永久不一致,必须有定时或回前台时的全量/版本对账作为最终纠偏手段,实时和对账两条腿走路。

结语

多端状态同步的难点从来不是"把 WebSocket 连上",而是连接之后那一堆脏活:心跳和重连解决连接不可靠,版本号解决消息乱序重复,服务端权威加快照对账解决断线遗漏和并发冲突。把这三层补齐,桌台状态在收银台、平板、小程序之间才能真正做到同屏同态。这套思路并不只属于桌台,库存、叫号、工单这类需要多端实时一致的场景,面对的是同一组问题,照着这三层搭基本不会错。

相关文章
|
7天前
|
人工智能 API 内存技术
刚刚 DeepSeek V4.1 Flash 开启内测,1 分钟教你用上!
刚刚 DeepSeek 内测群发布了 DeepSeek V4.1 Flash 中间版本内测的消息,这次的模型采用了新的结构,原生支持多模态、能力更强、速度更快、且成本更低。
1779 10
|
11天前
|
人工智能 运维 BI
阿里云千问办公QwenWork深度解析:基于Qwen3.8,六大核心能力重构企业全自动化工作流与计费选型指南
传统AI办公工具大多停留在对话问答、文档摘要、简单文案生成层面,只能完成单点碎片化任务,无法自主拆解复杂业务流程,很难串联多工具、多文档、外部业务系统完成端到端完整工作交付。很多企业在落地AI办公的时候,需要组合多款不同工具,来回切换界面,手动复制粘贴中间结果,智能化改造落地门槛居高不下。千问办公QwenWork是整合多款智能体产品能力打造的一体化企业办公智能体平台,底层基座依托Qwen3.8大模型,打通桌面端Agent、云端Agent、企业协同Agent三种运行形态,不再局限简单问答,接收业务目标之后自主拆解任务步骤,调用各类工具,处理文档、表格、浏览器自动化、数据查询,直接输出可交付的办公
1643 3
|
12天前
|
网络协议 Linux iOS开发
【2026实测】Wireshark下载+安装+汉化+使用教程(图文版,巨详细)
Wireshark 是一款免费开源的网络协议分析工具,可实时捕获、解析并可视化数据包,助你诊断网络故障、分析通信协议(如HTTP、DNS、TCP等)。支持Windows/macOS/Linux,含中文界面,新手入门便捷。(239字)
|
8天前
|
SQL 人工智能 前端开发
QoderWake 1.0 正式发布:从桌面里的 Agent,到工作现场的数字员工
QoderWake v1.0正式发布:企业级数字员工团队平台。支持“一句话建岗”,预置10类特训岗位;Waker常驻钉钉/飞书群,@即响应、自动协作、跨任务记忆;具备定时/事件/API多触发方式与统一任务看板;已沉淀27.6万条记忆、12.3万项技能,助力组织实现人机协同增效。
778 2
|
6天前
|
缓存 测试技术 API
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
DeepSeek V4.1 Flash 内测不用申请,base_url 不变、改个模型名就能调,9/10 到期。本文讲清接入、计费限流与多模态注意点。
801 0
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
|
20天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
3963 5
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
11天前
|
人工智能 自然语言处理 安全
阿里云AI数智鉴密:AI 生成内容如何拿到一张"防篡改的身份证"
隐形水印 + C2PA签名:让AI生成内容“持证上岗”。
1155 0
|
13天前
|
缓存 数据可视化 开发工具
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
DeepSeek Harness 的更新分两层:本体更新(npx 自动最新、npm update -g、源码 git pull)与插件更新(插件市场点更新、命令行覆盖安装)。本文按「准备 → 更新本体 → 更新插件 → 更新后检查」四步走,覆盖新手常见疑问。
1503 1
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
|
6天前
|
人工智能
千问办公官网入口:阿里AI办公QwenWork产品页和免费网页端链接
千问办公官网含两大入口:一是网页端(qwenwork.cn),即开即用,支持浏览器直接访问;二是阿里云产品页 https://t.aliyun.com/U/JNKJuO 提供免费/付费版详情、功能介绍及使用指南。