# 哈哈零售 SDK 使用示例

本文档提供完整的SDK使用示例，涵盖从初始化到实际业务场景的各种用法。

## 目录

1. [基础示例](#基础示例)
2. [设备管理场景](#设备管理场景)
3. [商品管理场景](#商品管理场景)
4. [订单处理场景](#订单处理场景)
5. [完整业务流程](#完整业务流程)

---

## 基础示例

### 1. SDK初始化

```java
import com.haha.sdk.HahaClient;
import com.haha.sdk.config.HahaConfig;
import com.haha.sdk.exception.HahaException;

public class HahaSDKExample {
    
    public static void main(String[] args) {
        // 创建配置
        HahaConfig config = HahaConfig.builder()
            .apiBaseUrl("http://api.hahabianli.com")
            .appId("your_app_id")
            .appSecret("your_app_secret")
            .connectTimeout(10)
            .readTimeout(30)
            .build();
        
        // 验证配置
        config.validate();
        
        // 创建客户端
        HahaClient client = new HahaClient(config);
        
        try {
            // 测试获取access_token
            String token = client.getAccessToken();
            System.out.println("获取Token成功: " + token);
            
        } catch (HahaException e) {
            System.err.println("初始化失败: " + e.getMessage());
        } finally {
            // 关闭客户端（可选）
            client.shutdown();
        }
    }
}
```

### 2. Spring Boot集成

```java
import com.haha.sdk.HahaClient;
import com.haha.sdk.config.HahaConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HahaSDKConfig {
    
    @Value("${haha.api.baseUrl}")
    private String apiBaseUrl;
    
    @Value("${haha.api.appId}")
    private String appId;
    
    @Value("${haha.api.appSecret}")
    private String appSecret;
    
    @Bean
    public HahaClient hahaClient() {
        HahaConfig config = HahaConfig.builder()
            .apiBaseUrl(apiBaseUrl)
            .appId(appId)
            .appSecret(appSecret)
            .build();
        
        return new HahaClient(config);
    }
}
```

**application.yml配置：**

```yaml
haha:
  api:
    baseUrl: http://api.hahabianli.com
    appId: your_app_id
    appSecret: your_app_secret
```

---

## 设备管理场景

### 场景1：查询并展示所有设备

```java
import com.haha.sdk.model.DeviceInfo;
import java.util.List;

public class DeviceListExample {
    
    public void listAllDevices(HahaClient client) {
        try {
            // 获取第一页
            List<DeviceInfo> devices = client.getDeviceApi().getDeviceList(1, 20);
            
            System.out.println("=== 设备列表 ===");
            for (DeviceInfo device : devices) {
                System.out.println("--------------------");
                System.out.println("设备ID: " + device.getId());
                System.out.println("设备名称: " + device.getName());
                System.out.println("设备地址: " + device.getAddress());
                System.out.println("设备状态: " + getStatusText(device.getStatus()));
            }
            
        } catch (HahaException e) {
            System.err.println("获取设备列表失败: " + e.getMessage());
        }
    }
    
    private String getStatusText(String status) {
        return "1".equals(status) ? "正常" : "冻结";
    }
}
```

### 场景2：设备开门完整流程

```java
import com.haha.sdk.model.OpenDoorResult;
import com.haha.sdk.model.DeviceOnlineStatus;

public class OpenDoorExample {
    
    public void openDoorWithCheck(HahaClient client, String deviceId) {
        try {
            // 1. 检查设备在线状态
            DeviceOnlineStatus status = client.getDeviceApi().getOnlineStatus(deviceId);
            if (status.getOnlineStatus() != 1) {
                System.out.println("设备离线，无法开门");
                System.out.println("最后在线时间: " + status.getLastOnlineTime());
                return;
            }
            
            // 2. 检查是否多门单开
            Integer multiDoorType = client.getDeviceApi().isMultiDoorUnique(deviceId);
            Integer doorIndex = null;
            
            if (multiDoorType == 2) {
                // 双门柜，需要指定开哪个门
                doorIndex = 0; // 0-A门, 1-B门
                System.out.println("这是双门柜，将打开A门");
            }
            
            // 3. 生成商户订单号
            String outTradeNo = "ORDER_" + System.currentTimeMillis();
            
            // 4. 开门
            OpenDoorResult result = client.getDeviceApi()
                .openDoor(deviceId, outTradeNo, doorIndex);
            
            System.out.println("开门成功！");
            System.out.println("商户订单号: " + outTradeNo);
            System.out.println("哈哈订单号: " + result.getOrderNo());
            
            // 5. 保存订单号，用于后续查询识别结果
            saveOrderInfo(outTradeNo, result.getOrderNo());
            
        } catch (HahaException e) {
            System.err.println("开门失败: " + e.getMessage());
        }
    }
    
    private void saveOrderInfo(String outTradeNo, String orderNo) {
        // 保存到数据库
        System.out.println("订单信息已保存");
    }
}
```

### 场景3：设备状态监控

```java
import java.util.Map;

public class DeviceMonitorExample {
    
    public void monitorDevice(HahaClient client, String deviceId) {
        try {
            // 获取设备详细状态
            Map<String, Object> status = client.getDeviceApi().getDeviceStatus(deviceId);
            
            int deviceStatus = (int) status.get("device_status");
            int doorStatus = (int) status.get("door_status");
            int lockStatus = (int) status.get("lock_status");
            int cameraStatus = (int) status.get("camera_status");
            int networkStatus = (int) status.get("network_status");
            
            System.out.println("=== 设备状态报告 ===");
            System.out.println("设备ID: " + deviceId);
            System.out.println("设备状态: " + getDeviceStatusText(deviceStatus));
            System.out.println("门状态: " + (doorStatus == 1 ? "打开" : "关闭"));
            System.out.println("锁状态: " + (lockStatus == 1 ? "已锁" : "未锁"));
            System.out.println("摄像头: " + (cameraStatus == 1 ? "正常" : "异常"));
            System.out.println("网络: " + (networkStatus == 1 ? "连接" : "断开"));
            
            // 异常告警
            if (deviceStatus == 2) {
                System.err.println("⚠️ 警告：设备故障，需要维护！");
            }
            if (cameraStatus == 0) {
                System.err.println("⚠️ 警告：摄像头异常，可能影响识别！");
            }
            
        } catch (HahaException e) {
            System.err.println("获取设备状态失败: " + e.getMessage());
        }
    }
    
    private String getDeviceStatusText(int status) {
        switch (status) {
            case 0: return "离线";
            case 1: return "在线";
            case 2: return "故障";
            default: return "未知";
        }
    }
}
```

---

## 商品管理场景

### 场景4：商品库管理

```java
import java.util.List;
import java.util.Map;

public class GoodsManagementExample {
    
    // 从总库添加商品到商家库
    public void addGoodsToMerchant(HahaClient client) {
        try {
            // 1. 搜索商品总库
            Map<String, Object> result = client.getGoodsApi()
                .getTotalList(1, 20, "可乐");
            
            List<Map<String, Object>> list = (List) result.get("list");
            
            if (list.isEmpty()) {
                System.out.println("未找到商品");
                return;
            }
            
            // 2. 选择第一个商品
            Map<String, Object> goods = list.get(0);
            String goodsId = (String) goods.get("goods_id");
            String goodsName = (String) goods.get("goods_name");
            
            System.out.println("找到商品: " + goodsName);
            
            // 3. 添加到商家库，设置价格
            String price = "3.50";
            boolean success = client.getGoodsApi().addToMerchant(goodsId, price);
            
            if (success) {
                System.out.println("商品已添加到商家库，售价: " + price);
            }
            
        } catch (HahaException e) {
            System.err.println("操作失败: " + e.getMessage());
        }
    }
    
    // 设备商品上架
    public void setupDeviceGoods(HahaClient client, String deviceId) {
        try {
            // 1. 获取商家商品库
            Map<String, Object> result = client.getGoodsApi().getMerchantList(1, 100);
            List<Map<String, Object>> goodsList = (List) result.get("list");
            
            System.out.println("商家商品库共有 " + goodsList.size() + " 个商品");
            
            // 2. 将商品上架到指定设备
            for (Map<String, Object> goods : goodsList) {
                String goodsId = (String) goods.get("goods_id");
                String goodsName = (String) goods.get("goods_name");
                
                // 上架
                boolean success = client.getGoodsApi().setStatus(deviceId, goodsId, 1);
                
                if (success) {
                    System.out.println("✓ " + goodsName + " 已上架");
                }
            }
            
        } catch (HahaException e) {
            System.err.println("上架失败: " + e.getMessage());
        }
    }
}
```

### 场景5：新品申请流程

```java
public class NewGoodsExample {
    
    public void applyNewGoods(HahaClient client, String barcode, String name, String imageUrl) {
        try {
            // 1. 先查询商品是否已存在
            Map<String, Object> existingGoods = searchGoodsByBarcode(client, barcode);
            
            if (existingGoods != null) {
                System.out.println("商品已存在: " + existingGoods.get("goods_name"));
                return;
            }
            
            // 2. 提交新品申请
            String description = "这是一款新商品";
            boolean success = client.getGoodsApi()
                .applyNew(name, barcode, imageUrl, description);
            
            if (success) {
                System.out.println("新品申请已提交，等待审核");
                System.out.println("商品名称: " + name);
                System.out.println("商品条码: " + barcode);
            }
            
        } catch (HahaException e) {
            System.err.println("申请失败: " + e.getMessage());
        }
    }
    
    private Map<String, Object> searchGoodsByBarcode(HahaClient client, String barcode) {
        try {
            Map<String, Object> result = client.getGoodsApi()
                .getTotalList(1, 10, barcode);
            
            List<Map<String, Object>> list = (List) result.get("list");
            return list.isEmpty() ? null : list.get(0);
            
        } catch (HahaException e) {
            return null;
        }
    }
}
```

---

## 订单处理场景

### 场景6：处理识别结果通知

```java
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

public class OrderRecognizeExample {
    
    // 模拟接收哈哈平台的识别结果通知
    public void handleRecognizeCallback(HahaClient client, String orderNo) {
        try {
            // 1. 查询识别结果
            Map<String, Object> result = client.getOrderApi()
                .getRecognizeResult(orderNo, null);
            
            // 2. 解析识别结果
            List<Map<String, Object>> goodsList = (List) result.get("goods_list");
            Double totalAmount = (Double) result.get("total_amount");
            Double confidence = (Double) result.get("confidence");
            String videoUrl = (String) result.get("video_url");
            
            System.out.println("=== 识别结果 ===");
            System.out.println("订单号: " + orderNo);
            System.out.println("总金额: " + totalAmount);
            System.out.println("置信度: " + confidence);
            
            // 3. 显示商品明细
            System.out.println("\n商品明细:");
            for (Map<String, Object> goods : goodsList) {
                String goodsName = (String) goods.get("goods_name");
                int quantity = (int) goods.get("quantity");
                double price = (double) goods.get("price");
                double amount = (double) goods.get("amount");
                
                System.out.println("  " + goodsName + " x" + quantity + " = ¥" + amount);
            }
            
            // 4. 判断是否需要人工审核
            if (confidence < 0.8) {
                System.out.println("\n⚠️ 置信度较低，建议人工审核");
                System.out.println("视频地址: " + videoUrl);
                
                // 可以通过getOrderMedia获取更多图片
                Map<String, Object> media = client.getOrderApi().getOrderMedia(orderNo);
                System.out.println("开门前图片: " + media.get("before_image"));
                System.out.println("关门后图片: " + media.get("after_image"));
                
                return;
            }
            
            // 5. 置信度高，直接扣款
            boolean paySuccess = processPayment(orderNo, new BigDecimal(totalAmount));
            
            // 6. 通知哈哈平台支付结果
            int payStatus = paySuccess ? 1 : 2;
            String payTime = getCurrentTime();
            String transactionId = "PAY_" + System.currentTimeMillis();
            
            client.getOrderApi().setPayStatus(orderNo, payStatus, payTime, transactionId);
            
            if (paySuccess) {
                System.out.println("\n✓ 支付成功");
            } else {
                System.out.println("\n✗ 支付失败");
            }
            
        } catch (HahaException e) {
            System.err.println("处理识别结果失败: " + e.getMessage());
        }
    }
    
    private boolean processPayment(String orderNo, BigDecimal amount) {
        // 调用第三方支付接口扣款
        System.out.println("正在扣款 ¥" + amount + "...");
        return true; // 模拟支付成功
    }
    
    private String getCurrentTime() {
        return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
            .format(new java.util.Date());
    }
}
```

### 场景7：订单查询和对账

```java
public class OrderQueryExample {
    
    public void queryAndVerifyOrder(HahaClient client, String orderNo) {
        try {
            // 1. 查询订单详情
            Map<String, Object> order = client.getOrderApi().queryOrder(orderNo, null);
            
            String outTradeNo = (String) order.get("out_trade_no");
            String deviceId = (String) order.get("device_id");
            String orderStatus = (String) order.get("order_status");
            Double totalAmount = (Double) order.get("total_amount");
            String createTime = (String) order.get("create_time");
            String payTime = (String) order.get("pay_time");
            
            System.out.println("=== 订单详情 ===");
            System.out.println("哈哈订单号: " + orderNo);
            System.out.println("商户订单号: " + outTradeNo);
            System.out.println("设备ID: " + deviceId);
            System.out.println("订单状态: " + getOrderStatusText(orderStatus));
            System.out.println("订单金额: ¥" + totalAmount);
            System.out.println("创建时间: " + createTime);
            System.out.println("支付时间: " + payTime);
            
            // 2. 查询回调状态
            Map<String, Object> callbackStatus = client.getOrderApi()
                .getCallbackStatus(orderNo);
            
            int status = (int) callbackStatus.get("callback_status");
            int retryCount = (int) callbackStatus.get("retry_count");
            
            System.out.println("\n=== 回调状态 ===");
            System.out.println("推送状态: " + getCallbackStatusText(status));
            System.out.println("重试次数: " + retryCount);
            
        } catch (HahaException e) {
            System.err.println("查询失败: " + e.getMessage());
        }
    }
    
    private String getOrderStatusText(String status) {
        switch (status) {
            case "paying": return "支付中";
            case "paid": return "已支付";
            case "refund": return "已退款";
            case "closed": return "已关闭";
            default: return status;
        }
    }
    
    private String getCallbackStatusText(int status) {
        switch (status) {
            case 0: return "未推送";
            case 1: return "推送成功";
            case 2: return "推送失败";
            default: return "未知";
        }
    }
}
```

---

## 完整业务流程

### 场景8：完整购物流程

```java
import com.haha.sdk.HahaClient;
import com.haha.sdk.config.HahaConfig;
import com.haha.sdk.model.OpenDoorResult;
import com.haha.sdk.exception.HahaException;
import java.util.Map;

public class CompleteShoppingExample {
    
    private HahaClient client;
    
    public CompleteShoppingExample() {
        // 初始化SDK
        HahaConfig config = HahaConfig.builder()
            .apiBaseUrl("http://api.hahabianli.com")
            .appId("your_app_id")
            .appSecret("your_app_secret")
            .build();
        
        this.client = new HahaClient(config);
    }
    
    /**
     * 完整购物流程
     */
    public void completeShopping(String deviceId, String userId) {
        String orderNo = null;
        String outTradeNo = null;
        
        try {
            // ========== 步骤1: 开门前检查 ==========
            System.out.println("=== 步骤1: 开门前检查 ===");
            if (!checkDeviceStatus(deviceId)) {
                System.out.println("设备检查未通过，无法开门");
                return;
            }
            
            // ========== 步骤2: 用户验证 ==========
            System.out.println("\n=== 步骤2: 用户验证 ===");
            if (!verifyUser(userId)) {
                System.out.println("用户验证未通过");
                return;
            }
            
            // ========== 步骤3: 开门 ==========
            System.out.println("\n=== 步骤3: 开门 ===");
            outTradeNo = "ORDER_" + System.currentTimeMillis();
            OpenDoorResult result = client.getDeviceApi()
                .openDoor(deviceId, outTradeNo, null);
            orderNo = result.getOrderNo();
            
            System.out.println("开门成功！");
            System.out.println("商户订单号: " + outTradeNo);
            System.out.println("哈哈订单号: " + orderNo);
            
            // 保存订单
            saveOrder(userId, deviceId, outTradeNo, orderNo);
            
            // ========== 步骤4: 等待关门和识别 ==========
            System.out.println("\n=== 步骤4: 等待用户购物 ===");
            System.out.println("请用户取货并关门...");
            System.out.println("（实际业务中，识别结果会通过回调通知）");
            
            // 模拟等待
            Thread.sleep(5000);
            
            // ========== 步骤5: 处理识别结果 ==========
            System.out.println("\n=== 步骤5: 处理识别结果 ===");
            processRecognizeResult(orderNo);
            
            System.out.println("\n✓ 购物流程完成");
            
        } catch (HahaException e) {
            System.err.println("业务异常: " + e.getMessage());
            
            // 异常处理：如果已经开门，记录异常订单
            if (orderNo != null) {
                handleException(orderNo, e);
            }
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 清理资源
            cleanup();
        }
    }
    
    /**
     * 检查设备状态
     */
    private boolean checkDeviceStatus(String deviceId) throws HahaException {
        // 检查在线状态
        var status = client.getDeviceApi().getOnlineStatus(deviceId);
        if (status.getOnlineStatus() != 1) {
            System.out.println("✗ 设备离线");
            return false;
        }
        
        // 检查详细状态
        Map<String, Object> detailStatus = client.getDeviceApi().getDeviceStatus(deviceId);
        int deviceStatus = (int) detailStatus.get("device_status");
        int doorStatus = (int) detailStatus.get("door_status");
        
        if (deviceStatus != 1) {
            System.out.println("✗ 设备故障");
            return false;
        }
        
        if (doorStatus == 1) {
            System.out.println("✗ 设备使用中");
            return false;
        }
        
        System.out.println("✓ 设备状态正常");
        return true;
    }
    
    /**
     * 用户验证（示例）
     */
    private boolean verifyUser(String userId) {
        // 实际业务中需要验证：
        // 1. 用户是否签约免密支付
        // 2. 用户余额是否充足
        // 3. 用户是否被锁定
        
        System.out.println("✓ 用户验证通过");
        return true;
    }
    
    /**
     * 保存订单到本地数据库
     */
    private void saveOrder(String userId, String deviceId, String outTradeNo, String orderNo) {
        System.out.println("✓ 订单已保存到数据库");
    }
    
    /**
     * 处理识别结果
     */
    private void processRecognizeResult(String orderNo) throws HahaException {
        // 查询识别结果
        Map<String, Object> result = client.getOrderApi()
            .getRecognizeResult(orderNo, null);
        
        Double totalAmount = (Double) result.get("total_amount");
        Double confidence = (Double) result.get("confidence");
        
        System.out.println("识别完成");
        System.out.println("总金额: ¥" + totalAmount);
        System.out.println("置信度: " + confidence);
        
        // 判断是否需要人工审核
        if (confidence < 0.8) {
            System.out.println("⚠️ 置信度较低，转人工审核");
            return;
        }
        
        // 扣款
        System.out.println("正在扣款...");
        boolean paySuccess = true; // 调用支付接口
        
        // 通知哈哈平台
        int payStatus = paySuccess ? 1 : 2;
        client.getOrderApi().setPayStatus(
            orderNo, 
            payStatus, 
            getCurrentTime(), 
            "PAY_" + System.currentTimeMillis()
        );
        
        System.out.println(paySuccess ? "✓ 支付成功" : "✗ 支付失败");
    }
    
    /**
     * 异常处理
     */
    private void handleException(String orderNo, Exception e) {
        System.err.println("记录异常订单: " + orderNo);
        // 保存到异常订单表，人工处理
    }
    
    /**
     * 清理资源
     */
    private void cleanup() {
        // 必要时清理资源
    }
    
    private String getCurrentTime() {
        return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
            .format(new java.util.Date());
    }
    
    /**
     * 主函数示例
     */
    public static void main(String[] args) {
        CompleteShoppingExample example = new CompleteShoppingExample();
        
        // 模拟用户购物
        String deviceId = "device001";
        String userId = "user123";
        
        example.completeShopping(deviceId, userId);
    }
}
```

---

## 异常处理最佳实践

```java
import com.haha.sdk.exception.HahaException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExceptionHandlingExample {
    
    private static final Logger log = LoggerFactory.getLogger(ExceptionHandlingExample.class);
    
    public void handleWithRetry(HahaClient client, String deviceId) {
        int maxRetry = 3;
        int retryCount = 0;
        
        while (retryCount < maxRetry) {
            try {
                // 尝试开门
                OpenDoorResult result = client.getDeviceApi()
                    .openDoor(deviceId, "ORDER_" + System.currentTimeMillis(), null);
                
                log.info("开门成功: {}", result.getOrderNo());
                break;
                
            } catch (HahaException e) {
                retryCount++;
                
                // 根据错误码判断是否需要重试
                Integer errorCode = e.getCode();
                
                if (errorCode != null) {
                    switch (errorCode) {
                        case -3:
                        case -4:
                            // token问题，SDK会自动刷新，可以重试
                            log.warn("Token问题，重试第{}次", retryCount);
                            break;
                            
                        case -101:
                            // 设备离线，不需要重试
                            log.error("设备离线，停止重试");
                            return;
                            
                        case -103:
                            // 设备使用中，可以等待后重试
                            log.warn("设备使用中，等待5秒后重试");
                            try {
                                Thread.sleep(5000);
                            } catch (InterruptedException ie) {
                                Thread.currentThread().interrupt();
                            }
                            break;
                            
                        default:
                            log.error("开门失败: {}", e.getMessage());
                            return;
                    }
                } else {
                    log.error("未知错误: {}", e.getMessage(), e);
                    return;
                }
                
                if (retryCount >= maxRetry) {
                    log.error("重试{}次后仍然失败", maxRetry);
                }
            }
        }
    }
}
```

---

## 总结

本文档提供了哈哈零售SDK的完整使用示例，涵盖：

1. ✅ SDK初始化和Spring Boot集成
2. ✅ 设备管理（列表、开门、状态监控）
3. ✅ 商品管理（添加、上架、新品申请）
4. ✅ 订单处理（识别结果、支付状态）
5. ✅ 完整购物流程
6. ✅ 异常处理和重试机制

更多信息请参考 [README.md](README.md) 和 [API文档](https://showdoc.hahabianli.com/web/#/685869388/275897889)。
