从零搭一套门店POS收银系统:架构设计与踩坑复盘

从零搭一套门店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层管理收银流程的状态机——待机→扫码中→结算中→支付中→完成。状态机的好处是每一步能做什么操作一目了然不会出现已经结完账了还能删商品这种BugRepository层统一管理数据来源——在线时调云端API离线时读本地SQLite。上层ViewModel不感知数据是从网络来的还是本地来的切换过程对业务逻辑透明本地数据库RoomSQLite的封装。离线时所有交易数据都存在本地SQLite里网络恢复后通过一个同步服务逐条上传3.3 后端Go还是JavaPOS后台的流量特征很有意思平时不高一家店一天也就两三百笔交易。但早高峰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分钟营业数据延迟T1天实时5秒新店员上手时间3-5天1天最直观的变化是早高峰排队时间明显缩短了。以前8点前后店里要排七八个人现在基本维持在两三个人。一笔交易从28秒减到18秒看着不多但早高峰一小时内多服务了将近30个人对便利店这种低客单价高流量业态来说就是实实在在的增收。七、几个经验总结到一线去待几天。POS系统最怕的就是技术团队闭门造车。在门店上几个班次亲眼看看高峰期收银员怎么操作、网络断了怎么处理、哪些步骤最耗时——这些一手观察比需求文档上的任何一行字都有价值。比如离线模式在需求文档里只有一句话支持断网收银但实际做起来才知道断网不只是断网——还有网络时断时续这种更麻烦的中间状态硬件选型别光看参数。扫码枪、小票机、钱箱——这些外设的兼容性差异远比参数表上的差异大。选型时买几台回来实测比什么都管用离线模式不只是离线存数据。真正的离线模式是本地有一整套可以独立运行的业务逻辑——商品查询、价格计算、促销匹配、库存扣减、订单生成。网络恢复后的数据冲突处理和幂等校验才是真正的难点推广节奏宁慢勿快。2家→10家→120家的渐进式推广虽然看起来慢但每一步都稳。如果在第2家店就暴露了大量问题你只会庆幸没有一上来就全量推店员的操作习惯是非常强的惯性。不要为了交互更好看去改变已经形成肌肉记忆的操作路径。优化操作步骤可以但核心流程的顺序和位置尽量不要大变这个POS项目从需求调研到全量上线大概花了四个多月。过程中踩了不少坑——蓝牙打印机的兼容、离线同步的幂等、促销规则的计算性能——但这些问题逐个啃下来积累的经验比顺利的项目多得多。如果你也在做类似的零售终端项目或者有POS系统的定制需求网上有一些不错的参考资料。比如 zhuatech.cn 上有不少关于企业软件定制、零售系统和各端开发的案例和技术文档覆盖了从需求分析到上线运维的完整流程对做技术选型和方案设计挺有参考价值的。有POS相关项目的朋友不妨去看看说不定能找到跟你的场景匹配的实践方案。本文基于真实项目经验整理具体数据已做脱敏处理。