技术摘要
本文从系统架构视角拆解链饷购B2R模式中"门店端操作系统"的设计。重点不在上游供应链分润,而在下游门店侧:品牌如何通过一套SAAS系统把300-1000家门店的进货、陈列、动销、店员激励管起来。文章给出门店SAAS的四层架构(门店端APP、店员任务引擎、动销数据看板、品牌运营后台),重点设计店员任务状态机、门店库存实时同步、短链溯源核销三个模块,并讨论多门店并发下的数据一致性与店员权限边界。
背景与痛点
大家好,我是微三云生态系统架构师彭丹,每天带你洞察行业新风口,拆解爆款新模式。
B2R模式的上游逻辑大家都讲得很多:品牌直供门店,砍掉省代市代,流通成本再分配。但真正决定B2R能不能跑通的,往往是下游门店这一公里------品牌给门店供了货,门店老板愿不愿意把货摆出来?店员愿不愿意主动推荐?动销数据能不能实时回传?
链饷购2.0提出下半年300家、冲刺1000家门店的目标。这个量级靠人工督导根本管不过来,必须靠一套门店SAAS系统把终端运营数字化。技术上要解决四件事:
- 门店老板在自己手机上能完成下单、收货、库存盘点、对账;
- 店员有任务、有激励,不是被动等老板安排;
- 品牌方能实时看到每家店的动销、库存、缺货预警;
- 短链溯源------每瓶酒从品牌仓库到消费者手里,节点可查。
系统架构设计
整体采用"四端四层"架构:
#mermaid-svg-LlRYmx2TJioB7jkC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LlRYmx2TJioB7jkC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LlRYmx2TJioB7jkC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LlRYmx2TJioB7jkC .error-icon{fill:#552222;}#mermaid-svg-LlRYmx2TJioB7jkC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LlRYmx2TJioB7jkC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LlRYmx2TJioB7jkC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LlRYmx2TJioB7jkC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LlRYmx2TJioB7jkC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LlRYmx2TJioB7jkC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LlRYmx2TJioB7jkC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LlRYmx2TJioB7jkC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LlRYmx2TJioB7jkC .marker.cross{stroke:#333333;}#mermaid-svg-LlRYmx2TJioB7jkC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LlRYmx2TJioB7jkC p{margin:0;}#mermaid-svg-LlRYmx2TJioB7jkC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LlRYmx2TJioB7jkC .cluster-label text{fill:#333;}#mermaid-svg-LlRYmx2TJioB7jkC .cluster-label span{color:#333;}#mermaid-svg-LlRYmx2TJioB7jkC .cluster-label span p{background-color:transparent;}#mermaid-svg-LlRYmx2TJioB7jkC .label text,#mermaid-svg-LlRYmx2TJioB7jkC span{fill:#333;color:#333;}#mermaid-svg-LlRYmx2TJioB7jkC .node rect,#mermaid-svg-LlRYmx2TJioB7jkC .node circle,#mermaid-svg-LlRYmx2TJioB7jkC .node ellipse,#mermaid-svg-LlRYmx2TJioB7jkC .node polygon,#mermaid-svg-LlRYmx2TJioB7jkC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LlRYmx2TJioB7jkC .rough-node .label text,#mermaid-svg-LlRYmx2TJioB7jkC .node .label text,#mermaid-svg-LlRYmx2TJioB7jkC .image-shape .label,#mermaid-svg-LlRYmx2TJioB7jkC .icon-shape .label{text-anchor:middle;}#mermaid-svg-LlRYmx2TJioB7jkC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LlRYmx2TJioB7jkC .rough-node .label,#mermaid-svg-LlRYmx2TJioB7jkC .node .label,#mermaid-svg-LlRYmx2TJioB7jkC .image-shape .label,#mermaid-svg-LlRYmx2TJioB7jkC .icon-shape .label{text-align:center;}#mermaid-svg-LlRYmx2TJioB7jkC .node.clickable{cursor:pointer;}#mermaid-svg-LlRYmx2TJioB7jkC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LlRYmx2TJioB7jkC .arrowheadPath{fill:#333333;}#mermaid-svg-LlRYmx2TJioB7jkC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LlRYmx2TJioB7jkC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LlRYmx2TJioB7jkC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LlRYmx2TJioB7jkC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LlRYmx2TJioB7jkC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LlRYmx2TJioB7jkC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LlRYmx2TJioB7jkC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LlRYmx2TJioB7jkC .cluster text{fill:#333;}#mermaid-svg-LlRYmx2TJioB7jkC .cluster span{color:#333;}#mermaid-svg-LlRYmx2TJioB7jkC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LlRYmx2TJioB7jkC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LlRYmx2TJioB7jkC rect.text{fill:none;stroke-width:0;}#mermaid-svg-LlRYmx2TJioB7jkC .icon-shape,#mermaid-svg-LlRYmx2TJioB7jkC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LlRYmx2TJioB7jkC .icon-shape p,#mermaid-svg-LlRYmx2TJioB7jkC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LlRYmx2TJioB7jkC .icon-shape .label rect,#mermaid-svg-LlRYmx2TJioB7jkC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LlRYmx2TJioB7jkC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LlRYmx2TJioB7jkC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LlRYmx2TJioB7jkC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 四层
四端
品牌运营后台
门店老板APP
店员小程序
消费者扫码页
订单与库存层
任务与激励层
动销数据层
短链溯源层
核心模块划分:
| 模块 | 职责 |
|---|---|
| 门店端APP | 门店老板下单、收货、盘点、对账 |
| 店员任务引擎 | 给店员下发陈列/推荐/开瓶任务,核销后发激励 |
| 动销数据看板 | 品牌方看单店/区域/品类动销,缺货预警 |
| 短链溯源 | 每瓶商品唯一码,扫码即查流通路径 |
技术选型:门店端用小程序(免安装、微信生态),品牌后台用Web管理端,数据层用MySQL+Redis缓存动销看板,短链溯源用区块链存证哈希(上链只存哈希不存原文,降低成本)。
核心模块实现
模块一:门店库存实时同步
门店老板收货后,系统把商品入库;消费者在门店扫码消费,系统自动扣库存。数据结构:
sql
CREATE TABLE store_inventory (
store_id BIGINT,
sku_id BIGINT,
batch_no VARCHAR(64),
qty_available INT,
qty_locked INT,
updated_at DATETIME,
PRIMARY KEY (store_id, sku_id, batch_no)
);
关键问题:门店端可能离线(地下室、信号差),店员盘点和销售动作需要本地暂存,联网后再同步。设计上采用本地SQLite+服务端乐观锁:
- 门店APP本地写一份待同步操作队列;
- 联网后批量上报,服务端按
(store_id, sku_id, batch_no)做CAS更新; - 冲突时(比如两个店员同时盘点同一SKU),以后提交者为准并产生一条差异记录,老板端APP弹提醒。
伪代码:
python
def sync_inventory(store_id, ops):
for op in ops:
cas_sql = """
UPDATE store_inventory
SET qty_available = qty_available + %s,
updated_at = NOW()
WHERE store_id=%s AND sku_id=%s AND batch_no=%s
AND updated_at = %s
"""
affected = db.execute(cas_sql, op.delta, store_id, op.sku, op.batch, op.client_ver)
if affected == 0:
# 版本冲突,拉最新库存生成差异单
db.save_conflict(op)
模块二:店员任务状态机
店员不是雇员,是门店老板自己的员工或兼职。品牌通过SAAS给店员下发任务,店员完成后扫码核销,品牌直接发激励到店员微信。任务状态机:
text
ASSIGNED -> ACCEPTED -> EXECUTING -> SUBMITTED -> APPROVED -> REWARDED
-> REJECTED -> RESUBMIT
任务类型举例:
| 任务类型 | 核销方式 | 激励 |
|---|---|---|
| 陈列拍照 | 上传货架照片,AI识别商品是否在排面 | 5元/店/次 |
| 开瓶扫码 | 消费者开瓶后扫瓶身码,店员拿激励 | 8元/瓶 |
| 新客首单 | 新客扫码下单,关联店员ID | 3元/单 |
关键设计:店员激励由品牌直接发,不经过门店老板账户,避免老板截留。店员每次核销都需要上传照片或扫码动作,系统做图像识别校验(防止拿旧照片重复交任务)。
模块三:动销数据看板
品牌方最关心的不是"我发了多少货",而是"门店实际卖了多少"。看板按三个维度聚合:
text
门店维度:单店日动销、库存周转天数、缺货率
区域维度:城市/大区汇总,区域经理KPI
品类维度:SKU动销排名,滞销品预警
数据来源:
- 门店APP的销售上报(小程序码收款自动关联);
- 店员开瓶扫码;
- 消费者扫码溯源时的地理位置回传。
看板用Redis预计算小时级聚合,MySQL存明细。门店老板看到自己店的数据,品牌运营看全量,区域经理看辖区------通过数据权限层做行级过滤。
模块四:短链溯源核销
每瓶商品出厂时贴唯一码,码和批次、门店绑定。消费者扫码后看到:
- 这瓶酒从哪个仓发出来;
- 经过哪家门店;
- 是不是首次扫码(防窜货)。
text
trace_code -> {
factory_batch: "B20260918-001",
warehouse: "长沙中心仓",
store_id: 88231,
first_scan_time: "2026-09-19 20:34:11",
scan_latlng: [28.2, 112.9]
}
如果二次扫码的地理位置和首次扫码的城市不一致,系统判为疑似窜货,自动告警给品牌方。这是B2R模式防止"门店把货窜到价低区域"的关键技术手段。
风控与边界
合规设计:
- 店员激励由品牌直接打款到店员个人微信,按劳务报酬代扣个税;
- 门店与品牌签购销合同,货权转移发生在门店收货确认时;
- 短链溯源数据只用于防窜货和质量追踪,不做消费者画像外泄。
异常处理:
- 门店离线操作:本地队列最多存7天,超期未同步的数据标记为脏数据,要求老板重新盘点;
- 店员刷单:同一店员一天提交超过20次陈列任务,或照片EXIF时间异常,自动拦截;
- 窜货告警:人工复核后再处罚门店,避免地理位置漂移误判。
性能边界: 1000家门店、单店日均销售20单,日订单量约2万,动销看板分钟级刷新可支撑。上到1万家门店时,看板层需要引入ClickHouse做OLAP。
适用与不适用:
- 适合:高客单价、有陈列要求、有防窜货需求的酒水、大健康、美妆品牌;
- 不适合:低客单价快消(单瓶激励覆盖不了SAAS部署成本)、完全走线上无门店体验的品类。
总结与展望
链饷购B2R门店SAAS的技术核心,是把"品牌管门店"这件事从人治变成系统治。门店库存同步解决离线场景,店员任务引擎解决终端积极性,动销看板解决品牌数据可见,短链溯源解决窜货和假货问题。
落地建议:先选30-50家种子门店跑通任务核销和动销看板,再批量扩张。1000家门店规模下,SAAS系统的稳定性和店员体验直接决定品牌方愿不愿意续费。在东莞做零售SAAS系统开发的团队,门店终端操作系统是B2R赛道的核心资产,建议把任务引擎和短链溯源产品化,作为可复用模块输出给不同品牌方。
📌 含AI辅助内容
本文部分内容由AI辅助整理优化,技术方案仅供参考,实际落地请结合业务场景评估。
#链饷购模式 #B2R门店SAAS #店员任务引擎 #短链溯源 #动销数据看板 #门店库存同步 #终端操作系统