从零搭一套门店POS收银系统:架构设计与踩坑复盘
去年给一个连锁便利店品牌从零搭了一套POS收银系统,覆盖收银、库存、会员、促销和离线模式。这篇文章把整个过程的技术决策和踩坑记录整理出来。

一、背景

去年接了一个连锁便利店的项目。客户当时全国大概120家门店,用的是一套老旧的Windows收银软件,功能倒是够用,但几个硬伤越来越严重:

  • 软件只支持Windows,每台收银机还得配一台台式机或者厚重的Windows平板。硬件成本高不说,启动慢、容易死机、IT维护成本也大
  • 断网就瘫痪。便利店偶尔会遇到网络故障,老系统断网后连基本收银都做不了
  • 会员和促销完全靠人工。收银员要记住哪些商品在打折、哪个会员到了升级门槛、优惠券能不能叠加用——记错了就是少收钱或多收钱
  • 数据不同步。总部想看实时销售数据,靠的是每台收银机每天收班后自动上传一次汇总。周五的数据可能周一才看到
  • 第三方接口对接困难。接入新的支付方式(刷脸支付、数字人民币)、对接外卖平台(美团、饿了么)、接通电子发票——每加一个功能都像打补丁

老板的需求很直接:能不能重新搞一套POS?要快、要稳、要便宜、要断网也能用。最好跑在安卓平板上,硬件成本能省一大笔。

这个需求描述听着简单,实际隐含了三个技术目标:低硬件成本(安卓)、高可用(离线可用)、可扩展(方便对接新渠道)。接下来就是四个月的设计、开发和试点推广。

二、需求怎么梳理的

POS系统看着就是个收银工具,但真正做起来发现触角伸得到处都是。我花了两周泡在门店里,跟着店员上了几个班次,把整个收银流程从开门到打烊走了一遍。很多细节不在门店站一天是发现不了的:

  • 早高峰(7:30-9:00)客流量是平时的3倍,收银员根本没时间看屏幕提示,全程靠肌肉记忆操作。所以操作路径必须比原来的系统更短,不能多出任何一步
  • 熟客买烟会说"老规矩",收银员要知道他平时抽哪个牌子、什么价位。换一个收银员就不知道了
  • 晚班收银员一个人值班,同时要收银、补货、搞卫生、接外卖订单。系统要能在这些任务间快速切换
  • 下雨天网络特别差。店在一个老小区底层,4G信号本来就不稳,雨天更差。离线模式不是"锦上添花",是必须品

收集完这些一线反馈后,跟总部管理层对齐了业务目标。最终确定下来的核心功能清单大概是:

模块核心功能
收银商品扫码/搜码、挂单/取单、整单取消/单品退货、多种支付方式组合、小票打印、日结交班
库存实时扣减、安全库存预警、效期管理(短保商品到期提醒)、盘点(支持扫码枪逐品盘点)
会员手机号快速查询、积分累计/抵扣、优惠券核销、会员等级自动升降、储值卡消费
促销满减/满折/买赠/第二件半价、时段特价(早市/晚市)、会员价/普通价双价格、优惠互斥规则、促销活动定时生效/失效
离线断网时正常收银、支持现金和扫码支付(扫码支付需等网络恢复后补扣)、网络恢复后自动同步订单、库存和会员数据
外卖对接美团/饿了么,自动接单、语音播报、自动打印小票、库存同步(线上售完自动下架)

三、技术方案选型

3.1 硬件:为什么选了Android平板

老系统跑在Windows上,换新系统第一个决策就是硬件平台。Windows台式机一台三千多,Windows平板要四五千。120家店,每家两台收银机,光硬件就要近百万。而且Windows设备启动慢、功耗高、触摸体验差。

Android商用平板这两年成熟了很多,一台两千出头就能买到不错的配置(8核CPU、4GB内存、64GB存储)。自带扫码摄像头(省去了外接扫码枪的钱)、支持NFC(刷脸支付设备也省了)、可以接蓝牙小票打印机。最关键的是店员对安卓操作没有学习门槛——跟我们用手机一样。

最终选的设备是一线品牌的商用Android平板,配一个蓝牙小票打印机和一个钱箱。单店硬件成本从五千多降到了不到三千。

3.2 客户端:原生Android还是跨端方案

POS客户端对响应速度要求极高——扫码后0.3秒内必须显示出商品信息,慢了收银员就会不耐烦。而且需要深度调用硬件——摄像头扫码、蓝牙打印、NFC读卡。这些对原生Android来说都是标准API,用跨端框架反而要多一层桥接。

所以客户端用Kotlin写的原生Android应用。架构用了MVVM + Repository模式:

  • View层:Jetpack Compose写的UI。POS界面元素密集——商品列表、分类标签、金额汇总、会员信息——Compose的声明式写法比XML维护起来舒服很多
  • ViewModel层:管理收银流程的状态机——待机→扫码中→结算中→支付中→完成。状态机的好处是每一步能做什么操作一目了然,不会出现"已经结完账了还能删商品"这种Bug
  • Repository层:统一管理数据来源——在线时调云端API,离线时读本地SQLite。上层ViewModel不感知数据是从网络来的还是本地来的,切换过程对业务逻辑透明
  • 本地数据库:Room(SQLite的封装)。离线时所有交易数据都存在本地SQLite里,网络恢复后通过一个同步服务逐条上传

3.3 后端:Go还是Java

POS后台的流量特征很有意思:平时不高,一家店一天也就两三百笔交易。但早高峰8:00-9:00这一个小时间,120家店同时收银,并发量集中在很窄的窗口里。而且每笔交易的响应时间直接决定收银员的等待时间——超过1秒就开始焦躁。

最终选了Go + Gin框架。主要考量是Go的并发模型(goroutine)在处理这种短时间高并发的场景下资源消耗更可控。数据库用PostgreSQL,订单表按门店ID做了分区,把120家店的数据分散到不同的物理分区上,避免热点竞争。

缓存用Redis,存了两类数据:商品信息(价格、规格、促销标签——这些变化不频繁但读取量极大),和会员信息(积分、等级、优惠券列表——每笔交易都可能要查)。商品信息的缓存策略是写时更新——后台改了价格主动刷新缓存,不依赖过期淘汰。

3.4 离线方案:最难啃的骨头

离线模式是整个项目里技术难度最高、也最决定成败的功能。便利店不像大商超——网络条件参差不齐,有些店在老小区底商、有些在商场负一层、有些在地铁站内。断网是常态不是意外。

离线方案的核心设计思路是本地优先(Local-First)

  • 商品数据全量本地化:每家店的商品SKU数量在2000-5000个,JSON格式全量存下来不到5MB。每天早上开店时同步一次增量更新,之后全天靠本地数据收银。断网不影响商品查询和价格计算
  • 促销规则本地计算:满减、折扣、买赠这些促销逻辑没有放在服务端,而是以规则配置的形式下发到客户端。客户端有一个轻量的规则引擎,根据本地的商品数据和会员信息直接在平板上算出优惠金额。断网时促销照样生效
  • 交易数据本地暂存:断网期间的每一笔交易都完整记录在本地SQLite里,包括订单详情、支付方式、会员信息、优惠明细。网络恢复后通过一个增量同步服务逐条上传到云端,上传成功后才标记为已同步
  • 库存本地扣减:断网时销售的商品会先从本地库存表里扣减,恢复网络后再跟云端库存做一次对账。如果出现了断网期间云端被其他渠道(外卖平台)卖掉的冲突,以云端为准并自动更新本地库存
  • 支付兜底:现金支付断网不受影响。微信/支付宝扫码支付需要网络——断网时收银员可以选择"离线收款",系统生成一条待处理记录,恢复网络后自动向支付渠道发起扣款。如果扣款失败(余额不足等),标记为异常订单并通知店长处理

上线后发现一个意想不到的问题:有些店网络断断续续——一会连上一会断——导致同步服务频繁切换状态,产生了少量重复订单。后来加了一个基于订单号的幂等校验:云端收到同步请求时先查一下这个订单号是否已经存在,存在就跳过。问题解决。

四、踩过的坑

4.1 扫码枪和摄像头扫码的差距

一开始我们想省钱——用平板的摄像头扫码,省去外接扫码枪。测试环境下没啥问题,但门店实际用起来发现:光线暗的时候识别慢,条码污损时基本识别不了,连续扫码时摄像头对焦跟不上。收银员扫三个商品就要等一秒对焦,积少成多,高峰期排队就变长了。

后来还是给每家店配了蓝牙扫码枪。贵了两三百块,但扫码速度从平均1.2秒降到了0.2秒。这个钱不该省。

4.2 促销规则引擎的性能陷阱

有一个周末促销活动:全场满88减15,零食类满50减8,饮料类第二件半价,会员再享95折,新会员首单减10。这五条规则同时生效,而且互有叠加和互斥关系。第一版规则引擎在处理这种复杂促销时,每加一个商品进购物车都要重新计算整单的优惠——一个10件商品的订单要算50次规则匹配。

优化方案:规则匹配结果做缓存。购物车里商品不变的情况下,促销计算结果不变。只有商品增删或数量变化时才重新计算。同时把规则引擎的计算跑在后台线程,不阻塞主线程的UI渲染。优化后结算页的响应时间从800ms降到了150ms。

4.3 小票打印机的蓝牙兼容性

市面上常见的蓝牙热敏小票打印机有十几个品牌,每家厂商的蓝牙通信协议微有差异——有的走SPP、有的走BLE、有的同时支持但实现有Bug。我们买了六款主流型号回来逐个适配,发现其中一款在连续打印三张以上小票后会随机断开连接。

最终方案:设备选型时只推荐两款我们充分测试过的型号;打印模块加了自动重连机制——检测到蓝牙断开后自动尝试重连,最多重试三次;重连失败则弹窗提示店员手动检查打印机。

五、试点推行的节奏

技术团队的习惯是一开发完就想全量推。但POS系统直接关系到门店收银——出了Bug就是少收钱、多收钱、或者收不了钱。任何一种情况都是严重事故。所以推得很谨慎:

  • 第1-2周:选2家店试点,新老系统并行(新系统收银、老系统备着)。每天收集店员反馈
  • 第3-4周:扩展到10家店,修复了试点阶段暴露的几十个问题——主要是操作路径上的细节调整和个别边缘机型的兼容问题
  • 第5-8周:推广到全部120家店。每家店安排一天现场培训+跟班,培训重点是离线模式怎么用和促销规则怎么看
  • 第9-12周:远程支持+bug修复。大部分问题通过远程日志诊断解决,硬件问题转本地IT处理

店员对系统的接受度比预期好。主要原因是操作流程比老系统短了——比如会员查找,以前要切到一个独立页面输入手机号,现在直接在收银主界面上方有一个常驻搜索框,输手机号后三位就能匹配。一个高频操作省了两步点击,店员的感知非常直接。

六、上线后的效果

上线三个月后拉了几个关键数据:

维度老系统新系统
单笔交易平均耗时28秒18秒
断网可用性不可用完全可用
单店硬件成本~5500元~2800元
促销规则生效时间总部下发→店长手动设置(约半天)总后台配置→自动同步(约5分钟)
营业数据延迟T+1天实时(<5秒)
新店员上手时间3-5天1天

最直观的变化是早高峰排队时间明显缩短了。以前8点前后店里要排七八个人,现在基本维持在两三个人。一笔交易从28秒减到18秒看着不多,但早高峰一小时内多服务了将近30个人,对便利店这种低客单价高流量业态来说,就是实实在在的增收。

七、几个经验总结

  1. 到一线去待几天。POS系统最怕的就是技术团队闭门造车。在门店上几个班次,亲眼看看高峰期收银员怎么操作、网络断了怎么处理、哪些步骤最耗时——这些一手观察比需求文档上的任何一行字都有价值。比如离线模式在需求文档里只有一句话"支持断网收银",但实际做起来才知道断网不只是断网——还有"网络时断时续"这种更麻烦的中间状态
  2. 硬件选型别光看参数。扫码枪、小票机、钱箱——这些外设的兼容性差异远比参数表上的差异大。选型时买几台回来实测比什么都管用
  3. 离线模式不只是"离线存数据"。真正的离线模式是本地有一整套可以独立运行的业务逻辑——商品查询、价格计算、促销匹配、库存扣减、订单生成。网络恢复后的数据冲突处理和幂等校验才是真正的难点
  4. 推广节奏宁慢勿快。2家→10家→120家的渐进式推广虽然看起来慢,但每一步都稳。如果在第2家店就暴露了大量问题,你只会庆幸没有一上来就全量推
  5. 店员的操作习惯是非常强的惯性。不要为了"交互更好看"去改变已经形成肌肉记忆的操作路径。优化操作步骤可以,但核心流程的顺序和位置尽量不要大变

这个POS项目从需求调研到全量上线大概花了四个多月。过程中踩了不少坑——蓝牙打印机的兼容、离线同步的幂等、促销规则的计算性能——但这些问题逐个啃下来,积累的经验比顺利的项目多得多。

如果你也在做类似的零售终端项目,或者有POS系统的定制需求,网上有一些不错的参考资料。比如 zhuatech.cn 上有不少关于企业软件定制、零售系统和各端开发的案例和技术文档,覆盖了从需求分析到上线运维的完整流程,对做技术选型和方案设计挺有参考价值的。有POS相关项目的朋友不妨去看看,说不定能找到跟你的场景匹配的实践方案。

本文基于真实项目经验整理,具体数据已做脱敏处理。