From 7b5aa23d5cdc55779ff3048d6cc2e04e82b24b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 26 Oct 2024 23:15:31 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E3=80=91=20=E4=BA=A7=E5=93=81=E5=8F=91=E5=B8=83=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=B6=85=E7=BA=A7=E8=A1=A8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/domain/FieldsVo.java | 26 +- .../dal/dataobject/tdengine/FieldParser.java | 44 +-- .../dal/dataobject/tdengine/TableData.java | 2 +- .../dal/dataobject/tdengine/TableManager.java | 26 +- .../tdengine/{TdField.java => TdFieldDO.java} | 10 +- .../dal/dataobject/tdengine/TdResponse.java | 18 +- .../dal/dataobject/tdengine/TdRestApi.java | 23 +- .../iot/dal/tdengine/TdEngineMapper.java | 87 ++++-- .../IotDbStructureDataServiceImpl.java | 282 +++++++++++------- .../iot/service/tdengine/TdEngineService.java | 75 ++--- .../service/tdengine/TdEngineServiceImpl.java | 44 ++- .../IotThinkModelFunctionServiceImpl.java | 2 + .../mapper/tdengine/TdEngineMapper.xml | 223 +++++++------- 13 files changed, 491 insertions(+), 371 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/{TdField.java => TdFieldDO.java} (68%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java index 07aede29d3..fc86a8f7ee 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java @@ -1,14 +1,18 @@ package cn.iocoder.yudao.module.iot.domain; +import lombok.Builder; import lombok.Data; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +/** + * 字段信息 VO + */ @Data +@Builder public class FieldsVo { - private static final long serialVersionUID = 1L; /** * 字段名称 @@ -24,24 +28,4 @@ public class FieldsVo { * 字段字节大小 */ private Integer size; - - public static FieldsVo fieldsTranscoding(Fields fields) throws SQLException { -// if (StringUtils.isBlank(fields.getFieldName()) || fields.getDataType() == null) { -// throw new SQLException("invalid operation: fieldName or dataType can not be null"); -// } -// FieldsVo fieldsVo = new FieldsVo(); -// fieldsVo.setFieldName(fields.getFieldName()); -// fieldsVo.setDataType(fields.getDataType().getDataType()); -// fieldsVo.setSize(fields.getSize()); -// return fieldsVo; - return null; - } - - public static List fieldsTranscoding(List fieldsList) throws SQLException { - List fieldsVoList = new ArrayList<>(); - for (Fields fields : fieldsList) { - fieldsVoList.add(fieldsTranscoding(fields)); - } - return fieldsVoList; - } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index e045e5ea3c..344d65c9a5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -10,7 +10,6 @@ import java.util.stream.Collectors; /** * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 - * */ public class FieldParser { @@ -35,49 +34,58 @@ public class FieldParser { * @param property 物模型属性 * @return TdField对象 */ - public static TdField parse(ThingModelProperty property) { + public static TdFieldDO parse(ThingModelProperty property) { String fieldName = property.getIdentifier().toLowerCase(); ThingModelDataType type = property.getDataType(); // 将物模型字段类型映射为td字段类型 String fType = TYPE_MAPPING.get(type.getType().toUpperCase()); - int len = -1; // 如果字段类型为NCHAR,默认长度为64 + int dataLength = 0; if ("NCHAR".equals(fType)) { - len = 64; + dataLength = 64; } - - return new TdField(fieldName, fType, len); + return new TdFieldDO(fieldName, fType, dataLength); } /** - * 获取物模型中的字段列表 + * 从物模型中获取字段列表 + * + * @param thingModel 物模型响应对象 + * @return 字段列表 */ - public static List parse(ThingModelRespVO thingModel) { - return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList()); + public static List parse(ThingModelRespVO thingModel) { + return thingModel.getModel().getProperties().stream() + .map(FieldParser::parse) + .collect(Collectors.toList()); } /** - * 将从库中查出来的字段信息转换为td字段对象 + * 将从库中查出来的字段信息转换为 TDengine 字段对象 + * + * @param rows 从库中查出的字段信息列表 + * @return 转换后的 TDengine 字段对象列表 */ - public static List parse(List> rows) { + public static List parse(List> rows) { return rows.stream().map(row -> { String type = row.get(1).toString().toUpperCase(); - return new TdField( + int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; + return new TdFieldDO( row.get(0).toString(), type, - type.equals("NCHAR") ? Integer.parseInt(row.get(2).toString()) : -1); + dataLength + ); }).collect(Collectors.toList()); } /** * 获取字段字义 */ - public static String getFieldDefine(TdField field) { - return "`" + field.getName() + "`" + " " - + (field.getLength() > 0 ? String.format("%s(%d)", field.getType(), field.getLength()) - : field.getType()); + public static String getFieldDefine(TdFieldDO field) { + return "`" + field.getFieldName() + "`" + " " + + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) + : field.getDataType()); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java index 5c3c585e7f..e9acbb7f0a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java @@ -36,4 +36,4 @@ public class TableData { * 超级表名称 */ private String superTableName; -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java index b2762f6ed3..65663d8053 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java @@ -45,7 +45,7 @@ public class TableManager { /** * 获取创建表sql */ - public static String getCreateSTableSql(String tbName, List fields, TdField... tags) { + public static String getCreateSTableSql(String tbName, List fields, TdFieldDO... tags) { if (fields.isEmpty()) { return null; } @@ -53,7 +53,7 @@ public class TableManager { // 生成字段片段 StringBuilder sbField = new StringBuilder("time TIMESTAMP,"); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbField.append(FieldParser.getFieldDefine(field)); sbField.append(","); } @@ -63,7 +63,7 @@ public class TableManager { // 生成tag StringBuilder sbTag = new StringBuilder(); - for (TdField tag : tags) { + for (TdFieldDO tag : tags) { sbTag.append(FieldParser.getFieldDefine(tag)) .append(","); } @@ -76,7 +76,7 @@ public class TableManager { /** * 获取创建普通表sql */ - public static String getCreateCTableSql(String tbName, List fields) { + public static String getCreateCTableSql(String tbName, List fields) { if (fields.size() == 0) { return null; } @@ -84,7 +84,7 @@ public class TableManager { //生成字段片段 StringBuilder sbField = new StringBuilder("time timestamp,"); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbField.append(FieldParser.getFieldDefine(field)); sbField.append(","); } @@ -116,9 +116,9 @@ public class TableManager { /** * 获取添加字段sql */ - public static String getAddSTableColumnSql(String tbName, List fields) { + public static String getAddSTableColumnSql(String tbName, List fields) { StringBuilder sbAdd = new StringBuilder(); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL, tbName, FieldParser.getFieldDefine(field) @@ -130,9 +130,9 @@ public class TableManager { /** * 获取修改字段sql */ - public static String getModifySTableColumnSql(String tbName, List fields) { + public static String getModifySTableColumnSql(String tbName, List fields) { StringBuilder sbModify = new StringBuilder(); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL, tbName, FieldParser.getFieldDefine(field) @@ -144,15 +144,15 @@ public class TableManager { /** * 获取删除字段sql */ - public static String getDropSTableColumnSql(String tbName, List fields) { + public static String getDropSTableColumnSql(String tbName, List fields) { StringBuilder sbDrop = new StringBuilder(); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL, tbName, - field.getName() + field.getFieldName() )); } return sbDrop.toString(); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java similarity index 68% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java index f040a017ad..d9cf28f1bb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,20 +11,21 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor -public class TdField { +@Builder +public class TdFieldDO { /** * 字段名称 */ - private String name; + private String fieldName; /** * 字段类型 */ - private String type; + private String dataType; /** * 字段长度 */ - private int length; + private Integer dataLength = 0; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java index 9353565be8..aca5cece5b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java @@ -17,13 +17,25 @@ public class TdResponse { public static final int CODE_SUCCESS = 0; public static final int CODE_TB_NOT_EXIST = 866; + /** + * 状态 + */ private String status; + /** + * 错误码 + */ private int code; + /** + * 错误信息 + */ private String desc; - //[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] - private List data; + /** + * 列信息 + * [["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] + */ + private List data; -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java index ef6b8b5b41..6653ece40e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java @@ -14,23 +14,23 @@ import org.springframework.stereotype.Service; @Service public class TdRestApi { - @Value("${spring.datasource.dynamic.datasource.master.url}") + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; - @Value("${spring.datasource.dynamic.datasource.master.username}") + @Value("${spring.datasource.dynamic.datasource.tdengine.username}") private String username; - @Value("${spring.datasource.dynamic.datasource.master.password}") + @Value("${spring.datasource.dynamic.datasource.tdengine.password}") private String password; + /** + * 获取 REST API URL + */ private String getRestApiUrl() { - //jdbc:TAOS-RS://127.0.0.1:6041/iotkit?xxxx - String restUrl = url.replace("jdbc:TAOS-RS://" , "") - .replaceAll("\\?.*" , ""); - // /rest/sql/iotkit + String restUrl = url.replace("jdbc:TAOS-RS://", "") + .replaceAll("\\?.*", ""); int idx = restUrl.lastIndexOf("/"); - //127.0.0.1:6041/rest/sql/iotkit - return String.format("%s/rest/sql/%s" , restUrl.substring(0, idx), restUrl.substring(idx + 1)); + return String.format("%s/rest/sql/%s", restUrl.substring(0, idx), restUrl.substring(idx + 1)); } @@ -48,11 +48,10 @@ public class TdRestApi { * 执行sql */ public TdResponse execSql(String sql) { - log.info("exec td sql:{}" , sql); + log.info("exec td sql:{}", sql); HttpRequest request = newApiRequest(sql); HttpResponse response = request.execute(); return JSONUtil.toBean(response.body(), TdResponse.class); } - -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java index 6a925f4e5a..85e324a77c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.domain.FieldsVo; import cn.iocoder.yudao.module.iot.domain.SelectDto; import cn.iocoder.yudao.module.iot.domain.TableDto; @@ -17,24 +18,80 @@ import java.util.Map; @DS("tdengine") public interface TdEngineMapper { + /** + * 创建数据库 + * + * @param dataBaseName 数据库名称 + */ + @InterceptorIgnore(tenantLine = "true") void createDatabase(@Param("dataBaseName") String dataBaseName); - void createSuperTable(@Param("schemaFields") List schemaFields, - @Param("tagsFields") List tagsFields, + /** + * 创建超级表 + * + * @param schemaFields schema字段 + * @param tagsFields tags字段 + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + void createSuperTable(@Param("schemaFields") List schemaFields, + @Param("tagsFields") List tagsFields, @Param("dataBaseName") String dataBaseName, @Param("superTableName") String superTableName); + /** + * 查看超级表 - 显示当前数据库下的所有超级表信息 + * SQL:SHOW STABLES [LIKE tb_name_wildcard]; + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + List> showSuperTables(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName); + + /** + * 查看超级表 - 获取超级表的结构信息 + * SQL:DESCRIBE [db_name.]stb_name; + *

+ * * @param dataBaseName 数据库名称 + * * @param superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + List> describeSuperTable(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName); + + /** + * 为超级表添加列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param field 字段信息 + */ + @InterceptorIgnore(tenantLine = "true") + void addColumnForSuperTable(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName, + @Param("field") TdFieldDO field); + + /** + * 为超级表删除列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param field 字段信息 + */ + @InterceptorIgnore(tenantLine = "true") + void dropColumnForSuperTable(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName, + @Param("field") TdFieldDO field); + void createTable(TableDto tableDto); void insertData(TableDto tableDto); List> selectByTimestamp(SelectDto selectDto); - void addColumnForSuperTable(@Param("superTableName") String superTableName, - @Param("fieldsVo") FieldsVo fieldsVo); - - void dropColumnForSuperTable(@Param("superTableName") String superTableName, - @Param("fieldsVo") FieldsVo fieldsVo); void addTagForSuperTable(@Param("superTableName") String superTableName, @Param("fieldsVo") FieldsVo fieldsVo); @@ -44,14 +101,6 @@ public interface TdEngineMapper { Map getCountByTimestamp(SelectDto selectDto); - /** - * 检查表是否存在 - * - * @param dataBaseName 数据库名称 - * @param tableName 表名称 - */ - Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName); - Map getLastData(SelectDto selectDto); List> getHistoryData(SelectVisualDto selectVisualDto); @@ -62,13 +111,5 @@ public interface TdEngineMapper { List> getLastDataByTags(TagsSelectDao tagsSelectDao); - /** - * 创建超级表 - * - * @param sql sql - * @return 返回值 - */ - @InterceptorIgnore(tenantLine = "true") - Integer createSuperTableDevice(String sql); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java index f8dd6feb5d..d42fd72fc9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java @@ -1,19 +1,22 @@ package cn.iocoder.yudao.module.iot.service.tdengine; -import cn.hutool.json.JSONUtil; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; +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.TdRestApi; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -22,147 +25,206 @@ import java.util.stream.Collectors; public class IotDbStructureDataServiceImpl implements IotDbStructureDataService { @Resource - private TdEngineMapper tdEngineMapper; + private TdEngineService tdEngineService; @Resource private TdRestApi tdRestApi; + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") + private String url; + @Override public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - // 获取物模型中的属性定义 - List fields = FieldParser.parse(thingModel); - String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); + // 1. 解析物模型,获得字段列表 + List schemaFields = new ArrayList<>(); + schemaFields.add(TdFieldDO.builder(). + fieldName("time"). + dataType("TIMESTAMP"). + build()); + schemaFields.addAll(FieldParser.parse(thingModel)); - // 生成创建超级表的 SQL - String sql = TableManager.getCreateSTableSql(tbName, fields, new TdField("device_id", "NCHAR", 64)); - if (sql == null) { - log.warn("生成的 SQL 为空,无法创建超级表"); - return; - } - log.info("执行 SQL: {}", sql); + // 3. 设置超级表的标签 + List tagsFields = new ArrayList<>(); + tagsFields.add(TdFieldDO.builder(). + fieldName("product_key"). + dataType("NCHAR"). + dataLength(64). + build()); + tagsFields.add(TdFieldDO.builder(). + fieldName("device_key"). + dataType("NCHAR"). + dataLength(64). + build()); + tagsFields.add(TdFieldDO.builder(). + fieldName("device_name"). + dataType("NCHAR"). + dataLength(64). + build()); + // 年 + tagsFields.add(TdFieldDO.builder(). + fieldName("year"). + dataType("INT"). + build()); + // 月 + tagsFields.add(TdFieldDO.builder(). + fieldName("month"). + dataType("INT"). + build()); + // 日 + tagsFields.add(TdFieldDO.builder(). + fieldName("day"). + dataType("INT"). + build()); + // 时 + tagsFields.add(TdFieldDO.builder(). + fieldName("hour"). + dataType("INT"). + build()); - // 执行 SQL 创建超级表 - tdEngineMapper.createSuperTableDevice(sql); + + // 4. 获取超级表的名称 + String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); + + // 5. 创建超级表 + String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + tdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); } @Override public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { try { - // 获取旧字段信息 String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); - String sql = TableManager.getDescTableSql(tbName); - TdResponse response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("获取表描述错误: " + JSONUtil.toJsonStr(response)); - } + List oldFields = getTableFields(tbName); + List newFields = FieldParser.parse(thingModel); - List oldFields = FieldParser.parse(response.getData()); - List newFields = FieldParser.parse(thingModel); - - // 找出新增的字段 - List addFields = newFields.stream() - .filter(f -> oldFields.stream().noneMatch(old -> old.getName().equals(f.getName()))) - .collect(Collectors.toList()); - if (!addFields.isEmpty()) { - sql = TableManager.getAddSTableColumnSql(tbName, addFields); - response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("添加表字段错误: " + JSONUtil.toJsonStr(response)); - } - } - - // 找出修改的字段 - List modifyFields = newFields.stream() - .filter(f -> oldFields.stream().anyMatch(old -> - old.getName().equals(f.getName()) && - (!old.getType().equals(f.getType()) || old.getLength() != f.getLength()))) - .collect(Collectors.toList()); - if (!modifyFields.isEmpty()) { - sql = TableManager.getModifySTableColumnSql(tbName, modifyFields); - response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("修改表字段错误: " + JSONUtil.toJsonStr(response)); - } - } - - // 找出删除的字段 - List dropFields = oldFields.stream() - .filter(f -> !"time".equals(f.getName()) && !"device_id".equals(f.getName()) && - newFields.stream().noneMatch(n -> n.getName().equals(f.getName()))) - .collect(Collectors.toList()); - if (!dropFields.isEmpty()) { - sql = TableManager.getDropSTableColumnSql(tbName, dropFields); - response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("删除表字段错误: " + JSONUtil.toJsonStr(response)); - } - } + updateTableFields(tbName, oldFields, newFields); } catch (Throwable e) { log.error("更新物模型超级表失败", e); } } + // 获取表字段 + private List getTableFields(String tableName) { + List fields = new ArrayList<>(); + // 获取超级表的描述信息 + List> maps = tdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName); + if (maps != null) { + // 过滤掉 note 字段为 TAG 的记录 + maps = maps.stream().filter(map -> !"TAG".equals(map.get("note"))).toList(); + // 过滤掉 time 字段 + maps = maps.stream().filter(map -> !"time".equals(map.get("field"))).toList(); + // 解析字段信息 + fields = FieldParser.parse(maps.stream() + .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) + .collect(Collectors.toList())); + } + return fields; + } + + // 更新表字段 + private void updateTableFields(String tableName, List oldFields, List newFields) { + // 获取新增字段 + List addFields = getAddFields(oldFields, newFields); + // 获取修改字段 + List modifyFields = getModifyFields(oldFields, newFields); + // 获取删除字段 + List dropFields = getDropFields(oldFields, newFields); + + String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + // 添加新增字段 + if (CollUtil.isNotEmpty(addFields)) { + tdEngineService.addColumnForSuperTable(dataBaseName,tableName, addFields); + } + // 删除旧字段 + if (CollUtil.isNotEmpty(dropFields)) { + tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, dropFields); + } + // 修改字段(先删除再添加) + if (CollUtil.isNotEmpty(modifyFields)) { + tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, modifyFields); + tdEngineService.addColumnForSuperTable(dataBaseName,tableName, modifyFields); + } + } + + // 获取新增字段 + private List getAddFields(List oldFields, List newFields) { + return newFields.stream() + .filter(f -> oldFields.stream().noneMatch(old -> old.getFieldName().equals(f.getFieldName()))) + .collect(Collectors.toList()); + } + + // 获取修改字段 + private List getModifyFields(List oldFields, List newFields) { + return newFields.stream() + .filter(f -> oldFields.stream().anyMatch(old -> + old.getFieldName().equals(f.getFieldName()) && + (!old.getDataType().equals(f.getDataType()) || !Objects.equals(old.getDataLength(), f.getDataLength())))) + .collect(Collectors.toList()); + } + + // 获取删除字段 + private List getDropFields(List oldFields, List newFields) { + return oldFields.stream() + .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName()) && + newFields.stream().noneMatch(n -> n.getFieldName().equals(f.getFieldName()))) + .collect(Collectors.toList()); + } + @Override public void createSuperTableDataModel(IotProductDO product, List functionList) { - // 1. 生成 ThingModelRespVO - ThingModelRespVO thingModel = new ThingModelRespVO(); - thingModel.setId(product.getId()); - thingModel.setProductKey(product.getProductKey()); + ThingModelRespVO thingModel = buildThingModel(product, functionList); - // 1.1 设置属性、服务和事件 - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); - List properties = new ArrayList<>(); - - // 1.2 遍历功能列表并分类 - for (IotThinkModelFunctionDO function : functionList) { - if (Objects.requireNonNull(IotProductFunctionTypeEnum.valueOf(function.getType())) == IotProductFunctionTypeEnum.PROPERTY) { - ThingModelProperty property = new ThingModelProperty(); - property.setIdentifier(function.getIdentifier()); - property.setName(function.getName()); - property.setDescription(function.getDescription()); - property.setDataType(function.getProperty().getDataType()); - properties.add(property); - } - } - - // 1.3 判断属性列表是否为空 - if (properties.isEmpty()) { + if (thingModel.getModel().getProperties().isEmpty()) { log.warn("物模型属性列表为空,不创建超级表"); return; } - model.setProperties(properties); - thingModel.setModel(model); + String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); + String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + Integer tableExists = tdEngineService.checkSuperTableExists(dataBaseName, superTableName); - // 2. 判断是否已经创建,如果已经创建则进行更新 - String tbName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); - Integer iot = tdEngineMapper.checkTableExists("ruoyi_vue_pro", tbName); - if (iot != null && iot > 0) { - // 3. 更新 + if (tableExists != null && tableExists > 0) { updateSuperTable(thingModel, product.getDeviceType()); } else { - // 4. 创建 createSuperTable(thingModel, product.getDeviceType()); } } - /** - * 根据产品key获取产品属性超级表名 - */ - static String getProductPropertySTableName(Integer deviceType, String productKey) { - if (deviceType == 1) { - return String.format("gateway_sub_" + productKey).toLowerCase(); - } else if (deviceType == 2) { - return String.format("gateway_" + productKey).toLowerCase(); - } else { - return String.format("device_" + productKey).toLowerCase(); - } + private ThingModelRespVO buildThingModel(IotProductDO product, List functionList) { + ThingModelRespVO thingModel = new ThingModelRespVO(); + thingModel.setId(product.getId()); + thingModel.setProductKey(product.getProductKey()); + + ThingModelRespVO.Model model = new ThingModelRespVO.Model(); + List properties = functionList.stream() + .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(IotProductFunctionTypeEnum.valueOf(function.getType()))) + .map(this::buildThingModelProperty) + .collect(Collectors.toList()); + + model.setProperties(properties); + thingModel.setModel(model); + + return thingModel; } - /** - * 根据deviceId获取设备属性表名 - */ - static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { - return String.format(deviceType + "_" + productKey + "_" + deviceKey).toLowerCase(); + private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) { + ThingModelProperty property = new ThingModelProperty(); + property.setIdentifier(function.getIdentifier()); + property.setName(function.getName()); + property.setDescription(function.getDescription()); + property.setDataType(function.getProperty().getDataType()); + return property; } -} + + 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(); + }; + } + + static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { + return String.format("%s_%s_%s", deviceType, productKey, deviceKey).toLowerCase(); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java index b3ae6f8186..9319adbd77 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.service.tdengine; -import cn.iocoder.yudao.module.iot.domain.FieldsVo; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.domain.SelectDto; import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; @@ -27,12 +27,44 @@ public interface TdEngineService { * * @param schemaFields schema字段 * @param tagsFields tags字段 - * @param dataBaseName 数据库名称 * @param superTableName 超级表名称 - * @throws Exception 异常 */ - void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, - String superTableName) throws Exception; + void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName); + + /** + * 检查超级表是否存在 + */ + Integer checkSuperTableExists(String dataBaseName, String superTableName); + + + /** + * 获取超级表的结构信息 + */ + List> describeSuperTable(String dataBaseName, String superTableName); + + /** + * 为超级表添加列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param fieldsVo 字段信息 + */ + void addColumnForSuperTable(String dataBaseName,String superTableName, List fieldsVo); + + /** + * 为超级表删除列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param fieldsVo 字段信息 + */ + void dropColumnForSuperTable(String dataBaseName,String superTableName, List fieldsVo); + + /** + * 为超级表添加tag + */ + Long getCountByTimesTamp(SelectDto selectDto) throws Exception; + /** * 创建表 @@ -59,37 +91,6 @@ public interface TdEngineService { */ List> selectByTimesTamp(SelectDto selectDto) throws Exception; - /** - * 为超级表添加列 - * - * @param superTableName 超级表名称 - * @param fieldsVo 字段信息 - * @throws Exception 异常 - */ - void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; - - /** - * 为超级表删除列 - * - * @param superTableName 超级表名称 - * @param fieldsVo 字段信息 - * @throws Exception 异常 - */ - void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; - - /** - * 为超级表添加tag - */ - Long getCountByTimesTamp(SelectDto selectDto) throws Exception; - - /** - * 检查表是否存在 - * - * @return 1存在 0不存在 - * @throws Exception 异常 - */ - Integer checkTableExists(SelectDto selectDto) throws Exception; - /** * 初始化超级表 * @@ -138,4 +139,6 @@ public interface TdEngineService { * @return 数据 */ List> getAggregateData(SelectVisualDto selectVisualDto); + + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java index 70933617f7..d61b789222 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.module.iot.service.tdengine; -import cn.iocoder.yudao.module.iot.domain.FieldsVo; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; import cn.iocoder.yudao.module.iot.domain.SelectDto; import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -15,25 +17,27 @@ import java.util.Map; @Slf4j public class TdEngineServiceImpl implements TdEngineService { + @Resource + private TdEngineMapper tdEngineMapper; @Override public void createDateBase(String dataBaseName) { - + tdEngineMapper.createDatabase(dataBaseName); } @Override - public void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName) { - + public void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName) { + tdEngineMapper.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); } @Override public void createTable(TableDto tableDto) { - + tdEngineMapper.createTable(tableDto); } @Override public void insertData(TableDto tableDto) { - + tdEngineMapper.insertData(tableDto); } @Override @@ -42,13 +46,17 @@ public class TdEngineServiceImpl implements TdEngineService { } @Override - public void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { - + public void addColumnForSuperTable(String dataBaseName,String superTableName, List fields) { + for (TdFieldDO field : fields) { + tdEngineMapper.addColumnForSuperTable(dataBaseName,superTableName, field); + } } @Override - public void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { - + public void dropColumnForSuperTable(String dataBaseName,String superTableName, List fields) { + for (TdFieldDO field : fields) { + tdEngineMapper.dropColumnForSuperTable(dataBaseName,superTableName, field); + } } @Override @@ -56,11 +64,6 @@ public class TdEngineServiceImpl implements TdEngineService { return 0L; } - @Override - public Integer checkTableExists(SelectDto selectDto) { - return 0; - } - @Override public void initSTableFrame(String msg) { @@ -90,4 +93,15 @@ public class TdEngineServiceImpl implements TdEngineService { public List> getAggregateData(SelectVisualDto selectVisualDto) { return List.of(); } + + @Override + public Integer checkSuperTableExists(String dataBaseName, String superTableName) { + List> results = tdEngineMapper.showSuperTables(dataBaseName, superTableName); + return results == null || results.isEmpty() ? 0 : results.size(); + } + + @Override + public List> describeSuperTable(String dataBaseName, String superTableName) { + return tdEngineMapper.describeSuperTable(dataBaseName, superTableName); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index 1e356c6b7f..3f698999c7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -174,8 +174,10 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe public void createSuperTableDataModel(Long productId) { // 1. 查询产品 IotProductDO product = productService.getProduct(productId); + // 2. 查询产品的物模型功能列表 List functionList = thinkModelFunctionMapper.selectListByProductId(productId); + // 3. 生成 TDengine 的数据模型 dbStructureDataService.createSuperTableDataModel(product, functionList); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml index f722e0130e..f1d7481981 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml @@ -8,7 +8,7 @@ - create table if not exists #{dataBaseName}.#{superTableName} + CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName} @@ -16,46 +16,48 @@ - - timestamp + + TIMESTAMP - - tinyint + + TINYINT - - smallint + + SMALLINT - - int + + INT - - bigint + + BIGINT - - float + + FLOAT - - double + + DOUBLE - - binary + + BINARY - - nchar + + NCHAR - - bool + + BOOL - - json + + JSON - - (#{item.size}) + + ( + ${item.dataLength} + ) - tags + TAGS @@ -64,43 +66,45 @@ - - timestamp + + TIMESTAMP - - tinyint + + TINYINT - - smallint + + SMALLINT - - int + + INT - - bigint + + BIGINT - - float + + FLOAT - - double + + DOUBLE - - binary + + BINARY - - nchar + + NCHAR - - bool + + BOOL - - json + + JSON - - (#{item.size}) + + ( + ${item.dataLength} + ) @@ -135,7 +139,6 @@ - - ALTER - STABLE - #{superTableName} - ADD - COLUMN - - #{fieldsVo.fieldName} + ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN + + #{field.fieldName} - + - - timestamp + + TIMESTAMP - - tinyint + + TINYINT - - smallint + + SMALLINT - - int + + INT - - bigint + + BIGINT - - float + + FLOAT - - double + + DOUBLE - - binary + + BINARY - - nchar + + NCHAR - - bool + + BOOL - - json + + JSON - + ( - #{fieldsVo.size} + #{field.dataLength} ) - ALTER - STABLE - #{superTableName} - DROP - COLUMN - - #{fieldsVo.fieldName} + ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN + + #{field.fieldName} @@ -215,49 +210,49 @@ #{superTableName} ADD TAG - - #{fieldsVo.fieldName} + + #{fieldDO.fieldName} - + - + timestamp - + tinyint - + smallint - + int - + bigint - + float - + double - + binary - + nchar - + bool - + json - + ( - #{fieldsVo.size} + #{fieldDO.dataLength} ) @@ -279,11 +274,8 @@ FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime} - + SHOW ${dataBaseName}.STABLES LIKE '${superTableName}' + + - - - ${sql} - +