Commit e013b1add48585753874f8d6ce51d03299fc0f3a
1 parent
b4ce72ea
feat(iot):【网关设备:80%】动态注册的初步实现(已测试)
Showing
6 changed files
with
54 additions
and
101 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java
| @@ -4,7 +4,6 @@ import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | @@ -4,7 +4,6 @@ import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | ||
| 4 | import cn.idev.excel.annotation.ExcelProperty; | 4 | import cn.idev.excel.annotation.ExcelProperty; |
| 5 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | 5 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| 6 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | 6 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 7 | -import cn.iocoder.yudao.module.iot.enums.DictTypeConstants; | ||
| 8 | import io.swagger.v3.oas.annotations.media.Schema; | 7 | import io.swagger.v3.oas.annotations.media.Schema; |
| 9 | import lombok.Data; | 8 | import lombok.Data; |
| 10 | 9 | ||
| @@ -80,10 +79,6 @@ public class IotDeviceRespVO { | @@ -80,10 +79,6 @@ public class IotDeviceRespVO { | ||
| 80 | @ExcelProperty("设备密钥") | 79 | @ExcelProperty("设备密钥") |
| 81 | private String deviceSecret; | 80 | private String deviceSecret; |
| 82 | 81 | ||
| 83 | - @Schema(description = "认证类型(如一机一密、动态注册)", example = "2") | ||
| 84 | - @ExcelProperty("认证类型(如一机一密、动态注册)") | ||
| 85 | - private String authType; | ||
| 86 | - | ||
| 87 | @Schema(description = "设备配置", example = "{\"abc\": \"efg\"}") | 82 | @Schema(description = "设备配置", example = "{\"abc\": \"efg\"}") |
| 88 | private String config; | 83 | private String config; |
| 89 | 84 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
| @@ -123,11 +123,6 @@ public class IotDeviceDO extends TenantBaseDO { | @@ -123,11 +123,6 @@ public class IotDeviceDO extends TenantBaseDO { | ||
| 123 | * 设备密钥,用于设备认证 | 123 | * 设备密钥,用于设备认证 |
| 124 | */ | 124 | */ |
| 125 | private String deviceSecret; | 125 | private String deviceSecret; |
| 126 | - /** | ||
| 127 | - * 认证类型(如一机一密、动态注册) | ||
| 128 | - */ | ||
| 129 | - // TODO @haohao:是不是要枚举哈 | ||
| 130 | - private String authType; | ||
| 131 | 126 | ||
| 132 | /** | 127 | /** |
| 133 | * 设备位置的纬度 | 128 | * 设备位置的纬度 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
| @@ -810,7 +810,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -810,7 +810,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 810 | @Override | 810 | @Override |
| 811 | public IotDeviceRegisterRespDTO registerDevice(IotDeviceRegisterReqDTO reqDTO) { | 811 | public IotDeviceRegisterRespDTO registerDevice(IotDeviceRegisterReqDTO reqDTO) { |
| 812 | // 1.1 校验产品 | 812 | // 1.1 校验产品 |
| 813 | - IotProductDO product = productService.getProductByProductKey(reqDTO.getProductKey()); | 813 | + IotProductDO product = TenantUtils.executeIgnore(() -> |
| 814 | + productService.getProductByProductKey(reqDTO.getProductKey())); | ||
| 814 | if (product == null) { | 815 | if (product == null) { |
| 815 | throw exception(PRODUCT_NOT_EXISTS); | 816 | throw exception(PRODUCT_NOT_EXISTS); |
| 816 | } | 817 | } |
| @@ -822,21 +823,23 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -822,21 +823,23 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 822 | if (ObjUtil.notEqual(product.getProductSecret(), reqDTO.getProductSecret())) { | 823 | if (ObjUtil.notEqual(product.getProductSecret(), reqDTO.getProductSecret())) { |
| 823 | throw exception(DEVICE_REGISTER_SECRET_INVALID); | 824 | throw exception(DEVICE_REGISTER_SECRET_INVALID); |
| 824 | } | 825 | } |
| 825 | - // 1.4 校验设备是否已存在(已存在则不允许重复注册) | ||
| 826 | - IotDeviceDO device = getSelf().getDeviceFromCache(reqDTO.getProductKey(), reqDTO.getDeviceName()); | ||
| 827 | - if (device != null) { | ||
| 828 | - throw exception(DEVICE_REGISTER_ALREADY_EXISTS); | ||
| 829 | - } | 826 | + return TenantUtils.execute(product.getTenantId(), () -> { |
| 827 | + // 1.4 校验设备是否已存在(已存在则不允许重复注册) | ||
| 828 | + IotDeviceDO device = getSelf().getDeviceFromCache(reqDTO.getProductKey(), reqDTO.getDeviceName()); | ||
| 829 | + if (device != null) { | ||
| 830 | + throw exception(DEVICE_REGISTER_ALREADY_EXISTS); | ||
| 831 | + } | ||
| 830 | 832 | ||
| 831 | - // 2.1 自动创建设备 | ||
| 832 | - IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO() | ||
| 833 | - .setDeviceName(reqDTO.getDeviceName()) | ||
| 834 | - .setProductId(product.getId()); | ||
| 835 | - device = createDevice0(createReqVO); | ||
| 836 | - log.info("[registerDevice][产品({}) 自动创建设备({})]", | ||
| 837 | - reqDTO.getProductKey(), reqDTO.getDeviceName()); | ||
| 838 | - // 2.2 返回设备密钥 | ||
| 839 | - return new IotDeviceRegisterRespDTO(device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); | 833 | + // 2.1 自动创建设备 |
| 834 | + IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO() | ||
| 835 | + .setDeviceName(reqDTO.getDeviceName()) | ||
| 836 | + .setProductId(product.getId()); | ||
| 837 | + device = createDevice0(createReqVO); | ||
| 838 | + log.info("[registerDevice][产品({}) 自动创建设备({})]", | ||
| 839 | + reqDTO.getProductKey(), reqDTO.getDeviceName()); | ||
| 840 | + // 2.2 返回设备密钥 | ||
| 841 | + return new IotDeviceRegisterRespDTO(device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); | ||
| 842 | + }); | ||
| 840 | } | 843 | } |
| 841 | 844 | ||
| 842 | @Override | 845 | @Override |
| @@ -845,7 +848,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -845,7 +848,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 845 | IotDeviceDO gatewayDevice = getSelf().getDeviceFromCache(reqDTO.getGatewayProductKey(), reqDTO.getGatewayDeviceName()); | 848 | IotDeviceDO gatewayDevice = getSelf().getDeviceFromCache(reqDTO.getGatewayProductKey(), reqDTO.getGatewayDeviceName()); |
| 846 | 849 | ||
| 847 | // 2. 遍历注册每个子设备 | 850 | // 2. 遍历注册每个子设备 |
| 848 | - return registerSubDevices0(gatewayDevice, reqDTO.getSubDevices()); | 851 | + return TenantUtils.execute(gatewayDevice.getTenantId(), () -> |
| 852 | + registerSubDevices0(gatewayDevice, reqDTO.getSubDevices())); | ||
| 849 | } | 853 | } |
| 850 | 854 | ||
| 851 | @Override | 855 | @Override |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
| @@ -68,10 +68,8 @@ public class IotProductServiceImpl implements IotProductService { | @@ -68,10 +68,8 @@ public class IotProductServiceImpl implements IotProductService { | ||
| 68 | @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id") | 68 | @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id") |
| 69 | public void updateProduct(IotProductSaveReqVO updateReqVO) { | 69 | public void updateProduct(IotProductSaveReqVO updateReqVO) { |
| 70 | updateReqVO.setProductKey(null); // 不更新产品标识 | 70 | updateReqVO.setProductKey(null); // 不更新产品标识 |
| 71 | - // 1.1 校验存在 | ||
| 72 | - IotProductDO iotProductDO = validateProductExists(updateReqVO.getId()); | ||
| 73 | - // 1.2 发布状态不可更新 | ||
| 74 | - validateProductStatus(iotProductDO); | 71 | + // 1. 校验存在 |
| 72 | + validateProductExists(updateReqVO.getId()); | ||
| 75 | 73 | ||
| 76 | // 2. 更新 | 74 | // 2. 更新 |
| 77 | IotProductDO updateObj = BeanUtils.toBean(updateReqVO, IotProductDO.class); | 75 | IotProductDO updateObj = BeanUtils.toBean(updateReqVO, IotProductDO.class); |
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java
| @@ -48,77 +48,10 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { | @@ -48,77 +48,10 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { | ||
| 48 | private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; | 48 | private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; |
| 49 | 49 | ||
| 50 | /** | 50 | /** |
| 51 | - * 产品密钥(从 iot_product 表的 product_secret 字段获取),用于动态注册 | ||
| 52 | - */ | ||
| 53 | - private static final String PRODUCT_SECRET = "your_product_secret"; | ||
| 54 | - | ||
| 55 | - /** | ||
| 56 | - * 动态注册的设备名称 | ||
| 57 | - */ | ||
| 58 | - private static final String REGISTER_DEVICE_NAME = "test-register-device"; | ||
| 59 | - | ||
| 60 | - /** | ||
| 61 | * 直连设备 Token:从 {@link #testAuth()} 方法获取后,粘贴到这里 | 51 | * 直连设备 Token:从 {@link #testAuth()} 方法获取后,粘贴到这里 |
| 62 | */ | 52 | */ |
| 63 | private static final String TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTMwNTA1NSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.mf3MEATCn5bp6cXgULunZjs8d00RGUxj96JEz0hMS7k"; | 53 | private static final String TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTMwNTA1NSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.mf3MEATCn5bp6cXgULunZjs8d00RGUxj96JEz0hMS7k"; |
| 64 | 54 | ||
| 65 | - // ===================== 动态注册测试 ===================== | ||
| 66 | - | ||
| 67 | - /** | ||
| 68 | - * 直连设备动态注册测试(一型一密) | ||
| 69 | - * <p> | ||
| 70 | - * 使用产品密钥(productSecret)验证身份,成功后返回设备密钥(deviceSecret) | ||
| 71 | - * <p> | ||
| 72 | - * 注意:此接口不需要 Token 认证 | ||
| 73 | - */ | ||
| 74 | - @Test | ||
| 75 | - public void testDeviceRegister() { | ||
| 76 | - // 1.1 构建请求 | ||
| 77 | - String url = String.format("http://%s:%d/auth/register/device", SERVER_HOST, SERVER_PORT); | ||
| 78 | - // 1.2 构建请求参数 | ||
| 79 | - IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO(); | ||
| 80 | - reqDTO.setProductKey(PRODUCT_KEY); | ||
| 81 | - reqDTO.setDeviceName(REGISTER_DEVICE_NAME); | ||
| 82 | - reqDTO.setProductSecret(PRODUCT_SECRET); | ||
| 83 | - String payload = JsonUtils.toJsonString(reqDTO); | ||
| 84 | - // 1.3 输出请求 | ||
| 85 | - log.info("[testDeviceRegister][请求 URL: {}]", url); | ||
| 86 | - log.info("[testDeviceRegister][请求体: {}]", payload); | ||
| 87 | - | ||
| 88 | - // 2.1 发送请求 | ||
| 89 | - String response = HttpUtil.post(url, payload); | ||
| 90 | - // 2.2 输出结果 | ||
| 91 | - log.info("[testDeviceRegister][响应体: {}]", response); | ||
| 92 | - log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]"); | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | - /** | ||
| 96 | - * 测试动态注册后使用 deviceSecret 进行认证 | ||
| 97 | - * <p> | ||
| 98 | - * 此测试需要先执行 testDeviceRegister 获取 deviceSecret | ||
| 99 | - */ | ||
| 100 | - @Test | ||
| 101 | - public void testAuthAfterRegister() { | ||
| 102 | - // 1.1 构建请求 | ||
| 103 | - String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); | ||
| 104 | - // TODO 将 testDeviceRegister 返回的 deviceSecret 填入此处 | ||
| 105 | - String deviceSecret = "返回的deviceSecret"; | ||
| 106 | - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, REGISTER_DEVICE_NAME, deviceSecret); | ||
| 107 | - IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() | ||
| 108 | - .setClientId(authInfo.getClientId()) | ||
| 109 | - .setUsername(authInfo.getUsername()) | ||
| 110 | - .setPassword(authInfo.getPassword()); | ||
| 111 | - String payload = JsonUtils.toJsonString(authReqDTO); | ||
| 112 | - // 1.2 输出请求 | ||
| 113 | - log.info("[testAuthAfterRegister][请求 URL: {}]", url); | ||
| 114 | - log.info("[testAuthAfterRegister][请求体: {}]", payload); | ||
| 115 | - | ||
| 116 | - // 2.1 发送请求 | ||
| 117 | - String response = HttpUtil.post(url, payload); | ||
| 118 | - // 2.2 输出结果 | ||
| 119 | - log.info("[testAuthAfterRegister][响应体: {}]", response); | ||
| 120 | - } | ||
| 121 | - | ||
| 122 | // ===================== 认证测试 ===================== | 55 | // ===================== 认证测试 ===================== |
| 123 | 56 | ||
| 124 | /** | 57 | /** |
| @@ -128,7 +61,7 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { | @@ -128,7 +61,7 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { | ||
| 128 | public void testAuth() { | 61 | public void testAuth() { |
| 129 | // 1.1 构建请求 | 62 | // 1.1 构建请求 |
| 130 | String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); | 63 | String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); |
| 131 | - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET); | 64 | + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET); |
| 132 | IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() | 65 | IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() |
| 133 | .setClientId(authInfo.getClientId()) | 66 | .setClientId(authInfo.getClientId()) |
| 134 | .setUsername(authInfo.getUsername()) | 67 | .setUsername(authInfo.getUsername()) |
| @@ -213,4 +146,34 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { | @@ -213,4 +146,34 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { | ||
| 213 | } | 146 | } |
| 214 | } | 147 | } |
| 215 | 148 | ||
| 149 | + // ===================== 动态注册测试 ===================== | ||
| 150 | + | ||
| 151 | + /** | ||
| 152 | + * 直连设备动态注册测试(一型一密) | ||
| 153 | + * <p> | ||
| 154 | + * 使用产品密钥(productSecret)验证身份,成功后返回设备密钥(deviceSecret) | ||
| 155 | + * <p> | ||
| 156 | + * 注意:此接口不需要 Token 认证 | ||
| 157 | + */ | ||
| 158 | + @Test | ||
| 159 | + public void testDeviceRegister() { | ||
| 160 | + // 1.1 构建请求 | ||
| 161 | + String url = String.format("http://%s:%d/auth/register/device", SERVER_HOST, SERVER_PORT); | ||
| 162 | + // 1.2 构建请求参数 | ||
| 163 | + IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO(); | ||
| 164 | + reqDTO.setProductKey(PRODUCT_KEY); | ||
| 165 | + reqDTO.setDeviceName("test-" + System.currentTimeMillis()); | ||
| 166 | + reqDTO.setProductSecret("test-product-secret"); | ||
| 167 | + String payload = JsonUtils.toJsonString(reqDTO); | ||
| 168 | + // 1.3 输出请求 | ||
| 169 | + log.info("[testDeviceRegister][请求 URL: {}]", url); | ||
| 170 | + log.info("[testDeviceRegister][请求体: {}]", payload); | ||
| 171 | + | ||
| 172 | + // 2.1 发送请求 | ||
| 173 | + String response = HttpUtil.post(url, payload); | ||
| 174 | + // 2.2 输出结果 | ||
| 175 | + log.info("[testDeviceRegister][响应体: {}]", response); | ||
| 176 | + log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]"); | ||
| 177 | + } | ||
| 178 | + | ||
| 216 | } | 179 | } |
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewayDeviceHttpProtocolIntegrationTest.java
| @@ -95,8 +95,6 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { | @@ -95,8 +95,6 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { | ||
| 95 | 95 | ||
| 96 | // ===================== 拓扑管理测试 ===================== | 96 | // ===================== 拓扑管理测试 ===================== |
| 97 | 97 | ||
| 98 | - // TODO @芋艿:待测试 | ||
| 99 | - | ||
| 100 | /** | 98 | /** |
| 101 | * 添加子设备拓扑关系测试 | 99 | * 添加子设备拓扑关系测试 |
| 102 | * <p> | 100 | * <p> |
| @@ -222,7 +220,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { | @@ -222,7 +220,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { | ||
| 222 | // 1.2 构建请求参数 | 220 | // 1.2 构建请求参数 |
| 223 | IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO(); | 221 | IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO(); |
| 224 | subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY); | 222 | subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY); |
| 225 | - subDevice.setDeviceName(SUB_DEVICE_NAME); | 223 | + subDevice.setDeviceName("mougezishebei"); |
| 226 | String payload = JsonUtils.toJsonString(MapUtil.builder() | 224 | String payload = JsonUtils.toJsonString(MapUtil.builder() |
| 227 | .put("id", IdUtil.fastSimpleUUID()) | 225 | .put("id", IdUtil.fastSimpleUUID()) |
| 228 | .put("method", IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod()) | 226 | .put("method", IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod()) |