【优化功能】 优化 tdengine 操作数据库相关代码

This commit is contained in:
安浩浩 2024-11-08 23:07:37 +08:00
parent d7b8cf547f
commit e3dcea9cb3
51 changed files with 1262 additions and 1442 deletions

View File

@ -1,18 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
@Data
public class BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 数据库名称
*/
private String dataBaseName;
/**
* 超级表名称
*/
private String superTableName;
}

View File

@ -1,35 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
/**
* 设备数据导出 excel DTO
*/
@Data
public class DeviceDataExportExcelDto {
/**
* 设备标识
*/
private String deviceKey;
/**
* 导出形式 1 单个参数导出 2 全部参数导出
*/
private String exportType;
/**
* 导出开始时间
*/
private String exportBeginTime;
/**
* 导出结束时间
*/
private String exportEndTime;
/**
* 导出参数空则导出全部
*/
private String exportParameter;
}

View File

@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
/**
* @ClassDescription: 查询可视化所需入参对象
* @ClassName: SelectDto
* @Author: andyz
* @Date: 2022-07-29 14:12:26
* @Version 1.0
*/
@Data
public class DeviceDataVo {
private String deviceId;
private Long lastTime;
}

View File

@ -1,65 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
@Data
public class Fields {
private static final long serialVersionUID = 1L;
/**
* 字段名称
*/
private String fieldName;
/**
* 字段值
*/
private Object fieldValue;
/**
* 字段数据类型
*/
// private DataTypeEnum dataType;
/**
* 字段字节大小
*/
private Integer size;
public Fields() {
}
public Fields(String fieldName, String dataType, Integer size) {
// this.fieldName = fieldName;
// //根据规则匹配字段数据类型
// switch (dataType.toLowerCase()) {
// case ("json"):
// this.dataType = DataTypeEnum.JSON;
// this.size = size;
// break;
// case ("string"):
// this.dataType = DataTypeEnum.NCHAR;
// this.size = size;
// break;
// case ("binary"):
// this.dataType = DataTypeEnum.BINARY;
// this.size = size;
// break;
// case ("int"):
// this.dataType = DataTypeEnum.INT;
// break;
// case ("bool"):
// this.dataType = DataTypeEnum.BOOL;
// break;
// case ("decimal"):
// this.dataType = DataTypeEnum.DOUBLE;
// break;
// case ("timestamp"):
// if ("eventTime".equals(fieldName)) {
// this.fieldName = "eventTime";
// }
// this.dataType = DataTypeEnum.TIMESTAMP;
// break;
// }
}
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Builder;
import lombok.Data;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 字段信息 VO
*/
@Data
@Builder
public class FieldsVo {
/**
* 字段名称
*/
private String fieldName;
/**
* 字段数据类型
*/
private String dataType;
/**
* 字段字节大小
*/
private Integer size;
}

View File

@ -1,71 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.sql.Timestamp;
public class IotSequential extends BaseEntity {
private static final long serialVersionUID = 1L;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8")
private Timestamp statetime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8")
private Timestamp endtime;
private String deviceid;
private String eventtime;
private String serviceid;
private String devices;
public String getDeviceid() {
return deviceid;
}
public void setDeviceid(String deviceid) {
this.deviceid = deviceid;
}
public String getEventtime() {
return eventtime;
}
public void setEventtime(String eventtime) {
this.eventtime = eventtime;
}
public String getServiceid() {
return serviceid;
}
public void setServiceid(String serviceid) {
this.serviceid = serviceid;
}
public String getDevices() {
return devices;
}
public void setDevices(String devices) {
this.devices = devices;
}
public Timestamp getStatetime() {
return statetime;
}
public void setStatetime(Timestamp statetime) {
this.statetime = statetime;
}
public Timestamp getEndtime() {
return endtime;
}
public void setEndtime(Timestamp endtime) {
this.endtime = endtime;
}
}

View File

@ -1,25 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 统计的时间数据
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageCountVo {
/**
* 时间
*/
private String time;
/**
* 数据值
*/
private Object data;
}

View File

@ -1,29 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Optional;
@Data
public class ProductSuperTableModel {
private static final long serialVersionUID = 1L;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS", timezone = "GMT+8")
private Timestamp ts;
private String superTableName;
/**
* columnsName,columnsProperty
*/
private HashMap<Optional, Optional> columns;
/**
* tagsName,tagsProperty
*/
private HashMap<Optional, Optional> tags;
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
import java.util.Set;
@Data
public class SelectDto {
// @NotBlank(message = "invalid operation: dataBaseName can not be empty")
private String dataBaseName;
// @NotBlank(message = "invalid operation: tableName can not be empty")
private String tableName;
// @NotBlank(message = "invalid operation: fieldName can not be empty")
private String fieldName;
// @NotNull(message = "invalid operation: startTime can not be null")
private Long startTime;
// @NotNull(message = "invalid operation: endTime can not be null")
private Long endTime;
private String type;
private Set<Integer> orgIds;
private String deviceId;
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
import java.util.List;
@Data
public class SuperTableDto extends BaseEntity {
/**
* 超级表的表结构业务相关
* 第一个字段的数据类型必须为timestamp
* 字符相关数据类型必须指定大小
* 字段名称和字段数据类型不能为空
*/
// @NotEmpty(message = "invalid operation: schemaFields can not be empty")
private List<Fields> schemaFields;
/**
* 超级表的标签字段可以作为子表在超级表里的标识
* 字符相关数据类型必须指定大小
* 字段名称和字段数据类型不能为空
*/
// @NotEmpty(message = "invalid operation: tagsFields can not be empty")
private List<Fields> tagsFields;
/**
* 字段信息对象超级表添加列时使用该属性
*/
private Fields fields;
}

View File

@ -1,26 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
import java.util.List;
@Data
public class TableDto extends BaseEntity {
/**
* 超级表普通列字段的值
* 值需要与创建超级表时普通列字段的数据类型对应上
*/
private List<Fields> schemaFieldValues;
/**
* 超级表标签字段的值
* 值需要与创建超级表时标签字段的数据类型对应上
*/
private List<Fields> tagsFieldValues;
/**
* 表名称
*/
private String tableName;
}

View File

@ -1,25 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import lombok.Data;
@Data
public class TagsSelectDao {
// @NotBlank(message = "invalid operation: dataBaseName can not be empty")
private String dataBaseName;
// @NotBlank(message = "invalid operation: stableName can not be empty")
private String stableName;
// @NotBlank(message = "invalid operation: tagsName can not be empty")
private String tagsName;
// @NotNull(message = "invalid operation: startTime can not be null")
private Long startTime;
// @NotNull(message = "invalid operation: endTime can not be null")
private Long endTime;
}

View File

@ -1,73 +0,0 @@
package cn.iocoder.yudao.module.iot.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.sql.Timestamp;
public class Weather {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8")
private Timestamp ts;
private Float temperature;
private Float humidity;
private String location;
private String note;
private int groupId;
public Weather() {
}
public Weather(Timestamp ts, float temperature, float humidity) {
this.ts = ts;
this.temperature = temperature;
this.humidity = humidity;
}
public Timestamp getTs() {
return ts;
}
public void setTs(Timestamp ts) {
this.ts = ts;
}
public Float getTemperature() {
return temperature;
}
public void setTemperature(Float temperature) {
this.temperature = temperature;
}
public Float getHumidity() {
return humidity;
}
public void setHumidity(Float humidity) {
this.humidity = humidity;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}

View File

@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.Data;
import java.util.Set;
/**
* 查询DO
*/
@Data
public class SelectDO {
/**
* 数据库名称
*/
private String dataBaseName;
/**
* 超级表名称
*/
private String tableName;
/**
* 查询字段
*/
private String fieldName;
/**
* 开始时间
*/
private Long startTime;
/**
* 结束时间
*/
private Long endTime;
/**
* 查询类型
*/
private String type;
/**
* 查询条件
*/
private Set<Integer> orgIds;
/**
* 设备ID
*/
private String deviceId;
}

View File

@ -1,11 +1,11 @@
package cn.iocoder.yudao.module.iot.domain.visual; package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.Data; import lombok.Data;
import java.util.Map; import java.util.Map;
@Data @Data
public class SelectVisualDto { public class SelectVisualDO {
/** /**
* 数据库名称 * 数据库名称

View File

@ -1,27 +0,0 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import cn.iocoder.yudao.module.iot.domain.BaseEntity;
import lombok.Data;
import java.util.List;
@Data
public class TableDO extends BaseEntity {
/**
* 超级表普通列字段的值
* 值需要与创建超级表时普通列字段的数据类型对应上
*/
private List<TdFieldDO> schemaFieldValues;
/**
* 超级表标签字段的值
* 值需要与创建超级表时标签字段的数据类型对应上
*/
private List<TdFieldDO> tagsFieldValues;
/**
* 表名称
*/
private String tableName;
}

View File

@ -1,44 +0,0 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.Data;
import java.util.List;
/**
* TableData 类用于存储和操作 TDengine 表数据
*/
@Data
public class TableData {
/**
* 超级表普通列字段的名称
*/
private List<String> schemaFieldList;
/**
* 超级表普通列字段的值
* 值需要与创建超级表时普通列字段的数据类型对应上
*/
private List<Object> schemaValueList;
/**
* 超级表标签字段的名称
*/
private List<String> tagsFieldList;
/**
* 超级表标签字段的值
* 值需要与创建超级表时标签字段的数据类型对应上
*/
private List<Object> tagsValueList;
/**
* 表名称
*/
private String tableName;
/**
* 超级表名称
*/
private String superTableName;
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.Data;
/**
* tags查询DO
*/
@Data
public class TagsSelectDO {
/**
* 数据库名称
*/
private String dataBaseName;
/**
* 超级表名称
*/
private String stableName;
/**
* tags名称
*/
private String tagsName;
/**
* tags值
*/
private Long startTime;
/**
* tags值
*/
private Long endTime;
}

View File

@ -19,11 +19,6 @@ public class TdFieldDO {
*/ */
private String fieldName; private String fieldName;
/**
* 字段值
*/
private Object fieldValue;
/** /**
* 字段类型 * 字段类型
*/ */
@ -34,6 +29,11 @@ public class TdFieldDO {
*/ */
private Integer dataLength = 0; private Integer dataLength = 0;
/**
* 字段值
*/
private Object fieldValue;
public TdFieldDO(String fieldName, String dataType, Integer dataLength) { public TdFieldDO(String fieldName, String dataType, Integer dataLength) {
this.fieldName = fieldName; this.fieldName = fieldName;
this.dataType = dataType; this.dataType = dataType;

View File

@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* TD 引擎的数据库
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TdTableDO {
/**
* 数据库名称
*/
private String dataBaseName;
/**
* 超级表名称
*/
private String superTableName;
/**
* 表名称
*/
private String tableName;
/**
* COLUMN 字段
*/
private TdFieldDO column;
/**
* TAG 字段
*/
private TdFieldDO tag;
/**
* COLUMN 字段 - 列表
*/
private List<TdFieldDO> columns;
/**
* TAG 字段 - 列表
*/
private List<TdFieldDO> tags;
public TdTableDO(String dataBaseName, String superTableName, List<TdFieldDO> columns, List<TdFieldDO> tags) {
this.dataBaseName = dataBaseName;
this.superTableName = superTableName;
this.columns = columns;
this.tags = tags;
}
public TdTableDO(String dataBaseName, String superTableName) {
this.dataBaseName = dataBaseName;
this.superTableName = superTableName;
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
/**
* TD 引擎的数据写入 Mapper
*/
@Mapper
@DS("tdengine")
public interface TdEngineDataWriterMapper {
/**
* 插入数据 - 指定列插入数据
*
* @param table 数据
* dataBaseName 数据库名
* tableName 表名
* columns
*/
@InterceptorIgnore(tenantLine = "true")
void insertData(TdTableDO table);
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* TD 引擎的数据库 Mapper
*/
@Mapper
@DS("tdengine")
public interface TdEngineDatabaseMapper {
/**
* 创建数据库
* SQLCREATE DATABASE [IF NOT EXISTS] db_name [database_options];
*
* @param dataBaseName 数据库名称
*/
@InterceptorIgnore(tenantLine = "true")
void createDatabase(@Param("dataBaseName") String dataBaseName);
}

View File

@ -1,129 +0,0 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
import cn.iocoder.yudao.module.iot.domain.FieldsVo;
import cn.iocoder.yudao.module.iot.domain.SelectDto;
import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
@DS("tdengine")
public interface TdEngineMapper {
/**
* 创建数据库
*
* @param dataBaseName 数据库名称
*/
@InterceptorIgnore(tenantLine = "true")
void createDatabase(@Param("dataBaseName") String dataBaseName);
/**
* 创建超级表
*
* @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("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);
/**
* 创建表 - 创建超级表的子表
*
* @param tableDO 表信息
*/
@InterceptorIgnore(tenantLine = "true")
void createTable(TableDO tableDO);
/**
* 插入数据 - 指定列插入数据
*
* @param tableDto 数据
*/
@InterceptorIgnore(tenantLine = "true")
void insertData(TableDO tableDto);
List<Map<String, Object>> selectByTimestamp(SelectDto selectDto);
void addTagForSuperTable(@Param("superTableName") String superTableName,
@Param("fieldsVo") FieldsVo fieldsVo);
void dropTagForSuperTable(@Param("superTableName") String superTableName,
@Param("fieldsVo") FieldsVo fieldsVo);
Map<String, Long> getCountByTimestamp(SelectDto selectDto);
Map<String, Object> getLastData(SelectDto selectDto);
@InterceptorIgnore(tenantLine = "true")
List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto);
List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto);
List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
@InterceptorIgnore(tenantLine = "true")
Long getHistoryCount(SelectVisualDto selectVisualDto);
}

View File

@ -0,0 +1,85 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
/**
* TD 引擎的查询 Mapper
*/
@Mapper
@DS("tdengine")
public interface TdEngineQueryMapper {
/**
* 根据时间戳查询数据
*
* @param selectDO 查询条件
* @return 查询结果
*/
List<Map<String, Object>> selectByTimestamp(SelectDO selectDO);
/**
* 根据时间戳获取数据条数
*
* @param selectDO 查询条件
* @return 数据条数
*/
Map<String, Long> getCountByTimestamp(SelectDO selectDO);
/**
* 获取最新数据
*
* @param selectDO 查询条件
* @return 最新数据
*/
Map<String, Object> getLastData(SelectDO selectDO);
/**
* 获取历史数据
*
* @param selectVisualDO 查询条件
* @return 历史数据列表
*/
@InterceptorIgnore(tenantLine = "true")
List<Map<String, Object>> getHistoryData(SelectVisualDO selectVisualDO);
/**
* 获取实时数据
*
* @param selectVisualDO 查询条件
* @return 实时数据列表
*/
List<Map<String, Object>> getRealtimeData(SelectVisualDO selectVisualDO);
/**
* 获取聚合数据
*
* @param selectVisualDO 查询条件
* @return 聚合数据列表
*/
List<Map<String, Object>> getAggregateData(SelectVisualDO selectVisualDO);
/**
* 根据标签获取最新数据
*
* @param tagsSelectDO 查询条件
* @return 最新数据列表
*/
List<Map<String, Object>> getLastDataByTags(TagsSelectDO tagsSelectDO);
/**
* 获取历史数据条数
*
* @param selectVisualDO 查询条件
* @return 数据条数
*/
@InterceptorIgnore(tenantLine = "true")
Long getHistoryCount(SelectVisualDO selectVisualDO);
}

View File

@ -0,0 +1,111 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
/**
* TD 引擎的超级表 Mapper
*/
@Mapper
@DS("tdengine")
public interface TdEngineSuperTableMapper {
/**
* 创建超级表
* SQLCREATE STABLE [IF NOT EXISTS] stb_name (create_definition [, create_definition] ...) TAGS (create_definition [, create_definition] ...) [table_options];
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* columns 列信息
* tags 标签信息
*/
@InterceptorIgnore(tenantLine = "true")
void createSuperTable(TdTableDO superTable);
/**
* 查看超级表 - 显示当前数据库下的所有超级表信息
* SQLSHOW STABLES [LIKE tb_name_wildcard];
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
*/
@InterceptorIgnore(tenantLine = "true")
List<Map<String, Object>> showSuperTables(TdTableDO superTable);
/**
* 查看超级表 - 获取超级表的结构信息
* SQLDESCRIBE [db_name.]stb_name;
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
*/
@InterceptorIgnore(tenantLine = "true")
List<Map<String, Object>> describeSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 增加列
* SQLALTER STABLE stb_name ADD COLUMN col_name column_type;
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* column 列信息
*/
@InterceptorIgnore(tenantLine = "true")
void addColumnForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 删除列
* SQLALTER STABLE stb_name DROP COLUMN col_name;
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* column 列信息
*/
@InterceptorIgnore(tenantLine = "true")
void dropColumnForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 修改列宽
* SQLALTER STABLE stb_name MODIFY COLUMN col_name data_type(length);
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* column 列信息
*/
@InterceptorIgnore(tenantLine = "true")
void modifyColumnWidthForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 为超级表添加标签
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* tag 标签信息
*/
@InterceptorIgnore(tenantLine = "true")
void addTagForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 为超级表删除标签
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* tag 标签信息
*/
@InterceptorIgnore(tenantLine = "true")
void dropTagForSuperTable(TdTableDO superTable);
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.iot.dal.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper;
/**
* TD 引擎的表 Mapper
*/
@Mapper
@DS("tdengine")
public interface TdEngineTableMapper {
/**
* 创建子表 - 创建子表
* SQLCREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...);
*
* @param table 表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* tableName 子表名称
* tags TAG 字段
*/
@InterceptorIgnore(tenantLine = "true")
void createTable(TdTableDO table);
/**
* 创建子表 - 创建子表并指定标签的值
* SQLCREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...);
*
* @param table 表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* tableName 子表名称
* tags TAG 字段
*/
@InterceptorIgnore(tenantLine = "true")
void createTableWithTags(TdTableDO table);
}

View File

@ -6,13 +6,13 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
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.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
import cn.iocoder.yudao.module.iot.service.tdengine.TdEngineQueryService;
import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@ -39,13 +39,12 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
private IotThingModelMessageService thingModelMessageService; private IotThingModelMessageService thingModelMessageService;
@Resource @Resource
private IotThinkModelFunctionService thinkModelFunctionService; private IotThinkModelFunctionService thinkModelFunctionService;
@Resource
private TdEngineQueryService tdEngineQueryService;
@Resource @Resource
private DeviceDataRedisDAO deviceDataRedisDAO; private DeviceDataRedisDAO deviceDataRedisDAO;
@Resource
private TdEngineMapper tdEngineMapper;
@Override @Override
public void saveDeviceData(String productKey, String deviceName, String message) { public void saveDeviceData(String productKey, String deviceName, String message) {
// 1. 根据产品 key 和设备名称获得设备信息 // 1. 根据产品 key 和设备名称获得设备信息
@ -112,18 +111,18 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
// 1. 获取设备信息 // 1. 获取设备信息
IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
// 2. 获取设备属性历史数据 // 2. 获取设备属性历史数据
SelectVisualDto selectVisualDto = new SelectVisualDto(); SelectVisualDO selectVisualDO = new SelectVisualDO();
selectVisualDto.setDataBaseName(getDatabaseName()); selectVisualDO.setDataBaseName(getDatabaseName());
selectVisualDto.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));
selectVisualDto.setFieldName(deviceDataReqVO.getIdentifier()); selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier());
selectVisualDto.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
selectVisualDto.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put("rows", deviceDataReqVO.getPageSize()); params.put("rows", deviceDataReqVO.getPageSize());
params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
selectVisualDto.setParams(params); selectVisualDO.setParams(params);
pageResult.setList(tdEngineMapper.getHistoryData(selectVisualDto)); pageResult.setList(tdEngineQueryService.getHistoryData(selectVisualDO));
pageResult.setTotal(tdEngineMapper.getHistoryCount(selectVisualDto)); pageResult.setTotal(tdEngineQueryService.getHistoryCount(selectVisualDO));
return pageResult; return pageResult;
} }

View File

@ -1,26 +1,15 @@
package cn.iocoder.yudao.module.iot.service.tdengine; package cn.iocoder.yudao.module.iot.service.tdengine;
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.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
import java.util.List; import java.util.List;
/** /**
* 数据结构接口 * IoT 超级表服务负责根据物模型创建和更新超级表以及创建超级表的子表等操作
*/ */
public interface IotDbStructureDataService { public interface IotSuperTableService {
/**
* 创建物模型定义
*/
void createSuperTable(ThingModelRespVO thingModel, Integer deviceType);
/**
* 更新物模型定义
*/
void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType);
/** /**
* 创建超级表数据模型 * 创建超级表数据模型

View File

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

View File

@ -1,142 +0,0 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO;
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.TagsSelectDao;
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
import java.util.List;
import java.util.Map;
/**
* TdEngineService
*/
public interface IotTdEngineService {
/**
* 创建数据库
*
* @param dataBaseName 数据库名称
* @throws Exception 异常
*/
void createDateBase(String dataBaseName) throws Exception;
/**
* 创建超级表
*
* @param schemaFields schema字段
* @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 superTableName 超级表名称
* @param fieldsVo 字段信息
*/
void addColumnForSuperTable(String dataBaseName, String superTableName, List<TdFieldDO> fieldsVo);
/**
* 为超级表删除列
*
* @param dataBaseName 数据库名称
* @param superTableName 超级表名称
* @param fieldsVo 字段信息
*/
void dropColumnForSuperTable(String dataBaseName, String superTableName, List<TdFieldDO> fieldsVo);
/**
* 为超级表添加tag
*/
Long getCountByTimesTamp(SelectDto selectDto) throws Exception;
/**
* 创建表
*
* @param tableDto 表信息
*/
void createTable(TableDO tableDto);
/**
* 插入数据
*
* @param tableDto 表信息
*/
void insertData(TableDO tableDto);
/**
* 根据时间戳查询数据
*
* @param selectDto 查询条件
* @return 数据
* @throws Exception 异常
*/
List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) throws Exception;
/**
* 初始化超级表
*
* @param msg 消息
* @throws Exception 异常
*/
void initSTableFrame(String msg) throws Exception;
/**
* 获取最新数据
*
* @param selectDto 查询条件
* @return 数据
* @throws Exception 异常
*/
Map<String, Object> getLastData(SelectDto selectDto) throws Exception;
/**
* 根据tag查询最新数据
*
* @param tagsSelectDao 查询条件
* @return 数据
*/
Map<String, Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
/**
* 获取历史数据
*
* @param selectVisualDto 查询条件
* @return 数据
*/
List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
/**
* 获取实时数据
*
* @param selectVisualDto 查询条件
* @return 数据
*/
List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto);
/**
* 获取聚合数据
*
* @param selectVisualDto 查询条件
* @return 数据
*/
List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto);
}

View File

@ -1,107 +0,0 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO;
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.TagsSelectDao;
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
@Slf4j
public class IotTdEngineServiceImpl implements IotTdEngineService {
@Resource
private TdEngineMapper tdEngineMapper;
@Override
public void createDateBase(String dataBaseName) {
tdEngineMapper.createDatabase(dataBaseName);
}
@Override
public void createSuperTable(List<TdFieldDO> schemaFields, List<TdFieldDO> tagsFields, String dataBaseName, String superTableName) {
tdEngineMapper.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
}
@Override
public void createTable(TableDO tableDto) {
tdEngineMapper.createTable(tableDto);
}
@Override
public void insertData(TableDO tableDto) {
tdEngineMapper.insertData(tableDto);
}
@Override
public List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) {
return List.of();
}
@Override
public void addColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fields) {
for (TdFieldDO field : fields) {
tdEngineMapper.addColumnForSuperTable(dataBaseName,superTableName, field);
}
}
@Override
public void dropColumnForSuperTable(String dataBaseName,String superTableName, List<TdFieldDO> fields) {
for (TdFieldDO field : fields) {
tdEngineMapper.dropColumnForSuperTable(dataBaseName,superTableName, field);
}
}
@Override
public Long getCountByTimesTamp(SelectDto selectDto) {
return 0L;
}
@Override
public void initSTableFrame(String msg) {
}
@Override
public Map<String, Object> getLastData(SelectDto selectDto) {
return Map.of();
}
@Override
public Map<String, Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao) {
return Map.of();
}
@Override
public List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto) {
return List.of();
}
@Override
public List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto) {
return List.of();
}
@Override
public List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto) {
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

@ -14,5 +14,5 @@ public interface IotThingModelMessageService {
* @param device 设备 * @param device 设备
* @param thingModelMessage 物模型消息 * @param thingModelMessage 物模型消息
*/ */
void saveThingModelMessage(IotDeviceDO device,ThingModelMessage thingModelMessage); void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage);
} }

View File

@ -6,8 +6,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSt
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
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.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
@ -23,6 +23,9 @@ import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* 物模型消息 Service 实现类
*/
@Slf4j @Slf4j
@Service @Service
public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { public class IotThingModelMessageServiceImpl implements IotThingModelMessageService {
@ -35,7 +38,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
@Resource @Resource
private IotDeviceService iotDeviceService; private IotDeviceService iotDeviceService;
@Resource @Resource
private IotTdEngineService iotTdEngineService; private TdEngineTableService tdEngineTableService;
@Resource
private TdEngineSuperTableService tdEngineSuperTableService;
@Resource
private TdEngineDataWriterService tdEngineDataWriterService;
@Resource @Resource
private DeviceDataRedisDAO deviceDataRedisDAO; private DeviceDataRedisDAO deviceDataRedisDAO;
@ -90,14 +97,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
return; return;
} }
// 构建并保存设备属性 // 构建并保存设备属性数据
TableDO tableData = new TableDO(); tdEngineDataWriterService.insertData(TdTableDO.builder().build()
tableData.setDataBaseName(getDatabaseName()); .setDataBaseName(getDatabaseName())
tableData.setSuperTableName(getProductPropertySTableName(device.getDeviceType(), device.getProductKey())); .setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()))
tableData.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); .setColumns(schemaFieldValues));
tableData.setSchemaFieldValues(schemaFieldValues);
iotTdEngineService.insertData(tableData);
} }
/** /**
@ -135,7 +139,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
String superTableName = getProductPropertySTableName(deviceType, productKey); String superTableName = getProductPropertySTableName(deviceType, productKey);
String dataBaseName = getDatabaseName(); String dataBaseName = getDatabaseName();
List<Map<String, Object>> maps = iotTdEngineService.describeSuperTable(dataBaseName, superTableName); List<Map<String, Object>> maps = tdEngineSuperTableService.describeSuperTable(new TdTableDO(dataBaseName, superTableName));
List<TdFieldDO> tagsFieldValues = new ArrayList<>(); List<TdFieldDO> tagsFieldValues = new ArrayList<>();
if (maps != null) { if (maps != null) {
@ -159,13 +163,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
// 创建设备数据表 // 创建设备数据表
String tableName = getDeviceTableName(productKey, deviceName); String tableName = getDeviceTableName(productKey, deviceName);
TableDO tableDto = new TableDO(); tdEngineTableService.createTable(TdTableDO.builder().build()
tableDto.setDataBaseName(dataBaseName); .setDataBaseName(dataBaseName)
tableDto.setSuperTableName(superTableName); .setSuperTableName(superTableName)
tableDto.setTableName(tableName); .setTableName(tableName)
tableDto.setTagsFieldValues(tagsFieldValues); .setTags(tagsFieldValues));
iotTdEngineService.createTable(tableDto);
} }
/** /**

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
/**
* TD 引擎的数据写入 Service 接口
*/
public interface TdEngineDataWriterService {
/**
* 插入数据 - 指定列插入数据
*
* @param table 数据
* dataBaseName 数据库名
* tableName 表名
* columns
*/
void insertData(TdTableDO table);
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
/**
* TD 引擎的数据写入 Service 实现类
*/
@Service
public class TdEngineDataWriterServiceImpl implements TdEngineDataWriterService {
@Resource
private TdEngineDataWriterMapper tdEngineDataWriterMapper;
@Override
public void insertData(TdTableDO table) {
tdEngineDataWriterMapper.insertData(table);
}
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
/**
* TD 引擎的数据库 Service 接口
*/
public interface TdEngineDatabaseService {
/**
* 创建数据库
*
* @param dataBaseName 数据库名称
*/
void createDatabase(String dataBaseName);
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* TD 引擎的数据库 Service 实现类
*/
@Service
@Slf4j
public class TdEngineDatabaseServiceImpl implements TdEngineDatabaseService {
@Resource
private TdEngineDatabaseMapper tdEngineDatabaseMapper;
@Override
public void createDatabase(String dataBaseName) {
tdEngineDatabaseMapper.createDatabase(dataBaseName);
}
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
import java.util.List;
import java.util.Map;
/**
* TD 引擎的查询 Service 接口
*/
public interface TdEngineQueryService {
/**
* 获取历史数据
*
* @param selectVisualDO 查询条件
* @return 历史数据列表
*/
List<Map<String, Object>> getHistoryData(SelectVisualDO selectVisualDO);
/**
* 获取历史数据条数
*
* @param selectVisualDO 查询条件
* @return 数据条数
*/
Long getHistoryCount(SelectVisualDO selectVisualDO);
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineQueryMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class TdEngineQueryServiceImpl implements TdEngineQueryService {
@Resource
private TdEngineQueryMapper tdEngineQueryMapper;
@Override
public List<Map<String, Object>> getHistoryData(SelectVisualDO selectVisualDO) {
return tdEngineQueryMapper.getHistoryData(selectVisualDO);
}
@Override
public Long getHistoryCount(SelectVisualDO selectVisualDO) {
return tdEngineQueryMapper.getHistoryCount(selectVisualDO);
}
}

View File

@ -0,0 +1,123 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import java.util.List;
import java.util.Map;
/**
* TD 引擎的超级表 Service 接口
*/
public interface TdEngineSuperTableService {
/**
* 创建超级表
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* columns 列信息
* tags 标签信息
*/
void createSuperTable(TdTableDO superTable);
/**
* 查看超级表 - 显示当前数据库下的所有超级表信息
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
*/
List<Map<String, Object>> showSuperTables(TdTableDO superTable);
/**
* 查看超级表 - 获取超级表的结构信息
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
*/
List<Map<String, Object>> describeSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 增加列
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* column 列信息
*/
void addColumnForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 删除列
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* column 列信息
*/
void dropColumnForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 修改列宽
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* column 列信息
*/
void modifyColumnWidthForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 为超级表添加标签
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* tag 标签信息
*/
void addTagForSuperTable(TdTableDO superTable);
/**
* 修改超级表 - 为超级表删除标签
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* tag 标签信息
*/
void dropTagForSuperTable(TdTableDO superTable);
/**
* 检查超级表是否存在
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* @return 超级表数量
*/
Integer checkSuperTableExists(TdTableDO superTable);
/**
* 为超级表添加列
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* columns 列信息
*/
void addColumnsForSuperTable(TdTableDO superTable);
/**
* 为超级表删除列
*
* @param superTable 超级表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* columns 列信息
*/
void dropColumnsForSuperTable(TdTableDO superTable);
}

View File

@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineSuperTableMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* TD 引擎的超级表 Service 实现类
*/
@Slf4j
@Service
public class TdEngineSuperTableServiceImpl implements TdEngineSuperTableService {
@Resource
private TdEngineSuperTableMapper tdEngineSuperTableMapper;
@Override
public void createSuperTable(TdTableDO superTable) {
tdEngineSuperTableMapper.createSuperTable(superTable);
}
@Override
public List<Map<String, Object>> showSuperTables(TdTableDO superTable) {
return tdEngineSuperTableMapper.showSuperTables(superTable);
}
@Override
public List<Map<String, Object>> describeSuperTable(TdTableDO superTable) {
return tdEngineSuperTableMapper.describeSuperTable(superTable);
}
@Override
public void addColumnForSuperTable(TdTableDO superTable) {
tdEngineSuperTableMapper.addColumnForSuperTable(superTable);
}
@Override
public void dropColumnForSuperTable(TdTableDO superTable) {
tdEngineSuperTableMapper.dropColumnForSuperTable(superTable);
}
@Override
public void modifyColumnWidthForSuperTable(TdTableDO superTable) {
tdEngineSuperTableMapper.modifyColumnWidthForSuperTable(superTable);
}
@Override
public void addTagForSuperTable(TdTableDO superTable) {
tdEngineSuperTableMapper.addTagForSuperTable(superTable);
}
@Override
public void dropTagForSuperTable(TdTableDO superTable) {
tdEngineSuperTableMapper.dropTagForSuperTable(superTable);
}
@Override
public Integer checkSuperTableExists(TdTableDO superTable) {
List<Map<String, Object>> results = tdEngineSuperTableMapper.showSuperTables(superTable);
return results == null || results.isEmpty() ? 0 : results.size();
}
@Override
public void addColumnsForSuperTable(TdTableDO superTable) {
for (TdFieldDO column : superTable.getColumns()) {
tdEngineSuperTableMapper.addColumnForSuperTable(TdTableDO.builder()
.dataBaseName(superTable.getDataBaseName())
.superTableName(superTable.getSuperTableName())
.column(column)
.build());
}
}
@Override
public void dropColumnsForSuperTable(TdTableDO superTable) {
for (TdFieldDO column : superTable.getColumns()) {
tdEngineSuperTableMapper.dropColumnForSuperTable(TdTableDO.builder()
.dataBaseName(superTable.getDataBaseName())
.superTableName(superTable.getSuperTableName())
.column(column)
.build());
}
}
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
/**
* TD 引擎的表 Service 接口
*/
public interface TdEngineTableService {
/**
* 创建表 - 创建超级表的子表
*
* @param table 表信息
* dataBaseName 数据库名称
* superTableName 超级表名称
* tableName 子表名称
* tags TAG 字段
*/
void createTable(TdTableDO table);
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* TD 引擎的表 Service 实现类
*/
@Slf4j
@Service
public class TdEngineTableServiceImpl implements TdEngineTableService {
@Resource
private TdEngineTableMapper tdEngineTableMapper;
@Override
public void createTable(TdTableDO table) {
tdEngineTableMapper.createTable(table);
}
}

View File

@ -22,7 +22,7 @@ import cn.iocoder.yudao.module.iot.enums.product.IotAccessModeEnum;
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.product.IotProductService;
import cn.iocoder.yudao.module.iot.service.tdengine.IotDbStructureDataService; import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -52,7 +52,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
@Resource @Resource
private IotProductService productService; private IotProductService productService;
@Resource @Resource
private IotDbStructureDataService dbStructureDataService; private IotSuperTableService dbStructureDataService;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper">
<!-- 插入数据 -->
<insert id="insertData">
INSERT INTO ${dataBaseName}.${tableName}
<foreach item="item" collection="columns" separator=","
open="(" close=")">
${item.fieldName}
</foreach>
VALUES
<foreach item="item" collection="columns" separator=","
open="(" close=")">
#{item.fieldValue}
</foreach>
</insert>
</mapper>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper">
<!-- 创建数据库 -->
<update id="createDatabase" parameterType="String">
CREATE DATABASE IF NOT EXISTS ${dataBaseName}
</update>
</mapper>

View File

@ -1,327 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper">
<!-- TODO 对 $ 符号有安全要求的话,后期改为接口方式 -->
<update id="createDatabase" parameterType="String">
CREATE DATABASE IF NOT EXISTS ${dataBaseName}
</update>
<update id="createSuperTable">
CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName}
<foreach item="item" collection="schemaFields" separator=","
open="(" close=")" index="">
<if test="item.fieldName != null || item.fieldName != ''">
${item.fieldName}
</if>
<if test="item.dataType != null || item.dataType != ''">
<choose>
<when test="item.dataType == 'TIMESTAMP'">
TIMESTAMP
</when>
<when test="item.dataType == 'TINYINT'">
TINYINT
</when>
<when test="item.dataType == 'SMALLINT'">
SMALLINT
</when>
<when test="item.dataType == 'INT'">
INT
</when>
<when test="item.dataType == 'BIGINT'">
BIGINT
</when>
<when test="item.dataType == 'FLOAT'">
FLOAT
</when>
<when test="item.dataType == 'DOUBLE'">
DOUBLE
</when>
<when test="item.dataType == 'BINARY'">
BINARY
</when>
<when test="item.dataType == 'NCHAR'">
NCHAR
</when>
<when test="item.dataType == 'BOOL'">
BOOL
</when>
<when test="item.dataType == 'JSON'">
JSON
</when>
</choose>
</if>
<if test="item.dataLength > 0">
(
${item.dataLength}
)
</if>
</foreach>
TAGS
<!--tdEngine不支持动态tags里的数据类型只能使用choose标签比对-->
<foreach item="item" collection="tagsFields" separator=","
open="(" close=")" index="">
<if test="item.fieldName != null || item.fieldName != ''">
${item.fieldName}
</if>
<if test="item.dataType != null || item.dataType != ''">
<choose>
<when test="item.dataType == 'TIMESTAMP'">
TIMESTAMP
</when>
<when test="item.dataType == 'TINYINT'">
TINYINT
</when>
<when test="item.dataType == 'SMALLINT'">
SMALLINT
</when>
<when test="item.dataType == 'INT'">
INT
</when>
<when test="item.dataType == 'BIGINT'">
BIGINT
</when>
<when test="item.dataType == 'FLOAT'">
FLOAT
</when>
<when test="item.dataType == 'DOUBLE'">
DOUBLE
</when>
<when test="item.dataType == 'BINARY'">
BINARY
</when>
<when test="item.dataType == 'NCHAR'">
NCHAR
</when>
<when test="item.dataType == 'BOOL'">
BOOL
</when>
<when test="item.dataType == 'JSON'">
JSON
</when>
</choose>
</if>
<if test="item.dataLength > 0">
(
${item.dataLength}
)
</if>
</foreach>
</update>
<!-- CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...);-->
<update id="createTable">
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
USING ${dataBaseName}.${superTableName}
<foreach item="item" collection="tagsFieldValues" separator="," open="(" close=")">
${item.fieldName}
</foreach>
TAGS
<foreach item="item" collection="tagsFieldValues" separator="," open="(" close=")">
#{item.fieldValue}
</foreach>
</update>
<!-- insert into d1004 (ts, voltage, phase) values("2018-10-04 14:38:06", 223, 0.29) -->
<insert id="insertData">
INSERT INTO ${dataBaseName}.${tableName}
<foreach item="item" collection="schemaFieldValues" separator=","
open="(" close=")" index="">
${item.fieldName}
</foreach>
VALUES
<foreach item="item" collection="schemaFieldValues" separator=","
open="(" close=")" index="">
#{item.fieldValue}
</foreach>
</insert>
<select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto"
resultType="Map">
select * from #{dataBaseName}.#{tableName}
<!--查询这里不能使用#{}占位符的方式使用这种方式tdEngine不识别为列名只能使用${}占位的方式-->
<!--因为tdEngine引擎一次只执行一条sql所以有效预防了sql的注入且该服务该接口为内部调用所以可以使用${}-->
where ${fieldName}
between #{startTime} and #{endTime}
</select>
<update id="addColumnForSuperTable">
ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN
<if test="field.fieldName != null || field.fieldName != ''">
#{field.fieldName}
</if>
<if test="field.dataType != null || field.dataType != ''">
<choose>
<when test="field.dataType == 'TIMESTAMP'">
TIMESTAMP
</when>
<when test="field.dataType == 'TINYINT'">
TINYINT
</when>
<when test="field.dataType == 'SMALLINT'">
SMALLINT
</when>
<when test="field.dataType == 'INT'">
INT
</when>
<when test="field.dataType == 'BIGINT'">
BIGINT
</when>
<when test="field.dataType == 'FLOAT'">
FLOAT
</when>
<when test="field.dataType == 'DOUBLE'">
DOUBLE
</when>
<when test="field.dataType == 'BINARY'">
BINARY
</when>
<when test="field.dataType == 'NCHAR'">
NCHAR
</when>
<when test="field.dataType == 'BOOL'">
BOOL
</when>
<when test="field.dataType == 'JSON'">
JSON
</when>
</choose>
</if>
<if test="field.dataLength > 0">
(
#{field.dataLength}
)
</if>
</update>
<update id="dropColumnForSuperTable">
ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN
<if test="field.fieldName != null || field.fieldName != ''">
#{field.fieldName}
</if>
</update>
<update id="addTagForSuperTable">
ALTER
STABLE
#{superTableName}
ADD
TAG
<if test="field.fieldName != null || fieldDO.fieldName != ''">
#{fieldDO.fieldName}
</if>
<if test="fieldDO.dataType != null || fieldDO.dataType != ''">
<choose>
<when test="fieldDO.dataType == 'timestamp'">
timestamp
</when>
<when test="fieldDO.dataType == 'tinyint'">
tinyint
</when>
<when test="fieldDO.dataType == 'smallint'">
smallint
</when>
<when test="fieldDO.dataType == 'int'">
int
</when>
<when test="fieldDO.dataType == 'bigint'">
bigint
</when>
<when test="fieldDO.dataType == 'float'">
float
</when>
<when test="fieldDO.dataType == 'double'">
double
</when>
<when test="fieldDO.dataType == 'binary'">
binary
</when>
<when test="fieldDO.dataType == 'nchar'">
nchar
</when>
<when test="fieldDO.dataType == 'bool'">
bool
</when>
<when test="fieldDO.dataType == 'json'">
json
</when>
</choose>
</if>
<if test="fieldDO.dataLength > 0">
(
#{fieldDO.dataLength}
)
</if>
</update>
<update id="dropTagForSuperTable">
ALTER
STABLE
#{superTableName}
DROP
TAG
<if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
#{fieldsVo.fieldName}
</if>
</update>
<select id="getCountByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto"
resultType="java.util.Map">
SELECT count(0) AS count
FROM #{dataBaseName}.#{tableName}
WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
</select>
<select id="showSuperTables" resultType="java.util.Map">
SHOW ${dataBaseName}.STABLES LIKE '${superTableName}'
</select>
<select id="getLastData" resultType="java.util.Map">
select last(time), *
from #{tableName}
where device_id = #{deviceId}
</select>
<select id="getLastDataByTags" parameterType="cn.iocoder.yudao.module.iot.domain.TagsSelectDao"
resultType="Map">
select last(*)
from #{dataBaseName}.#{stableName}
group by ${tagsName}
</select>
<select id="getHistoryData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
SELECT ${fieldName} as data, time
FROM ${dataBaseName}.${tableName}
WHERE time BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
ORDER BY time DESC
LIMIT #{params.rows} offset #{params.page}
</select>
<select id="getRealtimeData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
SELECT #{fieldName}, time
FROM #{dataBaseName}.#{tableName}
</select>
<select id="getAggregateData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
SELECT #{aggregate}(${fieldName})
FROM #{dataBaseName}.#{tableName}
WHERE ts BETWEEN #{startTime} AND #{endTime} interval (${interval})
LIMIT #{num}
</select>
<select id="describeSuperTable" resultType="java.util.Map">
DESCRIBE ${dataBaseName}.${superTableName}
</select>
<select id="getHistoryCount" resultType="java.lang.Long">
SELECT count(time)
FROM ${dataBaseName}.${tableName}
WHERE time BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
</select>
</mapper>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineQueryMapper">
<!-- 根据时间戳查询数据 -->
<select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO"
resultType="Map">
SELECT * FROM ${dataBaseName}.${tableName}
WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
</select>
<!-- 获取时间范围内的数据条数 -->
<select id="getCountByTimestamp" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO"
resultType="java.util.Map">
SELECT COUNT(0) AS count
FROM ${dataBaseName}.${tableName}
WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
</select>
<!-- 获取最新数据 -->
<select id="getLastData" resultType="java.util.Map">
SELECT LAST(time), *
FROM ${tableName}
WHERE device_id = #{deviceId}
</select>
<!-- 根据标签获取最新数据 -->
<select id="getLastDataByTags" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO"
resultType="Map">
SELECT LAST(*)
FROM ${dataBaseName}.${stableName}
GROUP BY ${tagsName}
</select>
<!-- 获取历史数据 -->
<select id="getHistoryData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO">
SELECT ${fieldName} AS data, time
FROM ${dataBaseName}.${tableName}
WHERE time BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
ORDER BY time DESC
LIMIT #{params.rows} OFFSET #{params.page}
</select>
<!-- 获取实时数据 -->
<select id="getRealtimeData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO">
SELECT ${fieldName}, time
FROM ${dataBaseName}.${tableName}
</select>
<!-- 获取聚合数据 -->
<select id="getAggregateData" resultType="java.util.Map"
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO">
SELECT ${aggregate}(${fieldName})
FROM ${dataBaseName}.${tableName}
WHERE ts BETWEEN #{startTime} AND #{endTime} INTERVAL (${interval})
LIMIT #{num}
</select>
<!-- 获取历史数据条数 -->
<select id="getHistoryCount" resultType="java.lang.Long">
SELECT COUNT(time)
FROM ${dataBaseName}.${tableName}
WHERE time BETWEEN #{startTime} AND #{endTime}
AND ${fieldName} IS NOT NULL
</select>
</mapper>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineSuperTableMapper">
<!-- 创建超级表 -->
<update id="createSuperTable">
CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName}
<foreach item="item" collection="columns" separator=","
open="(" close=")">
${item.fieldName} ${item.dataType}
<if test="item.dataLength > 0">
(${item.dataLength})
</if>
</foreach>
TAGS
<foreach item="item" collection="tags" separator=","
open="(" close=")">
${item.fieldName} ${item.dataType}
<if test="item.dataLength > 0">
(${item.dataLength})
</if>
</foreach>
</update>
<!-- 查看超级表 -->
<select id="showSuperTables" resultType="java.util.Map">
SHOW ${dataBaseName}.STABLES LIKE '${superTableName}'
</select>
<!-- 描述超级表结构 -->
<select id="describeSuperTable" resultType="java.util.Map">
DESCRIBE ${dataBaseName}.${superTableName}
</select>
<!-- 为超级表添加列 -->
<update id="addColumnForSuperTable">
ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN ${column.fieldName} ${column.dataType}
<if test="column.dataLength > 0">
(${column.dataLength})
</if>
</update>
<!-- 为超级表删除列 -->
<update id="dropColumnForSuperTable">
ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN ${column.fieldName}
</update>
<!-- 修改列宽 -->
<update id="modifyColumnWidthForSuperTable">
ALTER STABLE ${dataBaseName}.${superTableName} MODIFY COLUMN ${column.fieldName} ${column.dataType}
<if test="column.dataLength > 0">
(${column.dataLength})
</if>
</update>
<!-- 为超级表添加标签 -->
<update id="addTagForSuperTable">
ALTER STABLE ${dataBaseName}.${superTableName} ADD TAG ${tag.fieldName} ${tag.dataType}
<if test="tag.dataLength > 0">
(${tag.dataLength})
</if>
</update>
<!-- 为超级表删除标签 -->
<update id="dropTagForSuperTable">
ALTER STABLE ${dataBaseName}.${superTableName} DROP TAG ${tag.fieldName}
</update>
</mapper>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper">
<!-- 创建子表 -->
<update id="createTable">
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
USING ${dataBaseName}.${superTableName}
TAGS
<foreach item="item" collection="tags" separator=","
open="(" close=")">
#{item.fieldValue}
</foreach>
</update>
<!-- 创建子表,带有 TAGS -->
<update id="createTableWithTags">
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
USING ${dataBaseName}.${superTableName}
<foreach item="item" collection="tags" separator="," open="(" close=")">
#{item.fieldName}
</foreach>
TAGS
<foreach item="item" collection="tags" separator=","
open="(" close=")">
#{item.fieldValue}
</foreach>
</update>
</mapper>