From a364153d4a9a34375151d8527a5ad54ed60f6c22 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 22:23:31 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E3=80=91IoT=EF=BC=9Adevice=20=E5=92=8C=20thingmodel=20?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=A2=9E=E5=8A=A0=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mysql/thingmodel/IotThingModelMapper.java | 8 ++-- .../iot/dal/redis/RedisKeyConstants.java | 18 +++++++- .../iot/service/device/IotDeviceService.java | 7 +-- .../service/device/IotDeviceServiceImpl.java | 43 ++++++++++++++++++- .../data/IotDevicePropertyServiceImpl.java | 6 +-- .../IotDeviceUpstreamServiceImpl.java | 9 +--- .../thingmodel/IotThingModelService.java | 19 ++++---- .../thingmodel/IotThingModelServiceImpl.java | 37 ++++++++++++++-- 8 files changed, 115 insertions(+), 32 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java index 1258893c57..4c563c65eb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java @@ -49,6 +49,10 @@ public interface IotThingModelMapper extends BaseMapperX { return selectList(IotThingModelDO::getProductId, productId); } + default List selectListByProductKey(String productKey) { + return selectList(IotThingModelDO::getProductKey, productKey); + } + default List selectListByProductIdAndType(Long productId, Integer type) { return selectList(IotThingModelDO::getProductId, productId, IotThingModelDO::getType, type); @@ -68,8 +72,4 @@ public interface IotThingModelMapper extends BaseMapperX { IotThingModelDO::getName, name); } - default List selectListByProductKey(String productKey) { - return selectList(IotThingModelDO::getProductKey, productKey); - } - } \ 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/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java index 04858248fa..d7096b55dd 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 @@ -10,7 +10,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; public interface RedisKeyConstants { /** - * 设备属性数据缓存,采用 HASH 结构 + * 设备属性的数据缓存,采用 HASH 结构 *

* KEY 格式:device_property:{deviceKey} * HASH KEY:identifier 属性标识 @@ -26,4 +26,20 @@ public interface RedisKeyConstants { */ String DEVICE_REPORT_TIME = "device_report_time"; + /** + * 设备信息的数据缓存,使用 Spring Cache 操作 + * + * KEY 格式:device_${productKey}_${deviceKey} + * VALUE 数据类型:String(JSON) + */ + String DEVICE = "device"; + + /** + * 物模型的数据缓存,使用 Spring Cache 操作 + * + * KEY 格式:thing_model_${productKey} + * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表 + */ + String THING_MODEL_LIST = "thing_model_list"; + } 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 f01460a1b5..3efc4054fe 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 @@ -113,15 +113,16 @@ public interface IotDeviceService { */ Long getDeviceCountByGroupId(Long groupId); - // TODO @芋艿:增加缓存 /** - * 根据产品 key 和设备名称,获得设备信息 + * 【缓存】根据产品 key 和设备名称,获得设备信息 + * + * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! * * @param productKey 产品 key * @param deviceName 设备名称 * @return 设备信息 */ - IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName); + 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 683570916f..786e8e0ef0 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 @@ -4,22 +4,27 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +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.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; +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.transaction.annotation.Transactional; @@ -78,6 +83,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds()); // 2.1 转换 VO 为 DO + // TODO @芋艿:state 相关的参数。另外,到底叫 state,还是 status 好! IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> { o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); // 生成并设置必要的字段 @@ -109,6 +115,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2. 更新到数据库 IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); deviceMapper.updateById(updateObj); + + // 3. 清空对应缓存 + deleteDeviceCache(device); } @Override @@ -125,6 +134,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 3. 更新设备分组 deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO() .setId(device.getId()).setGroupIds(updateReqVO.getGroupIds()))); + + // 4. 清空对应缓存 + deleteDeviceCache(devices); } @Override @@ -138,6 +150,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2. 删除设备 deviceMapper.deleteById(id); + + // 3. 清空对应缓存 + deleteDeviceCache(device); } @Override @@ -160,6 +175,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2. 删除设备 deviceMapper.deleteByIds(ids); + + // 3. 清空对应缓存 + deleteDeviceCache(devices); } /** @@ -213,6 +231,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Override public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { + // TODO @芋艿:state 相关的参数。另外,到底叫 state,还是 status 好! + // TODO @芋艿:各种时间,需要 check 下,优化处理下! // 1. 校验存在 IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); @@ -233,6 +253,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { } // 2.3 更新到数据库 deviceMapper.updateById(updateDevice); + + // 3. 清空对应缓存 + deleteDeviceCache(device); } @Override @@ -246,7 +269,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { } @Override - public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) { + @TenantIgnore + @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null") + public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); } @@ -367,4 +392,20 @@ public class IotDeviceServiceImpl implements IotDeviceService { return respVO; } + private void deleteDeviceCache(IotDeviceDO device) { + // 保证在 @CacheEvict 之前,忽略租户 + TenantUtils.executeIgnore(() -> getSelf().deleteDeviceCache0(device)); + } + + private void deleteDeviceCache(List devices) { + devices.forEach(this::deleteDeviceCache); + } + + @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") + public void deleteDeviceCache0(IotDeviceDO device) {} + + private IotDeviceServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } \ 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/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index fba2fd6f30..3f4c90b24f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -118,21 +118,21 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } @Override - @TenantIgnore // TODO @芋艿:租户的缓存问题,需要考虑下。因为会存在一会又 tenantId,一会没有! + @TenantIgnore public void saveDeviceProperty(IotDeviceMessage message) { if (!(message.getData() instanceof Map)) { log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message); return; } // 1. 获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(message.getProductKey(), message.getDeviceName()); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(message.getProductKey(), message.getDeviceName()); if (device == null) { log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message); return; } // 2. 根据物模型,拼接合法的属性 - List thingModels = thingModelService.getThingModelListByProductId(device.getProductId()); + List thingModels = thingModelService.getThingModelListByProductKeyFromCache(device.getProductKey()); Map properties = new HashMap<>(); ((Map) message.getData()).forEach((key, value) -> { if (CollUtil.findOne(thingModels, thingModel -> thingModel.getIdentifier().equals(key)) == null) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java index d930d0ad08..4a456406ca 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.device.upstream; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; @@ -47,7 +46,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { public void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { // 1.1 获得设备 log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO); - IotDeviceDO device = getDevice(reportReqDTO); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); if (device == null) { log.error("[reportDevicePropertyData][设备({}/{})不存在]", reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); @@ -71,11 +71,6 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { // TODO 芋艿:待实现 } - private IotDeviceDO getDevice(IotDeviceUpstreamAbstractReqDTO reqDTO) { - return TenantUtils.executeIgnore(() -> // 需要忽略租户,因为请求时,未带租户编号 - deviceService.getDeviceByProductKeyAndDeviceName(reqDTO.getProductKey(), reqDTO.getDeviceName())); - } - private void updateDeviceLastTime(IotDeviceDO deviceDO, IotDeviceUpstreamAbstractReqDTO reqDTO) { // TODO 芋艿:插件状态 // TODO 芋艿:操作时间 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index 71d47a53f5..cc81e5f02f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -46,7 +46,6 @@ public interface IotThingModelService { */ IotThingModelDO getThingModel(Long id); - // TODO @芋艿:增加缓存 /** * 获得产品物模型列表 * @@ -55,6 +54,16 @@ public interface IotThingModelService { */ List getThingModelListByProductId(Long productId); + /** + * 【缓存】获得产品物模型列表 + * + * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! + * + * @param productKey 产品标识 + * @return 产品物模型列表 + */ + List getThingModelListByProductKeyFromCache(String productKey); + /** * 获得产品物模型分页 * @@ -63,14 +72,6 @@ public interface IotThingModelService { */ PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO); - /** - * 获得产品物模型列表 - * - * @param productKey 产品 Key - * @return 产品物模型列表 - */ - List getProductThingModelListByProductKey(String productKey); - /** * 获得产品物模型列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index f0ce947d52..5c9f5fd051 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -2,8 +2,11 @@ package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; 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.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; @@ -14,11 +17,14 @@ import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.*; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -69,6 +75,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? + + // 7. 删除缓存 + deleteThingModelListCache(createReqVO.getProductKey()); return thingModel.getId(); } @@ -92,6 +101,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { if (Objects.equals(updateReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } + + // 6. 删除缓存 + deleteThingModelListCache(updateReqVO.getProductKey()); } @Override @@ -113,6 +125,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { if (Objects.equals(thingModel.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); } + + // 4. 删除缓存 + deleteThingModelListCache(thingModel.getProductKey()); } @Override @@ -126,13 +141,15 @@ public class IotThingModelServiceImpl implements IotThingModelService { } @Override - public PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { - return thingModelMapper.selectPage(pageReqVO); + @TenantIgnore + @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") + public List getThingModelListByProductKeyFromCache(String productKey) { + return thingModelMapper.selectListByProductKey(productKey); } @Override - public List getProductThingModelListByProductKey(String productKey) { - return thingModelMapper.selectListByProductKey(productKey); + public PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { + return thingModelMapper.selectPage(pageReqVO); } @Override @@ -333,4 +350,16 @@ public class IotThingModelServiceImpl implements IotThingModelService { .setDirection(direction.getDirection())); } + private void deleteThingModelListCache(String productKey) { + // 保证在 @CacheEvict 之前,忽略租户 + TenantUtils.executeIgnore(() -> getSelf().deleteThingModelListCache0(productKey)); + } + + @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") + public void deleteThingModelListCache0(String productKey) {} + + private IotThingModelServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + }