Commit 7b5aa23d5cdc55779ff3048d6cc2e04e82b24b3e

Authored by 安浩浩
1 parent ea8dd67e

【功能优化】 产品发布创建超级表优化

Showing 13 changed files with 486 additions and 366 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java
1 package cn.iocoder.yudao.module.iot.domain; 1 package cn.iocoder.yudao.module.iot.domain;
2 2
  3 +import lombok.Builder;
3 import lombok.Data; 4 import lombok.Data;
4 5
5 import java.sql.SQLException; 6 import java.sql.SQLException;
6 import java.util.ArrayList; 7 import java.util.ArrayList;
7 import java.util.List; 8 import java.util.List;
8 9
  10 +/**
  11 + * 字段信息 VO
  12 + */
9 @Data 13 @Data
  14 +@Builder
10 public class FieldsVo { 15 public class FieldsVo {
11 - private static final long serialVersionUID = 1L;  
12 16
13 /** 17 /**
14 * 字段名称 18 * 字段名称
@@ -24,24 +28,4 @@ public class FieldsVo { @@ -24,24 +28,4 @@ public class FieldsVo {
24 * 字段字节大小 28 * 字段字节大小
25 */ 29 */
26 private Integer size; 30 private Integer size;
27 -  
28 - public static FieldsVo fieldsTranscoding(Fields fields) throws SQLException {  
29 -// if (StringUtils.isBlank(fields.getFieldName()) || fields.getDataType() == null) {  
30 -// throw new SQLException("invalid operation: fieldName or dataType can not be null");  
31 -// }  
32 -// FieldsVo fieldsVo = new FieldsVo();  
33 -// fieldsVo.setFieldName(fields.getFieldName());  
34 -// fieldsVo.setDataType(fields.getDataType().getDataType());  
35 -// fieldsVo.setSize(fields.getSize());  
36 -// return fieldsVo;  
37 - return null;  
38 - }  
39 -  
40 - public static List<FieldsVo> fieldsTranscoding(List<Fields> fieldsList) throws SQLException {  
41 - List<FieldsVo> fieldsVoList = new ArrayList<>();  
42 - for (Fields fields : fieldsList) {  
43 - fieldsVoList.add(fieldsTranscoding(fields));  
44 - }  
45 - return fieldsVoList;  
46 - }  
47 } 31 }
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; @@ -10,7 +10,6 @@ import java.util.stream.Collectors;
10 10
11 /** 11 /**
12 * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 12 * FieldParser 类用于解析和转换物模型字段到 TDengine 字段
13 - *  
14 */ 13 */
15 public class FieldParser { 14 public class FieldParser {
16 15
@@ -35,49 +34,58 @@ public class FieldParser { @@ -35,49 +34,58 @@ public class FieldParser {
35 * @param property 物模型属性 34 * @param property 物模型属性
36 * @return TdField对象 35 * @return TdField对象
37 */ 36 */
38 - public static TdField parse(ThingModelProperty property) { 37 + public static TdFieldDO parse(ThingModelProperty property) {
39 String fieldName = property.getIdentifier().toLowerCase(); 38 String fieldName = property.getIdentifier().toLowerCase();
40 ThingModelDataType type = property.getDataType(); 39 ThingModelDataType type = property.getDataType();
41 40
42 // 将物模型字段类型映射为td字段类型 41 // 将物模型字段类型映射为td字段类型
43 String fType = TYPE_MAPPING.get(type.getType().toUpperCase()); 42 String fType = TYPE_MAPPING.get(type.getType().toUpperCase());
44 43
45 - int len = -1;  
46 // 如果字段类型为NCHAR,默认长度为64 44 // 如果字段类型为NCHAR,默认长度为64
  45 + int dataLength = 0;
47 if ("NCHAR".equals(fType)) { 46 if ("NCHAR".equals(fType)) {
48 - len = 64; 47 + dataLength = 64;
49 } 48 }
50 -  
51 - return new TdField(fieldName, fType, len); 49 + return new TdFieldDO(fieldName, fType, dataLength);
52 } 50 }
53 51
54 /** 52 /**
55 - * 获取物模型中的字段列表 53 + * 从物模型中获取字段列表
  54 + *
  55 + * @param thingModel 物模型响应对象
  56 + * @return 字段列表
56 */ 57 */
57 - public static List<TdField> parse(ThingModelRespVO thingModel) {  
58 - return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList()); 58 + public static List<TdFieldDO> parse(ThingModelRespVO thingModel) {
  59 + return thingModel.getModel().getProperties().stream()
  60 + .map(FieldParser::parse)
  61 + .collect(Collectors.toList());
59 } 62 }
60 63
61 /** 64 /**
62 - * 将从库中查出来的字段信息转换为td字段对象 65 + * 将从库中查出来的字段信息转换为 TDengine 字段对象
  66 + *
  67 + * @param rows 从库中查出的字段信息列表
  68 + * @return 转换后的 TDengine 字段对象列表
63 */ 69 */
64 - public static List<TdField> parse(List<List<Object>> rows) { 70 + public static List<TdFieldDO> parse(List<List<Object>> rows) {
65 return rows.stream().map(row -> { 71 return rows.stream().map(row -> {
66 String type = row.get(1).toString().toUpperCase(); 72 String type = row.get(1).toString().toUpperCase();
67 - return new TdField( 73 + int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1;
  74 + return new TdFieldDO(
68 row.get(0).toString(), 75 row.get(0).toString(),
69 type, 76 type,
70 - type.equals("NCHAR") ? Integer.parseInt(row.get(2).toString()) : -1); 77 + dataLength
  78 + );
71 }).collect(Collectors.toList()); 79 }).collect(Collectors.toList());
72 } 80 }
73 81
74 /** 82 /**
75 * 获取字段字义 83 * 获取字段字义
76 */ 84 */
77 - public static String getFieldDefine(TdField field) {  
78 - return "`" + field.getName() + "`" + " "  
79 - + (field.getLength() > 0 ? String.format("%s(%d)", field.getType(), field.getLength())  
80 - : field.getType()); 85 + public static String getFieldDefine(TdFieldDO field) {
  86 + return "`" + field.getFieldName() + "`" + " "
  87 + + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength())
  88 + : field.getDataType());
81 } 89 }
82 90
83 -} 91 -}
  92 +}
84 \ No newline at end of file 93 \ No newline at end of file
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 { @@ -36,4 +36,4 @@ public class TableData {
36 * 超级表名称 36 * 超级表名称
37 */ 37 */
38 private String superTableName; 38 private String superTableName;
39 -} 39 -}
  40 +}
40 \ No newline at end of file 41 \ No newline at end of file
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 { @@ -45,7 +45,7 @@ public class TableManager {
45 /** 45 /**
46 * 获取创建表sql 46 * 获取创建表sql
47 */ 47 */
48 - public static String getCreateSTableSql(String tbName, List<TdField> fields, TdField... tags) { 48 + public static String getCreateSTableSql(String tbName, List<TdFieldDO> fields, TdFieldDO... tags) {
49 if (fields.isEmpty()) { 49 if (fields.isEmpty()) {
50 return null; 50 return null;
51 } 51 }
@@ -53,7 +53,7 @@ public class TableManager { @@ -53,7 +53,7 @@ public class TableManager {
53 // 生成字段片段 53 // 生成字段片段
54 StringBuilder sbField = new StringBuilder("time TIMESTAMP,"); 54 StringBuilder sbField = new StringBuilder("time TIMESTAMP,");
55 55
56 - for (TdField field : fields) { 56 + for (TdFieldDO field : fields) {
57 sbField.append(FieldParser.getFieldDefine(field)); 57 sbField.append(FieldParser.getFieldDefine(field));
58 sbField.append(","); 58 sbField.append(",");
59 } 59 }
@@ -63,7 +63,7 @@ public class TableManager { @@ -63,7 +63,7 @@ public class TableManager {
63 63
64 // 生成tag 64 // 生成tag
65 StringBuilder sbTag = new StringBuilder(); 65 StringBuilder sbTag = new StringBuilder();
66 - for (TdField tag : tags) { 66 + for (TdFieldDO tag : tags) {
67 sbTag.append(FieldParser.getFieldDefine(tag)) 67 sbTag.append(FieldParser.getFieldDefine(tag))
68 .append(","); 68 .append(",");
69 } 69 }
@@ -76,7 +76,7 @@ public class TableManager { @@ -76,7 +76,7 @@ public class TableManager {
76 /** 76 /**
77 * 获取创建普通表sql 77 * 获取创建普通表sql
78 */ 78 */
79 - public static String getCreateCTableSql(String tbName, List<TdField> fields) { 79 + public static String getCreateCTableSql(String tbName, List<TdFieldDO> fields) {
80 if (fields.size() == 0) { 80 if (fields.size() == 0) {
81 return null; 81 return null;
82 } 82 }
@@ -84,7 +84,7 @@ public class TableManager { @@ -84,7 +84,7 @@ public class TableManager {
84 //生成字段片段 84 //生成字段片段
85 StringBuilder sbField = new StringBuilder("time timestamp,"); 85 StringBuilder sbField = new StringBuilder("time timestamp,");
86 86
87 - for (TdField field : fields) { 87 + for (TdFieldDO field : fields) {
88 sbField.append(FieldParser.getFieldDefine(field)); 88 sbField.append(FieldParser.getFieldDefine(field));
89 sbField.append(","); 89 sbField.append(",");
90 } 90 }
@@ -116,9 +116,9 @@ public class TableManager { @@ -116,9 +116,9 @@ public class TableManager {
116 /** 116 /**
117 * 获取添加字段sql 117 * 获取添加字段sql
118 */ 118 */
119 - public static String getAddSTableColumnSql(String tbName, List<TdField> fields) { 119 + public static String getAddSTableColumnSql(String tbName, List<TdFieldDO> fields) {
120 StringBuilder sbAdd = new StringBuilder(); 120 StringBuilder sbAdd = new StringBuilder();
121 - for (TdField field : fields) { 121 + for (TdFieldDO field : fields) {
122 sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL, 122 sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL,
123 tbName, 123 tbName,
124 FieldParser.getFieldDefine(field) 124 FieldParser.getFieldDefine(field)
@@ -130,9 +130,9 @@ public class TableManager { @@ -130,9 +130,9 @@ public class TableManager {
130 /** 130 /**
131 * 获取修改字段sql 131 * 获取修改字段sql
132 */ 132 */
133 - public static String getModifySTableColumnSql(String tbName, List<TdField> fields) { 133 + public static String getModifySTableColumnSql(String tbName, List<TdFieldDO> fields) {
134 StringBuilder sbModify = new StringBuilder(); 134 StringBuilder sbModify = new StringBuilder();
135 - for (TdField field : fields) { 135 + for (TdFieldDO field : fields) {
136 sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL, 136 sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL,
137 tbName, 137 tbName,
138 FieldParser.getFieldDefine(field) 138 FieldParser.getFieldDefine(field)
@@ -144,15 +144,15 @@ public class TableManager { @@ -144,15 +144,15 @@ public class TableManager {
144 /** 144 /**
145 * 获取删除字段sql 145 * 获取删除字段sql
146 */ 146 */
147 - public static String getDropSTableColumnSql(String tbName, List<TdField> fields) { 147 + public static String getDropSTableColumnSql(String tbName, List<TdFieldDO> fields) {
148 StringBuilder sbDrop = new StringBuilder(); 148 StringBuilder sbDrop = new StringBuilder();
149 - for (TdField field : fields) { 149 + for (TdFieldDO field : fields) {
150 sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL, 150 sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL,
151 tbName, 151 tbName,
152 - field.getName() 152 + field.getFieldName()
153 )); 153 ));
154 } 154 }
155 return sbDrop.toString(); 155 return sbDrop.toString();
156 } 156 }
157 157
158 -} 158 -}
  159 +}
159 \ No newline at end of file 160 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java
1 package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; 1 package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
2 2
3 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
  4 +import lombok.Builder;
4 import lombok.Data; 5 import lombok.Data;
5 import lombok.NoArgsConstructor; 6 import lombok.NoArgsConstructor;
6 7
@@ -10,20 +11,21 @@ import lombok.NoArgsConstructor; @@ -10,20 +11,21 @@ import lombok.NoArgsConstructor;
10 @Data 11 @Data
11 @NoArgsConstructor 12 @NoArgsConstructor
12 @AllArgsConstructor 13 @AllArgsConstructor
13 -public class TdField { 14 +@Builder
  15 +public class TdFieldDO {
14 16
15 /** 17 /**
16 * 字段名称 18 * 字段名称
17 */ 19 */
18 - private String name; 20 + private String fieldName;
19 21
20 /** 22 /**
21 * 字段类型 23 * 字段类型
22 */ 24 */
23 - private String type; 25 + private String dataType;
24 26
25 /** 27 /**
26 * 字段长度 28 * 字段长度
27 */ 29 */
28 - private int length; 30 + private Integer dataLength = 0;
29 } 31 }
30 \ No newline at end of file 32 \ No newline at end of file
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 { @@ -17,13 +17,25 @@ public class TdResponse {
17 public static final int CODE_SUCCESS = 0; 17 public static final int CODE_SUCCESS = 0;
18 public static final int CODE_TB_NOT_EXIST = 866; 18 public static final int CODE_TB_NOT_EXIST = 866;
19 19
  20 + /**
  21 + * 状态
  22 + */
20 private String status; 23 private String status;
21 24
  25 + /**
  26 + * 错误码
  27 + */
22 private int code; 28 private int code;
23 29
  30 + /**
  31 + * 错误信息
  32 + */
24 private String desc; 33 private String desc;
25 34
26 - //[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]]  
27 - private List<Object[]> data; 35 + /**
  36 + * 列信息
  37 + * [["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]]
  38 + */
  39 + private List data;
28 40
29 -} 41 -}
  42 +}
30 \ No newline at end of file 43 \ No newline at end of file
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; @@ -14,23 +14,23 @@ import org.springframework.stereotype.Service;
14 @Service 14 @Service
15 public class TdRestApi { 15 public class TdRestApi {
16 16
17 - @Value("${spring.datasource.dynamic.datasource.master.url}") 17 + @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
18 private String url; 18 private String url;
19 19
20 - @Value("${spring.datasource.dynamic.datasource.master.username}") 20 + @Value("${spring.datasource.dynamic.datasource.tdengine.username}")
21 private String username; 21 private String username;
22 22
23 - @Value("${spring.datasource.dynamic.datasource.master.password}") 23 + @Value("${spring.datasource.dynamic.datasource.tdengine.password}")
24 private String password; 24 private String password;
25 25
  26 + /**
  27 + * 获取 REST API URL
  28 + */
26 private String getRestApiUrl() { 29 private String getRestApiUrl() {
27 - //jdbc:TAOS-RS://127.0.0.1:6041/iotkit?xxxx  
28 - String restUrl = url.replace("jdbc:TAOS-RS://" , "")  
29 - .replaceAll("\\?.*" , "");  
30 - // /rest/sql/iotkit 30 + String restUrl = url.replace("jdbc:TAOS-RS://", "")
  31 + .replaceAll("\\?.*", "");
31 int idx = restUrl.lastIndexOf("/"); 32 int idx = restUrl.lastIndexOf("/");
32 - //127.0.0.1:6041/rest/sql/iotkit  
33 - return String.format("%s/rest/sql/%s" , restUrl.substring(0, idx), restUrl.substring(idx + 1)); 33 + return String.format("%s/rest/sql/%s", restUrl.substring(0, idx), restUrl.substring(idx + 1));
34 } 34 }
35 35
36 36
@@ -48,11 +48,10 @@ public class TdRestApi { @@ -48,11 +48,10 @@ public class TdRestApi {
48 * 执行sql 48 * 执行sql
49 */ 49 */
50 public TdResponse execSql(String sql) { 50 public TdResponse execSql(String sql) {
51 - log.info("exec td sql:{}" , sql); 51 + log.info("exec td sql:{}", sql);
52 HttpRequest request = newApiRequest(sql); 52 HttpRequest request = newApiRequest(sql);
53 HttpResponse response = request.execute(); 53 HttpResponse response = request.execute();
54 return JSONUtil.toBean(response.body(), TdResponse.class); 54 return JSONUtil.toBean(response.body(), TdResponse.class);
55 } 55 }
56 56
57 -  
58 -} 57 +}
59 \ No newline at end of file 58 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java
1 package cn.iocoder.yudao.module.iot.dal.tdengine; 1 package cn.iocoder.yudao.module.iot.dal.tdengine;
2 2
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
3 import cn.iocoder.yudao.module.iot.domain.FieldsVo; 4 import cn.iocoder.yudao.module.iot.domain.FieldsVo;
4 import cn.iocoder.yudao.module.iot.domain.SelectDto; 5 import cn.iocoder.yudao.module.iot.domain.SelectDto;
5 import cn.iocoder.yudao.module.iot.domain.TableDto; 6 import cn.iocoder.yudao.module.iot.domain.TableDto;
@@ -17,24 +18,80 @@ import java.util.Map; @@ -17,24 +18,80 @@ import java.util.Map;
17 @DS("tdengine") 18 @DS("tdengine")
18 public interface TdEngineMapper { 19 public interface TdEngineMapper {
19 20
  21 + /**
  22 + * 创建数据库
  23 + *
  24 + * @param dataBaseName 数据库名称
  25 + */
  26 + @InterceptorIgnore(tenantLine = "true")
20 void createDatabase(@Param("dataBaseName") String dataBaseName); 27 void createDatabase(@Param("dataBaseName") String dataBaseName);
21 28
22 - void createSuperTable(@Param("schemaFields") List<FieldsVo> schemaFields,  
23 - @Param("tagsFields") List<FieldsVo> tagsFields, 29 + /**
  30 + * 创建超级表
  31 + *
  32 + * @param schemaFields schema字段
  33 + * @param tagsFields tags字段
  34 + * @param dataBaseName 数据库名称
  35 + * @param superTableName 超级表名称
  36 + */
  37 + @InterceptorIgnore(tenantLine = "true")
  38 + void createSuperTable(@Param("schemaFields") List<TdFieldDO> schemaFields,
  39 + @Param("tagsFields") List<TdFieldDO> tagsFields,
24 @Param("dataBaseName") String dataBaseName, 40 @Param("dataBaseName") String dataBaseName,
25 @Param("superTableName") String superTableName); 41 @Param("superTableName") String superTableName);
26 42
  43 + /**
  44 + * 查看超级表 - 显示当前数据库下的所有超级表信息
  45 + * SQL:SHOW STABLES [LIKE tb_name_wildcard];
  46 + *
  47 + * @param dataBaseName 数据库名称
  48 + * @param superTableName 超级表名称
  49 + */
  50 + @InterceptorIgnore(tenantLine = "true")
  51 + List<Map<String, Object>> showSuperTables(@Param("dataBaseName") String dataBaseName,
  52 + @Param("superTableName") String superTableName);
  53 +
  54 + /**
  55 + * 查看超级表 - 获取超级表的结构信息
  56 + * SQL:DESCRIBE [db_name.]stb_name;
  57 + * <p>
  58 + * * @param dataBaseName 数据库名称
  59 + * * @param superTableName 超级表名称
  60 + */
  61 + @InterceptorIgnore(tenantLine = "true")
  62 + List<Map<String, Object>> describeSuperTable(@Param("dataBaseName") String dataBaseName,
  63 + @Param("superTableName") String superTableName);
  64 +
  65 + /**
  66 + * 为超级表添加列
  67 + *
  68 + * @param dataBaseName 数据库名称
  69 + * @param superTableName 超级表名称
  70 + * @param field 字段信息
  71 + */
  72 + @InterceptorIgnore(tenantLine = "true")
  73 + void addColumnForSuperTable(@Param("dataBaseName") String dataBaseName,
  74 + @Param("superTableName") String superTableName,
  75 + @Param("field") TdFieldDO field);
  76 +
  77 + /**
  78 + * 为超级表删除列
  79 + *
  80 + * @param dataBaseName 数据库名称
  81 + * @param superTableName 超级表名称
  82 + * @param field 字段信息
  83 + */
  84 + @InterceptorIgnore(tenantLine = "true")
  85 + void dropColumnForSuperTable(@Param("dataBaseName") String dataBaseName,
  86 + @Param("superTableName") String superTableName,
  87 + @Param("field") TdFieldDO field);
  88 +
27 void createTable(TableDto tableDto); 89 void createTable(TableDto tableDto);
28 90
29 void insertData(TableDto tableDto); 91 void insertData(TableDto tableDto);
30 92
31 List<Map<String, Object>> selectByTimestamp(SelectDto selectDto); 93 List<Map<String, Object>> selectByTimestamp(SelectDto selectDto);
32 94
33 - void addColumnForSuperTable(@Param("superTableName") String superTableName,  
34 - @Param("fieldsVo") FieldsVo fieldsVo);  
35 -  
36 - void dropColumnForSuperTable(@Param("superTableName") String superTableName,  
37 - @Param("fieldsVo") FieldsVo fieldsVo);  
38 95
39 void addTagForSuperTable(@Param("superTableName") String superTableName, 96 void addTagForSuperTable(@Param("superTableName") String superTableName,
40 @Param("fieldsVo") FieldsVo fieldsVo); 97 @Param("fieldsVo") FieldsVo fieldsVo);
@@ -44,14 +101,6 @@ public interface TdEngineMapper { @@ -44,14 +101,6 @@ public interface TdEngineMapper {
44 101
45 Map<String, Long> getCountByTimestamp(SelectDto selectDto); 102 Map<String, Long> getCountByTimestamp(SelectDto selectDto);
46 103
47 - /**  
48 - * 检查表是否存在  
49 - *  
50 - * @param dataBaseName 数据库名称  
51 - * @param tableName 表名称  
52 - */  
53 - Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName);  
54 -  
55 Map<String, Object> getLastData(SelectDto selectDto); 104 Map<String, Object> getLastData(SelectDto selectDto);
56 105
57 List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto); 106 List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
@@ -62,13 +111,5 @@ public interface TdEngineMapper { @@ -62,13 +111,5 @@ public interface TdEngineMapper {
62 111
63 List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao); 112 List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
64 113
65 - /**  
66 - * 创建超级表  
67 - *  
68 - * @param sql sql  
69 - * @return 返回值  
70 - */  
71 - @InterceptorIgnore(tenantLine = "true")  
72 - Integer createSuperTableDevice(String sql);  
73 114
74 } 115 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.tdengine; 1 package cn.iocoder.yudao.module.iot.service.tdengine;
2 2
3 -import cn.hutool.json.JSONUtil; 3 +import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; 4 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
5 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; 5 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; 7 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdRestApi;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; 10 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
9 -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;  
10 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; 11 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
11 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
12 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
  14 +import org.springframework.beans.factory.annotation.Value;
13 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
14 16
15 import java.util.ArrayList; 17 import java.util.ArrayList;
16 import java.util.List; 18 import java.util.List;
  19 +import java.util.Map;
17 import java.util.Objects; 20 import java.util.Objects;
18 import java.util.stream.Collectors; 21 import java.util.stream.Collectors;
19 22
@@ -22,147 +25,206 @@ import java.util.stream.Collectors; @@ -22,147 +25,206 @@ import java.util.stream.Collectors;
22 public class IotDbStructureDataServiceImpl implements IotDbStructureDataService { 25 public class IotDbStructureDataServiceImpl implements IotDbStructureDataService {
23 26
24 @Resource 27 @Resource
25 - private TdEngineMapper tdEngineMapper; 28 + private TdEngineService tdEngineService;
26 29
27 @Resource 30 @Resource
28 private TdRestApi tdRestApi; 31 private TdRestApi tdRestApi;
29 32
  33 + @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
  34 + private String url;
  35 +
30 @Override 36 @Override
31 public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { 37 public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
32 - // 获取物模型中的属性定义  
33 - List<TdField> fields = FieldParser.parse(thingModel);  
34 - String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());  
35 -  
36 - // 生成创建超级表的 SQL  
37 - String sql = TableManager.getCreateSTableSql(tbName, fields, new TdField("device_id", "NCHAR", 64));  
38 - if (sql == null) {  
39 - log.warn("生成的 SQL 为空,无法创建超级表");  
40 - return;  
41 - }  
42 - log.info("执行 SQL: {}", sql);  
43 -  
44 - // 执行 SQL 创建超级表  
45 - tdEngineMapper.createSuperTableDevice(sql); 38 + // 1. 解析物模型,获得字段列表
  39 + List<TdFieldDO> schemaFields = new ArrayList<>();
  40 + schemaFields.add(TdFieldDO.builder().
  41 + fieldName("time").
  42 + dataType("TIMESTAMP").
  43 + build());
  44 + schemaFields.addAll(FieldParser.parse(thingModel));
  45 +
  46 + // 3. 设置超级表的标签
  47 + List<TdFieldDO> tagsFields = new ArrayList<>();
  48 + tagsFields.add(TdFieldDO.builder().
  49 + fieldName("product_key").
  50 + dataType("NCHAR").
  51 + dataLength(64).
  52 + build());
  53 + tagsFields.add(TdFieldDO.builder().
  54 + fieldName("device_key").
  55 + dataType("NCHAR").
  56 + dataLength(64).
  57 + build());
  58 + tagsFields.add(TdFieldDO.builder().
  59 + fieldName("device_name").
  60 + dataType("NCHAR").
  61 + dataLength(64).
  62 + build());
  63 + // 年
  64 + tagsFields.add(TdFieldDO.builder().
  65 + fieldName("year").
  66 + dataType("INT").
  67 + build());
  68 + // 月
  69 + tagsFields.add(TdFieldDO.builder().
  70 + fieldName("month").
  71 + dataType("INT").
  72 + build());
  73 + // 日
  74 + tagsFields.add(TdFieldDO.builder().
  75 + fieldName("day").
  76 + dataType("INT").
  77 + build());
  78 + // 时
  79 + tagsFields.add(TdFieldDO.builder().
  80 + fieldName("hour").
  81 + dataType("INT").
  82 + build());
  83 +
  84 +
  85 + // 4. 获取超级表的名称
  86 + String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
  87 +
  88 + // 5. 创建超级表
  89 + String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
  90 + tdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
46 } 91 }
47 92
48 @Override 93 @Override
49 public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { 94 public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
50 try { 95 try {
51 - // 获取旧字段信息  
52 String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); 96 String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
53 - String sql = TableManager.getDescTableSql(tbName);  
54 - TdResponse response = tdRestApi.execSql(sql);  
55 - if (response.getCode() != TdResponse.CODE_SUCCESS) {  
56 - throw new RuntimeException("获取表描述错误: " + JSONUtil.toJsonStr(response));  
57 - }  
58 -  
59 - List<TdField> oldFields = FieldParser.parse(response.getData());  
60 - List<TdField> newFields = FieldParser.parse(thingModel);  
61 -  
62 - // 找出新增的字段  
63 - List<TdField> addFields = newFields.stream()  
64 - .filter(f -> oldFields.stream().noneMatch(old -> old.getName().equals(f.getName())))  
65 - .collect(Collectors.toList());  
66 - if (!addFields.isEmpty()) {  
67 - sql = TableManager.getAddSTableColumnSql(tbName, addFields);  
68 - response = tdRestApi.execSql(sql);  
69 - if (response.getCode() != TdResponse.CODE_SUCCESS) {  
70 - throw new RuntimeException("添加表字段错误: " + JSONUtil.toJsonStr(response));  
71 - }  
72 - }  
73 -  
74 - // 找出修改的字段  
75 - List<TdField> modifyFields = newFields.stream()  
76 - .filter(f -> oldFields.stream().anyMatch(old ->  
77 - old.getName().equals(f.getName()) &&  
78 - (!old.getType().equals(f.getType()) || old.getLength() != f.getLength())))  
79 - .collect(Collectors.toList());  
80 - if (!modifyFields.isEmpty()) {  
81 - sql = TableManager.getModifySTableColumnSql(tbName, modifyFields);  
82 - response = tdRestApi.execSql(sql);  
83 - if (response.getCode() != TdResponse.CODE_SUCCESS) {  
84 - throw new RuntimeException("修改表字段错误: " + JSONUtil.toJsonStr(response));  
85 - }  
86 - }  
87 -  
88 - // 找出删除的字段  
89 - List<TdField> dropFields = oldFields.stream()  
90 - .filter(f -> !"time".equals(f.getName()) && !"device_id".equals(f.getName()) &&  
91 - newFields.stream().noneMatch(n -> n.getName().equals(f.getName())))  
92 - .collect(Collectors.toList());  
93 - if (!dropFields.isEmpty()) {  
94 - sql = TableManager.getDropSTableColumnSql(tbName, dropFields);  
95 - response = tdRestApi.execSql(sql);  
96 - if (response.getCode() != TdResponse.CODE_SUCCESS) {  
97 - throw new RuntimeException("删除表字段错误: " + JSONUtil.toJsonStr(response));  
98 - }  
99 - } 97 + List<TdFieldDO> oldFields = getTableFields(tbName);
  98 + List<TdFieldDO> newFields = FieldParser.parse(thingModel);
  99 +
  100 + updateTableFields(tbName, oldFields, newFields);
100 } catch (Throwable e) { 101 } catch (Throwable e) {
101 log.error("更新物模型超级表失败", e); 102 log.error("更新物模型超级表失败", e);
102 } 103 }
103 } 104 }
104 105
105 - @Override  
106 - public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {  
107 - // 1. 生成 ThingModelRespVO  
108 - ThingModelRespVO thingModel = new ThingModelRespVO();  
109 - thingModel.setId(product.getId());  
110 - thingModel.setProductKey(product.getProductKey()); 106 + // 获取表字段
  107 + private List<TdFieldDO> getTableFields(String tableName) {
  108 + List<TdFieldDO> fields = new ArrayList<>();
  109 + // 获取超级表的描述信息
  110 + List<Map<String, Object>> maps = tdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName);
  111 + if (maps != null) {
  112 + // 过滤掉 note 字段为 TAG 的记录
  113 + maps = maps.stream().filter(map -> !"TAG".equals(map.get("note"))).toList();
  114 + // 过滤掉 time 字段
  115 + maps = maps.stream().filter(map -> !"time".equals(map.get("field"))).toList();
  116 + // 解析字段信息
  117 + fields = FieldParser.parse(maps.stream()
  118 + .map(map -> List.of(map.get("field"), map.get("type"), map.get("length")))
  119 + .collect(Collectors.toList()));
  120 + }
  121 + return fields;
  122 + }
111 123
112 - // 1.1 设置属性、服务和事件  
113 - ThingModelRespVO.Model model = new ThingModelRespVO.Model();  
114 - List<ThingModelProperty> properties = new ArrayList<>();  
115 -  
116 - // 1.2 遍历功能列表并分类  
117 - for (IotThinkModelFunctionDO function : functionList) {  
118 - if (Objects.requireNonNull(IotProductFunctionTypeEnum.valueOf(function.getType())) == IotProductFunctionTypeEnum.PROPERTY) {  
119 - ThingModelProperty property = new ThingModelProperty();  
120 - property.setIdentifier(function.getIdentifier());  
121 - property.setName(function.getName());  
122 - property.setDescription(function.getDescription());  
123 - property.setDataType(function.getProperty().getDataType());  
124 - properties.add(property);  
125 - } 124 + // 更新表字段
  125 + private void updateTableFields(String tableName, List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
  126 + // 获取新增字段
  127 + List<TdFieldDO> addFields = getAddFields(oldFields, newFields);
  128 + // 获取修改字段
  129 + List<TdFieldDO> modifyFields = getModifyFields(oldFields, newFields);
  130 + // 获取删除字段
  131 + List<TdFieldDO> dropFields = getDropFields(oldFields, newFields);
  132 +
  133 + String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
  134 + // 添加新增字段
  135 + if (CollUtil.isNotEmpty(addFields)) {
  136 + tdEngineService.addColumnForSuperTable(dataBaseName,tableName, addFields);
  137 + }
  138 + // 删除旧字段
  139 + if (CollUtil.isNotEmpty(dropFields)) {
  140 + tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, dropFields);
126 } 141 }
  142 + // 修改字段(先删除再添加)
  143 + if (CollUtil.isNotEmpty(modifyFields)) {
  144 + tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, modifyFields);
  145 + tdEngineService.addColumnForSuperTable(dataBaseName,tableName, modifyFields);
  146 + }
  147 + }
  148 +
  149 + // 获取新增字段
  150 + private List<TdFieldDO> getAddFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
  151 + return newFields.stream()
  152 + .filter(f -> oldFields.stream().noneMatch(old -> old.getFieldName().equals(f.getFieldName())))
  153 + .collect(Collectors.toList());
  154 + }
  155 +
  156 + // 获取修改字段
  157 + private List<TdFieldDO> getModifyFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
  158 + return newFields.stream()
  159 + .filter(f -> oldFields.stream().anyMatch(old ->
  160 + old.getFieldName().equals(f.getFieldName()) &&
  161 + (!old.getDataType().equals(f.getDataType()) || !Objects.equals(old.getDataLength(), f.getDataLength()))))
  162 + .collect(Collectors.toList());
  163 + }
  164 +
  165 + // 获取删除字段
  166 + private List<TdFieldDO> getDropFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
  167 + return oldFields.stream()
  168 + .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName()) &&
  169 + newFields.stream().noneMatch(n -> n.getFieldName().equals(f.getFieldName())))
  170 + .collect(Collectors.toList());
  171 + }
  172 +
  173 + @Override
  174 + public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
  175 + ThingModelRespVO thingModel = buildThingModel(product, functionList);
127 176
128 - // 1.3 判断属性列表是否为空  
129 - if (properties.isEmpty()) { 177 + if (thingModel.getModel().getProperties().isEmpty()) {
130 log.warn("物模型属性列表为空,不创建超级表"); 178 log.warn("物模型属性列表为空,不创建超级表");
131 return; 179 return;
132 } 180 }
133 181
134 - model.setProperties(properties);  
135 - thingModel.setModel(model); 182 + String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
  183 + String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
  184 + Integer tableExists = tdEngineService.checkSuperTableExists(dataBaseName, superTableName);
136 185
137 - // 2. 判断是否已经创建,如果已经创建则进行更新  
138 - String tbName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());  
139 - Integer iot = tdEngineMapper.checkTableExists("ruoyi_vue_pro", tbName);  
140 - if (iot != null && iot > 0) {  
141 - // 3. 更新 186 + if (tableExists != null && tableExists > 0) {
142 updateSuperTable(thingModel, product.getDeviceType()); 187 updateSuperTable(thingModel, product.getDeviceType());
143 } else { 188 } else {
144 - // 4. 创建  
145 createSuperTable(thingModel, product.getDeviceType()); 189 createSuperTable(thingModel, product.getDeviceType());
146 } 190 }
147 } 191 }
148 192
149 - /**  
150 - * 根据产品key获取产品属性超级表名  
151 - */ 193 + private ThingModelRespVO buildThingModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
  194 + ThingModelRespVO thingModel = new ThingModelRespVO();
  195 + thingModel.setId(product.getId());
  196 + thingModel.setProductKey(product.getProductKey());
  197 +
  198 + ThingModelRespVO.Model model = new ThingModelRespVO.Model();
  199 + List<ThingModelProperty> properties = functionList.stream()
  200 + .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(IotProductFunctionTypeEnum.valueOf(function.getType())))
  201 + .map(this::buildThingModelProperty)
  202 + .collect(Collectors.toList());
  203 +
  204 + model.setProperties(properties);
  205 + thingModel.setModel(model);
  206 +
  207 + return thingModel;
  208 + }
  209 +
  210 + private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) {
  211 + ThingModelProperty property = new ThingModelProperty();
  212 + property.setIdentifier(function.getIdentifier());
  213 + property.setName(function.getName());
  214 + property.setDescription(function.getDescription());
  215 + property.setDataType(function.getProperty().getDataType());
  216 + return property;
  217 + }
  218 +
152 static String getProductPropertySTableName(Integer deviceType, String productKey) { 219 static String getProductPropertySTableName(Integer deviceType, String productKey) {
153 - if (deviceType == 1) {  
154 - return String.format("gateway_sub_" + productKey).toLowerCase();  
155 - } else if (deviceType == 2) {  
156 - return String.format("gateway_" + productKey).toLowerCase();  
157 - } else {  
158 - return String.format("device_" + productKey).toLowerCase();  
159 - } 220 + return switch (deviceType) {
  221 + case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase();
  222 + case 2 -> String.format("gateway_%s", productKey).toLowerCase();
  223 + default -> String.format("device_%s", productKey).toLowerCase();
  224 + };
160 } 225 }
161 226
162 - /**  
163 - * 根据deviceId获取设备属性表名  
164 - */  
165 static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { 227 static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) {
166 - return String.format(deviceType + "_" + productKey + "_" + deviceKey).toLowerCase(); 228 + return String.format("%s_%s_%s", deviceType, productKey, deviceKey).toLowerCase();
167 } 229 }
168 -} 230 -}
  231 +}
169 \ No newline at end of file 232 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java
1 package cn.iocoder.yudao.module.iot.service.tdengine; 1 package cn.iocoder.yudao.module.iot.service.tdengine;
2 2
3 -import cn.iocoder.yudao.module.iot.domain.FieldsVo; 3 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
4 import cn.iocoder.yudao.module.iot.domain.SelectDto; 4 import cn.iocoder.yudao.module.iot.domain.SelectDto;
5 import cn.iocoder.yudao.module.iot.domain.TableDto; 5 import cn.iocoder.yudao.module.iot.domain.TableDto;
6 import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; 6 import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
@@ -27,68 +27,69 @@ public interface TdEngineService { @@ -27,68 +27,69 @@ public interface TdEngineService {
27 * 27 *
28 * @param schemaFields schema字段 28 * @param schemaFields schema字段
29 * @param tagsFields tags字段 29 * @param tagsFields tags字段
30 - * @param dataBaseName 数据库名称  
31 * @param superTableName 超级表名称 30 * @param superTableName 超级表名称
32 - * @throws Exception 异常  
33 */ 31 */
34 - void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName,  
35 - String superTableName) throws Exception; 32 + void createSuperTable(List<TdFieldDO> schemaFields, List<TdFieldDO> tagsFields, String dataBaseName, String superTableName);
36 33
37 /** 34 /**
38 - * 创建表  
39 - *  
40 - * @param tableDto 表信息  
41 - * @throws Exception 异常 35 + * 检查超级表是否存在
42 */ 36 */
43 - void createTable(TableDto tableDto) throws Exception; 37 + Integer checkSuperTableExists(String dataBaseName, String superTableName);
44 38
45 - /**  
46 - * 插入数据  
47 - *  
48 - * @param tableDto 表信息  
49 - * @throws Exception 异常  
50 - */  
51 - void insertData(TableDto tableDto) throws Exception;  
52 39
53 /** 40 /**
54 - * 根据时间戳查询数据  
55 - *  
56 - * @param selectDto 查询条件  
57 - * @return 数据  
58 - * @throws Exception 异常 41 + * 获取超级表的结构信息
59 */ 42 */
60 - List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) throws Exception; 43 + List<Map<String, Object>> describeSuperTable(String dataBaseName, String superTableName);
61 44
62 /** 45 /**
63 * 为超级表添加列 46 * 为超级表添加列
64 * 47 *
  48 + * @param dataBaseName 数据库名称
65 * @param superTableName 超级表名称 49 * @param superTableName 超级表名称
66 * @param fieldsVo 字段信息 50 * @param fieldsVo 字段信息
67 - * @throws Exception 异常  
68 */ 51 */
69 - void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; 52 + void addColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fieldsVo);
70 53
71 /** 54 /**
72 * 为超级表删除列 55 * 为超级表删除列
73 * 56 *
  57 + * @param dataBaseName 数据库名称
74 * @param superTableName 超级表名称 58 * @param superTableName 超级表名称
75 * @param fieldsVo 字段信息 59 * @param fieldsVo 字段信息
76 - * @throws Exception 异常  
77 */ 60 */
78 - void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; 61 + void dropColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fieldsVo);
79 62
80 /** 63 /**
81 * 为超级表添加tag 64 * 为超级表添加tag
82 */ 65 */
83 Long getCountByTimesTamp(SelectDto selectDto) throws Exception; 66 Long getCountByTimesTamp(SelectDto selectDto) throws Exception;
84 67
  68 +
  69 + /**
  70 + * 创建表
  71 + *
  72 + * @param tableDto 表信息
  73 + * @throws Exception 异常
  74 + */
  75 + void createTable(TableDto tableDto) throws Exception;
  76 +
85 /** 77 /**
86 - * 检查表是否存在 78 + * 插入数据
87 * 79 *
88 - * @return 1存在 0不存在 80 + * @param tableDto 表信息
89 * @throws Exception 异常 81 * @throws Exception 异常
90 */ 82 */
91 - Integer checkTableExists(SelectDto selectDto) throws Exception; 83 + void insertData(TableDto tableDto) throws Exception;
  84 +
  85 + /**
  86 + * 根据时间戳查询数据
  87 + *
  88 + * @param selectDto 查询条件
  89 + * @return 数据
  90 + * @throws Exception 异常
  91 + */
  92 + List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) throws Exception;
92 93
93 /** 94 /**
94 * 初始化超级表 95 * 初始化超级表
@@ -138,4 +139,6 @@ public interface TdEngineService { @@ -138,4 +139,6 @@ public interface TdEngineService {
138 * @return 数据 139 * @return 数据
139 */ 140 */
140 List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto); 141 List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto);
  142 +
  143 +
141 } 144 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.tdengine; 1 package cn.iocoder.yudao.module.iot.service.tdengine;
2 2
3 -import cn.iocoder.yudao.module.iot.domain.FieldsVo; 3 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
  4 +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;
4 import cn.iocoder.yudao.module.iot.domain.SelectDto; 5 import cn.iocoder.yudao.module.iot.domain.SelectDto;
5 import cn.iocoder.yudao.module.iot.domain.TableDto; 6 import cn.iocoder.yudao.module.iot.domain.TableDto;
6 import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; 7 import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
7 import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; 8 import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
  9 +import jakarta.annotation.Resource;
8 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
9 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
10 12
@@ -15,25 +17,27 @@ import java.util.Map; @@ -15,25 +17,27 @@ import java.util.Map;
15 @Slf4j 17 @Slf4j
16 public class TdEngineServiceImpl implements TdEngineService { 18 public class TdEngineServiceImpl implements TdEngineService {
17 19
  20 + @Resource
  21 + private TdEngineMapper tdEngineMapper;
18 22
19 @Override 23 @Override
20 public void createDateBase(String dataBaseName) { 24 public void createDateBase(String dataBaseName) {
21 - 25 + tdEngineMapper.createDatabase(dataBaseName);
22 } 26 }
23 27
24 @Override 28 @Override
25 - public void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName, String superTableName) {  
26 - 29 + public void createSuperTable(List<TdFieldDO> schemaFields, List<TdFieldDO> tagsFields, String dataBaseName, String superTableName) {
  30 + tdEngineMapper.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
27 } 31 }
28 32
29 @Override 33 @Override
30 public void createTable(TableDto tableDto) { 34 public void createTable(TableDto tableDto) {
31 - 35 + tdEngineMapper.createTable(tableDto);
32 } 36 }
33 37
34 @Override 38 @Override
35 public void insertData(TableDto tableDto) { 39 public void insertData(TableDto tableDto) {
36 - 40 + tdEngineMapper.insertData(tableDto);
37 } 41 }
38 42
39 @Override 43 @Override
@@ -42,13 +46,17 @@ public class TdEngineServiceImpl implements TdEngineService { @@ -42,13 +46,17 @@ public class TdEngineServiceImpl implements TdEngineService {
42 } 46 }
43 47
44 @Override 48 @Override
45 - public void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) {  
46 - 49 + public void addColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fields) {
  50 + for (TdFieldDO field : fields) {
  51 + tdEngineMapper.addColumnForSuperTable(dataBaseName,superTableName, field);
  52 + }
47 } 53 }
48 54
49 @Override 55 @Override
50 - public void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) {  
51 - 56 + public void dropColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fields) {
  57 + for (TdFieldDO field : fields) {
  58 + tdEngineMapper.dropColumnForSuperTable(dataBaseName,superTableName, field);
  59 + }
52 } 60 }
53 61
54 @Override 62 @Override
@@ -57,11 +65,6 @@ public class TdEngineServiceImpl implements TdEngineService { @@ -57,11 +65,6 @@ public class TdEngineServiceImpl implements TdEngineService {
57 } 65 }
58 66
59 @Override 67 @Override
60 - public Integer checkTableExists(SelectDto selectDto) {  
61 - return 0;  
62 - }  
63 -  
64 - @Override  
65 public void initSTableFrame(String msg) { 68 public void initSTableFrame(String msg) {
66 69
67 } 70 }
@@ -90,4 +93,15 @@ public class TdEngineServiceImpl implements TdEngineService { @@ -90,4 +93,15 @@ public class TdEngineServiceImpl implements TdEngineService {
90 public List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto) { 93 public List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto) {
91 return List.of(); 94 return List.of();
92 } 95 }
  96 +
  97 + @Override
  98 + public Integer checkSuperTableExists(String dataBaseName, String superTableName) {
  99 + List<Map<String, Object>> results = tdEngineMapper.showSuperTables(dataBaseName, superTableName);
  100 + return results == null || results.isEmpty() ? 0 : results.size();
  101 + }
  102 +
  103 + @Override
  104 + public List<Map<String, Object>> describeSuperTable(String dataBaseName, String superTableName) {
  105 + return tdEngineMapper.describeSuperTable(dataBaseName, superTableName);
  106 + }
93 } 107 }
94 \ No newline at end of file 108 \ No newline at end of file
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 @@ -174,8 +174,10 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
174 public void createSuperTableDataModel(Long productId) { 174 public void createSuperTableDataModel(Long productId) {
175 // 1. 查询产品 175 // 1. 查询产品
176 IotProductDO product = productService.getProduct(productId); 176 IotProductDO product = productService.getProduct(productId);
  177 +
177 // 2. 查询产品的物模型功能列表 178 // 2. 查询产品的物模型功能列表
178 List<IotThinkModelFunctionDO> functionList = thinkModelFunctionMapper.selectListByProductId(productId); 179 List<IotThinkModelFunctionDO> functionList = thinkModelFunctionMapper.selectListByProductId(productId);
  180 +
179 // 3. 生成 TDengine 的数据模型 181 // 3. 生成 TDengine 的数据模型
180 dbStructureDataService.createSuperTableDataModel(product, functionList); 182 dbStructureDataService.createSuperTableDataModel(product, functionList);
181 } 183 }
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 </update> 8 </update>
9 9
10 <update id="createSuperTable"> 10 <update id="createSuperTable">
11 - create table if not exists #{dataBaseName}.#{superTableName} 11 + CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName}
12 <foreach item="item" collection="schemaFields" separator="," 12 <foreach item="item" collection="schemaFields" separator=","
13 open="(" close=")" index=""> 13 open="(" close=")" index="">
14 <if test="item.fieldName != null || item.fieldName != ''"> 14 <if test="item.fieldName != null || item.fieldName != ''">
@@ -16,46 +16,48 @@ @@ -16,46 +16,48 @@
16 </if> 16 </if>
17 <if test="item.dataType != null || item.dataType != ''"> 17 <if test="item.dataType != null || item.dataType != ''">
18 <choose> 18 <choose>
19 - <when test="item.dataType == 'timestamp'">  
20 - timestamp 19 + <when test="item.dataType == 'TIMESTAMP'">
  20 + TIMESTAMP
21 </when> 21 </when>
22 - <when test="item.dataType == 'tinyint'">  
23 - tinyint 22 + <when test="item.dataType == 'TINYINT'">
  23 + TINYINT
24 </when> 24 </when>
25 - <when test="item.dataType == 'smallint'">  
26 - smallint 25 + <when test="item.dataType == 'SMALLINT'">
  26 + SMALLINT
27 </when> 27 </when>
28 - <when test="item.dataType == 'int'">  
29 - int 28 + <when test="item.dataType == 'INT'">
  29 + INT
30 </when> 30 </when>
31 - <when test="item.dataType == 'bigint'">  
32 - bigint 31 + <when test="item.dataType == 'BIGINT'">
  32 + BIGINT
33 </when> 33 </when>
34 - <when test="item.dataType == 'float'">  
35 - float 34 + <when test="item.dataType == 'FLOAT'">
  35 + FLOAT
36 </when> 36 </when>
37 - <when test="item.dataType == 'double'">  
38 - double 37 + <when test="item.dataType == 'DOUBLE'">
  38 + DOUBLE
39 </when> 39 </when>
40 - <when test="item.dataType == 'binary'">  
41 - binary 40 + <when test="item.dataType == 'BINARY'">
  41 + BINARY
42 </when> 42 </when>
43 - <when test="item.dataType == 'nchar'">  
44 - nchar 43 + <when test="item.dataType == 'NCHAR'">
  44 + NCHAR
45 </when> 45 </when>
46 - <when test="item.dataType == 'bool'">  
47 - bool 46 + <when test="item.dataType == 'BOOL'">
  47 + BOOL
48 </when> 48 </when>
49 - <when test="item.dataType == 'json'">  
50 - json 49 + <when test="item.dataType == 'JSON'">
  50 + JSON
51 </when> 51 </when>
52 </choose> 52 </choose>
53 </if> 53 </if>
54 - <if test="item.size != null">  
55 - (#{item.size}) 54 + <if test="item.dataLength > 0">
  55 + (
  56 + ${item.dataLength}
  57 + )
56 </if> 58 </if>
57 </foreach> 59 </foreach>
58 - tags 60 + TAGS
59 <!--tdEngine不支持动态tags里的数据类型,只能使用choose标签比对--> 61 <!--tdEngine不支持动态tags里的数据类型,只能使用choose标签比对-->
60 <foreach item="item" collection="tagsFields" separator="," 62 <foreach item="item" collection="tagsFields" separator=","
61 open="(" close=")" index=""> 63 open="(" close=")" index="">
@@ -64,43 +66,45 @@ @@ -64,43 +66,45 @@
64 </if> 66 </if>
65 <if test="item.dataType != null || item.dataType != ''"> 67 <if test="item.dataType != null || item.dataType != ''">
66 <choose> 68 <choose>
67 - <when test="item.dataType == 'timestamp'">  
68 - timestamp 69 + <when test="item.dataType == 'TIMESTAMP'">
  70 + TIMESTAMP
69 </when> 71 </when>
70 - <when test="item.dataType == 'tinyint'">  
71 - tinyint 72 + <when test="item.dataType == 'TINYINT'">
  73 + TINYINT
72 </when> 74 </when>
73 - <when test="item.dataType == 'smallint'">  
74 - smallint 75 + <when test="item.dataType == 'SMALLINT'">
  76 + SMALLINT
75 </when> 77 </when>
76 - <when test="item.dataType == 'int'">  
77 - int 78 + <when test="item.dataType == 'INT'">
  79 + INT
78 </when> 80 </when>
79 - <when test="item.dataType == 'bigint'">  
80 - bigint 81 + <when test="item.dataType == 'BIGINT'">
  82 + BIGINT
81 </when> 83 </when>
82 - <when test="item.dataType == 'float'">  
83 - float 84 + <when test="item.dataType == 'FLOAT'">
  85 + FLOAT
84 </when> 86 </when>
85 - <when test="item.dataType == 'double'">  
86 - double 87 + <when test="item.dataType == 'DOUBLE'">
  88 + DOUBLE
87 </when> 89 </when>
88 - <when test="item.dataType == 'binary'">  
89 - binary 90 + <when test="item.dataType == 'BINARY'">
  91 + BINARY
90 </when> 92 </when>
91 - <when test="item.dataType == 'nchar'">  
92 - nchar 93 + <when test="item.dataType == 'NCHAR'">
  94 + NCHAR
93 </when> 95 </when>
94 - <when test="item.dataType == 'bool'">  
95 - bool 96 + <when test="item.dataType == 'BOOL'">
  97 + BOOL
96 </when> 98 </when>
97 - <when test="item.dataType == 'json'">  
98 - json 99 + <when test="item.dataType == 'JSON'">
  100 + JSON
99 </when> 101 </when>
100 </choose> 102 </choose>
101 </if> 103 </if>
102 - <if test="item.size != null">  
103 - (#{item.size}) 104 + <if test="item.dataLength > 0">
  105 + (
  106 + ${item.dataLength}
  107 + )
104 </if> 108 </if>
105 </foreach> 109 </foreach>
106 </update> 110 </update>
@@ -135,7 +139,6 @@ @@ -135,7 +139,6 @@
135 </foreach> 139 </foreach>
136 </insert> 140 </insert>
137 141
138 -  
139 <select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto" 142 <select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto"
140 resultType="Map"> 143 resultType="Map">
141 select * from #{dataBaseName}.#{tableName} 144 select * from #{dataBaseName}.#{tableName}
@@ -146,66 +149,58 @@ @@ -146,66 +149,58 @@
146 </select> 149 </select>
147 150
148 <update id="addColumnForSuperTable"> 151 <update id="addColumnForSuperTable">
149 - ALTER  
150 - STABLE  
151 - #{superTableName}  
152 - ADD  
153 - COLUMN  
154 - <if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">  
155 - #{fieldsVo.fieldName} 152 + ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN
  153 + <if test="field.fieldName != null || field.fieldName != ''">
  154 + #{field.fieldName}
156 </if> 155 </if>
157 - <if test="fieldsVo.dataType != null || fieldsVo.dataType != ''"> 156 + <if test="field.dataType != null || field.dataType != ''">
158 <choose> 157 <choose>
159 - <when test="fieldsVo.dataType == 'timestamp'">  
160 - timestamp 158 + <when test="field.dataType == 'TIMESTAMP'">
  159 + TIMESTAMP
161 </when> 160 </when>
162 - <when test="fieldsVo.dataType == 'tinyint'">  
163 - tinyint 161 + <when test="field.dataType == 'TINYINT'">
  162 + TINYINT
164 </when> 163 </when>
165 - <when test="fieldsVo.dataType == 'smallint'">  
166 - smallint 164 + <when test="field.dataType == 'SMALLINT'">
  165 + SMALLINT
167 </when> 166 </when>
168 - <when test="fieldsVo.dataType == 'int'">  
169 - int 167 + <when test="field.dataType == 'INT'">
  168 + INT
170 </when> 169 </when>
171 - <when test="fieldsVo.dataType == 'bigint'">  
172 - bigint 170 + <when test="field.dataType == 'BIGINT'">
  171 + BIGINT
173 </when> 172 </when>
174 - <when test="fieldsVo.dataType == 'float'">  
175 - float 173 + <when test="field.dataType == 'FLOAT'">
  174 + FLOAT
176 </when> 175 </when>
177 - <when test="fieldsVo.dataType == 'double'">  
178 - double 176 + <when test="field.dataType == 'DOUBLE'">
  177 + DOUBLE
179 </when> 178 </when>
180 - <when test="fieldsVo.dataType == 'binary'">  
181 - binary 179 + <when test="field.dataType == 'BINARY'">
  180 + BINARY
182 </when> 181 </when>
183 - <when test="fieldsVo.dataType == 'nchar'">  
184 - nchar 182 + <when test="field.dataType == 'NCHAR'">
  183 + NCHAR
185 </when> 184 </when>
186 - <when test="fieldsVo.dataType == 'bool'">  
187 - bool 185 + <when test="field.dataType == 'BOOL'">
  186 + BOOL
188 </when> 187 </when>
189 - <when test="fieldsVo.dataType == 'json'">  
190 - json 188 + <when test="field.dataType == 'JSON'">
  189 + JSON
191 </when> 190 </when>
192 </choose> 191 </choose>
193 </if> 192 </if>
194 - <if test="fieldsVo.size != null"> 193 + <if test="field.dataLength > 0">
195 ( 194 (
196 - #{fieldsVo.size} 195 + #{field.dataLength}
197 ) 196 )
198 </if> 197 </if>
199 </update> 198 </update>
200 199
201 <update id="dropColumnForSuperTable"> 200 <update id="dropColumnForSuperTable">
202 - ALTER  
203 - STABLE  
204 - #{superTableName}  
205 - DROP  
206 - COLUMN  
207 - <if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">  
208 - #{fieldsVo.fieldName} 201 + ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN
  202 + <if test="field.fieldName != null || field.fieldName != ''">
  203 + #{field.fieldName}
209 </if> 204 </if>
210 </update> 205 </update>
211 206
@@ -215,49 +210,49 @@ @@ -215,49 +210,49 @@
215 #{superTableName} 210 #{superTableName}
216 ADD 211 ADD
217 TAG 212 TAG
218 - <if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">  
219 - #{fieldsVo.fieldName} 213 + <if test="field.fieldName != null || fieldDO.fieldName != ''">
  214 + #{fieldDO.fieldName}
220 </if> 215 </if>
221 - <if test="fieldsVo.dataType != null || fieldsVo.dataType != ''"> 216 + <if test="fieldDO.dataType != null || fieldDO.dataType != ''">
222 <choose> 217 <choose>
223 - <when test="fieldsVo.dataType == 'timestamp'"> 218 + <when test="fieldDO.dataType == 'timestamp'">
224 timestamp 219 timestamp
225 </when> 220 </when>
226 - <when test="fieldsVo.dataType == 'tinyint'"> 221 + <when test="fieldDO.dataType == 'tinyint'">
227 tinyint 222 tinyint
228 </when> 223 </when>
229 - <when test="fieldsVo.dataType == 'smallint'"> 224 + <when test="fieldDO.dataType == 'smallint'">
230 smallint 225 smallint
231 </when> 226 </when>
232 - <when test="fieldsVo.dataType == 'int'"> 227 + <when test="fieldDO.dataType == 'int'">
233 int 228 int
234 </when> 229 </when>
235 - <when test="fieldsVo.dataType == 'bigint'"> 230 + <when test="fieldDO.dataType == 'bigint'">
236 bigint 231 bigint
237 </when> 232 </when>
238 - <when test="fieldsVo.dataType == 'float'"> 233 + <when test="fieldDO.dataType == 'float'">
239 float 234 float
240 </when> 235 </when>
241 - <when test="fieldsVo.dataType == 'double'"> 236 + <when test="fieldDO.dataType == 'double'">
242 double 237 double
243 </when> 238 </when>
244 - <when test="fieldsVo.dataType == 'binary'"> 239 + <when test="fieldDO.dataType == 'binary'">
245 binary 240 binary
246 </when> 241 </when>
247 - <when test="fieldsVo.dataType == 'nchar'"> 242 + <when test="fieldDO.dataType == 'nchar'">
248 nchar 243 nchar
249 </when> 244 </when>
250 - <when test="fieldsVo.dataType == 'bool'"> 245 + <when test="fieldDO.dataType == 'bool'">
251 bool 246 bool
252 </when> 247 </when>
253 - <when test="fieldsVo.dataType == 'json'"> 248 + <when test="fieldDO.dataType == 'json'">
254 json 249 json
255 </when> 250 </when>
256 </choose> 251 </choose>
257 </if> 252 </if>
258 - <if test="fieldsVo.size != null"> 253 + <if test="fieldDO.dataLength > 0">
259 ( 254 (
260 - #{fieldsVo.size} 255 + #{fieldDO.dataLength}
261 ) 256 )
262 </if> 257 </if>
263 </update> 258 </update>
@@ -279,11 +274,8 @@ @@ -279,11 +274,8 @@
279 FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime} 274 FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
280 </select> 275 </select>
281 276
282 - <select id="checkTableExists" resultType="java.lang.Integer">  
283 - SELECT COUNT(0)  
284 - FROM information_schema.ins_tables  
285 - WHERE db_name = #{dataBaseName}  
286 - AND table_name = #{tableName} 277 + <select id="showSuperTables" resultType="java.util.Map">
  278 + SHOW ${dataBaseName}.STABLES LIKE '${superTableName}'
287 </select> 279 </select>
288 280
289 <select id="getLastData" resultType="java.util.Map"> 281 <select id="getLastData" resultType="java.util.Map">
@@ -304,11 +296,13 @@ @@ -304,11 +296,13 @@
304 FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime} 296 FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime}
305 LIMIT #{num} 297 LIMIT #{num}
306 </select> 298 </select>
  299 +
307 <select id="getRealtimeData" resultType="java.util.Map" 300 <select id="getRealtimeData" resultType="java.util.Map"
308 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto"> 301 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
309 SELECT #{fieldName}, ts 302 SELECT #{fieldName}, ts
310 FROM #{dataBaseName}.#{tableName} LIMIT #{num} 303 FROM #{dataBaseName}.#{tableName} LIMIT #{num}
311 </select> 304 </select>
  305 +
312 <select id="getAggregateData" resultType="java.util.Map" 306 <select id="getAggregateData" resultType="java.util.Map"
313 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto"> 307 parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
314 SELECT #{aggregate}(${fieldName}) 308 SELECT #{aggregate}(${fieldName})
@@ -316,9 +310,8 @@ @@ -316,9 +310,8 @@
316 LIMIT #{num} 310 LIMIT #{num}
317 </select> 311 </select>
318 312
319 -  
320 - <insert id="createSuperTableDevice">  
321 - ${sql}  
322 - </insert> 313 + <select id="describeSuperTable" resultType="java.util.Map">
  314 + DESCRIBE ${dataBaseName}.${superTableName}
  315 + </select>
323 316
324 </mapper> 317 </mapper>