Commit 05c54827152157983661a391c73d04b2615638c7

Authored by 安浩浩
1 parent 6a35ca72

【新增功能】 集成 tdengine

Showing 37 changed files with 2016 additions and 3 deletions
yudao-dependencies/pom.xml
... ... @@ -33,6 +33,7 @@
33 33 <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
34 34 <kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
35 35 <opengauss.jdbc.version>5.0.2</opengauss.jdbc.version>
  36 + <taos.version>3.3.3</taos.version>
36 37 <!-- 消息队列 -->
37 38 <rocketmq-spring.version>2.3.0</rocketmq-spring.version>
38 39 <!-- 服务保障相关 -->
... ... @@ -253,6 +254,12 @@
253 254 <version>${kingbase.jdbc.version}</version>
254 255 </dependency>
255 256  
  257 + <dependency>
  258 + <groupId>com.taosdata.jdbc</groupId>
  259 + <artifactId>taos-jdbcdriver</artifactId>
  260 + <version>${taos.version}</version>
  261 + </dependency>
  262 +
256 263 <!-- Job 定时任务相关 -->
257 264 <dependency>
258 265 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
... ... @@ -63,6 +63,10 @@
63 63 <artifactId>opengauss-jdbc</artifactId>
64 64 <optional>true</optional>
65 65 </dependency>
  66 + <dependency>
  67 + <groupId>com.taosdata.jdbc</groupId>
  68 + <artifactId>taos-jdbcdriver</artifactId>
  69 + </dependency>
66 70  
67 71 <dependency>
68 72 <groupId>com.alibaba</groupId>
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * @ClassDescription: tdEngine的基础实体类
  7 + * @ClassName: BaseEntity
  8 + * @Author: fxlinks
  9 + * @Date: 2021-12-30 14:39:25
  10 + * @Version 1.0
  11 + */
  12 +@Data
  13 +public class BaseEntity {
  14 + private static final long serialVersionUID = 1L;
  15 +
  16 + /**
  17 + * 数据库名称
  18 + */
  19 + private String dataBaseName;
  20 +
  21 + /**
  22 + * 超级表名称
  23 + */
  24 + private String superTableName;
  25 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 设备数据导出 excel DTO
  7 + */
  8 +@Data
  9 +public class DeviceDataExportExcelDto {
  10 +
  11 + /**
  12 + * 设备标识
  13 + */
  14 + private String deviceKey;
  15 +
  16 + /**
  17 + * 导出形式 1 单个参数导出 2 全部参数导出
  18 + */
  19 + private String exportType;
  20 +
  21 + /**
  22 + * 导出开始时间
  23 + */
  24 + private String exportBeginTime;
  25 +
  26 + /**
  27 + * 导出结束时间
  28 + */
  29 + private String exportEndTime;
  30 +
  31 + /**
  32 + * 导出参数,空则导出全部
  33 + */
  34 + private String exportParameter;
  35 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * @ClassDescription: 查询可视化所需入参对象
  7 + * @ClassName: SelectDto
  8 + * @Author: andyz
  9 + * @Date: 2022-07-29 14:12:26
  10 + * @Version 1.0
  11 + */
  12 +@Data
  13 +public class DeviceDataVo {
  14 +
  15 +
  16 + private String deviceId;
  17 +
  18 + private Long lastTime;
  19 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * @ClassDescription: 建表的字段实体类
  7 + * @ClassName: Fields
  8 + * @Author: fxlinks
  9 + * @Date: 2021-12-28 09:09:04
  10 + * @Version 1.0
  11 + */
  12 +@Data
  13 +public class Fields {
  14 + private static final long serialVersionUID = 1L;
  15 +
  16 + /**
  17 + * 字段名称
  18 + */
  19 + private String fieldName;
  20 +
  21 + /**
  22 + * 字段值
  23 + */
  24 + private Object fieldValue;
  25 +
  26 + /**
  27 + * 字段数据类型
  28 + */
  29 +// private DataTypeEnum dataType;
  30 +
  31 + /**
  32 + * 字段字节大小
  33 + */
  34 + private Integer size;
  35 +
  36 + public Fields() {
  37 + }
  38 +
  39 + public Fields(String fieldName, String dataType, Integer size) {
  40 +// this.fieldName = fieldName;
  41 +// //根据规则匹配字段数据类型
  42 +// switch (dataType.toLowerCase()) {
  43 +// case ("json"):
  44 +// this.dataType = DataTypeEnum.JSON;
  45 +// this.size = size;
  46 +// break;
  47 +// case ("string"):
  48 +// this.dataType = DataTypeEnum.NCHAR;
  49 +// this.size = size;
  50 +// break;
  51 +// case ("binary"):
  52 +// this.dataType = DataTypeEnum.BINARY;
  53 +// this.size = size;
  54 +// break;
  55 +// case ("int"):
  56 +// this.dataType = DataTypeEnum.INT;
  57 +// break;
  58 +// case ("bool"):
  59 +// this.dataType = DataTypeEnum.BOOL;
  60 +// break;
  61 +// case ("decimal"):
  62 +// this.dataType = DataTypeEnum.DOUBLE;
  63 +// break;
  64 +// case ("timestamp"):
  65 +// if ("eventTime".equals(fieldName)) {
  66 +// this.fieldName = "eventTime";
  67 +// }
  68 +// this.dataType = DataTypeEnum.TIMESTAMP;
  69 +// break;
  70 +// }
  71 + }
  72 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.sql.SQLException;
  6 +import java.util.ArrayList;
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * @ClassDescription: 建表的字段实体类的vo类
  11 + * @ClassName: FieldsVo
  12 + * @Author: fxlinks
  13 + * @Date: 2021-12-28 11:30:06
  14 + * @Version 1.0
  15 + */
  16 +@Data
  17 +public class FieldsVo {
  18 + private static final long serialVersionUID = 1L;
  19 +
  20 + /**
  21 + * 字段名称
  22 + */
  23 + private String fieldName;
  24 +
  25 + /**
  26 + * 字段数据类型
  27 + */
  28 + private String dataType;
  29 +
  30 + /**
  31 + * 字段字节大小
  32 + */
  33 + private Integer size;
  34 +
  35 + /**
  36 + * @param fields 字段实体类
  37 + * @return FieldsVo 字段实体vo类
  38 + * @MethodDescription 字段实体类转为vo类
  39 + * @author fx
  40 + * @Date 2021/12/28 13:48
  41 + */
  42 + public static FieldsVo fieldsTranscoding(Fields fields) throws SQLException {
  43 +// if (StringUtils.isBlank(fields.getFieldName()) || fields.getDataType() == null) {
  44 +// throw new SQLException("invalid operation: fieldName or dataType can not be null");
  45 +// }
  46 +// FieldsVo fieldsVo = new FieldsVo();
  47 +// fieldsVo.setFieldName(fields.getFieldName());
  48 +// fieldsVo.setDataType(fields.getDataType().getDataType());
  49 +// fieldsVo.setSize(fields.getSize());
  50 +// return fieldsVo;
  51 + return null;
  52 + }
  53 +
  54 + /**
  55 + * @param fieldsList 字段实体类集合
  56 + * @return List<FieldsVo> 字段实体vo类集合
  57 + * @MethodDescription 字段实体类集合转为vo类集合
  58 + * @author fx
  59 + * @Date 2021/12/28 14:00
  60 + */
  61 + public static List<FieldsVo> fieldsTranscoding(List<Fields> fieldsList) throws SQLException {
  62 + List<FieldsVo> fieldsVoList = new ArrayList<>();
  63 + for (Fields fields : fieldsList) {
  64 + fieldsVoList.add(fieldsTranscoding(fields));
  65 + }
  66 + return fieldsVoList;
  67 + }
  68 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonFormat;
  4 +
  5 +import java.sql.Timestamp;
  6 +
  7 +public class IotSequential extends BaseEntity {
  8 + private static final long serialVersionUID = 1L;
  9 +
  10 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8")
  11 + private Timestamp statetime;
  12 +
  13 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8")
  14 + private Timestamp endtime;
  15 +
  16 + private String deviceid;
  17 +
  18 + private String eventtime;
  19 +
  20 + private String serviceid;
  21 +
  22 + private String devices;
  23 +
  24 + public String getDeviceid() {
  25 + return deviceid;
  26 + }
  27 +
  28 + public void setDeviceid(String deviceid) {
  29 + this.deviceid = deviceid;
  30 + }
  31 +
  32 + public String getEventtime() {
  33 + return eventtime;
  34 + }
  35 +
  36 + public void setEventtime(String eventtime) {
  37 + this.eventtime = eventtime;
  38 + }
  39 +
  40 + public String getServiceid() {
  41 + return serviceid;
  42 + }
  43 +
  44 + public void setServiceid(String serviceid) {
  45 + this.serviceid = serviceid;
  46 + }
  47 +
  48 + public String getDevices() {
  49 + return devices;
  50 + }
  51 +
  52 + public void setDevices(String devices) {
  53 + this.devices = devices;
  54 + }
  55 +
  56 + public Timestamp getStatetime() {
  57 + return statetime;
  58 + }
  59 +
  60 + public void setStatetime(Timestamp statetime) {
  61 + this.statetime = statetime;
  62 + }
  63 +
  64 + public Timestamp getEndtime() {
  65 + return endtime;
  66 + }
  67 +
  68 + public void setEndtime(Timestamp endtime) {
  69 + this.endtime = endtime;
  70 + }
  71 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * 统计的时间数据
  9 + */
  10 +@Data
  11 +@NoArgsConstructor
  12 +@AllArgsConstructor
  13 +public class MessageCountVo {
  14 +
  15 + /**
  16 + * 时间
  17 + */
  18 + private String time;
  19 +
  20 + /**
  21 + * 数据值
  22 + */
  23 + private Object data;
  24 +
  25 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonFormat;
  4 +import lombok.Data;
  5 +
  6 +import java.sql.Timestamp;
  7 +import java.util.HashMap;
  8 +import java.util.Optional;
  9 +
  10 +/**
  11 + * @Description: 产品超级表模型
  12 + * @Author: fx
  13 + * @Website: http://www.sxfxck.com
  14 + * @CreateDate: 2022/1/1$ 19:37$
  15 + * @UpdateUser: fx
  16 + * @UpdateDate: 2022/1/1$ 19:37$
  17 + * @UpdateRemark: 修改内容
  18 + * @Version: V1.0
  19 + */
  20 +@Data
  21 +public class ProductSuperTableModel {
  22 + private static final long serialVersionUID = 1L;
  23 +
  24 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8")
  25 + private Timestamp ts;
  26 +
  27 + private String superTableName;
  28 +
  29 + /**
  30 + * columnsName,columnsProperty
  31 + */
  32 + private HashMap<Optional, Optional> columns;
  33 +
  34 + /**
  35 + * tagsName,tagsProperty
  36 + */
  37 + private HashMap<Optional, Optional> tags;
  38 +
  39 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Set;
  6 +
  7 +/**
  8 + * @ClassDescription: 查询所需入参对象
  9 + * @ClassName: SelectDto
  10 + * @Author: fxlinks
  11 + * @Date: 2022-01-07 14:12:26
  12 + * @Version 1.0
  13 + */
  14 +@Data
  15 +public class SelectDto {
  16 +
  17 + // @NotBlank(message = "invalid operation: dataBaseName can not be empty")
  18 + private String dataBaseName;
  19 +
  20 +// @NotBlank(message = "invalid operation: tableName can not be empty")
  21 + private String tableName;
  22 +
  23 + // @NotBlank(message = "invalid operation: fieldName can not be empty")
  24 + private String fieldName;
  25 +
  26 + // @NotNull(message = "invalid operation: startTime can not be null")
  27 + private Long startTime;
  28 +
  29 + // @NotNull(message = "invalid operation: endTime can not be null")
  30 + private Long endTime;
  31 +
  32 + private String type;
  33 +
  34 + private Set<Integer> orgIds;
  35 +
  36 + private String deviceId;
  37 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * @ClassDescription: 创建超级表需要的入参的实体类
  9 + * @ClassName: SuperTableDto
  10 + * @Author: fxlinks
  11 + * @Date: 2021-12-28 15:03:45
  12 + * @Version 1.0
  13 + */
  14 +@Data
  15 +public class SuperTableDto extends BaseEntity {
  16 +
  17 + /**
  18 + * 超级表的表结构(业务相关)
  19 + * 第一个字段的数据类型必须为timestamp
  20 + * 字符相关数据类型必须指定大小
  21 + * 字段名称和字段数据类型不能为空
  22 + */
  23 +// @NotEmpty(message = "invalid operation: schemaFields can not be empty")
  24 + private List<Fields> schemaFields;
  25 +
  26 + /**
  27 + * 超级表的标签字段,可以作为子表在超级表里的标识
  28 + * 字符相关数据类型必须指定大小
  29 + * 字段名称和字段数据类型不能为空
  30 + */
  31 +// @NotEmpty(message = "invalid operation: tagsFields can not be empty")
  32 + private List<Fields> tagsFields;
  33 +
  34 + /**
  35 + * 字段信息对象,超级表添加列时使用该属性
  36 + */
  37 + private Fields fields;
  38 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * @ClassDescription: 创建超级表的子表需要的入参的实体类
  9 + * @ClassName: TableDto
  10 + * @Author: fxlinks
  11 + * @Date: 2021-12-30 14:42:47
  12 + * @Version 1.0
  13 + */
  14 +@Data
  15 +public class TableDto extends BaseEntity {
  16 +
  17 + /**
  18 + * 超级表普通列字段的值
  19 + * 值需要与创建超级表时普通列字段的数据类型对应上
  20 + */
  21 + private List<Fields> schemaFieldValues;
  22 +
  23 + /**
  24 + * 超级表标签字段的值
  25 + * 值需要与创建超级表时标签字段的数据类型对应上
  26 + */
  27 + private List<Fields> tagsFieldValues;
  28 +
  29 + /**
  30 + * 表名称
  31 + */
  32 + private String tableName;
  33 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import lombok.Data;
  4 +
  5 +
  6 +/**
  7 + * @program: fxlinks
  8 + * @description: 标签查询模型
  9 + * @packagename: com.fx.tdengine.api.domain.rule
  10 + * @author: fx
  11 + * @e-mainl: 13733918655@163.com
  12 + * @date: 2022-07-27 18:40
  13 + **/
  14 +@Data
  15 +public class TagsSelectDao {
  16 +
  17 +// @NotBlank(message = "invalid operation: dataBaseName can not be empty")
  18 + private String dataBaseName;
  19 +
  20 +// @NotBlank(message = "invalid operation: stableName can not be empty")
  21 + private String stableName;
  22 +
  23 +// @NotBlank(message = "invalid operation: tagsName can not be empty")
  24 + private String tagsName;
  25 +
  26 + // @NotNull(message = "invalid operation: startTime can not be null")
  27 + private Long startTime;
  28 +
  29 + // @NotNull(message = "invalid operation: endTime can not be null")
  30 + private Long endTime;
  31 +
  32 +
  33 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain;
  2 +
  3 +import com.fasterxml.jackson.annotation.JsonFormat;
  4 +
  5 +import java.sql.Timestamp;
  6 +
  7 +public class Weather {
  8 +
  9 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8")
  10 + private Timestamp ts;
  11 + private Float temperature;
  12 + private Float humidity;
  13 + private String location;
  14 + private String note;
  15 + private int groupId;
  16 +
  17 + public Weather() {
  18 + }
  19 +
  20 + public Weather(Timestamp ts, float temperature, float humidity) {
  21 + this.ts = ts;
  22 + this.temperature = temperature;
  23 + this.humidity = humidity;
  24 + }
  25 +
  26 + public Timestamp getTs() {
  27 + return ts;
  28 + }
  29 +
  30 + public void setTs(Timestamp ts) {
  31 + this.ts = ts;
  32 + }
  33 +
  34 + public Float getTemperature() {
  35 + return temperature;
  36 + }
  37 +
  38 + public void setTemperature(Float temperature) {
  39 + this.temperature = temperature;
  40 + }
  41 +
  42 + public Float getHumidity() {
  43 + return humidity;
  44 + }
  45 +
  46 + public void setHumidity(Float humidity) {
  47 + this.humidity = humidity;
  48 + }
  49 +
  50 + public String getLocation() {
  51 + return location;
  52 + }
  53 +
  54 + public void setLocation(String location) {
  55 + this.location = location;
  56 + }
  57 +
  58 + public int getGroupId() {
  59 + return groupId;
  60 + }
  61 +
  62 + public void setGroupId(int groupId) {
  63 + this.groupId = groupId;
  64 + }
  65 +
  66 + public String getNote() {
  67 + return note;
  68 + }
  69 +
  70 + public void setNote(String note) {
  71 + this.note = note;
  72 + }
  73 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.domain.visual;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.Map;
  6 +
  7 +/**
  8 + * @ClassDescription: 查询可视化所需入参对象
  9 + * @ClassName: SelectDto
  10 + * @Author: andyz
  11 + * @Date: 2022-07-29 14:12:26
  12 + * @Version 1.0
  13 + */
  14 +@Data
  15 +public class SelectVisualDto {
  16 +
  17 +// @NotBlank(message = "invalid operation: tableName can not be empty")
  18 + private String dataBaseName;
  19 +
  20 +// @NotBlank(message = "invalid operation: tableName can not be empty")
  21 + private String tableName;
  22 +
  23 +// @NotBlank(message = "invalid operation: fieldName can not be empty") //属性
  24 + private String fieldName;
  25 + /**
  26 + * 查询类型,0历史数据,1实时数据,2聚合数据
  27 + */
  28 + private int type;
  29 + /**
  30 + * 查询的数据量
  31 + */
  32 + private int num;
  33 + /**
  34 + * 聚合函数
  35 + */
  36 + private String aggregate;
  37 + /**
  38 + * 统计间隔数字+s/m/h/d
  39 + * 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天
  40 + */
  41 + private String interval;
  42 + // @NotNull(message = "invalid operation: startTime can not be null")
  43 + private Long startTime;
  44 +
  45 + // @NotNull(message = "invalid operation: endTime can not be null")
  46 + private Long endTime;
  47 +
  48 + /**
  49 + * 请求参数
  50 + */
  51 + private Map<String, Object> params;
  52 +
  53 + private String sql;
  54 +
  55 + private String deviceId;
  56 +
  57 + private Long lastTime;
  58 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java
... ... @@ -30,6 +30,15 @@ public enum IotProductFunctionTypeEnum implements IntArrayValuable {
30 30 */
31 31 private final String description;
32 32  
  33 + public static IotProductFunctionTypeEnum valueOf(Integer type) {
  34 + for (IotProductFunctionTypeEnum value : values()) {
  35 + if (value.getType().equals(type)) {
  36 + return value;
  37 + }
  38 + }
  39 + return null;
  40 + }
  41 +
33 42 @Override
34 43 public int[] array() {
35 44 return ARRAYS;
... ...
yudao-module-iot/yudao-module-iot-biz/pom.xml
... ... @@ -42,6 +42,11 @@
42 42 <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
43 43 </dependency>
44 44  
  45 + <dependency>
  46 + <groupId>cn.iocoder.boot</groupId>
  47 + <artifactId>yudao-spring-boot-starter-redis</artifactId>
  48 + </dependency>
  49 +
45 50 <!-- Test 测试相关 -->
46 51 <dependency>
47 52 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
... ... @@ -83,11 +83,10 @@ public class IotProductController {
83 83 return success(BeanUtils.toBean(pageResult, IotProductRespVO.class));
84 84 }
85 85  
86   - // TODO @haohao:改成 simple-list 哈
87   - @GetMapping("/list-all-simple")
  86 + @GetMapping("/simple-list")
88 87 @Operation(summary = "获得所有产品列表")
89 88 @PreAuthorize("@ss.hasPermission('iot:product:query')")
90   - public CommonResult<List<IotProductSimpleRespVO>> listAllSimpleProducts() {
  89 + public CommonResult<List<IotProductSimpleRespVO>> getSimpleProductList() {
91 90 List<IotProductDO> list = productService.getProductList();
92 91 return success(BeanUtils.toBean(list, IotProductSimpleRespVO.class));
93 92 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
  2 +
  3 +import lombok.*;
  4 +
  5 +import java.util.List;
  6 +import java.util.Map;
  7 +
  8 +@Data
  9 +@Builder
  10 +@NoArgsConstructor
  11 +@AllArgsConstructor
  12 +@ToString
  13 +public class ThingModelRespVO {
  14 +
  15 + /**
  16 + * 产品编号
  17 + */
  18 + private Long id;
  19 +
  20 + /**
  21 + * 产品标识
  22 + */
  23 + private String productKey;
  24 +
  25 + /**
  26 + * 物模型
  27 + */
  28 + private Model model;
  29 +
  30 + /**
  31 + * 物模型
  32 + */
  33 + @Data
  34 + public static class Model {
  35 +
  36 + /**
  37 + * 属性列表
  38 + */
  39 + private List<ThingModelProperty> properties;
  40 +
  41 + /**
  42 + * 服务列表
  43 + */
  44 + private List<ThingModelService> services;
  45 +
  46 + /**
  47 + * 事件列表
  48 + */
  49 + private List<ThingModelEvent> events;
  50 + }
  51 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +
  4 +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelDataType;
  7 +
  8 +import java.util.HashMap;
  9 +import java.util.List;
  10 +import java.util.stream.Collectors;
  11 +
  12 +public class FieldParser {
  13 +
  14 + /**
  15 + * 物模型到td数据类型映射
  16 + */
  17 + private static final HashMap<String, String> TYPE_MAPPING = new HashMap<>() {{
  18 + put("INT", "INT");
  19 + put("FLOAT", "FLOAT");
  20 + put("DOUBLE", "DOUBLE");
  21 + put("BOOL", "BOOL");
  22 + put("ENUM", "NCHAR");
  23 + put("TEXT", "NCHAR");
  24 + put("DATE", "NCHAR");
  25 + }};
  26 +
  27 +
  28 + /**
  29 + * 将物模型字段转换为td字段
  30 + *
  31 + * @param property 物模型属性
  32 + * @return TdField对象
  33 + */
  34 + public static TdField parse(ThingModelProperty property) {
  35 + String fieldName = property.getIdentifier().toLowerCase();
  36 + ThingModelDataType type = property.getDataType();
  37 +
  38 + // 将物模型字段类型映射为td字段类型
  39 + String fType = TYPE_MAPPING.get(type.getType().toUpperCase());
  40 +
  41 + int len = -1;
  42 + // 如果字段类型为NCHAR,默认长度为64
  43 + if ("NCHAR".equals(fType)) {
  44 + len = 64;
  45 + }
  46 +
  47 + return new TdField(fieldName, fType, len);
  48 + }
  49 +
  50 + /**
  51 + * 获取物模型中的字段列表
  52 + */
  53 + public static List<TdField> parse(ThingModelRespVO thingModel) {
  54 + return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList());
  55 + }
  56 +
  57 + /**
  58 + * 将从库中查出来的字段信息转换为td字段对象
  59 + */
  60 + public static List<TdField> parse(List rows) {
  61 + return (List<TdField>) rows.stream().map((r) -> {
  62 + List row = (List) r;
  63 + String type = row.get(1).toString().toUpperCase();
  64 + return new TdField(
  65 + row.get(0).toString(),
  66 + type,
  67 + type.equals("NCHAR") ? Integer.parseInt(row.get(2).toString()) : -1);
  68 + }).collect(Collectors.toList());
  69 + }
  70 +
  71 + /**
  72 + * 获取字段字义
  73 + */
  74 + public static String getFieldDefine(TdField field) {
  75 + return "`" + field.getName() + "`" + " " + (field.getLength() > 0 ?
  76 + String.format("%s(%d)", field.getType(), field.getLength())
  77 + : field.getType());
  78 + }
  79 +
  80 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +@Data
  8 +public class TableData {
  9 +
  10 + /**
  11 + * 超级表普通列字段的名称
  12 + */
  13 + private List<String> schemaFieldList;
  14 +
  15 + /**
  16 + * 超级表标签字段的值
  17 + * 值需要与创建超级表时标签字段的数据类型对应上
  18 + */
  19 + private List<Object> tagsValueList;
  20 +
  21 + /**
  22 + * 超级表普通列字段的值
  23 + * 值需要与创建超级表时普通列字段的数据类型对应上
  24 + */
  25 + private List<Object> schemaValueList;
  26 +
  27 + /**
  28 + * 表名称
  29 + */
  30 + private String tableName;
  31 +
  32 + /**
  33 + * 超级表名称
  34 + */
  35 + private String superTableName;
  36 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +import java.util.List;
  4 +
  5 +public class TableManager {
  6 +
  7 + /**
  8 + * 创建超级表模板(含存在判断)
  9 + */
  10 + private static final String CREATE_STABLE_INE_TPL = "CREATE STABLE IF NOT EXISTS %s (%s) TAGS (%s);";
  11 +
  12 + /**
  13 + * 删除超级表
  14 + */
  15 + private static final String DROP_STABLE_TPL = "DROP STABLE IF EXISTS %s;";
  16 +
  17 + /**
  18 + * 获取表的结构信息
  19 + */
  20 + private static final String DESC_TB_TPL = "DESCRIBE %s;";
  21 +
  22 + /**
  23 + * 超级表增加列
  24 + */
  25 + private static final String ALTER_STABLE_ADD_COL_TPL = "ALTER STABLE %s ADD COLUMN %s;";
  26 +
  27 + /**
  28 + * 超级表修改列
  29 + */
  30 + private static final String ALTER_STABLE_MODIFY_COL_TPL = "ALTER STABLE %s MODIFY COLUMN %s;";
  31 +
  32 + /**
  33 + * 超级表删除列
  34 + */
  35 + private static final String ALTER_STABLE_DROP_COL_TPL = "ALTER STABLE %s DROP COLUMN %s;";
  36 +
  37 + /**
  38 + * 创建普通表模板(含存在判断)
  39 + */
  40 + private static final String CREATE_CTABLE_INE_TPL = "CREATE TABLE IF NOT EXISTS %s (%s)";
  41 +
  42 + /**
  43 + * 获取创建表sql
  44 + */
  45 + public static String getCreateSTableSql(String tbName, List<TdField> fields, TdField... tags) {
  46 + if (fields.isEmpty()) {
  47 + return null;
  48 + }
  49 +
  50 + // 生成字段片段
  51 + StringBuilder sbField = new StringBuilder("time TIMESTAMP,");
  52 +
  53 + for (TdField field : fields) {
  54 + sbField.append(FieldParser.getFieldDefine(field));
  55 + sbField.append(",");
  56 + }
  57 + sbField.deleteCharAt(sbField.length() - 1);
  58 +
  59 + String fieldFrag = sbField.toString();
  60 +
  61 + // 生成tag
  62 + StringBuilder sbTag = new StringBuilder();
  63 + for (TdField tag : tags) {
  64 + sbTag.append(FieldParser.getFieldDefine(tag))
  65 + .append(",");
  66 + }
  67 + sbTag.deleteCharAt(sbTag.length() - 1);
  68 +
  69 + return String.format(CREATE_STABLE_INE_TPL, tbName, fieldFrag, sbTag);
  70 +
  71 + }
  72 +
  73 + /**
  74 + * 获取创建普通表sql
  75 + */
  76 + public static String getCreateCTableSql(String tbName, List<TdField> fields) {
  77 + if (fields.size() == 0) {
  78 + return null;
  79 + }
  80 +
  81 + //生成字段片段
  82 + StringBuilder sbField = new StringBuilder("time timestamp,");
  83 +
  84 + for (TdField field : fields) {
  85 + sbField.append(FieldParser.getFieldDefine(field));
  86 + sbField.append(",");
  87 + }
  88 + sbField.deleteCharAt(sbField.length() - 1);
  89 +
  90 + String fieldFrag = sbField.toString();
  91 +
  92 + return String.format(CREATE_CTABLE_INE_TPL, tbName, fieldFrag);
  93 +
  94 + }
  95 +
  96 +
  97 + /**
  98 + * 取正确的表名
  99 + *
  100 + * @param name 表象
  101 + */
  102 + public static String rightTbName(String name) {
  103 + return name.toLowerCase().replace("-" , "_");
  104 + }
  105 +
  106 + /**
  107 + * 获取表详情的sql
  108 + */
  109 + public static String getDescTableSql(String tbName) {
  110 + return String.format(DESC_TB_TPL, tbName);
  111 + }
  112 +
  113 + /**
  114 + * 获取添加字段sql
  115 + */
  116 + public static String getAddSTableColumnSql(String tbName, List<TdField> fields) {
  117 + StringBuilder sbAdd = new StringBuilder();
  118 + for (TdField field : fields) {
  119 + sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL,
  120 + tbName,
  121 + FieldParser.getFieldDefine(field)
  122 + ));
  123 + }
  124 + return sbAdd.toString();
  125 + }
  126 +
  127 + /**
  128 + * 获取修改字段sql
  129 + */
  130 + public static String getModifySTableColumnSql(String tbName, List<TdField> fields) {
  131 + StringBuilder sbModify = new StringBuilder();
  132 + for (TdField field : fields) {
  133 + sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL,
  134 + tbName,
  135 + FieldParser.getFieldDefine(field)
  136 + ));
  137 + }
  138 + return sbModify.toString();
  139 + }
  140 +
  141 + /**
  142 + * 获取删除字段sql
  143 + */
  144 + public static String getDropSTableColumnSql(String tbName, List<TdField> fields) {
  145 + StringBuilder sbDrop = new StringBuilder();
  146 + for (TdField field : fields) {
  147 + sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL,
  148 + tbName,
  149 + field.getName()
  150 + ));
  151 + }
  152 + return sbDrop.toString();
  153 + }
  154 +
  155 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * TD 引擎的字段
  9 + */
  10 +@Data
  11 +@NoArgsConstructor
  12 +@AllArgsConstructor
  13 +public class TdField {
  14 +
  15 + /**
  16 + * 字段名称
  17 + */
  18 + private String name;
  19 +
  20 + /**
  21 + * 字段类型
  22 + */
  23 + private String type;
  24 +
  25 + /**
  26 + * 字段长度
  27 + */
  28 + private int length;
  29 +}
0 30 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +import java.util.List;
  8 +
  9 +@Data
  10 +@NoArgsConstructor
  11 +@AllArgsConstructor
  12 +public class TdResponse {
  13 +
  14 + public static final int CODE_SUCCESS = 0;
  15 + public static final int CODE_TB_NOT_EXIST = 866;
  16 +
  17 + private String status;
  18 +
  19 + private int code;
  20 +
  21 + private String desc;
  22 +
  23 + //[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]]
  24 + private List data;
  25 +
  26 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +import cn.hutool.http.HttpRequest;
  4 +import cn.hutool.http.HttpResponse;
  5 +import cn.hutool.json.JSONUtil;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.beans.factory.annotation.Value;
  8 +import org.springframework.stereotype.Service;
  9 +
  10 +@Slf4j
  11 +@Service
  12 +public class TdRestApi {
  13 +
  14 + @Value("${spring.datasource.dynamic.datasource.master.url}")
  15 + private String url;
  16 +
  17 + @Value("${spring.datasource.dynamic.datasource.master.username}")
  18 + private String username;
  19 +
  20 + @Value("${spring.datasource.dynamic.datasource.master.password}")
  21 + private String password;
  22 +
  23 + private String getRestApiUrl() {
  24 + //jdbc:TAOS-RS://127.0.0.1:6041/iotkit?xxxx
  25 + String restUrl = url.replace("jdbc:TAOS-RS://" , "")
  26 + .replaceAll("\\?.*" , "");
  27 + // /rest/sql/iotkit
  28 + int idx = restUrl.lastIndexOf("/");
  29 + //127.0.0.1:6041/rest/sql/iotkit
  30 + return String.format("%s/rest/sql/%s" , restUrl.substring(0, idx), restUrl.substring(idx + 1));
  31 + }
  32 +
  33 +
  34 + /**
  35 + * 新建td api请求对象
  36 + */
  37 + public HttpRequest newApiRequest(String sql) {
  38 + return HttpRequest
  39 + .post(getRestApiUrl())
  40 + .body(sql)
  41 + .basicAuth(username, password);
  42 + }
  43 +
  44 + /**
  45 + * 执行sql
  46 + */
  47 + public TdResponse execSql(String sql) {
  48 + log.info("exec td sql:{}" , sql);
  49 + HttpRequest request = newApiRequest(sql);
  50 + HttpResponse response = request.execute();
  51 + return JSONUtil.toBean(response.body(), TdResponse.class);
  52 + }
  53 +
  54 +
  55 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * 统计的时间数据
  9 + */
  10 +@Data
  11 +@NoArgsConstructor
  12 +@AllArgsConstructor
  13 +public class TimeData {
  14 +
  15 + /**
  16 + * 时间
  17 + */
  18 + private long time;
  19 +
  20 + /**
  21 + * 数据值
  22 + */
  23 + private Object data;
  24 +
  25 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.tdengine;
  2 +
  3 +import cn.iocoder.yudao.module.iot.domain.FieldsVo;
  4 +import cn.iocoder.yudao.module.iot.domain.SelectDto;
  5 +import cn.iocoder.yudao.module.iot.domain.TableDto;
  6 +import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
  7 +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
  8 +import com.baomidou.dynamic.datasource.annotation.DS;
  9 +import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +import org.apache.ibatis.annotations.Param;
  12 +
  13 +import java.util.List;
  14 +import java.util.Map;
  15 +
  16 +@Mapper
  17 +@DS("tdengine")
  18 +public interface TdEngineMapper {
  19 +
  20 + void createDatabase(@Param("dataBaseName") String dataBaseName);
  21 +
  22 + void createSuperTable(@Param("schemaFields") List<FieldsVo> schemaFields,
  23 + @Param("tagsFields") List<FieldsVo> tagsFields,
  24 + @Param("dataBaseName") String dataBaseName,
  25 + @Param("superTableName") String superTableName);
  26 +
  27 + void createTable(TableDto tableDto);
  28 +
  29 + void insertData(TableDto tableDto);
  30 +
  31 + List<Map<String, Object>> selectByTimestamp(SelectDto selectDto);
  32 +
  33 + void addColumnForSuperTable(@Param("superTableName") String superTableName,
  34 + @Param("fieldsVo") FieldsVo fieldsVo);
  35 +
  36 + void dropColumnForSuperTable(@Param("superTableName") String superTableName,
  37 + @Param("fieldsVo") FieldsVo fieldsVo);
  38 +
  39 + void addTagForSuperTable(@Param("superTableName") String superTableName,
  40 + @Param("fieldsVo") FieldsVo fieldsVo);
  41 +
  42 + void dropTagForSuperTable(@Param("superTableName") String superTableName,
  43 + @Param("fieldsVo") FieldsVo fieldsVo);
  44 +
  45 + Map<String, Long> getCountByTimestamp(SelectDto selectDto);
  46 +
  47 + /**
  48 + * 检查表是否存在
  49 + *
  50 + * @param dataBaseName 数据库名称
  51 + * @param tableName 表名称
  52 + */
  53 + Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName);
  54 +
  55 + Map<String, Object> getLastData(SelectDto selectDto);
  56 +
  57 + List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
  58 +
  59 + List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto);
  60 +
  61 + List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto);
  62 +
  63 + List<Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
  64 +
  65 + /**
  66 + * 创建超级表
  67 + *
  68 + * @param sql sql
  69 + * @return 返回值
  70 + */
  71 + @InterceptorIgnore(tenantLine = "true")
  72 + Integer createSuperTableDevice(String sql);
  73 +
  74 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.aspect;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.aspectj.lang.ProceedingJoinPoint;
  5 +import org.aspectj.lang.annotation.Around;
  6 +import org.aspectj.lang.annotation.Aspect;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import java.sql.Timestamp;
  10 +import java.util.Map;
  11 +
  12 +/**
  13 + * TaosAspect 是一个处理 Taos 数据库返回值的切面。
  14 + */
  15 +@Aspect
  16 +@Component
  17 +@Slf4j
  18 +public class TaosAspect {
  19 +
  20 + @Around("execution(java.util.Map<String,Object> cn.iocoder.yudao.module.iot.dal.tdengine.*.*(..))")
  21 + public Object handleType(ProceedingJoinPoint joinPoint) {
  22 + Map<String, Object> result = null;
  23 + try {
  24 + result = (Map<String, Object>) joinPoint.proceed();
  25 + result.replaceAll((key, value) -> {
  26 + if (value instanceof byte[]) {
  27 + return new String((byte[]) value);
  28 + } else if (value instanceof Timestamp) {
  29 + return ((Timestamp) value).getTime();
  30 + }
  31 + return value;
  32 + });
  33 + } catch (Throwable e) {
  34 + log.error("TaosAspect handleType error", e);
  35 + }
  36 + return result;
  37 + }
  38 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
... ... @@ -8,8 +8,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSaveReq
8 8 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
9 9 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
10 10 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
  11 +import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
  12 +import com.baomidou.dynamic.datasource.annotation.DSTransactional;
11 13 import jakarta.annotation.Resource;
  14 +
  15 +import org.springframework.context.annotation.Lazy;
12 16 import org.springframework.stereotype.Service;
  17 +import org.springframework.transaction.annotation.Transactional;
13 18 import org.springframework.validation.annotation.Validated;
14 19  
15 20 import java.util.List;
... ... @@ -31,6 +36,10 @@ public class IotProductServiceImpl implements IotProductService {
31 36 @Resource
32 37 private IotProductMapper productMapper;
33 38  
  39 + @Resource
  40 + @Lazy
  41 + private IotThinkModelFunctionService thinkModelFunctionService;
  42 +
34 43 @Override
35 44 public Long createProduct(IotProductSaveReqVO createReqVO) {
36 45 // 1. 生成 ProductKey
... ... @@ -106,11 +115,17 @@ public class IotProductServiceImpl implements IotProductService {
106 115 }
107 116  
108 117 @Override
  118 + @DSTransactional(rollbackFor = Exception.class)
109 119 public void updateProductStatus(Long id, Integer status) {
110 120 // 1. 校验存在
111 121 validateProductExists(id);
112 122 // 2. 更新
113 123 IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build();
  124 + // 3. 产品是发布状态
  125 + if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getType())) {
  126 + // 3.1 创建超级表数据模型
  127 + thinkModelFunctionService.createSuperTableDataModel(id);
  128 + }
114 129 productMapper.updateById(updateObj);
115 130 }
116 131  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.tdengine;
  2 +
  3 +
  4 +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
  7 +
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * 数据结构接口
  12 + */
  13 +public interface IotDbStructureDataService {
  14 +
  15 + /**
  16 + * 创建物模型定义
  17 + */
  18 + void createSuperTable(ThingModelRespVO thingModel, Integer deviceType);
  19 +
  20 + /**
  21 + * 更新物模型定义
  22 + */
  23 + void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType);
  24 +
  25 + /**
  26 + * 创建超级表数据模型
  27 + */
  28 + void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList);
  29 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.tdengine;
  2 +
  3 +import cn.hutool.json.JSONUtil;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*;
  8 +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
  9 +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;
  10 +import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
  11 +import jakarta.annotation.Resource;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.springframework.stereotype.Service;
  14 +
  15 +import java.util.ArrayList;
  16 +import java.util.List;
  17 +import java.util.Objects;
  18 +import java.util.stream.Collectors;
  19 +
  20 +@Service
  21 +@Slf4j
  22 +public class IotDbStructureDataServiceImpl implements IotDbStructureDataService {
  23 +
  24 + @Resource
  25 + private TdEngineMapper tdEngineMapper;
  26 +
  27 + @Resource
  28 + private TdRestApi tdRestApi;
  29 +
  30 + @Override
  31 + public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
  32 + // 获取物模型中的属性定义
  33 + List<TdField> fields = FieldParser.parse(thingModel);
  34 + String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
  35 +
  36 + // 生成创建超级表的 SQL
  37 + String sql = TableManager.getCreateSTableSql(tbName, fields, new TdField("device_id", "NCHAR", 64));
  38 + if (sql == null) {
  39 + log.warn("生成的 SQL 为空,无法创建超级表");
  40 + return;
  41 + }
  42 + log.info("执行 SQL: {}", sql);
  43 +
  44 + // 执行 SQL 创建超级表
  45 + tdEngineMapper.createSuperTableDevice(sql);
  46 + }
  47 +
  48 + @Override
  49 + public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
  50 + try {
  51 + // 获取旧字段信息
  52 + String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
  53 + String sql = TableManager.getDescTableSql(tbName);
  54 + TdResponse response = tdRestApi.execSql(sql);
  55 + if (response.getCode() != TdResponse.CODE_SUCCESS) {
  56 + throw new RuntimeException("获取表描述错误: " + JSONUtil.toJsonStr(response));
  57 + }
  58 +
  59 + List<TdField> oldFields = FieldParser.parse(response.getData());
  60 + List<TdField> newFields = FieldParser.parse(thingModel);
  61 +
  62 + // 找出新增的字段
  63 + List<TdField> addFields = newFields.stream()
  64 + .filter(f -> oldFields.stream().noneMatch(old -> old.getName().equals(f.getName())))
  65 + .collect(Collectors.toList());
  66 + if (!addFields.isEmpty()) {
  67 + sql = TableManager.getAddSTableColumnSql(tbName, addFields);
  68 + response = tdRestApi.execSql(sql);
  69 + if (response.getCode() != TdResponse.CODE_SUCCESS) {
  70 + throw new RuntimeException("添加表字段错误: " + JSONUtil.toJsonStr(response));
  71 + }
  72 + }
  73 +
  74 + // 找出修改的字段
  75 + List<TdField> modifyFields = newFields.stream()
  76 + .filter(f -> oldFields.stream().anyMatch(old ->
  77 + old.getName().equals(f.getName()) &&
  78 + (!old.getType().equals(f.getType()) || old.getLength() != f.getLength())))
  79 + .collect(Collectors.toList());
  80 + if (!modifyFields.isEmpty()) {
  81 + sql = TableManager.getModifySTableColumnSql(tbName, modifyFields);
  82 + response = tdRestApi.execSql(sql);
  83 + if (response.getCode() != TdResponse.CODE_SUCCESS) {
  84 + throw new RuntimeException("修改表字段错误: " + JSONUtil.toJsonStr(response));
  85 + }
  86 + }
  87 +
  88 + // 找出删除的字段
  89 + List<TdField> dropFields = oldFields.stream()
  90 + .filter(f -> !"time".equals(f.getName()) && !"device_id".equals(f.getName()) &&
  91 + newFields.stream().noneMatch(n -> n.getName().equals(f.getName())))
  92 + .collect(Collectors.toList());
  93 + if (!dropFields.isEmpty()) {
  94 + sql = TableManager.getDropSTableColumnSql(tbName, dropFields);
  95 + response = tdRestApi.execSql(sql);
  96 + if (response.getCode() != TdResponse.CODE_SUCCESS) {
  97 + throw new RuntimeException("删除表字段错误: " + JSONUtil.toJsonStr(response));
  98 + }
  99 + }
  100 + } catch (Throwable e) {
  101 + log.error("更新物模型超级表失败", e);
  102 + }
  103 + }
  104 +
  105 + @Override
  106 + public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
  107 + // 1. 生成 ThingModelRespVO
  108 + ThingModelRespVO thingModel = new ThingModelRespVO();
  109 + thingModel.setId(product.getId());
  110 + thingModel.setProductKey(product.getProductKey());
  111 +
  112 + // 1.1 设置属性、服务和事件
  113 + ThingModelRespVO.Model model = new ThingModelRespVO.Model();
  114 + List<ThingModelProperty> properties = new ArrayList<>();
  115 +
  116 + // 1.2 遍历功能列表并分类
  117 + for (IotThinkModelFunctionDO function : functionList) {
  118 + if (Objects.requireNonNull(IotProductFunctionTypeEnum.valueOf(function.getType())) == IotProductFunctionTypeEnum.PROPERTY) {
  119 + ThingModelProperty property = new ThingModelProperty();
  120 + property.setIdentifier(function.getIdentifier());
  121 + property.setName(function.getName());
  122 + property.setDescription(function.getDescription());
  123 + property.setDataType(function.getProperty().getDataType());
  124 + properties.add(property);
  125 + }
  126 + }
  127 +
  128 + // 1.3 判断属性列表是否为空
  129 + if (properties.isEmpty()) {
  130 + log.warn("物模型属性列表为空,不创建超级表");
  131 + return;
  132 + }
  133 +
  134 + model.setProperties(properties);
  135 + thingModel.setModel(model);
  136 +
  137 + // 2. 判断是否已经创建,如果已经创建则进行更新
  138 + String tbName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
  139 + Integer iot = tdEngineMapper.checkTableExists("ruoyi_vue_pro", tbName);
  140 + if (iot != null && iot > 0) {
  141 + // 3. 更新
  142 + updateSuperTable(thingModel, product.getDeviceType());
  143 + } else {
  144 + // 4. 创建
  145 + createSuperTable(thingModel, product.getDeviceType());
  146 + }
  147 + }
  148 +
  149 + /**
  150 + * 根据产品key获取产品属性超级表名
  151 + */
  152 + static String getProductPropertySTableName(Integer deviceType, String productKey) {
  153 + if (deviceType == 1) {
  154 + return String.format("gateway_sub_" + productKey).toLowerCase();
  155 + } else if (deviceType == 2) {
  156 + return String.format("gateway_" + productKey).toLowerCase();
  157 + } else {
  158 + return String.format("device_" + productKey).toLowerCase();
  159 + }
  160 + }
  161 +
  162 + /**
  163 + * 根据deviceId获取设备属性表名
  164 + */
  165 + static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) {
  166 + return String.format(deviceType + "_" + productKey + "_" + deviceKey).toLowerCase();
  167 + }
  168 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.tdengine;
  2 +
  3 +import cn.iocoder.yudao.module.iot.domain.FieldsVo;
  4 +import cn.iocoder.yudao.module.iot.domain.SelectDto;
  5 +import cn.iocoder.yudao.module.iot.domain.TableDto;
  6 +import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
  7 +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
  8 +
  9 +import java.util.List;
  10 +import java.util.Map;
  11 +
  12 +/**
  13 + * TdEngineService
  14 + */
  15 +public interface TdEngineService {
  16 +
  17 + /**
  18 + * 创建数据库
  19 + *
  20 + * @param dataBaseName 数据库名称
  21 + * @throws Exception 异常
  22 + */
  23 + void createDateBase(String dataBaseName) throws Exception;
  24 +
  25 + /**
  26 + * 创建超级表
  27 + *
  28 + * @param schemaFields schema字段
  29 + * @param tagsFields tags字段
  30 + * @param dataBaseName 数据库名称
  31 + * @param superTableName 超级表名称
  32 + * @throws Exception 异常
  33 + */
  34 + void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName,
  35 + String superTableName) throws Exception;
  36 +
  37 + /**
  38 + * 创建表
  39 + *
  40 + * @param tableDto 表信息
  41 + * @throws Exception 异常
  42 + */
  43 + void createTable(TableDto tableDto) throws Exception;
  44 +
  45 + /**
  46 + * 插入数据
  47 + *
  48 + * @param tableDto 表信息
  49 + * @throws Exception 异常
  50 + */
  51 + void insertData(TableDto tableDto) throws Exception;
  52 +
  53 + /**
  54 + * 根据时间戳查询数据
  55 + *
  56 + * @param selectDto 查询条件
  57 + * @return 数据
  58 + * @throws Exception 异常
  59 + */
  60 + List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) throws Exception;
  61 +
  62 + /**
  63 + * 为超级表添加列
  64 + *
  65 + * @param superTableName 超级表名称
  66 + * @param fieldsVo 字段信息
  67 + * @throws Exception 异常
  68 + */
  69 + void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception;
  70 +
  71 + /**
  72 + * 为超级表删除列
  73 + *
  74 + * @param superTableName 超级表名称
  75 + * @param fieldsVo 字段信息
  76 + * @throws Exception 异常
  77 + */
  78 + void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception;
  79 +
  80 + /**
  81 + * 为超级表添加tag
  82 + */
  83 + Long getCountByTimesTamp(SelectDto selectDto) throws Exception;
  84 +
  85 + /**
  86 + * 检查表是否存在
  87 + *
  88 + * @return 1存在 0不存在
  89 + * @throws Exception 异常
  90 + */
  91 + Integer checkTableExists(SelectDto selectDto) throws Exception;
  92 +
  93 + /**
  94 + * 初始化超级表
  95 + *
  96 + * @param msg 消息
  97 + * @throws Exception 异常
  98 + */
  99 + void initSTableFrame(String msg) throws Exception;
  100 +
  101 + /**
  102 + * 获取最新数据
  103 + *
  104 + * @param selectDto 查询条件
  105 + * @return 数据
  106 + * @throws Exception 异常
  107 + */
  108 + Map<String, Object> getLastData(SelectDto selectDto) throws Exception;
  109 +
  110 + /**
  111 + * 根据tag查询最新数据
  112 + *
  113 + * @param tagsSelectDao 查询条件
  114 + * @return 数据
  115 + */
  116 + Map<String, Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao);
  117 +
  118 + /**
  119 + * 获取历史数据
  120 + *
  121 + * @param selectVisualDto 查询条件
  122 + * @return 数据
  123 + */
  124 + List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto);
  125 +
  126 + /**
  127 + * 获取实时数据
  128 + *
  129 + * @param selectVisualDto 查询条件
  130 + * @return 数据
  131 + */
  132 + List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto);
  133 +
  134 + /**
  135 + * 获取聚合数据
  136 + *
  137 + * @param selectVisualDto 查询条件
  138 + * @return 数据
  139 + */
  140 + List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto);
  141 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.tdengine;
  2 +
  3 +import cn.iocoder.yudao.module.iot.domain.FieldsVo;
  4 +import cn.iocoder.yudao.module.iot.domain.SelectDto;
  5 +import cn.iocoder.yudao.module.iot.domain.TableDto;
  6 +import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
  7 +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +
  14 +@Service
  15 +@Slf4j
  16 +public class TdEngineServiceImpl implements TdEngineService {
  17 +
  18 +
  19 + @Override
  20 + public void createDateBase(String dataBaseName) {
  21 +
  22 + }
  23 +
  24 + @Override
  25 + public void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName, String superTableName) {
  26 +
  27 + }
  28 +
  29 + @Override
  30 + public void createTable(TableDto tableDto) {
  31 +
  32 + }
  33 +
  34 + @Override
  35 + public void insertData(TableDto tableDto) {
  36 +
  37 + }
  38 +
  39 + @Override
  40 + public List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) {
  41 + return List.of();
  42 + }
  43 +
  44 + @Override
  45 + public void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) {
  46 +
  47 + }
  48 +
  49 + @Override
  50 + public void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) {
  51 +
  52 + }
  53 +
  54 + @Override
  55 + public Long getCountByTimesTamp(SelectDto selectDto) {
  56 + return 0L;
  57 + }
  58 +
  59 + @Override
  60 + public Integer checkTableExists(SelectDto selectDto) {
  61 + return 0;
  62 + }
  63 +
  64 + @Override
  65 + public void initSTableFrame(String msg) {
  66 +
  67 + }
  68 +
  69 + @Override
  70 + public Map<String, Object> getLastData(SelectDto selectDto) {
  71 + return Map.of();
  72 + }
  73 +
  74 + @Override
  75 + public Map<String, Map<String, Object>> getLastDataByTags(TagsSelectDao tagsSelectDao) {
  76 + return Map.of();
  77 + }
  78 +
  79 + @Override
  80 + public List<Map<String, Object>> getHistoryData(SelectVisualDto selectVisualDto) {
  81 + return List.of();
  82 + }
  83 +
  84 + @Override
  85 + public List<Map<String, Object>> getRealtimeData(SelectVisualDto selectVisualDto) {
  86 + return List.of();
  87 + }
  88 +
  89 + @Override
  90 + public List<Map<String, Object>> getAggregateData(SelectVisualDto selectVisualDto) {
  91 + return List.of();
  92 + }
  93 +}
0 94 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java
... ... @@ -62,4 +62,10 @@ public interface IotThinkModelFunctionService {
62 62 */
63 63 PageResult<IotThinkModelFunctionDO> getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO);
64 64  
  65 + /**
  66 + * 创建超级表数据模型
  67 + *
  68 + * @param productId 产品编号
  69 + */
  70 + void createSuperTableDataModel(Long productId);
65 71 }
66 72 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java
... ... @@ -15,10 +15,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMode
15 15 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionPageReqVO;
16 16 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO;
17 17 import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert;
  18 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
18 19 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
19 20 import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper;
20 21 import cn.iocoder.yudao.module.iot.enums.product.IotAccessModeEnum;
21 22 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
  23 +import cn.iocoder.yudao.module.iot.service.product.IotProductService;
  24 +import cn.iocoder.yudao.module.iot.service.tdengine.IotDbStructureDataService;
22 25 import jakarta.annotation.Resource;
23 26 import lombok.extern.slf4j.Slf4j;
24 27 import org.springframework.stereotype.Service;
... ... @@ -45,6 +48,11 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
45 48 @Resource
46 49 private IotThinkModelFunctionMapper thinkModelFunctionMapper;
47 50  
  51 + @Resource
  52 + private IotProductService productService;
  53 + @Resource
  54 + private IotDbStructureDataService dbStructureDataService;
  55 +
48 56 @Override
49 57 @Transactional(rollbackFor = Exception.class)
50 58 public Long createThinkModelFunction(IotThinkModelFunctionSaveReqVO createReqVO) {
... ... @@ -162,6 +170,16 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
162 170 return thinkModelFunctionMapper.selectPage(pageReqVO);
163 171 }
164 172  
  173 + @Override
  174 + public void createSuperTableDataModel(Long productId) {
  175 + // 1. 查询产品
  176 + IotProductDO product = productService.getProduct(productId);
  177 + // 2. 查询产品的物模型功能列表
  178 + List<IotThinkModelFunctionDO> functionList = thinkModelFunctionMapper.selectListByProductId(productId);
  179 + // 3. 生成 TDengine 的数据模型
  180 + dbStructureDataService.createSuperTableDataModel(product, functionList);
  181 + }
  182 +
165 183 /**
166 184 * 创建默认的事件和服务
167 185 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +
  4 +<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper">
  5 +
  6 + <update id="createDatabase" parameterType="String">
  7 + create database if not exists #{dataBaseName}
  8 + </update>
  9 +
  10 + <update id="createSuperTable">
  11 + create table if not exists #{dataBaseName}.#{superTableName}
  12 + <foreach item="item" collection="schemaFields" separator=","
  13 + open="(" close=")" index="">
  14 + <if test="item.fieldName != null || item.fieldName != ''">
  15 + ${item.fieldName}
  16 + </if>
  17 + <if test="item.dataType != null || item.dataType != ''">
  18 + <choose>
  19 + <when test="item.dataType == 'timestamp'">
  20 + timestamp
  21 + </when>
  22 + <when test="item.dataType == 'tinyint'">
  23 + tinyint
  24 + </when>
  25 + <when test="item.dataType == 'smallint'">
  26 + smallint
  27 + </when>
  28 + <when test="item.dataType == 'int'">
  29 + int
  30 + </when>
  31 + <when test="item.dataType == 'bigint'">
  32 + bigint
  33 + </when>
  34 + <when test="item.dataType == 'float'">
  35 + float
  36 + </when>
  37 + <when test="item.dataType == 'double'">
  38 + double
  39 + </when>
  40 + <when test="item.dataType == 'binary'">
  41 + binary
  42 + </when>
  43 + <when test="item.dataType == 'nchar'">
  44 + nchar
  45 + </when>
  46 + <when test="item.dataType == 'bool'">
  47 + bool
  48 + </when>
  49 + <when test="item.dataType == 'json'">
  50 + json
  51 + </when>
  52 + </choose>
  53 + </if>
  54 + <if test="item.size != null">
  55 + (#{item.size})
  56 + </if>
  57 + </foreach>
  58 + tags
  59 + <!--tdEngine不支持动态tags里的数据类型,只能使用choose标签比对-->
  60 + <foreach item="item" collection="tagsFields" separator=","
  61 + open="(" close=")" index="">
  62 + <if test="item.fieldName != null || item.fieldName != ''">
  63 + ${item.fieldName}
  64 + </if>
  65 + <if test="item.dataType != null || item.dataType != ''">
  66 + <choose>
  67 + <when test="item.dataType == 'timestamp'">
  68 + timestamp
  69 + </when>
  70 + <when test="item.dataType == 'tinyint'">
  71 + tinyint
  72 + </when>
  73 + <when test="item.dataType == 'smallint'">
  74 + smallint
  75 + </when>
  76 + <when test="item.dataType == 'int'">
  77 + int
  78 + </when>
  79 + <when test="item.dataType == 'bigint'">
  80 + bigint
  81 + </when>
  82 + <when test="item.dataType == 'float'">
  83 + float
  84 + </when>
  85 + <when test="item.dataType == 'double'">
  86 + double
  87 + </when>
  88 + <when test="item.dataType == 'binary'">
  89 + binary
  90 + </when>
  91 + <when test="item.dataType == 'nchar'">
  92 + nchar
  93 + </when>
  94 + <when test="item.dataType == 'bool'">
  95 + bool
  96 + </when>
  97 + <when test="item.dataType == 'json'">
  98 + json
  99 + </when>
  100 + </choose>
  101 + </if>
  102 + <if test="item.size != null">
  103 + (#{item.size})
  104 + </if>
  105 + </foreach>
  106 + </update>
  107 +
  108 + <update id="createTable">
  109 + create table
  110 + if not exists #{dataBaseName}.#{tableName}
  111 + using #{dataBaseName}.#{superTableName}
  112 + tags
  113 + <foreach item="item" collection="tagsFieldValues" separator=","
  114 + open="(" close=")" index="">
  115 + #{item.fieldValue}
  116 + </foreach>
  117 + </update>
  118 +
  119 + <insert id="insertData">
  120 + insert into #{dataBaseName}.#{tableName}
  121 + <foreach item="item" collection="schemaFieldValues" separator=","
  122 + open="(" close=")" index="">
  123 + #{item.fieldName}
  124 + </foreach>
  125 + using #{dataBaseName}.#{superTableName}
  126 + tags
  127 + <foreach item="item" collection="tagsFieldValues" separator=","
  128 + open="(" close=")" index="">
  129 + #{item.fieldValue}
  130 + </foreach>
  131 + values
  132 + <foreach item="item" collection="schemaFieldValues" separator=","
  133 + open="(" close=")" index="">
  134 + #{item.fieldValue}
  135 + </foreach>
  136 + </insert>
  137 +
  138 +
  139 + <select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto"
  140 + resultType="Map">
  141 + select * from #{dataBaseName}.#{tableName}
  142 + <!--查询这里不能使用#{}占位符的方式,使用这种方式,tdEngine不识别为列名,只能使用${}占位的方式-->
  143 + <!--因为tdEngine引擎一次只执行一条sql,所以有效预防了sql的注入,且该服务该接口为内部调用,所以可以使用${}-->
  144 + where ${fieldName}
  145 + between #{startTime} and #{endTime}
  146 + </select>
  147 +
  148 + <update id="addColumnForSuperTable">
  149 + ALTER
  150 + STABLE
  151 + #{superTableName}
  152 + ADD
  153 + COLUMN
  154 + <if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
  155 + #{fieldsVo.fieldName}
  156 + </if>
  157 + <if test="fieldsVo.dataType != null || fieldsVo.dataType != ''">
  158 + <choose>
  159 + <when test="fieldsVo.dataType == 'timestamp'">
  160 + timestamp
  161 + </when>
  162 + <when test="fieldsVo.dataType == 'tinyint'">
  163 + tinyint
  164 + </when>
  165 + <when test="fieldsVo.dataType == 'smallint'">
  166 + smallint
  167 + </when>
  168 + <when test="fieldsVo.dataType == 'int'">
  169 + int
  170 + </when>
  171 + <when test="fieldsVo.dataType == 'bigint'">
  172 + bigint
  173 + </when>
  174 + <when test="fieldsVo.dataType == 'float'">
  175 + float
  176 + </when>
  177 + <when test="fieldsVo.dataType == 'double'">
  178 + double
  179 + </when>
  180 + <when test="fieldsVo.dataType == 'binary'">
  181 + binary
  182 + </when>
  183 + <when test="fieldsVo.dataType == 'nchar'">
  184 + nchar
  185 + </when>
  186 + <when test="fieldsVo.dataType == 'bool'">
  187 + bool
  188 + </when>
  189 + <when test="fieldsVo.dataType == 'json'">
  190 + json
  191 + </when>
  192 + </choose>
  193 + </if>
  194 + <if test="fieldsVo.size != null">
  195 + (
  196 + #{fieldsVo.size}
  197 + )
  198 + </if>
  199 + </update>
  200 +
  201 + <update id="dropColumnForSuperTable">
  202 + ALTER
  203 + STABLE
  204 + #{superTableName}
  205 + DROP
  206 + COLUMN
  207 + <if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
  208 + #{fieldsVo.fieldName}
  209 + </if>
  210 + </update>
  211 +
  212 + <update id="addTagForSuperTable">
  213 + ALTER
  214 + STABLE
  215 + #{superTableName}
  216 + ADD
  217 + TAG
  218 + <if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
  219 + #{fieldsVo.fieldName}
  220 + </if>
  221 + <if test="fieldsVo.dataType != null || fieldsVo.dataType != ''">
  222 + <choose>
  223 + <when test="fieldsVo.dataType == 'timestamp'">
  224 + timestamp
  225 + </when>
  226 + <when test="fieldsVo.dataType == 'tinyint'">
  227 + tinyint
  228 + </when>
  229 + <when test="fieldsVo.dataType == 'smallint'">
  230 + smallint
  231 + </when>
  232 + <when test="fieldsVo.dataType == 'int'">
  233 + int
  234 + </when>
  235 + <when test="fieldsVo.dataType == 'bigint'">
  236 + bigint
  237 + </when>
  238 + <when test="fieldsVo.dataType == 'float'">
  239 + float
  240 + </when>
  241 + <when test="fieldsVo.dataType == 'double'">
  242 + double
  243 + </when>
  244 + <when test="fieldsVo.dataType == 'binary'">
  245 + binary
  246 + </when>
  247 + <when test="fieldsVo.dataType == 'nchar'">
  248 + nchar
  249 + </when>
  250 + <when test="fieldsVo.dataType == 'bool'">
  251 + bool
  252 + </when>
  253 + <when test="fieldsVo.dataType == 'json'">
  254 + json
  255 + </when>
  256 + </choose>
  257 + </if>
  258 + <if test="fieldsVo.size != null">
  259 + (
  260 + #{fieldsVo.size}
  261 + )
  262 + </if>
  263 + </update>
  264 +
  265 + <update id="dropTagForSuperTable">
  266 + ALTER
  267 + STABLE
  268 + #{superTableName}
  269 + DROP
  270 + TAG
  271 + <if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
  272 + #{fieldsVo.fieldName}
  273 + </if>
  274 + </update>
  275 +
  276 + <select id="getCountByTimestamp" parameterType="cn.iocoder.yudao.module.iot.domain.SelectDto"
  277 + resultType="java.util.Map">
  278 + SELECT count(0) AS count
  279 + FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime}
  280 + </select>
  281 +
  282 + <select id="checkTableExists" resultType="java.lang.Integer">
  283 + SELECT COUNT(0)
  284 + FROM information_schema.ins_tables
  285 + WHERE db_name = #{dataBaseName}
  286 + AND table_name = #{tableName}
  287 + </select>
  288 +
  289 + <select id="getLastData" resultType="java.util.Map">
  290 + select last(time), *
  291 + from #{tableName}
  292 + where device_id = #{deviceId}
  293 + </select>
  294 +
  295 + <select id="getLastDataByTags" parameterType="cn.iocoder.yudao.module.iot.domain.TagsSelectDao"
  296 + resultType="Map">
  297 + select last(*)
  298 + from #{dataBaseName}.#{stableName} group by ${tagsName}
  299 + </select>
  300 +
  301 + <select id="getHistoryData" resultType="java.util.Map"
  302 + parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
  303 + SELECT #{fieldName}, ts
  304 + FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime}
  305 + LIMIT #{num}
  306 + </select>
  307 + <select id="getRealtimeData" resultType="java.util.Map"
  308 + parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
  309 + SELECT #{fieldName}, ts
  310 + FROM #{dataBaseName}.#{tableName} LIMIT #{num}
  311 + </select>
  312 + <select id="getAggregateData" resultType="java.util.Map"
  313 + parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
  314 + SELECT #{aggregate}(${fieldName})
  315 + FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime} interval (${interval})
  316 + LIMIT #{num}
  317 + </select>
  318 +
  319 +
  320 + <insert id="createSuperTableDevice">
  321 + ${sql}
  322 + </insert>
  323 +
  324 +</mapper>
... ...