Commit 5db1c5116c338653d7e97555ddbb0e70a8651299

Authored by YunaiV
1 parent 56b2b540

refactor(iot): 对齐设备影子代码风格

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceShadowController.java
@@ -57,31 +57,23 @@ public class IotDeviceShadowController { @@ -57,31 +57,23 @@ public class IotDeviceShadowController {
57 public CommonResult<IotDeviceShadowRespVO> getDeviceShadow(@RequestParam("deviceId") Long deviceId) { 57 public CommonResult<IotDeviceShadowRespVO> getDeviceShadow(@RequestParam("deviceId") Long deviceId) {
58 IotDeviceDO device = deviceService.validateDeviceExists(deviceId); 58 IotDeviceDO device = deviceService.validateDeviceExists(deviceId);
59 IotDeviceShadowDO shadow = deviceShadowService.getOrCreateShadow(device); 59 IotDeviceShadowDO shadow = deviceShadowService.getOrCreateShadow(device);
60 - 60 + // 拼接 VO
61 List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductIdAndType( 61 List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductIdAndType(
62 device.getProductId(), IotThingModelTypeEnum.PROPERTY.getType()); 62 device.getProductId(), IotThingModelTypeEnum.PROPERTY.getType());
63 Map<String, Object> desired = shadow.getDesired() != null ? shadow.getDesired() : MapUtil.empty(); 63 Map<String, Object> desired = shadow.getDesired() != null ? shadow.getDesired() : MapUtil.empty();
64 Map<String, Object> reported = shadow.getReported() != null ? shadow.getReported() : MapUtil.empty(); 64 Map<String, Object> reported = shadow.getReported() != null ? shadow.getReported() : MapUtil.empty();
65 Map<String, Object> metadata = shadow.getMetadata() != null ? shadow.getMetadata() : MapUtil.empty(); 65 Map<String, Object> metadata = shadow.getMetadata() != null ? shadow.getMetadata() : MapUtil.empty();
66 - // 拼接 VO  
67 List<IotDeviceShadowPropertyRespVO> properties = convertList(thingModels, thingModel -> { 66 List<IotDeviceShadowPropertyRespVO> properties = convertList(thingModels, thingModel -> {
68 ThingModelProperty prop = thingModel.getProperty(); 67 ThingModelProperty prop = thingModel.getProperty();
69 Assert.notNull(prop, "属性不能为空"); 68 Assert.notNull(prop, "属性不能为空");
70 - return new IotDeviceShadowPropertyRespVO()  
71 - .setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName())  
72 - .setDataType(prop.getDataType()).setDataSpecs(prop.getDataSpecs())  
73 - .setDataSpecsList(prop.getDataSpecsList())  
74 - .setDesiredValue(desired.get(thingModel.getIdentifier()))  
75 - .setReportedValue(reported.get(thingModel.getIdentifier())) 69 + return new IotDeviceShadowPropertyRespVO().setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName())
  70 + .setDataType(prop.getDataType()).setDataSpecs(prop.getDataSpecs()).setDataSpecsList(prop.getDataSpecsList())
  71 + .setDesiredValue(desired.get(thingModel.getIdentifier())).setReportedValue(reported.get(thingModel.getIdentifier()))
76 .setDesiredTime(IotDeviceShadowProtocolUtils.getDesiredTimestamp(metadata, thingModel.getIdentifier())) 72 .setDesiredTime(IotDeviceShadowProtocolUtils.getDesiredTimestamp(metadata, thingModel.getIdentifier()))
77 .setReportedTime(IotDeviceShadowProtocolUtils.getReportedTimestamp(metadata, thingModel.getIdentifier())); 73 .setReportedTime(IotDeviceShadowProtocolUtils.getReportedTimestamp(metadata, thingModel.getIdentifier()));
78 }); 74 });
79 -  
80 IotDeviceShadowRespVO respVO = BeanUtils.toBean(shadow, IotDeviceShadowRespVO.class) 75 IotDeviceShadowRespVO respVO = BeanUtils.toBean(shadow, IotDeviceShadowRespVO.class)
81 - .setProperties(properties)  
82 - .setDesired(desired)  
83 - .setReported(reported)  
84 - .setMetadata(metadata); 76 + .setProperties(properties).setDesired(desired).setReported(reported).setMetadata(metadata);
85 return success(respVO); 77 return success(respVO);
86 } 78 }
87 79
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/shadow/IotDeviceShadowRespVO.java
@@ -21,7 +21,7 @@ public class IotDeviceShadowRespVO { @@ -21,7 +21,7 @@ public class IotDeviceShadowRespVO {
21 @Schema(description = "最后更新时间(epoch 秒)", example = "1469564576") 21 @Schema(description = "最后更新时间(epoch 秒)", example = "1469564576")
22 private Long shadowTime; 22 private Long shadowTime;
23 23
24 - @Schema(description = "是否启用", example = "0") 24 + @Schema(description = "影子状态", example = "0")
25 private Integer status; 25 private Integer status;
26 26
27 @Schema(description = "属性对比列表") 27 @Schema(description = "属性对比列表")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceShadowDO.java
@@ -34,6 +34,8 @@ public class IotDeviceShadowDO extends TenantBaseDO { @@ -34,6 +34,8 @@ public class IotDeviceShadowDO extends TenantBaseDO {
34 private Long id; 34 private Long id;
35 /** 35 /**
36 * 设备编号 36 * 设备编号
  37 + * <p>
  38 + * 关联 {@link IotDeviceDO#getId()}
37 */ 39 */
38 private Long deviceId; 40 private Long deviceId;
39 /** 41 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/shadow/IotDeviceShadowServiceImpl.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device.shadow; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device.shadow;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.date.DateUtil; 4 import cn.hutool.core.date.DateUtil;
5 import cn.hutool.core.lang.Assert; 5 import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.map.MapUtil;
6 import cn.hutool.core.util.ObjUtil; 7 import cn.hutool.core.util.ObjUtil;
7 import cn.hutool.core.util.StrUtil; 8 import cn.hutool.core.util.StrUtil;
8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 9 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@@ -96,6 +97,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -96,6 +97,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
96 } 97 }
97 } 98 }
98 99
  100 + /**
  101 + * 处理设备影子上行:解析 params,并按内层 method 分发到 get、update
  102 + * <p>
  103 + * 影子未启用时,直接忽略,不回复
  104 + *
  105 + * @param device 设备
  106 + * @param message 上行消息
  107 + */
99 private void handleDeviceShadowUpdate0(IotDeviceDO device, IotDeviceMessage message) { 108 private void handleDeviceShadowUpdate0(IotDeviceDO device, IotDeviceMessage message) {
100 // 1.1 解析请求:params 结构为 { method, state: { reported }, version } 109 // 1.1 解析请求:params 结构为 { method, state: { reported }, version }
101 IotDeviceShadowUpdateReqDTO reqDTO = JsonUtils.convertObject(message.getParams(), 110 IotDeviceShadowUpdateReqDTO reqDTO = JsonUtils.convertObject(message.getParams(),
@@ -125,6 +134,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -125,6 +134,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
125 shadow.getVersion()); 134 shadow.getVersion());
126 } 135 }
127 136
  137 + /**
  138 + * 处理设备上报的 update:version 为 -1 时清空影子,否则合并 reported,并回复新版本
  139 + *
  140 + * @param device 设备
  141 + * @param message 上行消息
  142 + * @param reqDTO 上行 params
  143 + * @param shadow DB 中的当前影子,用于错误回复时携带版本
  144 + */
128 private void handleReportedUpdate(IotDeviceDO device, IotDeviceMessage message, 145 private void handleReportedUpdate(IotDeviceDO device, IotDeviceMessage message,
129 IotDeviceShadowUpdateReqDTO reqDTO, IotDeviceShadowDO shadow) { 146 IotDeviceShadowUpdateReqDTO reqDTO, IotDeviceShadowDO shadow) {
130 // 1. version 为 -1 时,清空影子,无需 state 147 // 1. version 为 -1 时,清空影子,无需 state
@@ -269,13 +286,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -269,13 +286,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
269 } 286 }
270 287
271 // 2.1 合并属性,并校验属性数 288 // 2.1 合并属性,并校验属性数
272 - Map<String, Object> state = mergeMap(desired ? latest.getDesired() : latest.getReported(), values); 289 + Map<String, Object> state = new HashMap<>(MapUtil.emptyIfNull(desired ? latest.getDesired() : latest.getReported()));
  290 + state.putAll(values);
273 if (state.size() > MAX_PROPERTY_COUNT) { 291 if (state.size() > MAX_PROPERTY_COUNT) {
274 throw exception(DEVICE_SHADOW_PROPERTY_COUNT_EXCEEDED); 292 throw exception(DEVICE_SHADOW_PROPERTY_COUNT_EXCEEDED);
275 } 293 }
276 // 2.2 记录属性的更新时间 294 // 2.2 记录属性的更新时间
277 long now = DateUtil.currentSeconds(); 295 long now = DateUtil.currentSeconds();
278 - Map<String, Object> metadata = mergeMap(latest.getMetadata(), null); 296 + Map<String, Object> metadata = new HashMap<>(MapUtil.emptyIfNull(latest.getMetadata()));
279 if (desired) { 297 if (desired) {
280 IotDeviceShadowProtocolUtils.putDesiredTimestamps(metadata, values.keySet(), now); 298 IotDeviceShadowProtocolUtils.putDesiredTimestamps(metadata, values.keySet(), now);
281 } else { 299 } else {
@@ -338,6 +356,9 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -338,6 +356,9 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
338 }); 356 });
339 } 357 }
340 358
  359 + /**
  360 + * 回复全量影子:设备 get 请求的回复,或 desired 更新后的主动推送(requestId 为空)
  361 + */
341 private void sendShadowGetReply(IotDeviceDO device, String serverId, String requestId, IotDeviceShadowDO shadow) { 362 private void sendShadowGetReply(IotDeviceDO device, String serverId, String requestId, IotDeviceShadowDO shadow) {
342 sendShadowReply(device, serverId, requestId, SUCCESS.getCode(), SUCCESS.getMsg(), 363 sendShadowReply(device, serverId, requestId, SUCCESS.getCode(), SUCCESS.getMsg(),
343 IotDeviceShadowProtocolUtils.buildGetData(shadow.getDesired(), shadow.getReported(), 364 IotDeviceShadowProtocolUtils.buildGetData(shadow.getDesired(), shadow.getReported(),
@@ -381,12 +402,4 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -381,12 +402,4 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
381 } 402 }
382 } 403 }
383 404
384 - private static Map<String, Object> mergeMap(Map<String, Object> oldMap, Map<String, Object> newMap) {  
385 - Map<String, Object> result = oldMap != null ? new HashMap<>(oldMap) : new HashMap<>();  
386 - if (CollUtil.isNotEmpty(newMap)) {  
387 - result.putAll(newMap);  
388 - }  
389 - return result;  
390 - }  
391 -  
392 } 405 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceShadowProtocolUtils.java
1 package cn.iocoder.yudao.module.iot.core.util; 1 package cn.iocoder.yudao.module.iot.core.util;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
  4 +import lombok.experimental.UtilityClass;
4 5
5 import java.util.HashMap; 6 import java.util.HashMap;
6 import java.util.Map; 7 import java.util.Map;
@@ -13,11 +14,9 @@ import java.util.Set; @@ -13,11 +14,9 @@ import java.util.Set;
13 * 14 *
14 * @author 芋道源码 15 * @author 芋道源码
15 */ 16 */
  17 +@UtilityClass
16 public final class IotDeviceShadowProtocolUtils { 18 public final class IotDeviceShadowProtocolUtils {
17 19
18 - private IotDeviceShadowProtocolUtils() {  
19 - }  
20 -  
21 // ========== 内层 method(上行 params.method) ========== 20 // ========== 内层 method(上行 params.method) ==========
22 21
23 public static final String INNER_METHOD_GET = "get"; 22 public static final String INNER_METHOD_GET = "get";