# 哈哈零售 Java SDK [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE) [![Java](https://img.shields.io/badge/Java-17+-orange.svg)](https://www.oracle.com/java/technologies/downloads/) [![Maven](https://img.shields.io/badge/Maven-3.6+-red.svg)](https://maven.apache.org/) 哈哈零售系统 Java SDK,为智能视觉识别售卖机系统提供完整的API接口封装。 ## 📋 目录 - [功能特性](#功能特性) - [快速开始](#快速开始) - [核心功能](#核心功能) - [设备管理](#1-设备管理) - [商品管理](#2-商品管理) - [订单管理](#3-订单管理) - [高级用法](#高级用法) - [异常处理](#异常处理) - [完整接口列表](#完整接口列表) - [常见问题](#常见问题) --- ## 功能特性 ✅ **完整接口覆盖** - 支持哈哈零售平台全部31个API接口 ✅ **自动令牌管理** - 自动获取、刷新和管理access_token(15天有效期) ✅ **自动签名** - 内置MD5签名算法,无需手动处理 ✅ **类型安全** - 提供完整的类型定义和泛型支持 ✅ **日志支持** - 集成SLF4J日志框架 ✅ **连接池管理** - 基于OkHttp的高性能HTTP客户端 --- ## 快速开始 ### 1. Maven依赖 ```xml com.haha haha-sdk 1.0.0 ``` ### 2. 初始化SDK ```java import com.haha.sdk.HahaClient; import com.haha.sdk.config.HahaConfig; // 创建配置 HahaConfig config = HahaConfig.builder() .apiBaseUrl("http://api.hahabianli.com") // API地址(默认值) .appId("your_app_id") // 商家AppId .appSecret("your_app_secret") // 商家AppSecret .connectTimeout(10) // 连接超时(秒) .readTimeout(30) // 读取超时(秒) .build(); // 创建客户端 HahaClient client = new HahaClient(config); // 使用完毕后关闭(可选) // client.shutdown(); ``` ### 3. 快速调用示例 ```java import com.haha.sdk.exception.HahaException; try { // 获取设备列表 List devices = client.getDeviceApi().getDeviceList(1, 20); // 开门 OpenDoorResult result = client.getDeviceApi() .openDoor("device001", "ORDER" + System.currentTimeMillis(), null); System.out.println("订单号: " + result.getOrderNo()); } catch (HahaException e) { System.err.println("错误: " + e.getMessage()); } ``` --- ## 核心功能 ### 1. 设备管理 通过 `client.getDeviceApi()` 访问设备相关接口: #### 1.1 获取设备列表 ```java // 获取第1页,每页20条 List devices = client.getDeviceApi().getDeviceList(1, 20); for (DeviceInfo device : devices) { System.out.println("设备ID: " + device.getId()); System.out.println("设备名称: " + device.getName()); System.out.println("设备地址: " + device.getAddress()); System.out.println("设备状态: " + device.getStatus()); // 1-正常, 2-冻结 } ``` #### 1.2 查询设备是否多门单开 ```java String deviceId = "device001"; Integer multiDoorType = client.getDeviceApi().isMultiDoorUnique(deviceId); // 返回值:0-非多门单开,2-双门单开,3-三门单开,4-四门单开 if (multiDoorType == 2) { System.out.println("这是双门单开设备"); } ``` #### 1.3 设备开门 ```java String deviceId = "device001"; String outTradeNo = "ORDER" + System.currentTimeMillis(); // 商户订单号 Integer doorIndex = null; // 多门单开时必传:0-A门,1-B门 OpenDoorResult result = client.getDeviceApi().openDoor(deviceId, outTradeNo, doorIndex); System.out.println("哈哈平台订单号: " + result.getOrderNo()); ``` #### 1.4 查询设备在线状态 ```java DeviceOnlineStatus status = client.getDeviceApi().getOnlineStatus("device001"); System.out.println("在线状态: " + status.getOnlineStatus()); // 0-离线, 1-在线 System.out.println("最后在线时间: " + status.getLastOnlineTime()); ``` #### 1.5 设置设备音量 ```java // 音量范围:0-100,0表示静音 boolean success = client.getDeviceApi().setVolume("device001", 80); ``` #### 1.6 查询设备和锁的状态 ```java Map status = client.getDeviceApi().getDeviceStatus("device001"); System.out.println("设备状态: " + status.get("device_status")); // 0-离线, 1-在线, 2-故障 System.out.println("门状态: " + status.get("door_status")); // 0-关闭, 1-打开 System.out.println("锁状态: " + status.get("lock_status")); // 0-未锁, 1-已锁 System.out.println("摄像头: " + status.get("camera_status")); // 0-异常, 1-正常 System.out.println("网络: " + status.get("network_status")); // 0-断开, 1-连接 ``` --- ### 2. 商品管理 通过 `client.getGoodsApi()` 访问商品相关接口: #### 2.1 获取设备可售卖商品列表 ```java List> goodsList = client.getGoodsApi() .getDeviceGoodsList("device001"); for (Map goods : goodsList) { System.out.println("商品ID: " + goods.get("goods_id")); System.out.println("商品名: " + goods.get("goods_name")); System.out.println("价格: " + goods.get("price")); System.out.println("库存: " + goods.get("stock")); } ``` #### 2.2 获取商品总库 ```java // 分页查询,支持关键词搜索 Map result = client.getGoodsApi() .getTotalList(1, 20, "可乐"); int total = (int) result.get("total"); List> list = (List>) result.get("list"); ``` #### 2.3 添加商品至商家商品库 ```java String goodsId = "goods001"; // 来自商品总库的商品ID String price = "3.50"; // 售价 boolean success = client.getGoodsApi().addToMerchant(goodsId, price); ``` #### 2.4 获取商家商品库 ```java Map result = client.getGoodsApi().getMerchantList(1, 20); int total = (int) result.get("total"); List> list = (List>) result.get("list"); ``` #### 2.5 商品上下架 ```java String deviceId = "device001"; String goodsId = "goods001"; int status = 1; // 0-下架, 1-上架 boolean success = client.getGoodsApi().setStatus(deviceId, goodsId, status); ``` #### 2.6 获取和更新设备层模板 ```java // 获取层模板 Map template = client.getGoodsApi().getLayerTemplate("device001"); // 更新层模板(layers为JSON格式的层级配置) String layersJson = "[{\"layer_no\":1,\"layer_name\":\"第一层\",\"goods_list\":[]}]"; boolean success = client.getGoodsApi().updateLayerTemplate("device001", layersJson); ``` #### 2.7 新品申请 ```java boolean success = client.getGoodsApi().applyNew( "新商品名称", "6901234567890", "http://example.com/image.jpg", "商品描述(可选)" ); ``` #### 2.8 查询商品编号对应关系 ```java // 通过商家商品ID查询 Map mapping = client.getGoodsApi() .getIdMapping("merchant_001", null); // 或通过哈哈平台商品ID查询 mapping = client.getGoodsApi().getIdMapping(null, "haha_001"); ``` #### 2.9 商品合并 ```java String targetGoodsId = "goods001"; // 保留的商品 String sourceGoodsIds = "goods002,goods003"; // 要合并的商品(逗号分隔) boolean success = client.getGoodsApi().mergeGoods(targetGoodsId, sourceGoodsIds); ``` --- ### 3. 订单管理 通过 `client.getOrderApi()` 访问订单相关接口: #### 3.1 查询识别结果 ```java // 方式1:通过哈哈平台订单号查询 Map result = client.getOrderApi() .getRecognizeResult("HH202401240001", null); // 方式2:通过商户订单号查询 result = client.getOrderApi() .getRecognizeResult(null, "ORDER123456"); // 解析结果 List> 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("总金额: " + totalAmount); System.out.println("置信度: " + confidence); ``` #### 3.2 查询订单详情 ```java Map order = client.getOrderApi() .queryOrder("HH202401240001", null); String orderStatus = (String) order.get("order_status"); // paying, paid, refund, closed System.out.println("订单状态: " + orderStatus); ``` #### 3.3 设置订单支付状态 ```java String orderNo = "HH202401240001"; int payStatus = 1; // 1-支付成功, 2-支付失败 String payTime = "2024-01-24 15:30:00"; String transactionId = "WX123456789"; // 第三方支付流水号 boolean success = client.getOrderApi() .setPayStatus(orderNo, payStatus, payTime, transactionId); ``` #### 3.4 获取开门前后图片或视频 ```java Map media = client.getOrderApi() .getOrderMedia("HH202401240001"); String beforeImage = (String) media.get("before_image"); String afterImage = (String) media.get("after_image"); String videoUrl = (String) media.get("video_url"); ``` #### 3.5 查询回调状态 ```java Map callbackStatus = client.getOrderApi() .getCallbackStatus("HH202401240001"); int status = (int) callbackStatus.get("callback_status"); // 0-未推送, 1-成功, 2-失败 int retryCount = (int) callbackStatus.get("retry_count"); ``` #### 3.6 获取静态柜分层识别结果 ```java Map layerResult = client.getOrderApi() .getLayerRecognize("HH202401240001"); List> layers = (List) layerResult.get("layers"); for (Map layer : layers) { int layerNo = (int) layer.get("layer_no"); String beforeImage = (String) layer.get("before_image"); String afterImage = (String) layer.get("after_image"); List> goodsList = (List) layer.get("goods_list"); } ``` --- ## 高级用法 ### 手动管理access_token SDK会自动管理token,但如果需要手动设置: ```java // 手动设置token(如果你已经有token并自行管理) client.setAccessToken("your_access_token", 1296000); // 15天有效期(秒) // 或者让SDK自动获取token String token = client.getAccessToken(); ``` ### 自定义HTTP配置 ```java HahaConfig config = HahaConfig.builder() .apiBaseUrl("http://api.hahabianli.com") .appId("your_app_id") .appSecret("your_app_secret") .connectTimeout(15) // 连接超时15秒 .readTimeout(60) // 读取超时60秒 .writeTimeout(60) // 写入超时60秒 .enableLog(true) // 启用日志 .build(); HahaClient client = new HahaClient(config); ``` ### 资源释放 ```java // 程序退出前关闭客户端,释放连接池资源 client.shutdown(); ``` --- ## 异常处理 SDK统一使用 `HahaException` 异常: ```java import com.haha.sdk.exception.HahaException; try { OpenDoorResult result = client.getDeviceApi() .openDoor("device001", "ORDER001", null); } catch (HahaException e) { // 错误码(对应API文档的错误码) Integer errorCode = e.getCode(); // 错误信息 String errorMsg = e.getMessage(); System.err.println("错误码: " + errorCode + ", 错误信息: " + errorMsg); // 如果需要查看底层异常 if (e.getCause() != null) { e.getCause().printStackTrace(); } } ``` ### 常见错误码 | 错误码 | 说明 | 处理方案 | |--------|------|----------| | 1 | SUCCESS | 操作成功 | | 0 | FAIL | 操作失败 | | -1 | 参数错误 | 检查请求参数 | | -2 | 签名错误 | 检查AppSecret | | -3 | token无效 | 重新获取token | | -4 | token过期 | SDK会自动刷新 | | -100 | 设备不存在 | 检查设备ID | | -101 | 设备离线 | 等待设备上线 | | -103 | 设备使用中 | 等待购物结束 | --- ## 完整接口列表 ### 设备接口(9个) | 方法 | 说明 | |------|------| | `getDeviceList` | 获取设备列表 | | `isMultiDoorUnique` | 查询是否多门单开 | | `openDoor` | 设备开门 | | `getOnlineStatus` | 查询在线状态 | | `setVolume` | 设置音量 | | `getDeviceStatus` | 查询设备和锁状态 | ### 商品接口(10个) | 方法 | 说明 | |------|------| | `getDeviceGoodsList` | 设备可售商品列表 | | `getTotalList` | 商品总库列表 | | `addToMerchant` | 添加至商家库 | | `getMerchantList` | 商家商品库列表 | | `setStatus` | 商品上下架 | | `getLayerTemplate` | 获取层模板 | | `updateLayerTemplate` | 更新层模板 | | `applyNew` | 新品申请 | | `getIdMapping` | 商品ID对应关系 | | `mergeGoods` | 商品合并 | ### 订单接口(8个) | 方法 | 说明 | |------|------| | `getRecognizeResult` | 查询识别结果 | | `queryOrder` | 查询订单详情 | | `setPayStatus` | 设置支付状态 | | `getOrderMedia` | 获取图片/视频 | | `getCallbackStatus` | 查询回调状态 | | `getLayerRecognize` | 静态柜分层结果 | --- ## 常见问题 ### Q1: access_token有效期多久? A: 15天(1296000秒)。SDK会自动管理,到期前2天内会自动刷新。 ### Q2: 如何处理识别结果通知? A: 哈哈平台会通过回调地址推送识别结果。商户需要: 1. 在商家后台配置回调地址 2. 接收POST通知 3. 根据商品列表扣款 4. 调用`setPayStatus`接口告知支付结果 ### Q3: 识别置信度低于0.8怎么办? A: 建议通过`getOrderMedia`接口获取购物视频进行人工审核。 ### Q4: 多门柜如何开门? A: 先调用`isMultiDoorUnique`判断设备类型,如果返回2(双门),开门时需传递`doorIndex`参数(0或1)。 ### Q5: SDK是否线程安全? A: 是的。`HahaClient`可以在多线程环境中安全使用,建议作为单例管理。 --- ## 技术支持 - 文档地址: https://showdoc.hahabianli.com/web/#/685869388/275897889 - API基础地址: http://api.hahabianli.com - 联系方式: 请联系哈哈运营人员 --- ## 开源协议 Apache License 2.0 --- **版本**: 1.0.0 **更新时间**: 2024-01-24