diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java index f19a1bad68..01451506d6 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java @@ -5,7 +5,7 @@ import lombok.Data; /** * IoT 设备事件上报 Request DTO *

- * 用于 thing.event.{eventId}.post 消息的 params 参数 + * 用于 thing.event.post 消息的 params 参数 * * @author 芋道源码 * @see 阿里云 - 设备上报事件 @@ -16,7 +16,7 @@ public class IotDeviceEventPostReqDTO { /** * 事件标识符 */ - private String eventId; + private String identifier; /** * 事件输出参数 @@ -31,27 +31,24 @@ public class IotDeviceEventPostReqDTO { /** * 创建事件上报 DTO * - * @param eventId 事件标识符 + * @param identifier 事件标识符 * @param value 事件值 * @return DTO 对象 */ - public static IotDeviceEventPostReqDTO of(String eventId, Object value) { - return of(eventId, value, null); + public static IotDeviceEventPostReqDTO of(String identifier, Object value) { + return of(identifier, value, null); } /** * 创建事件上报 DTO(带时间) * - * @param eventId 事件标识符 + * @param identifier 事件标识符 * @param value 事件值 * @param time 上报时间 * @return DTO 对象 */ - public static IotDeviceEventPostReqDTO of(String eventId, Object value, Long time) { - return new IotDeviceEventPostReqDTO() - .setEventId(eventId) - .setValue(value) - .setTime(time); + public static IotDeviceEventPostReqDTO of(String identifier, Object value, Long time) { + return new IotDeviceEventPostReqDTO().setIdentifier(identifier).setValue(value).setTime(time); } } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java index 2bc4880070..ee2c917dd0 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.core.util; +import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.HmacAlgorithm; import lombok.AllArgsConstructor; @@ -53,27 +54,31 @@ public class IotDeviceAuthUtils { public static AuthInfo getAuthInfo(String productKey, String deviceName, String deviceSecret) { String clientId = buildClientId(productKey, deviceName); String username = buildUsername(productKey, deviceName); - String content = "clientId" + clientId + - "deviceName" + deviceName + - "deviceSecret" + deviceSecret + - "productKey" + productKey; - String password = buildPassword(deviceSecret, content); + String password = buildPassword(deviceSecret, + buildContent(clientId, productKey, deviceName, deviceSecret)); return new AuthInfo(clientId, username, password); } - private static String buildClientId(String productKey, String deviceName) { + public static String buildClientId(String productKey, String deviceName) { return String.format("%s.%s", productKey, deviceName); } - private static String buildUsername(String productKey, String deviceName) { + public static String buildUsername(String productKey, String deviceName) { return String.format("%s&%s", deviceName, productKey); } - private static String buildPassword(String deviceSecret, String content) { - return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, deviceSecret.getBytes()) + public static String buildPassword(String deviceSecret, String content) { + return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(deviceSecret)) .digestHex(content); } + public static String buildContent(String clientId, String productKey, String deviceName, String deviceSecret) { + return "clientId" + clientId + + "deviceName" + deviceName + + "deviceSecret" + deviceSecret + + "productKey" + productKey; + } + public static DeviceInfo parseUsername(String username) { String[] usernameParts = username.split("&"); if (usernameParts.length != 2) { diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java b/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java new file mode 100644 index 0000000000..dba9e6465a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java @@ -0,0 +1,136 @@ +package cn.iocoder.yudao.module.iot.gateway.protocol.http; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; +import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + + +/** + * IoT 直连设备 HTTP 协议集成测试(手动测试) + * + *

测试场景:直连设备(IotProductDeviceTypeEnum 的 DIRECT 类型)通过 HTTP 协议直接连接平台 + * + *

使用步骤: + *

    + *
  1. 启动 yudao-module-iot-gateway 服务(HTTP 端口 8082)
  2. + *
  3. 运行 {@link #testAuth()} 获取 token,将返回的 token 粘贴到 {@link #TOKEN} 常量
  4. + *
  5. 运行 {@link #testPropertyPost()} 测试属性上报,或运行 {@link #testEventPost()} 测试事件上报
  6. + *
+ * + * @author 芋道源码 + */ +@Slf4j +@SuppressWarnings("HttpUrlsUsage") +public class IotDirectDeviceHttpProtocolIntegrationTest { + + private static final String SERVER_HOST = "127.0.0.1"; + private static final int SERVER_PORT = 8092; + + // 设备信息(根据实际情况修改 PRODUCT_KEY、DEVICE_NAME、DEVICE_SECRET,从 iot_device 表查询) + private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; + private static final String DEVICE_NAME = "small"; + private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; + + /** + * 设备 Token:从 {@link #testAuth()} 方法获取后,粘贴到这里 + */ + private static final String TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTMwNTA1NSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.mf3MEATCn5bp6cXgULunZjs8d00RGUxj96JEz0hMS7k"; + + /** + * 认证测试:获取设备 Token + */ + @Test + public void testAuth() { + // 1.1 构建请求 + String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); + IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET); + IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() + .setClientId(authInfo.getClientId()) + .setUsername(authInfo.getUsername()) + .setPassword(authInfo.getPassword()); + String payload = JsonUtils.toJsonString(authReqDTO); + // 1.2 输出请求 + log.info("[testAuth][请求 URL: {}]", url); + log.info("[testAuth][请求体: {}]", payload); + + // 2.1 发送请求 + String response = HttpUtil.post(url, payload); + // 2.2 输出结果 + log.info("[testAuth][响应体: {}]", response); + log.info("[testAuth][请将返回的 token 复制到 TOKEN 常量中]"); + } + + /** + * 属性上报测试 + */ + @Test + public void testPropertyPost() { + // 1.1 构建请求 + String url = String.format("http://%s:%d/topic/sys/%s/%s/thing/property/post", + SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME); + String payload = JsonUtils.toJsonString(MapUtil.builder() + .put("id", IdUtil.fastSimpleUUID()) + .put("method", IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()) + .put("version", "1.0") + .put("params", IotDevicePropertyPostReqDTO.of(MapUtil.builder() + .put("width", 1) + .put("height", "2") + .build()) + ) + .build()); + // 1.2 输出请求 + log.info("[testPropertyPost][请求 URL: {}]", url); + log.info("[testPropertyPost][请求体: {}]", payload); + + // 2.1 发送请求 + try (HttpResponse httpResponse = HttpUtil.createPost(url) + .header("Authorization", TOKEN) + .body(payload) + .execute()) { + // 2.2 输出结果 + log.info("[testPropertyPost][响应体: {}]", httpResponse.body()); + } + } + + /** + * 事件上报测试 + */ + @Test + public void testEventPost() { + // 1.1 构建请求 + String url = String.format("http://%s:%d/topic/sys/%s/%s/thing/event/post", + SERVER_HOST, SERVER_PORT, PRODUCT_KEY, DEVICE_NAME); + String payload = JsonUtils.toJsonString(MapUtil.builder() + .put("id", IdUtil.fastSimpleUUID()) + .put("method", IotDeviceMessageMethodEnum.EVENT_POST.getMethod()) + .put("version", "1.0") + .put("params", IotDeviceEventPostReqDTO.of( + "eat", + MapUtil.builder().put("rice", 3).build(), + System.currentTimeMillis()) + ) + .build()); + // 1.2 输出请求 + log.info("[testEventPost][请求 URL: {}]", url); + log.info("[testEventPost][请求体: {}]", payload); + + // 2.1 发送请求 + try (HttpResponse httpResponse = HttpUtil.createPost(url) + .header("Authorization", TOKEN) + .body(payload) + .execute()) { + // 2.2 输出结果 + log.info("[testEventPost][响应体: {}]", httpResponse.body()); + } + } + +}