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

This commit is contained in:
安浩浩 2024-10-26 23:15:31 +08:00
parent ea8dd67e9e
commit 7b5aa23d5c
13 changed files with 491 additions and 371 deletions

View File

@ -1,14 +1,18 @@
package cn.iocoder.yudao.module.iot.domain; package cn.iocoder.yudao.module.iot.domain;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* 字段信息 VO
*/
@Data @Data
@Builder
public class FieldsVo { public class FieldsVo {
private static final long serialVersionUID = 1L;
/** /**
* 字段名称 * 字段名称
@ -24,24 +28,4 @@ public class FieldsVo {
* 字段字节大小 * 字段字节大小
*/ */
private Integer size; 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<FieldsVo> fieldsTranscoding(List<Fields> fieldsList) throws SQLException {
List<FieldsVo> fieldsVoList = new ArrayList<>();
for (Fields fields : fieldsList) {
fieldsVoList.add(fieldsTranscoding(fields));
}
return fieldsVoList;
}
} }

View File

@ -10,7 +10,6 @@ import java.util.stream.Collectors;
/** /**
* FieldParser 类用于解析和转换物模型字段到 TDengine 字段 * FieldParser 类用于解析和转换物模型字段到 TDengine 字段
*
*/ */
public class FieldParser { public class FieldParser {
@ -35,49 +34,58 @@ public class FieldParser {
* @param property 物模型属性 * @param property 物模型属性
* @return TdField对象 * @return TdField对象
*/ */
public static TdField parse(ThingModelProperty property) { public static TdFieldDO parse(ThingModelProperty property) {
String fieldName = property.getIdentifier().toLowerCase(); String fieldName = property.getIdentifier().toLowerCase();
ThingModelDataType type = property.getDataType(); ThingModelDataType type = property.getDataType();
// 将物模型字段类型映射为td字段类型 // 将物模型字段类型映射为td字段类型
String fType = TYPE_MAPPING.get(type.getType().toUpperCase()); String fType = TYPE_MAPPING.get(type.getType().toUpperCase());
int len = -1;
// 如果字段类型为NCHAR默认长度为64 // 如果字段类型为NCHAR默认长度为64
int dataLength = 0;
if ("NCHAR".equals(fType)) { if ("NCHAR".equals(fType)) {
len = 64; dataLength = 64;
} }
return new TdFieldDO(fieldName, fType, dataLength);
return new TdField(fieldName, fType, len);
} }
/** /**
* 获取物模型中的字段列表 * 从物模型中获取字段列表
*
* @param thingModel 物模型响应对象
* @return 字段列表
*/ */
public static List<TdField> parse(ThingModelRespVO thingModel) { public static List<TdFieldDO> parse(ThingModelRespVO thingModel) {
return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList()); return thingModel.getModel().getProperties().stream()
.map(FieldParser::parse)
.collect(Collectors.toList());
} }
/** /**
* 将从库中查出来的字段信息转换为td字段对象 * 将从库中查出来的字段信息转换为 TDengine 字段对象
*
* @param rows 从库中查出的字段信息列表
* @return 转换后的 TDengine 字段对象列表
*/ */
public static List<TdField> parse(List<List<Object>> rows) { public static List<TdFieldDO> parse(List<List<Object>> rows) {
return rows.stream().map(row -> { return rows.stream().map(row -> {
String type = row.get(1).toString().toUpperCase(); 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(), row.get(0).toString(),
type, type,
type.equals("NCHAR") ? Integer.parseInt(row.get(2).toString()) : -1); dataLength
);
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
/** /**
* 获取字段字义 * 获取字段字义
*/ */
public static String getFieldDefine(TdField field) { public static String getFieldDefine(TdFieldDO field) {
return "`" + field.getName() + "`" + " " return "`" + field.getFieldName() + "`" + " "
+ (field.getLength() > 0 ? String.format("%s(%d)", field.getType(), field.getLength()) + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength())
: field.getType()); : field.getDataType());
} }
} }

View File

@ -45,7 +45,7 @@ public class TableManager {
/** /**
* 获取创建表sql * 获取创建表sql
*/ */
public static String getCreateSTableSql(String tbName, List<TdField> fields, TdField... tags) { public static String getCreateSTableSql(String tbName, List<TdFieldDO> fields, TdFieldDO... tags) {
if (fields.isEmpty()) { if (fields.isEmpty()) {
return null; return null;
} }
@ -53,7 +53,7 @@ public class TableManager {
// 生成字段片段 // 生成字段片段
StringBuilder sbField = new StringBuilder("time TIMESTAMP,"); StringBuilder sbField = new StringBuilder("time TIMESTAMP,");
for (TdField field : fields) { for (TdFieldDO field : fields) {
sbField.append(FieldParser.getFieldDefine(field)); sbField.append(FieldParser.getFieldDefine(field));
sbField.append(","); sbField.append(",");
} }
@ -63,7 +63,7 @@ public class TableManager {
// 生成tag // 生成tag
StringBuilder sbTag = new StringBuilder(); StringBuilder sbTag = new StringBuilder();
for (TdField tag : tags) { for (TdFieldDO tag : tags) {
sbTag.append(FieldParser.getFieldDefine(tag)) sbTag.append(FieldParser.getFieldDefine(tag))
.append(","); .append(",");
} }
@ -76,7 +76,7 @@ public class TableManager {
/** /**
* 获取创建普通表sql * 获取创建普通表sql
*/ */
public static String getCreateCTableSql(String tbName, List<TdField> fields) { public static String getCreateCTableSql(String tbName, List<TdFieldDO> fields) {
if (fields.size() == 0) { if (fields.size() == 0) {
return null; return null;
} }
@ -84,7 +84,7 @@ public class TableManager {
//生成字段片段 //生成字段片段
StringBuilder sbField = new StringBuilder("time timestamp,"); StringBuilder sbField = new StringBuilder("time timestamp,");
for (TdField field : fields) { for (TdFieldDO field : fields) {
sbField.append(FieldParser.getFieldDefine(field)); sbField.append(FieldParser.getFieldDefine(field));
sbField.append(","); sbField.append(",");
} }
@ -116,9 +116,9 @@ public class TableManager {
/** /**
* 获取添加字段sql * 获取添加字段sql
*/ */
public static String getAddSTableColumnSql(String tbName, List<TdField> fields) { public static String getAddSTableColumnSql(String tbName, List<TdFieldDO> fields) {
StringBuilder sbAdd = new StringBuilder(); StringBuilder sbAdd = new StringBuilder();
for (TdField field : fields) { for (TdFieldDO field : fields) {
sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL, sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL,
tbName, tbName,
FieldParser.getFieldDefine(field) FieldParser.getFieldDefine(field)
@ -130,9 +130,9 @@ public class TableManager {
/** /**
* 获取修改字段sql * 获取修改字段sql
*/ */
public static String getModifySTableColumnSql(String tbName, List<TdField> fields) { public static String getModifySTableColumnSql(String tbName, List<TdFieldDO> fields) {
StringBuilder sbModify = new StringBuilder(); StringBuilder sbModify = new StringBuilder();
for (TdField field : fields) { for (TdFieldDO field : fields) {
sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL, sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL,
tbName, tbName,
FieldParser.getFieldDefine(field) FieldParser.getFieldDefine(field)
@ -144,12 +144,12 @@ public class TableManager {
/** /**
* 获取删除字段sql * 获取删除字段sql
*/ */
public static String getDropSTableColumnSql(String tbName, List<TdField> fields) { public static String getDropSTableColumnSql(String tbName, List<TdFieldDO> fields) {
StringBuilder sbDrop = new StringBuilder(); StringBuilder sbDrop = new StringBuilder();
for (TdField field : fields) { for (TdFieldDO field : fields) {
sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL, sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL,
tbName, tbName,
field.getName() field.getFieldName()
)); ));
} }
return sbDrop.toString(); return sbDrop.toString();

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -10,20 +11,21 @@ import lombok.NoArgsConstructor;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @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;
} }

View File

@ -17,13 +17,25 @@ public class TdResponse {
public static final int CODE_SUCCESS = 0; public static final int CODE_SUCCESS = 0;
public static final int CODE_TB_NOT_EXIST = 866; public static final int CODE_TB_NOT_EXIST = 866;
/**
* 状态
*/
private String status; private String status;
/**
* 错误码
*/
private int code; private int code;
/**
* 错误信息
*/
private String desc; private String desc;
//[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] /**
private List<Object[]> data; * 列信息
* [["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]]
*/
private List data;
} }

View File

@ -14,22 +14,22 @@ import org.springframework.stereotype.Service;
@Service @Service
public class TdRestApi { public class TdRestApi {
@Value("${spring.datasource.dynamic.datasource.master.url}") @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
private String url; private String url;
@Value("${spring.datasource.dynamic.datasource.master.username}") @Value("${spring.datasource.dynamic.datasource.tdengine.username}")
private String username; private String username;
@Value("${spring.datasource.dynamic.datasource.master.password}") @Value("${spring.datasource.dynamic.datasource.tdengine.password}")
private String password; private String password;
/**
* 获取 REST API URL
*/
private String getRestApiUrl() { private String getRestApiUrl() {
//jdbc:TAOS-RS://127.0.0.1:6041/iotkit?xxxx
String restUrl = url.replace("jdbc:TAOS-RS://", "") String restUrl = url.replace("jdbc:TAOS-RS://", "")
.replaceAll("\\?.*", ""); .replaceAll("\\?.*", "");
// /rest/sql/iotkit
int idx = restUrl.lastIndexOf("/"); 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));
} }
@ -54,5 +54,4 @@ public class TdRestApi {
return JSONUtil.toBean(response.body(), TdResponse.class); return JSONUtil.toBean(response.body(), TdResponse.class);
} }
} }

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.iot.dal.tdengine; 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.FieldsVo;
import cn.iocoder.yudao.module.iot.domain.SelectDto; import cn.iocoder.yudao.module.iot.domain.SelectDto;
import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TableDto;
@ -17,24 +18,80 @@ import java.util.Map;
@DS("tdengine") @DS("tdengine")
public interface TdEngineMapper { public interface TdEngineMapper {
/**
* 创建数据库
*
* @param dataBaseName 数据库名称
*/
@InterceptorIgnore(tenantLine = "true")
void createDatabase(@Param("dataBaseName") String dataBaseName); void createDatabase(@Param("dataBaseName") String dataBaseName);
void createSuperTable(@Param("schemaFields") List<FieldsVo> schemaFields, /**
@Param("tagsFields") List<FieldsVo> tagsFields, * 创建超级表
*
* @param schemaFields schema字段
* @param tagsFields tags字段
* @param dataBaseName 数据库名称
* @param superTableName 超级表名称
*/
@InterceptorIgnore(tenantLine = "true")
void createSuperTable(@Param("schemaFields") List<TdFieldDO> schemaFields,
@Param("tagsFields") List<TdFieldDO> tagsFields,
@Param("dataBaseName") String dataBaseName, @Param("dataBaseName") String dataBaseName,
@Param("superTableName") String superTableName); @Param("superTableName") String superTableName);
/**
* 查看超级表 - 显示当前数据库下的所有超级表信息
* SQLSHOW STABLES [LIKE tb_name_wildcard];
*
* @param dataBaseName 数据库名称
* @param superTableName 超级表名称
*/
@InterceptorIgnore(tenantLine = "true")
List<Map<String, Object>> showSuperTables(@Param("dataBaseName") String dataBaseName,
@Param("superTableName") String superTableName);
/**
* 查看超级表 - 获取超级表的结构信息
* SQLDESCRIBE [db_name.]stb_name;
* <p>
* * @param dataBaseName 数据库名称
* * @param superTableName 超级表名称
*/
@InterceptorIgnore(tenantLine = "true")
List<Map<String, Object>> 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 createTable(TableDto tableDto);
void insertData(TableDto tableDto); void insertData(TableDto tableDto);
List<Map<String, Object>> selectByTimestamp(SelectDto selectDto); List<Map<String, Object>> 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, void addTagForSuperTable(@Param("superTableName") String superTableName,
@Param("fieldsVo") FieldsVo fieldsVo); @Param("fieldsVo") FieldsVo fieldsVo);
@ -44,14 +101,6 @@ public interface TdEngineMapper {
Map<String, Long> getCountByTimestamp(SelectDto selectDto); Map<String, Long> getCountByTimestamp(SelectDto selectDto);
/**
* 检查表是否存在
*
* @param dataBaseName 数据库名称
* @param tableName 表名称
*/
Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName);
Map<String, Object> getLastData(SelectDto selectDto); Map<String, Object> getLastData(SelectDto selectDto);
List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto); List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
@ -62,13 +111,5 @@ public interface TdEngineMapper {
List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao); List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
/**
* 创建超级表
*
* @param sql sql
* @return 返回值
*/
@InterceptorIgnore(tenantLine = "true")
Integer createSuperTableDevice(String sql);
} }

View File

@ -1,19 +1,22 @@
package cn.iocoder.yudao.module.iot.service.tdengine; 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.ThingModelProperty;
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; 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.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.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -22,147 +25,206 @@ import java.util.stream.Collectors;
public class IotDbStructureDataServiceImpl implements IotDbStructureDataService { public class IotDbStructureDataServiceImpl implements IotDbStructureDataService {
@Resource @Resource
private TdEngineMapper tdEngineMapper; private TdEngineService tdEngineService;
@Resource @Resource
private TdRestApi tdRestApi; private TdRestApi tdRestApi;
@Value("${spring.datasource.dynamic.datasource.tdengine.url}")
private String url;
@Override @Override
public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
// 获取物模型中的属性定义 // 1. 解析物模型获得字段列表
List<TdField> fields = FieldParser.parse(thingModel); List<TdFieldDO> schemaFields = new ArrayList<>();
String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); schemaFields.add(TdFieldDO.builder().
fieldName("time").
dataType("TIMESTAMP").
build());
schemaFields.addAll(FieldParser.parse(thingModel));
// 生成创建超级表的 SQL // 3. 设置超级表的标签
String sql = TableManager.getCreateSTableSql(tbName, fields, new TdField("device_id", "NCHAR", 64)); List<TdFieldDO> tagsFields = new ArrayList<>();
if (sql == null) { tagsFields.add(TdFieldDO.builder().
log.warn("生成的 SQL 为空,无法创建超级表"); fieldName("product_key").
return; dataType("NCHAR").
} dataLength(64).
log.info("执行 SQL: {}", sql); 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 @Override
public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
try { try {
// 获取旧字段信息
String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
String sql = TableManager.getDescTableSql(tbName); List<TdFieldDO> oldFields = getTableFields(tbName);
TdResponse response = tdRestApi.execSql(sql); List<TdFieldDO> newFields = FieldParser.parse(thingModel);
if (response.getCode() != TdResponse.CODE_SUCCESS) {
throw new RuntimeException("获取表描述错误: " + JSONUtil.toJsonStr(response));
}
List<TdField> oldFields = FieldParser.parse(response.getData()); updateTableFields(tbName, oldFields, newFields);
List<TdField> newFields = FieldParser.parse(thingModel);
// 找出新增的字段
List<TdField> 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<TdField> 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<TdField> 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));
}
}
} catch (Throwable e) { } catch (Throwable e) {
log.error("更新物模型超级表失败", e); log.error("更新物模型超级表失败", e);
} }
} }
// 获取表字段
private List<TdFieldDO> getTableFields(String tableName) {
List<TdFieldDO> fields = new ArrayList<>();
// 获取超级表的描述信息
List<Map<String, Object>> 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<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
// 获取新增字段
List<TdFieldDO> addFields = getAddFields(oldFields, newFields);
// 获取修改字段
List<TdFieldDO> modifyFields = getModifyFields(oldFields, newFields);
// 获取删除字段
List<TdFieldDO> 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<TdFieldDO> getAddFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
return newFields.stream()
.filter(f -> oldFields.stream().noneMatch(old -> old.getFieldName().equals(f.getFieldName())))
.collect(Collectors.toList());
}
// 获取修改字段
private List<TdFieldDO> getModifyFields(List<TdFieldDO> oldFields, List<TdFieldDO> 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<TdFieldDO> getDropFields(List<TdFieldDO> oldFields, List<TdFieldDO> 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 @Override
public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) { public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
// 1. 生成 ThingModelRespVO ThingModelRespVO thingModel = buildThingModel(product, functionList);
if (thingModel.getModel().getProperties().isEmpty()) {
log.warn("物模型属性列表为空,不创建超级表");
return;
}
String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
Integer tableExists = tdEngineService.checkSuperTableExists(dataBaseName, superTableName);
if (tableExists != null && tableExists > 0) {
updateSuperTable(thingModel, product.getDeviceType());
} else {
createSuperTable(thingModel, product.getDeviceType());
}
}
private ThingModelRespVO buildThingModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
ThingModelRespVO thingModel = new ThingModelRespVO(); ThingModelRespVO thingModel = new ThingModelRespVO();
thingModel.setId(product.getId()); thingModel.setId(product.getId());
thingModel.setProductKey(product.getProductKey()); thingModel.setProductKey(product.getProductKey());
// 1.1 设置属性服务和事件
ThingModelRespVO.Model model = new ThingModelRespVO.Model(); ThingModelRespVO.Model model = new ThingModelRespVO.Model();
List<ThingModelProperty> properties = new ArrayList<>(); List<ThingModelProperty> properties = functionList.stream()
.filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(IotProductFunctionTypeEnum.valueOf(function.getType())))
.map(this::buildThingModelProperty)
.collect(Collectors.toList());
// 1.2 遍历功能列表并分类 model.setProperties(properties);
for (IotThinkModelFunctionDO function : functionList) { thingModel.setModel(model);
if (Objects.requireNonNull(IotProductFunctionTypeEnum.valueOf(function.getType())) == IotProductFunctionTypeEnum.PROPERTY) {
return thingModel;
}
private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) {
ThingModelProperty property = new ThingModelProperty(); ThingModelProperty property = new ThingModelProperty();
property.setIdentifier(function.getIdentifier()); property.setIdentifier(function.getIdentifier());
property.setName(function.getName()); property.setName(function.getName());
property.setDescription(function.getDescription()); property.setDescription(function.getDescription());
property.setDataType(function.getProperty().getDataType()); property.setDataType(function.getProperty().getDataType());
properties.add(property); return property;
}
} }
// 1.3 判断属性列表是否为空
if (properties.isEmpty()) {
log.warn("物模型属性列表为空,不创建超级表");
return;
}
model.setProperties(properties);
thingModel.setModel(model);
// 2. 判断是否已经创建,如果已经创建则进行更新
String tbName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
Integer iot = tdEngineMapper.checkTableExists("ruoyi_vue_pro", tbName);
if (iot != null && iot > 0) {
// 3. 更新
updateSuperTable(thingModel, product.getDeviceType());
} else {
// 4. 创建
createSuperTable(thingModel, product.getDeviceType());
}
}
/**
* 根据产品key获取产品属性超级表名
*/
static String getProductPropertySTableName(Integer deviceType, String productKey) { static String getProductPropertySTableName(Integer deviceType, String productKey) {
if (deviceType == 1) { return switch (deviceType) {
return String.format("gateway_sub_" + productKey).toLowerCase(); case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase();
} else if (deviceType == 2) { case 2 -> String.format("gateway_%s", productKey).toLowerCase();
return String.format("gateway_" + productKey).toLowerCase(); default -> String.format("device_%s", productKey).toLowerCase();
} else { };
return String.format("device_" + productKey).toLowerCase();
}
} }
/**
* 根据deviceId获取设备属性表名
*/
static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) {
return String.format(deviceType + "_" + productKey + "_" + deviceKey).toLowerCase(); return String.format("%s_%s_%s", deviceType, productKey, deviceKey).toLowerCase();
} }
} }

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.iot.service.tdengine; 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.SelectDto;
import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TableDto;
import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
@ -27,12 +27,44 @@ public interface TdEngineService {
* *
* @param schemaFields schema字段 * @param schemaFields schema字段
* @param tagsFields tags字段 * @param tagsFields tags字段
* @param superTableName 超级表名称
*/
void createSuperTable(List<TdFieldDO> schemaFields, List<TdFieldDO> tagsFields, String dataBaseName, String superTableName);
/**
* 检查超级表是否存在
*/
Integer checkSuperTableExists(String dataBaseName, String superTableName);
/**
* 获取超级表的结构信息
*/
List<Map<String, Object>> describeSuperTable(String dataBaseName, String superTableName);
/**
* 为超级表添加列
*
* @param dataBaseName 数据库名称 * @param dataBaseName 数据库名称
* @param superTableName 超级表名称 * @param superTableName 超级表名称
* @throws Exception 异常 * @param fieldsVo 字段信息
*/ */
void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName, void addColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fieldsVo);
String superTableName) throws Exception;
/**
* 为超级表删除列
*
* @param dataBaseName 数据库名称
* @param superTableName 超级表名称
* @param fieldsVo 字段信息
*/
void dropColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fieldsVo);
/**
* 为超级表添加tag
*/
Long getCountByTimesTamp(SelectDto selectDto) throws Exception;
/** /**
* 创建表 * 创建表
@ -59,37 +91,6 @@ public interface TdEngineService {
*/ */
List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) throws Exception; List<Map<String, Object>> 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 数据 * @return 数据
*/ */
List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto); List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto);
} }

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.iot.service.tdengine; 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.SelectDto;
import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TableDto;
import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -15,25 +17,27 @@ import java.util.Map;
@Slf4j @Slf4j
public class TdEngineServiceImpl implements TdEngineService { public class TdEngineServiceImpl implements TdEngineService {
@Resource
private TdEngineMapper tdEngineMapper;
@Override @Override
public void createDateBase(String dataBaseName) { public void createDateBase(String dataBaseName) {
tdEngineMapper.createDatabase(dataBaseName);
} }
@Override @Override
public void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName, String superTableName) { public void createSuperTable(List<TdFieldDO> schemaFields, List<TdFieldDO> tagsFields, String dataBaseName, String superTableName) {
tdEngineMapper.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
} }
@Override @Override
public void createTable(TableDto tableDto) { public void createTable(TableDto tableDto) {
tdEngineMapper.createTable(tableDto);
} }
@Override @Override
public void insertData(TableDto tableDto) { public void insertData(TableDto tableDto) {
tdEngineMapper.insertData(tableDto);
} }
@Override @Override
@ -42,13 +46,17 @@ public class TdEngineServiceImpl implements TdEngineService {
} }
@Override @Override
public void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { public void addColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fields) {
for (TdFieldDO field : fields) {
tdEngineMapper.addColumnForSuperTable(dataBaseName,superTableName, field);
}
} }
@Override @Override
public void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { public void dropColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fields) {
for (TdFieldDO field : fields) {
tdEngineMapper.dropColumnForSuperTable(dataBaseName,superTableName, field);
}
} }
@Override @Override
@ -56,11 +64,6 @@ public class TdEngineServiceImpl implements TdEngineService {
return 0L; return 0L;
} }
@Override
public Integer checkTableExists(SelectDto selectDto) {
return 0;
}
@Override @Override
public void initSTableFrame(String msg) { public void initSTableFrame(String msg) {
@ -90,4 +93,15 @@ public class TdEngineServiceImpl implements TdEngineService {
public List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto) { public List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto) {
return List.of(); return List.of();
} }
@Override
public Integer checkSuperTableExists(String dataBaseName, String superTableName) {
List<Map<String, Object>> results = tdEngineMapper.showSuperTables(dataBaseName, superTableName);
return results == null || results.isEmpty() ? 0 : results.size();
}
@Override
public List<Map<String, Object>> describeSuperTable(String dataBaseName, String superTableName) {
return tdEngineMapper.describeSuperTable(dataBaseName, superTableName);
}
} }

View File

@ -174,8 +174,10 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
public void createSuperTableDataModel(Long productId) { public void createSuperTableDataModel(Long productId) {
// 1. 查询产品 // 1. 查询产品
IotProductDO product = productService.getProduct(productId); IotProductDO product = productService.getProduct(productId);
// 2. 查询产品的物模型功能列表 // 2. 查询产品的物模型功能列表
List<IotThinkModelFunctionDO> functionList = thinkModelFunctionMapper.selectListByProductId(productId); List<IotThinkModelFunctionDO> functionList = thinkModelFunctionMapper.selectListByProductId(productId);
// 3. 生成 TDengine 的数据模型 // 3. 生成 TDengine 的数据模型
dbStructureDataService.createSuperTableDataModel(product, functionList); dbStructureDataService.createSuperTableDataModel(product, functionList);
} }

View File

@ -8,7 +8,7 @@
</update> </update>
<update id="createSuperTable"> <update id="createSuperTable">
create table if not exists #{dataBaseName}.#{superTableName} CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName}
<foreach item="item" collection="schemaFields" separator="," <foreach item="item" collection="schemaFields" separator=","
open="(" close=")" index=""> open="(" close=")" index="">
<if test="item.fieldName != null || item.fieldName != ''"> <if test="item.fieldName != null || item.fieldName != ''">
@ -16,46 +16,48 @@
</if> </if>
<if test="item.dataType != null || item.dataType != ''"> <if test="item.dataType != null || item.dataType != ''">
<choose> <choose>
<when test="item.dataType == 'timestamp'"> <when test="item.dataType == 'TIMESTAMP'">
timestamp TIMESTAMP
</when> </when>
<when test="item.dataType == 'tinyint'"> <when test="item.dataType == 'TINYINT'">
tinyint TINYINT
</when> </when>
<when test="item.dataType == 'smallint'"> <when test="item.dataType == 'SMALLINT'">
smallint SMALLINT
</when> </when>
<when test="item.dataType == 'int'"> <when test="item.dataType == 'INT'">
int INT
</when> </when>
<when test="item.dataType == 'bigint'"> <when test="item.dataType == 'BIGINT'">
bigint BIGINT
</when> </when>
<when test="item.dataType == 'float'"> <when test="item.dataType == 'FLOAT'">
float FLOAT
</when> </when>
<when test="item.dataType == 'double'"> <when test="item.dataType == 'DOUBLE'">
double DOUBLE
</when> </when>
<when test="item.dataType == 'binary'"> <when test="item.dataType == 'BINARY'">
binary BINARY
</when> </when>
<when test="item.dataType == 'nchar'"> <when test="item.dataType == 'NCHAR'">
nchar NCHAR
</when> </when>
<when test="item.dataType == 'bool'"> <when test="item.dataType == 'BOOL'">
bool BOOL
</when> </when>
<when test="item.dataType == 'json'"> <when test="item.dataType == 'JSON'">
json JSON
</when> </when>
</choose> </choose>
</if> </if>
<if test="item.size != null"> <if test="item.dataLength > 0">
(#{item.size}) (
${item.dataLength}
)
</if> </if>
</foreach> </foreach>
tags TAGS
<!--tdEngine不支持动态tags里的数据类型只能使用choose标签比对--> <!--tdEngine不支持动态tags里的数据类型只能使用choose标签比对-->
<foreach item="item" collection="tagsFields" separator="," <foreach item="item" collection="tagsFields" separator=","
open="(" close=")" index=""> open="(" close=")" index="">
@ -64,43 +66,45 @@
</if> </if>
<if test="item.dataType != null || item.dataType != ''"> <if test="item.dataType != null || item.dataType != ''">
<choose> <choose>
<when test="item.dataType == 'timestamp'"> <when test="item.dataType == 'TIMESTAMP'">
timestamp TIMESTAMP
</when> </when>
<when test="item.dataType == 'tinyint'"> <when test="item.dataType == 'TINYINT'">
tinyint TINYINT
</when> </when>
<when test="item.dataType == 'smallint'"> <when test="item.dataType == 'SMALLINT'">
smallint SMALLINT
</when> </when>
<when test="item.dataType == 'int'"> <when test="item.dataType == 'INT'">
int INT
</when> </when>
<when test="item.dataType == 'bigint'"> <when test="item.dataType == 'BIGINT'">
bigint BIGINT
</when> </when>
<when test="item.dataType == 'float'"> <when test="item.dataType == 'FLOAT'">
float FLOAT
</when> </when>
<when test="item.dataType == 'double'"> <when test="item.dataType == 'DOUBLE'">
double DOUBLE
</when> </when>
<when test="item.dataType == 'binary'"> <when test="item.dataType == 'BINARY'">
binary BINARY
</when> </when>
<when test="item.dataType == 'nchar'"> <when test="item.dataType == 'NCHAR'">
nchar NCHAR
</when> </when>
<when test="item.dataType == 'bool'"> <when test="item.dataType == 'BOOL'">
bool BOOL
</when> </when>
<when test="item.dataType == 'json'"> <when test="item.dataType == 'JSON'">
json JSON
</when> </when>
</choose> </choose>
</if> </if>
<if test="item.size != null"> <if test="item.dataLength > 0">
(#{item.size}) (
${item.dataLength}
)
</if> </if>
</foreach> </foreach>
</update> </update>
@ -135,7 +139,6 @@
</foreach> </foreach>
</insert> </insert>
<select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto" <select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto"
resultType="Map"> resultType="Map">
select * from #{dataBaseName}.#{tableName} select * from #{dataBaseName}.#{tableName}
@ -146,66 +149,58 @@
</select> </select>
<update id="addColumnForSuperTable"> <update id="addColumnForSuperTable">
ALTER ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN
STABLE <if test="field.fieldName != null || field.fieldName != ''">
#{superTableName} #{field.fieldName}
ADD
COLUMN
<if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
#{fieldsVo.fieldName}
</if> </if>
<if test="fieldsVo.dataType != null || fieldsVo.dataType != ''"> <if test="field.dataType != null || field.dataType != ''">
<choose> <choose>
<when test="fieldsVo.dataType == 'timestamp'"> <when test="field.dataType == 'TIMESTAMP'">
timestamp TIMESTAMP
</when> </when>
<when test="fieldsVo.dataType == 'tinyint'"> <when test="field.dataType == 'TINYINT'">
tinyint TINYINT
</when> </when>
<when test="fieldsVo.dataType == 'smallint'"> <when test="field.dataType == 'SMALLINT'">
smallint SMALLINT
</when> </when>
<when test="fieldsVo.dataType == 'int'"> <when test="field.dataType == 'INT'">
int INT
</when> </when>
<when test="fieldsVo.dataType == 'bigint'"> <when test="field.dataType == 'BIGINT'">
bigint BIGINT
</when> </when>
<when test="fieldsVo.dataType == 'float'"> <when test="field.dataType == 'FLOAT'">
float FLOAT
</when> </when>
<when test="fieldsVo.dataType == 'double'"> <when test="field.dataType == 'DOUBLE'">
double DOUBLE
</when> </when>
<when test="fieldsVo.dataType == 'binary'"> <when test="field.dataType == 'BINARY'">
binary BINARY
</when> </when>
<when test="fieldsVo.dataType == 'nchar'"> <when test="field.dataType == 'NCHAR'">
nchar NCHAR
</when> </when>
<when test="fieldsVo.dataType == 'bool'"> <when test="field.dataType == 'BOOL'">
bool BOOL
</when> </when>
<when test="fieldsVo.dataType == 'json'"> <when test="field.dataType == 'JSON'">
json JSON
</when> </when>
</choose> </choose>
</if> </if>
<if test="fieldsVo.size != null"> <if test="field.dataLength > 0">
( (
#{fieldsVo.size} #{field.dataLength}
) )
</if> </if>
</update> </update>
<update id="dropColumnForSuperTable"> <update id="dropColumnForSuperTable">
ALTER ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN
STABLE <if test="field.fieldName != null || field.fieldName != ''">
#{superTableName} #{field.fieldName}
DROP
COLUMN
<if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
#{fieldsVo.fieldName}
</if> </if>
</update> </update>
@ -215,49 +210,49 @@
#{superTableName} #{superTableName}
ADD ADD
TAG TAG
<if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''"> <if test="field.fieldName != null || fieldDO.fieldName != ''">
#{fieldsVo.fieldName} #{fieldDO.fieldName}
</if> </if>
<if test="fieldsVo.dataType != null || fieldsVo.dataType != ''"> <if test="fieldDO.dataType != null || fieldDO.dataType != ''">
<choose> <choose>
<when test="fieldsVo.dataType == 'timestamp'"> <when test="fieldDO.dataType == 'timestamp'">
timestamp timestamp
</when> </when>
<when test="fieldsVo.dataType == 'tinyint'"> <when test="fieldDO.dataType == 'tinyint'">
tinyint tinyint
</when> </when>
<when test="fieldsVo.dataType == 'smallint'"> <when test="fieldDO.dataType == 'smallint'">
smallint smallint
</when> </when>
<when test="fieldsVo.dataType == 'int'"> <when test="fieldDO.dataType == 'int'">
int int
</when> </when>
<when test="fieldsVo.dataType == 'bigint'"> <when test="fieldDO.dataType == 'bigint'">
bigint bigint
</when> </when>
<when test="fieldsVo.dataType == 'float'"> <when test="fieldDO.dataType == 'float'">
float float
</when> </when>
<when test="fieldsVo.dataType == 'double'"> <when test="fieldDO.dataType == 'double'">
double double
</when> </when>
<when test="fieldsVo.dataType == 'binary'"> <when test="fieldDO.dataType == 'binary'">
binary binary
</when> </when>
<when test="fieldsVo.dataType == 'nchar'"> <when test="fieldDO.dataType == 'nchar'">
nchar nchar
</when> </when>
<when test="fieldsVo.dataType == 'bool'"> <when test="fieldDO.dataType == 'bool'">
bool bool
</when> </when>
<when test="fieldsVo.dataType == 'json'"> <when test="fieldDO.dataType == 'json'">
json json
</when> </when>
</choose> </choose>
</if> </if>
<if test="fieldsVo.size != null"> <if test="fieldDO.dataLength > 0">
( (
#{fieldsVo.size} #{fieldDO.dataLength}
) )
</if> </if>
</update> </update>
@ -279,11 +274,8 @@
FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime} FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
</select> </select>
<select id="checkTableExists" resultType="java.lang.Integer"> <select id="showSuperTables" resultType="java.util.Map">
SELECT COUNT(0) SHOW ${dataBaseName}.STABLES LIKE '${superTableName}'
FROM information_schema.ins_tables
WHERE db_name = #{dataBaseName}
AND table_name = #{tableName}
</select> </select>
<select id="getLastData" resultType="java.util.Map"> <select id="getLastData" resultType="java.util.Map">
@ -304,11 +296,13 @@
FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime} FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime}
LIMIT #{num} LIMIT #{num}
</select> </select>
<select id="getRealtimeData" resultType="java.util.Map" <select id="getRealtimeData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto"> parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
SELECT #{fieldName}, ts SELECT #{fieldName}, ts
FROM #{dataBaseName}.#{tableName} LIMIT #{num} FROM #{dataBaseName}.#{tableName} LIMIT #{num}
</select> </select>
<select id="getAggregateData" resultType="java.util.Map" <select id="getAggregateData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto"> parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
SELECT #{aggregate}(${fieldName}) SELECT #{aggregate}(${fieldName})
@ -316,9 +310,8 @@
LIMIT #{num} LIMIT #{num}
</select> </select>
<select id="describeSuperTable" resultType="java.util.Map">
<insert id="createSuperTableDevice"> DESCRIBE ${dataBaseName}.${superTableName}
${sql} </select>
</insert>
</mapper> </mapper>