From 33fed79820e1e042a8fc0db911308a9dc85506d9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 11 Jun 2025 20:35:09 +0800 Subject: [PATCH] =?UTF-8?q?reactor=EF=BC=9A=E3=80=90IoT=20=E7=89=A9?= =?UTF-8?q?=E8=81=94=E7=BD=91=E3=80=91=E4=BC=98=E5=8C=96=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E7=9A=84=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/DictTypeConstants.java | 2 + .../iot/api/device/IoTDeviceApiImpl.java | 36 ++- .../dal/dataobject/product/IotProductDO.java | 6 + .../iot/dal/redis/RedisKeyConstants.java | 11 +- .../device/IotDeviceMessageSubscriber.java | 22 +- .../iot/service/device/IotDeviceService.java | 40 ++- .../service/device/IotDeviceServiceImpl.java | 46 +++- .../service/product/IotProductService.java | 10 + .../product/IotProductServiceImpl.java | 15 +- .../IotRuleSceneDeviceControlAction.java | 2 +- .../mapper/device/IotDeviceMessageMapper.xml | 17 +- .../iot/core/biz/IotDeviceCommonApi.java | 6 +- ...nfoReqDTO.java => IotDeviceGetReqDTO.java} | 11 +- ...InfoRespDTO.java => IotDeviceRespDTO.java} | 23 +- .../iot/gateway/enums/ErrorCodeConstants.java | 3 + .../http/router/IotHttpAbstractHandler.java | 13 +- .../http/router/IotHttpAuthHandler.java | 18 +- .../http/router/IotHttpUpstreamHandler.java | 2 +- .../auth/IotDeviceTokenServiceImpl.java | 2 +- .../service/device/IotDeviceCacheService.java | 75 ------ .../device/IotDeviceCacheServiceImpl.java | 241 ------------------ .../device/IotDeviceClientServiceImpl.java | 107 -------- .../service/device/IotDeviceService.java | 29 +++ .../service/device/IotDeviceServiceImpl.java | 81 ++++++ .../message/IotDeviceMessageService.java | 24 +- .../message/IotDeviceMessageServiceImpl.java | 115 +++++++++ .../device/remote/IotDeviceApiImpl.java | 74 ++++++ .../message/IotDeviceMessageServiceImpl.java | 139 ---------- 28 files changed, 486 insertions(+), 684 deletions(-) rename yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/{IotDeviceInfoReqDTO.java => IotDeviceGetReqDTO.java} (60%) rename yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/{IotDeviceInfoRespDTO.java => IotDeviceRespDTO.java} (64%) delete mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheService.java delete mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceClientServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceService.java create mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceServiceImpl.java rename yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/{ => device}/message/IotDeviceMessageService.java (57%) create mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java index 04df143bed..dc94854566 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java @@ -16,4 +16,6 @@ public class DictTypeConstants { public static final String DEVICE_STATE = "iot_device_state"; + public static final String CODEC_TYPE = "iot_codec_type"; + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java index 1996e6e26f..eb55b1852a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java @@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.iot.api.device; import cn.iocoder.yudao.framework.common.enums.RpcConstants; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import jakarta.annotation.security.PermitAll; import org.springframework.context.annotation.Primary; @@ -30,6 +33,8 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { @Resource private IotDeviceService deviceService; + @Resource + private IotProductService productService; @Override @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth") @@ -39,24 +44,17 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { } @Override - @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/info") + @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/get") // 特殊:方便调用,暂时使用 POST,实际更推荐 GET @PermitAll - public CommonResult getDeviceInfo(@RequestBody IotDeviceInfoReqDTO infoReqDTO) { - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - infoReqDTO.getProductKey(), infoReqDTO.getDeviceName()); - - if (device == null) { - return success(null); - } - - IotDeviceInfoRespDTO respDTO = new IotDeviceInfoRespDTO(); - respDTO.setDeviceId(device.getId()); - respDTO.setProductKey(device.getProductKey()); - respDTO.setDeviceName(device.getDeviceName()); - respDTO.setDeviceKey(device.getDeviceKey()); - respDTO.setTenantId(device.getTenantId()); - - return success(respDTO); + public CommonResult getDevice(@RequestBody IotDeviceGetReqDTO getReqDTO) { + IotDeviceDO device = getReqDTO.getId() != null ? deviceService.getDeviceFromCache(getReqDTO.getId()) + : deviceService.getDeviceFromCache(getReqDTO.getProductKey(), getReqDTO.getDeviceName()); + return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { + IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); + if (product != null) { + deviceDTO.setCodecType(product.getCodecType()); + } + })); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java index 3caebbccb8..2611a45f0e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java @@ -70,6 +70,12 @@ public class IotProductDO extends TenantBaseDO { */ private Integer netType; + /** + * 编解码器类型 + * + * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#CODEC_TYPE} + */ + private String codecType; /** * 接入网关协议 *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java index 836a2ed1c9..b074be47d8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java @@ -39,11 +39,20 @@ public interface RedisKeyConstants { /** * 设备信息的数据缓存,使用 Spring Cache 操作(忽略租户) * - * KEY 格式:device_${productKey}_${deviceName} + * KEY 格式 1:device_${id} + * KEY 格式 2:device_${productKey}_${deviceName} * VALUE 数据类型:String(JSON) */ String DEVICE = "iot:device"; + /** + * 产品信息的数据缓存,使用 Spring Cache 操作(忽略租户) + * + * KEY 格式:product_${id} + * VALUE 数据类型:String(JSON) + */ + String PRODUCT = "iot:product"; + /** * 物模型的数据缓存,使用 Spring Cache 操作(忽略租户) * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java index 3da5765010..d830812e75 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.device; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; @@ -61,18 +62,19 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber { + // 1.1 更新设备的最后时间 + IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId()); + devicePropertyService.updateDeviceReportTime(device.getProductKey(), device.getDeviceName(), LocalDateTime.now()); + // 1.2 更新设备的连接 server + devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId()); - // 2. 未上线的设备,强制上线 - forceDeviceOnline(message, device); + // 2. 未上线的设备,强制上线 + forceDeviceOnline(message, device); - // 3. 核心:处理消息 - deviceMessageService.handleUpstreamDeviceMessage(message, device); + // 3. 核心:处理消息 + deviceMessageService.handleUpstreamDeviceMessage(message, device); + }); } private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index e25ab722c2..6d3a23542f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -105,6 +105,14 @@ public interface IotDeviceService { */ IotDeviceDO validateDeviceExists(Long id); + /** + * 【缓存】校验设备是否存在 + * + * @param id 设备 ID + * @return 设备对象 + */ + IotDeviceDO validateDeviceExistsFromCache(Long id); + /** * 获得设备 * @@ -113,6 +121,27 @@ public interface IotDeviceService { */ IotDeviceDO getDevice(Long id); + /** + * 【缓存】获得设备信息 + *

+ * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! + * + * @param id 编号 + * @return IoT 设备 + */ + IotDeviceDO getDeviceFromCache(Long id); + + /** + * 【缓存】根据产品 key 和设备名称,获得设备信息 + *

+ * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! + * + * @param productKey 产品 key + * @param deviceName 设备名称 + * @return 设备信息 + */ + IotDeviceDO getDeviceFromCache(String productKey, String deviceName); + /** * 根据设备 key 获得设备 * @@ -177,17 +206,6 @@ public interface IotDeviceService { */ Long getDeviceCountByGroupId(Long groupId); - /** - * 【缓存】根据产品 key 和设备名称,获得设备信息 - *

- * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! - * - * @param productKey 产品 key - * @param deviceName 设备名称 - * @return 设备信息 - */ - IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); - /** * 导入设备 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 9795b8338f..e1f80df704 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -28,6 +28,7 @@ import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -221,6 +222,15 @@ public class IotDeviceServiceImpl implements IotDeviceService { return device; } + @Override + public IotDeviceDO validateDeviceExistsFromCache(Long id) { + IotDeviceDO device = getSelf().getDeviceFromCache(id); + if (device == null) { + throw exception(DEVICE_NOT_EXISTS); + } + return device; + } + /** * 校验网关设备是否存在 * @@ -241,6 +251,20 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectById(id); } + @Override + @Cacheable(value = RedisKeyConstants.DEVICE, key = "#id", unless = "#result == null") + @TenantIgnore // 忽略租户信息 + public IotDeviceDO getDeviceFromCache(Long id) { + return deviceMapper.selectById(id); + } + + @Override + @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null") + @TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的 + public IotDeviceDO getDeviceFromCache(String productKey, String deviceName) { + return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); + } + @Override public IotDeviceDO getDeviceByDeviceKey(String deviceKey) { return deviceMapper.selectByDeviceKey(deviceKey); @@ -308,13 +332,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectCountByGroupId(groupId); } - @Override - @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null") - @TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的 - public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { - return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); - } - /** * 生成 deviceKey * @@ -425,14 +442,13 @@ public class IotDeviceServiceImpl implements IotDeviceService { devices.forEach(this::deleteDeviceCache); } - @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") + @Caching(evict = { + @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.id"), + @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") + }) public void deleteDeviceCache0(IotDeviceDO device) { } - private IotDeviceServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - @Override public Long getDeviceCount(LocalDateTime createTime) { return deviceMapper.selectCountByCreateTime(createTime); @@ -477,7 +493,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { } String deviceName = deviceInfo.getDeviceName(); String productKey = deviceInfo.getProductKey(); - IotDeviceDO device = getSelf().getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName); + IotDeviceDO device = getSelf().getDeviceFromCache(productKey, deviceName); if (device == null) { log.warn("[authDevice][设备({}/{}) 不存在]", productKey, deviceName); return false; @@ -492,4 +508,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { return true; } + private IotDeviceServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java index 8497d73aa9..9d94219c50 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java @@ -47,6 +47,16 @@ public interface IotProductService { */ IotProductDO getProduct(Long id); + /** + * 【缓存】获得产品 + *

+ * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! + * + * @param id 编号 + * @return 产品 + */ + IotProductDO getProductFromCache(Long id); + /** * 根据产品 key 获得产品 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 4ccdd77cad..44e4819938 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -2,15 +2,19 @@ package cn.iocoder.yudao.module.iot.service.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -56,6 +60,7 @@ public class IotProductServiceImpl implements IotProductService { } @Override + @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id") public void updateProduct(IotProductSaveReqVO updateReqVO) { updateReqVO.setProductKey(null); // 不更新产品标识 // 1.1 校验存在 @@ -68,6 +73,7 @@ public class IotProductServiceImpl implements IotProductService { } @Override + @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id") public void deleteProduct(Long id) { // 1.1 校验存在 IotProductDO iotProductDO = validateProductExists(id); @@ -106,6 +112,13 @@ public class IotProductServiceImpl implements IotProductService { return productMapper.selectById(id); } + @Override + @Cacheable(value = RedisKeyConstants.PRODUCT, key = "#id", unless = "#result == null") + @TenantIgnore // 忽略租户信息 + public IotProductDO getProductFromCache(Long id) { + return productMapper.selectById(id); + } + @Override public IotProductDO getProductByProductKey(String productKey) { return productMapper.selectByProductKey(productKey); @@ -118,6 +131,7 @@ public class IotProductServiceImpl implements IotProductService { @Override @DSTransactional(rollbackFor = Exception.class) + @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id") public void updateProductStatus(Long id, Integer status) { // 1. 校验存在 validateProductExists(id); @@ -143,5 +157,4 @@ public class IotProductServiceImpl implements IotProductService { return productMapper.selectCountByCreateTime(createTime); } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java index ff57e999a0..0ae4f4bc0d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java @@ -31,7 +31,7 @@ public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { Assert.notNull(control, "设备控制配置不能为空"); // 遍历每个设备,下发消息 control.getDeviceNames().forEach(deviceName -> { - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(control.getProductKey(), deviceName); + IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName); if (device == null) { log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config); return; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml index 5949f56bb5..3fd63e2788 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml @@ -9,7 +9,6 @@ ts TIMESTAMP, id NCHAR(50), report_time TIMESTAMP, - device_id BIGINT, tenant_id BIGINT, server_id NCHAR(50), upstream BOOL, @@ -29,21 +28,21 @@ INSERT INTO device_message_${deviceId} ( - ts, id, report_time, device_id, tenant_id, - server_id, upstream, request_id, method, params, - data, code + ts, id, report_time, tenant_id, server_id, + upstream, request_id, method, params, data, + code ) USING device_message TAGS (#{deviceId}) VALUES ( - #{ts}, #{id}, #{reportTime}, #{deviceId}, #{tenantId}, - #{serverId}, #{upstream}, #{requestId}, #{method}, #{params}, - #{data}, #{code} + #{ts}, #{id}, #{reportTime}, #{tenantId}, #{serverId}, + #{upstream}, #{requestId}, #{method}, #{params}, #{data}, + #{code} ) - \ No newline at end of file + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java index e636393a83..29d540e73e 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.iot.core.biz; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; /** * IoT 设备通用 API @@ -26,6 +26,6 @@ public interface IotDeviceCommonApi { * @param infoReqDTO 设备信息请求 * @return 设备信息 */ - CommonResult getDeviceInfo(IotDeviceInfoReqDTO infoReqDTO); + CommonResult getDevice(IotDeviceGetReqDTO infoReqDTO); } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoReqDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceGetReqDTO.java similarity index 60% rename from yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoReqDTO.java rename to yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceGetReqDTO.java index 7668bbbe92..981509dd6a 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceGetReqDTO.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.core.biz.dto; -import jakarta.validation.constraints.NotBlank; import lombok.Data; /** @@ -9,18 +8,20 @@ import lombok.Data; * @author 芋道源码 */ @Data -public class IotDeviceInfoReqDTO { +public class IotDeviceGetReqDTO { + + /** + * 设备编号 + */ + private Long id; /** * 产品标识 */ - @NotBlank(message = "产品标识不能为空") private String productKey; - /** * 设备名称 */ - @NotBlank(message = "设备名称不能为空") private String deviceName; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoRespDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceRespDTO.java similarity index 64% rename from yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoRespDTO.java rename to yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceRespDTO.java index 3ac81358af..add1167801 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoRespDTO.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceRespDTO.java @@ -8,31 +8,34 @@ import lombok.Data; * @author 芋道源码 */ @Data -public class IotDeviceInfoRespDTO { +public class IotDeviceRespDTO { /** * 设备编号 */ - private Long deviceId; - + private Long id; /** * 产品标识 */ private String productKey; - /** * 设备名称 */ private String deviceName; - - /** - * 设备密钥 - */ - private String deviceKey; - /** * 租户编号 */ private Long tenantId; + // ========== 产品相关字段 ========== + + /** + * 产品编号 + */ + private Long productId; + /** + * 编解码器类型 + */ + private String codecType; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/enums/ErrorCodeConstants.java index bdf264fd89..90afda224e 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/enums/ErrorCodeConstants.java @@ -13,4 +13,7 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_AUTH_FAIL = new ErrorCode(1_051_001_000, "设备鉴权失败"); // 对应阿里云 20000 ErrorCode DEVICE_TOKEN_EXPIRED = new ErrorCode(1_051_001_002, "token 失效。需重新调用 auth 进行鉴权,获取token"); // 对应阿里云 20001 + // ========== 设备信息 1-050-002-000 ============ + ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_051_002_001, "设备({}/{}) 不存在"); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java index 25898a0686..f5461c2c51 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java @@ -36,14 +36,10 @@ public abstract class IotHttpAbstractHandler implements Handler public final void handle(RoutingContext context) { try { // 1. 前置处理 - CommonResult result = beforeHandle(context); - if (result != null) { - writeResponse(context, result); - return; - } + beforeHandle(context); // 2. 执行逻辑 - result = handle0(context); + CommonResult result = handle0(context); writeResponse(context, result); } catch (ServiceException e) { writeResponse(context, CommonResult.error(e.getCode(), e.getMessage())); @@ -55,11 +51,11 @@ public abstract class IotHttpAbstractHandler implements Handler protected abstract CommonResult handle0(RoutingContext context); - private CommonResult beforeHandle(RoutingContext context) { + private void beforeHandle(RoutingContext context) { // 如果不需要认证,则不走前置处理 String path = context.request().path(); if (ObjUtil.equal(path, IotHttpAuthHandler.PATH)) { - return null; + return; } // 解析参数 @@ -84,7 +80,6 @@ public abstract class IotHttpAbstractHandler implements Handler || ObjUtil.notEqual(deviceName, deviceInfo.getDeviceName())) { throw exception(FORBIDDEN); } - return null; } @SuppressWarnings("deprecation") diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java index a2a25a1ecc..7b2e923349 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java @@ -9,11 +9,10 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; -import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService; +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; @@ -35,19 +34,16 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { private final IotHttpUpstreamProtocol protocol; - private final IotDeviceMessageProducer deviceMessageProducer; - private final IotDeviceTokenService deviceTokenService; - private final IotDeviceCommonApi deviceClientService; + private final IotDeviceCommonApi deviceApi; private final IotDeviceMessageService deviceMessageService; public IotHttpAuthHandler(IotHttpUpstreamProtocol protocol) { this.protocol = protocol; - this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class); this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); - this.deviceClientService = SpringUtil.getBean(IotDeviceCommonApi.class); + this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class); this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); } @@ -69,9 +65,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { } // 2.1 执行认证 - CommonResult result = deviceClientService.authDevice(new IotDeviceAuthReqDTO() + CommonResult result = deviceApi.authDevice(new IotDeviceAuthReqDTO() .setClientId(clientId).setUsername(username).setPassword(password)); - result.checkError();; + result.checkError(); if (!BooleanUtil.isTrue(result.getData())) { throw exception(DEVICE_AUTH_FAIL); } @@ -82,9 +78,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { Assert.notBlank(token, "生成 token 不能为空位"); // 3. 执行上线 - IotDeviceMessage message = deviceMessageService.buildDeviceMessageOfStateOnline( + IotDeviceMessage message = IotDeviceMessage.buildStateOnline(); + deviceMessageService.sendDeviceMessage(message, deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId()); - deviceMessageProducer.sendDeviceMessage(message); // 构建响应数据 return success(MapUtil.of("token", token)); diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java index 96c2a3c0f1..bee1516a44 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; -import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService; +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; import io.vertx.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java index e6fe2fb816..79ba4e77e7 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java @@ -61,7 +61,7 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService { JSONObject payload = jwt.getPayloads(); // 检查过期时间 Long exp = payload.getLong("exp"); - if (exp == null || exp > System.currentTimeMillis() / 1000) { + if (exp == null || exp < System.currentTimeMillis() / 1000) { throw exception(DEVICE_TOKEN_EXPIRED); } String productKey = payload.getStr("productKey"); diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheService.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheService.java deleted file mode 100644 index efd8dc60f5..0000000000 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheService.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.iot.gateway.service.device; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT 设备缓存 Service 接口 - * - * @author 芋道源码 - */ -public interface IotDeviceCacheService { - - /** - * 设备信息 - */ - @Data - @AllArgsConstructor - @NoArgsConstructor - class DeviceInfo { - /** - * 设备编号 - */ - private Long deviceId; - /** - * 产品标识 - */ - private String productKey; - /** - * 设备名称 - */ - private String deviceName; - /** - * 设备密钥 - */ - private String deviceKey; - /** - * 租户编号 - */ - private Long tenantId; - } - - /** - * 根据 productKey 和 deviceName 获取设备信息 - * - * @param productKey 产品标识 - * @param deviceName 设备名称 - * @return 设备信息,如果不存在返回 null - */ - DeviceInfo getDeviceInfo(String productKey, String deviceName); - - /** - * 根据 deviceId 获取设备信息 - * - * @param deviceId 设备编号 - * @return 设备信息,如果不存在返回 null - */ - DeviceInfo getDeviceInfoById(Long deviceId); - - /** - * 清除设备缓存 - * - * @param deviceId 设备编号 - */ - void evictDeviceCache(Long deviceId); - - /** - * 清除设备缓存 - * - * @param productKey 产品标识 - * @param deviceName 设备名称 - */ - void evictDeviceCache(String productKey, String deviceName); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheServiceImpl.java deleted file mode 100644 index 5de9d6b719..0000000000 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheServiceImpl.java +++ /dev/null @@ -1,241 +0,0 @@ -package cn.iocoder.yudao.module.iot.gateway.service.device; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantLock; - -/** - * IoT 设备缓存 Service 实现类 - *

- * 使用本地缓存 + 远程 API 的方式获取设备信息,提高性能并避免敏感信息传输 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class IotDeviceCacheServiceImpl implements IotDeviceCacheService { - - /** - * 设备信息本地缓存 - * Key: deviceId - * Value: DeviceInfo - */ - private final ConcurrentHashMap deviceIdCache = new ConcurrentHashMap<>(); - - /** - * 设备名称到设备ID的映射缓存 - * Key: productKey:deviceName - * Value: deviceId - */ - private final ConcurrentHashMap deviceNameCache = new ConcurrentHashMap<>(); - - /** - * 锁对象,防止并发请求同一设备信息 - */ - private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>(); - - @Override - public DeviceInfo getDeviceInfo(String productKey, String deviceName) { - if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) { - log.warn("[getDeviceInfo][参数为空][productKey: {}][deviceName: {}]", productKey, deviceName); - return null; - } - - String cacheKey = buildDeviceNameCacheKey(productKey, deviceName); - - // 1. 先从缓存获取 deviceId - Long deviceId = deviceNameCache.get(cacheKey); - if (deviceId != null) { - DeviceInfo deviceInfo = deviceIdCache.get(deviceId); - if (deviceInfo != null) { - log.debug("[getDeviceInfo][缓存命中][productKey: {}][deviceName: {}][deviceId: {}]", - productKey, deviceName, deviceId); - return deviceInfo; - } - } - - // 2. 缓存未命中,从远程 API 获取 - return loadDeviceInfoFromApi(productKey, deviceName, cacheKey); - } - - @Override - public DeviceInfo getDeviceInfoById(Long deviceId) { - if (deviceId == null) { - log.warn("[getDeviceInfoById][deviceId 为空]"); - return null; - } - - // 1. 先从缓存获取 - DeviceInfo deviceInfo = deviceIdCache.get(deviceId); - if (deviceInfo != null) { - log.debug("[getDeviceInfoById][缓存命中][deviceId: {}]", deviceId); - return deviceInfo; - } - - // 2. 缓存未命中,从远程 API 获取 - return loadDeviceInfoByIdFromApi(deviceId); - } - - @Override - public void evictDeviceCache(Long deviceId) { - if (deviceId == null) { - return; - } - - DeviceInfo deviceInfo = deviceIdCache.remove(deviceId); - if (deviceInfo != null) { - String cacheKey = buildDeviceNameCacheKey(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); - deviceNameCache.remove(cacheKey); - log.info("[evictDeviceCache][清除设备缓存][deviceId: {}]", deviceId); - } - } - - @Override - public void evictDeviceCache(String productKey, String deviceName) { - if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) { - return; - } - - String cacheKey = buildDeviceNameCacheKey(productKey, deviceName); - Long deviceId = deviceNameCache.remove(cacheKey); - if (deviceId != null) { - deviceIdCache.remove(deviceId); - log.info("[evictDeviceCache][清除设备缓存][productKey: {}][deviceName: {}]", productKey, deviceName); - } - } - - /** - * 从远程 API 加载设备信息 - * - * @param productKey 产品标识 - * @param deviceName 设备名称 - * @param cacheKey 缓存键 - * @return 设备信息 - */ - private DeviceInfo loadDeviceInfoFromApi(String productKey, String deviceName, String cacheKey) { - // 使用锁防止并发请求同一设备信息 - ReentrantLock lock = lockMap.computeIfAbsent(cacheKey, k -> new ReentrantLock()); - lock.lock(); - try { - // 双重检查,防止重复请求 - Long deviceId = deviceNameCache.get(cacheKey); - if (deviceId != null) { - DeviceInfo deviceInfo = deviceIdCache.get(deviceId); - if (deviceInfo != null) { - return deviceInfo; - } - } - - log.info("[loadDeviceInfoFromApi][从远程API获取设备信息][productKey: {}][deviceName: {}]", - productKey, deviceName); - - try { - // 调用远程 API 获取设备信息 - IotDeviceCommonApi deviceCommonApi = SpringUtil.getBean(IotDeviceCommonApi.class); - IotDeviceInfoReqDTO reqDTO = new IotDeviceInfoReqDTO(); - reqDTO.setProductKey(productKey); - reqDTO.setDeviceName(deviceName); - - CommonResult result = deviceCommonApi.getDeviceInfo(reqDTO); - - if (result == null || !result.isSuccess() || result.getData() == null) { - log.warn("[loadDeviceInfoFromApi][远程API调用失败][productKey: {}][deviceName: {}][result: {}]", - productKey, deviceName, result); - return null; - } - - IotDeviceInfoRespDTO respDTO = result.getData(); - DeviceInfo deviceInfo = new DeviceInfo( - respDTO.getDeviceId(), - respDTO.getProductKey(), - respDTO.getDeviceName(), - respDTO.getDeviceKey(), - respDTO.getTenantId()); - - // 缓存设备信息 - cacheDeviceInfo(deviceInfo, cacheKey); - - log.info("[loadDeviceInfoFromApi][设备信息获取成功并已缓存][deviceInfo: {}]", deviceInfo); - return deviceInfo; - - } catch (Exception e) { - log.error("[loadDeviceInfoFromApi][远程API调用异常][productKey: {}][deviceName: {}]", - productKey, deviceName, e); - return null; - } - } finally { - lock.unlock(); - // 清理锁对象,避免内存泄漏 - if (lockMap.size() > 1000) { // 简单的清理策略 - lockMap.entrySet().removeIf(entry -> !entry.getValue().hasQueuedThreads()); - } - } - } - - /** - * 从远程 API 根据 deviceId 加载设备信息 - * - * @param deviceId 设备编号 - * @return 设备信息 - */ - private DeviceInfo loadDeviceInfoByIdFromApi(Long deviceId) { - String lockKey = "deviceId:" + deviceId; - ReentrantLock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock()); - lock.lock(); - try { - // 双重检查 - DeviceInfo deviceInfo = deviceIdCache.get(deviceId); - if (deviceInfo != null) { - return deviceInfo; - } - - log.info("[loadDeviceInfoByIdFromApi][从远程API获取设备信息][deviceId: {}]", deviceId); - - try { - // TODO: 这里需要添加根据 deviceId 获取设备信息的 API - // 暂时返回 null,等待 API 完善 - log.warn("[loadDeviceInfoByIdFromApi][根据deviceId获取设备信息的API尚未实现][deviceId: {}]", deviceId); - return null; - - } catch (Exception e) { - log.error("[loadDeviceInfoByIdFromApi][远程API调用异常][deviceId: {}]", deviceId, e); - return null; - } - } finally { - lock.unlock(); - } - } - - /** - * 缓存设备信息 - * - * @param deviceInfo 设备信息 - * @param cacheKey 缓存键 - */ - private void cacheDeviceInfo(DeviceInfo deviceInfo, String cacheKey) { - if (deviceInfo != null && deviceInfo.getDeviceId() != null) { - deviceIdCache.put(deviceInfo.getDeviceId(), deviceInfo); - deviceNameCache.put(cacheKey, deviceInfo.getDeviceId()); - } - } - - /** - * 构建设备名称缓存键 - * - * @param productKey 产品标识 - * @param deviceName 设备名称 - * @return 缓存键 - */ - private String buildDeviceNameCacheKey(String productKey, String deviceName) { - return productKey + ":" + deviceName; - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceClientServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceClientServiceImpl.java deleted file mode 100644 index ab499c42c7..0000000000 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceClientServiceImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -package cn.iocoder.yudao.module.iot.gateway.service.device; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.bean.BeanUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO; -import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import java.util.LinkedHashMap; - -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; - -/** - * Iot 设备信息 Service 实现类:调用远程的 device http 接口,进行设备认证、设备获取等 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { - - @Resource - private IotGatewayProperties gatewayProperties; - - private RestTemplate restTemplate; - - @PostConstruct - public void init() { - IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc(); - restTemplate = new RestTemplateBuilder() - .rootUri(rpc.getUrl() + "/rpc-api/iot/device") - .readTimeout(rpc.getReadTimeout()) - .connectTimeout(rpc.getConnectTimeout()) - .build(); - } - - @Override - public CommonResult authDevice(IotDeviceAuthReqDTO authReqDTO) { - return doPost("/auth", authReqDTO); - } - - @Override - public CommonResult getDeviceInfo(IotDeviceInfoReqDTO infoReqDTO) { - return doPostForDeviceInfo("/info", infoReqDTO); - } - - @SuppressWarnings("unchecked") - private CommonResult doPost(String url, T requestBody) { - try { - CommonResult result = restTemplate.postForObject(url, requestBody, - (Class>) (Class) CommonResult.class); - log.info("[doPost][url({}) requestBody({}) result({})]", url, requestBody, result); - Assert.notNull(result, "请求结果不能为空"); - return result; - } catch (Exception e) { - log.error("[doPost][url({}) requestBody({}) 发生异常]", url, requestBody, e); - return CommonResult.error(INTERNAL_SERVER_ERROR); - } - } - - @SuppressWarnings("unchecked") - private CommonResult doPostForDeviceInfo(String url, T requestBody) { - try { - // 使用 ParameterizedTypeReference 来处理泛型类型 - ParameterizedTypeReference>> typeRef = new ParameterizedTypeReference>>() { - }; - - HttpEntity requestEntity = new HttpEntity<>(requestBody); - ResponseEntity>> response = restTemplate.exchange(url, - HttpMethod.POST, requestEntity, typeRef); - - CommonResult> rawResult = response.getBody(); - log.info("[doPostForDeviceInfo][url({}) requestBody({}) rawResult({})]", url, requestBody, rawResult); - Assert.notNull(rawResult, "请求结果不能为空"); - - // 手动转换数据类型 - CommonResult result = new CommonResult<>(); - result.setCode(rawResult.getCode()); - result.setMsg(rawResult.getMsg()); - - if (rawResult.isSuccess() && rawResult.getData() != null) { - // 将 LinkedHashMap 转换为 IotDeviceInfoRespDTO - IotDeviceInfoRespDTO deviceInfo = BeanUtil.toBean(rawResult.getData(), IotDeviceInfoRespDTO.class); - result.setData(deviceInfo); - } - - return result; - } catch (Exception e) { - log.error("[doPostForDeviceInfo][url({}) requestBody({}) 发生异常]", url, requestBody, e); - return CommonResult.error(INTERNAL_SERVER_ERROR); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceService.java new file mode 100644 index 0000000000..c0d4943dab --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceService.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.gateway.service.device; + +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; + +/** + * IoT 设备信息 Service 接口 + * + * @author 芋道源码 + */ +public interface IotDeviceService { + + /** + * 根据 productKey 和 deviceName 获取设备信息 + * + * @param productKey 产品标识 + * @param deviceName 设备名称 + * @return 设备信息 + */ + IotDeviceRespDTO getDeviceFromCache(String productKey, String deviceName); + + /** + * 根据 id 获取设备信息 + * + * @param id 设备编号 + * @return 设备信息 + */ + IotDeviceRespDTO getDeviceFromCache(Long id); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceServiceImpl.java new file mode 100644 index 0000000000..fee48d10ec --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceServiceImpl.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.iot.gateway.service.device; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.Duration; + +import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache; + +/** + * IoT 设备信息 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class IotDeviceServiceImpl implements IotDeviceService { + + private static final Duration CACHE_EXPIRE = Duration.ofMinutes(1); + + /** + * 通过 id 查询设备的缓存 + */ + private final LoadingCache deviceCaches = buildAsyncReloadingCache( + CACHE_EXPIRE, + new CacheLoader<>() { + + @Override + public IotDeviceRespDTO load(Long id) { + CommonResult result = deviceApi.getDevice(new IotDeviceGetReqDTO().setId(id)); + IotDeviceRespDTO device = result.getCheckedData(); + Assert.notNull(device, "设备({}) 不能为空", id); + // 相互缓存 + deviceCaches2.put(new KeyValue<>(device.getProductKey(), device.getDeviceName()), device); + return device; + } + + }); + + /** + * 通过 productKey + deviceName 查询设备的缓存 + */ + private final LoadingCache, IotDeviceRespDTO> deviceCaches2 = buildAsyncReloadingCache( + CACHE_EXPIRE, + new CacheLoader<>() { + + @Override + public IotDeviceRespDTO load(KeyValue kv) { + CommonResult result = deviceApi.getDevice(new IotDeviceGetReqDTO() + .setProductKey(kv.getKey()).setDeviceName(kv.getValue())); + IotDeviceRespDTO device = result.getCheckedData(); + Assert.notNull(device, "设备({}/{}) 不能为空", kv.getKey(), kv.getValue()); + // 相互缓存 + deviceCaches.put(device.getId(), device); + return device; + } + }); + + @Resource + private IotDeviceCommonApi deviceApi; + + @Override + public IotDeviceRespDTO getDeviceFromCache(String productKey, String deviceName) { + return deviceCaches2.getUnchecked(new KeyValue<>(productKey, deviceName)); + } + + @Override + public IotDeviceRespDTO getDeviceFromCache(Long id) { + return deviceCaches.getUnchecked(id); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageService.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageService.java similarity index 57% rename from yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageService.java rename to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageService.java index 2feea15eb2..24134ba94a 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageService.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageService.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.gateway.service.message; +package cn.iocoder.yudao.module.iot.gateway.service.device.message; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -26,30 +26,20 @@ public interface IotDeviceMessageService { * @param bytes 消息内容 * @param productKey 产品 Key * @param deviceName 设备名称 - * @param serverId 设备连接的 serverId * @return 解码后的消息内容 */ IotDeviceMessage decodeDeviceMessage(byte[] bytes, - String productKey, String deviceName, String serverId); + String productKey, String deviceName); /** - * 构建【设备上线】消息 + * 发送消息 * + * @param message 消息 * @param productKey 产品 Key * @param deviceName 设备名称 - * @param serverId 设备连接的 serverId - * @return 消息 + * @param serverId 设备连接的 serverId */ - IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId); - - /** - * 构建【设备下线】消息 - * - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @param serverId 设备连接的 serverId - * @return 消息 - */ - IotDeviceMessage buildDeviceMessageOfStateOffline(String productKey, String deviceName, String serverId); + void sendDeviceMessage(IotDeviceMessage message, + String productKey, String deviceName, String serverId); } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java new file mode 100644 index 0000000000..ad174c7990 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java @@ -0,0 +1,115 @@ +package cn.iocoder.yudao.module.iot.gateway.service.device.message; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS; + +/** + * IoT 设备消息 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { + + /** + * 编解码器 + */ + private final Map codes; + + @Resource + private IotDeviceService deviceService; + + @Resource + private IotDeviceMessageProducer deviceMessageProducer; + + public IotDeviceMessageServiceImpl(List codes) { + this.codes = CollectionUtils.convertMap(codes, IotAlinkDeviceMessageCodec::type); + } + + @Override + public byte[] encodeDeviceMessage(IotDeviceMessage message, + String productKey, String deviceName) { + // 1.1 获取设备信息 + IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); + if (device == null) { + throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); + } + // 1.2 获取编解码器 + IotAlinkDeviceMessageCodec codec = codes.get(device.getCodecType()); + if (codec == null) { + throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType())); + } + + // 2. 编码消息 + return codec.encode(message); + } + + @Override + public IotDeviceMessage decodeDeviceMessage(byte[] bytes, + String productKey, String deviceName) { + // 1.1 获取设备信息 + IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); + if (device == null) { + throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); + } + // 1.2 获取编解码器 + IotAlinkDeviceMessageCodec codec = codes.get(device.getCodecType()); + if (codec == null) { + throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType())); + } + + // 2. 解码消息 + return codec.decode(bytes); + } + + @Override + public void sendDeviceMessage(IotDeviceMessage message, + String productKey, String deviceName, String serverId) { + // 1. 获取设备信息 + IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); + if (device == null) { + throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); + } + + // 2. 发送消息 + appendDeviceMessage(message, device, serverId); + deviceMessageProducer.sendDeviceMessage(message); + } + + /** + * 补充消息的后端字段 + * + * @param message 消息 + * @param device 设备信息 + * @param serverId 设备连接的 serverId + * @return 消息 + */ + private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message, + IotDeviceRespDTO device, String serverId) { + message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) + .setDeviceId(device.getId()).setTenantId(device.getTenantId()).setServerId(serverId); + // 特殊:如果设备没有指定 requestId,则使用 messageId + if (StrUtil.isEmpty(message.getRequestId())) { + message.setRequestId(message.getId()); + } + return message; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java new file mode 100644 index 0000000000..b325103743 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.iot.gateway.service.device.remote; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * Iot 设备信息 Service 实现类:调用远程的 device http 接口,进行设备认证、设备获取等 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class IotDeviceApiImpl implements IotDeviceCommonApi { + + @Resource + private IotGatewayProperties gatewayProperties; + + private RestTemplate restTemplate; + + @PostConstruct + public void init() { + IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc(); + restTemplate = new RestTemplateBuilder() + .rootUri(rpc.getUrl() + "/rpc-api/iot/device") + .readTimeout(rpc.getReadTimeout()) + .connectTimeout(rpc.getConnectTimeout()) + .build(); + } + + @Override + public CommonResult authDevice(IotDeviceAuthReqDTO authReqDTO) { + return doPost("/auth", authReqDTO, new ParameterizedTypeReference<>() { }); + } + + @Override + public CommonResult getDevice(IotDeviceGetReqDTO getReqDTO) { + return doPost("/get", getReqDTO, new ParameterizedTypeReference<>() { }); + } + + private CommonResult doPost(String url, T body, + ParameterizedTypeReference> responseType) { + try { + // 请求 + HttpEntity requestEntity = new HttpEntity<>(body); + ResponseEntity> response = restTemplate.exchange( + url, HttpMethod.POST, requestEntity, responseType); + // 响应 + CommonResult result = response.getBody(); + Assert.notNull(result, "请求结果不能为空"); + return result; + } catch (Exception e) { + log.error("[doPost][url({}) body({}) 发生异常]", url, body, e); + return CommonResult.error(INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java deleted file mode 100644 index 5987d5561a..0000000000 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java +++ /dev/null @@ -1,139 +0,0 @@ -package cn.iocoder.yudao.module.iot.gateway.service.message; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; -import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceCacheService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -/** - * IoT 设备消息 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { - - /** - * 编解码器 - */ - private final Map codes; - - @Resource - private IotDeviceCacheService deviceCacheService; - - public IotDeviceMessageServiceImpl(List codes) { - this.codes = CollectionUtils.convertMap(codes, IotAlinkDeviceMessageCodec::type); - } - - @Override - public byte[] encodeDeviceMessage(IotDeviceMessage message, - String productKey, String deviceName) { - // 获取设备信息以确定编解码类型 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName); - if (deviceInfo == null) { - log.warn("[encodeDeviceMessage][设备信息不存在][productKey: {}][deviceName: {}]", - productKey, deviceName); - return null; - } - - String codecType = "alink"; // 默认使用 alink 编解码器 - IotAlinkDeviceMessageCodec codec = codes.get(codecType); - if (codec == null) { - log.error("[encodeDeviceMessage][编解码器不存在][codecType: {}]", codecType); - return null; - } - - return codec.encode(message); - } - - @Override - public IotDeviceMessage decodeDeviceMessage(byte[] bytes, - String productKey, String deviceName, String serverId) { - // 获取设备信息 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName); - if (deviceInfo == null) { - log.warn("[decodeDeviceMessage][设备信息不存在][productKey: {}][deviceName: {}]", - productKey, deviceName); - return null; - } - - String codecType = "alink"; // 默认使用 alink 编解码器 - IotAlinkDeviceMessageCodec codec = codes.get(codecType); - if (codec == null) { - log.error("[decodeDeviceMessage][编解码器不存在][codecType: {}]", codecType); - return null; - } - - IotDeviceMessage message = codec.decode(bytes); - if (message == null) { - log.warn("[decodeDeviceMessage][消息解码失败][productKey: {}][deviceName: {}]", - productKey, deviceName); - return null; - } - - // 补充后端字段 - return appendDeviceMessage(message, deviceInfo, serverId); - } - - @Override - public IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId) { - // 获取设备信息 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName); - if (deviceInfo == null) { - log.warn("[buildDeviceMessageOfStateOnline][设备信息不存在][productKey: {}][deviceName: {}]", - productKey, deviceName); - return null; - } - - IotDeviceMessage message = IotDeviceMessage.requestOf(null, - IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), null); - - return appendDeviceMessage(message, deviceInfo, serverId); - } - - @Override - public IotDeviceMessage buildDeviceMessageOfStateOffline(String productKey, String deviceName, String serverId) { - // 获取设备信息 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName); - if (deviceInfo == null) { - log.warn("[buildDeviceMessageOfStateOffline][设备信息不存在][productKey: {}][deviceName: {}]", - productKey, deviceName); - return null; - } - - IotDeviceMessage message = IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod(), - null); - return appendDeviceMessage(message, deviceInfo, serverId); - } - - /** - * 补充消息的后端字段 - * - * @param message 消息 - * @param device 设备信息 - * @param serverId 设备连接的 serverId - * @return 消息 - */ - private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message, - IotDeviceCacheService.DeviceInfo device, String serverId) { - message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) - .setDeviceId(device.getDeviceId()).setTenantId(device.getTenantId()).setServerId(serverId); - // 特殊:如果设备没有指定 requestId,则使用 messageId - if (StrUtil.isEmpty(message.getRequestId())) { - message.setRequestId(message.getId()); - } - return message; - } - -}