Commit 6e9b58cbcd4442af5b0a8343d6ead3e86788ddce

Authored by haohao
1 parent 666afcc3

refactor(iot): 影子透传按 serializeType 序列化,reply 对齐 CommonResult

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/shadow/IotDeviceShadowServiceImpl.java
@@ -97,7 +97,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -97,7 +97,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
97 97
98 private void handleDeviceShadowUpdate0(IotDeviceDO device, IotDeviceMessage message) { 98 private void handleDeviceShadowUpdate0(IotDeviceDO device, IotDeviceMessage message) {
99 // 1. 解析请求(影子 body 当前按 JSON 建模,非 JSON 设备在 gateway 透传层已拒绝) 99 // 1. 解析请求(影子 body 当前按 JSON 建模,非 JSON 设备在 gateway 透传层已拒绝)
100 - IotDeviceShadowUpdateReqDTO req = JsonUtils.convertObject(message.getParams(), IotDeviceShadowUpdateReqDTO.class); 100 + IotDeviceShadowUpdateReqDTO req = JsonUtils.convertObject(message.getParams(),
  101 + IotDeviceShadowUpdateReqDTO.class);
101 if (req == null) { 102 if (req == null) {
102 sendShadowReplySafely(device, message.getServerId(), IotDeviceShadowReplyDTO.errorOf( 103 sendShadowReplySafely(device, message.getServerId(), IotDeviceShadowReplyDTO.errorOf(
103 extractInnerMethod(message.getParams()), 0L, 104 extractInnerMethod(message.getParams()), 0L,
@@ -165,7 +166,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -165,7 +166,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
165 if (req.getVersion() != null && req.getVersion() == -1L) { 166 if (req.getVersion() != null && req.getVersion() == -1L) {
166 try { 167 try {
167 resetShadowWithRetry(deviceId); 168 resetShadowWithRetry(deviceId);
168 - sendShadowReplySafely(device, serverId, IotDeviceShadowReplyDTO.updateReplyOf(0L, DateUtil.currentSeconds())); 169 + sendShadowReplySafely(device, serverId,
  170 + IotDeviceShadowReplyDTO.updateReplyOf(0L, DateUtil.currentSeconds()));
169 } catch (ShadowCasExhaustedException ex) { 171 } catch (ShadowCasExhaustedException ex) {
170 handleCasExhaustedForDevice(device, serverId, ex); 172 handleCasExhaustedForDevice(device, serverId, ex);
171 } 173 }
@@ -266,10 +268,12 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -266,10 +268,12 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
266 } 268 }
267 Map<String, Object> mergedReported = mergeMap(latest.getReported(), req.getState().getReported()); 269 Map<String, Object> mergedReported = mergeMap(latest.getReported(), req.getState().getReported());
268 if (mergedReported.size() > MAX_PROPERTY_COUNT) { 270 if (mergedReported.size() > MAX_PROPERTY_COUNT) {
269 - throw new ShadowUpdateRejectedException(latest.getVersion(), IotDeviceShadowErrorCodeEnum.PROPERTY_COUNT_EXCEEDED); 271 + throw new ShadowUpdateRejectedException(latest.getVersion(),
  272 + IotDeviceShadowErrorCodeEnum.PROPERTY_COUNT_EXCEEDED);
270 } 273 }
271 long now = DateUtil.currentSeconds(); 274 long now = DateUtil.currentSeconds();
272 - Map<String, Object> metadata = latest.getMetadata() != null ? new HashMap<>(latest.getMetadata()) : new HashMap<>(); 275 + Map<String, Object> metadata = latest.getMetadata() != null ? new HashMap<>(latest.getMetadata())
  276 + : new HashMap<>();
273 IotDeviceShadowProtocolUtils.putReportedTimestamps(metadata, req.getState().getReported().keySet(), now); 277 IotDeviceShadowProtocolUtils.putReportedTimestamps(metadata, req.getState().getReported().keySet(), now);
274 return IotDeviceShadowDO.builder() 278 return IotDeviceShadowDO.builder()
275 .reported(mergedReported) 279 .reported(mergedReported)
@@ -282,7 +286,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -282,7 +286,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
282 private IotDeviceShadowDO buildDesiredUpdate(IotDeviceShadowDO latest, Map<String, Object> desired) { 286 private IotDeviceShadowDO buildDesiredUpdate(IotDeviceShadowDO latest, Map<String, Object> desired) {
283 long now = DateUtil.currentSeconds(); 287 long now = DateUtil.currentSeconds();
284 Map<String, Object> mergedDesired = mergeMap(latest.getDesired(), desired); 288 Map<String, Object> mergedDesired = mergeMap(latest.getDesired(), desired);
285 - Map<String, Object> metadata = latest.getMetadata() != null ? new HashMap<>(latest.getMetadata()) : new HashMap<>(); 289 + Map<String, Object> metadata = latest.getMetadata() != null ? new HashMap<>(latest.getMetadata())
  290 + : new HashMap<>();
286 IotDeviceShadowProtocolUtils.putDesiredTimestamps(metadata, desired.keySet(), now); 291 IotDeviceShadowProtocolUtils.putDesiredTimestamps(metadata, desired.keySet(), now);
287 return IotDeviceShadowDO.builder() 292 return IotDeviceShadowDO.builder()
288 .desired(mergedDesired) 293 .desired(mergedDesired)
@@ -307,7 +312,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService { @@ -307,7 +312,8 @@ public class IotDeviceShadowServiceImpl implements IotDeviceShadowService {
307 * 312 *
308 * @return 写入成功后的 update DO(回填未更新字段,便于构建全量回复) 313 * @return 写入成功后的 update DO(回填未更新字段,便于构建全量回复)
309 */ 314 */
310 - private IotDeviceShadowDO casUpdateShadow(Long deviceId, Function<IotDeviceShadowDO, IotDeviceShadowDO> updateBuilder) { 315 + private IotDeviceShadowDO casUpdateShadow(Long deviceId,
  316 + Function<IotDeviceShadowDO, IotDeviceShadowDO> updateBuilder) {
311 Long lastKnownVersion = 0L; 317 Long lastKnownVersion = 0L;
312 for (int i = 0; i < OPTIMISTIC_LOCK_RETRY; i++) { 318 for (int i = 0; i < OPTIMISTIC_LOCK_RETRY; i++) {
313 // 首次尝试走缓存,重试时直读 DB 获取最新 version 319 // 首次尝试走缓存,重试时直读 DB 获取最新 version
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/device/shadow/IotDeviceShadowServiceImplTest.java
@@ -339,16 +339,17 @@ public class IotDeviceShadowServiceImplTest extends BaseMockitoUnitTest { @@ -339,16 +339,17 @@ public class IotDeviceShadowServiceImplTest extends BaseMockitoUnitTest {
339 // 前两次 select 返回 v=1 的快照,之后返回 v=2 的最新快照 339 // 前两次 select 返回 v=1 的快照,之后返回 v=2 的最新快照
340 return selectCount.incrementAndGet() <= 2 ? staleShadow : latestShadow; 340 return selectCount.incrementAndGet() <= 2 ? staleShadow : latestShadow;
341 }); 341 });
342 - when(deviceShadowMapper.updateByDeviceIdAndVersion(any(), eq(device.getId()), anyLong())).thenAnswer(invocation -> {  
343 - Long expectVersion = invocation.getArgument(2);  
344 - if (expectVersion == 1L) {  
345 - return 0;  
346 - }  
347 - if (expectVersion == 2L) {  
348 - return 1;  
349 - }  
350 - return 0;  
351 - }); 342 + when(deviceShadowMapper.updateByDeviceIdAndVersion(any(), eq(device.getId()), anyLong()))
  343 + .thenAnswer(invocation -> {
  344 + Long expectVersion = invocation.getArgument(2);
  345 + if (expectVersion == 1L) {
  346 + return 0;
  347 + }
  348 + if (expectVersion == 2L) {
  349 + return 1;
  350 + }
  351 + return 0;
  352 + });
352 353
353 Map<String, Object> reported = new HashMap<>(); 354 Map<String, Object> reported = new HashMap<>();
354 reported.put("Temperature", 25); 355 reported.put("Temperature", 25);
@@ -364,7 +365,8 @@ public class IotDeviceShadowServiceImplTest extends BaseMockitoUnitTest { @@ -364,7 +365,8 @@ public class IotDeviceShadowServiceImplTest extends BaseMockitoUnitTest {
364 service.handleDeviceShadowUpdate(device, message); 365 service.handleDeviceShadowUpdate(device, message);
365 366
366 ArgumentCaptor<IotDeviceShadowDO> updateCaptor = ArgumentCaptor.forClass(IotDeviceShadowDO.class); 367 ArgumentCaptor<IotDeviceShadowDO> updateCaptor = ArgumentCaptor.forClass(IotDeviceShadowDO.class);
367 - verify(deviceShadowMapper, atLeastOnce()).updateByDeviceIdAndVersion(updateCaptor.capture(), eq(device.getId()), any()); 368 + verify(deviceShadowMapper, atLeastOnce()).updateByDeviceIdAndVersion(updateCaptor.capture(), eq(device.getId()),
  369 + any());
368 IotDeviceShadowDO finalUpdate = updateCaptor.getAllValues().get(updateCaptor.getAllValues().size() - 1); 370 IotDeviceShadowDO finalUpdate = updateCaptor.getAllValues().get(updateCaptor.getAllValues().size() - 1);
369 // 重试时应基于 v=2 的最新 reported 合并,保留 Humidity=50 371 // 重试时应基于 v=2 的最新 reported 合并,保留 Humidity=50
370 assertEquals(25, finalUpdate.getReported().get("Temperature")); 372 assertEquals(25, finalUpdate.getReported().get("Temperature"));
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/shadow/IotDeviceShadowErrorCodeEnum.java
@@ -6,8 +6,10 @@ import lombok.Getter; @@ -6,8 +6,10 @@ import lombok.Getter;
6 /** 6 /**
7 * IoT 设备影子设备侧错误码枚举 7 * IoT 设备影子设备侧错误码枚举
8 * <p> 8 * <p>
9 - * 设备侧 wire code,仅用于回填 {@code update_reply.code/msg},不走 {@link cn.iocoder.yudao.framework.common.pojo.CommonResult}。  
10 - * 服务端内部异常请使用 {@link cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants}(如 {@code DEVICE_SHADOW_NOT_ENABLED})。 9 + * 设备侧 wire code,仅用于回填 {@code update_reply.code/msg},不走
  10 + * {@link cn.iocoder.yudao.framework.common.pojo.CommonResult}。
  11 + * 服务端内部异常请使用 {@link cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants}(如
  12 + * {@code DEVICE_SHADOW_NOT_ENABLED})。
11 * 13 *
12 * @author 芋道源码 14 * @author 芋道源码
13 */ 15 */
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/shadow/IotDeviceShadowReplyDTO.java
@@ -14,8 +14,10 @@ import static cn.iocoder.yudao.module.iot.core.util.IotDeviceShadowProtocolUtils @@ -14,8 +14,10 @@ import static cn.iocoder.yudao.module.iot.core.util.IotDeviceShadowProtocolUtils
14 /** 14 /**
15 * IoT 设备影子下行 Reply DTO 15 * IoT 设备影子下行 Reply DTO
16 * <p> 16 * <p>
17 - * 扁平对齐 CommonResult + method,用于 {@link IotDeviceMessageMethodEnum#SHADOW_GET} 透传线 body。  
18 - * 错误回复的 {@link #data} 仍携带 version 供设备 resync,有意偏离纯 CommonResult(error 时 data 通常为 null)。 17 + * 扁平对齐 CommonResult + method,用于 {@link IotDeviceMessageMethodEnum#SHADOW_GET}
  18 + * 透传线 body。
  19 + * 错误回复的 {@link #data} 仍携带 version 供设备 resync,有意偏离纯 CommonResult(error 时 data
  20 + * 通常为 null)。
19 * 21 *
20 * @author 芋道源码 22 * @author 芋道源码
21 */ 23 */
@@ -40,12 +42,13 @@ public class IotDeviceShadowReplyDTO { @@ -40,12 +42,13 @@ public class IotDeviceShadowReplyDTO {
40 private String msg; 42 private String msg;
41 43
42 /** 44 /**
43 - * 业务数据:get_reply 为 {state, version, timestamp};update_reply 为 {version, timestamp} 45 + * 业务数据:get_reply 为 {state, version, timestamp};update_reply 为 {version,
  46 + * timestamp}
44 */ 47 */
45 private Object data; 48 private Object data;
46 49
47 public static IotDeviceShadowReplyDTO getReplyOf(Map<String, Object> desired, Map<String, Object> reported, 50 public static IotDeviceShadowReplyDTO getReplyOf(Map<String, Object> desired, Map<String, Object> reported,
48 - Long version, Long timestamp) { 51 + Long version, Long timestamp) {
49 Map<String, Object> state = new HashMap<>(); 52 Map<String, Object> state = new HashMap<>();
50 state.put(STATE_DESIRED, desired != null ? desired : new HashMap<>()); 53 state.put(STATE_DESIRED, desired != null ? desired : new HashMap<>());
51 state.put(STATE_REPORTED, reported != null ? reported : new HashMap<>()); 54 state.put(STATE_REPORTED, reported != null ? reported : new HashMap<>());
@@ -57,11 +60,12 @@ public class IotDeviceShadowReplyDTO { @@ -57,11 +60,12 @@ public class IotDeviceShadowReplyDTO {
57 } 60 }
58 61
59 public static IotDeviceShadowReplyDTO updateReplyOf(Long version, Long timestamp) { 62 public static IotDeviceShadowReplyDTO updateReplyOf(Long version, Long timestamp) {
60 - return of(INNER_METHOD_UPDATE_REPLY, SUCCESS.getCode(), SUCCESS.getMsg(), dataWithVersionTimestamp(version, timestamp)); 63 + return of(INNER_METHOD_UPDATE_REPLY, SUCCESS.getCode(), SUCCESS.getMsg(),
  64 + dataWithVersionTimestamp(version, timestamp));
61 } 65 }
62 66
63 public static IotDeviceShadowReplyDTO errorOf(String innerMethod, Long version, Integer errorCode, 67 public static IotDeviceShadowReplyDTO errorOf(String innerMethod, Long version, Integer errorCode,
64 - String errorMsg, Long timestamp) { 68 + String errorMsg, Long timestamp) {
65 return of(toReplyMethod(innerMethod), errorCode, errorMsg, dataWithVersionTimestamp(version, timestamp)); 69 return of(toReplyMethod(innerMethod), errorCode, errorMsg, dataWithVersionTimestamp(version, timestamp));
66 } 70 }
67 71
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java
@@ -43,7 +43,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -43,7 +43,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
43 43
44 @Override 44 @Override
45 public byte[] serializeDeviceMessage(IotDeviceMessage message, 45 public byte[] serializeDeviceMessage(IotDeviceMessage message,
46 - String productKey, String deviceName) { 46 + String productKey, String deviceName) {
47 if (IotDeviceMessageMethodEnum.isPassthrough(message.getMethod())) { 47 if (IotDeviceMessageMethodEnum.isPassthrough(message.getMethod())) {
48 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); 48 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
49 if (device == null) { 49 if (device == null) {
@@ -67,7 +67,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -67,7 +67,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
67 67
68 @Override 68 @Override
69 public byte[] serializeDeviceMessage(IotDeviceMessage message, 69 public byte[] serializeDeviceMessage(IotDeviceMessage message,
70 - IotSerializeTypeEnum serializeType) { 70 + IotSerializeTypeEnum serializeType) {
71 if (IotDeviceMessageMethodEnum.isPassthrough(message.getMethod())) { 71 if (IotDeviceMessageMethodEnum.isPassthrough(message.getMethod())) {
72 return encodePassthrough(message, serializeType, null, null); 72 return encodePassthrough(message, serializeType, null, null);
73 } 73 }
@@ -83,7 +83,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -83,7 +83,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
83 83
84 @Override 84 @Override
85 public IotDeviceMessage deserializeDeviceMessage(byte[] bytes, 85 public IotDeviceMessage deserializeDeviceMessage(byte[] bytes,
86 - String productKey, String deviceName) { 86 + String productKey, String deviceName) {
87 // 1.1 获取设备信息 87 // 1.1 获取设备信息
88 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); 88 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
89 if (device == null) { 89 if (device == null) {
@@ -111,7 +111,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -111,7 +111,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
111 111
112 @Override 112 @Override
113 public IotDeviceMessage deserializePassthroughMessage(byte[] payload, String method, 113 public IotDeviceMessage deserializePassthroughMessage(byte[] payload, String method,
114 - String productKey, String deviceName) { 114 + String productKey, String deviceName) {
115 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); 115 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
116 if (device == null) { 116 if (device == null) {
117 throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); 117 throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);
@@ -128,7 +128,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -128,7 +128,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
128 128
129 @Override 129 @Override
130 public void sendDeviceMessage(IotDeviceMessage message, 130 public void sendDeviceMessage(IotDeviceMessage message,
131 - String productKey, String deviceName, String serverId) { 131 + String productKey, String deviceName, String serverId) {
132 // 1. 获取设备信息 132 // 1. 获取设备信息
133 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); 133 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
134 if (device == null) { 134 if (device == null) {
@@ -148,7 +148,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -148,7 +148,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
148 * @param serverId 设备连接的 serverId 148 * @param serverId 设备连接的 serverId
149 */ 149 */
150 private void appendDeviceMessage(IotDeviceMessage message, 150 private void appendDeviceMessage(IotDeviceMessage message,
151 - IotDeviceRespDTO device, String serverId) { 151 + IotDeviceRespDTO device, String serverId) {
152 message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) 152 message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())
153 .setDeviceId(device.getId()).setTenantId(device.getTenantId()).setServerId(serverId); 153 .setDeviceId(device.getId()).setTenantId(device.getTenantId()).setServerId(serverId);
154 // 特殊:如果设备没有指定 requestId,则使用 messageId 154 // 特殊:如果设备没有指定 requestId,则使用 messageId
@@ -161,7 +161,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -161,7 +161,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
161 * 影子透传编码;不支持的 serializeType 返回 null,由下行发布方跳过发送 161 * 影子透传编码;不支持的 serializeType 返回 null,由下行发布方跳过发送
162 */ 162 */
163 private byte[] encodePassthrough(IotDeviceMessage message, IotSerializeTypeEnum serializeType, 163 private byte[] encodePassthrough(IotDeviceMessage message, IotSerializeTypeEnum serializeType,
164 - String productKey, String deviceName) { 164 + String productKey, String deviceName) {
165 return passthroughRaw("encodePassthrough", serializeType, productKey, deviceName, false, 165 return passthroughRaw("encodePassthrough", serializeType, productKey, deviceName, false,
166 serializer -> serializer.encodeRaw(message.getParams())); 166 serializer -> serializer.encodeRaw(message.getParams()));
167 } 167 }
@@ -170,14 +170,14 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -170,14 +170,14 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
170 * 影子透传解码;解析失败或不支持的 serializeType 返回 null 170 * 影子透传解码;解析失败或不支持的 serializeType 返回 null
171 */ 171 */
172 private Map<String, Object> decodePassthrough(byte[] bytes, IotSerializeTypeEnum serializeType, 172 private Map<String, Object> decodePassthrough(byte[] bytes, IotSerializeTypeEnum serializeType,
173 - String productKey, String deviceName) { 173 + String productKey, String deviceName) {
174 return passthroughRaw("decodePassthrough", serializeType, productKey, deviceName, true, 174 return passthroughRaw("decodePassthrough", serializeType, productKey, deviceName, true,
175 serializer -> serializer.decodeRaw(bytes)); 175 serializer -> serializer.decodeRaw(bytes));
176 } 176 }
177 177
178 private <T> T passthroughRaw(String op, IotSerializeTypeEnum serializeType, 178 private <T> T passthroughRaw(String op, IotSerializeTypeEnum serializeType,
179 - String productKey, String deviceName, boolean decode,  
180 - Function<IotMessageSerializer, T> action) { 179 + String productKey, String deviceName, boolean decode,
  180 + Function<IotMessageSerializer, T> action) {
181 IotMessageSerializer serializer = getPassthroughSerializer(serializeType); 181 IotMessageSerializer serializer = getPassthroughSerializer(serializeType);
182 if (serializer == null) { 182 if (serializer == null) {
183 warnPassthroughUnsupported(op, serializeType, productKey, deviceName); 183 warnPassthroughUnsupported(op, serializeType, productKey, deviceName);
@@ -204,7 +204,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -204,7 +204,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
204 } 204 }
205 205
206 private void warnPassthroughUnsupported(String op, IotSerializeTypeEnum serializeType, 206 private void warnPassthroughUnsupported(String op, IotSerializeTypeEnum serializeType,
207 - String productKey, String deviceName) { 207 + String productKey, String deviceName) {
208 if (productKey != null && deviceName != null) { 208 if (productKey != null && deviceName != null) {
209 log.warn("[{}][影子透传跳过:设备({}/{}) serializeType={} 不支持,目前仅 JSON 协议设备可用]", 209 log.warn("[{}][影子透传跳过:设备({}/{}) serializeType={} 不支持,目前仅 JSON 协议设备可用]",
210 op, productKey, deviceName, serializeType); 210 op, productKey, deviceName, serializeType);