20260731:平台对接优化,JSON解析

This commit is contained in:
zhugongping 2026-07-31 15:08:35 +08:00
parent 1b5536fc8c
commit 84f681e9d9
15 changed files with 798 additions and 353 deletions

View File

@ -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<RequestSchemaField> 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<RequestSchemaField> requestSchema = pushInterface.getRequestSchemaParsed();
String interfaceCode = resolveInterfaceCode(sensorType, pushInterface);
try {
@ -151,38 +153,33 @@ public class DcpPlatformAdapter {
return;
}
JSONArray fieldMappings = parseJsonArray(mapping.getFieldMapping());
List<FieldMappingItem> fieldMappings = mapping.getFieldMappingParsed();
String baseUrl = platform.getBaseUrl() + pushPath;
// 解析 extra_mapping 中的 itemFieldMappings 和 reportIntervalSec
int reportIntervalSec = 300;
JSONArray itemFieldMappings = null;
List<ExtraMappingConfig.ItemFieldMapping> 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<RequestSchemaField> 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<RequestSchemaField> 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<RequestSchemaField> 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<RequestSchemaField> requestSchema) {
if (!StringUtils.hasText(payload)) {
return null;
}
@ -406,7 +397,7 @@ public class DcpPlatformAdapter {
return null;
}
JSONArray fieldMappings = parseJsonArray(mapping.getFieldMapping());
List<FieldMappingItem> 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<FieldMappingItem> 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<RequestSchemaField> 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<RequestSchemaField> 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<FieldMappingItem> 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<RequestSchemaField> requestSchema, JSONObject reportData,
List<FieldMappingItem> 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<FieldMappingItem> 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<RequestSchemaField> requestSchema, JSONObject reportData, String fieldName,
List<ExtraMappingConfig.ItemFieldMapping> itemFieldMappings, String subDeviceId) {
RequestSchemaField arrayFieldSchema = findArrayFieldSchema(requestSchema, fieldName);
List<RequestSchemaField> 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<ExtraMappingConfig.ItemFieldMapping> itemFieldMappings, String subDeviceId) {
Map<String, Object> 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<ExtraMappingConfig.ItemFieldMapping> 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<RequestSchemaField> 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<RequestSchemaField> 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<RequestSchemaField> 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<FieldMappingItem> 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<FieldMappingItem> 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<ExtraMappingConfig.ItemFieldMapping> 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<String, Object> extractItemValues(JSONObject reportData, JSONArray itemFieldMappings) {
private Map<String, Object> extractItemValues(JSONObject reportData,
List<ExtraMappingConfig.ItemFieldMapping> itemFieldMappings) {
Map<String, Object> 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<FieldMappingItem> 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<ExtraMappingConfig.ItemFieldMapping> 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<FieldMappingItem> 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<ExtraMappingConfig.ItemFieldMapping> 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<RequestSchemaField> requestSchema) {
try {
// 1. 查询所有子设备。批量推送只依赖子映射顺序和推送接口 schema,
// 不再从推送接口 request_schema 读取入网用的 sensorDepths。
@ -1104,16 +1088,16 @@ public class DcpPlatformAdapter {
}
// 3. 准备公共参数(复用已有 fieldMapping + requestSchema)
JSONArray fieldMappings = parseJsonArray(mapping.getFieldMapping());
List<FieldMappingItem> 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<ExtraMappingConfig.ItemFieldMapping> 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();
}

View File

@ -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<FieldMappingItem> fieldMappingParsed;
/** extraMapping 类型安全解析 */
@TableField(exist = false)
@JsonIgnore
private transient ExtraMappingConfig extraMappingParsed;
public List<FieldMappingItem> getFieldMappingParsed() {
if (fieldMappingParsed == null && StringUtils.hasText(this.fieldMapping)) {
this.fieldMappingParsed = JSON.parseArray(this.fieldMapping, FieldMappingItem.class);
}
return fieldMappingParsed;
}
public void setFieldMappingParsed(List<FieldMappingItem> 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;

View File

@ -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;

View File

@ -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<String, ParamOverrideConfig> paramMappingParsed;
/** resultSave 类型安全解析 */
@TableField(exist = false)
@JsonIgnore
private transient List<ResultSaveField> resultSaveParsed;
public Map<String, ParamOverrideConfig> getParamMappingParsed() {
if (paramMappingParsed == null && StringUtils.hasText(this.paramMapping)) {
this.paramMappingParsed = JSON.parseObject(this.paramMapping,
new TypeReference<Map<String,ParamOverrideConfig>>(){}.getType());
}
return paramMappingParsed;
}
public void setParamMappingParsed(Map<String, ParamOverrideConfig> parsed) {
this.paramMappingParsed = parsed;
this.paramMapping = parsed == null ? null : JSON.toJSONString(parsed);
}
public List<ResultSaveField> getResultSaveParsed() {
if (resultSaveParsed == null && StringUtils.hasText(this.resultSave)) {
this.resultSaveParsed = JSON.parseArray(this.resultSave, ResultSaveField.class);
}
return resultSaveParsed;
}
public void setResultSaveParsed(List<ResultSaveField> parsed) {
this.resultSaveParsed = parsed;
this.resultSave = parsed == null ? null : JSON.toJSONString(parsed);
}
@ApiModelProperty("备注")
private String remark;
}

View File

@ -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;

View File

@ -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<RequestSchemaField> requestSchemaParsed;
/** responseSchema 类型安全解析 */
@TableField(exist = false)
@JsonIgnore
private transient List<ResponseSchemaField> responseSchemaParsed;
public List<RequestSchemaField> getRequestSchemaParsed() {
if (requestSchemaParsed == null && StringUtils.hasText(this.requestSchema)) {
this.requestSchemaParsed = JSON.parseArray(this.requestSchema, RequestSchemaField.class);
}
return requestSchemaParsed;
}
public void setRequestSchemaParsed(List<RequestSchemaField> parsed) {
this.requestSchemaParsed = parsed;
this.requestSchema = parsed == null ? null : JSON.toJSONString(parsed);
}
public List<ResponseSchemaField> getResponseSchemaParsed() {
if (responseSchemaParsed == null && StringUtils.hasText(this.responseSchema)) {
this.responseSchemaParsed = JSON.parseArray(this.responseSchema, ResponseSchemaField.class);
}
return responseSchemaParsed;
}
public void setResponseSchemaParsed(List<ResponseSchemaField> parsed) {
this.responseSchemaParsed = parsed;
this.responseSchema = parsed == null ? null : JSON.toJSONString(parsed);
}
@ApiModelProperty("排序")
private Integer sortOrder;

View File

@ -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;

View File

@ -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<String, Object> resolveParams(PlatformInterface iface, PlatformFlowStep step, FlowContext ctx) {
Map<String, Object> result = new LinkedHashMap<>();
if (!StringUtils.hasText(iface.getRequestSchema())) {
List<RequestSchemaField> schema = iface.getRequestSchemaParsed();
if (schema == null || schema.isEmpty()) {
return result;
}
// 合并接口默认 schema 和步骤覆盖 mapping
JSONArray schema = JSON.parseArray(iface.getRequestSchema());
Map<String, Object> overrides = StringUtils.hasText(step.getParamMapping())
? JSON.parseObject(step.getParamMapping(), Map.class)
: Collections.emptyMap();
Map<String, ParamOverrideConfig> 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<String, Object> ov = (Map<String, Object>) 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<PlatformDeviceSubMapping> 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<String> resolveGeneratedFieldNames(PlatformInterface iface, PlatformFlowStep step) {
LinkedHashMap<String, String> 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<RequestSchemaField> 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<String, ParamOverrideConfig> overrides = step.getParamMappingParsed();
if (overrides != null) {
for (Map.Entry<String, ParamOverrideConfig> 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<String> 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<String> 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());
OnboardConfig currentConfig = resolveCurrentOnboardConfig(ctx);
if (currentConfig != null) {
List<String> ids = new ArrayList<>();
String sensorId = config.getString("sensorId");
String refSensorId = config.getString("refSensorId");
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;
} catch (Exception ignored) {}
}
// 回退到从 extra_mapping 解析(直连设备等场景)
return collectSubDeviceSensorBindings(ctx.getDeviceMapping()).stream()
@ -567,7 +583,13 @@ public class PlatformFlowExecutor {
List<PlatformDeviceSubMapping> suffix = collectCascadedSubDeviceSuffix(ctx);
Collections.reverse(suffix);
List<String> 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<List<String>> 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");
OnboardConfig currentConfig = resolveCurrentOnboardConfig(ctx);
if (currentConfig != null) {
String sensorId = currentConfig.getSensorId();
String refSensorId = currentConfig.getRefSensorId();
if (StringUtils.hasText(sensorId) && StringUtils.hasText(refSensorId)) {
List<List<String>> pairs = new ArrayList<>();
pairs.add(Arrays.asList(refSensorId, sensorId));
return pairs;
}
} catch (Exception ignored) {}
}
// 回退:从 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();
}
try {
JSONObject config = JSON.parseObject(onboardConfig);
return normalizeText(config.getString(field));
} catch (Exception ignored) {
return null;
} 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();
}
}
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<String, SubDeviceSensorBinding> 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<ExtraMappingConfig.ArrayFieldConfig> 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<ExtraMappingConfig.SensorIdMapping> 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<String, SubDeviceSensorBinding> bindings,
JSONArray mappings, String referenceSerialNumber) {
List<ExtraMappingConfig.SubDeviceMapping> 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<String, Object> params, String path) {
Iterator<Map.Entry<String, Object>> it = params.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> 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<ResponseSchemaField> 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<ResultSaveField> 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());
}
}

View File

@ -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<ItemFieldMapping> itemFieldMappings;
/** 批量推送数组字段子设备映射配置 */
private List<ArrayFieldConfig> 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<SubDeviceMapping> 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<SensorIdMapping> sensorIdMappings;
}
@Data
public static class SensorIdMapping implements Serializable {
private String serialNumber;
private String sensorId;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
* <p>
* 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;
}

View File

@ -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<RequestSchemaField> itemSchema;
/** type=array 时的数组模式:single/reference_pair */
private String arrayMode;
}

View File

@ -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;
}

View File

@ -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;
}