最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot + MQTT實現(xiàn)取貨就走的智能售貨柜系統(tǒng)完整流程

 更新時間:2026年03月10日 10:11:23   作者:程序員大華  
本文介紹了一種智能售貨柜的工作原理和實現(xiàn)流程,該系統(tǒng)通過多傳感器數(shù)據(jù)融合、實時視頻流處理和異步處理,提供無感購物體驗,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧

大家好,我是大華。昨天在辦公樓底下,我用了一下那種開門拿貨,關門自動扣費的智能售貨柜,真挺方便的。

其實這種售貨柜并不少見,很多無人售貨店、地鐵站和景區(qū)都能經??炊?/p>

那這種流程是怎么實現(xiàn)的呢?下面我們來分析一下整個實現(xiàn)的流程。

場景:

  1. 你用微信掃描售貨柜上的二維碼
  2. 柜門咔嚓一聲自動打開
  3. 你拿出想要的商品,同時可以隨意更換
  4. 關上柜門,然后手機自動收到扣款通知
  5. 整個過程中無需任何額外操作

核心技術棧(Spingboot)

技術組件作用
Spring Boot后端主框架
Redis高速緩存
MQTT物聯(lián)網通信協(xié)議
MySQL關系型數(shù)據(jù)庫
消息隊列異步任務處理
計算機視覺拍攝商品識別

完整技術流程詳解

第一階段:掃碼開門(身份驗證與初始化)

用戶動作:微信掃碼 → 授權 → 柜門打開

后臺流程

  1. 身份認證:驗證微信賬號的合法性
  2. 設備狀態(tài)檢查:確認售貨柜是否可用
  3. 創(chuàng)建會話:在Redis中建立臨時購物車
  4. 數(shù)據(jù)采集:拍攝貨架初始照片,記錄傳感器數(shù)據(jù)
  5. 開門指令:通過MQTT協(xié)議發(fā)送開門命令

技術要點

  • 使用Redis存儲臨時會話,讀寫速度達到微秒級
  • MQTT協(xié)議專為物聯(lián)網設計,低功耗、高可靠
  • 初始快照為后續(xù)對比提供基準數(shù)據(jù)

第二階段:自由選購(實時事件追蹤)

用戶動作:拿取商品 → 可能更換 → 繼續(xù)選購

系統(tǒng)監(jiān)控

  1. 視覺追蹤:攝像頭實時識別手部動作和商品變化
  2. 重量感應:每個貨道的傳感器監(jiān)測重量變化
  3. 事件上報:實時將"拿取/放回"動作發(fā)送到后臺
  4. 實時記錄:在Redis中更新購物車狀態(tài)

技術難點突破

  • 實時視頻流處理,延遲控制在100ms以內
  • 多傳感器數(shù)據(jù)融合,提高識別準確率
  • 高并發(fā)事件處理,支持多用戶同時購物

第三階段:關門結算(異步清算流程)

用戶動作:關閉柜門 → 自動觸發(fā)結算

核心清算流程

關門信號 → 啟動異步任務 → 數(shù)據(jù)收集 → 三重校驗 → 支付扣款 → 狀態(tài)更新

詳細步驟

  1. 觸發(fā)結算:門磁傳感器檢測到關門動作
  2. 異步處理:避免用戶等待,另起線程處理復雜計算
  3. 數(shù)據(jù)收集:獲取關門快照和最終傳感器數(shù)據(jù)
  4. 三重校驗
    • 視覺對比:開門vs關門圖片差異分析
    • 重量分析:各貨道重量變化計算
    • 事件復核:核對實時記錄的事件序列
  5. 沖突解決:當三種方式結果不一致時的智能決策
  6. 支付執(zhí)行:調用支付接口完成扣款
  7. 狀態(tài)更新:標記訂單完成,清理緩存

示例代碼實現(xiàn)

1. 核心數(shù)據(jù)模型

// 訂單實體
@Entity
@Table(name = "vending_orders")
@Data
public class VendingOrder {
    @Id
    private String orderId;
    private String userId;          // 用戶ID
    private String deviceId;        // 設備ID
    private String status;          // 狀態(tài): OPEN/CLOSED/PAID/FAILED
    private BigDecimal amount;      // 訂單金額
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}
// Redis緩存中的設備會話
@Data
public class DeviceSession {
    private String sessionId;
    private String deviceId;
    private String orderId;
    private String userId;
    private String status;          // 會話狀態(tài)
    private LocalDateTime startTime;
    private List<DeviceEvent> events; // 購物事件記錄
}
// 設備事件
@Data
public class DeviceEvent {
    private String eventId;
    private String deviceId;
    private String orderId;
    private String type;            // PICK/PUT_BACK
    private String productId;
    private LocalDateTime eventTime;
    private String position;        // 貨道位置
}

2. 控制器層 - 處理HTTP請求

@RestController
@RequestMapping("/api/vending")
@Slf4j
public class VendingController {
    @Autowired
    private VendingService vendingService;
    @Autowired
    private SettlementService settlementService;
    /**
     * 掃碼開門接口
     */
    @PostMapping("/open")
    public ResponseEntity<ApiResponse> openDevice(
            @RequestParam String deviceId,
            @RequestParam String authToken) {
        log.info("收到開門請求: deviceId={}", deviceId);
        try {
            OpenResult result = vendingService.processOpenDevice(deviceId, authToken);
            return ResponseEntity.ok(ApiResponse.success(result));
        } catch (BusinessException e) {
            log.warn("開門業(yè)務異常: {}", e.getMessage());
            return ResponseEntity.badRequest().body(ApiResponse.error(e.getMessage()));
        }
    }
    /**
     * 查詢訂單狀態(tài)
     */
    @GetMapping("/order/{orderId}")
    public ResponseEntity<ApiResponse> getOrderStatus(@PathVariable String orderId) {
        VendingOrder order = vendingService.getOrderById(orderId);
        return ResponseEntity.ok(ApiResponse.success(order));
    }
}
// 統(tǒng)一API響應格式
@Data
class ApiResponse {
    private boolean success;
    private String message;
    private Object data;
    public static ApiResponse success(Object data) {
        ApiResponse response = new ApiResponse();
        response.setSuccess(true);
        response.setData(data);
        return response;
    }
    public static ApiResponse error(String message) {
        ApiResponse response = new ApiResponse();
        response.setSuccess(false);
        response.setMessage(message);
        return response;
    }
}

3. 核心服務層 - 開門處理

@Service
@Slf4j
public class VendingService {
    @Autowired
    private UserAuthService authService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private MqttService mqttService;
    /**
     * 處理開門請求的核心邏輯
     */
    public OpenResult processOpenDevice(String deviceId, String authToken) {
        // 1. 用戶身份驗證
        String userId = authService.verifyWechatToken(authToken);
        if (userId == null) {
            throw new BusinessException("用戶身份驗證失敗");
        }
        // 2. 檢查設備狀態(tài)
        DeviceStatus deviceStatus = deviceService.getDeviceStatus(deviceId);
        if (!deviceStatus.isAvailable()) {
            throw new BusinessException("設備暫不可用: " + deviceStatus.getStatus());
        }
        // 3. 創(chuàng)建訂單
        VendingOrder order = orderService.createOrder(userId, deviceId);
        log.info("創(chuàng)建訂單成功: orderId={}, userId={}, deviceId={}", 
                order.getOrderId(), userId, deviceId);
        // 4. 創(chuàng)建設備會話并緩存
        DeviceSession session = createDeviceSession(deviceId, order);
        cacheDeviceSession(session);
        // 5. 鎖定設備,避免重復開門
        deviceService.lockDevice(deviceId, order.getOrderId());
        // 6. 發(fā)送開門指令
        mqttService.sendOpenCommand(deviceId);
        // 7. 請求設備上報初始狀態(tài)
        mqttService.requestInitialSnapshot(deviceId);
        return new OpenResult(order.getOrderId(), deviceId, "開門指令已發(fā)送");
    }
    private DeviceSession createDeviceSession(String deviceId, VendingOrder order) {
        DeviceSession session = new DeviceSession();
        session.setSessionId(UUID.randomUUID().toString());
        session.setDeviceId(deviceId);
        session.setOrderId(order.getOrderId());
        session.setUserId(order.getUserId());
        session.setStatus("OPEN");
        session.setStartTime(LocalDateTime.now());
        session.setEvents(new ArrayList<>());
        return session;
    }
    private void cacheDeviceSession(DeviceSession session) {
        String key = buildSessionKey(session.getDeviceId());
        redisTemplate.opsForValue().set(key, session, Duration.ofMinutes(10));
        log.debug("設備會話已緩存: key={}", key);
    }
    private String buildSessionKey(String deviceId) {
        return "vending:session:" + deviceId;
    }
}

4. MQTT消息處理 - 設備通信

@Component
@Slf4j
public class MqttMessageHandler {
    @Autowired
    private VendingService vendingService;
    @Autowired
    private SettlementService settlementService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 處理關門事件 - 觸發(fā)結算流程
     */
    @MqttListener(topics = "device/${spring.application.env}/+/event/door_close")
    public void handleDoorClose(String message) {
        try {
            DoorCloseEvent event = JSON.parseObject(message, DoorCloseEvent.class);
            log.info("收到關門事件: deviceId={}", event.getDeviceId());
            // 異步處理結算,不阻塞MQTT線程
            CompletableFuture.runAsync(() -> {
                settlementService.startSettlementProcess(event.getDeviceId());
            });
        } catch (Exception e) {
            log.error("處理關門事件失敗: message={}", message, e);
        }
    }
    /**
     * 處理商品拿取/放回事件
     */
    @MqttListener(topics = "device/${spring.application.env}/+/event/product")
    public void handleProductEvent(String message) {
        try {
            ProductEvent event = JSON.parseObject(message, ProductEvent.class);
            log.debug("處理商品事件: deviceId={}, type={}, product={}", 
                     event.getDeviceId(), event.getEventType(), event.getProductId());
            // 記錄到Redis緩存
            recordProductEvent(event);
        } catch (Exception e) {
            log.error("處理商品事件失敗: message={}", message, e);
        }
    }
    /**
     * 處理設備上報的初始/最終快照
     */
    @MqttListener(topics = "device/${spring.application.env}/+/snapshot")
    public void handleSnapshot(String message) {
        try {
            DeviceSnapshot snapshot = JSON.parseObject(message, DeviceSnapshot.class);
            log.info("處理設備快照: deviceId={}, type={}", 
                    snapshot.getDeviceId(), snapshot.getSnapshotType());
            // 存儲快照數(shù)據(jù),用于后續(xù)對比分析
            storeDeviceSnapshot(snapshot);
        } catch (Exception e) {
            log.error("處理快照失敗: message={}", message, e);
        }
    }
    private void recordProductEvent(ProductEvent event) {
        String sessionKey = "vending:session:" + event.getDeviceId();
        DeviceSession session = (DeviceSession) redisTemplate.opsForValue().get(sessionKey);
        if (session != null) {
            DeviceEvent deviceEvent = convertToDeviceEvent(event, session.getOrderId());
            session.getEvents().add(deviceEvent);
            redisTemplate.opsForValue().set(sessionKey, session, Duration.ofMinutes(10));
        }
    }
}

5. 結算服務 - 核心業(yè)務邏輯

@Service
@Slf4j
public class SettlementService {
    @Autowired
    private OrderService orderService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private NotificationService notificationService;
    /**
     * 啟動結算流程
     */
    @Async("settlementExecutor")
    public void startSettlementProcess(String deviceId) {
        log.info("開始結算流程: deviceId={}", deviceId);
        try {
            // 1. 獲取設備會話
            DeviceSession session = getDeviceSession(deviceId);
            if (session == null) {
                log.error("設備會話不存在: deviceId={}", deviceId);
                return;
            }
            // 2. 更新訂單狀態(tài)為結算中
            orderService.updateOrderStatus(session.getOrderId(), "SETTLING");
            // 3. 獲取設備上報的最終數(shù)據(jù)
            SettlementData settlementData = collectSettlementData(deviceId);
            // 4. 執(zhí)行結算計算
            SettlementResult result = calculateSettlement(settlementData);
            // 5. 處理支付
            boolean paymentSuccess = processPayment(session, result);
            // 6. 更新訂單狀態(tài)
            updateOrderAfterSettlement(session, result, paymentSuccess);
            // 7. 清理資源
            cleanupAfterSettlement(deviceId, session.getOrderId());
            log.info("結算流程完成: deviceId={}, orderId={}, success={}", 
                    deviceId, session.getOrderId(), paymentSuccess);
        } catch (Exception e) {
            log.error("結算流程異常: deviceId={}", deviceId, e);
            handleSettlementFailure(deviceId, e);
        }
    }
    /**
     * 收集結算所需的所有數(shù)據(jù)
     */
    private SettlementData collectSettlementData(String deviceId) {
        SettlementData data = new SettlementData();
        // 獲取初始和最終快照
        data.setInitialSnapshot(deviceService.getInitialSnapshot(deviceId));
        data.setFinalSnapshot(deviceService.getFinalSnapshot(deviceId));
        // 獲取重量傳感器數(shù)據(jù)
        data.setWeightData(deviceService.getWeightSensorData(deviceId));
        // 獲取購物事件記錄
        data.setProductEvents(getRecordedEvents(deviceId));
        return data;
    }
    /**
     * 核心結算算法 - 三重校驗
     */
    private SettlementResult calculateSettlement(SettlementData data) {
        // 1. 視覺對比分析
        List<Product> visualProducts = analyzeVisualChanges(
            data.getInitialSnapshot(), 
            data.getFinalSnapshot()
        );
        // 2. 重量變化分析
        List<Product> weightProducts = analyzeWeightChanges(data.getWeightData());
        // 3. 事件記錄分析
        List<Product> eventProducts = analyzeEventSequence(data.getProductEvents());
        // 4. 沖突解決和結果融合
        return resolveProductConflicts(visualProducts, weightProducts, eventProducts);
    }
    /**
     * 沖突解決策略
     */
    private SettlementResult resolveProductConflicts(List<Product> visualProducts, 
                                                    List<Product> weightProducts,
                                                    List<Product> eventProducts) {
        SettlementResult result = new SettlementResult();
        // 策略1: 視覺識別優(yōu)先(最直接證據(jù))
        Map<String, Product> productMap = new HashMap<>();
        // 首先信任視覺識別結果
        for (Product product : visualProducts) {
            productMap.put(product.getPosition(), product);
        }
        // 用重量數(shù)據(jù)驗證和補充
        for (Product weightProduct : weightProducts) {
            Product visualProduct = productMap.get(weightProduct.getPosition());
            if (visualProduct == null) {
                // 視覺沒識別到但重量有變化,信任重量數(shù)據(jù)
                productMap.put(weightProduct.getPosition(), weightProduct);
            }
        }
        // 用事件記錄進行最終校驗
        result.setFinalProducts(new ArrayList<>(productMap.values()));
        result.setConflictResolved(true);
        log.debug("沖突解決完成: 視覺識別{}個, 重量變化{}個, 最終確認{}個", 
                 visualProducts.size(), weightProducts.size(), result.getFinalProducts().size());
        return result;
    }
}

6. 支付服務

@Service
@Slf4j
public class PaymentService {
    @Autowired
    private WechatPayService wechatPayService;
    @Autowired
    private OrderService orderService;
    /**
     * 執(zhí)行支付
     */
    public boolean processPayment(DeviceSession session, SettlementResult result) {
        try {
            PaymentRequest request = new PaymentRequest();
            request.setUserId(session.getUserId());
            request.setOrderId(session.getOrderId());
            request.setAmount(calculateTotalAmount(result.getFinalProducts()));
            request.setDescription("智能售貨柜購物");
            PaymentResponse response = wechatPayService.unifiedOrder(request);
            if ("SUCCESS".equals(response.getResultCode())) {
                log.info("支付成功: orderId={}, amount={}", 
                        session.getOrderId(), request.getAmount());
                return true;
            } else {
                log.warn("支付失敗: orderId={}, error={}", 
                        session.getOrderId(), response.getErrMsg());
                return false;
            }
        } catch (Exception e) {
            log.error("支付處理異常: orderId={}", session.getOrderId(), e);
            return false;
        }
    }
    private BigDecimal calculateTotalAmount(List<Product> products) {
        return products.stream()
                .map(Product::getPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

7. 配置類

@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfig {
    @Bean("settlementExecutor")
    public TaskExecutor settlementTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("settlement-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

總結

1.異步處理:結算流程異步化,用戶無需等待 2.三重校驗:視覺+重量+事件記錄,確保準確率 3.實時通信:MQTT保證設備與后臺實時通信 4.緩存優(yōu)化:Redis提升系統(tǒng)響應速度 5.異常容錯:完善的異常處理機制

這種系統(tǒng)完美融合了物聯(lián)網、云計算、移動支付等前沿技術,為用戶提供了拿了就走的無感購物體驗,代表了零售行業(yè)數(shù)字化轉型的最新成果。

到此這篇關于SpringBoot + MQTT實現(xiàn)取貨就走的智能售貨柜系統(tǒng)完整流程的文章就介紹到這了,更多相關SpringBoot MQTT智能售貨柜系統(tǒng)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Eclipse 出現(xiàn)A configuration with this name already exists問題解決方法

    Eclipse 出現(xiàn)A configuration with this name already exists問題解決方

    這篇文章主要介紹了Eclipse 出現(xiàn)A configuration with this name already exists問題解決方法的相關資料,需要的朋友可以參考下
    2016-11-11
  • springboot上傳文件過大的500異常解決

    springboot上傳文件過大的500異常解決

    這篇文章主要介紹了springboot上傳文件過大的500異常解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • IDEA?Debug過程中使用Drop?Frame或Reset?Frame實現(xiàn)操作回退的方法

    IDEA?Debug過程中使用Drop?Frame或Reset?Frame實現(xiàn)操作回退的方法

    在IDEA中就提供了一個幫助你回退代碼的機會,但這個方法并不是萬能的,好了,下面就來具體說說IDEA?Debug過程中使用Drop?Frame或Reset?Frame實現(xiàn)操作回退的方法,感興趣的朋友一起看看吧
    2022-04-04
  • 在Spring框架下配置Quartz集群的詳細步驟(MySQL數(shù)據(jù)源)

    在Spring框架下配置Quartz集群的詳細步驟(MySQL數(shù)據(jù)源)

    Quartz 是一個功能強大的調度庫,可以在 Java 應用中用于執(zhí)行定時任務,本文將介紹如何在 Spring 框架下配置 Quartz 集群,并使用 MySQL 作為數(shù)據(jù)源來存儲調度信息,文中有詳細的代碼供大家參考,需要的朋友可以參考下
    2025-01-01
  • SpringBoot使用MyBatis的XML文件進行SQL語句編寫

    SpringBoot使用MyBatis的XML文件進行SQL語句編寫

    在現(xiàn)代 Java Web 開發(fā)中,Spring Boot 和 MyBatis 是兩個非常流行的技術框架,本文將詳細介紹如何在 Spring Boot 項目中使用 MyBatis 的 XML 文件來編寫 SQL 語句,感興趣的可以了解下
    2025-07-07
  • SpringBoot實現(xiàn)動態(tài)端口切換黑魔法

    SpringBoot實現(xiàn)動態(tài)端口切換黑魔法

    這篇文章主要為大家詳細介紹了SpringBoot如何實現(xiàn)動態(tài)端口切換黑魔法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-12-12
  • maven package后Idea項目中找不到target文件的解決

    maven package后Idea項目中找不到target文件的解決

    在Idea中執(zhí)行mavenpackage打包后,target文件不顯示,點擊「ShowinExplore」可以在本地文件夾中查到,解決方法:在Idea的Maven工具窗口中,右鍵點擊項目,選擇Reimport,刷新項目即可
    2024-11-11
  • idea 開發(fā)神器之idea插件匯總

    idea 開發(fā)神器之idea插件匯總

    這篇文章主要介紹了idea 開發(fā)神器之idea插件匯總,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Java包裝類與異常處理的過程

    Java包裝類與異常處理的過程

    本文介紹了Java中包裝類和異常處理的概念,解析了它們的用途和使用方法,包裝類主要用于將基本數(shù)據(jù)類型轉換為對象,解決面向對象編程中的限制;異常處理機制則幫助程序優(yōu)雅地處理運行時錯誤,包括常見異常類型、異常處理語句及自定義異常的使用
    2026-04-04
  • 詳解Java編程中線程的掛起、恢復和終止的方法

    詳解Java編程中線程的掛起、恢復和終止的方法

    這篇文章主要介紹了詳解Java編程中線程的掛起、恢復和終止的方法,線程是Java學習中的重點和難點知識,需要的朋友可以參考下
    2015-09-09

最新評論

兴安盟| 遂平县| 会理县| 青田县| 凤山市| 依兰县| 阳高县| 江阴市| 仙居县| 桦甸市| 嘉义县| 易门县| 余干县| 察雅县| 嘉黎县| 安顺市| 平湖市| 泊头市| 阿图什市| 密山市| 和龙市| 柳河县| 紫阳县| 罗江县| 阿拉善盟| 阿勒泰市| 娄底市| 手机| 广饶县| 香港| 靖江市| 祥云县| 秀山| 靖边县| 海伦市| 郓城县| 乌兰察布市| 甘泉县| 兴和县| 三门县| 上饶县|