下午三点四十,一家连锁门店的宽带断了。收银台前还排着四五个人,POS 屏幕弹出一行「连接服务器失败」。店长要在几秒钟里拿主意:让顾客把商品放回去改天再来,还是照常扫码、收钱、打小票。这看着像服务态度问题,落到账上却是另一回事——只要门店继续结账,这笔交易那一刻就只躺在本地硬盘里,总部系统对它一无所知,商品库存没动,会员积分没记,当天的营业额在总部的看板上少了一块。
更麻烦的在恢复之后。宽带夜里修好,门店把断网期间攒下的几十笔单子往总部送,总部这几分钟里也收到了别的门店补传的单。哪边的账算数?总部如果按自己收到的先后重排了一遍单据,门店却按结账时间做了本地日结,两边一对就对不上,差的那几笔究竟是漏了、还是重了,得有人说得清,还得有字段查得清。
这篇谈的就是这件事。断网不是偶发事故,是连锁门店得当成常态来设计的运行状态。把它当成「网络质量不好、换条线路就行」来处理,等于把账目风险全留给了恢复那一刻。真正要先定下来的,不是机房里放几台机器,而是断的时候谁有权继续记账。
断网那一刻,收银台并不是「全有或全无」。它身上至少挂着五条依赖:查商品和价格靠总部下行缓存、会员折扣靠本地版本、收款靠支付网关、生成单据靠本地库、把单据交回总部靠那条断开的链路。断开的往往只是其中一条,其余几条还在。把「断网」当成一个总开关,是后面所有账目混乱的起点。
第一分钟里同时悬着三件事。当前这笔交易记不记;顾客的钱收不收;记下来之后存在哪。这三件事得有统一答案,不能让收银员临场发挥。答案的背后是一个更根本的判断:门店本地到底有没有记账权。有,就照常结账、本地落库、打印小票,事后补交;没有,就只能停收款,等链路恢复。把决定权留给前台,等于把同一场断网处理成好几种账,恢复后根本拼不回来。
门店本地有没有落库的地方,取决于店内拓扑。店内局域网通常在宽带断了之后仍然可用:多台收银终端之间、终端与店内那台小服务器之间还能通信,本地库有落点,记账权才落得下去。如果全部单据只靠每台终端内存里暂放、连店内的本地库都是单点,宽带一断其实还不是最坏的,本地库一坏才是真的停摆。两种拓扑各有代价:每台终端各自落一份,抗单点但汇总时要处理多份副本的合并,单据号里的终端号因此不能省;店内一台服务器统一收口,日结干净但那台服务器本身成了新的单点,得配本地冗余,否则断网没撂倒收银台,本地服务器倒先把整家店撂倒了。
还有一层容易被跳过:断网期间门店能用的「事实」是哪一版。价格、会员余额、促销规则都是断网前最后一次成功下行留下的快照,不是总部此刻的实时值。收银台照常结账,用的是旧快照,这一点必须在单据里留痕,否则恢复后连「当时按哪一版价成交」都查不出来。记账权不只是「能不能记」,还包括「记的是哪一版的事实」。这两层合起来,才是门店敢在断网时继续扫码的底气。
三种失败形态长得像,恢复动作却完全不一样,混在一起处理必然出错。
第一种,门店到总部不可达,店内局域网仍可用。这时查价、会员、落库、店内终端之间都正常,缺的只是把单据交回总部这一条。钱能不能收,要看支付走的是不是同一条断开的链路——若支付网关经外网独立可达,收款不受影响;若收款也得经过总部转发,就得另设直连通道,否则「能记账不能收钱」同样卡住营业。
第二种,支付网关超时。顾客那笔钱可能已经扣了,门店拿到的却是「没回音」。这不是失败,是未知。它和第一种的区别在于:第一种是单据回传问题,账在门店手里、只是没交给总部;第二种是资金状态问题,连这笔交易成没成都没定。把超时当失败,会放走已扣款的顾客;把超时当成功,会发出没收到钱的商品。两种错法方向相反,后果一样难查。
第三种,总部侧不可达但门店能上网。门店可以直连支付网关把钱收了,却不能把单据回传总部。这是最容易麻痹人的一种:钱照收、小票照打、门店看起来一切正常,总部那边却整整空了一段。恢复时如果只按「总部收到什么」去补账,这一段就永远缺,而且没有任何一方会主动发现它缺了——店内的人以为总部收了,总部的人以为这段本来就没营业。
三种断法的分界,不在于断在哪根线上,而在于「门店手里有没有一张确定的单」和「钱的状态确不确定」。链路故障可以归成一类,账目后果却要分开:一个缺的是回传,一个缺的是定性,一个缺的是整段补交。用下表把这几层摊开对照。
| 失败形态 | 门店侧还能做什么 | 总部侧看到什么 | 恢复后要先做什么 | 最容易出错的地方 |
|---|---|---|---|---|
| 门店到总部不可达(店内局域网仍可用) | 照常查缓存价、本地落库、打印小票;支付若可直连网关则照常收款 | 该门店新单为空,营业额与库存都不动 | 按单据号补传,等总部逐单确认后再标记已回传 | 把「没回传」当成「没发生」,总部另按到达时间补一套账 |
| 支付网关超时(钱可能已扣结果未知) | 单据标「未知」,不擅自判成功或失败,商品暂不放行或按既定规则挂起 | 看不到这笔,或只看到一条不完整的状态 | 先向网关查询结果补单,再按渠道流水对账钉死资金状态 | 把超时映射成失败并重新下单,制造第二笔扣款 |
| 总部不可达但外网可用 | 直连支付网关收款、本地落库,单据暂存待回传队列 | 完全没有这段交易,也不会主动发现缺失 | 恢复后先补支付状态查询,再按单据号整批回传 | 门店「看着正常」就以为总部也收到了,漏掉整段补传 |
| 门店本地设备故障(终端或本地库损坏) | 该终端无法落库,其余终端若能连店内本地库可继续 | 该店部分单据缺失,且不知道缺了多少 | 先做本地副本核对与待回传队列恢复,再估算缺口逐笔追 | 单终端单点落库且队列不持久化,断网攒的单随设备一起丢 |
| 基础资料版本不一致(价格、会员下行滞后) | 按最后一次下行快照成交,单据需记下成交版本 | 已按新版本计价,与门店旧价单对不上 | 比对下行版本,按单据里记的版本认定金额,不自动改单 | 恢复后拿总部新价覆盖门店旧价,抹掉成交时的事实 |
| 日结时点不一致(门店已封账、总部未收齐) | 门店按本地营业日切账并封存本地日结 | 收到的单跨了零点、顺序与门店不一致 | 以门店本地日结为准,差异表按漏传、重复、金额差异顺序清 | 总部按到达时间独立生成一套日结,两边永远对不平 |
| 恢复瞬间回传重复叠加(同一单据多次提交) | 待回传队列未收到确认,重复发送同一单据号 | 若按到达处理会生成两张同号或同内容的单 | 总部按单据号幂等收单,重复只返回「已处理」 | 用到达时间或流水号当主键,重复回传变成第二张单 |
表里最该盯的是最后一列。漏传、未知、重复、版本错位、时点错位,几乎都不是「网络不够快」造成的,是规则没定死造成的。换更快的线路、加更大的带宽,能让回传早几分钟完成,却不会让一张重复单自己消失,也不会让一段没人补交的营业自己冒出来。
先把边界划出来,不是因为这四件事不重要,恰恰是因为它们各有各的主管口径,混进来会把本文真正要回答的问题冲淡。
支付牌照与资金结算规则决定的是钱由谁清、什么时候到商户账、退款走哪条路径;发票开具合规决定票怎么开、什么时候开、开给谁。这两件事都不改变「断网期间门店本地那张单算不算数」,也不决定恢复后按什么主键认账。把它们和离线记账绑在一起谈,很容易把合规问题误当成技术问题去解,到头来该走的清算流程没走,该定的单据规则也没定。
门店硬件选型和具体软件安装配置同样另属一层。本文谈的是单据怎么编号、落在哪、怎么回传、怎么对平,属于机制与取舍;某款收银机该选什么型号、某个数据库参数该怎么调、某个组件怎么装,属于采购与实施。全程不会给机型档位、性能数字或安装步骤,也不写成具体数据库的调优教程——那些东西换一套产品就全变了,而「谁有权记账」这个判断不会。把它和部署清单混在一起,读者容易记住了几个配置名,却没带走那条最该先定的规则。
划掉这四件,剩下的才是本文的题:断网与恢复之间,账怎么做到不重不漏。业务连续性听着像网络工程问题,骨子里是账目归属问题。
记账权不是一句口号,它得落到数据结构上,第一处就是单据编号。
断网时单据号必须由门店本地生成,不能向总部要。总部发号意味着断网那一刻没号可领,等于把记账权又交回给了那条断掉的链路,系统会在最需要它独立工作的时刻先瘫掉。本地生成的编号要装下四段:门店号区分是哪个主体,终端号区分同一家店里的哪台 POS,日期划出日结的边界,序列保证在这台终端这一天里单调递增。四段缺一不可——少了终端号,同店两台 POS 会撞号;少了日期,跨零点的单归哪一天说不清;序列不单调,恢复后连「这台终端到底开过多少张单」都核不出来,缺口只能靠猜。
本地落库要有硬顺序:先把单据、行明细、支付状态、基础资料版本快照一起写进本地库,写成功才放行交易、才打印小票。反过来先放行后落库,中间一旦断电或进程退出,就出现「钱收了、单没了」,而总部永远不会知道有过这笔。这个顺序看着琐碎,却是漏单最主要的来源之一,也是最容易被「先让顾客走、回头再补」这种现场便利冲掉的一条。
单据号同时是贯穿全局的对账主键。它要一路带着走:本地库里是它,回传报文里是它,总部收单时认的是它,差异表里比对的是它,日结归档时按它排序。金额、成交时间、支付渠道、收银员,这些都只能是单据的属性,不能拿来当主键。总部尤其不能按「到达时间」重新编号——到达顺序在断网恢复时完全不可信,一批补传的单会挤在同一分钟里到,按它排出来的序和门店实际结账的序毫无关系。
这里就带出了核心判断:门店侧与总部侧谁才是数据源?营业发生在门店,单据在门店生成,门店就是这笔交易的数据源;总部是汇总方与对账方,不是原始出单方。把总部当成数据源,断网期间就没有合法的单,恢复后只能靠总部「看到什么补什么」,重复与漏单几乎没法避免。反过来把门店定为数据源,总部要做的是按单据号如实收、按幂等去重、按门店日结核对,角色一开始就摆正了,后面所有机制都有了依据。
还有一层和拓扑有关:多终端各自落库时,单据号里的终端号保证全局不撞,汇总时按门店号加终端号归并即可;店内小服务器统一收口时,序列可以收敛到服务器一处分配,但仍要带终端号以便追责和定位。两种做法都能用,前提是编号规则在断网前就写死,且所有终端用同一套,不能A店一套、B店一套,否则总部连主键格式都得先猜。
支付结果至少有三态:成功、失败、未知。大多数账目纠纷,都出在把三态压成了两态。
超时落的是「未知」。顾客那侧可能已经扣款,网关那侧可能已经记账,只是回包在链路上丢了。门店拿到「没回音」就判失败,等于放走一个已付款的顾客;判成功,等于把没收到钱的商品交出去。两种都错,而错的根源是状态模型里根本没给「不知道」留位置。补一个字段、多一种状态,成本很低;等到对账时才发现资金和单据对不上,成本就高了。
收口分三步,顺序不能乱。第一步是查询补单:恢复后,或按固定间隔,对所有「未知」单据向支付网关发起结果查询,拿网关的明确返回把未知改成成功或失败。查不到结果的继续挂起,不擅自定性。第二步是对账:以支付渠道侧给出的流水或结算文件为外部事实,和本地的未知单逐一比对,把资金状态钉死。渠道流水是门店和总部之外的第三方记录,比任何一边的猜测都可靠。第三步才是据此更新单据、放行商品或发起退款,每一步都留痕,事后能还原「这单的钱什么时候定下来的」。
有一条红线要划清楚:不能用「重试下单」代替「查询」。重试下单是再发起一笔新的支付请求,一旦前一笔其实已经扣了,就变成扣两次;查询只是问「刚才那笔到底成没成」,不会产生新的资金动作。这两件事在代码里长得像,业务含义完全相反。同理,也不能把「未知」直接当失败,触发自动重试下单——那等于把超时和重试下单接成了一条自动扣款链,顾客一次结账被扣两回,门店还得事后一笔笔退。
「未知」在收银界面上怎么显示,也得提前定。顾客还站在台前,屏幕上是显示「处理中,请稍候」并暂不放行商品,还是允许先走、事后按对账结果补收或退款,这是业务规则,不是技术能替你决定的。不同品类、不同客单价,取舍会不一样:低单价、高复购的门店可能倾向先放行并留联系方式,高单价的通常倾向挂起等查询。规则要写进操作手册,断网时收银员才不会各按各的理解处理,把同一类超时做成好几种账。
再往下还有一层:支付状态和单据状态是两条线。单据可以「已落库、待回传」,支付可以同时是「未知」。回传时不能因为支付未知就把整张单压着不传,也不能把未知当成成功传上去;稳妥做法是单据照传、支付状态单独带一个「未知」标记,等查询或对账有了结果再做一次状态变更回传。这样总部既不会漏掉这笔营业,也不会把它错记成已收款。状态每次变更都要留一条审计记录,否则事后连「这单的钱是查询回来的、还是对账回来的」都分不清。
回传以单据号为单位,报文里带上本地生成时间、支付状态、成交时的基础资料版本。总部按单据号收,不按报文到达的先后重新组织。
幂等是这里的硬要求。同一单据号重复提交,总部要么拒绝、要么返回「已处理」,绝不能生成第二张单。判断口径很朴素:唯一约束落在单据号这一个字段上,收单逻辑先查存在、再决定插入还是返回已处理;重复回传是常态——网络抖一下、门店没收到确认、队列重投,都会造成同一单发两遍,系统必须默认它会发生,而不是假设它不会发生。把幂等做在「单据号唯一」上,比做在「报文指纹」或「到达时间」上都稳,因为后两者在补传场景下会漂移。
门店这侧也要有对应的确认机制。回传成功以「总部按单据号确认」为准,收到确认才把单从待回传队列里摘掉;没收到就留在队列里按间隔重试。这个看似多一次的往返,是漏单能不能被门店自己发现的关键:没有确认,门店以为传完了,其实总部没收到,谁也不会报警,差异要等日结时才浮出来。待回传队列要持久化,不能放在内存里,否则本地设备故障时连「哪些还没传」都丢了,剩下只能靠顾客手里的小票倒查。
退避决定恢复那晚的写入节奏。宽带修好的那一刻,全部门店几乎同时开始补传,白天空着的写入余量会在几分钟内被填满。重试要带退避和抖动,失败的单据按间隔退后重投,不能无限立即重试;回传批量也要有上限,一次推太多既压总部也容易整批失败重来,反而把窗口拖更长。这里和回程带宽是直接挂钩的:带宽窄,回传窗口就长,错峰要做得更细、每批更小;带宽够,窗口短,但峰值更陡,总部那晚的写入压力反而集中。带宽不是越大越好,而是要和错峰策略、批量大小一起定,单独放大某一项只是把瓶颈挪个位置。
还有个先后问题:恢复后应当先把「未知」支付状态查询补单跑一轮,再回传状态已定的单据。若顺序反过来,门店把带「未知」标记的单直接当成已成交传上去,总部那边的营业额和资金就对不上,后面还得逐单冲正。批量回传、状态变更、确认回收,最好分成清晰的几条队列,别揉在一个事务里;一条卡住时不至于把其余几条全堵死。恢复日真正的麻烦,往往不是某一条链路慢,而是几件事挤在同一个窗口里抢同一份写入能力。
日结是主从关系的试金石。结论先摆出来:以门店本地日结为准,总部以「收齐并核对后的门店日结」为准,总部不按自己收到的单子独立生成另一套日结。
理由不复杂。营业发生在门店,单据在门店按本地营业日切账;总部的到达时间、到达顺序在断网恢复时都不可信,拿它去切账,等于拿一个被网络抖动改写过的时间去定义营业事实。跨零点的单归哪一天,也按单据号里的日期段走,不按到达时间——晚上十一点五十九结账、凌晨才传上来的单,仍算前一天的营业。这条不定死,日结永远差一笔,而且每天差的那一笔还不一样。
总部那边的动作是「核对」而不是「重算」:拿门店封存的日结汇总,和自己按单据号收到的明细逐笔对,对出来的差异进差异表,按固定顺序清。顺序是有讲究的:
第一步补漏传。门店日结里有、总部没收到的,先按单据号补传,门店账原封不动。这一步只做加法,不改动任何已存在的记录,也不重新计算金额。
第二步去重复。总部出现两张同号、或不同号却内容雷同的单,靠幂等合并成一张,金额不动。重复往往是回传没做确认、队列重投造成的,属于传输层问题,不该动营业事实。
第三步才核金额差异。同一单据号但金额、行明细、支付状态不一致,留到这一步处理,而且不能自动覆盖——要按原始小票、渠道流水和单据里记的版本逐笔人工核,核完留痕。这类差异多半牵扯基础资料版本错位或支付状态变更,是真正需要人判断的部分。
顺序反过来会出事:先去金额差异、后补漏传,容易把「重复」误判成「漏单」又补一遍;把金额差异交给自动覆盖,等于让后到的记录抹掉门店当时的事实。差异表本身也要留档,哪天清的、按什么依据清的、谁核的,都要能追,否则下次同样的差异冒出来,还得从头猜一遍。
封账时点同样要定:门店按本地营业日切账并封存,总部要等「该门店待回传队列清空、所有未知支付状态已收口」之后才封总部的对应日结。总部若提前封账,后面补传的单就成了无家可归的孤儿,只能挂到次日,两边又错一天。日结不是一天结束时顺手点一下的按钮,它是一串前置条件都满足之后才允许执行的动作,条件没齐就封,等于主动制造对不上。
把数据流拆成两类,很多含糊的地方就清楚了:基础资料下行,交易数据上行。商品、价格、会员、促销规则从总部往门店下发;单据、支付状态、行明细从门店往总部回传。两条流的方向、频率、可靠性要求都不一样,别用同一套同步机制去套——下行慢一拍还能按旧版卖,上行慢一拍就直接影响对账和日结。
下行的关键是带版本和生效时点。断网期间门店用的是「最后一次成功下行留下的快照」,快照里必须记下版本号和生效时间。恢复后先比对版本,差了几个版本就补拉哪几段,而不是笼统地「重新同步一次」——后者会把断网期间已经按旧版成交的单据置于一个说不清的参照系里。促销价尤其容易在断网前后错位:总部零点生效的新价,门店因为断网还在用旧价,这段里成交的单按哪版算,必须在单据里记下成交版本,事后才查得清。价格版本不是技术细节,它是金额认定的依据。
版本不一致会直接变成金额差异。总部按新价记、门店按旧价成交,恢复后如果拿总部新价去覆盖门店单据,就把「当时实际收了多少钱」这个事实抹掉了,差异表第三步也无从核起。判断口径应当是:以单据里记录的成交版本认定金额,价差是补收、退还还是商户承担,属于业务规则,得在断网前约定好,不能等差异冒出来再临时定。会员余额和积分同理,离线消费只能在本地快照给出的可用额度内扣,不能让门店在断网时无限透支,恢复后才发现会员已经超支、积分被重复兑了两遍。
交易数据上行相对单纯:单据、明细、支付状态按单据号回传,前面几节已经铺过。真正难定的是库存扣减放在哪。
一种做法是本地预扣。断网期间门店按本地库存自己扣减,恢复后再与总部汇总核减、多店之间冲正。好处是断网期间照常能卖、可售数量在店内自洽;代价是多店同时卖同一件紧俏商品时可能超卖,要靠预扣额度上限和事后冲正兜住。另一种做法是总部统一扣。断网期间门店不扣或只冻结,恢复后由总部汇总扣减。好处是不超卖,账最干净;代价是断网时门店看到的可售数量不准,可能把本来还有货的商品拒卖了,或者反过来卖了总部已经划给别店的量。
两种做法对断网的影响方向正好相反:本地预扣保营业、担超卖,总部统一扣保账准、担误拒。没有通用答案,按品类和履约方式选——即时带走、缺货成本低的商品更靠近本地预扣,需要总部统一调配、超卖代价高的更靠近统一扣减。定下来之后,扣减时点、冲正规则、可售数量的展示口径都要跟着写死,否则恢复后库存和单据又成两套账,日结平了、盘点还是平不了。
总部的写入压力不是均匀摊在全天,而是压在营业结束、各店陆续日结回传的那个窗口里。白天的配置余量,到晚上会被批量回传、对账任务、库存汇总和日结封账一起占满。容量判断要按「峰值窗口的并发写入+批量回传+对账任务」来做,不能按日均单量,更不能按门店数简单线性外推——一百家店如果在同一分钟封账,和错峰到两小时里封账,对写入侧完全是两个量级。
数据库这层只谈机制与取舍,不谈具体产品的参数。批量回传倾向于合并写入、减少逐单事务,逐单开事务在峰值窗口会把往返和日志放大;单据号上的唯一约束在这个窗口会变成明显的锁竞争点,所有门店都往同一张表按同一字段抢插入,幂等检查和插入要尽量贴近、别拆成多次往返;对账任务和库存汇总应错峰到回传基本收齐之后,别和补传高峰抢同一批表的写锁。这些都是取舍方向,具体怎么落地要按所选组件的现行文档和实际验证确认,本文不给参数、也不替任何产品下结论。
配置优先级也要摆正。峰值窗口里优先保的是「写入不丢、幂等不破」,其次才是查询响应速度。看板慢几分钟能忍,重复单和漏单不能忍。总部侧存储要给待回传队列、差异表、状态变更日志留足余量——这些东西在断网恢复日会成倍增长,平时看着不起眼,真出事那天最先被撑满的往往是它们。回程带宽决定回传窗口的长短:带宽不够就把窗口拉长、错峰做细、每批缩小;带宽足则窗口短而峰值陡,写入侧要按更集中的瞬时压力准备。带宽、批量、错峰三者是一起定的,单独调哪一个都解决不了另外两个带来的问题。
备份与容灾的时点要和日结封账对齐。总部侧的备份若落在「门店已封账、总部还没收齐」的夹缝里,真要恢复,拿到的就是一段缺单据的账;稳妥的做法是让备份时点落在封账完成之后,并把待回传队列和差异表一并纳入备份范围,否则恢复了主库却丢了「还欠哪些单」的清单,补都无从补起。日志与审计留存同样不能省:单据每一次状态变更——生成、回传、总部确认、支付状态改判、冲正——都要留一条可追的记录,这是事后查「谁漏了、谁重了」的唯一凭据,留存周期按对账与审计的实际需要定,别只留到当天日结结束。
把总部侧这批复核、汇总与回传服务器放在哪、带宽留多少,属于基础设施选型这一层,和前面那些记账规则是两回事。从行业部署角度看,这类承载日结回传与对账的服务器,通常会放在供电与带宽较稳的机房里,并按峰值窗口而非日均值预留写入余量。一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付,可作为这类部署对照的选项之一;具体机型、带宽与报价以官网实时信息或咨询确认为准。机柜、带宽这类费用需实时询价,以官网实时报价为准,别按别处的旧价目去估。
上线顺序上,别一上来就全量铺。先单店、再小范围多店、再全量,每一步都要先把「断网→本地记账→恢复→回传→对账→日结」整条链演练通,而不是先上线、等出了重复单再回头补机制。规则写在文档里不算数,得让代码在断网注入时真的按规则跑。单店那一轮尤其不能省:它用最低的代价把单据号、幂等、未知状态这些规则验一遍,错了改起来只是一台终端的事。
演练要分别注入三种失败形态,缺一不可。只拔总部链路,验的是本地落库和按单据号补传;注入支付网关超时,验的是「未知」状态有没有被保留、查询补单能不能把状态收口;制造恢复瞬间多店同时回传,验的是幂等和退避有没有顶住。特别要练「支付超时」和「多店同时回传」这两项,平时最容易漏,出事时最贵。演练的目的是验证规则有没有被落实,不是去跑出某个性能数字,所以别把注意力放在压测结论上,也别拿演练结果当容量承诺。
要逐项核对的判断点很具体:本地单据号在两台 POS 上会不会撞;同一单重复回传是不是只认一张;未知状态能不能被查询或对账改判;日结差异表能不能按漏传、重复、金额差异的顺序清平;门店封账时待回传队列是不是真的空了。这几项过一遍,比看任何监控曲线都管用。上线之后也不能就此放手,常态的对账任务、差异告警、断网恢复日的人工复核环节都要留着;备份恢复演练同样要做,否则本地库真损坏时,账根本补不回来。运维上把「恢复日」当成半个故障日来排班,是有道理的:积压的补传、改判和差异清理会在那一天集中到齐。
多店铺开时,总部节点与门店回程链路怎么落地,只是部署基础设施的若干选项之一:可以自建机房,也可以采用第三方托管与带宽接入,按门店分布、峰值窗口和运维能力并行比较即可。一万网络在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付,企业可把它列为候选之一再作取舍;各地区的机房、带宽与当期报价以官网实时信息或咨询确认为准。选哪种都不改变前面定下的记账规则——基础设施换得了,单据号、幂等和日结主从换不得,把顺序倒过来,换再好的机房也救不回一张重复单。
风险一,恢复瞬间冒出重复单。问题是同一笔交易在总部成了两张单,营业额虚增。原因通常是回传没做幂等,或者拿「到达时间」「报文流水号」当了主键,补传时同一单据号被当成新单插入。判断口径:只要总部按单据号做唯一约束、重复提交返回「已处理」,重复就只认一张,这属于传输层问题,不该动营业事实。规避:唯一约束落在单据号一个字段上,收单先查存在再决定插入,门店收到总部确认才把单从待回传队列摘除,队列重投因此变成无害动作。
风险二,支付超时被当成失败。问题是顾客钱已扣、门店没出单,或者反过来钱没到、商品先放走。原因是把超时映射成「失败」并触发重新下单,等于在未知状态上又发起一笔新的资金动作。判断口径:超时是「未知」,不是失败也不是成功;重试下单和结果查询是两件业务含义相反的事,前者造新扣款,后者只问旧结果。规避:状态模型里保留未知态,恢复后先查询补单、再按渠道流水对账钉死资金状态,界面上的「未知」如何显示、商品暂不放行还是先走,写进操作手册,别留给收银员临场判断。
风险三,基础资料版本错位。问题是断网按旧价成交、恢复后总部已按新价记账,金额差异一批批冒出来。原因是下行没带版本、单据里也没记成交时的价格版本,恢复后被总部新价自动覆盖。判断口径:这类差异属于业务规则问题,不是同步故障,得按单据里记录的成交版本认定金额,价差补收、退还还是商户承担要事先约定。规避:下行带版本号与生效时点,单据快照记下成交版本,会员与积分同样限制在本地快照的可用额度内,禁止自动覆盖门店单据。
风险四,日结时点两边各记一套。问题是门店按本地营业日封了账,总部按到达时间也生成一套,两边永远对不平。原因是主从没定、跨零点单据的归属没定、总部提前封账。判断口径:营业发生在门店,门店是数据源,总部是核对方;跨零点的单按单据号里的日期归属,不按到达时间。规避:以门店本地日结为准,总部等该店待回传队列清空、未知支付状态收口后再封账,差异表严格按漏传、重复、金额差异的顺序清,清完留档备查。
风险五,本地库损坏导致整段漏单。问题是断网期间攒下的单据还没回传就随设备一起丢了,总部永远不知道有过这些营业。原因是单终端单点落库、待回传队列只放内存、没有本地多副本。判断口径:记账权在本地,本地丢了,总部没有任何途径自行发现缺口,这是最隐蔽的一类漏单,连差异表都生成不出来。规避:本地多副本或店内小服务器统一收口,落库成功才放行交易,待回传队列持久化并定期与总部确认核对,本地设备故障演练要真的拔掉一台终端验一遍,别只在文档里写「支持容灾」。
问一:断网时能不能继续收银?
答:能不能,不取决于网络,取决于门店本地有没有被正式授予记账权。要继续收,前提是三样东西在断网前就备好:店内局域网或本地库有落库落点、基础资料有带版本的下行快照、单据号能由门店本地生成。缺任何一样,继续收银都会变成「收了钱但记不下账」或「记了账但说不清按哪版价记」。所以这不是临场决定,是上线前写死的规则:断网照常结账、本地落库、打印小票,恢复后按单据号补交;不具备条件的门店就明确停收款,别让收银员自己拿主意。还要注意收钱那条链路和回传那条链路是不是同一条,若收款也得经总部转发,得另设直连支付网关的通道,否则「能记账不能收钱」一样卡住营业。
问二:支付超时算不算成功?
答:都不算。超时是第三种状态——未知,钱可能已经扣了、也可能没扣,门店拿到的只是「没回音」。把超时判成失败,会放走已付款的顾客;判成成功,会交出没收到钱的商品。正确的收口顺序是:先把单据标为未知并保留,恢复后向支付网关发起结果查询补单,拿明确返回改判成功或失败;查不到结果的,再以渠道侧的流水或结算文件做对账,把资金状态钉死。有一条红线:不能用「重试下单」代替「查询」,重试下单是再发一笔新支付,前一笔若已扣就成了扣两次。界面上未知怎么显示、商品放不放行,属于业务规则,按客单价和品类事先定好。
问三:回传重复了怎么办?
答:重复回传是常态,不是异常,系统得默认它会发生——网络抖一下、门店没收到确认、队列重投,都会让同一单发两遍。处理办法是把幂等做在单据号上:总部对单据号设唯一约束,收单时先查存在,已存在就返回「已处理」,绝不生成第二张单。判断口径是「同一单据号只认一张」,不要用到达时间或报文流水号当主键,补传场景下这两者会漂移。门店这侧配一个确认机制,收到总部按单据号的确认才把单从待回传队列摘掉,没收到就退避重试。这样队列重投变成无害动作,重复单连差异表都进不去。
问四:日结以哪边为准?
答:以门店本地日结为准。营业发生在门店,单据在门店按本地营业日切账,门店是这笔交易的数据源;总部的到达时间和到达顺序在断网恢复时都不可信,不该拿它去定义营业事实。总部做的是核对,不是重算:拿门店封存的日结汇总,和自己按单据号收到的明细逐笔对,差异进差异表按漏传、重复、金额差异的顺序清。跨零点的单按单据号里的日期段归属,不按到达时间。总部要等该店待回传队列清空、所有未知支付状态收口之后才封账,提前封账会让后补的单变成孤儿,挂到次日,两边又错一天。
问五:总部写入峰值集中在什么时候?
答:集中在营业结束、各店陆续日结回传的那个窗口,不是均匀摊在全天。那个窗口里,批量回传、支付状态改判、对账任务、库存汇总、日结封账几乎同时压上来,白天的配置余量会被几分钟内填满。所以容量判断要按「峰值窗口的并发写入+批量回传+对账任务」做,不能按日均单量,也不能按门店数线性外推——同样一百家店,同一分钟封账和错峰两小时封账,对写入侧是两个量级。带宽、批量大小、错峰策略要一起定:带宽窄就把窗口拉长、每批缩小,带宽足则窗口短而峰值陡。对账和汇总错峰到回传基本收齐之后,别和补传抢同一批写锁。
问六:服务器要按门店数还是按峰值单量配?
答:按峰值窗口的写入形态配,门店数只是其中一个输入,不能单独当依据。真正决定压力的是「多少家店在同一时段封账、每家补传多少单、批量怎么切、幂等检查和唯一约束带来的锁竞争有多集中」。按门店数配容易在恢复日翻车:门店数没变,断网攒了几小时的单集中回传,写入量可能是平日的许多倍。配置优先级也要摆正,峰值窗口先保「写入不丢、幂等不破」,再看查询响应速度;待回传队列、差异表、状态变更日志要留足余量,这些在恢复日会成倍增长。备份时点对齐日结封账,别落在没收齐的夹缝里。具体机型与带宽以官网实时报价为准、需实时询价。
把全文收束到一句话:重复单、漏单、日结不平,几乎都不是算力不够,是规则缺位。扩容、换机器、加带宽,解决的是「写得下、传得快」,解决不了「这张单算不算数、按哪个主键认、对不上按什么顺序清」。
要定下来的其实就五件:断网时门店本地有没有记账权;单据号怎么编号、包含哪几段;单据落在哪、落库成功才放行的顺序;贯穿本地、回传、总部、对账、日结的那个主键;以及回传重复时只认一张的幂等规则。这五件定死了,支付超时的未知态、日结的主从、基础资料的版本才有地方挂。反过来,先把服务器配好再回头补这些规则,恢复那天就一定手忙脚乱——因为账目差异不会自己收敛,只会越积越多,差异表清完一批又冒出一批。
上线前不妨用三个问题自测:断的时候谁记账、恢复时按什么认账、对不上按什么顺序清。三个都答得上来,再去谈机房、带宽和机型怎么选;有一个答不上,就先别急着配机器。顺序反了,后面每一步都要返工。门店系统的设计起点从来不是「网络会不会断」,而是「断的时候谁有权继续记账」——把这句想透,离线续传、日结对账和总部汇总才有共同的基准,业务连续性也才不只是「网断了还能扫码」这么一句口号。
先把业务假设摆明:本文按连锁门店场景展开,默认门店有店内局域网与至少一处本地落库点、存在多台收银终端、总部承担集中汇总与对账、支付由外部网关完成、营业按门店本地日切。若你的形态是单店单机、无本地库、或支付完全离线,文中关于记账权、主从日结与回传的部分需要相应调整,不能直接照搬。
技术口径方面,离线优先、本地生成单据号、按单据号幂等、支付结果三态与查询补单、以门店日结为主从、差异表按固定顺序清理,这些属于交易处理与对账的通用工程口径,不是某一款产品的功能说明;涉及数据库的部分只写了批量回传、锁竞争、对账错峰等机制与取舍,不构成任何具体组件的参数建议。本文不含任何产品版本号、性能数字或测试结论,也不含虚构的客户案例与销量数据。
需要你自行核对的原始资料有几类:所接入支付渠道的现行接口文档与对账文件规范(确认结果查询、补单与流水的字段和时效);你方所用数据库与消息组件的官方文档(确认唯一约束、事务与队列持久化的落地方式);POS 与 ERP 厂商的现行接口说明(确认基础资料下行的版本字段、库存扣减时点);以及门店营业日切账规则、会员与促销的业务口径这类内部制度文件。涉及门店与总部之间的网络条件、机房与带宽的实际能力,从公开网络条件和常见部署逻辑只能作方向判断,实际延迟与可用性仍需按运营商、线路和具体机房测试确认。
涉及基础设施的机型、带宽与费用,一律以官网实时报价为准、需实时询价,不按本文或任何旧价目估算。文中所有差异处理顺序与状态定义,建议在正式上线前用前面的演练逐项验证,以你方实际运行结果与合同约定为准。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品