|
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMode
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
|
|
|
|
+import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
|
|
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
|
|
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
|
|
import jakarta.annotation.Resource;
|
|
import jakarta.annotation.Resource;
|
|
@@ -17,19 +18,44 @@ import org.springframework.stereotype.Service;
|
|
import java.util.*;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * IoT 超级表服务实现类,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。
|
|
|
|
+ */
|
|
@Service
|
|
@Service
|
|
@Slf4j
|
|
@Slf4j
|
|
-public class IotDbStructureDataServiceImpl implements IotDbStructureDataService {
|
|
|
|
|
|
+public class IotSuperTableServiceImpl implements IotSuperTableService {
|
|
|
|
|
|
@Resource
|
|
@Resource
|
|
- private IotTdEngineService iotTdEngineService;
|
|
|
|
|
|
+ private TdEngineSuperTableService tdEngineSuperTableService;
|
|
|
|
|
|
@Value("${spring.datasource.dynamic.datasource.tdengine.url}")
|
|
@Value("${spring.datasource.dynamic.datasource.tdengine.url}")
|
|
private String url;
|
|
private String url;
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
|
|
|
|
- // 1. 解析物模型,获得字段列表
|
|
|
|
|
|
+ public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
|
|
|
|
+ ThingModelRespVO thingModel = buildThingModel(product, functionList);
|
|
|
|
+
|
|
|
|
+ if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) {
|
|
|
|
+ log.warn("物模型属性列表为空,不创建超级表");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey());
|
|
|
|
+ String databaseName = getDatabaseName();
|
|
|
|
+ Integer tableExists = tdEngineSuperTableService.checkSuperTableExists(new TdTableDO(databaseName, superTableName));
|
|
|
|
+
|
|
|
|
+ if (tableExists != null && tableExists > 0) {
|
|
|
|
+ updateSuperTable(thingModel, product.getDeviceType());
|
|
|
|
+ } else {
|
|
|
|
+ createSuperTable(thingModel, product.getDeviceType());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 创建超级表
|
|
|
|
+ */
|
|
|
|
+ private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
|
|
|
|
+ // 解析物模型,获取字段列表
|
|
List<TdFieldDO> schemaFields = new ArrayList<>();
|
|
List<TdFieldDO> schemaFields = new ArrayList<>();
|
|
schemaFields.add(TdFieldDO.builder()
|
|
schemaFields.add(TdFieldDO.builder()
|
|
.fieldName("time")
|
|
.fieldName("time")
|
|
@@ -37,80 +63,102 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
|
|
.build());
|
|
.build());
|
|
schemaFields.addAll(FieldParser.parse(thingModel));
|
|
schemaFields.addAll(FieldParser.parse(thingModel));
|
|
|
|
|
|
- // 3. 设置超级表的标签
|
|
|
|
- List<TdFieldDO> tagsFields = Arrays.asList(
|
|
|
|
|
|
+ // 设置超级表的标签
|
|
|
|
+ List<TdFieldDO> tagsFields = List.of(
|
|
TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(),
|
|
TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(),
|
|
TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(),
|
|
TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(),
|
|
TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(),
|
|
TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(),
|
|
TdFieldDO.builder().fieldName("device_type").dataType("INT").build()
|
|
TdFieldDO.builder().fieldName("device_type").dataType("INT").build()
|
|
);
|
|
);
|
|
|
|
|
|
- // 4. 获取超级表的名称
|
|
|
|
- String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
|
|
|
|
|
|
+ // 获取超级表的名称和数据库名称
|
|
|
|
+ String superTableName = getSuperTableName(deviceType, thingModel.getProductKey());
|
|
|
|
+ String databaseName = getDatabaseName();
|
|
|
|
|
|
- // 5. 创建超级表
|
|
|
|
- String dataBaseName = getDatabaseName();
|
|
|
|
- iotTdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
|
|
|
|
|
|
+ // 创建超级表
|
|
|
|
+ tdEngineSuperTableService.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));
|
|
}
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
|
- public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 更新超级表
|
|
|
|
+ */
|
|
|
|
+ private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
|
|
|
|
+ String superTableName = getSuperTableName(deviceType, thingModel.getProductKey());
|
|
try {
|
|
try {
|
|
- String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
|
|
|
|
- List<TdFieldDO> oldFields = getTableFields(tbName);
|
|
|
|
|
|
+ List<TdFieldDO> oldFields = getTableFields(superTableName);
|
|
List<TdFieldDO> newFields = FieldParser.parse(thingModel);
|
|
List<TdFieldDO> newFields = FieldParser.parse(thingModel);
|
|
|
|
|
|
- updateTableFields(tbName, oldFields, newFields);
|
|
|
|
|
|
+ updateTableFields(superTableName, oldFields, newFields);
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
- log.error("更新物模型超级表失败", e);
|
|
|
|
|
|
+ log.error("更新物模型超级表失败: {}", e.getMessage(), e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // 获取表字段
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 获取表的字段信息
|
|
|
|
+ */
|
|
private List<TdFieldDO> getTableFields(String tableName) {
|
|
private List<TdFieldDO> getTableFields(String tableName) {
|
|
- List<TdFieldDO> fields = new ArrayList<>();
|
|
|
|
- // 获取超级表的描述信息
|
|
|
|
- List<Map<String, Object>> maps = iotTdEngineService.describeSuperTable(getDatabaseName(), tableName);
|
|
|
|
- if (maps != null) {
|
|
|
|
- // 过滤掉 note 字段为 TAG 的记录和 time 字段
|
|
|
|
- List<Map<String, Object>> filteredMaps = maps.stream()
|
|
|
|
- .filter(map -> !"TAG".equals(map.get("note")))
|
|
|
|
- .filter(map -> !"time".equals(map.get("field")))
|
|
|
|
- .toList();
|
|
|
|
- // 解析字段信息
|
|
|
|
- fields = FieldParser.parse(filteredMaps.stream()
|
|
|
|
- .map(map -> List.of(map.get("field"), map.get("type"), map.get("length")))
|
|
|
|
- .collect(Collectors.toList()));
|
|
|
|
|
|
+ List<Map<String, Object>> tableDescription = tdEngineSuperTableService.describeSuperTable(new TdTableDO(getDatabaseName(), tableName));
|
|
|
|
+ if (CollUtil.isEmpty(tableDescription)) {
|
|
|
|
+ return Collections.emptyList();
|
|
}
|
|
}
|
|
- return fields;
|
|
|
|
|
|
+
|
|
|
|
+ return tableDescription.stream()
|
|
|
|
+ .filter(map -> !"TAG".equals(map.get("note")))
|
|
|
|
+ .filter(map -> !"time".equals(map.get("field")))
|
|
|
|
+ .map(map -> TdFieldDO.builder()
|
|
|
|
+ .fieldName((String) map.get("field"))
|
|
|
|
+ .dataType((String) map.get("type"))
|
|
|
|
+ .dataLength((Integer) map.get("length"))
|
|
|
|
+ .build())
|
|
|
|
+ .collect(Collectors.toList());
|
|
}
|
|
}
|
|
|
|
|
|
- // 更新表字段
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 更新表的字段,包括新增、修改和删除字段
|
|
|
|
+ */
|
|
private void updateTableFields(String tableName, List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
private void updateTableFields(String tableName, List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
- // 获取新增字段
|
|
|
|
|
|
+ String databaseName = getDatabaseName();
|
|
|
|
+
|
|
|
|
+ // 获取新增、修改、删除的字段
|
|
List<TdFieldDO> addFields = getAddFields(oldFields, newFields);
|
|
List<TdFieldDO> addFields = getAddFields(oldFields, newFields);
|
|
- // 获取修改字段
|
|
|
|
List<TdFieldDO> modifyFields = getModifyFields(oldFields, newFields);
|
|
List<TdFieldDO> modifyFields = getModifyFields(oldFields, newFields);
|
|
- // 获取删除字段
|
|
|
|
List<TdFieldDO> dropFields = getDropFields(oldFields, newFields);
|
|
List<TdFieldDO> dropFields = getDropFields(oldFields, newFields);
|
|
|
|
|
|
- String dataBaseName = getDatabaseName();
|
|
|
|
// 添加新增字段
|
|
// 添加新增字段
|
|
if (CollUtil.isNotEmpty(addFields)) {
|
|
if (CollUtil.isNotEmpty(addFields)) {
|
|
- iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, addFields);
|
|
|
|
|
|
+ tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder()
|
|
|
|
+ .dataBaseName(databaseName)
|
|
|
|
+ .superTableName(tableName)
|
|
|
|
+ .columns(addFields)
|
|
|
|
+ .build());
|
|
}
|
|
}
|
|
// 删除旧字段
|
|
// 删除旧字段
|
|
if (CollUtil.isNotEmpty(dropFields)) {
|
|
if (CollUtil.isNotEmpty(dropFields)) {
|
|
- iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, dropFields);
|
|
|
|
|
|
+ tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder()
|
|
|
|
+ .dataBaseName(databaseName)
|
|
|
|
+ .superTableName(tableName)
|
|
|
|
+ .columns(dropFields)
|
|
|
|
+ .build());
|
|
}
|
|
}
|
|
// 修改字段(先删除再添加)
|
|
// 修改字段(先删除再添加)
|
|
if (CollUtil.isNotEmpty(modifyFields)) {
|
|
if (CollUtil.isNotEmpty(modifyFields)) {
|
|
- iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, modifyFields);
|
|
|
|
- iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, modifyFields);
|
|
|
|
|
|
+ tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder()
|
|
|
|
+ .dataBaseName(databaseName)
|
|
|
|
+ .superTableName(tableName)
|
|
|
|
+ .columns(modifyFields)
|
|
|
|
+ .build());
|
|
|
|
+ tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder()
|
|
|
|
+ .dataBaseName(databaseName)
|
|
|
|
+ .superTableName(tableName)
|
|
|
|
+ .columns(addFields)
|
|
|
|
+ .build());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // 获取新增字段
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 获取需要新增的字段
|
|
|
|
+ */
|
|
private List<TdFieldDO> getAddFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
private List<TdFieldDO> getAddFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
Set<String> oldFieldNames = oldFields.stream()
|
|
Set<String> oldFieldNames = oldFields.stream()
|
|
.map(TdFieldDO::getFieldName)
|
|
.map(TdFieldDO::getFieldName)
|
|
@@ -120,7 +168,9 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
}
|
|
}
|
|
|
|
|
|
- // 获取修改字段
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 获取需要修改的字段
|
|
|
|
+ */
|
|
private List<TdFieldDO> getModifyFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
private List<TdFieldDO> getModifyFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
Map<String, TdFieldDO> oldFieldMap = oldFields.stream()
|
|
Map<String, TdFieldDO> oldFieldMap = oldFields.stream()
|
|
.collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f));
|
|
.collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f));
|
|
@@ -128,78 +178,75 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
|
|
return newFields.stream()
|
|
return newFields.stream()
|
|
.filter(f -> {
|
|
.filter(f -> {
|
|
TdFieldDO oldField = oldFieldMap.get(f.getFieldName());
|
|
TdFieldDO oldField = oldFieldMap.get(f.getFieldName());
|
|
- return oldField != null &&
|
|
|
|
- (!oldField.getDataType().equals(f.getDataType()) ||
|
|
|
|
- !Objects.equals(oldField.getDataLength(), f.getDataLength()));
|
|
|
|
|
|
+ return oldField != null && (
|
|
|
|
+ !oldField.getDataType().equals(f.getDataType()) ||
|
|
|
|
+ !Objects.equals(oldField.getDataLength(), f.getDataLength())
|
|
|
|
+ );
|
|
})
|
|
})
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
}
|
|
}
|
|
|
|
|
|
- // 获取删除字段
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 获取需要删除的字段
|
|
|
|
+ */
|
|
private List<TdFieldDO> getDropFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
private List<TdFieldDO> getDropFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
|
|
Set<String> newFieldNames = newFields.stream()
|
|
Set<String> newFieldNames = newFields.stream()
|
|
.map(TdFieldDO::getFieldName)
|
|
.map(TdFieldDO::getFieldName)
|
|
.collect(Collectors.toSet());
|
|
.collect(Collectors.toSet());
|
|
return oldFields.stream()
|
|
return oldFields.stream()
|
|
- .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName()))
|
|
|
|
|
|
+ .filter(f -> !"time".equals(f.getFieldName()))
|
|
.filter(f -> !newFieldNames.contains(f.getFieldName()))
|
|
.filter(f -> !newFieldNames.contains(f.getFieldName()))
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
}
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
|
- public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
|
|
|
|
- ThingModelRespVO thingModel = buildThingModel(product, functionList);
|
|
|
|
-
|
|
|
|
- if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) {
|
|
|
|
- log.warn("物模型属性列表为空,不创建超级表");
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
|
|
|
|
- String dataBaseName = getDatabaseName();
|
|
|
|
- Integer tableExists = iotTdEngineService.checkSuperTableExists(dataBaseName, superTableName);
|
|
|
|
-
|
|
|
|
- if (tableExists != null && tableExists > 0) {
|
|
|
|
- updateSuperTable(thingModel, product.getDeviceType());
|
|
|
|
- } else {
|
|
|
|
- createSuperTable(thingModel, product.getDeviceType());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 构建物模型
|
|
|
|
+ */
|
|
private ThingModelRespVO buildThingModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
|
|
private ThingModelRespVO buildThingModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
|
|
ThingModelRespVO thingModel = new ThingModelRespVO();
|
|
ThingModelRespVO thingModel = new ThingModelRespVO();
|
|
thingModel.setId(product.getId());
|
|
thingModel.setId(product.getId());
|
|
thingModel.setProductKey(product.getProductKey());
|
|
thingModel.setProductKey(product.getProductKey());
|
|
|
|
|
|
- ThingModelRespVO.Model model = new ThingModelRespVO.Model();
|
|
|
|
List<ThingModelProperty> properties = functionList.stream()
|
|
List<ThingModelProperty> properties = functionList.stream()
|
|
.filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(
|
|
.filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(
|
|
IotProductFunctionTypeEnum.valueOfType(function.getType())))
|
|
IotProductFunctionTypeEnum.valueOfType(function.getType())))
|
|
.map(this::buildThingModelProperty)
|
|
.map(this::buildThingModelProperty)
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
+ ThingModelRespVO.Model model = new ThingModelRespVO.Model();
|
|
model.setProperties(properties);
|
|
model.setProperties(properties);
|
|
thingModel.setModel(model);
|
|
thingModel.setModel(model);
|
|
|
|
|
|
return thingModel;
|
|
return thingModel;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 构建物模型属性
|
|
|
|
+ */
|
|
private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) {
|
|
private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) {
|
|
ThingModelProperty property = BeanUtil.copyProperties(function, ThingModelProperty.class);
|
|
ThingModelProperty property = BeanUtil.copyProperties(function, ThingModelProperty.class);
|
|
property.setDataType(function.getProperty().getDataType());
|
|
property.setDataType(function.getProperty().getDataType());
|
|
return property;
|
|
return property;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 获取数据库名称
|
|
|
|
+ */
|
|
private String getDatabaseName() {
|
|
private String getDatabaseName() {
|
|
- return url.substring(url.lastIndexOf("/") + 1);
|
|
|
|
|
|
+ int index = url.lastIndexOf("/");
|
|
|
|
+ return index != -1 ? url.substring(index + 1) : url;
|
|
}
|
|
}
|
|
|
|
|
|
- static String getProductPropertySTableName(Integer deviceType, String productKey) {
|
|
|
|
- return switch (deviceType) {
|
|
|
|
- case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase();
|
|
|
|
- case 2 -> String.format("gateway_%s", productKey).toLowerCase();
|
|
|
|
- default -> String.format("device_%s", productKey).toLowerCase();
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 获取超级表名称
|
|
|
|
+ */
|
|
|
|
+ private String getSuperTableName(Integer deviceType, String productKey) {
|
|
|
|
+ String prefix = switch (deviceType) {
|
|
|
|
+ case 1 -> "gateway_sub_";
|
|
|
|
+ case 2 -> "gateway_";
|
|
|
|
+ default -> "device_";
|
|
};
|
|
};
|
|
|
|
+ return (prefix + productKey).toLowerCase();
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|