Commit 33fed79820e1e042a8fc0db911308a9dc85506d9

Authored by YunaiV
1 parent 66b42367

reactor:【IoT 物联网】优化设备缓存的加载逻辑

Showing 26 changed files with 352 additions and 550 deletions
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 { @@ -16,4 +16,6 @@ public class DictTypeConstants {
16 16
17 public static final String DEVICE_STATE = "iot_device_state"; 17 public static final String DEVICE_STATE = "iot_device_state";
18 18
  19 + public static final String CODEC_TYPE = "iot_codec_type";
  20 +
19 } 21 }
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; @@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.iot.api.device;
2 2
3 import cn.iocoder.yudao.framework.common.enums.RpcConstants; 3 import cn.iocoder.yudao.framework.common.enums.RpcConstants;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 6 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
7 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;  
8 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO; 8 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
  9 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
10 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 12 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  13 +import cn.iocoder.yudao.module.iot.service.product.IotProductService;
11 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
12 import jakarta.annotation.security.PermitAll; 15 import jakarta.annotation.security.PermitAll;
13 import org.springframework.context.annotation.Primary; 16 import org.springframework.context.annotation.Primary;
@@ -30,6 +33,8 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { @@ -30,6 +33,8 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi {
30 33
31 @Resource 34 @Resource
32 private IotDeviceService deviceService; 35 private IotDeviceService deviceService;
  36 + @Resource
  37 + private IotProductService productService;
33 38
34 @Override 39 @Override
35 @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth") 40 @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth")
@@ -39,24 +44,17 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { @@ -39,24 +44,17 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi {
39 } 44 }
40 45
41 @Override 46 @Override
42 - @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/info") 47 + @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/get") // 特殊:方便调用,暂时使用 POST,实际更推荐 GET
43 @PermitAll 48 @PermitAll
44 - public CommonResult<IotDeviceInfoRespDTO> getDeviceInfo(@RequestBody IotDeviceInfoReqDTO infoReqDTO) {  
45 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(  
46 - infoReqDTO.getProductKey(), infoReqDTO.getDeviceName());  
47 -  
48 - if (device == null) {  
49 - return success(null);  
50 - }  
51 -  
52 - IotDeviceInfoRespDTO respDTO = new IotDeviceInfoRespDTO();  
53 - respDTO.setDeviceId(device.getId());  
54 - respDTO.setProductKey(device.getProductKey());  
55 - respDTO.setDeviceName(device.getDeviceName());  
56 - respDTO.setDeviceKey(device.getDeviceKey());  
57 - respDTO.setTenantId(device.getTenantId());  
58 -  
59 - return success(respDTO); 49 + public CommonResult<IotDeviceRespDTO> getDevice(@RequestBody IotDeviceGetReqDTO getReqDTO) {
  50 + IotDeviceDO device = getReqDTO.getId() != null ? deviceService.getDeviceFromCache(getReqDTO.getId())
  51 + : deviceService.getDeviceFromCache(getReqDTO.getProductKey(), getReqDTO.getDeviceName());
  52 + return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> {
  53 + IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId());
  54 + if (product != null) {
  55 + deviceDTO.setCodecType(product.getCodecType());
  56 + }
  57 + }));
60 } 58 }
61 59
62 } 60 }
63 \ No newline at end of file 61 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java
@@ -71,6 +71,12 @@ public class IotProductDO extends TenantBaseDO { @@ -71,6 +71,12 @@ public class IotProductDO extends TenantBaseDO {
71 private Integer netType; 71 private Integer netType;
72 72
73 /** 73 /**
  74 + * 编解码器类型
  75 + *
  76 + * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#CODEC_TYPE}
  77 + */
  78 + private String codecType;
  79 + /**
74 * 接入网关协议 80 * 接入网关协议
75 * <p> 81 * <p>
76 * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotProtocolTypeEnum} 82 * 枚举 {@link cn.iocoder.yudao.module.iot.enums.product.IotProtocolTypeEnum}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java
@@ -39,12 +39,21 @@ public interface RedisKeyConstants { @@ -39,12 +39,21 @@ public interface RedisKeyConstants {
39 /** 39 /**
40 * 设备信息的数据缓存,使用 Spring Cache 操作(忽略租户) 40 * 设备信息的数据缓存,使用 Spring Cache 操作(忽略租户)
41 * 41 *
42 - * KEY 格式:device_${productKey}_${deviceName} 42 + * KEY 格式 1:device_${id}
  43 + * KEY 格式 2:device_${productKey}_${deviceName}
43 * VALUE 数据类型:String(JSON) 44 * VALUE 数据类型:String(JSON)
44 */ 45 */
45 String DEVICE = "iot:device"; 46 String DEVICE = "iot:device";
46 47
47 /** 48 /**
  49 + * 产品信息的数据缓存,使用 Spring Cache 操作(忽略租户)
  50 + *
  51 + * KEY 格式:product_${id}
  52 + * VALUE 数据类型:String(JSON)
  53 + */
  54 + String PRODUCT = "iot:product";
  55 +
  56 + /**
48 * 物模型的数据缓存,使用 Spring Cache 操作(忽略租户) 57 * 物模型的数据缓存,使用 Spring Cache 操作(忽略租户)
49 * 58 *
50 * KEY 格式:thing_model_${productKey} 59 * KEY 格式:thing_model_${productKey}
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; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.device;
2 2
3 import cn.hutool.core.util.ObjectUtil; 3 import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 4 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  5 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
5 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
7 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 8 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
@@ -61,18 +62,19 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber&lt;IotDevic @@ -61,18 +62,19 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber&lt;IotDevic
61 return; 62 return;
62 } 63 }
63 64
64 - // 1.1 更新设备的最后时间  
65 - // TODO 芋艿:后续加缓存;  
66 - IotDeviceDO device = deviceService.validateDeviceExists(message.getDeviceId());  
67 - devicePropertyService.updateDeviceReportTime(device.getProductKey(), device.getDeviceName(), LocalDateTime.now());  
68 - // 1.2 更新设备的连接 server  
69 - devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId()); 65 + TenantUtils.execute(message.getTenantId(), () -> {
  66 + // 1.1 更新设备的最后时间
  67 + IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId());
  68 + devicePropertyService.updateDeviceReportTime(device.getProductKey(), device.getDeviceName(), LocalDateTime.now());
  69 + // 1.2 更新设备的连接 server
  70 + devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId());
70 71
71 - // 2. 未上线的设备,强制上线  
72 - forceDeviceOnline(message, device); 72 + // 2. 未上线的设备,强制上线
  73 + forceDeviceOnline(message, device);
73 74
74 - // 3. 核心:处理消息  
75 - deviceMessageService.handleUpstreamDeviceMessage(message, device); 75 + // 3. 核心:处理消息
  76 + deviceMessageService.handleUpstreamDeviceMessage(message, device);
  77 + });
76 } 78 }
77 79
78 private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) { 80 private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
@@ -106,6 +106,14 @@ public interface IotDeviceService { @@ -106,6 +106,14 @@ public interface IotDeviceService {
106 IotDeviceDO validateDeviceExists(Long id); 106 IotDeviceDO validateDeviceExists(Long id);
107 107
108 /** 108 /**
  109 + * 【缓存】校验设备是否存在
  110 + *
  111 + * @param id 设备 ID
  112 + * @return 设备对象
  113 + */
  114 + IotDeviceDO validateDeviceExistsFromCache(Long id);
  115 +
  116 + /**
109 * 获得设备 117 * 获得设备
110 * 118 *
111 * @param id 编号 119 * @param id 编号
@@ -114,6 +122,27 @@ public interface IotDeviceService { @@ -114,6 +122,27 @@ public interface IotDeviceService {
114 IotDeviceDO getDevice(Long id); 122 IotDeviceDO getDevice(Long id);
115 123
116 /** 124 /**
  125 + * 【缓存】获得设备信息
  126 + * <p>
  127 + * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!!
  128 + *
  129 + * @param id 编号
  130 + * @return IoT 设备
  131 + */
  132 + IotDeviceDO getDeviceFromCache(Long id);
  133 +
  134 + /**
  135 + * 【缓存】根据产品 key 和设备名称,获得设备信息
  136 + * <p>
  137 + * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!!
  138 + *
  139 + * @param productKey 产品 key
  140 + * @param deviceName 设备名称
  141 + * @return 设备信息
  142 + */
  143 + IotDeviceDO getDeviceFromCache(String productKey, String deviceName);
  144 +
  145 + /**
117 * 根据设备 key 获得设备 146 * 根据设备 key 获得设备
118 * 147 *
119 * @param deviceKey 编号 148 * @param deviceKey 编号
@@ -178,17 +207,6 @@ public interface IotDeviceService { @@ -178,17 +207,6 @@ public interface IotDeviceService {
178 Long getDeviceCountByGroupId(Long groupId); 207 Long getDeviceCountByGroupId(Long groupId);
179 208
180 /** 209 /**
181 - * 【缓存】根据产品 key 和设备名称,获得设备信息  
182 - * <p>  
183 - * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!!  
184 - *  
185 - * @param productKey 产品 key  
186 - * @param deviceName 设备名称  
187 - * @return 设备信息  
188 - */  
189 - IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName);  
190 -  
191 - /**  
192 * 导入设备 210 * 导入设备
193 * 211 *
194 * @param importDevices 导入设备列表 212 * @param importDevices 导入设备列表
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; @@ -28,6 +28,7 @@ import jakarta.validation.ConstraintViolationException;
28 import lombok.extern.slf4j.Slf4j; 28 import lombok.extern.slf4j.Slf4j;
29 import org.springframework.cache.annotation.CacheEvict; 29 import org.springframework.cache.annotation.CacheEvict;
30 import org.springframework.cache.annotation.Cacheable; 30 import org.springframework.cache.annotation.Cacheable;
  31 +import org.springframework.cache.annotation.Caching;
31 import org.springframework.context.annotation.Lazy; 32 import org.springframework.context.annotation.Lazy;
32 import org.springframework.stereotype.Service; 33 import org.springframework.stereotype.Service;
33 import org.springframework.transaction.annotation.Transactional; 34 import org.springframework.transaction.annotation.Transactional;
@@ -221,6 +222,15 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -221,6 +222,15 @@ public class IotDeviceServiceImpl implements IotDeviceService {
221 return device; 222 return device;
222 } 223 }
223 224
  225 + @Override
  226 + public IotDeviceDO validateDeviceExistsFromCache(Long id) {
  227 + IotDeviceDO device = getSelf().getDeviceFromCache(id);
  228 + if (device == null) {
  229 + throw exception(DEVICE_NOT_EXISTS);
  230 + }
  231 + return device;
  232 + }
  233 +
224 /** 234 /**
225 * 校验网关设备是否存在 235 * 校验网关设备是否存在
226 * 236 *
@@ -242,6 +252,20 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -242,6 +252,20 @@ public class IotDeviceServiceImpl implements IotDeviceService {
242 } 252 }
243 253
244 @Override 254 @Override
  255 + @Cacheable(value = RedisKeyConstants.DEVICE, key = "#id", unless = "#result == null")
  256 + @TenantIgnore // 忽略租户信息
  257 + public IotDeviceDO getDeviceFromCache(Long id) {
  258 + return deviceMapper.selectById(id);
  259 + }
  260 +
  261 + @Override
  262 + @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null")
  263 + @TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的
  264 + public IotDeviceDO getDeviceFromCache(String productKey, String deviceName) {
  265 + return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
  266 + }
  267 +
  268 + @Override
245 public IotDeviceDO getDeviceByDeviceKey(String deviceKey) { 269 public IotDeviceDO getDeviceByDeviceKey(String deviceKey) {
246 return deviceMapper.selectByDeviceKey(deviceKey); 270 return deviceMapper.selectByDeviceKey(deviceKey);
247 } 271 }
@@ -308,13 +332,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -308,13 +332,6 @@ public class IotDeviceServiceImpl implements IotDeviceService {
308 return deviceMapper.selectCountByGroupId(groupId); 332 return deviceMapper.selectCountByGroupId(groupId);
309 } 333 }
310 334
311 - @Override  
312 - @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null")  
313 - @TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的  
314 - public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) {  
315 - return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);  
316 - }  
317 -  
318 /** 335 /**
319 * 生成 deviceKey 336 * 生成 deviceKey
320 * 337 *
@@ -425,14 +442,13 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -425,14 +442,13 @@ public class IotDeviceServiceImpl implements IotDeviceService {
425 devices.forEach(this::deleteDeviceCache); 442 devices.forEach(this::deleteDeviceCache);
426 } 443 }
427 444
428 - @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") 445 + @Caching(evict = {
  446 + @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.id"),
  447 + @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName")
  448 + })
429 public void deleteDeviceCache0(IotDeviceDO device) { 449 public void deleteDeviceCache0(IotDeviceDO device) {
430 } 450 }
431 451
432 - private IotDeviceServiceImpl getSelf() {  
433 - return SpringUtil.getBean(getClass());  
434 - }  
435 -  
436 @Override 452 @Override
437 public Long getDeviceCount(LocalDateTime createTime) { 453 public Long getDeviceCount(LocalDateTime createTime) {
438 return deviceMapper.selectCountByCreateTime(createTime); 454 return deviceMapper.selectCountByCreateTime(createTime);
@@ -477,7 +493,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -477,7 +493,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
477 } 493 }
478 String deviceName = deviceInfo.getDeviceName(); 494 String deviceName = deviceInfo.getDeviceName();
479 String productKey = deviceInfo.getProductKey(); 495 String productKey = deviceInfo.getProductKey();
480 - IotDeviceDO device = getSelf().getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName); 496 + IotDeviceDO device = getSelf().getDeviceFromCache(productKey, deviceName);
481 if (device == null) { 497 if (device == null) {
482 log.warn("[authDevice][设备({}/{}) 不存在]", productKey, deviceName); 498 log.warn("[authDevice][设备({}/{}) 不存在]", productKey, deviceName);
483 return false; 499 return false;
@@ -492,4 +508,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -492,4 +508,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
492 return true; 508 return true;
493 } 509 }
494 510
  511 + private IotDeviceServiceImpl getSelf() {
  512 + return SpringUtil.getBean(getClass());
  513 + }
  514 +
495 } 515 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
@@ -48,6 +48,16 @@ public interface IotProductService { @@ -48,6 +48,16 @@ public interface IotProductService {
48 IotProductDO getProduct(Long id); 48 IotProductDO getProduct(Long id);
49 49
50 /** 50 /**
  51 + * 【缓存】获得产品
  52 + * <p>
  53 + * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!!
  54 + *
  55 + * @param id 编号
  56 + * @return 产品
  57 + */
  58 + IotProductDO getProductFromCache(Long id);
  59 +
  60 + /**
51 * 根据产品 key 获得产品 61 * 根据产品 key 获得产品
52 * 62 *
53 * @param productKey 产品 key 63 * @param productKey 产品 key
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; @@ -2,15 +2,19 @@ package cn.iocoder.yudao.module.iot.service.product;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
5 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 6 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
6 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;
7 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 9 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
9 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; 10 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
  11 +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
10 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; 12 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
11 import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; 13 import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
12 import com.baomidou.dynamic.datasource.annotation.DSTransactional; 14 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
13 import jakarta.annotation.Resource; 15 import jakarta.annotation.Resource;
  16 +import org.springframework.cache.annotation.CacheEvict;
  17 +import org.springframework.cache.annotation.Cacheable;
14 import org.springframework.context.annotation.Lazy; 18 import org.springframework.context.annotation.Lazy;
15 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
16 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
@@ -56,6 +60,7 @@ public class IotProductServiceImpl implements IotProductService { @@ -56,6 +60,7 @@ public class IotProductServiceImpl implements IotProductService {
56 } 60 }
57 61
58 @Override 62 @Override
  63 + @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id")
59 public void updateProduct(IotProductSaveReqVO updateReqVO) { 64 public void updateProduct(IotProductSaveReqVO updateReqVO) {
60 updateReqVO.setProductKey(null); // 不更新产品标识 65 updateReqVO.setProductKey(null); // 不更新产品标识
61 // 1.1 校验存在 66 // 1.1 校验存在
@@ -68,6 +73,7 @@ public class IotProductServiceImpl implements IotProductService { @@ -68,6 +73,7 @@ public class IotProductServiceImpl implements IotProductService {
68 } 73 }
69 74
70 @Override 75 @Override
  76 + @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id")
71 public void deleteProduct(Long id) { 77 public void deleteProduct(Long id) {
72 // 1.1 校验存在 78 // 1.1 校验存在
73 IotProductDO iotProductDO = validateProductExists(id); 79 IotProductDO iotProductDO = validateProductExists(id);
@@ -107,6 +113,13 @@ public class IotProductServiceImpl implements IotProductService { @@ -107,6 +113,13 @@ public class IotProductServiceImpl implements IotProductService {
107 } 113 }
108 114
109 @Override 115 @Override
  116 + @Cacheable(value = RedisKeyConstants.PRODUCT, key = "#id", unless = "#result == null")
  117 + @TenantIgnore // 忽略租户信息
  118 + public IotProductDO getProductFromCache(Long id) {
  119 + return productMapper.selectById(id);
  120 + }
  121 +
  122 + @Override
110 public IotProductDO getProductByProductKey(String productKey) { 123 public IotProductDO getProductByProductKey(String productKey) {
111 return productMapper.selectByProductKey(productKey); 124 return productMapper.selectByProductKey(productKey);
112 } 125 }
@@ -118,6 +131,7 @@ public class IotProductServiceImpl implements IotProductService { @@ -118,6 +131,7 @@ public class IotProductServiceImpl implements IotProductService {
118 131
119 @Override 132 @Override
120 @DSTransactional(rollbackFor = Exception.class) 133 @DSTransactional(rollbackFor = Exception.class)
  134 + @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#id")
121 public void updateProductStatus(Long id, Integer status) { 135 public void updateProductStatus(Long id, Integer status) {
122 // 1. 校验存在 136 // 1. 校验存在
123 validateProductExists(id); 137 validateProductExists(id);
@@ -143,5 +157,4 @@ public class IotProductServiceImpl implements IotProductService { @@ -143,5 +157,4 @@ public class IotProductServiceImpl implements IotProductService {
143 return productMapper.selectCountByCreateTime(createTime); 157 return productMapper.selectCountByCreateTime(createTime);
144 } 158 }
145 159
146 -  
147 } 160 }
148 \ No newline at end of file 161 \ No newline at end of file
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 { @@ -31,7 +31,7 @@ public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction {
31 Assert.notNull(control, "设备控制配置不能为空"); 31 Assert.notNull(control, "设备控制配置不能为空");
32 // 遍历每个设备,下发消息 32 // 遍历每个设备,下发消息
33 control.getDeviceNames().forEach(deviceName -> { 33 control.getDeviceNames().forEach(deviceName -> {
34 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(control.getProductKey(), deviceName); 34 + IotDeviceDO device = deviceService.getDeviceFromCache(control.getProductKey(), deviceName);
35 if (device == null) { 35 if (device == null) {
36 log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config); 36 log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config);
37 return; 37 return;
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml
@@ -9,7 +9,6 @@ @@ -9,7 +9,6 @@
9 ts TIMESTAMP, 9 ts TIMESTAMP,
10 id NCHAR(50), 10 id NCHAR(50),
11 report_time TIMESTAMP, 11 report_time TIMESTAMP,
12 - device_id BIGINT,  
13 tenant_id BIGINT, 12 tenant_id BIGINT,
14 server_id NCHAR(50), 13 server_id NCHAR(50),
15 upstream BOOL, 14 upstream BOOL,
@@ -29,21 +28,21 @@ @@ -29,21 +28,21 @@
29 28
30 <insert id="insert"> 29 <insert id="insert">
31 INSERT INTO device_message_${deviceId} ( 30 INSERT INTO device_message_${deviceId} (
32 - ts, id, report_time, device_id, tenant_id,  
33 - server_id, upstream, request_id, method, params,  
34 - data, code 31 + ts, id, report_time, tenant_id, server_id,
  32 + upstream, request_id, method, params, data,
  33 + code
35 ) 34 )
36 USING device_message 35 USING device_message
37 TAGS (#{deviceId}) 36 TAGS (#{deviceId})
38 VALUES ( 37 VALUES (
39 - #{ts}, #{id}, #{reportTime}, #{deviceId}, #{tenantId},  
40 - #{serverId}, #{upstream}, #{requestId}, #{method}, #{params},  
41 - #{data}, #{code} 38 + #{ts}, #{id}, #{reportTime}, #{tenantId}, #{serverId},
  39 + #{upstream}, #{requestId}, #{method}, #{params}, #{data},
  40 + #{code}
42 ) 41 )
43 </insert> 42 </insert>
44 43
45 <select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO"> 44 <select id="selectPage" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO">
46 - SELECT ts, id, report_time, device_id, tenant_id, server_id, upstream, 45 + SELECT ts, id, report_time, device_id, tenant_id, server_id, upstream,
47 request_id, method, params, data, code 46 request_id, method, params, data, code
48 FROM device_message_${reqVO.deviceId} 47 FROM device_message_${reqVO.deviceId}
49 <where> 48 <where>
@@ -125,4 +124,4 @@ @@ -125,4 +124,4 @@
125 ORDER BY time ASC 124 ORDER BY time ASC
126 </select> 125 </select>
127 126
128 -</mapper>  
129 \ No newline at end of file 127 \ No newline at end of file
  128 +</mapper>
130 \ No newline at end of file 129 \ No newline at end of file
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; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.iot.core.biz;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
5 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;  
6 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO; 5 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
  6 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
7 7
8 /** 8 /**
9 * IoT 设备通用 API 9 * IoT 设备通用 API
@@ -26,6 +26,6 @@ public interface IotDeviceCommonApi { @@ -26,6 +26,6 @@ public interface IotDeviceCommonApi {
26 * @param infoReqDTO 设备信息请求 26 * @param infoReqDTO 设备信息请求
27 * @return 设备信息 27 * @return 设备信息
28 */ 28 */
29 - CommonResult<IotDeviceInfoRespDTO> getDeviceInfo(IotDeviceInfoReqDTO infoReqDTO); 29 + CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO infoReqDTO);
30 30
31 } 31 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoReqDTO.java renamed to yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceGetReqDTO.java
1 package cn.iocoder.yudao.module.iot.core.biz.dto; 1 package cn.iocoder.yudao.module.iot.core.biz.dto;
2 2
3 -import jakarta.validation.constraints.NotBlank;  
4 import lombok.Data; 3 import lombok.Data;
5 4
6 /** 5 /**
@@ -9,18 +8,20 @@ import lombok.Data; @@ -9,18 +8,20 @@ import lombok.Data;
9 * @author 芋道源码 8 * @author 芋道源码
10 */ 9 */
11 @Data 10 @Data
12 -public class IotDeviceInfoReqDTO { 11 +public class IotDeviceGetReqDTO {
  12 +
  13 + /**
  14 + * 设备编号
  15 + */
  16 + private Long id;
13 17
14 /** 18 /**
15 * 产品标识 19 * 产品标识
16 */ 20 */
17 - @NotBlank(message = "产品标识不能为空")  
18 private String productKey; 21 private String productKey;
19 -  
20 /** 22 /**
21 * 设备名称 23 * 设备名称
22 */ 24 */
23 - @NotBlank(message = "设备名称不能为空")  
24 private String deviceName; 25 private String deviceName;
25 26
26 } 27 }
27 \ No newline at end of file 28 \ No newline at end of file
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoRespDTO.java renamed to 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; @@ -8,31 +8,34 @@ import lombok.Data;
8 * @author 芋道源码 8 * @author 芋道源码
9 */ 9 */
10 @Data 10 @Data
11 -public class IotDeviceInfoRespDTO { 11 +public class IotDeviceRespDTO {
12 12
13 /** 13 /**
14 * 设备编号 14 * 设备编号
15 */ 15 */
16 - private Long deviceId;  
17 - 16 + private Long id;
18 /** 17 /**
19 * 产品标识 18 * 产品标识
20 */ 19 */
21 private String productKey; 20 private String productKey;
22 -  
23 /** 21 /**
24 * 设备名称 22 * 设备名称
25 */ 23 */
26 private String deviceName; 24 private String deviceName;
27 -  
28 /** 25 /**
29 - * 设备密钥 26 + * 租户编号
30 */ 27 */
31 - private String deviceKey; 28 + private Long tenantId;
  29 +
  30 + // ========== 产品相关字段 ==========
32 31
33 /** 32 /**
34 - * 租户编号 33 + * 产品编号
35 */ 34 */
36 - private Long tenantId; 35 + private Long productId;
  36 + /**
  37 + * 编解码器类型
  38 + */
  39 + private String codecType;
37 40
38 } 41 }
39 \ No newline at end of file 42 \ No newline at end of file
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 { @@ -13,4 +13,7 @@ public interface ErrorCodeConstants {
13 ErrorCode DEVICE_AUTH_FAIL = new ErrorCode(1_051_001_000, "设备鉴权失败"); // 对应阿里云 20000 13 ErrorCode DEVICE_AUTH_FAIL = new ErrorCode(1_051_001_000, "设备鉴权失败"); // 对应阿里云 20000
14 ErrorCode DEVICE_TOKEN_EXPIRED = new ErrorCode(1_051_001_002, "token 失效。需重新调用 auth 进行鉴权,获取token"); // 对应阿里云 20001 14 ErrorCode DEVICE_TOKEN_EXPIRED = new ErrorCode(1_051_001_002, "token 失效。需重新调用 auth 进行鉴权,获取token"); // 对应阿里云 20001
15 15
  16 + // ========== 设备信息 1-050-002-000 ============
  17 + ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_051_002_001, "设备({}/{}) 不存在");
  18 +
16 } 19 }
17 \ No newline at end of file 20 \ No newline at end of file
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&lt;RoutingContext&gt; @@ -36,14 +36,10 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt;
36 public final void handle(RoutingContext context) { 36 public final void handle(RoutingContext context) {
37 try { 37 try {
38 // 1. 前置处理 38 // 1. 前置处理
39 - CommonResult<Object> result = beforeHandle(context);  
40 - if (result != null) {  
41 - writeResponse(context, result);  
42 - return;  
43 - } 39 + beforeHandle(context);
44 40
45 // 2. 执行逻辑 41 // 2. 执行逻辑
46 - result = handle0(context); 42 + CommonResult<Object> result = handle0(context);
47 writeResponse(context, result); 43 writeResponse(context, result);
48 } catch (ServiceException e) { 44 } catch (ServiceException e) {
49 writeResponse(context, CommonResult.error(e.getCode(), e.getMessage())); 45 writeResponse(context, CommonResult.error(e.getCode(), e.getMessage()));
@@ -55,11 +51,11 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt; @@ -55,11 +51,11 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt;
55 51
56 protected abstract CommonResult<Object> handle0(RoutingContext context); 52 protected abstract CommonResult<Object> handle0(RoutingContext context);
57 53
58 - private CommonResult<Object> beforeHandle(RoutingContext context) { 54 + private void beforeHandle(RoutingContext context) {
59 // 如果不需要认证,则不走前置处理 55 // 如果不需要认证,则不走前置处理
60 String path = context.request().path(); 56 String path = context.request().path();
61 if (ObjUtil.equal(path, IotHttpAuthHandler.PATH)) { 57 if (ObjUtil.equal(path, IotHttpAuthHandler.PATH)) {
62 - return null; 58 + return;
63 } 59 }
64 60
65 // 解析参数 61 // 解析参数
@@ -84,7 +80,6 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt; @@ -84,7 +80,6 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt;
84 || ObjUtil.notEqual(deviceName, deviceInfo.getDeviceName())) { 80 || ObjUtil.notEqual(deviceName, deviceInfo.getDeviceName())) {
85 throw exception(FORBIDDEN); 81 throw exception(FORBIDDEN);
86 } 82 }
87 - return null;  
88 } 83 }
89 84
90 @SuppressWarnings("deprecation") 85 @SuppressWarnings("deprecation")
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; @@ -9,11 +9,10 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
9 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 9 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
12 -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;  
13 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 12 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
14 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 13 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
15 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; 14 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
16 -import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService; 15 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
17 import io.vertx.core.json.JsonObject; 16 import io.vertx.core.json.JsonObject;
18 import io.vertx.ext.web.RoutingContext; 17 import io.vertx.ext.web.RoutingContext;
19 18
@@ -35,19 +34,16 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -35,19 +34,16 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
35 34
36 private final IotHttpUpstreamProtocol protocol; 35 private final IotHttpUpstreamProtocol protocol;
37 36
38 - private final IotDeviceMessageProducer deviceMessageProducer;  
39 -  
40 private final IotDeviceTokenService deviceTokenService; 37 private final IotDeviceTokenService deviceTokenService;
41 38
42 - private final IotDeviceCommonApi deviceClientService; 39 + private final IotDeviceCommonApi deviceApi;
43 40
44 private final IotDeviceMessageService deviceMessageService; 41 private final IotDeviceMessageService deviceMessageService;
45 42
46 public IotHttpAuthHandler(IotHttpUpstreamProtocol protocol) { 43 public IotHttpAuthHandler(IotHttpUpstreamProtocol protocol) {
47 this.protocol = protocol; 44 this.protocol = protocol;
48 - this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);  
49 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); 45 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);
50 - this.deviceClientService = SpringUtil.getBean(IotDeviceCommonApi.class); 46 + this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
51 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); 47 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
52 } 48 }
53 49
@@ -69,9 +65,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -69,9 +65,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
69 } 65 }
70 66
71 // 2.1 执行认证 67 // 2.1 执行认证
72 - CommonResult<Boolean> result = deviceClientService.authDevice(new IotDeviceAuthReqDTO() 68 + CommonResult<Boolean> result = deviceApi.authDevice(new IotDeviceAuthReqDTO()
73 .setClientId(clientId).setUsername(username).setPassword(password)); 69 .setClientId(clientId).setUsername(username).setPassword(password));
74 - result.checkError();; 70 + result.checkError();
75 if (!BooleanUtil.isTrue(result.getData())) { 71 if (!BooleanUtil.isTrue(result.getData())) {
76 throw exception(DEVICE_AUTH_FAIL); 72 throw exception(DEVICE_AUTH_FAIL);
77 } 73 }
@@ -82,9 +78,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -82,9 +78,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
82 Assert.notBlank(token, "生成 token 不能为空位"); 78 Assert.notBlank(token, "生成 token 不能为空位");
83 79
84 // 3. 执行上线 80 // 3. 执行上线
85 - IotDeviceMessage message = deviceMessageService.buildDeviceMessageOfStateOnline( 81 + IotDeviceMessage message = IotDeviceMessage.buildStateOnline();
  82 + deviceMessageService.sendDeviceMessage(message,
86 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId()); 83 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId());
87 - deviceMessageProducer.sendDeviceMessage(message);  
88 84
89 // 构建响应数据 85 // 构建响应数据
90 return success(MapUtil.of("token", token)); 86 return success(MapUtil.of("token", token));
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; @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
9 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 9 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
10 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 10 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
11 -import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService; 11 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
12 import io.vertx.ext.web.RoutingContext; 12 import io.vertx.ext.web.RoutingContext;
13 import lombok.RequiredArgsConstructor; 13 import lombok.RequiredArgsConstructor;
14 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
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 { @@ -61,7 +61,7 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService {
61 JSONObject payload = jwt.getPayloads(); 61 JSONObject payload = jwt.getPayloads();
62 // 检查过期时间 62 // 检查过期时间
63 Long exp = payload.getLong("exp"); 63 Long exp = payload.getLong("exp");
64 - if (exp == null || exp > System.currentTimeMillis() / 1000) { 64 + if (exp == null || exp < System.currentTimeMillis() / 1000) {
65 throw exception(DEVICE_TOKEN_EXPIRED); 65 throw exception(DEVICE_TOKEN_EXPIRED);
66 } 66 }
67 String productKey = payload.getStr("productKey"); 67 String productKey = payload.getStr("productKey");
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheService.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.service.device;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Data;  
5 -import lombok.NoArgsConstructor;  
6 -  
7 -/**  
8 - * IoT 设备缓存 Service 接口  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -public interface IotDeviceCacheService {  
13 -  
14 - /**  
15 - * 设备信息  
16 - */  
17 - @Data  
18 - @AllArgsConstructor  
19 - @NoArgsConstructor  
20 - class DeviceInfo {  
21 - /**  
22 - * 设备编号  
23 - */  
24 - private Long deviceId;  
25 - /**  
26 - * 产品标识  
27 - */  
28 - private String productKey;  
29 - /**  
30 - * 设备名称  
31 - */  
32 - private String deviceName;  
33 - /**  
34 - * 设备密钥  
35 - */  
36 - private String deviceKey;  
37 - /**  
38 - * 租户编号  
39 - */  
40 - private Long tenantId;  
41 - }  
42 -  
43 - /**  
44 - * 根据 productKey 和 deviceName 获取设备信息  
45 - *  
46 - * @param productKey 产品标识  
47 - * @param deviceName 设备名称  
48 - * @return 设备信息,如果不存在返回 null  
49 - */  
50 - DeviceInfo getDeviceInfo(String productKey, String deviceName);  
51 -  
52 - /**  
53 - * 根据 deviceId 获取设备信息  
54 - *  
55 - * @param deviceId 设备编号  
56 - * @return 设备信息,如果不存在返回 null  
57 - */  
58 - DeviceInfo getDeviceInfoById(Long deviceId);  
59 -  
60 - /**  
61 - * 清除设备缓存  
62 - *  
63 - * @param deviceId 设备编号  
64 - */  
65 - void evictDeviceCache(Long deviceId);  
66 -  
67 - /**  
68 - * 清除设备缓存  
69 - *  
70 - * @param productKey 产品标识  
71 - * @param deviceName 设备名称  
72 - */  
73 - void evictDeviceCache(String productKey, String deviceName);  
74 -  
75 -}  
76 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.service.device;  
2 -  
3 -import cn.hutool.core.util.StrUtil;  
4 -import cn.hutool.extra.spring.SpringUtil;  
5 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
6 -import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;  
7 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;  
8 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO;  
9 -import lombok.extern.slf4j.Slf4j;  
10 -import org.springframework.stereotype.Service;  
11 -  
12 -import java.util.concurrent.ConcurrentHashMap;  
13 -import java.util.concurrent.locks.ReentrantLock;  
14 -  
15 -/**  
16 - * IoT 设备缓存 Service 实现类  
17 - * <p>  
18 - * 使用本地缓存 + 远程 API 的方式获取设备信息,提高性能并避免敏感信息传输  
19 - *  
20 - * @author 芋道源码  
21 - */  
22 -@Service  
23 -@Slf4j  
24 -public class IotDeviceCacheServiceImpl implements IotDeviceCacheService {  
25 -  
26 - /**  
27 - * 设备信息本地缓存  
28 - * Key: deviceId  
29 - * Value: DeviceInfo  
30 - */  
31 - private final ConcurrentHashMap<Long, DeviceInfo> deviceIdCache = new ConcurrentHashMap<>();  
32 -  
33 - /**  
34 - * 设备名称到设备ID的映射缓存  
35 - * Key: productKey:deviceName  
36 - * Value: deviceId  
37 - */  
38 - private final ConcurrentHashMap<String, Long> deviceNameCache = new ConcurrentHashMap<>();  
39 -  
40 - /**  
41 - * 锁对象,防止并发请求同一设备信息  
42 - */  
43 - private final ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();  
44 -  
45 - @Override  
46 - public DeviceInfo getDeviceInfo(String productKey, String deviceName) {  
47 - if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) {  
48 - log.warn("[getDeviceInfo][参数为空][productKey: {}][deviceName: {}]", productKey, deviceName);  
49 - return null;  
50 - }  
51 -  
52 - String cacheKey = buildDeviceNameCacheKey(productKey, deviceName);  
53 -  
54 - // 1. 先从缓存获取 deviceId  
55 - Long deviceId = deviceNameCache.get(cacheKey);  
56 - if (deviceId != null) {  
57 - DeviceInfo deviceInfo = deviceIdCache.get(deviceId);  
58 - if (deviceInfo != null) {  
59 - log.debug("[getDeviceInfo][缓存命中][productKey: {}][deviceName: {}][deviceId: {}]",  
60 - productKey, deviceName, deviceId);  
61 - return deviceInfo;  
62 - }  
63 - }  
64 -  
65 - // 2. 缓存未命中,从远程 API 获取  
66 - return loadDeviceInfoFromApi(productKey, deviceName, cacheKey);  
67 - }  
68 -  
69 - @Override  
70 - public DeviceInfo getDeviceInfoById(Long deviceId) {  
71 - if (deviceId == null) {  
72 - log.warn("[getDeviceInfoById][deviceId 为空]");  
73 - return null;  
74 - }  
75 -  
76 - // 1. 先从缓存获取  
77 - DeviceInfo deviceInfo = deviceIdCache.get(deviceId);  
78 - if (deviceInfo != null) {  
79 - log.debug("[getDeviceInfoById][缓存命中][deviceId: {}]", deviceId);  
80 - return deviceInfo;  
81 - }  
82 -  
83 - // 2. 缓存未命中,从远程 API 获取  
84 - return loadDeviceInfoByIdFromApi(deviceId);  
85 - }  
86 -  
87 - @Override  
88 - public void evictDeviceCache(Long deviceId) {  
89 - if (deviceId == null) {  
90 - return;  
91 - }  
92 -  
93 - DeviceInfo deviceInfo = deviceIdCache.remove(deviceId);  
94 - if (deviceInfo != null) {  
95 - String cacheKey = buildDeviceNameCacheKey(deviceInfo.getProductKey(), deviceInfo.getDeviceName());  
96 - deviceNameCache.remove(cacheKey);  
97 - log.info("[evictDeviceCache][清除设备缓存][deviceId: {}]", deviceId);  
98 - }  
99 - }  
100 -  
101 - @Override  
102 - public void evictDeviceCache(String productKey, String deviceName) {  
103 - if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) {  
104 - return;  
105 - }  
106 -  
107 - String cacheKey = buildDeviceNameCacheKey(productKey, deviceName);  
108 - Long deviceId = deviceNameCache.remove(cacheKey);  
109 - if (deviceId != null) {  
110 - deviceIdCache.remove(deviceId);  
111 - log.info("[evictDeviceCache][清除设备缓存][productKey: {}][deviceName: {}]", productKey, deviceName);  
112 - }  
113 - }  
114 -  
115 - /**  
116 - * 从远程 API 加载设备信息  
117 - *  
118 - * @param productKey 产品标识  
119 - * @param deviceName 设备名称  
120 - * @param cacheKey 缓存键  
121 - * @return 设备信息  
122 - */  
123 - private DeviceInfo loadDeviceInfoFromApi(String productKey, String deviceName, String cacheKey) {  
124 - // 使用锁防止并发请求同一设备信息  
125 - ReentrantLock lock = lockMap.computeIfAbsent(cacheKey, k -> new ReentrantLock());  
126 - lock.lock();  
127 - try {  
128 - // 双重检查,防止重复请求  
129 - Long deviceId = deviceNameCache.get(cacheKey);  
130 - if (deviceId != null) {  
131 - DeviceInfo deviceInfo = deviceIdCache.get(deviceId);  
132 - if (deviceInfo != null) {  
133 - return deviceInfo;  
134 - }  
135 - }  
136 -  
137 - log.info("[loadDeviceInfoFromApi][从远程API获取设备信息][productKey: {}][deviceName: {}]",  
138 - productKey, deviceName);  
139 -  
140 - try {  
141 - // 调用远程 API 获取设备信息  
142 - IotDeviceCommonApi deviceCommonApi = SpringUtil.getBean(IotDeviceCommonApi.class);  
143 - IotDeviceInfoReqDTO reqDTO = new IotDeviceInfoReqDTO();  
144 - reqDTO.setProductKey(productKey);  
145 - reqDTO.setDeviceName(deviceName);  
146 -  
147 - CommonResult<IotDeviceInfoRespDTO> result = deviceCommonApi.getDeviceInfo(reqDTO);  
148 -  
149 - if (result == null || !result.isSuccess() || result.getData() == null) {  
150 - log.warn("[loadDeviceInfoFromApi][远程API调用失败][productKey: {}][deviceName: {}][result: {}]",  
151 - productKey, deviceName, result);  
152 - return null;  
153 - }  
154 -  
155 - IotDeviceInfoRespDTO respDTO = result.getData();  
156 - DeviceInfo deviceInfo = new DeviceInfo(  
157 - respDTO.getDeviceId(),  
158 - respDTO.getProductKey(),  
159 - respDTO.getDeviceName(),  
160 - respDTO.getDeviceKey(),  
161 - respDTO.getTenantId());  
162 -  
163 - // 缓存设备信息  
164 - cacheDeviceInfo(deviceInfo, cacheKey);  
165 -  
166 - log.info("[loadDeviceInfoFromApi][设备信息获取成功并已缓存][deviceInfo: {}]", deviceInfo);  
167 - return deviceInfo;  
168 -  
169 - } catch (Exception e) {  
170 - log.error("[loadDeviceInfoFromApi][远程API调用异常][productKey: {}][deviceName: {}]",  
171 - productKey, deviceName, e);  
172 - return null;  
173 - }  
174 - } finally {  
175 - lock.unlock();  
176 - // 清理锁对象,避免内存泄漏  
177 - if (lockMap.size() > 1000) { // 简单的清理策略  
178 - lockMap.entrySet().removeIf(entry -> !entry.getValue().hasQueuedThreads());  
179 - }  
180 - }  
181 - }  
182 -  
183 - /**  
184 - * 从远程 API 根据 deviceId 加载设备信息  
185 - *  
186 - * @param deviceId 设备编号  
187 - * @return 设备信息  
188 - */  
189 - private DeviceInfo loadDeviceInfoByIdFromApi(Long deviceId) {  
190 - String lockKey = "deviceId:" + deviceId;  
191 - ReentrantLock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock());  
192 - lock.lock();  
193 - try {  
194 - // 双重检查  
195 - DeviceInfo deviceInfo = deviceIdCache.get(deviceId);  
196 - if (deviceInfo != null) {  
197 - return deviceInfo;  
198 - }  
199 -  
200 - log.info("[loadDeviceInfoByIdFromApi][从远程API获取设备信息][deviceId: {}]", deviceId);  
201 -  
202 - try {  
203 - // TODO: 这里需要添加根据 deviceId 获取设备信息的 API  
204 - // 暂时返回 null,等待 API 完善  
205 - log.warn("[loadDeviceInfoByIdFromApi][根据deviceId获取设备信息的API尚未实现][deviceId: {}]", deviceId);  
206 - return null;  
207 -  
208 - } catch (Exception e) {  
209 - log.error("[loadDeviceInfoByIdFromApi][远程API调用异常][deviceId: {}]", deviceId, e);  
210 - return null;  
211 - }  
212 - } finally {  
213 - lock.unlock();  
214 - }  
215 - }  
216 -  
217 - /**  
218 - * 缓存设备信息  
219 - *  
220 - * @param deviceInfo 设备信息  
221 - * @param cacheKey 缓存键  
222 - */  
223 - private void cacheDeviceInfo(DeviceInfo deviceInfo, String cacheKey) {  
224 - if (deviceInfo != null && deviceInfo.getDeviceId() != null) {  
225 - deviceIdCache.put(deviceInfo.getDeviceId(), deviceInfo);  
226 - deviceNameCache.put(cacheKey, deviceInfo.getDeviceId());  
227 - }  
228 - }  
229 -  
230 - /**  
231 - * 构建设备名称缓存键  
232 - *  
233 - * @param productKey 产品标识  
234 - * @param deviceName 设备名称  
235 - * @return 缓存键  
236 - */  
237 - private String buildDeviceNameCacheKey(String productKey, String deviceName) {  
238 - return productKey + ":" + deviceName;  
239 - }  
240 -  
241 -}  
242 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.service.device;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  4 +
  5 +/**
  6 + * IoT 设备信息 Service 接口
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +public interface IotDeviceService {
  11 +
  12 + /**
  13 + * 根据 productKey 和 deviceName 获取设备信息
  14 + *
  15 + * @param productKey 产品标识
  16 + * @param deviceName 设备名称
  17 + * @return 设备信息
  18 + */
  19 + IotDeviceRespDTO getDeviceFromCache(String productKey, String deviceName);
  20 +
  21 + /**
  22 + * 根据 id 获取设备信息
  23 + *
  24 + * @param id 设备编号
  25 + * @return 设备信息
  26 + */
  27 + IotDeviceRespDTO getDeviceFromCache(Long id);
  28 +
  29 +}
0 \ No newline at end of file 30 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.service.device;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.core.KeyValue;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
  7 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
  8 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  9 +import com.google.common.cache.CacheLoader;
  10 +import com.google.common.cache.LoadingCache;
  11 +import jakarta.annotation.Resource;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.springframework.stereotype.Service;
  14 +
  15 +import java.time.Duration;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
  18 +
  19 +/**
  20 + * IoT 设备信息 Service 实现类
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@Service
  25 +@Slf4j
  26 +public class IotDeviceServiceImpl implements IotDeviceService {
  27 +
  28 + private static final Duration CACHE_EXPIRE = Duration.ofMinutes(1);
  29 +
  30 + /**
  31 + * 通过 id 查询设备的缓存
  32 + */
  33 + private final LoadingCache<Long, IotDeviceRespDTO> deviceCaches = buildAsyncReloadingCache(
  34 + CACHE_EXPIRE,
  35 + new CacheLoader<>() {
  36 +
  37 + @Override
  38 + public IotDeviceRespDTO load(Long id) {
  39 + CommonResult<IotDeviceRespDTO> result = deviceApi.getDevice(new IotDeviceGetReqDTO().setId(id));
  40 + IotDeviceRespDTO device = result.getCheckedData();
  41 + Assert.notNull(device, "设备({}) 不能为空", id);
  42 + // 相互缓存
  43 + deviceCaches2.put(new KeyValue<>(device.getProductKey(), device.getDeviceName()), device);
  44 + return device;
  45 + }
  46 +
  47 + });
  48 +
  49 + /**
  50 + * 通过 productKey + deviceName 查询设备的缓存
  51 + */
  52 + private final LoadingCache<KeyValue<String, String>, IotDeviceRespDTO> deviceCaches2 = buildAsyncReloadingCache(
  53 + CACHE_EXPIRE,
  54 + new CacheLoader<>() {
  55 +
  56 + @Override
  57 + public IotDeviceRespDTO load(KeyValue<String, String> kv) {
  58 + CommonResult<IotDeviceRespDTO> result = deviceApi.getDevice(new IotDeviceGetReqDTO()
  59 + .setProductKey(kv.getKey()).setDeviceName(kv.getValue()));
  60 + IotDeviceRespDTO device = result.getCheckedData();
  61 + Assert.notNull(device, "设备({}/{}) 不能为空", kv.getKey(), kv.getValue());
  62 + // 相互缓存
  63 + deviceCaches.put(device.getId(), device);
  64 + return device;
  65 + }
  66 + });
  67 +
  68 + @Resource
  69 + private IotDeviceCommonApi deviceApi;
  70 +
  71 + @Override
  72 + public IotDeviceRespDTO getDeviceFromCache(String productKey, String deviceName) {
  73 + return deviceCaches2.getUnchecked(new KeyValue<>(productKey, deviceName));
  74 + }
  75 +
  76 + @Override
  77 + public IotDeviceRespDTO getDeviceFromCache(Long id) {
  78 + return deviceCaches.getUnchecked(id);
  79 + }
  80 +
  81 +}
0 \ No newline at end of file 82 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageService.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageService.java
1 -package cn.iocoder.yudao.module.iot.gateway.service.message; 1 +package cn.iocoder.yudao.module.iot.gateway.service.device.message;
2 2
3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4 4
@@ -26,30 +26,20 @@ public interface IotDeviceMessageService { @@ -26,30 +26,20 @@ public interface IotDeviceMessageService {
26 * @param bytes 消息内容 26 * @param bytes 消息内容
27 * @param productKey 产品 Key 27 * @param productKey 产品 Key
28 * @param deviceName 设备名称 28 * @param deviceName 设备名称
29 - * @param serverId 设备连接的 serverId  
30 * @return 解码后的消息内容 29 * @return 解码后的消息内容
31 */ 30 */
32 IotDeviceMessage decodeDeviceMessage(byte[] bytes, 31 IotDeviceMessage decodeDeviceMessage(byte[] bytes,
33 - String productKey, String deviceName, String serverId); 32 + String productKey, String deviceName);
34 33
35 /** 34 /**
36 - * 构建【设备上线】消息 35 + * 发送消息
37 * 36 *
  37 + * @param message 消息
38 * @param productKey 产品 Key 38 * @param productKey 产品 Key
39 * @param deviceName 设备名称 39 * @param deviceName 设备名称
40 - * @param serverId 设备连接的 serverId  
41 - * @return 消息 40 + * @param serverId 设备连接的 serverId
42 */ 41 */
43 - IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId);  
44 -  
45 - /**  
46 - * 构建【设备下线】消息  
47 - *  
48 - * @param productKey 产品 Key  
49 - * @param deviceName 设备名称  
50 - * @param serverId 设备连接的 serverId  
51 - * @return 消息  
52 - */  
53 - IotDeviceMessage buildDeviceMessageOfStateOffline(String productKey, String deviceName, String serverId); 42 + void sendDeviceMessage(IotDeviceMessage message,
  43 + String productKey, String deviceName, String serverId);
54 44
55 } 45 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java
1 -package cn.iocoder.yudao.module.iot.gateway.service.message; 1 +package cn.iocoder.yudao.module.iot.gateway.service.device.message;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 5 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 8 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
8 import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; 9 import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec;
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceCacheService; 10 +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
10 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
11 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
12 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
@@ -15,6 +16,9 @@ import java.time.LocalDateTime; @@ -15,6 +16,9 @@ import java.time.LocalDateTime;
15 import java.util.List; 16 import java.util.List;
16 import java.util.Map; 17 import java.util.Map;
17 18
  19 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  20 +import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS;
  21 +
18 /** 22 /**
19 * IoT 设备消息 Service 实现类 23 * IoT 设备消息 Service 实现类
20 * 24 *
@@ -30,7 +34,10 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -30,7 +34,10 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
30 private final Map<String, IotAlinkDeviceMessageCodec> codes; 34 private final Map<String, IotAlinkDeviceMessageCodec> codes;
31 35
32 @Resource 36 @Resource
33 - private IotDeviceCacheService deviceCacheService; 37 + private IotDeviceService deviceService;
  38 +
  39 + @Resource
  40 + private IotDeviceMessageProducer deviceMessageProducer;
34 41
35 public IotDeviceMessageServiceImpl(List<IotAlinkDeviceMessageCodec> codes) { 42 public IotDeviceMessageServiceImpl(List<IotAlinkDeviceMessageCodec> codes) {
36 this.codes = CollectionUtils.convertMap(codes, IotAlinkDeviceMessageCodec::type); 43 this.codes = CollectionUtils.convertMap(codes, IotAlinkDeviceMessageCodec::type);
@@ -39,82 +46,51 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -39,82 +46,51 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
39 @Override 46 @Override
40 public byte[] encodeDeviceMessage(IotDeviceMessage message, 47 public byte[] encodeDeviceMessage(IotDeviceMessage message,
41 String productKey, String deviceName) { 48 String productKey, String deviceName) {
42 - // 获取设备信息以确定编解码类型  
43 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);  
44 - if (deviceInfo == null) {  
45 - log.warn("[encodeDeviceMessage][设备信息不存在][productKey: {}][deviceName: {}]",  
46 - productKey, deviceName);  
47 - return null; 49 + // 1.1 获取设备信息
  50 + IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
  51 + if (device == null) {
  52 + throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);
48 } 53 }
49 -  
50 - String codecType = "alink"; // 默认使用 alink 编解码器  
51 - IotAlinkDeviceMessageCodec codec = codes.get(codecType); 54 + // 1.2 获取编解码器
  55 + IotAlinkDeviceMessageCodec codec = codes.get(device.getCodecType());
52 if (codec == null) { 56 if (codec == null) {
53 - log.error("[encodeDeviceMessage][编解码器不存在][codecType: {}]", codecType);  
54 - return null; 57 + throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType()));
55 } 58 }
56 59
  60 + // 2. 编码消息
57 return codec.encode(message); 61 return codec.encode(message);
58 } 62 }
59 63
60 @Override 64 @Override
61 public IotDeviceMessage decodeDeviceMessage(byte[] bytes, 65 public IotDeviceMessage decodeDeviceMessage(byte[] bytes,
62 - String productKey, String deviceName, String serverId) {  
63 - // 获取设备信息  
64 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);  
65 - if (deviceInfo == null) {  
66 - log.warn("[decodeDeviceMessage][设备信息不存在][productKey: {}][deviceName: {}]",  
67 - productKey, deviceName);  
68 - return null; 66 + String productKey, String deviceName) {
  67 + // 1.1 获取设备信息
  68 + IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
  69 + if (device == null) {
  70 + throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);
69 } 71 }
70 -  
71 - String codecType = "alink"; // 默认使用 alink 编解码器  
72 - IotAlinkDeviceMessageCodec codec = codes.get(codecType); 72 + // 1.2 获取编解码器
  73 + IotAlinkDeviceMessageCodec codec = codes.get(device.getCodecType());
73 if (codec == null) { 74 if (codec == null) {
74 - log.error("[decodeDeviceMessage][编解码器不存在][codecType: {}]", codecType);  
75 - return null;  
76 - }  
77 -  
78 - IotDeviceMessage message = codec.decode(bytes);  
79 - if (message == null) {  
80 - log.warn("[decodeDeviceMessage][消息解码失败][productKey: {}][deviceName: {}]",  
81 - productKey, deviceName);  
82 - return null; 75 + throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType()));
83 } 76 }
84 77
85 - // 补充后端字段  
86 - return appendDeviceMessage(message, deviceInfo, serverId);  
87 - }  
88 -  
89 - @Override  
90 - public IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId) {  
91 - // 获取设备信息  
92 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);  
93 - if (deviceInfo == null) {  
94 - log.warn("[buildDeviceMessageOfStateOnline][设备信息不存在][productKey: {}][deviceName: {}]",  
95 - productKey, deviceName);  
96 - return null;  
97 - }  
98 -  
99 - IotDeviceMessage message = IotDeviceMessage.requestOf(null,  
100 - IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), null);  
101 -  
102 - return appendDeviceMessage(message, deviceInfo, serverId); 78 + // 2. 解码消息
  79 + return codec.decode(bytes);
103 } 80 }
104 81
105 @Override 82 @Override
106 - public IotDeviceMessage buildDeviceMessageOfStateOffline(String productKey, String deviceName, String serverId) {  
107 - // 获取设备信息  
108 - IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);  
109 - if (deviceInfo == null) {  
110 - log.warn("[buildDeviceMessageOfStateOffline][设备信息不存在][productKey: {}][deviceName: {}]",  
111 - productKey, deviceName);  
112 - return null; 83 + public void sendDeviceMessage(IotDeviceMessage message,
  84 + String productKey, String deviceName, String serverId) {
  85 + // 1. 获取设备信息
  86 + IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
  87 + if (device == null) {
  88 + throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);
113 } 89 }
114 90
115 - IotDeviceMessage message = IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod(),  
116 - null);  
117 - return appendDeviceMessage(message, deviceInfo, serverId); 91 + // 2. 发送消息
  92 + appendDeviceMessage(message, device, serverId);
  93 + deviceMessageProducer.sendDeviceMessage(message);
118 } 94 }
119 95
120 /** 96 /**
@@ -126,9 +102,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -126,9 +102,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
126 * @return 消息 102 * @return 消息
127 */ 103 */
128 private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message, 104 private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message,
129 - IotDeviceCacheService.DeviceInfo device, String serverId) { 105 + IotDeviceRespDTO device, String serverId) {
130 message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) 106 message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())
131 - .setDeviceId(device.getDeviceId()).setTenantId(device.getTenantId()).setServerId(serverId); 107 + .setDeviceId(device.getId()).setTenantId(device.getTenantId()).setServerId(serverId);
132 // 特殊:如果设备没有指定 requestId,则使用 messageId 108 // 特殊:如果设备没有指定 requestId,则使用 messageId
133 if (StrUtil.isEmpty(message.getRequestId())) { 109 if (StrUtil.isEmpty(message.getRequestId())) {
134 message.setRequestId(message.getId()); 110 message.setRequestId(message.getId());
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceClientServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java
1 -package cn.iocoder.yudao.module.iot.gateway.service.device; 1 +package cn.iocoder.yudao.module.iot.gateway.service.device.remote;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 -import cn.hutool.core.bean.BeanUtil;  
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 5 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
8 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;  
9 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO; 7 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
  8 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
10 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 9 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
11 import jakarta.annotation.PostConstruct; 10 import jakarta.annotation.PostConstruct;
12 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
@@ -19,8 +18,6 @@ import org.springframework.http.ResponseEntity; @@ -19,8 +18,6 @@ import org.springframework.http.ResponseEntity;
19 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
20 import org.springframework.web.client.RestTemplate; 19 import org.springframework.web.client.RestTemplate;
21 20
22 -import java.util.LinkedHashMap;  
23 -  
24 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; 21 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
25 22
26 /** 23 /**
@@ -30,7 +27,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC @@ -30,7 +27,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC
30 */ 27 */
31 @Service 28 @Service
32 @Slf4j 29 @Slf4j
33 -public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { 30 +public class IotDeviceApiImpl implements IotDeviceCommonApi {
34 31
35 @Resource 32 @Resource
36 private IotGatewayProperties gatewayProperties; 33 private IotGatewayProperties gatewayProperties;
@@ -49,57 +46,27 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { @@ -49,57 +46,27 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi {
49 46
50 @Override 47 @Override
51 public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) { 48 public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) {
52 - return doPost("/auth", authReqDTO); 49 + return doPost("/auth", authReqDTO, new ParameterizedTypeReference<>() { });
53 } 50 }
54 51
55 @Override 52 @Override
56 - public CommonResult<IotDeviceInfoRespDTO> getDeviceInfo(IotDeviceInfoReqDTO infoReqDTO) {  
57 - return doPostForDeviceInfo("/info", infoReqDTO); 53 + public CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO getReqDTO) {
  54 + return doPost("/get", getReqDTO, new ParameterizedTypeReference<>() { });
58 } 55 }
59 56
60 - @SuppressWarnings("unchecked")  
61 - private <T> CommonResult<Boolean> doPost(String url, T requestBody) { 57 + private <T, R> CommonResult<R> doPost(String url, T body,
  58 + ParameterizedTypeReference<CommonResult<R>> responseType) {
62 try { 59 try {
63 - CommonResult<Boolean> result = restTemplate.postForObject(url, requestBody,  
64 - (Class<CommonResult<Boolean>>) (Class<?>) CommonResult.class);  
65 - log.info("[doPost][url({}) requestBody({}) result({})]", url, requestBody, result); 60 + // 请求
  61 + HttpEntity<T> requestEntity = new HttpEntity<>(body);
  62 + ResponseEntity<CommonResult<R>> response = restTemplate.exchange(
  63 + url, HttpMethod.POST, requestEntity, responseType);
  64 + // 响应
  65 + CommonResult<R> result = response.getBody();
66 Assert.notNull(result, "请求结果不能为空"); 66 Assert.notNull(result, "请求结果不能为空");
67 return result; 67 return result;
68 } catch (Exception e) { 68 } catch (Exception e) {
69 - log.error("[doPost][url({}) requestBody({}) 发生异常]", url, requestBody, e);  
70 - return CommonResult.error(INTERNAL_SERVER_ERROR);  
71 - }  
72 - }  
73 -  
74 - @SuppressWarnings("unchecked")  
75 - private <T> CommonResult<IotDeviceInfoRespDTO> doPostForDeviceInfo(String url, T requestBody) {  
76 - try {  
77 - // 使用 ParameterizedTypeReference 来处理泛型类型  
78 - ParameterizedTypeReference<CommonResult<LinkedHashMap<String, Object>>> typeRef = new ParameterizedTypeReference<CommonResult<LinkedHashMap<String, Object>>>() {  
79 - };  
80 -  
81 - HttpEntity<T> requestEntity = new HttpEntity<>(requestBody);  
82 - ResponseEntity<CommonResult<LinkedHashMap<String, Object>>> response = restTemplate.exchange(url,  
83 - HttpMethod.POST, requestEntity, typeRef);  
84 -  
85 - CommonResult<LinkedHashMap<String, Object>> rawResult = response.getBody();  
86 - log.info("[doPostForDeviceInfo][url({}) requestBody({}) rawResult({})]", url, requestBody, rawResult);  
87 - Assert.notNull(rawResult, "请求结果不能为空");  
88 -  
89 - // 手动转换数据类型  
90 - CommonResult<IotDeviceInfoRespDTO> result = new CommonResult<>();  
91 - result.setCode(rawResult.getCode());  
92 - result.setMsg(rawResult.getMsg());  
93 -  
94 - if (rawResult.isSuccess() && rawResult.getData() != null) {  
95 - // 将 LinkedHashMap 转换为 IotDeviceInfoRespDTO  
96 - IotDeviceInfoRespDTO deviceInfo = BeanUtil.toBean(rawResult.getData(), IotDeviceInfoRespDTO.class);  
97 - result.setData(deviceInfo);  
98 - }  
99 -  
100 - return result;  
101 - } catch (Exception e) {  
102 - log.error("[doPostForDeviceInfo][url({}) requestBody({}) 发生异常]", url, requestBody, e); 69 + log.error("[doPost][url({}) body({}) 发生异常]", url, body, e);
103 return CommonResult.error(INTERNAL_SERVER_ERROR); 70 return CommonResult.error(INTERNAL_SERVER_ERROR);
104 } 71 }
105 } 72 }