From 84f681e9d950fa36069fa53086c3d77c4ebd46e5 Mon Sep 17 00:00:00 2001 From: zhugongping Date: Fri, 31 Jul 2026 15:08:35 +0800 Subject: [PATCH] =?UTF-8?q?20260731:=E5=B9=B3=E5=8F=B0=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=8CJSON=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/dcp/DcpPlatformAdapter.java | 375 ++++++++---------- .../domain/PlatformDeviceMapping.java | 40 ++ .../domain/PlatformDeviceSubMapping.java | 22 + .../iot/platform/domain/PlatformFlowStep.java | 44 ++ .../iot/platform/domain/PlatformInstance.java | 7 + .../platform/domain/PlatformInterface.java | 41 ++ .../iot/platform/domain/PlatformUserAuth.java | 39 ++ .../platform/flow/PlatformFlowExecutor.java | 358 ++++++++++------- .../model/json/ExtraMappingConfig.java | 62 +++ .../platform/model/json/FieldMappingItem.java | 22 + .../platform/model/json/OnboardConfig.java | 25 ++ .../model/json/ParamOverrideConfig.java | 34 ++ .../model/json/RequestSchemaField.java | 44 ++ .../model/json/ResponseSchemaField.java | 19 + .../platform/model/json/ResultSaveField.java | 19 + 15 files changed, 798 insertions(+), 353 deletions(-) create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ExtraMappingConfig.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/FieldMappingItem.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/OnboardConfig.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ParamOverrideConfig.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/RequestSchemaField.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResponseSchemaField.java create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResultSaveField.java diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java index 2d03498d..45c97e4f 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java @@ -15,6 +15,7 @@ import com.fastbee.iot.domain.Device; import com.fastbee.iot.model.ThingsModels.ValueItem; import com.fastbee.iot.platform.domain.*; import com.fastbee.iot.platform.flow.FlowContext; +import com.fastbee.iot.platform.model.json.*; import com.fastbee.iot.platform.flow.PlatformFlowExecutor; import com.fastbee.iot.platform.mapper.PlatformFlowMapper; import com.fastbee.iot.platform.mapper.PlatformInterfaceMapper; @@ -94,8 +95,9 @@ public class DcpPlatformAdapter { return; } String sensorType = mapping.getSensorType(); - PlatformInterface pushInterface = loadPushInterface(mapping); - JSONArray requestSchema = loadRequestSchema(pushInterface); + PlatformInterface pushInterface = interfaceMapper.selectById(mapping.getPushInterfaceId()); + + List requestSchema = pushInterface.getRequestSchemaParsed(); String interfaceCode = resolveInterfaceCode(sensorType, pushInterface); try { @@ -127,8 +129,8 @@ public class DcpPlatformAdapter { return; } String sensorType = mapping.getSensorType(); - PlatformInterface pushInterface = loadPushInterface(mapping); - JSONArray requestSchema = loadRequestSchema(pushInterface); + PlatformInterface pushInterface = interfaceMapper.selectById(mapping.getPushInterfaceId()); + List requestSchema = pushInterface.getRequestSchemaParsed(); String interfaceCode = resolveInterfaceCode(sensorType, pushInterface); try { @@ -151,38 +153,33 @@ public class DcpPlatformAdapter { return; } - JSONArray fieldMappings = parseJsonArray(mapping.getFieldMapping()); + List fieldMappings = mapping.getFieldMappingParsed(); String baseUrl = platform.getBaseUrl() + pushPath; // 解析 extra_mapping 中的 itemFieldMappings 和 reportIntervalSec int reportIntervalSec = 300; - JSONArray itemFieldMappings = null; + List itemFieldMappings = null; String fieldName = resolveArrayFieldName(requestSchema); - JSONObject extra = null; - if (StringUtils.hasText(mapping.getExtraMapping())) { - extra = JSON.parseObject(mapping.getExtraMapping()); - reportIntervalSec = extra.getIntValue("reportIntervalSec", 300); - itemFieldMappings = extra.getJSONArray("itemFieldMappings"); - if (StringUtils.hasText(extra.getString("fieldName"))) { - fieldName = extra.getString("fieldName"); + ExtraMappingConfig extra = mapping.getExtraMappingParsed(); + if (extra != null) { + reportIntervalSec = extra.getReportIntervalSec() != null ? extra.getReportIntervalSec() : 300; + itemFieldMappings = extra.getItemFieldMappings(); + if (StringUtils.hasText(extra.getFieldName())) { + fieldName = extra.getFieldName(); } } - JSONObject arrayFieldSchema = findArrayFieldSchema(requestSchema, fieldName); + RequestSchemaField arrayFieldSchema = findArrayFieldSchema(requestSchema, fieldName); String arrayMode = resolveArrayMode(arrayFieldSchema, extra); // 从 onboard_config 取 sensorId String sensorId = reportSerialNumber; - if (StringUtils.hasText(subMapping.getOnboardConfig())) { - JSONObject config = JSON.parseObject(subMapping.getOnboardConfig()); - String cfgSensorId = config.getString("sensorId"); - if (StringUtils.hasText(cfgSensorId)) { - sensorId = cfgSensorId; - } + OnboardConfig onboardCfg = subMapping.getOnboardConfigParsed(); + if (onboardCfg != null && StringUtils.hasText(onboardCfg.getSensorId())) { + sensorId = onboardCfg.getSensorId(); } // 构建当前设备的数据项 - JSONObject currentItem = buildArrayItem( - platform, mapping, requestSchema, reportData, fieldName, itemFieldMappings, sensorId); + JSONObject currentItem = buildArrayItem(userAuth, mapping, requestSchema, reportData, fieldName, itemFieldMappings, sensorId); if (currentItem == null || currentItem.isEmpty()) { log.warn("[DCP] 网关子设备 {} 数据映射为空,跳过推送", reportSerialNumber); return; @@ -261,33 +258,32 @@ public class DcpPlatformAdapter { /** * 请求 schema 是否包含数组字段 */ - private boolean hasArrayField(JSONArray requestSchema) { + private boolean hasArrayField(List requestSchema) { if (requestSchema == null || requestSchema.isEmpty()) { return false; } - for (int i = 0; i < requestSchema.size(); i++) { - JSONObject field = requestSchema.getJSONObject(i); - if ("array".equalsIgnoreCase(field.getString("type"))) { + for (RequestSchemaField field : requestSchema) { + if ("array".equalsIgnoreCase(field.getType())) { return true; } } return false; } - private String resolveArrayFieldName(JSONArray requestSchema) { - JSONObject firstArrayField = findFirstArrayFieldSchema(requestSchema); - if (firstArrayField != null && StringUtils.hasText(firstArrayField.getString("name"))) { - return firstArrayField.getString("name"); + private String resolveArrayFieldName(List requestSchema) { + RequestSchemaField firstArrayField = findFirstArrayFieldSchema(requestSchema); + if (firstArrayField != null && StringUtils.hasText(firstArrayField.getName())) { + return firstArrayField.getName(); } return "sensorData"; } - private String resolveArrayMode(JSONObject arrayFieldSchema, JSONObject extra) { + private String resolveArrayMode(RequestSchemaField arrayFieldSchema, ExtraMappingConfig extra) { String mode = null; - if (extra != null && StringUtils.hasText(extra.getString("arrayMode"))) { - mode = extra.getString("arrayMode"); - } else if (arrayFieldSchema != null && StringUtils.hasText(arrayFieldSchema.getString("arrayMode"))) { - mode = arrayFieldSchema.getString("arrayMode"); + if (extra != null && StringUtils.hasText(extra.getArrayMode())) { + mode = extra.getArrayMode(); + } else if (arrayFieldSchema != null && StringUtils.hasText(arrayFieldSchema.getArrayMode())) { + mode = arrayFieldSchema.getArrayMode(); } if (!StringUtils.hasText(mode)) { return ARRAY_MODE_REFERENCE_PAIR; @@ -310,14 +306,9 @@ public class DcpPlatformAdapter { } // 兼容历史配置:从 onboard_config 取基准点原始序列号 - if (StringUtils.hasText(subMapping.getOnboardConfig())) { - try { - JSONObject config = JSON.parseObject(subMapping.getOnboardConfig()); - String refSensorId = config.getString("refSerialNumber"); - if (StringUtils.hasText(refSensorId)) { - return refSensorId; - } - } catch (Exception ignored) {} + OnboardConfig config = subMapping.getOnboardConfigParsed(); + if (config != null && StringUtils.hasText(config.getRefSerialNumber())) { + return config.getRefSerialNumber(); } // 回退:用网关本身的序列号 return mapping.getSerialNumber(); @@ -385,7 +376,7 @@ public class DcpPlatformAdapter { private void handleNormalPush(PlatformInstance platform, PlatformUserAuth userAuth, PlatformDeviceMapping mapping, String reportSerialNumber, String payload, String pushPath, - String interfaceCode, JSONArray requestSchema) { + String interfaceCode, List requestSchema) { Object body = buildPushBody(platform, userAuth, mapping, payload, requestSchema); if (body == null) { log.warn("[DCP] 设备 {} 字段映射结果为空,跳过推送", mapping.getSerialNumber()); @@ -397,7 +388,7 @@ public class DcpPlatformAdapter { } private Object buildPushBody(PlatformInstance platform, PlatformUserAuth userAuth,PlatformDeviceMapping mapping, - String payload, JSONArray requestSchema) { + String payload, List requestSchema) { if (!StringUtils.hasText(payload)) { return null; } @@ -406,7 +397,7 @@ public class DcpPlatformAdapter { return null; } - JSONArray fieldMappings = parseJsonArray(mapping.getFieldMapping()); + List fieldMappings = mapping.getFieldMappingParsed(); if (requestSchema == null || requestSchema.isEmpty()) { return buildLegacyPushBody(mapping.getPlatformDeviceId(), fieldMappings, reportData); } @@ -421,7 +412,7 @@ public class DcpPlatformAdapter { return arr; } - private Object buildLegacyPushBody(String platformDeviceId, JSONArray fieldMappings, JSONObject reportData) { + private Object buildLegacyPushBody(String platformDeviceId, List fieldMappings, JSONObject reportData) { if (fieldMappings == null || fieldMappings.isEmpty()) { return null; } @@ -430,10 +421,9 @@ public class DcpPlatformAdapter { dcpData.put("deviceId", platformDeviceId); dcpData.put("epoch", nowIsoTimestamp()); - for (int i = 0; i < fieldMappings.size(); i++) { - JSONObject fm = fieldMappings.getJSONObject(i); - String identifier = fm.getString("identifier"); - String platformField = fm.getString("platformField"); + for (FieldMappingItem fm : fieldMappings) { + String identifier = fm.getIdentifier(); + String platformField = fm.getPlatformField(); if (!StringUtils.hasText(identifier) || !StringUtils.hasText(platformField)) { continue; } @@ -459,7 +449,7 @@ public class DcpPlatformAdapter { */ private void handleSubDeviceSimplePush(PlatformInstance platform, PlatformUserAuth userAuth, PlatformDeviceMapping mapping, PlatformDeviceSubMapping subMapping, String reportSerialNumber, String payload, - String pushPath, String interfaceCode, JSONArray requestSchema) { + String pushPath, String interfaceCode, List requestSchema) { Object body = buildSubDevicePushBody(platform, userAuth, mapping, subMapping, payload, requestSchema); if (body == null) { log.warn("[DCP] 网关子设备 {} 字段映射结果为空,跳过推送", reportSerialNumber); @@ -477,7 +467,7 @@ public class DcpPlatformAdapter { */ private Object buildSubDevicePushBody(PlatformInstance platform, PlatformUserAuth userAuth, PlatformDeviceMapping mapping, PlatformDeviceSubMapping subMapping, String payload, - JSONArray requestSchema) { + List requestSchema) { JSONObject reportData = parsePayloadToMap(payload); if (reportData == null || reportData.isEmpty()) { return null; @@ -487,7 +477,7 @@ public class DcpPlatformAdapter { String platformDeviceId = StringUtils.hasText(subMapping.getPlatformDeviceId()) ? subMapping.getPlatformDeviceId() : mapping.getPlatformDeviceId(); - JSONArray fieldMappings = parseJsonArray(mapping.getFieldMapping()); + List fieldMappings = mapping.getFieldMappingParsed(); if (requestSchema == null || requestSchema.isEmpty()) { return buildLegacyPushBody(platformDeviceId, fieldMappings, reportData); } @@ -528,18 +518,17 @@ public class DcpPlatformAdapter { } private JSONObject buildObjectBySchema(PlatformInstance platform, PlatformUserAuth userAuth, PlatformDeviceMapping mapping, - JSONArray requestSchema, JSONObject reportData, - JSONArray fieldMappings) { + List requestSchema, JSONObject reportData, + List fieldMappings) { JSONObject body = new JSONObject(); if (requestSchema == null || requestSchema.isEmpty()) { return body; } - for (int i = 0; i < requestSchema.size(); i++) { - JSONObject schemaField = requestSchema.getJSONObject(i); - if ("array".equalsIgnoreCase(schemaField.getString("type"))) { + for (RequestSchemaField schemaField : requestSchema) { + if ("array".equalsIgnoreCase(schemaField.getType())) { continue; } - String fieldName = schemaField.getString("name"); + String fieldName = schemaField.getName(); if (!StringUtils.hasText(fieldName)) { continue; } @@ -552,15 +541,15 @@ public class DcpPlatformAdapter { } private Object resolveSchemaFieldValue(PlatformInstance platform, PlatformUserAuth userAuth, PlatformDeviceMapping mapping, - JSONObject schemaField, JSONObject reportData, - JSONArray fieldMappings) { - String source = schemaField.getString("source"); - String fieldName = schemaField.getString("name"); - String configField = schemaField.getString("field"); - String contextKey = schemaField.getString("contextKey"); + RequestSchemaField schemaField, JSONObject reportData, + List fieldMappings) { + String source = schemaField.getSource(); + String fieldName = schemaField.getName(); + String configField = schemaField.getField(); + String contextKey = schemaField.getContextKey(); if ("fixed".equals(source)) { - return schemaField.get("value"); + return schemaField.getValue(); } if ("device".equals(source)) { return resolveDeviceField(mapping, configField); @@ -590,11 +579,11 @@ public class DcpPlatformAdapter { return null; } - private JSONObject buildArrayItem(PlatformInstance platform, PlatformDeviceMapping mapping, - JSONArray requestSchema, JSONObject reportData, String fieldName, - JSONArray itemFieldMappings, String subDeviceId) { - JSONObject arrayFieldSchema = findArrayFieldSchema(requestSchema, fieldName); - JSONArray itemSchema = arrayFieldSchema == null ? null : arrayFieldSchema.getJSONArray("itemSchema"); + private JSONObject buildArrayItem(PlatformUserAuth userAuth, PlatformDeviceMapping mapping, + List requestSchema, JSONObject reportData, String fieldName, + List itemFieldMappings, String subDeviceId) { + RequestSchemaField arrayFieldSchema = findArrayFieldSchema(requestSchema, fieldName); + List itemSchema = arrayFieldSchema == null ? null : arrayFieldSchema.getItemSchema(); if (itemSchema == null || itemSchema.isEmpty()) { return buildLegacyArrayItem(reportData, itemFieldMappings, subDeviceId); } @@ -602,20 +591,18 @@ public class DcpPlatformAdapter { JSONObject item = new JSONObject(); boolean hasThingsModelField = false; boolean hasThingsModelValue = false; - for (int i = 0; i < itemSchema.size(); i++) { - JSONObject schemaField = itemSchema.getJSONObject(i); - String itemFieldName = schemaField.getString("name"); + for (RequestSchemaField schemaField : itemSchema) { + String itemFieldName = schemaField.getName(); if (!StringUtils.hasText(itemFieldName)) { continue; } - if (isThingsModelSource(schemaField.getString("source"))) { + if (isThingsModelSource(schemaField.getSource())) { hasThingsModelField = true; } - Object value = resolveArrayItemFieldValue( - platform, mapping, schemaField, reportData, itemFieldMappings, subDeviceId); + Object value = resolveArrayItemFieldValue(userAuth, mapping, schemaField, reportData, itemFieldMappings, subDeviceId); if (value != null) { item.put(itemFieldName, value); - if (isThingsModelSource(schemaField.getString("source"))) { + if (isThingsModelSource(schemaField.getSource())) { hasThingsModelValue = true; } } @@ -626,51 +613,51 @@ public class DcpPlatformAdapter { return item; } - private JSONObject buildLegacyArrayItem(JSONObject reportData, JSONArray itemFieldMappings, String subDeviceId) { + private JSONObject buildLegacyArrayItem(JSONObject reportData, List itemFieldMappings, String subDeviceId) { Map itemValues = extractItemValues(reportData, itemFieldMappings); Object height = itemValues.get("height"); Object temperature = itemValues.get("temperature"); if (height == null && temperature == null) { return null; } - String heightIdentifier = findIdentifier(itemFieldMappings, "height"); + String heightIdentifier = findIdentifierFromItem(itemFieldMappings, "height"); String epochIdentifier = StringUtils.hasText(heightIdentifier) - ? heightIdentifier : findFirstIdentifier(itemFieldMappings); + ? heightIdentifier : findFirstIdentifierFromItem(itemFieldMappings); Object epoch = resolveEpoch(reportData, epochIdentifier); return buildSensorDataItem(epoch, height, temperature, subDeviceId); } - private Object resolveArrayItemFieldValue(PlatformInstance platform, PlatformDeviceMapping mapping, - JSONObject schemaField, JSONObject reportData, - JSONArray itemFieldMappings, String subDeviceId) { - String source = schemaField.getString("source"); - String fieldName = schemaField.getString("name"); + private Object resolveArrayItemFieldValue(PlatformUserAuth userAuth, PlatformDeviceMapping mapping, + RequestSchemaField schemaField, JSONObject reportData, + List itemFieldMappings, String subDeviceId) { + String source = schemaField.getSource(); + String fieldName = schemaField.getName(); if ("fixed".equals(source)) { - return schemaField.get("value"); + return schemaField.getValue(); } if ("subDeviceId".equals(source)) { return subDeviceId; } if ("ts".equals(source)) { - return resolveTimestampValue(reportData, itemFieldMappings, fieldName, schemaField.getString("contextKey")); + return resolveTimestampValueFromItem(reportData, itemFieldMappings, fieldName, schemaField.getContextKey()); } if ("device".equals(source)) { - return resolveDeviceField(mapping, schemaField.getString("field")); + return resolveDeviceField(mapping, schemaField.getField()); } if ("auth".equals(source)) { - return resolveJsonField(platform.getAuthConfig(), schemaField.getString("field")); + return resolveJsonField(userAuth.getAuthConfig(), schemaField.getField()); } if ("extra".equals(source)) { - return resolveJsonField(platform.getExtraConfig(), schemaField.getString("field")); + return resolveJsonField(userAuth.getExtraConfig(), schemaField.getField()); } if (isThingsModelSource(source)) { - String identifier = findIdentifier(itemFieldMappings, fieldName); + String identifier = findIdentifierFromItem(itemFieldMappings, fieldName); return StringUtils.hasText(identifier) ? reportData.get(identifier) : null; } if ("context".equals(source)) { - String contextKey = schemaField.getString("contextKey"); + String contextKey = schemaField.getContextKey(); if ("reportTime".equals(contextKey) || "epoch".equals(fieldName)) { - return resolveTimestampValue(reportData, itemFieldMappings, fieldName, contextKey); + return resolveTimestampValueFromItem(reportData, itemFieldMappings, fieldName, contextKey); } Object contextValue = reportData.get(contextKey); if (contextValue != null) { @@ -680,33 +667,51 @@ public class DcpPlatformAdapter { return null; } - private JSONObject findArrayFieldSchema(JSONArray requestSchema, String fieldName) { + private RequestSchemaField findArrayFieldSchema(List requestSchema, String fieldName) { if (requestSchema == null || requestSchema.isEmpty() || !StringUtils.hasText(fieldName)) { return null; } - for (int i = 0; i < requestSchema.size(); i++) { - JSONObject schemaField = requestSchema.getJSONObject(i); - if ("array".equalsIgnoreCase(schemaField.getString("type")) - && fieldName.equals(schemaField.getString("name"))) { + for (RequestSchemaField schemaField : requestSchema) { + if ("array".equalsIgnoreCase(schemaField.getType()) + && fieldName.equals(schemaField.getName())) { return schemaField; } } return null; } - private JSONObject findFirstArrayFieldSchema(JSONArray requestSchema) { + private RequestSchemaField findFirstArrayFieldSchema(List requestSchema) { if (requestSchema == null || requestSchema.isEmpty()) { return null; } - for (int i = 0; i < requestSchema.size(); i++) { - JSONObject schemaField = requestSchema.getJSONObject(i); - if ("array".equalsIgnoreCase(schemaField.getString("type"))) { + for (RequestSchemaField schemaField : requestSchema) { + if ("array".equalsIgnoreCase(schemaField.getType())) { return schemaField; } } return null; } + private boolean isThingsModelSource(String source) { + return "thingsModel".equals(source) || "identifier".equals(source); + } + + private boolean hasPayloadDrivenValue(JSONObject body, List requestSchema) { + if (body == null || body.isEmpty() || requestSchema == null || requestSchema.isEmpty()) { + return true; + } + for (RequestSchemaField schemaField : requestSchema) { + if ("array".equalsIgnoreCase(schemaField.getType())) { + continue; + } + if (isThingsModelSource(schemaField.getSource()) || "context".equals(schemaField.getSource()) || "ts".equals(schemaField.getSource()) + && body.containsKey(schemaField.getName())) { + return false; + } + } + return true; + } + private JSONObject copyJsonObject(JSONObject source) { if (source == null || source.isEmpty()) { return new JSONObject(); @@ -714,51 +719,6 @@ public class DcpPlatformAdapter { return JSON.parseObject(source.toJSONString()); } - private boolean hasPayloadDrivenValue(JSONObject body, JSONArray requestSchema) { - if (body == null || body.isEmpty() || requestSchema == null || requestSchema.isEmpty()) { - return true; - } - for (int i = 0; i < requestSchema.size(); i++) { - JSONObject schemaField = requestSchema.getJSONObject(i); - if ("array".equalsIgnoreCase(schemaField.getString("type"))) { - continue; - } - if (usesPayload(schemaField.getString("source")) - && body.containsKey(schemaField.getString("name"))) { - return false; - } - } - return true; - } - - private boolean usesPayload(String source) { - return isThingsModelSource(source) || "context".equals(source) || "ts".equals(source); - } - - private boolean isThingsModelSource(String source) { - return "thingsModel".equals(source) || "identifier".equals(source); - } - - private PlatformInterface loadPushInterface(PlatformDeviceMapping mapping) { - if (mapping.getPushInterfaceId() == null) { - return null; - } - return interfaceMapper.selectById(mapping.getPushInterfaceId()); - } - - private JSONArray loadRequestSchema(PlatformInterface pushInterface) { - if (pushInterface == null || !StringUtils.hasText(pushInterface.getRequestSchema())) { - return null; - } - try { - return JSON.parseArray(pushInterface.getRequestSchema()); - } catch (Exception e) { - log.warn("[DCP] 解析 requestSchema 失败 interfaceId={} err={}", - pushInterface.getId(), e.getMessage()); - return null; - } - } - private String resolvePushPath(PlatformDeviceMapping mapping, PlatformInterface pushInterface) { if (pushInterface != null && StringUtils.hasText(pushInterface.getPath())) { return pushInterface.getPath(); @@ -775,19 +735,7 @@ public class DcpPlatformAdapter { return "dcp_push_" + sensorType; } - private JSONArray parseJsonArray(String json) { - if (!StringUtils.hasText(json)) { - return null; - } - try { - return JSON.parseArray(json); - } catch (Exception e) { - log.warn("[DCP] 解析 JSON 数组失败: {}", e.getMessage()); - return null; - } - } - - private Object resolveThingsModelValue(JSONObject reportData, JSONArray fieldMappings, + private Object resolveThingsModelValue(JSONObject reportData, List fieldMappings, String platformField, String fallbackKey) { String identifier = findIdentifier(fieldMappings, platformField); if (StringUtils.hasText(identifier)) { @@ -799,7 +747,7 @@ public class DcpPlatformAdapter { return null; } - private Object resolveTimestampValue(JSONObject reportData, JSONArray fieldMappings, + private Object resolveTimestampValue(JSONObject reportData, List fieldMappings, String platformField, String fallbackKey) { String identifier = findIdentifier(fieldMappings, platformField); if (StringUtils.hasText(identifier)) { @@ -815,6 +763,22 @@ public class DcpPlatformAdapter { return nowIsoTimestamp(); } + private Object resolveTimestampValueFromItem(JSONObject reportData, List itemFieldMappings, + String platformField, String fallbackKey) { + String identifier = findIdentifierFromItem(itemFieldMappings, platformField); + if (StringUtils.hasText(identifier)) { + return resolveEpoch(reportData, identifier); + } + if (StringUtils.hasText(fallbackKey) && reportData.containsKey(fallbackKey + "__ts")) { + return resolveEpoch(reportData, fallbackKey); + } + String firstIdentifier = findFirstIdentifierFromItem(itemFieldMappings); + if (StringUtils.hasText(firstIdentifier)) { + return resolveEpoch(reportData, firstIdentifier); + } + return nowIsoTimestamp(); + } + private Object resolveDeviceField(PlatformDeviceMapping mapping, String field) { if (mapping == null || !StringUtils.hasText(field)) { return null; @@ -863,41 +827,61 @@ public class DcpPlatformAdapter { return item; } - private Map extractItemValues(JSONObject reportData, JSONArray itemFieldMappings) { + private Map extractItemValues(JSONObject reportData, + List itemFieldMappings) { Map values = new HashMap<>(); if (itemFieldMappings == null) return values; - for (int i = 0; i < itemFieldMappings.size(); i++) { - JSONObject im = itemFieldMappings.getJSONObject(i); - String platformField = im.getString("platformField"); - String identifier = im.getString("identifier"); - if (StringUtils.hasText(identifier) && StringUtils.hasText(platformField)) { - values.put(platformField, reportData.get(identifier)); + for (ExtraMappingConfig.ItemFieldMapping im : itemFieldMappings) { + if (StringUtils.hasText(im.getIdentifier()) && StringUtils.hasText(im.getPlatformField())) { + values.put(im.getPlatformField(), reportData.get(im.getIdentifier())); } } return values; } - private String findIdentifier(JSONArray itemFieldMappings, String platformField) { + private String findIdentifier(List itemFieldMappings, String platformField) { if (itemFieldMappings == null || !StringUtils.hasText(platformField)) { return null; } - for (int i = 0; i < itemFieldMappings.size(); i++) { - JSONObject im = itemFieldMappings.getJSONObject(i); - if (platformField.equals(im.getString("platformField"))) { - return im.getString("identifier"); + for (FieldMappingItem im : itemFieldMappings) { + if (platformField.equals(im.getPlatformField())) { + return im.getIdentifier(); } } return null; } - private String findFirstIdentifier(JSONArray itemFieldMappings) { + private String findIdentifierFromItem(List itemFieldMappings, String platformField) { + if (itemFieldMappings == null || !StringUtils.hasText(platformField)) { + return null; + } + for (ExtraMappingConfig.ItemFieldMapping im : itemFieldMappings) { + if (platformField.equals(im.getPlatformField())) { + return im.getIdentifier(); + } + } + return null; + } + + private String findFirstIdentifier(List itemFieldMappings) { if (itemFieldMappings == null || itemFieldMappings.isEmpty()) { return null; } - for (int i = 0; i < itemFieldMappings.size(); i++) { - String identifier = itemFieldMappings.getJSONObject(i).getString("identifier"); - if (StringUtils.hasText(identifier)) { - return identifier; + for (FieldMappingItem im : itemFieldMappings) { + if (StringUtils.hasText(im.getIdentifier())) { + return im.getIdentifier(); + } + } + return null; + } + + private String findFirstIdentifierFromItem(List itemFieldMappings) { + if (itemFieldMappings == null || itemFieldMappings.isEmpty()) { + return null; + } + for (ExtraMappingConfig.ItemFieldMapping im : itemFieldMappings) { + if (StringUtils.hasText(im.getIdentifier())) { + return im.getIdentifier(); } } return null; @@ -1071,7 +1055,7 @@ public class DcpPlatformAdapter { private void pushBatchSubDevicesData(PlatformInstance platform, PlatformUserAuth userAuth, PlatformDeviceMapping mapping, PlatformDeviceSubMapping currentSubMapping, String reportSerialNumber, String payload, String pushPath, String interfaceCode, - JSONArray requestSchema) { + List requestSchema) { try { // 1. 查询所有子设备。批量推送只依赖子映射顺序和推送接口 schema, // 不再从推送接口 request_schema 读取入网用的 sensorDepths。 @@ -1104,16 +1088,16 @@ public class DcpPlatformAdapter { } // 3. 准备公共参数(复用已有 fieldMapping + requestSchema) - JSONArray fieldMappings = parseJsonArray(mapping.getFieldMapping()); + List fieldMappings = mapping.getFieldMappingParsed(); String fieldName = resolveArrayFieldName(requestSchema); - JSONObject extra = null; - if (StringUtils.hasText(mapping.getExtraMapping())) { - extra = JSON.parseObject(mapping.getExtraMapping()); - if (StringUtils.hasText(extra.getString("fieldName"))) { - fieldName = extra.getString("fieldName"); + List itemFieldMappings = null; + ExtraMappingConfig extra = mapping.getExtraMappingParsed(); + if (extra != null) { + if (StringUtils.hasText(extra.getFieldName())) { + fieldName = extra.getFieldName(); } + itemFieldMappings = extra.getItemFieldMappings(); } - JSONArray itemFieldMappings = extra != null ? extra.getJSONArray("itemFieldMappings") : null; // 解析当前上报子设备的 payload JSONObject currentReportData = parsePayloadToMap(payload); @@ -1137,7 +1121,7 @@ public class DcpPlatformAdapter { } String sensorId = resolveSubDeviceSensorId(sub); - JSONObject sensorDataItem = buildArrayItem(platform, mapping, requestSchema, reportData, fieldName, itemFieldMappings, sensorId); + JSONObject sensorDataItem = buildArrayItem(userAuth, mapping, requestSchema, reportData, fieldName, itemFieldMappings, sensorId); if (sensorDataItem == null || sensorDataItem.isEmpty()) { log.warn("[DCP] 子设备 {} 数据映射为空,跳过其 sensorData", subSn); continue; @@ -1253,16 +1237,9 @@ public class DcpPlatformAdapter { * 优先从 onboard_config 中取 sensorId,否则使用子设备序列号。 */ private String resolveSubDeviceSensorId(PlatformDeviceSubMapping subMapping) { - if (StringUtils.hasText(subMapping.getOnboardConfig())) { - try { - JSONObject config = JSON.parseObject(subMapping.getOnboardConfig()); - String sensorId = config.getString("sensorId"); - if (StringUtils.hasText(sensorId)) { - return sensorId; - } - } catch (Exception e) { - log.debug("[DCP] 解析 onboardConfig 失败 subSn={}", subMapping.getSubSerialNumber()); - } + OnboardConfig config = subMapping.getOnboardConfigParsed(); + if (config != null && StringUtils.hasText(config.getSensorId())) { + return config.getSensorId(); } return subMapping.getSubSerialNumber(); } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceMapping.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceMapping.java index 34ba5046..4ca73f43 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceMapping.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceMapping.java @@ -1,16 +1,22 @@ package com.fastbee.iot.platform.domain; +import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fastbee.iot.platform.model.json.ExtraMappingConfig; +import com.fastbee.iot.platform.model.json.FieldMappingItem; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.util.StringUtils; import java.io.Serializable; import java.util.Date; +import java.util.List; /** * 平台设备绑定关系 @@ -71,6 +77,40 @@ public class PlatformDeviceMapping implements Serializable { @ApiModelProperty("扩展映射配置,如静力水准子设备编号与DCP sensorId映射") private String extraMapping; + /** fieldMapping 类型安全解析 */ + @TableField(exist = false) + @JsonIgnore + private transient List fieldMappingParsed; + + /** extraMapping 类型安全解析 */ + @TableField(exist = false) + @JsonIgnore + private transient ExtraMappingConfig extraMappingParsed; + + public List getFieldMappingParsed() { + if (fieldMappingParsed == null && StringUtils.hasText(this.fieldMapping)) { + this.fieldMappingParsed = JSON.parseArray(this.fieldMapping, FieldMappingItem.class); + } + return fieldMappingParsed; + } + + public void setFieldMappingParsed(List parsed) { + this.fieldMappingParsed = parsed; + this.fieldMapping = parsed == null ? null : JSON.toJSONString(parsed); + } + + public ExtraMappingConfig getExtraMappingParsed() { + if (extraMappingParsed == null && StringUtils.hasText(this.extraMapping)) { + this.extraMappingParsed = JSON.parseObject(this.extraMapping, ExtraMappingConfig.class); + } + return extraMappingParsed; + } + + public void setExtraMappingParsed(ExtraMappingConfig parsed) { + this.extraMappingParsed = parsed; + this.extraMapping = parsed == null ? null : JSON.toJSONString(parsed); + } + @ApiModelProperty("入网状态(0=未入网,1=已入网)") private Integer netStatus; diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceSubMapping.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceSubMapping.java index dc0a538f..830e0576 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceSubMapping.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformDeviceSubMapping.java @@ -1,12 +1,17 @@ package com.fastbee.iot.platform.domain; +import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fastbee.iot.platform.model.json.OnboardConfig; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.util.StringUtils; import java.io.Serializable; import java.util.Date; @@ -45,6 +50,23 @@ public class PlatformDeviceSubMapping implements Serializable { @ApiModelProperty("入网时传给平台的标识参数(平台相关)") private String onboardConfig; + /** onboardConfig 类型安全解析 */ + @TableField(exist = false) + @JsonIgnore + private transient OnboardConfig onboardConfigParsed; + + public OnboardConfig getOnboardConfigParsed() { + if (onboardConfigParsed == null && StringUtils.hasText(this.onboardConfig)) { + this.onboardConfigParsed = JSON.parseObject(this.onboardConfig, OnboardConfig.class); + } + return onboardConfigParsed; + } + + public void setOnboardConfigParsed(OnboardConfig parsed) { + this.onboardConfigParsed = parsed; + this.onboardConfig = parsed == null ? null : JSON.toJSONString(parsed); + } + @ApiModelProperty("入网状态(0=未入网,1=已入网)") private Integer netStatus; diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformFlowStep.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformFlowStep.java index 131bffc1..7d3dbaf7 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformFlowStep.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformFlowStep.java @@ -1,13 +1,22 @@ package com.fastbee.iot.platform.domain; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fastbee.iot.platform.model.json.ParamOverrideConfig; +import com.fastbee.iot.platform.model.json.ResultSaveField; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.util.StringUtils; import java.io.Serializable; +import java.util.List; +import java.util.Map; /** * 流程步骤 @@ -40,6 +49,41 @@ public class PlatformFlowStep implements Serializable { @ApiModelProperty("需持久化的出参字段 [{field,saveTo}]") private String resultSave; + /** paramMapping 类型安全解析 */ + @TableField(exist = false) + @JsonIgnore + private transient Map paramMappingParsed; + + /** resultSave 类型安全解析 */ + @TableField(exist = false) + @JsonIgnore + private transient List resultSaveParsed; + + public Map getParamMappingParsed() { + if (paramMappingParsed == null && StringUtils.hasText(this.paramMapping)) { + this.paramMappingParsed = JSON.parseObject(this.paramMapping, + new TypeReference>(){}.getType()); + } + return paramMappingParsed; + } + + public void setParamMappingParsed(Map parsed) { + this.paramMappingParsed = parsed; + this.paramMapping = parsed == null ? null : JSON.toJSONString(parsed); + } + + public List getResultSaveParsed() { + if (resultSaveParsed == null && StringUtils.hasText(this.resultSave)) { + this.resultSaveParsed = JSON.parseArray(this.resultSave, ResultSaveField.class); + } + return resultSaveParsed; + } + + public void setResultSaveParsed(List parsed) { + this.resultSaveParsed = parsed; + this.resultSave = parsed == null ? null : JSON.toJSONString(parsed); + } + @ApiModelProperty("备注") private String remark; } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInstance.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInstance.java index d8d367be..e2494a1e 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInstance.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInstance.java @@ -1,12 +1,17 @@ package com.fastbee.iot.platform.domain; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.util.StringUtils; import java.io.Serializable; import java.util.Date; @@ -36,6 +41,7 @@ public class PlatformInstance implements Serializable { @ApiModelProperty("认证类型(1=密码Token,2=ApiKey,3=OAuth2)") private Integer authType; + // ========================= 已弃用 ========================= @ApiModelProperty("认证配置模板(新增用户时复制){userName,password,clientId,scope}") private String authConfig; @@ -51,6 +57,7 @@ public class PlatformInstance implements Serializable { @ApiModelProperty("初始化状态(0=未初始化,1=已初始化)") private Integer initStatus; + // ========================= 已弃用 ========================= @ApiModelProperty("是否启用(0=禁用,1=启用)") private String enable; diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInterface.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInterface.java index 10f47e0b..a4a8ded1 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInterface.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformInterface.java @@ -1,15 +1,22 @@ package com.fastbee.iot.platform.domain; +import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fastbee.iot.platform.model.json.ResponseSchemaField; +import com.fastbee.iot.platform.model.json.RequestSchemaField; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.util.StringUtils; import java.io.Serializable; import java.util.Date; +import java.util.List; /** * 平台接口定义 @@ -48,6 +55,40 @@ public class PlatformInterface implements Serializable { @ApiModelProperty("出参字段定义 JSON") private String responseSchema; + /** requestSchema 类型安全解析 */ + @TableField(exist = false) + @JsonIgnore + private transient List requestSchemaParsed; + + /** responseSchema 类型安全解析 */ + @TableField(exist = false) + @JsonIgnore + private transient List responseSchemaParsed; + + public List getRequestSchemaParsed() { + if (requestSchemaParsed == null && StringUtils.hasText(this.requestSchema)) { + this.requestSchemaParsed = JSON.parseArray(this.requestSchema, RequestSchemaField.class); + } + return requestSchemaParsed; + } + + public void setRequestSchemaParsed(List parsed) { + this.requestSchemaParsed = parsed; + this.requestSchema = parsed == null ? null : JSON.toJSONString(parsed); + } + + public List getResponseSchemaParsed() { + if (responseSchemaParsed == null && StringUtils.hasText(this.responseSchema)) { + this.responseSchemaParsed = JSON.parseArray(this.responseSchema, ResponseSchemaField.class); + } + return responseSchemaParsed; + } + + public void setResponseSchemaParsed(List parsed) { + this.responseSchemaParsed = parsed; + this.responseSchema = parsed == null ? null : JSON.toJSONString(parsed); + } + @ApiModelProperty("排序") private Integer sortOrder; diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformUserAuth.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformUserAuth.java index eeaa0f8c..c3eee1df 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformUserAuth.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/domain/PlatformUserAuth.java @@ -1,12 +1,17 @@ package com.fastbee.iot.platform.domain; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.util.StringUtils; import java.io.Serializable; import java.util.Date; @@ -50,6 +55,40 @@ public class PlatformUserAuth implements Serializable { @ApiModelProperty("用户级配置 {projectId,projectName,...}") private String extraConfig; + /** authConfig 惰性解析 */ + @TableField(exist = false) + @JsonIgnore + private transient JSONObject authConfigParsed; + + /** extraConfig 惰性解析 */ + @TableField(exist = false) + @JsonIgnore + private transient JSONObject extraConfigParsed; + + public JSONObject getAuthConfigParsed() { + if (authConfigParsed == null && StringUtils.hasText(this.authConfig)) { + this.authConfigParsed = JSON.parseObject(this.authConfig); + } + return authConfigParsed; + } + + public void setAuthConfigParsed(JSONObject parsed) { + this.authConfigParsed = parsed; + this.authConfig = parsed == null ? null : parsed.toJSONString(); + } + + public JSONObject getExtraConfigParsed() { + if (extraConfigParsed == null && StringUtils.hasText(this.extraConfig)) { + this.extraConfigParsed = JSON.parseObject(this.extraConfig); + } + return extraConfigParsed; + } + + public void setExtraConfigParsed(JSONObject parsed) { + this.extraConfigParsed = parsed; + this.extraConfig = parsed == null ? null : parsed.toJSONString(); + } + @ApiModelProperty("是否启用(0=禁用,1=启用)") private String enable; diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/PlatformFlowExecutor.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/PlatformFlowExecutor.java index a854d24a..291f28aa 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/PlatformFlowExecutor.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/PlatformFlowExecutor.java @@ -14,6 +14,7 @@ import com.fastbee.iot.mapper.DeviceMapper; import com.fastbee.iot.mapper.SubGatewayMapper; import com.fastbee.iot.platform.domain.*; import com.fastbee.iot.platform.mapper.*; +import com.fastbee.iot.platform.model.json.*; import com.fastbee.iot.platform.service.IPlatformPushLogService; import com.dtflys.forest.http.ForestRequestType; import lombok.extern.slf4j.Slf4j; @@ -227,78 +228,91 @@ public class PlatformFlowExecutor { * 解析步骤入参 * source 枚举: fixed / auth / extra / context / device / thingsModel / ts */ - @SuppressWarnings("unchecked") private Map resolveParams(PlatformInterface iface, PlatformFlowStep step, FlowContext ctx) { Map result = new LinkedHashMap<>(); - if (!StringUtils.hasText(iface.getRequestSchema())) { + List schema = iface.getRequestSchemaParsed(); + if (schema == null || schema.isEmpty()) { return result; } // 合并接口默认 schema 和步骤覆盖 mapping - JSONArray schema = JSON.parseArray(iface.getRequestSchema()); - Map overrides = StringUtils.hasText(step.getParamMapping()) - ? JSON.parseObject(step.getParamMapping(), Map.class) - : Collections.emptyMap(); + Map overrides = step.getParamMappingParsed(); + if (overrides == null) { + overrides = Collections.emptyMap(); + } - // 优先从 userAuth 获取 authConfig,fallback 到 platform.authTemplate + // 获取 userAuth authConfig JSONObject authConfig = new JSONObject(); - if (ctx.getPlatformUserAuth() != null && StringUtils.hasText(ctx.getPlatformUserAuth().getAuthConfig())) { - authConfig = JSON.parseObject(ctx.getPlatformUserAuth().getAuthConfig()); - } else if (StringUtils.hasText(ctx.getPlatform().getAuthConfig())) { - authConfig = JSON.parseObject(ctx.getPlatform().getAuthConfig()); + if (ctx.getPlatformUserAuth() != null) { + JSONObject parsed = ctx.getPlatformUserAuth().getAuthConfigParsed(); + if (parsed != null) { + authConfig = parsed; + } } - // 优先从 userAuth 获取 extraConfig,fallback 到 platform.extraConfig + // 获取 userAuth extraConfig JSONObject extraConfig = new JSONObject(); - if (ctx.getPlatformUserAuth() != null && StringUtils.hasText(ctx.getPlatformUserAuth().getExtraConfig())) { - extraConfig = JSON.parseObject(ctx.getPlatformUserAuth().getExtraConfig()); - } else if (StringUtils.hasText(ctx.getPlatform().getExtraConfig())) { - extraConfig = JSON.parseObject(ctx.getPlatform().getExtraConfig()); + if (ctx.getPlatformUserAuth() != null) { + JSONObject parsed = ctx.getPlatformUserAuth().getExtraConfigParsed(); + if (parsed != null) { + extraConfig = parsed; + } } - for (int i = 0; i < schema.size(); i++) { - JSONObject field = schema.getJSONObject(i); - String name = field.getString("name"); + for (RequestSchemaField field : schema) { + String name = field.getName(); + if (name == null) continue; // 步骤覆盖优先 if (overrides.containsKey(name)) { - Object override = overrides.get(name); - if (override instanceof Map) { - Map ov = (Map) override; - String src = (String) ov.get("source"); - String type = ov.get("type") != null ? String.valueOf(ov.get("type")) : field.getString("type"); - result.put(name, normalizeValueByType(type, resolveValue(src, ov, authConfig, extraConfig, ctx))); + ParamOverrideConfig override = overrides.get(name); + if (override != null) { + String src = override.getSource() != null ? override.getSource() : field.getSource(); + String type = override.getType() != null ? override.getType() : field.getType(); + result.put(name, normalizeValueByType(type, + resolveValue(src, mergeOverride(field, override), authConfig, extraConfig, ctx))); } else { - result.put(name, normalizeValueByType(field.getString("type"), override)); + result.put(name, normalizeValueByType(field.getType(), null)); } continue; } - String source = field.getString("source"); - result.put(name, normalizeValueByType(field.getString("type"), resolveValue(source, field, authConfig, extraConfig, ctx))); + result.put(name, normalizeValueByType(field.getType(), + resolveValue(field.getSource(), field, authConfig, extraConfig, ctx))); } return result; } - private Object resolveValue(String source, Object fieldDef, JSONObject authConfig, + private RequestSchemaField mergeOverride(RequestSchemaField schemaField, ParamOverrideConfig override) { + if (override == null) return schemaField; + RequestSchemaField merged = new RequestSchemaField(); + merged.setName(schemaField.getName()); + merged.setSource(override.getSource() != null ? override.getSource() : schemaField.getSource()); + merged.setType(override.getType() != null ? override.getType() : schemaField.getType()); + merged.setValue(override.getValue() != null ? override.getValue() : schemaField.getValue()); + merged.setField(override.getField() != null ? override.getField() : schemaField.getField()); + merged.setContextKey(override.getContextKey() != null ? override.getContextKey() : schemaField.getContextKey()); + merged.setIdentifier(override.getIdentifier() != null ? override.getIdentifier() : schemaField.getIdentifier()); + return merged; + } + + private Object resolveValue(String source, RequestSchemaField fieldDef, JSONObject authConfig, JSONObject extraConfig, FlowContext ctx) { if (source == null) return null; - JSONObject fd = fieldDef instanceof JSONObject ? (JSONObject) fieldDef - : JSON.parseObject(JSON.toJSONString(fieldDef)); switch (source) { case "fixed": - return fd.get("value"); + return fieldDef.getValue(); case "uuid": return newGeneratedId(); case "auth": - return authConfig.get(fd.getString("field")); + return authConfig.get(fieldDef.getField()); case "extra": - return extraConfig.get(fd.getString("field")); + return extraConfig.get(fieldDef.getField()); case "context": - return ctx.getFromContext(fd.getString("contextKey")); + return ctx.getFromContext(fieldDef.getContextKey()); case "device": if (ctx.getDeviceMapping() == null) return null; - String df = fd.getString("field"); + String df = fieldDef.getField(); if ("platform_device_id".equals(df)) return ctx.getDeviceMapping().getPlatformDeviceId(); if ("platform_point_id".equals(df)) return ctx.getDeviceMapping().getPlatformPointId(); // 支持从设备绑定表读取项目信息 @@ -310,7 +324,7 @@ public class PlatformFlowExecutor { if (StringUtils.hasText(ctx.getCurrentMonitorSerialNumber())) { return ctx.getCurrentMonitorSerialNumber(); } - return normalizeValueByType(fd.getString("type"), resolveGatewaySerialNumber(ctx.getDeviceMapping())); + return normalizeValueByType(fieldDef.getType(), resolveGatewaySerialNumber(ctx.getDeviceMapping())); } if ("device_name".equals(df)) { return resolveDeviceName(ctx); @@ -323,11 +337,11 @@ public class PlatformFlowExecutor { } return null; case "thingsModel": - return resolveThingsModelValue(ctx, fd); + return resolveThingsModelValue(ctx, fieldDef); case "ts": - return resolveThingsModelTs(ctx, fd); + return resolveThingsModelTs(ctx, fieldDef); case "onboard_config": - return resolveOnboardConfigValue(ctx, fd); + return resolveOnboardConfigValue(ctx, fieldDef); case "subDeviceSensorIds": return collectSubDeviceSensorIds(ctx); case "cascadedSubDeviceSensorIds": @@ -341,7 +355,7 @@ public class PlatformFlowExecutor { } } - private Object resolveThingsModelValue(FlowContext ctx, JSONObject fieldDef) { + private Object resolveThingsModelValue(FlowContext ctx, RequestSchemaField fieldDef) { JSONObject payload = parsePayloadToObject(ctx.getPayload()); if (payload == null) { return null; @@ -350,7 +364,7 @@ public class PlatformFlowExecutor { return StringUtils.hasText(identifier) ? payload.get(identifier) : null; } - private Object resolveThingsModelTs(FlowContext ctx, JSONObject fieldDef) { + private Object resolveThingsModelTs(FlowContext ctx, RequestSchemaField fieldDef) { JSONObject payload = parsePayloadToObject(ctx.getPayload()); if (payload == null) { return null; @@ -366,13 +380,13 @@ public class PlatformFlowExecutor { return ts != null ? ts : payload.get("timestamp"); } - private String resolveThingsModelIdentifier(JSONObject fieldDef) { - String identifier = fieldDef.getString("identifier"); + private String resolveThingsModelIdentifier(RequestSchemaField fieldDef) { + String identifier = fieldDef.getIdentifier(); if (StringUtils.hasText(identifier)) { return identifier; } - String field = fieldDef.getString("field"); - return StringUtils.hasText(field) ? field : fieldDef.getString("contextKey"); + String field = fieldDef.getField(); + return StringUtils.hasText(field) ? field : fieldDef.getContextKey(); } private JSONObject parsePayloadToObject(String payload) { @@ -413,15 +427,18 @@ public class PlatformFlowExecutor { return null; } - private Object resolveOnboardConfigValue(FlowContext ctx, JSONObject fieldDef) { - String name = fieldDef.getString("name"); + private Object resolveOnboardConfigValue(FlowContext ctx, RequestSchemaField fieldDef) { + String name = fieldDef.getName(); List monitorSubMappings = ctx.getMonitorSubMappings(); if (ctx.getCurrentMonitorIndex() != null && CollectionUtils.isNotEmpty(monitorSubMappings)) { int index = ctx.getCurrentMonitorIndex(); PlatformDeviceSubMapping platformDeviceSubMapping = monitorSubMappings.get(index); - JSONObject authConfig = StringUtils.hasText(platformDeviceSubMapping.getOnboardConfig()) - ? JSON.parseObject(platformDeviceSubMapping.getOnboardConfig()) : new JSONObject(); - String value = authConfig.getString(name); + OnboardConfig config = platformDeviceSubMapping.getOnboardConfigParsed(); + if (config == null) { + throw new RuntimeException("入网失败,子设备" + platformDeviceSubMapping.getSubDeviceName() + "未配置值:" + name); + } + // 从 OnboardConfig 按字段名反射获取值(支持 sensorId/sensorDepth/refSerialNumber/refSensorId) + String value = resolveOnboardConfigField(config, name); if (value == null) { throw new RuntimeException("入网失败,子设备" + platformDeviceSubMapping.getSubDeviceName() + "未配置值:" + name); } @@ -500,21 +517,24 @@ public class PlatformFlowExecutor { private Set resolveGeneratedFieldNames(PlatformInterface iface, PlatformFlowStep step) { LinkedHashMap sources = new LinkedHashMap<>(); try { - if (iface != null && StringUtils.hasText(iface.getRequestSchema())) { - JSONArray schema = JSON.parseArray(iface.getRequestSchema()); - for (int i = 0; i < schema.size(); i++) { - JSONObject field = schema.getJSONObject(i); - if (field != null && StringUtils.hasText(field.getString("name"))) { - sources.put(field.getString("name"), field.getString("source")); + if (iface != null) { + List schema = iface.getRequestSchemaParsed(); + if (schema != null) { + for (RequestSchemaField field : schema) { + if (field != null && StringUtils.hasText(field.getName())) { + sources.put(field.getName(), field.getSource()); + } } } } - if (step != null && StringUtils.hasText(step.getParamMapping())) { - JSONObject overrides = JSON.parseObject(step.getParamMapping()); - for (String fieldName : overrides.keySet()) { - JSONObject override = overrides.getJSONObject(fieldName); - if (override != null && StringUtils.hasText(override.getString("source"))) { - sources.put(fieldName, override.getString("source")); + if (step != null) { + Map overrides = step.getParamMappingParsed(); + if (overrides != null) { + for (Map.Entry entry : overrides.entrySet()) { + ParamOverrideConfig override = entry.getValue(); + if (override != null && StringUtils.hasText(override.getSource())) { + sources.put(entry.getKey(), override.getSource()); + } } } } @@ -531,10 +551,8 @@ public class PlatformFlowExecutor { private List collectSubDeviceSensorIds(FlowContext ctx) { if (StringUtils.hasText(ctx.getCurrentMonitorSerialNumber()) && StringUtils.hasText(ctx.getCurrentReferenceSerialNumber())) { - String refSensorId = resolveOnboardSensorId( - ctx.getReferenceSubMappingOnboardConfig(), ctx.getCurrentReferenceSerialNumber()); - String monitorSensorId = resolveOnboardSensorId( - ctx.getSubMappingOnboardConfig(), ctx.getCurrentMonitorSerialNumber()); + String refSensorId = resolveOnboardSensorIdByCtx(ctx, true); + String monitorSensorId = resolveOnboardSensorIdByCtx(ctx, false); List ids = new ArrayList<>(); if (StringUtils.hasText(refSensorId)) ids.add(refSensorId); if (StringUtils.hasText(monitorSensorId)) ids.add(monitorSensorId); @@ -544,16 +562,14 @@ public class PlatformFlowExecutor { } // 网关入网时,从子表 onboard_config 获取 sensorId - if (StringUtils.hasText(ctx.getSubMappingOnboardConfig())) { - try { - JSONObject config = JSON.parseObject(ctx.getSubMappingOnboardConfig()); - List ids = new ArrayList<>(); - String sensorId = config.getString("sensorId"); - String refSensorId = config.getString("refSensorId"); - if (StringUtils.hasText(refSensorId)) ids.add(refSensorId); - if (StringUtils.hasText(sensorId)) ids.add(sensorId); - if (!ids.isEmpty()) return ids; - } catch (Exception ignored) {} + OnboardConfig currentConfig = resolveCurrentOnboardConfig(ctx); + if (currentConfig != null) { + List ids = new ArrayList<>(); + String sensorId = currentConfig.getSensorId(); + String refSensorId = currentConfig.getRefSensorId(); + if (StringUtils.hasText(refSensorId)) ids.add(refSensorId); + if (StringUtils.hasText(sensorId)) ids.add(sensorId); + if (!ids.isEmpty()) return ids; } // 回退到从 extra_mapping 解析(直连设备等场景) return collectSubDeviceSensorBindings(ctx.getDeviceMapping()).stream() @@ -567,7 +583,13 @@ public class PlatformFlowExecutor { List suffix = collectCascadedSubDeviceSuffix(ctx); Collections.reverse(suffix); List sensorIds = suffix.stream() - .map(sub -> resolveOnboardSensorId(sub.getOnboardConfig(), sub.getSubSerialNumber())) + .map(sub -> { + OnboardConfig cfg = sub.getOnboardConfigParsed(); + if (cfg != null && StringUtils.hasText(cfg.getSensorId())) { + return cfg.getSensorId(); + } + return normalizeText(sub.getSubSerialNumber()); + }) .collect(Collectors.toList()); if (sensorIds.stream().anyMatch(id -> !StringUtils.hasText(id))) { throw new RuntimeException("深部位移子设备缺少 sensorId"); @@ -634,14 +656,14 @@ public class PlatformFlowExecutor { } private Object readSensorDepth(PlatformDeviceSubMapping mapping) { - if (mapping == null || !StringUtils.hasText(mapping.getOnboardConfig())) { + if (mapping == null) { return null; } - try { - return JSON.parseObject(mapping.getOnboardConfig()).get("sensorDepth"); - } catch (Exception e) { - throw new RuntimeException("子设备 " + mapping.getSubSerialNumber() + " 入网参数不是有效JSON"); + OnboardConfig config = mapping.getOnboardConfigParsed(); + if (config == null) { + return null; } + return config.getSensorDepth(); } /** @@ -650,12 +672,13 @@ public class PlatformFlowExecutor { */ private List> collectReferenceSubDeviceSensorIdPairs(FlowContext ctx) { if (StringUtils.hasText(ctx.getCurrentMonitorSerialNumber())) { - String sensorId = resolveOnboardSensorId( - ctx.getSubMappingOnboardConfig(), ctx.getCurrentMonitorSerialNumber()); - String refSensorId = resolveOnboardSensorId( - ctx.getReferenceSubMappingOnboardConfig(), ctx.getCurrentReferenceSerialNumber()); + String sensorId = resolveOnboardSensorIdByCtx(ctx, false); + String refSensorId = resolveOnboardSensorIdByCtx(ctx, true); if (!StringUtils.hasText(refSensorId)) { - refSensorId = resolveOnboardConfigText(ctx.getSubMappingOnboardConfig(), "refSensorId"); + OnboardConfig currentConfig = resolveCurrentOnboardConfig(ctx); + if (currentConfig != null) { + refSensorId = currentConfig.getRefSensorId(); + } } if (StringUtils.hasText(sensorId) && StringUtils.hasText(refSensorId)) { if (sameText(refSensorId, sensorId)) { @@ -672,17 +695,15 @@ public class PlatformFlowExecutor { } // 网关入网:onboardConfig 已指定当前监测点 - if (StringUtils.hasText(ctx.getSubMappingOnboardConfig())) { - try { - JSONObject config = JSON.parseObject(ctx.getSubMappingOnboardConfig()); - String sensorId = config.getString("sensorId"); - String refSensorId = config.getString("refSensorId"); - if (StringUtils.hasText(sensorId) && StringUtils.hasText(refSensorId)) { - List> pairs = new ArrayList<>(); - pairs.add(Arrays.asList(refSensorId, sensorId)); - return pairs; - } - } catch (Exception ignored) {} + OnboardConfig currentConfig = resolveCurrentOnboardConfig(ctx); + if (currentConfig != null) { + String sensorId = currentConfig.getSensorId(); + String refSensorId = currentConfig.getRefSensorId(); + if (StringUtils.hasText(sensorId) && StringUtils.hasText(refSensorId)) { + List> pairs = new ArrayList<>(); + pairs.add(Arrays.asList(refSensorId, sensorId)); + return pairs; + } } // 回退:从 extra_mapping 解析(兼容直连设备场景) PlatformDeviceMapping mapping = ctx.getDeviceMapping(); @@ -711,20 +732,52 @@ public class PlatformFlowExecutor { return pairs; } - private String resolveOnboardSensorId(String onboardConfig, String fallbackSerialNumber) { - String sensorId = resolveOnboardConfigText(onboardConfig, "sensorId"); - return StringUtils.hasText(sensorId) ? sensorId : normalizeText(fallbackSerialNumber); + /** 获取当前监测点子设备的 OnboardConfig POJO */ + private OnboardConfig resolveCurrentOnboardConfig(FlowContext ctx) { + if (ctx.getCurrentMonitorIndex() != null && CollectionUtils.isNotEmpty(ctx.getMonitorSubMappings())) { + int index = ctx.getCurrentMonitorIndex(); + return ctx.getMonitorSubMappings().get(index).getOnboardConfigParsed(); + } + if (StringUtils.hasText(ctx.getSubMappingOnboardConfig())) { + return JSON.parseObject(ctx.getSubMappingOnboardConfig(), OnboardConfig.class); + } + return null; } - private String resolveOnboardConfigText(String onboardConfig, String field) { - if (!StringUtils.hasText(onboardConfig) || !StringUtils.hasText(field)) { - return null; + /** + * 解析 sensorId:优先从子映射 OnboardConfig 获取,否则返回序列号 + * @param ctx 上下文 + * @param isReference true=基准点, false=监测点 + */ + private String resolveOnboardSensorIdByCtx(FlowContext ctx, boolean isReference) { + String configStr = isReference ? ctx.getReferenceSubMappingOnboardConfig() : ctx.getSubMappingOnboardConfig(); + String fallbackSn = isReference ? ctx.getCurrentReferenceSerialNumber() : ctx.getCurrentMonitorSerialNumber(); + + if (StringUtils.hasText(configStr)) { + OnboardConfig config = JSON.parseObject(configStr, OnboardConfig.class); + if (config != null && StringUtils.hasText(config.getSensorId())) { + return config.getSensorId(); + } + } else if (!isReference && ctx.getCurrentMonitorIndex() != null + && CollectionUtils.isNotEmpty(ctx.getMonitorSubMappings())) { + int index = ctx.getCurrentMonitorIndex(); + OnboardConfig config = ctx.getMonitorSubMappings().get(index).getOnboardConfigParsed(); + if (config != null && StringUtils.hasText(config.getSensorId())) { + return config.getSensorId(); + } } - try { - JSONObject config = JSON.parseObject(onboardConfig); - return normalizeText(config.getString(field)); - } catch (Exception ignored) { - return null; + return normalizeText(fallbackSn); + } + + /** 从 OnboardConfig POJO 按字段名取值 */ + private String resolveOnboardConfigField(OnboardConfig config, String field) { + if (config == null || !StringUtils.hasText(field)) return null; + switch (field) { + case "sensorId": return config.getSensorId(); + case "sensorDepth": return config.getSensorDepth(); + case "refSerialNumber": return config.getRefSerialNumber(); + case "refSensorId": return config.getRefSensorId(); + default: return null; } } @@ -751,28 +804,35 @@ public class PlatformFlowExecutor { private String appendConfiguredSubDeviceBindings(LinkedHashMap bindings, PlatformDeviceMapping mapping) { - if (!StringUtils.hasText(mapping.getExtraMapping())) { + ExtraMappingConfig extra = mapping.getExtraMappingParsed(); + if (extra == null) { return null; } try { - JSONObject extra = JSON.parseObject(mapping.getExtraMapping()); String referenceSerialNumber = null; - JSONArray arrayFields = extra.getJSONArray("arrayFields"); + List arrayFields = extra.getArrayFields(); if (arrayFields != null) { - for (int i = 0; i < arrayFields.size(); i++) { - JSONObject arrayField = arrayFields.getJSONObject(i); + for (ExtraMappingConfig.ArrayFieldConfig arrayField : arrayFields) { referenceSerialNumber = appendConfiguredMappingRows(bindings, - arrayField == null ? null : arrayField.getJSONArray("subDeviceMappings"), + arrayField == null ? null : arrayField.getSubDeviceMappings(), referenceSerialNumber); } } - JSONObject staticLevel = extra.getJSONObject("staticLevel"); + ExtraMappingConfig.StaticLevelConfig staticLevel = extra.getStaticLevel(); if (staticLevel != null) { - referenceSerialNumber = normalizeText(staticLevel.getString("referenceSerialNumber")); - referenceSerialNumber = appendConfiguredMappingRows( - bindings, staticLevel.getJSONArray("sensorIdMappings"), referenceSerialNumber); + referenceSerialNumber = normalizeText(staticLevel.getReferenceSerialNumber()); + List sensorIdMappings = staticLevel.getSensorIdMappings(); + if (sensorIdMappings != null) { + for (ExtraMappingConfig.SensorIdMapping item : sensorIdMappings) { + if (item == null) continue; + String sn = normalizeText(item.getSerialNumber()); + if (!StringUtils.hasText(sn)) continue; + String sid = normalizeText(item.getSensorId()); + upsertSubDeviceBinding(bindings, sn, StringUtils.hasText(sid) ? sid : sn); + } + } } return referenceSerialNumber; } catch (Exception e) { @@ -783,24 +843,20 @@ public class PlatformFlowExecutor { } private String appendConfiguredMappingRows(LinkedHashMap bindings, - JSONArray mappings, String referenceSerialNumber) { + List mappings, + String referenceSerialNumber) { if (mappings == null || mappings.isEmpty()) { return referenceSerialNumber; } String ref = referenceSerialNumber; - for (int i = 0; i < mappings.size(); i++) { - JSONObject item = mappings.getJSONObject(i); - if (item == null) { - continue; - } - String serialNumber = normalizeText(item.getString("serialNumber")); - if (!StringUtils.hasText(serialNumber)) { - continue; - } - String sensorId = normalizeText(item.getString("sensorId")); + for (ExtraMappingConfig.SubDeviceMapping item : mappings) { + if (item == null) continue; + String serialNumber = normalizeText(item.getSerialNumber()); + if (!StringUtils.hasText(serialNumber)) continue; + String sensorId = normalizeText(item.getSensorId()); upsertSubDeviceBinding(bindings, serialNumber, StringUtils.hasText(sensorId) ? sensorId : serialNumber); - if (Boolean.TRUE.equals(item.getBoolean("isReference"))) { + if (Boolean.TRUE.equals(item.getIsReference())) { ref = serialNumber; } } @@ -925,13 +981,7 @@ public class PlatformFlowExecutor { /** 移除已替换到路径中的变量,避免重复放入 body */ private void removePathVars(Map params, String path) { - Iterator> it = params.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry e = it.next(); - if (path.contains("{" + e.getKey() + "}")) { - it.remove(); - } - } + params.entrySet().removeIf(e -> path.contains("{" + e.getKey() + "}")); } private Object buildResponseData(PlatformInterface iface, String responseBody) { @@ -973,19 +1023,18 @@ public class PlatformFlowExecutor { private JSONObject extractResponseBySchema(PlatformInterface iface, Object parsedResponse) { JSONObject extracted = new JSONObject(); - if (iface == null || !StringUtils.hasText(iface.getResponseSchema()) || parsedResponse == null) { + List schema = iface.getResponseSchemaParsed(); + if (schema == null || schema.isEmpty() || parsedResponse == null) { return extracted; } try { - JSONArray schema = JSON.parseArray(iface.getResponseSchema()); - for (int i = 0; i < schema.size(); i++) { - JSONObject field = schema.getJSONObject(i); - if (field == null || !StringUtils.hasText(field.getString("name"))) { + for (ResponseSchemaField field : schema) { + if (field == null || !StringUtils.hasText(field.getName())) { continue; } - Object value = readPathValue(parsedResponse, field.getString("path")); + Object value = readPathValue(parsedResponse, field.getPath()); if (value != null) { - extracted.put(field.getString("name"), value); + extracted.put(field.getName(), value); } } } catch (Exception e) { @@ -1101,14 +1150,15 @@ public class PlatformFlowExecutor { */ private void persistResultSave(PlatformFlowStep step, Object responseData, FlowContext context) { try { - JSONArray saves = JSON.parseArray(step.getResultSave()); + List saves = step.getResultSaveParsed(); + if (saves == null || saves.isEmpty()) return; JSONObject response = toJsonObject(responseData); if (response == null) return; - //PlatformInstance platform = context.getPlatform(); + PlatformUserAuth userAuth = context.getPlatformUserAuth(); - JSONObject extra = StringUtils.hasText(userAuth.getExtraConfig()) - ? JSON.parseObject(userAuth.getExtraConfig()) : new JSONObject(); + JSONObject extra = userAuth.getExtraConfigParsed(); + if (extra == null) extra = new JSONObject(); boolean extraChanged = false; boolean userAuthFieldChanged = false; // 平台用户授权信息(token、userId) @@ -1119,10 +1169,9 @@ public class PlatformFlowExecutor { // 保存平台映射关系(测点ID、设备ID) PlatformDeviceMapping mappingUpdate = new PlatformDeviceMapping(); - for (int i = 0; i < saves.size(); i++) { - JSONObject save = saves.getJSONObject(i); - String field = save.getString("field"); - String saveTo = save.getString("saveTo"); + for (ResultSaveField save : saves) { + String field = save.getField(); + String saveTo = save.getSaveTo(); Object value = response.get(field); if (value == null || saveTo == null) continue; @@ -1185,6 +1234,7 @@ public class PlatformFlowExecutor { } } catch (Exception e) { log.warn("[PlatformFlow] persistResultSave 失败: {}", e.getMessage()); + throw new RuntimeException("执行流程❌,保存数据失败: " + e.getMessage()); } } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ExtraMappingConfig.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ExtraMappingConfig.java new file mode 100644 index 00000000..b53316c3 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ExtraMappingConfig.java @@ -0,0 +1,62 @@ +package com.fastbee.iot.platform.model.json; + +import com.fastbee.iot.platform.domain.PlatformDeviceMapping; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @see PlatformDeviceMapping#extraMapping POJO + */ +@Data +public class ExtraMappingConfig implements Serializable { + + /** 覆盖推送接口中的数组字段名 */ + private String fieldName; + + /** 数组模式:single / reference_pair */ + private String arrayMode; + + /** 静力水准缓存超时秒数 */ + private Integer reportIntervalSec; + + /** 数组项字段映射 */ + private List itemFieldMappings; + + /** 批量推送数组字段子设备映射配置 */ + private List arrayFields; + + /** 静力水准配置 */ + private StaticLevelConfig staticLevel; + + @Data + public static class ItemFieldMapping implements Serializable { + private String identifier; + private String platformField; + } + + @Data + public static class ArrayFieldConfig implements Serializable { + private List subDeviceMappings; + } + + @Data + public static class SubDeviceMapping implements Serializable { + private String serialNumber; + private String sensorId; + private Boolean isReference; + } + + @Data + public static class StaticLevelConfig implements Serializable { + private String referenceSerialNumber; + private List sensorIdMappings; + } + + @Data + public static class SensorIdMapping implements Serializable { + private String serialNumber; + private String sensorId; + } +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/FieldMappingItem.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/FieldMappingItem.java new file mode 100644 index 00000000..68b6a0ab --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/FieldMappingItem.java @@ -0,0 +1,22 @@ +package com.fastbee.iot.platform.model.json; + +import com.fastbee.iot.platform.domain.PlatformDeviceMapping; +import lombok.Data; + +import java.io.Serializable; + +/** + * @see PlatformDeviceMapping.fieldMapping 字段定义 POJO:{identifier, platformField, transform} + */ +@Data +public class FieldMappingItem implements Serializable { + + /** 物模型标识符 */ + private String identifier; + + /** 平台侧字段名 */ + private String platformField; + + /** 转换方式(可选) */ + private String transform; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/OnboardConfig.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/OnboardConfig.java new file mode 100644 index 00000000..2f8daa05 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/OnboardConfig.java @@ -0,0 +1,25 @@ +package com.fastbee.iot.platform.model.json; + +import com.fastbee.iot.platform.domain.PlatformDeviceSubMapping; +import lombok.Data; + +import java.io.Serializable; + +/** + * @see PlatformDeviceSubMapping.onboardConfig POJO + */ +@Data +public class OnboardConfig implements Serializable { + + /** 平台传感器 ID */ + private String sensorId; + + /** 传感器深度 */ + private String sensorDepth; + + /** 基准点序列号 */ + private String refSerialNumber; + + /** 基准点传感器 ID */ + private String refSensorId; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ParamOverrideConfig.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ParamOverrideConfig.java new file mode 100644 index 00000000..aecd848f --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ParamOverrideConfig.java @@ -0,0 +1,34 @@ +package com.fastbee.iot.platform.model.json; + +import com.fastbee.iot.platform.domain.PlatformFlowStep; +import lombok.Data; + +import java.io.Serializable; + +/** + * @see PlatformFlowStep.paramMapping POJO + *

+ * JSON 结构为 Map,key 为字段名,value 为参数覆盖定义: + * { "grant_type": {"source": "fixed", "value": "password"}, ... } + */ +@Data +public class ParamOverrideConfig implements Serializable { + + /** 值来源:fixed/auth/extra/context 等 */ + private String source; + + /** 类型:string/number 等 */ + private String type; + + /** source=auth/extra 时的子字段名 */ + private String field; + + /** source=fixed 时的固定值 */ + private Object value; + + /** source=context 时的上下文 key */ + private String contextKey; + + /** source=thingsModel 时的物模型标识符 */ + private String identifier; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/RequestSchemaField.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/RequestSchemaField.java new file mode 100644 index 00000000..7e518fba --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/RequestSchemaField.java @@ -0,0 +1,44 @@ +package com.fastbee.iot.platform.model.json; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.fastbee.iot.platform.domain.PlatformInterface; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @see PlatformInterface.requestSchema 字段定义 POJO + * 支持 14 种 source 类型和递归嵌套的 itemSchema + */ +@Data +public class RequestSchemaField implements Serializable { + + /** 字段名(出参 key) */ + private String name; + + /** 值来源:fixed/auth/extra/device/thingsModel/ts/context/uuid/onboard_config/等 */ + private String source; + + /** 字段类型:string/number/boolean/array */ + private String type; + + /** source=fixed 时的固定值 */ + private Object value; + + /** source=auth/extra/device 时的子字段名 */ + private String field; + + /** source=context 时的上下文 key */ + private String contextKey; + + /** source=thingsModel 时的物模型标识符 */ + private String identifier; + + /** type=array 时的数组成员 schema */ + @JSONField(name = "itemSchema") + private List itemSchema; + + /** type=array 时的数组模式:single/reference_pair */ + private String arrayMode; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResponseSchemaField.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResponseSchemaField.java new file mode 100644 index 00000000..9019e7f9 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResponseSchemaField.java @@ -0,0 +1,19 @@ +package com.fastbee.iot.platform.model.json; + +import com.fastbee.iot.platform.domain.PlatformInterface; +import lombok.Data; + +import java.io.Serializable; + +/** + * @see PlatformInterface.responseSchema 字段定义 POJO:{name, path} + */ +@Data +public class ResponseSchemaField implements Serializable { + + /** 存储到 FlowContext 的 key 名 */ + private String name; + + /** JSON path 表达式,如 $.data.id、data[0].deviceId */ + private String path; +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResultSaveField.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResultSaveField.java new file mode 100644 index 00000000..9a4c5311 --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/model/json/ResultSaveField.java @@ -0,0 +1,19 @@ +package com.fastbee.iot.platform.model.json; + +import com.fastbee.iot.platform.domain.PlatformFlowStep; +import lombok.Data; + +import java.io.Serializable; + +/** + * @see PlatformFlowStep.resultSave 字段定义 POJO:{field, saveTo} + */ +@Data +public class ResultSaveField implements Serializable { + + /** 响应中的字段名 */ + private String field; + + /** 保存目标,如 platform_user_auth.access_token、device_mapping.platform_device_id、extra_config.projectId */ + private String saveTo; +}