一、背景与问题定义

1.1 业务场景

传统小微零售商户(如便利店、小卖部)面临以下痛点:

  • 夜间人力成本高:22:00-次日8:00需专人值守,但订单量稀疏
  • 多平台运营繁琐:同时接入美团、饿了么、京东秒送等多个外卖平台,需多端切换
  • 订单履约效率低:骑手到店后需等待店主开门、找货、核验,平均耗时3-5分钟

1.2 技术目标

构建一套支持以下能力的系统:

能力

技术要求

多平台订单聚合

统一接入美团、饿了么、京东秒送、抖音小时达等平台

无人仓自动履约

骑手扫码开门→自助取货→系统自动核销订单

智能调度

多平台骑手比价呼叫,降低配送成本

数据可视化

多店销售数据统一分析


二、系统架构设计

2.1 整体架构图

三、核心模块技术实现

3.1 外卖平台聚合:多协议适配器模式

不同外卖平台的API协议差异较大,采用适配器模式统一接入:
 

/**
 * 外卖平台适配器接口
 */
public interface PlatformAdapter {
    /**
     * 拉取订单列表
     */
    List<Order> pullOrders(String shopId, LocalDateTime startTime);
    
    /**
     * 确认订单
     */
    boolean confirmOrder(String orderId);
    
    /**
     * 取消订单
     */
    boolean cancelOrder(String orderId, String reason);
    
    /**
     * 更新配送状态
     */
    boolean updateDeliveryStatus(String orderId, DeliveryStatus status);
}

/**
 * 美团平台适配器
 */
@Component
public class MeituanAdapter implements PlatformAdapter {
    
    @Override
    public List<Order> pullOrders(String shopId, LocalDateTime startTime) {
        // 1. 构造美团API请求
        MeituanOrderRequest request = new MeituanOrderRequest();
        request.setAppId(meituanConfig.getAppId());
        request.setShopId(shopId);
        request.setStartTime(startTime);
        
        // 2. 调用美团API
        String response = HttpUtil.post(
            meituanConfig.getOrderUrl(),
            signRequest(request)  // 美团签名算法
        );
        
        // 3. 解析并转换为统一订单模型
        MeituanOrderResponse mtResponse = JSON.parseObject(response, MeituanOrderResponse.class);
        return convertToUnifiedOrder(mtResponse);
    }
    
    private Order convertToUnifiedOrder(MeituanOrderResponse mtOrder) {
        Order order = new Order();
        order.setPlatform(Platform.MEITUAN);
        order.setOrderId(mtOrder.getOrderId());
        order.setShopId(mtOrder.getShopId());
        order.setItems(convertItems(mtOrder.getItems()));
        order.setTotalAmount(mtOrder.getTotalAmount());
        order.setDeliveryAddress(mtOrder.getAddress());
        order.setCreateTime(mtOrder.getCreateTime());
        return order;
    }
    
    // ... 其他方法实现
}

/**
 * 饿了么平台适配器
 */
@Component
public class ElemeAdapter implements PlatformAdapter {
    // 实现饿了么特有的签名算法和数据格式转换
    @Override
    public List<Order> pullOrders(String shopId, LocalDateTime startTime) {
        // 饿了么签名:HMAC-SHA256
        // 数据格式:XML/JSON 混合
        // ...
    }
}

关键技术点

平台

签名算法

数据格式

轮询频率

美团

MD5

JSON

30秒

饿了么

HMAC-SHA256

XML/JSON

30秒

京东秒送

RSA

JSON

60秒

抖音小时达

HMAC-SHA256

JSON

60秒


3.2 无人仓履约:状态机驱动的自动化流程

无人仓的核心是订单状态流转硬件联动
 

/**
 * 订单状态枚举
 */
public enum OrderStatus {
    PENDING,          // 待接单
    CONFIRMED,        // 已确认
    PREPARING,        // 备货中
    WAITING_PICKUP,   // 待取货(骑手已到店)
    PICKING,          // 取货中(门已开)
    COMPLETED,        // 已完成
    CANCELLED         // 已取消
}

/**
 * 无人仓履约状态机
 */
@Component
public class WarehouseStateMachine {
    
    @Autowired
    private SmartLockService lockService;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Autowired
    private SmsService smsService;
    
    /**
     * 骑手扫码开门
     */
    public boolean openDoor(String orderId, String riderPhone) {
        Order order = orderRepository.findById(orderId);
        
        // 1. 验证订单状态
        if (order.getStatus() != OrderStatus.WAITING_PICKUP) {
            throw new BusinessException("订单状态异常,无法开门");
        }
        
        // 2. 验证骑手身份
        if (!verifyRider(order, riderPhone)) {
            throw new BusinessException("骑手身份验证失败");
        }
        
        // 3. 开门并更新状态
        boolean opened = lockService.openLock(order.getShopId());
        if (opened) {
            order.setStatus(OrderStatus.PICKING);
            order.setDoorOpenTime(LocalDateTime.now());
            orderRepository.save(order);
            
            // 4. 发送开门成功通知
            smsService.send(order.getRiderPhone(), "门已开启,请尽快取货");
            
            // 5. 启动超时监控(10分钟未关门自动告警)
            scheduleTimeoutMonitor(orderId);
            
            return true;
        }
        return false;
    }
    
    /**
     * 骑手取货完成,关门核销
     */
    public boolean completePickup(String orderId) {
        Order order = orderRepository.findById(orderId);
        
        // 1. 核销库存
        inventoryService.deductStock(order.getItems());
        
        // 2. 触发骑手端完成回调
        notifyRiderApp(orderId, "取货完成");
        
        // 3. 更新订单状态
        order.setStatus(OrderStatus.COMPLETED);
        order.setCompleteTime(LocalDateTime.now());
        orderRepository.save(order);
        
        // 4. 关闭门锁
        lockService.closeLock(order.getShopId());
        
        return true;
    }
    
    /**
     * 超时监控:骑手开门后10分钟未完成取货,触发告警
     */
    @Scheduled(fixedDelay = 60000) // 每分钟检查一次
    public void checkTimeoutOrders() {
        List<Order> timeoutOrders = orderRepository.findByStatusAndDoorOpenTimeBefore(
            OrderStatus.PICKING,
            LocalDateTime.now().minusMinutes(10)
        );
        
        for (Order order : timeoutOrders) {
            // 发送告警通知给店主
            smsService.send(order.getShopPhone(), 
                String.format("订单[%s]取货超时,请及时处理", order.getOrderId()));
            
            // 自动关门
            lockService.closeLock(order.getShopId());
        }
    }
}

硬件联动方案

硬件

通信协议

控制方式

备注

智能门锁

蓝牙/4G

HTTP API

支持远程开锁、状态上报

监控摄像头

RTSP

ONVIF协议

取货过程录像存档

电子价签

蓝牙

低功耗广播

库存不足时变红提醒


3.3 智能调度:多平台骑手比价算法

class DeliveryScheduler:
    """骑手调度器"""
    
    def __init__(self):
        self.platforms = {
            'meituan': MeituanDelivery(),
            'eleme': ElemeDelivery(),
            'jd': JdDelivery(),
            'fengniao': FengniaoDelivery()
        }
    
    def dispatch(self, order: Order) -> DispatchResult:
        """
        智能调度:比价后选择最优骑手平台
        """
        # 1. 获取各平台报价
        quotes = []
        for platform_name, platform in self.platforms.items():
            quote = platform.get_quote(
                order.shop_address,
                order.delivery_address,
                order.items_weight
            )
            if quote:
                quotes.append({
                    'platform': platform_name,
                    'price': quote.price,
                    'eta': quote.estimated_time,  # 预计到达时间(分钟)
                    'reliability': quote.reliability  # 骑手准时率
                })
        
        # 2. 综合评分:价格权重60%,时效权重30%,可靠性权重10%
        for quote in quotes:
            score = (
                0.6 * (1 - quote['price'] / max(q['price'] for q in quotes)) +
                0.3 * (1 - quote['eta'] / max(q['eta'] for q in quotes)) +
                0.1 * quote['reliability']
            )
            quote['score'] = score
        
        # 3. 选择最优平台
        best_quote = max(quotes, key=lambda q: q['score'])
        
        # 4. 发单
        result = self.platforms[best_quote['platform']].place_order(order)
        
        return DispatchResult(
            platform=best_quote['platform'],
            price=best_quote['price'],
            rider_id=result.rider_id,
            estimated_arrival_time=result.eta
        )

四、数据一致性保障

4.1 分布式事务处理

订单履约涉及多个外部系统(外卖平台、门锁、库存),采用最终一致性+补偿机制

@Service
public class OrderService {
    
    @Autowired
    private PlatformAdapter platformAdapter;
    
    @Autowired
    private WarehouseStateMachine warehouseStateMachine;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Transactional
    public void processOrder(Order order) {
        try {
            // 1. 预占库存(本地事务)
            inventoryService.reserveStock(order.getItems());
            
            // 2. 确认外卖平台订单(外部调用)
            boolean confirmed = platformAdapter.confirmOrder(order.getPlatformOrderId());
            if (!confirmed) {
                throw new BusinessException("平台订单确认失败");
            }
            
            // 3. 更新本地订单状态
            order.setStatus(OrderStatus.CONFIRMED);
            orderRepository.save(order);
            
        } catch (Exception e) {
            // 4. 补偿:释放预占库存
            inventoryService.releaseReservedStock(order.getItems());
            throw e;
        }
    }
    
    /**
     * 定时对账:确保本地订单与平台订单状态一致
     */
    @Scheduled(cron = "0 */5 * * * ?") // 每5分钟对账一次
    public void reconcileOrders() {
        List<Order> pendingOrders = orderRepository.findByStatusIn(
            Arrays.asList(OrderStatus.PENDING, OrderStatus.CONFIRMED)
        );
        
        for (Order order : pendingOrders) {
            // 查询平台订单真实状态
            OrderStatus platformStatus = platformAdapter.getOrderStatus(
                order.getPlatformOrderId()
            );
            
            // 如果平台已取消,但本地未同步
            if (platformStatus == OrderStatus.CANCELLED 
                && order.getStatus() != OrderStatus.CANCELLED) {
                
                // 补偿:释放库存
                inventoryService.releaseReservedStock(order.getItems());
                
                // 更新本地状态
                order.setStatus(OrderStatus.CANCELLED);
                orderRepository.save(order);
            }
        }
    }
}

五、实战经验与踩坑记录

5.1 外卖平台API限流处理

问题:美团、饿了么等平台对API调用频率有限制,频繁拉取订单会被限流。

解决方案

@Component
public class RateLimiter {
    
    // 美团:每分钟最多60次请求
    private RateLimiter meituanLimiter = RateLimiter.create(1.0); // 1次/秒
    
    // 饿了么:每分钟最多30次请求
    private RateLimiter elemeLimiter = RateLimiter.create(0.5); // 0.5次/秒
    
    public <T> T executeWithLimit(String platform, Supplier<T> task) {
        RateLimiter limiter = getLimiter(platform);
        limiter.acquire(); // 阻塞等待令牌
        return task.get();
    }
}

5.2 门锁离线降级方案

问题:4G门锁在网络不稳定时可能无法远程控制。

解决方案

public class SmartLockService {
    
    /**
     * 开门(支持降级)
     */
    public boolean openLock(String shopId) {
        // 1. 优先尝试远程开门
        boolean remoteSuccess = tryRemoteOpen(shopId);
        if (remoteSuccess) {
            return true;
        }
        
        // 2. 降级:生成临时密码
        String tempPassword = generateTempPassword(shopId);
        sendPasswordToRider(tempPassword);
        
        // 3. 记录降级事件,用于后续分析
        logDegradationEvent(shopId, "REMOTE_OPEN_FAILED");
        
        return true;
    }
}

5.3 骑手取货纠纷处理

问题:骑手声称已取货,但系统未收到关门信号,导致订单卡在"取货中"状态。

解决方案

  • 双保险验证:门锁状态 + 监控视频截图
  • 超时自动完成:骑手开门后15分钟未关门,系统自动标记为"已完成"并通知店主复核
  • 纠纷申诉通道:骑手可通过APP上传取货照片作为凭证

六、总结与展望

本文介绍了一套基于"外卖聚合+无人仓"的轻量级无人零售系统技术架构,核心要点包括:

  1. 多协议适配器模式:统一接入不同外卖平台,降低维护成本
  2. 状态机驱动履约:通过订单状态流转控制门锁、库存等硬件联动
  3. 智能调度算法:多平台骑手比价,降低配送成本
  4. 最终一致性保障:分布式事务+定时对账,确保数据准确

此类系统的优势在于低成本启动(无需采购智能货柜,仅需对接现有门锁)和快速部署(商户可在1天内完成接入),适合小微零售场景的数字化改造。

未来优化方向

  • 引入AI视觉识别:骑手取货时自动核验商品数量
  • 动态定价:根据时段、库存、订单密度自动调整商品价格

参考文献

  1. 美团开放平台API文档. https://open.meituan.com
  2. 饿了么开放平台API文档. https://open.alimama.com
  3. Spring State Machine官方文档. https://spring.io/projects/spring-statemachine
  4. Google Guava RateLimiter源码分析.

注:本文技术方案参考零售SaaS领域实践(如嘚嘚象等产品采用的架构),所有实现基于开源技术栈,不涉及特定商业产品推广。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐