diff --git a/docs/knowledge-base/06-platform-integration-design.md b/docs/knowledge-base/06-platform-integration-design.md index 8f055f9b..e957971c 100644 --- a/docs/knowledge-base/06-platform-integration-design.md +++ b/docs/knowledge-base/06-platform-integration-design.md @@ -120,11 +120,27 @@ DDL 以 `sql/platform-integration.sql` 为准。 | `auth` | 从 `platform_instance.auth_config` 按 `field` 取值 | | `extra` | 从 `platform_instance.extra_config` 按 `field` 取值 | | `context` | 从 `FlowContext` 按 `contextKey` 取值 | -| `device` | 从 `PlatformDeviceMapping` 取 `platform_device_id`、`platform_point_id`、`serial_number`、`gateway_serial_number`、`monitor_serial_number` | -| `onboard_config` | 从当前子映射 `onboard_config` 取值 | +| `device` | 从 `PlatformDeviceMapping` / 当前网关子设备上下文取 `platform_device_id`、`platform_point_id`、`serial_number`、`device_name`、`gateway_serial_number`、`monitor_serial_number` | +| `thingsModel` | 从流程上下文 payload 按物模型标识取值,通常用于手动流程或扩展流程 | +| `ts` | 从流程上下文 payload 取时间戳 | +| `onboard_config` | 入网参数,直接取接口 `request_schema` 或步骤覆盖配置中的 `value`,不再读取子映射表 `onboard_config` | | `subDeviceSensorIds` | 收集子设备 sensorId 列表 | | `referenceSubDeviceSensorIdPairs` | 收集 `[基准点sensorId, 监测点sensorId]` 配对列表,可能展开为多次请求 | +`onboard_config` 是设备入网专用的可配置入参。接口管理页或流程步骤覆盖页直接维护 `value`: + +```json +{"name":"sensorDepths","type":"array","source":"onboard_config","value":[1.7,3.4,5.1,6.8]} +{"name":"holeDepth","type":"number","source":"onboard_config","value":[1.7,3.4,5.1,6.8]} +``` + +网关多 monitor 入网时,`PlatformDeviceMappingServiceImpl` 按 `PlatformDeviceSubMappingMapper.selectByParentId` 返回顺序筛选 monitor,并把当前下标写入 `FlowContext.currentMonitorIndex`。流程执行器解析 `onboard_config` 时: + +- `value` 不是数组:每个子设备使用同一个值。 +- `value` 是数组:按当前 monitor 下标取对应元素。 +- schema `type=array` 且取到单值:最终请求体会包装为单元素数组,例如 `1.7` -> `[1.7]`。 +- 数组长度小于待入网 monitor 数量:直接抛错,避免错绑。 + 实时推送适配器中支持的 `source`: | source | 含义 | @@ -149,7 +165,7 @@ DDL 以 `sql/platform-integration.sql` 为准。 | `platform_id` | 所属平台 | | `name` / `code` | 流程名称与标识 | | `flow_type` | `1=平台初始化`、`2=设备入网`、`3=Token刷新` | -| `sensor_type` | 入网流程适用传感器类型,如 `inclino`、`stress`、`hyeto`、`static_level` | +| `sensor_type` | 入网流程适用传感器类型,如 `inclino`、`deep_inclino`、`stress`、`hyeto`、`static_level` | | `enable` | 是否启用 | ### 3.4 `platform_flow_step` 流程步骤 @@ -185,7 +201,7 @@ DDL 以 `sql/platform-integration.sql` 为准。 | `device_id` / `serial_number` | FastBee 设备 ID 与序列号 | | `platform_device_id` | 平台侧 deviceId,直连设备入网后回填 | | `platform_point_id` | 平台侧 pointId,直连设备入网后回填 | -| `sensor_type` | `inclino`、`stress`、`hyeto`、`static_level` | +| `sensor_type` | 传感器类型,来自 `platform_sensor_type` 字典;当前包含 `inclino`、`deep_inclino`、`stress`、`hyeto`、`static_level` | | `device_type` | `0=直连设备`、`1=网关设备` | | `push_interface_id` | 推送接口 ID,用于动态字段映射和推送路径 | | `field_mapping` | 标量字段映射,格式如 `[{identifier, platformField, transform}]` | @@ -206,7 +222,7 @@ DDL 以 `sql/platform-integration.sql` 为准。 | `device_role` | `reference=基准点`、`monitor=监测点` | | `platform_device_id` | 平台分配给该监测点的 deviceId | | `platform_point_id` | 平台分配给该监测点的 pointId | -| `onboard_config` | 入网参数 JSON,例如 `sensorId`、`refSensorId`、`refSerialNumber` | +| `onboard_config` | 子设备平台标识 JSON,例如 `sensorId`、`refSensorId`、`refSerialNumber`;普通绑定接口参数不放这里 | | `net_status` | 子设备入网状态 | 唯一约束:`parent_mapping_id + sub_serial_number`。 @@ -326,8 +342,10 @@ execute(flow, context) 2. 筛选 `device_role=monitor` 且未入网的子设备。 3. 对每个 monitor 创建独立 `FlowContext`: - `currentMonitorSerialNumber = monitor.sub_serial_number` + - `currentMonitorDeviceName = monitor.sub_device_name` + - `currentMonitorIndex = monitor 在待入网 monitor 列表中的下标` - `subMappingOnboardConfig = monitor.onboard_config` -4. 执行同一个入网流程。 +4. 执行同一个入网流程。若绑定接口 schema 使用 `source=onboard_config` 且 `value` 为数组,会按 `currentMonitorIndex` 拆出当前子设备的值。 5. 从上下文 `step1.id`、`step2.id` 取 pointId/deviceId,写回当前 monitor 子表。 6. 所有 monitor 都入网后,主绑定 `net_status=1`。 7. 若部分成功部分失败,已成功子设备会保留结果,接口返回明确失败信息。 @@ -355,19 +373,11 @@ WHERE s.sub_serial_number = #{serialNumber} AND m.enable = '1' ### 5.4 DCP 普通推送 -DCP 推送接口优先使用设备绑定的 `push_interface_id`: +DCP 实时推送必须在设备绑定中配置 `push_interface_id`: -- 有推送接口:使用接口 `path`、`code`、`request_schema`。 -- 未配置或解析失败:按 `sensor_type` 回退到内置路径。 - -内置路径: - -| sensor_type | 路径 | -|---|---| -| `inclino` | `/api/app/inclino-data/many` | -| `stress` | `/api/app/stress-data/many` | -| `hyeto` | `/api/app/hyeto-data/many` | -| `static_level` | `/api/app/static-level-data/many` | +- 使用推送接口的 `path`、`code`、`request_schema` 组装并发送请求。 +- 未配置推送接口时,`DcpPlatformAdapter.resolvePushPath` 直接抛错,不再按 `sensor_type` 回退到内置路径。 +- `resolveInterfaceCode` 仍会在接口 `code` 为空时使用 `dcp_push_{sensorType}` 作为日志标识兜底。 请求体构建规则: @@ -468,6 +478,7 @@ Redis TTL 为 `reportIntervalSec * 2`,读取时超过 `reportIntervalSec * 1.5 - 按平台维护接口。 - 配置 method、path、content-type、请求 schema、响应 schema。 - 请求 schema 用于流程执行和设备绑定页动态字段映射。 +- `source=onboard_config` 的取值配置编辑 `value`,支持填写 JSON 数组或数字/字符串。保存时前端会把 `[1.7,3.4]` 转成数组,把 `6.8` 转成数字。 ### 6.3 流程编排 @@ -481,6 +492,7 @@ Redis TTL 为 `reportIntervalSec * 2`,读取时超过 `reportIntervalSec * 1.5 - 新增、编辑流程及步骤。 - 查看流程详情时返回 `{flow, steps}`。 - 手动执行:`POST /platform/flow/{id}/execute`,可选 `mappingId`。 +- 步骤入参覆盖同样支持 `source=onboard_config`,语义与接口管理一致:配置 `value`,不配置子映射字段名。 ### 6.4 设备绑定 @@ -557,6 +569,47 @@ SET @dcp_platform_id = (SELECT id FROM platform_instance WHERE code = 'dcp' LIMI - 静力水准推送接口定义了数组字段 `sensorData`,其 `itemSchema` 包含 `epoch`、`height`、`temperature`、`sensorId`。 - 轴力计和雨量计 DCP 文档要求累计值递增;当前代码未在适配器中实现“查询上次成功值并跳过非递增值”的通用校验,若目标平台严格校验,需要在推送前补充该逻辑或依赖平台返回失败日志。 +### 7.4 深部位移计扩展配置 + +`sql/platform-integration.sql` 已在 `platform_sensor_type` 字典中维护 `deep_inclino`,前端传感器类型下拉也支持从字典读取和手动输入。`sql/platform-dcp-init-data.sql` 当前未内置深部位移计绑定接口和入网流程,需要在接口管理和流程编排中配置。 + +DCP 深部位移绑定接口: + +| 项 | 值 | +|---|---| +| code 建议 | `dcp_bind_deep_inclino` | +| method | `POST` | +| path | `/api/app/deep-inclino-device` | +| content-type | `application/json` | + +典型 `request_schema`: + +```json +[ + {"name":"projectId","type":"string","source":"extra","field":"projectId","required":true}, + {"name":"pointId","type":"string","source":"context","contextKey":"step1.id","required":true}, + {"name":"name","type":"string","source":"device","field":"serial_number","required":true}, + {"name":"moduleId","type":"string","source":"uuid","required":true}, + {"name":"virtualId","type":"string","source":"uuid","required":true}, + {"name":"sensorDepths","type":"array","source":"onboard_config","value":[1.7,3.4,5.1,6.8],"required":true}, + {"name":"holeDepth","type":"number","source":"onboard_config","value":[1.7,3.4,5.1,6.8],"required":true}, + {"name":"sensorIds","type":"array","source":"subDeviceSensorIds","required":true}, + {"name":"axisDirection","type":"string","source":"onboard_config","value":"X","required":false}, + {"name":"holeElevation","type":"number","source":"onboard_config","value":0,"required":false}, + {"name":"zipDuring","type":"number","source":"onboard_config","value":0,"required":false}, + {"name":"frequency","type":"number","source":"onboard_config","value":2147483647,"required":false}, + {"name":"remark","type":"string","source":"onboard_config","value":"","required":false} +] +``` + +网关多子设备绑定深部位移计时,`sensorDepths` 与 `holeDepth` 这类数组值按待入网 monitor 子设备顺序逐个拆分: + +- 第 1 个 monitor:`sensorDepths` 取 `1.7`,因字段类型为 `array`,最终请求为 `"sensorDepths":[1.7]`;`holeDepth` 为 `1.7`。 +- 第 2 个 monitor:`sensorDepths` 为 `[3.4]`,`holeDepth` 为 `3.4`。 +- 依此类推。 + +深度类字段按工程习惯使用正数,表示距孔口向下的距离;若需要平台侧高程,按 `holeElevation - sensorDepth` 计算,不通过负深度表达方向。 + --- ## 8. 关键复用点 @@ -577,6 +630,7 @@ SET @dcp_platform_id = (SELECT id FROM platform_instance WHERE code = 'dcp' LIMI - 当前适配器只有 DCP。新增平台需要新增 adapter,并在 `DeviceReportTrigger.dispatchPush` 中按平台 `code` 路由。 - 文档中的“可视化配置”已覆盖平台、接口、流程、设备绑定、子映射和日志,但平台适配器路由仍需要代码扩展。 +- DCP 实时推送必须配置 `push_interface_id`,未配置时不会按传感器类型自动回退路径。 - 实时推送只支持 POST JSON,流程执行支持 GET、form、JSON。 - `idempotency_key` 字段已建表,但实时推送未生成幂等键。 - `PlatformInstanceController.getInfo` 只清空 `access_token`,注释提到密码脱敏,但当前代码未改写 `auth_config` 内密码。 diff --git a/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformPushLogController.java b/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformPushLogController.java index bb675122..8c24e08c 100644 --- a/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformPushLogController.java +++ b/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformPushLogController.java @@ -50,4 +50,12 @@ public class PlatformPushLogController extends BaseController { public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(pushLogService.removeByIds(Arrays.asList(ids))); } + + @PreAuthorize("@ss.hasPermi('platform:pushLog:remove')") + @DeleteMapping("/clean") + @ApiOperation("批量删除推送日志") + public AjaxResult clean(@PathVariable Long[] ids) { + pushLogService.cleanPushLog(); + return success(); + } } 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 4d4f6b1c..50c3f2b7 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 @@ -816,7 +816,7 @@ public class DcpPlatformAdapter { } private Object resolveDeviceField(PlatformDeviceMapping mapping, String field) { - if (!StringUtils.hasText(field)) { + if (mapping == null || !StringUtils.hasText(field)) { return null; } switch (field) { @@ -826,11 +826,22 @@ public class DcpPlatformAdapter { return mapping.getPlatformPointId(); case "serial_number": return mapping.getSerialNumber(); + case "device_name": + return resolveDeviceName(mapping); default: return null; } } + private String resolveDeviceName(PlatformDeviceMapping mapping) { + if (mapping == null) { + return null; + } + return StringUtils.hasText(mapping.getDeviceName()) + ? mapping.getDeviceName() + : mapping.getSerialNumber(); + } + private Object resolveJsonField(String json, String field) { if (!StringUtils.hasText(json) || !StringUtils.hasText(field)) { return null; 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 ffa72167..d6098a85 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 @@ -33,6 +33,9 @@ public class PlatformDeviceMapping implements Serializable { @ApiModelProperty("FastBee设备序列号") private String serialNumber; + @ApiModelProperty("FastBee设备名称") + private String deviceName; + @ApiModelProperty("DCP侧deviceId,入网后自动回填") private String platformDeviceId; 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 697225c5..dc0a538f 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 @@ -30,6 +30,9 @@ public class PlatformDeviceSubMapping implements Serializable { @ApiModelProperty("子设备序列号") private String subSerialNumber; + @ApiModelProperty("子设备名称") + private String subDeviceName; + @ApiModelProperty("角色: reference=基准 monitor=监测") private String deviceRole; diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/FlowContext.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/FlowContext.java index 4dd879af..087a25a2 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/FlowContext.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/flow/FlowContext.java @@ -11,19 +11,50 @@ import java.util.Map; */ public class FlowContext { + /** + * 平台实体 + */ private final PlatformInstance platform; + /** + * 绑定推送关系 + */ private final PlatformDeviceMapping deviceMapping; - /** 设备上报的原始 payload(实时推送时有值) */ + /** + * 设备上报的原始 payload(实时推送时有值) + */ private final String payload; - /** 步骤出参存储,key = step.resultKey,value = 解析后的 JSON Map */ + /** + * 步骤出参存储,key = step.resultKey,value = 解析后的 JSON Map + */ private final Map stepResults = new HashMap<>(); - /** 网关入网时,指定当前处理的监测点序列号 */ + /** + * 网关入网时,指定当前处理的监测点序列号 + */ private String currentMonitorSerialNumber; - /** 网关子设备入网时,当前子映射行的 onboard_config JSON(平台相关参数) */ + /** + * 网关入网时,指定当前处理的监测点名称 + */ + private String currentMonitorDeviceName; + /** + * 网关入网时,当前监测点在 PlatformDeviceSubMapping 监测点列表中的顺序 + */ + private Integer currentMonitorIndex; + + /** + * 网关子设备入网时,当前子映射行的 onboard_config JSON(平台相关参数) + */ private String subMappingOnboardConfig; - /** 网关子设备入网时,当前基准点序列号 */ + /** + * 网关子设备入网时,当前基准点序列号 + */ private String currentReferenceSerialNumber; - /** 网关子设备入网时,基准点子映射行的 onboard_config JSON */ + /** + * 网关子设备入网时,当前基准点名称 + */ + private String currentReferenceDeviceName; + /** + * 网关子设备入网时,基准点子映射行的 onboard_config JSON + */ private String referenceSubMappingOnboardConfig; public FlowContext(PlatformInstance platform, PlatformDeviceMapping deviceMapping, String payload) { @@ -60,6 +91,22 @@ public class FlowContext { this.currentMonitorSerialNumber = currentMonitorSerialNumber; } + public String getCurrentMonitorDeviceName() { + return currentMonitorDeviceName; + } + + public void setCurrentMonitorDeviceName(String currentMonitorDeviceName) { + this.currentMonitorDeviceName = currentMonitorDeviceName; + } + + public Integer getCurrentMonitorIndex() { + return currentMonitorIndex; + } + + public void setCurrentMonitorIndex(Integer currentMonitorIndex) { + this.currentMonitorIndex = currentMonitorIndex; + } + public String getSubMappingOnboardConfig() { return subMappingOnboardConfig; } @@ -76,6 +123,14 @@ public class FlowContext { this.currentReferenceSerialNumber = currentReferenceSerialNumber; } + public String getCurrentReferenceDeviceName() { + return currentReferenceDeviceName; + } + + public void setCurrentReferenceDeviceName(String currentReferenceDeviceName) { + this.currentReferenceDeviceName = currentReferenceDeviceName; + } + public String getReferenceSubMappingOnboardConfig() { return referenceSubMappingOnboardConfig; } 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 1887e701..9cba00fa 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 @@ -225,7 +225,7 @@ public class PlatformFlowExecutor { /** * 解析步骤入参 - * source 枚举: fixed / auth / extra / context / device + * source 枚举: fixed / auth / extra / context / device / thingsModel / ts */ @SuppressWarnings("unchecked") private Map resolveParams(PlatformInterface iface, PlatformFlowStep step, FlowContext ctx) { @@ -297,6 +297,9 @@ public class PlatformFlowExecutor { } return normalizeValueByType(fd.getString("type"), resolveGatewaySerialNumber(ctx.getDeviceMapping())); } + if ("device_name".equals(df)) { + return resolveDeviceName(ctx); + } if ("gateway_serial_number".equals(df)) { return resolveGatewaySerialNumber(ctx.getDeviceMapping()); } @@ -304,9 +307,12 @@ public class PlatformFlowExecutor { return ctx.getCurrentMonitorSerialNumber(); } return null; + case "thingsModel": + return resolveThingsModelValue(ctx, fd); + case "ts": + return resolveThingsModelTs(ctx, fd); case "onboard_config": - // 从子映射的 onboard_config JSON 中取指定字段 - return resolveOnboardConfigField(ctx, fd.getString("field")); + return resolveOnboardConfigValue(ctx, fd); case "subDeviceSensorIds": return collectSubDeviceSensorIds(ctx); case "referenceSubDeviceSensorIdPairs": @@ -316,19 +322,94 @@ public class PlatformFlowExecutor { } } - /** - * 从子映射 onboard_config 中取字段值 - */ - private Object resolveOnboardConfigField(FlowContext ctx, String field) { - if (!StringUtils.hasText(field) || !StringUtils.hasText(ctx.getSubMappingOnboardConfig())) { + private Object resolveThingsModelValue(FlowContext ctx, JSONObject fieldDef) { + JSONObject payload = parsePayloadToObject(ctx.getPayload()); + if (payload == null) { + return null; + } + String identifier = resolveThingsModelIdentifier(fieldDef); + return StringUtils.hasText(identifier) ? payload.get(identifier) : null; + } + + private Object resolveThingsModelTs(FlowContext ctx, JSONObject fieldDef) { + JSONObject payload = parsePayloadToObject(ctx.getPayload()); + if (payload == null) { + return null; + } + String identifier = resolveThingsModelIdentifier(fieldDef); + if (StringUtils.hasText(identifier)) { + Object value = payload.get(identifier + "__ts"); + if (value != null) { + return value; + } + } + Object ts = payload.get("ts"); + return ts != null ? ts : payload.get("timestamp"); + } + + private String resolveThingsModelIdentifier(JSONObject fieldDef) { + String identifier = fieldDef.getString("identifier"); + if (StringUtils.hasText(identifier)) { + return identifier; + } + String field = fieldDef.getString("field"); + return StringUtils.hasText(field) ? field : fieldDef.getString("contextKey"); + } + + private JSONObject parsePayloadToObject(String payload) { + if (!StringUtils.hasText(payload)) { return null; } try { - JSONObject config = JSON.parseObject(ctx.getSubMappingOnboardConfig()); - return config.get(field); + String trimmed = payload.trim(); + if (trimmed.startsWith("{")) { + return JSON.parseObject(trimmed); + } + if (trimmed.startsWith("[")) { + JSONObject result = new JSONObject(); + JSONArray arr = JSON.parseArray(trimmed); + for (int i = 0; i < arr.size(); i++) { + JSONObject item = arr.getJSONObject(i); + if (item == null) { + continue; + } + String id = item.getString("identifier"); + if (!StringUtils.hasText(id)) { + id = item.getString("id"); + } + if (!StringUtils.hasText(id)) { + continue; + } + result.put(id, item.get("value")); + Object ts = item.get("ts"); + if (ts != null) { + result.put(id + "__ts", ts); + } + } + return result; + } } catch (Exception e) { - return null; + log.warn("[PlatformFlow] 解析设备上报 payload 失败: {}", e.getMessage()); } + return null; + } + + private Object resolveOnboardConfigValue(FlowContext ctx, JSONObject fieldDef) { + String name = fieldDef.getString("name"); + if (!fieldDef.containsKey("value")) { + throw new RuntimeException("入网参数 " + name + " 未配置值"); + } + Object value = fieldDef.get("value"); + if (value instanceof JSONArray && ctx.getCurrentMonitorIndex() != null) { + JSONArray values = (JSONArray) value; + int index = ctx.getCurrentMonitorIndex(); + if (index >= values.size()) { + throw new RuntimeException("入网参数 " + name + " 数组长度不足,当前子设备序号=" + + (index + 1) + ",数组长度=" + values.size()); + } + return values.get(index); + } + return value; } private String newGeneratedId() { @@ -679,6 +760,9 @@ public class PlatformFlowExecutor { } private String resolveGatewaySerialNumber(PlatformDeviceMapping mapping) { + if (mapping == null) { + return null; + } String serialNumber = normalizeText(mapping.getSerialNumber()); if (StringUtils.hasText(serialNumber)) { return serialNumber; @@ -696,6 +780,21 @@ public class PlatformFlowExecutor { } } + private String resolveDeviceName(FlowContext ctx) { + if (ctx == null || ctx.getDeviceMapping() == null) { + return null; + } + if (StringUtils.hasText(ctx.getCurrentMonitorSerialNumber())) { + return StringUtils.hasText(ctx.getCurrentMonitorDeviceName()) + ? ctx.getCurrentMonitorDeviceName() + : ctx.getCurrentMonitorSerialNumber(); + } + PlatformDeviceMapping mapping = ctx.getDeviceMapping(); + return StringUtils.hasText(mapping.getDeviceName()) + ? mapping.getDeviceName() + : mapping.getSerialNumber(); + } + private boolean sameText(String left, String right) { return Objects.equals(normalizeText(left), normalizeText(right)); } @@ -1093,7 +1192,9 @@ public class PlatformFlowExecutor { return value; } if (!(value instanceof String)) { - return value; + JSONArray array = new JSONArray(); + array.add(value); + return array; } String text = ((String) value).trim(); if (!StringUtils.hasText(text)) { diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/mapper/PlatformPushLogMapper.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/mapper/PlatformPushLogMapper.java index 5755ece1..eb8109a3 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/mapper/PlatformPushLogMapper.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/mapper/PlatformPushLogMapper.java @@ -11,4 +11,7 @@ public interface PlatformPushLogMapper extends BaseMapperX { */ String selectLastSuccessResponseBody(@Param("deviceId") Long deviceId, @Param("interfaceCode") String interfaceCode); + + void cleanPushLog(); + } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformPushLogService.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformPushLogService.java index 6eb0d253..13a0cbaf 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformPushLogService.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformPushLogService.java @@ -17,4 +17,10 @@ public interface IPlatformPushLogService extends IService { /** 手动重推:重新发送已记录的请求,更新日志状态 */ void retry(Long id); + + /** + * 情况日志 + */ + void cleanPushLog(); + } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java index 9975ab8c..32ca6171 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java @@ -125,12 +125,16 @@ public class PlatformDeviceMappingServiceImpl extends ServiceImpl failedMessages = new ArrayList<>(); - for (PlatformDeviceSubMapping monitor : monitors) { + for (int i = 0; i < monitors.size(); i++) { + PlatformDeviceSubMapping monitor = monitors.get(i); FlowContext ctx = new FlowContext(platform, mapping, null); ctx.setCurrentMonitorSerialNumber(monitor.getSubSerialNumber()); + ctx.setCurrentMonitorDeviceName(monitor.getSubDeviceName()); + ctx.setCurrentMonitorIndex(i); ctx.setSubMappingOnboardConfig(monitor.getOnboardConfig()); if (reference != null) { ctx.setCurrentReferenceSerialNumber(reference.getSubSerialNumber()); + ctx.setCurrentReferenceDeviceName(reference.getSubDeviceName()); ctx.setReferenceSubMappingOnboardConfig(reference.getOnboardConfig()); } @@ -173,6 +177,12 @@ public class PlatformDeviceMappingServiceImpl extends ServiceImpl subMappings) { List references = subMappings.stream() diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformPushLogServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformPushLogServiceImpl.java index 89bb03be..48890077 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformPushLogServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformPushLogServiceImpl.java @@ -38,6 +38,7 @@ public class PlatformPushLogServiceImpl extends ServiceImpl + diff --git a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/platform/PlatformPushLogMapper.xml b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/platform/PlatformPushLogMapper.xml index 891c0e8a..1d4c128f 100644 --- a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/platform/PlatformPushLogMapper.xml +++ b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/platform/PlatformPushLogMapper.xml @@ -32,4 +32,7 @@ LIMIT 1 + + truncate table platform_push_log + diff --git a/sql/platform-integration.sql b/sql/platform-integration.sql index 5e025831..8799fa2c 100644 --- a/sql/platform-integration.sql +++ b/sql/platform-integration.sql @@ -119,6 +119,7 @@ CREATE TABLE IF NOT EXISTS `platform_device_mapping` ( `platform_id` bigint(20) NOT NULL COMMENT '所属平台ID', `device_id` bigint(20) NOT NULL COMMENT 'FastBee设备ID', `serial_number` varchar(64) NOT NULL COMMENT 'FastBee设备序列号', + `device_name` varchar(64) DEFAULT NULL COMMENT 'FastBee设备名称', `platform_device_id` varchar(100) DEFAULT NULL COMMENT 'DCP侧deviceId,入网后自动回填', `platform_point_id` varchar(100) DEFAULT NULL COMMENT 'DCP侧pointId,入网后自动回填', `sensor_type` varchar(50) NOT NULL COMMENT '传感器类型,来自字典 platform_sensor_type', @@ -145,6 +146,10 @@ ALTER TABLE `platform_device_mapping` ADD COLUMN IF NOT EXISTS `device_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '设备类型: 0=直连设备 1=网关设备(多子设备)' AFTER `sensor_type`; +ALTER TABLE `platform_device_mapping` + ADD COLUMN IF NOT EXISTS `device_name` varchar(64) DEFAULT NULL + COMMENT 'FastBee设备名称' AFTER `serial_number`; + -- ---------------------------- -- 5.2 子设备映射(网关多子设备通用) -- ---------------------------- @@ -152,6 +157,7 @@ CREATE TABLE IF NOT EXISTS `platform_device_sub_mapping` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `parent_mapping_id` bigint(20) NOT NULL COMMENT '父映射ID (platform_device_mapping.id)', `sub_serial_number` varchar(64) NOT NULL COMMENT '子设备序列号', + `sub_device_name` varchar(64) DEFAULT NULL COMMENT '子设备名称', `device_role` varchar(20) NOT NULL DEFAULT 'monitor' COMMENT '角色: reference=基准 monitor=监测', `platform_device_id` varchar(100) DEFAULT NULL COMMENT '平台分配的设备ID (入网后回填)', `platform_point_id` varchar(100) DEFAULT NULL COMMENT '平台分配的测点/辅助ID (入网后回填)', @@ -165,6 +171,10 @@ CREATE TABLE IF NOT EXISTS `platform_device_sub_mapping` ( UNIQUE KEY `uk_parent_sub` (`parent_mapping_id`, `sub_serial_number`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='平台设备子映射(网关多子设备通用)'; +ALTER TABLE `platform_device_sub_mapping` + ADD COLUMN IF NOT EXISTS `sub_device_name` varchar(64) DEFAULT NULL + COMMENT '子设备名称' AFTER `sub_serial_number`; + -- ---------------------------- -- 6. 推送日志 -- ---------------------------- diff --git a/yack-web/src/api/platform/pushLog.js b/yack-web/src/api/platform/pushLog.js index dd7ef0a7..8ceb5f4f 100644 --- a/yack-web/src/api/platform/pushLog.js +++ b/yack-web/src/api/platform/pushLog.js @@ -15,3 +15,11 @@ export function retryPlatformPushLog(id) { export function delPlatformPushLog(ids) { return request({ url: `/platform/push-log/${ids}`, method: 'delete' }) } + +// 清空推送日志 +export function cleanPushLog() { + return request({ + url: '/platform/push-log/clean', + method: 'delete' + }) +} diff --git a/yack-web/src/views/platform/components/InterfaceDrawer.vue b/yack-web/src/views/platform/components/InterfaceDrawer.vue index 67bf8ea3..a98f5ecd 100644 --- a/yack-web/src/views/platform/components/InterfaceDrawer.vue +++ b/yack-web/src/views/platform/components/InterfaceDrawer.vue @@ -156,8 +156,9 @@ + - + 每次请求自动生成UUID 自动取设备绑定页子设备映射中的 sensorId 列表 按基准点与每个监测点成对拆分请求 @@ -388,7 +389,12 @@ export default { if (!json) return [] try { const arr = JSON.parse(json) - return arr.map(f => ({ ...f, itemSchema: f.itemSchema || [], required: !!f.required })) + return arr.map(f => ({ + ...f, + value: f.source === 'onboard_config' ? this.stringifySchemaValue(f.value) : f.value, + itemSchema: f.itemSchema || [], + required: !!f.required + })) } catch (e) { return [] } }, handleDelete(row) { @@ -474,7 +480,8 @@ export default { }) } if (r.source === 'fixed') field.value = r.value - if (r.source === 'auth' || r.source === 'extra' || r.source === 'device' || r.source === 'onboard_config') field.field = r.field + if (r.source === 'onboard_config') field.value = this.parseSchemaValue(r.value) + if (r.source === 'auth' || r.source === 'extra' || r.source === 'device') field.field = r.field if (r.source === 'context') field.contextKey = r.contextKey return field })) @@ -490,6 +497,27 @@ export default { this.getList() }).finally(() => { this.submitLoading = false }) }) + }, + parseSchemaValue(value) { + if (typeof value !== 'string') return value + const text = value.trim() + if (!text) return '' + if ((text.startsWith('[') && text.endsWith(']')) || (text.startsWith('{') && text.endsWith('}'))) { + return JSON.parse(text) + } + if (/^-?\d+(\.\d+)?$/.test(text)) { + return Number(text) + } + if (text === 'true' || text === 'false') { + return text === 'true' + } + return value + }, + stringifySchemaValue(value) { + if (Array.isArray(value) || (value && typeof value === 'object')) { + return JSON.stringify(value) + } + return value == null ? '' : String(value) } } } diff --git a/yack-web/src/views/platform/device-mapping/index.vue b/yack-web/src/views/platform/device-mapping/index.vue index 6ce02e6f..0f7c1d7f 100644 --- a/yack-web/src/views/platform/device-mapping/index.vue +++ b/yack-web/src/views/platform/device-mapping/index.vue @@ -43,6 +43,7 @@
+ + + +