diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceShadowController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceShadowController.java index 37f5703..b1a929e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceShadowController.java +++ b/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 { public CommonResult getDeviceShadow(@RequestParam("deviceId") Long deviceId) { IotDeviceDO device = deviceService.validateDeviceExists(deviceId); IotDeviceShadowDO shadow = deviceShadowService.getOrCreateShadow(device); - + // 拼接 VO List thingModels = thingModelService.getThingModelListByProductIdAndType( device.getProductId(), IotThingModelTypeEnum.PROPERTY.getType()); Map desired = shadow.getDesired() != null ? shadow.getDesired() : MapUtil.empty(); Map reported = shadow.getReported() != null ? shadow.getReported() : MapUtil.empty(); Map metadata = shadow.getMetadata() != null ? shadow.getMetadata() : MapUtil.empty(); - // 拼接 VO List properties = convertList(thingModels, thingModel -> { ThingModelProperty prop = thingModel.getProperty(); Assert.notNull(prop, "属性不能为空"); - return new IotDeviceShadowPropertyRespVO() - .setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName()) - .setDataType(prop.getDataType()).setDataSpecs(prop.getDataSpecs()) - .setDataSpecsList(prop.getDataSpecsList()) - .setDesiredValue(desired.get(thingModel.getIdentifier())) - .setReportedValue(reported.get(thingModel.getIdentifier())) + return new IotDeviceShadowPropertyRespVO().setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName()) + .setDataType(prop.getDataType()).setDataSpecs(prop.getDataSpecs()).setDataSpecsList(prop.getDataSpecsList()) + .setDesiredValue(desired.get(thingModel.getIdentifier())).setReportedValue(reported.get(thingModel.getIdentifier())) .setDesiredTime(IotDeviceShadowProtocolUtils.getDesiredTimestamp(metadata, thingModel.getIdentifier())) .setReportedTime(IotDeviceShadowProtocolUtils.getReportedTimestamp(metadata, thingModel.getIdentifier())); }); - IotDeviceShadowRespVO respVO = BeanUtils.toBean(shadow, IotDeviceShadowRespVO.class) - .setProperties(properties) - .setDesired(desired) - .setReported(reported) - .setMetadata(metadata); + .setProperties(properties).setDesired(desired).setReported(reported).setMetadata(metadata); return success(respVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/shadow/IotDeviceShadowRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/shadow/IotDeviceShadowRespVO.java index e712a75..2f2ec0e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/shadow/IotDeviceShadowRespVO.java +++ b/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 { @Schema(description = "最后更新时间(epoch 秒)", example = "1469564576") private Long shadowTime; - @Schema(description = "是否启用", example = "0") + @Schema(description = "影子状态", example = "0") private Integer status; @Schema(description = "属性对比列表") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceShadowDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceShadowDO.java index b89524c..1a94c6e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceShadowDO.java +++ b/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 { private Long id; /** * 设备编号 + *

+ * 关联 {@link IotDeviceDO#getId()} */ private Long deviceId; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/shadow/IotDeviceShadowServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/shadow/IotDeviceShadowServiceImpl.java index ed5cc37..4cc2d2c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/shadow/IotDeviceShadowServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -96,6 +97,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { } } + /** + * 处理设备影子上行:解析 params,并按内层 method 分发到 get、update + *

+ * 影子未启用时,直接忽略,不回复 + * + * @param device 设备 + * @param message 上行消息 + */ private void handleDeviceShadowUpdate0(IotDeviceDO device, IotDeviceMessage message) { // 1.1 解析请求:params 结构为 { method, state: { reported }, version } IotDeviceShadowUpdateReqDTO reqDTO = JsonUtils.convertObject(message.getParams(), @@ -125,6 +134,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { shadow.getVersion()); } + /** + * 处理设备上报的 update:version 为 -1 时清空影子,否则合并 reported,并回复新版本 + * + * @param device 设备 + * @param message 上行消息 + * @param reqDTO 上行 params + * @param shadow DB 中的当前影子,用于错误回复时携带版本 + */ private void handleReportedUpdate(IotDeviceDO device, IotDeviceMessage message, IotDeviceShadowUpdateReqDTO reqDTO, IotDeviceShadowDO shadow) { // 1. version 为 -1 时,清空影子,无需 state @@ -269,13 +286,14 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { } // 2.1 合并属性,并校验属性数 - Map state = mergeMap(desired ? latest.getDesired() : latest.getReported(), values); + Map state = new HashMap<>(MapUtil.emptyIfNull(desired ? latest.getDesired() : latest.getReported())); + state.putAll(values); if (state.size() > MAX_PROPERTY_COUNT) { throw exception(DEVICE_SHADOW_PROPERTY_COUNT_EXCEEDED); } // 2.2 记录属性的更新时间 long now = DateUtil.currentSeconds(); - Map metadata = mergeMap(latest.getMetadata(), null); + Map metadata = new HashMap<>(MapUtil.emptyIfNull(latest.getMetadata())); if (desired) { IotDeviceShadowProtocolUtils.putDesiredTimestamps(metadata, values.keySet(), now); } else { @@ -338,6 +356,9 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { }); } + /** + * 回复全量影子:设备 get 请求的回复,或 desired 更新后的主动推送(requestId 为空) + */ private void sendShadowGetReply(IotDeviceDO device, String serverId, String requestId, IotDeviceShadowDO shadow) { sendShadowReply(device, serverId, requestId, SUCCESS.getCode(), SUCCESS.getMsg(), IotDeviceShadowProtocolUtils.buildGetData(shadow.getDesired(), shadow.getReported(), @@ -381,12 +402,4 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { } } - private static Map mergeMap(Map oldMap, Map newMap) { - Map result = oldMap != null ? new HashMap<>(oldMap) : new HashMap<>(); - if (CollUtil.isNotEmpty(newMap)) { - result.putAll(newMap); - } - return result; - } - } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceShadowProtocolUtils.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceShadowProtocolUtils.java index 9e9e9ad..fea3fe7 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceShadowProtocolUtils.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceShadowProtocolUtils.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.core.util; import cn.hutool.core.map.MapUtil; +import lombok.experimental.UtilityClass; import java.util.HashMap; import java.util.Map; @@ -13,11 +14,9 @@ import java.util.Set; * * @author 芋道源码 */ +@UtilityClass public final class IotDeviceShadowProtocolUtils { - private IotDeviceShadowProtocolUtils() { - } - // ========== 内层 method(上行 params.method) ========== public static final String INNER_METHOD_GET = "get";