【新增功能】 集成 tdengine
This commit is contained in:
parent
6a35ca7290
commit
05c5482715
|
@ -33,6 +33,7 @@
|
|||
<dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
|
||||
<kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
|
||||
<opengauss.jdbc.version>5.0.2</opengauss.jdbc.version>
|
||||
<taos.version>3.3.3</taos.version>
|
||||
<!-- 消息队列 -->
|
||||
<rocketmq-spring.version>2.3.0</rocketmq-spring.version>
|
||||
<!-- 服务保障相关 -->
|
||||
|
@ -253,6 +254,12 @@
|
|||
<version>${kingbase.jdbc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
<version>${taos.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
|
|
|
@ -63,6 +63,10 @@
|
|||
<artifactId>opengauss-jdbc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.yudao.module.iot.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @ClassDescription: tdEngine的基础实体类
|
||||
* @ClassName: BaseEntity
|
||||
* @Author: fxlinks
|
||||
* @Date: 2021-12-30 14:39:25
|
||||
* @Version 1.0
|
||||
*/
|
||||
@Data
|
||||
public class BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 数据库名称
|
||||
*/
|
||||
private String dataBaseName;
|
||||
|
||||
/**
|
||||
* 超级表名称
|
||||
*/
|
||||
private String superTableName;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package cn.iocoder.yudao.module.iot.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @ClassDescription: 建表的字段实体类
|
||||
* @ClassName: Fields
|
||||
* @Author: fxlinks
|
||||
* @Date: 2021-12-28 09:09:04
|
||||
* @Version 1.0
|
||||
*/
|
||||
@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;
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package cn.iocoder.yudao.module.iot.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassDescription: 建表的字段实体类的vo类
|
||||
* @ClassName: FieldsVo
|
||||
* @Author: fxlinks
|
||||
* @Date: 2021-12-28 11:30:06
|
||||
* @Version 1.0
|
||||
*/
|
||||
@Data
|
||||
public class FieldsVo {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
private String fieldName;
|
||||
|
||||
/**
|
||||
* 字段数据类型
|
||||
*/
|
||||
private String dataType;
|
||||
|
||||
/**
|
||||
* 字段字节大小
|
||||
*/
|
||||
private Integer size;
|
||||
|
||||
/**
|
||||
* @param fields 字段实体类
|
||||
* @return FieldsVo 字段实体vo类
|
||||
* @MethodDescription 字段实体类转为vo类
|
||||
* @author fx
|
||||
* @Date 2021/12/28 13:48
|
||||
*/
|
||||
public static FieldsVo fieldsTranscoding(Fields fields) throws SQLException {
|
||||
// if (StringUtils.isBlank(fields.getFieldName()) || fields.getDataType() == null) {
|
||||
// throw new SQLException("invalid operation: fieldName or dataType can not be null");
|
||||
// }
|
||||
// FieldsVo fieldsVo = new FieldsVo();
|
||||
// fieldsVo.setFieldName(fields.getFieldName());
|
||||
// fieldsVo.setDataType(fields.getDataType().getDataType());
|
||||
// fieldsVo.setSize(fields.getSize());
|
||||
// return fieldsVo;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fieldsList 字段实体类集合
|
||||
* @return List<FieldsVo> 字段实体vo类集合
|
||||
* @MethodDescription 字段实体类集合转为vo类集合
|
||||
* @author fx
|
||||
* @Date 2021/12/28 14:00
|
||||
*/
|
||||
public static List<FieldsVo> fieldsTranscoding(List<Fields> fieldsList) throws SQLException {
|
||||
List<FieldsVo> fieldsVoList = new ArrayList<>();
|
||||
for (Fields fields : fieldsList) {
|
||||
fieldsVoList.add(fieldsTranscoding(fields));
|
||||
}
|
||||
return fieldsVoList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
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;
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* @Description: 产品超级表模型
|
||||
* @Author: fx
|
||||
* @Website: http://www.sxfxck.com
|
||||
* @CreateDate: 2022/1/1$ 19:37$
|
||||
* @UpdateUser: fx
|
||||
* @UpdateDate: 2022/1/1$ 19:37$
|
||||
* @UpdateRemark: 修改内容
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@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;
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.yudao.module.iot.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @ClassDescription: 查询所需入参对象
|
||||
* @ClassName: SelectDto
|
||||
* @Author: fxlinks
|
||||
* @Date: 2022-01-07 14:12:26
|
||||
* @Version 1.0
|
||||
*/
|
||||
@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;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cn.iocoder.yudao.module.iot.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassDescription: 创建超级表需要的入参的实体类
|
||||
* @ClassName: SuperTableDto
|
||||
* @Author: fxlinks
|
||||
* @Date: 2021-12-28 15:03:45
|
||||
* @Version 1.0
|
||||
*/
|
||||
@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;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cn.iocoder.yudao.module.iot.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @ClassDescription: 创建超级表的子表需要的入参的实体类
|
||||
* @ClassName: TableDto
|
||||
* @Author: fxlinks
|
||||
* @Date: 2021-12-30 14:42:47
|
||||
* @Version 1.0
|
||||
*/
|
||||
@Data
|
||||
public class TableDto extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 超级表普通列字段的值
|
||||
* 值需要与创建超级表时普通列字段的数据类型对应上
|
||||
*/
|
||||
private List<Fields> schemaFieldValues;
|
||||
|
||||
/**
|
||||
* 超级表标签字段的值
|
||||
* 值需要与创建超级表时标签字段的数据类型对应上
|
||||
*/
|
||||
private List<Fields> tagsFieldValues;
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
private String tableName;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cn.iocoder.yudao.module.iot.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* @program: fxlinks
|
||||
* @description: 标签查询模型
|
||||
* @packagename: com.fx.tdengine.api.domain.rule
|
||||
* @author: fx
|
||||
* @e-mainl: 13733918655@163.com
|
||||
* @date: 2022-07-27 18:40
|
||||
**/
|
||||
@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;
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package cn.iocoder.yudao.module.iot.domain.visual;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @ClassDescription: 查询可视化所需入参对象
|
||||
* @ClassName: SelectDto
|
||||
* @Author: andyz
|
||||
* @Date: 2022-07-29 14:12:26
|
||||
* @Version 1.0
|
||||
*/
|
||||
@Data
|
||||
public class SelectVisualDto {
|
||||
|
||||
// @NotBlank(message = "invalid operation: tableName 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;
|
||||
/**
|
||||
* 查询类型,0历史数据,1实时数据,2聚合数据
|
||||
*/
|
||||
private int type;
|
||||
/**
|
||||
* 查询的数据量
|
||||
*/
|
||||
private int num;
|
||||
/**
|
||||
* 聚合函数
|
||||
*/
|
||||
private String aggregate;
|
||||
/**
|
||||
* 统计间隔数字+s/m/h/d
|
||||
* 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天
|
||||
*/
|
||||
private String interval;
|
||||
// @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 Map<String, Object> params;
|
||||
|
||||
private String sql;
|
||||
|
||||
private String deviceId;
|
||||
|
||||
private Long lastTime;
|
||||
}
|
|
@ -30,6 +30,15 @@ public enum IotProductFunctionTypeEnum implements IntArrayValuable {
|
|||
*/
|
||||
private final String description;
|
||||
|
||||
public static IotProductFunctionTypeEnum valueOf(Integer type) {
|
||||
for (IotProductFunctionTypeEnum value : values()) {
|
||||
if (value.getType().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
|
|
|
@ -42,6 +42,11 @@
|
|||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
|
|
|
@ -83,11 +83,10 @@ public class IotProductController {
|
|||
return success(BeanUtils.toBean(pageResult, IotProductRespVO.class));
|
||||
}
|
||||
|
||||
// TODO @haohao:改成 simple-list 哈
|
||||
@GetMapping("/list-all-simple")
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得所有产品列表")
|
||||
@PreAuthorize("@ss.hasPermission('iot:product:query')")
|
||||
public CommonResult<List<IotProductSimpleRespVO>> listAllSimpleProducts() {
|
||||
public CommonResult<List<IotProductSimpleRespVO>> getSimpleProductList() {
|
||||
List<IotProductDO> list = productService.getProductList();
|
||||
return success(BeanUtils.toBean(list, IotProductSimpleRespVO.class));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class ThingModelRespVO {
|
||||
|
||||
/**
|
||||
* 产品编号
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 产品标识
|
||||
*/
|
||||
private String productKey;
|
||||
|
||||
/**
|
||||
* 物模型
|
||||
*/
|
||||
private Model model;
|
||||
|
||||
/**
|
||||
* 物模型
|
||||
*/
|
||||
@Data
|
||||
public static class Model {
|
||||
|
||||
/**
|
||||
* 属性列表
|
||||
*/
|
||||
private List<ThingModelProperty> properties;
|
||||
|
||||
/**
|
||||
* 服务列表
|
||||
*/
|
||||
private List<ThingModelService> services;
|
||||
|
||||
/**
|
||||
* 事件列表
|
||||
*/
|
||||
private List<ThingModelEvent> events;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelDataType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FieldParser {
|
||||
|
||||
/**
|
||||
* 物模型到td数据类型映射
|
||||
*/
|
||||
private static final HashMap<String, String> TYPE_MAPPING = new HashMap<>() {{
|
||||
put("INT", "INT");
|
||||
put("FLOAT", "FLOAT");
|
||||
put("DOUBLE", "DOUBLE");
|
||||
put("BOOL", "BOOL");
|
||||
put("ENUM", "NCHAR");
|
||||
put("TEXT", "NCHAR");
|
||||
put("DATE", "NCHAR");
|
||||
}};
|
||||
|
||||
|
||||
/**
|
||||
* 将物模型字段转换为td字段
|
||||
*
|
||||
* @param property 物模型属性
|
||||
* @return TdField对象
|
||||
*/
|
||||
public static TdField parse(ThingModelProperty property) {
|
||||
String fieldName = property.getIdentifier().toLowerCase();
|
||||
ThingModelDataType type = property.getDataType();
|
||||
|
||||
// 将物模型字段类型映射为td字段类型
|
||||
String fType = TYPE_MAPPING.get(type.getType().toUpperCase());
|
||||
|
||||
int len = -1;
|
||||
// 如果字段类型为NCHAR,默认长度为64
|
||||
if ("NCHAR".equals(fType)) {
|
||||
len = 64;
|
||||
}
|
||||
|
||||
return new TdField(fieldName, fType, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取物模型中的字段列表
|
||||
*/
|
||||
public static List<TdField> parse(ThingModelRespVO thingModel) {
|
||||
return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将从库中查出来的字段信息转换为td字段对象
|
||||
*/
|
||||
public static List<TdField> parse(List rows) {
|
||||
return (List<TdField>) rows.stream().map((r) -> {
|
||||
List row = (List) r;
|
||||
String type = row.get(1).toString().toUpperCase();
|
||||
return new TdField(
|
||||
row.get(0).toString(),
|
||||
type,
|
||||
type.equals("NCHAR") ? Integer.parseInt(row.get(2).toString()) : -1);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段字义
|
||||
*/
|
||||
public static String getFieldDefine(TdField field) {
|
||||
return "`" + field.getName() + "`" + " " + (field.getLength() > 0 ?
|
||||
String.format("%s(%d)", field.getType(), field.getLength())
|
||||
: field.getType());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TableData {
|
||||
|
||||
/**
|
||||
* 超级表普通列字段的名称
|
||||
*/
|
||||
private List<String> schemaFieldList;
|
||||
|
||||
/**
|
||||
* 超级表标签字段的值
|
||||
* 值需要与创建超级表时标签字段的数据类型对应上
|
||||
*/
|
||||
private List<Object> tagsValueList;
|
||||
|
||||
/**
|
||||
* 超级表普通列字段的值
|
||||
* 值需要与创建超级表时普通列字段的数据类型对应上
|
||||
*/
|
||||
private List<Object> schemaValueList;
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 超级表名称
|
||||
*/
|
||||
private String superTableName;
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TableManager {
|
||||
|
||||
/**
|
||||
* 创建超级表模板(含存在判断)
|
||||
*/
|
||||
private static final String CREATE_STABLE_INE_TPL = "CREATE STABLE IF NOT EXISTS %s (%s) TAGS (%s);";
|
||||
|
||||
/**
|
||||
* 删除超级表
|
||||
*/
|
||||
private static final String DROP_STABLE_TPL = "DROP STABLE IF EXISTS %s;";
|
||||
|
||||
/**
|
||||
* 获取表的结构信息
|
||||
*/
|
||||
private static final String DESC_TB_TPL = "DESCRIBE %s;";
|
||||
|
||||
/**
|
||||
* 超级表增加列
|
||||
*/
|
||||
private static final String ALTER_STABLE_ADD_COL_TPL = "ALTER STABLE %s ADD COLUMN %s;";
|
||||
|
||||
/**
|
||||
* 超级表修改列
|
||||
*/
|
||||
private static final String ALTER_STABLE_MODIFY_COL_TPL = "ALTER STABLE %s MODIFY COLUMN %s;";
|
||||
|
||||
/**
|
||||
* 超级表删除列
|
||||
*/
|
||||
private static final String ALTER_STABLE_DROP_COL_TPL = "ALTER STABLE %s DROP COLUMN %s;";
|
||||
|
||||
/**
|
||||
* 创建普通表模板(含存在判断)
|
||||
*/
|
||||
private static final String CREATE_CTABLE_INE_TPL = "CREATE TABLE IF NOT EXISTS %s (%s)";
|
||||
|
||||
/**
|
||||
* 获取创建表sql
|
||||
*/
|
||||
public static String getCreateSTableSql(String tbName, List<TdField> fields, TdField... tags) {
|
||||
if (fields.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 生成字段片段
|
||||
StringBuilder sbField = new StringBuilder("time TIMESTAMP,");
|
||||
|
||||
for (TdField field : fields) {
|
||||
sbField.append(FieldParser.getFieldDefine(field));
|
||||
sbField.append(",");
|
||||
}
|
||||
sbField.deleteCharAt(sbField.length() - 1);
|
||||
|
||||
String fieldFrag = sbField.toString();
|
||||
|
||||
// 生成tag
|
||||
StringBuilder sbTag = new StringBuilder();
|
||||
for (TdField tag : tags) {
|
||||
sbTag.append(FieldParser.getFieldDefine(tag))
|
||||
.append(",");
|
||||
}
|
||||
sbTag.deleteCharAt(sbTag.length() - 1);
|
||||
|
||||
return String.format(CREATE_STABLE_INE_TPL, tbName, fieldFrag, sbTag);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取创建普通表sql
|
||||
*/
|
||||
public static String getCreateCTableSql(String tbName, List<TdField> fields) {
|
||||
if (fields.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//生成字段片段
|
||||
StringBuilder sbField = new StringBuilder("time timestamp,");
|
||||
|
||||
for (TdField field : fields) {
|
||||
sbField.append(FieldParser.getFieldDefine(field));
|
||||
sbField.append(",");
|
||||
}
|
||||
sbField.deleteCharAt(sbField.length() - 1);
|
||||
|
||||
String fieldFrag = sbField.toString();
|
||||
|
||||
return String.format(CREATE_CTABLE_INE_TPL, tbName, fieldFrag);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 取正确的表名
|
||||
*
|
||||
* @param name 表象
|
||||
*/
|
||||
public static String rightTbName(String name) {
|
||||
return name.toLowerCase().replace("-" , "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表详情的sql
|
||||
*/
|
||||
public static String getDescTableSql(String tbName) {
|
||||
return String.format(DESC_TB_TPL, tbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取添加字段sql
|
||||
*/
|
||||
public static String getAddSTableColumnSql(String tbName, List<TdField> fields) {
|
||||
StringBuilder sbAdd = new StringBuilder();
|
||||
for (TdField field : fields) {
|
||||
sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL,
|
||||
tbName,
|
||||
FieldParser.getFieldDefine(field)
|
||||
));
|
||||
}
|
||||
return sbAdd.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取修改字段sql
|
||||
*/
|
||||
public static String getModifySTableColumnSql(String tbName, List<TdField> fields) {
|
||||
StringBuilder sbModify = new StringBuilder();
|
||||
for (TdField field : fields) {
|
||||
sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL,
|
||||
tbName,
|
||||
FieldParser.getFieldDefine(field)
|
||||
));
|
||||
}
|
||||
return sbModify.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取删除字段sql
|
||||
*/
|
||||
public static String getDropSTableColumnSql(String tbName, List<TdField> fields) {
|
||||
StringBuilder sbDrop = new StringBuilder();
|
||||
for (TdField field : fields) {
|
||||
sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL,
|
||||
tbName,
|
||||
field.getName()
|
||||
));
|
||||
}
|
||||
return sbDrop.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* TD 引擎的字段
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TdField {
|
||||
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 字段类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 字段长度
|
||||
*/
|
||||
private int length;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TdResponse {
|
||||
|
||||
public static final int CODE_SUCCESS = 0;
|
||||
public static final int CODE_TB_NOT_EXIST = 866;
|
||||
|
||||
private String status;
|
||||
|
||||
private int code;
|
||||
|
||||
private String desc;
|
||||
|
||||
//[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]]
|
||||
private List data;
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TdRestApi {
|
||||
|
||||
@Value("${spring.datasource.dynamic.datasource.master.url}")
|
||||
private String url;
|
||||
|
||||
@Value("${spring.datasource.dynamic.datasource.master.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${spring.datasource.dynamic.datasource.master.password}")
|
||||
private String password;
|
||||
|
||||
private String getRestApiUrl() {
|
||||
//jdbc:TAOS-RS://127.0.0.1:6041/iotkit?xxxx
|
||||
String restUrl = url.replace("jdbc:TAOS-RS://" , "")
|
||||
.replaceAll("\\?.*" , "");
|
||||
// /rest/sql/iotkit
|
||||
int idx = restUrl.lastIndexOf("/");
|
||||
//127.0.0.1:6041/rest/sql/iotkit
|
||||
return String.format("%s/rest/sql/%s" , restUrl.substring(0, idx), restUrl.substring(idx + 1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新建td api请求对象
|
||||
*/
|
||||
public HttpRequest newApiRequest(String sql) {
|
||||
return HttpRequest
|
||||
.post(getRestApiUrl())
|
||||
.body(sql)
|
||||
.basicAuth(username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行sql
|
||||
*/
|
||||
public TdResponse execSql(String sql) {
|
||||
log.info("exec td sql:{}" , sql);
|
||||
HttpRequest request = newApiRequest(sql);
|
||||
HttpResponse response = request.execute();
|
||||
return JSONUtil.toBean(response.body(), TdResponse.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 统计的时间数据
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TimeData {
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
private long time;
|
||||
|
||||
/**
|
||||
* 数据值
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.domain.FieldsVo;
|
||||
import cn.iocoder.yudao.module.iot.domain.SelectDto;
|
||||
import cn.iocoder.yudao.module.iot.domain.TableDto;
|
||||
import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
|
||||
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
|
||||
import 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 {
|
||||
|
||||
void createDatabase(@Param("dataBaseName") String dataBaseName);
|
||||
|
||||
void createSuperTable(@Param("schemaFields") List<FieldsVo> schemaFields,
|
||||
@Param("tagsFields") List<FieldsVo> tagsFields,
|
||||
@Param("dataBaseName") String dataBaseName,
|
||||
@Param("superTableName") String superTableName);
|
||||
|
||||
void createTable(TableDto tableDto);
|
||||
|
||||
void insertData(TableDto tableDto);
|
||||
|
||||
List<Map<String, Object>> selectByTimestamp(SelectDto selectDto);
|
||||
|
||||
void addColumnForSuperTable(@Param("superTableName") String superTableName,
|
||||
@Param("fieldsVo") FieldsVo fieldsVo);
|
||||
|
||||
void dropColumnForSuperTable(@Param("superTableName") String superTableName,
|
||||
@Param("fieldsVo") FieldsVo fieldsVo);
|
||||
|
||||
void addTagForSuperTable(@Param("superTableName") String superTableName,
|
||||
@Param("fieldsVo") FieldsVo fieldsVo);
|
||||
|
||||
void dropTagForSuperTable(@Param("superTableName") String superTableName,
|
||||
@Param("fieldsVo") FieldsVo fieldsVo);
|
||||
|
||||
Map<String, Long> getCountByTimestamp(SelectDto selectDto);
|
||||
|
||||
/**
|
||||
* 检查表是否存在
|
||||
*
|
||||
* @param dataBaseName 数据库名称
|
||||
* @param tableName 表名称
|
||||
*/
|
||||
Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName);
|
||||
|
||||
Map<String, Object> getLastData(SelectDto selectDto);
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 创建超级表
|
||||
*
|
||||
* @param sql sql
|
||||
* @return 返回值
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
Integer createSuperTableDevice(String sql);
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cn.iocoder.yudao.module.iot.framework.aspect;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TaosAspect 是一个处理 Taos 数据库返回值的切面。
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class TaosAspect {
|
||||
|
||||
@Around("execution(java.util.Map<String,Object> cn.iocoder.yudao.module.iot.dal.tdengine.*.*(..))")
|
||||
public Object handleType(ProceedingJoinPoint joinPoint) {
|
||||
Map<String, Object> result = null;
|
||||
try {
|
||||
result = (Map<String, Object>) joinPoint.proceed();
|
||||
result.replaceAll((key, value) -> {
|
||||
if (value instanceof byte[]) {
|
||||
return new String((byte[]) value);
|
||||
} else if (value instanceof Timestamp) {
|
||||
return ((Timestamp) value).getTime();
|
||||
}
|
||||
return value;
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
log.error("TaosAspect handleType error", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -8,8 +8,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSaveReq
|
|||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -31,6 +36,10 @@ public class IotProductServiceImpl implements IotProductService {
|
|||
@Resource
|
||||
private IotProductMapper productMapper;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private IotThinkModelFunctionService thinkModelFunctionService;
|
||||
|
||||
@Override
|
||||
public Long createProduct(IotProductSaveReqVO createReqVO) {
|
||||
// 1. 生成 ProductKey
|
||||
|
@ -106,11 +115,17 @@ public class IotProductServiceImpl implements IotProductService {
|
|||
}
|
||||
|
||||
@Override
|
||||
@DSTransactional(rollbackFor = Exception.class)
|
||||
public void updateProductStatus(Long id, Integer status) {
|
||||
// 1. 校验存在
|
||||
validateProductExists(id);
|
||||
// 2. 更新
|
||||
IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build();
|
||||
// 3. 产品是发布状态
|
||||
if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getType())) {
|
||||
// 3.1 创建超级表数据模型
|
||||
thinkModelFunctionService.createSuperTableDataModel(id);
|
||||
}
|
||||
productMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
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.thinkmodelfunction.IotThinkModelFunctionDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据结构接口
|
||||
*/
|
||||
public interface IotDbStructureDataService {
|
||||
|
||||
/**
|
||||
* 创建物模型定义
|
||||
*/
|
||||
void createSuperTable(ThingModelRespVO thingModel, Integer deviceType);
|
||||
|
||||
/**
|
||||
* 更新物模型定义
|
||||
*/
|
||||
void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType);
|
||||
|
||||
/**
|
||||
* 创建超级表数据模型
|
||||
*/
|
||||
void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList);
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class IotDbStructureDataServiceImpl implements IotDbStructureDataService {
|
||||
|
||||
@Resource
|
||||
private TdEngineMapper tdEngineMapper;
|
||||
|
||||
@Resource
|
||||
private TdRestApi tdRestApi;
|
||||
|
||||
@Override
|
||||
public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
|
||||
// 获取物模型中的属性定义
|
||||
List<TdField> fields = FieldParser.parse(thingModel);
|
||||
String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
|
||||
|
||||
// 生成创建超级表的 SQL
|
||||
String sql = TableManager.getCreateSTableSql(tbName, fields, new TdField("device_id", "NCHAR", 64));
|
||||
if (sql == null) {
|
||||
log.warn("生成的 SQL 为空,无法创建超级表");
|
||||
return;
|
||||
}
|
||||
log.info("执行 SQL: {}", sql);
|
||||
|
||||
// 执行 SQL 创建超级表
|
||||
tdEngineMapper.createSuperTableDevice(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
|
||||
try {
|
||||
// 获取旧字段信息
|
||||
String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
|
||||
String sql = TableManager.getDescTableSql(tbName);
|
||||
TdResponse response = tdRestApi.execSql(sql);
|
||||
if (response.getCode() != TdResponse.CODE_SUCCESS) {
|
||||
throw new RuntimeException("获取表描述错误: " + JSONUtil.toJsonStr(response));
|
||||
}
|
||||
|
||||
List<TdField> oldFields = FieldParser.parse(response.getData());
|
||||
List<TdField> newFields = FieldParser.parse(thingModel);
|
||||
|
||||
// 找出新增的字段
|
||||
List<TdField> addFields = newFields.stream()
|
||||
.filter(f -> oldFields.stream().noneMatch(old -> old.getName().equals(f.getName())))
|
||||
.collect(Collectors.toList());
|
||||
if (!addFields.isEmpty()) {
|
||||
sql = TableManager.getAddSTableColumnSql(tbName, addFields);
|
||||
response = tdRestApi.execSql(sql);
|
||||
if (response.getCode() != TdResponse.CODE_SUCCESS) {
|
||||
throw new RuntimeException("添加表字段错误: " + JSONUtil.toJsonStr(response));
|
||||
}
|
||||
}
|
||||
|
||||
// 找出修改的字段
|
||||
List<TdField> modifyFields = newFields.stream()
|
||||
.filter(f -> oldFields.stream().anyMatch(old ->
|
||||
old.getName().equals(f.getName()) &&
|
||||
(!old.getType().equals(f.getType()) || old.getLength() != f.getLength())))
|
||||
.collect(Collectors.toList());
|
||||
if (!modifyFields.isEmpty()) {
|
||||
sql = TableManager.getModifySTableColumnSql(tbName, modifyFields);
|
||||
response = tdRestApi.execSql(sql);
|
||||
if (response.getCode() != TdResponse.CODE_SUCCESS) {
|
||||
throw new RuntimeException("修改表字段错误: " + JSONUtil.toJsonStr(response));
|
||||
}
|
||||
}
|
||||
|
||||
// 找出删除的字段
|
||||
List<TdField> dropFields = oldFields.stream()
|
||||
.filter(f -> !"time".equals(f.getName()) && !"device_id".equals(f.getName()) &&
|
||||
newFields.stream().noneMatch(n -> n.getName().equals(f.getName())))
|
||||
.collect(Collectors.toList());
|
||||
if (!dropFields.isEmpty()) {
|
||||
sql = TableManager.getDropSTableColumnSql(tbName, dropFields);
|
||||
response = tdRestApi.execSql(sql);
|
||||
if (response.getCode() != TdResponse.CODE_SUCCESS) {
|
||||
throw new RuntimeException("删除表字段错误: " + JSONUtil.toJsonStr(response));
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error("更新物模型超级表失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
|
||||
// 1. 生成 ThingModelRespVO
|
||||
ThingModelRespVO thingModel = new ThingModelRespVO();
|
||||
thingModel.setId(product.getId());
|
||||
thingModel.setProductKey(product.getProductKey());
|
||||
|
||||
// 1.1 设置属性、服务和事件
|
||||
ThingModelRespVO.Model model = new ThingModelRespVO.Model();
|
||||
List<ThingModelProperty> properties = new ArrayList<>();
|
||||
|
||||
// 1.2 遍历功能列表并分类
|
||||
for (IotThinkModelFunctionDO function : functionList) {
|
||||
if (Objects.requireNonNull(IotProductFunctionTypeEnum.valueOf(function.getType())) == IotProductFunctionTypeEnum.PROPERTY) {
|
||||
ThingModelProperty property = new ThingModelProperty();
|
||||
property.setIdentifier(function.getIdentifier());
|
||||
property.setName(function.getName());
|
||||
property.setDescription(function.getDescription());
|
||||
property.setDataType(function.getProperty().getDataType());
|
||||
properties.add(property);
|
||||
}
|
||||
}
|
||||
|
||||
// 1.3 判断属性列表是否为空
|
||||
if (properties.isEmpty()) {
|
||||
log.warn("物模型属性列表为空,不创建超级表");
|
||||
return;
|
||||
}
|
||||
|
||||
model.setProperties(properties);
|
||||
thingModel.setModel(model);
|
||||
|
||||
// 2. 判断是否已经创建,如果已经创建则进行更新
|
||||
String tbName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
|
||||
Integer iot = tdEngineMapper.checkTableExists("ruoyi_vue_pro", tbName);
|
||||
if (iot != null && iot > 0) {
|
||||
// 3. 更新
|
||||
updateSuperTable(thingModel, product.getDeviceType());
|
||||
} else {
|
||||
// 4. 创建
|
||||
createSuperTable(thingModel, product.getDeviceType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据产品key获取产品属性超级表名
|
||||
*/
|
||||
static String getProductPropertySTableName(Integer deviceType, String productKey) {
|
||||
if (deviceType == 1) {
|
||||
return String.format("gateway_sub_" + productKey).toLowerCase();
|
||||
} else if (deviceType == 2) {
|
||||
return String.format("gateway_" + productKey).toLowerCase();
|
||||
} else {
|
||||
return String.format("device_" + productKey).toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据deviceId获取设备属性表名
|
||||
*/
|
||||
static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) {
|
||||
return String.format(deviceType + "_" + productKey + "_" + deviceKey).toLowerCase();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.domain.FieldsVo;
|
||||
import cn.iocoder.yudao.module.iot.domain.SelectDto;
|
||||
import cn.iocoder.yudao.module.iot.domain.TableDto;
|
||||
import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
|
||||
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TdEngineService
|
||||
*/
|
||||
public interface TdEngineService {
|
||||
|
||||
/**
|
||||
* 创建数据库
|
||||
*
|
||||
* @param dataBaseName 数据库名称
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void createDateBase(String dataBaseName) throws Exception;
|
||||
|
||||
/**
|
||||
* 创建超级表
|
||||
*
|
||||
* @param schemaFields schema字段
|
||||
* @param tagsFields tags字段
|
||||
* @param dataBaseName 数据库名称
|
||||
* @param superTableName 超级表名称
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName,
|
||||
String superTableName) throws Exception;
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
*
|
||||
* @param tableDto 表信息
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void createTable(TableDto tableDto) throws Exception;
|
||||
|
||||
/**
|
||||
* 插入数据
|
||||
*
|
||||
* @param tableDto 表信息
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void insertData(TableDto tableDto) throws Exception;
|
||||
|
||||
/**
|
||||
* 根据时间戳查询数据
|
||||
*
|
||||
* @param selectDto 查询条件
|
||||
* @return 数据
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) throws Exception;
|
||||
|
||||
/**
|
||||
* 为超级表添加列
|
||||
*
|
||||
* @param superTableName 超级表名称
|
||||
* @param fieldsVo 字段信息
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception;
|
||||
|
||||
/**
|
||||
* 为超级表删除列
|
||||
*
|
||||
* @param superTableName 超级表名称
|
||||
* @param fieldsVo 字段信息
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception;
|
||||
|
||||
/**
|
||||
* 为超级表添加tag
|
||||
*/
|
||||
Long getCountByTimesTamp(SelectDto selectDto) throws Exception;
|
||||
|
||||
/**
|
||||
* 检查表是否存在
|
||||
*
|
||||
* @return 1存在 0不存在
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
Integer checkTableExists(SelectDto selectDto) throws Exception;
|
||||
|
||||
/**
|
||||
* 初始化超级表
|
||||
*
|
||||
* @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);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.domain.FieldsVo;
|
||||
import cn.iocoder.yudao.module.iot.domain.SelectDto;
|
||||
import cn.iocoder.yudao.module.iot.domain.TableDto;
|
||||
import cn.iocoder.yudao.module.iot.domain.TagsSelectDao;
|
||||
import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TdEngineServiceImpl implements TdEngineService {
|
||||
|
||||
|
||||
@Override
|
||||
public void createDateBase(String dataBaseName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSuperTable(List<FieldsVo> schemaFields, List<FieldsVo> tagsFields, String dataBaseName, String superTableName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTable(TableDto tableDto) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertData(TableDto tableDto) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> selectByTimesTamp(SelectDto selectDto) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCountByTimesTamp(SelectDto selectDto) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer checkTableExists(SelectDto selectDto) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
|
@ -62,4 +62,10 @@ public interface IotThinkModelFunctionService {
|
|||
*/
|
||||
PageResult<IotThinkModelFunctionDO> getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 创建超级表数据模型
|
||||
*
|
||||
* @param productId 产品编号
|
||||
*/
|
||||
void createSuperTableDataModel(Long productId);
|
||||
}
|
|
@ -15,10 +15,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMode
|
|||
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert;
|
||||
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.mysql.thinkmodelfunction.IotThinkModelFunctionMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotAccessModeEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||
import cn.iocoder.yudao.module.iot.service.tdengine.IotDbStructureDataService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -45,6 +48,11 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
|
|||
@Resource
|
||||
private IotThinkModelFunctionMapper thinkModelFunctionMapper;
|
||||
|
||||
@Resource
|
||||
private IotProductService productService;
|
||||
@Resource
|
||||
private IotDbStructureDataService dbStructureDataService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createThinkModelFunction(IotThinkModelFunctionSaveReqVO createReqVO) {
|
||||
|
@ -162,6 +170,16 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
|
|||
return thinkModelFunctionMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSuperTableDataModel(Long productId) {
|
||||
// 1. 查询产品
|
||||
IotProductDO product = productService.getProduct(productId);
|
||||
// 2. 查询产品的物模型功能列表
|
||||
List<IotThinkModelFunctionDO> functionList = thinkModelFunctionMapper.selectListByProductId(productId);
|
||||
// 3. 生成 TDengine 的数据模型
|
||||
dbStructureDataService.createSuperTableDataModel(product, functionList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建默认的事件和服务
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
<?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">
|
||||
|
||||
<update id="createDatabase" parameterType="String">
|
||||
create database if not exists #{dataBaseName}
|
||||
</update>
|
||||
|
||||
<update id="createSuperTable">
|
||||
create table 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.size != null">
|
||||
(#{item.size})
|
||||
</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.size != null">
|
||||
(#{item.size})
|
||||
</if>
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<update id="createTable">
|
||||
create table
|
||||
if not exists #{dataBaseName}.#{tableName}
|
||||
using #{dataBaseName}.#{superTableName}
|
||||
tags
|
||||
<foreach item="item" collection="tagsFieldValues" separator=","
|
||||
open="(" close=")" index="">
|
||||
#{item.fieldValue}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<insert id="insertData">
|
||||
insert into #{dataBaseName}.#{tableName}
|
||||
<foreach item="item" collection="schemaFieldValues" separator=","
|
||||
open="(" close=")" index="">
|
||||
#{item.fieldName}
|
||||
</foreach>
|
||||
using #{dataBaseName}.#{superTableName}
|
||||
tags
|
||||
<foreach item="item" collection="tagsFieldValues" separator=","
|
||||
open="(" close=")" index="">
|
||||
#{item.fieldValue}
|
||||
</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
|
||||
#{superTableName}
|
||||
ADD
|
||||
COLUMN
|
||||
<if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
|
||||
#{fieldsVo.fieldName}
|
||||
</if>
|
||||
<if test="fieldsVo.dataType != null || fieldsVo.dataType != ''">
|
||||
<choose>
|
||||
<when test="fieldsVo.dataType == 'timestamp'">
|
||||
timestamp
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'tinyint'">
|
||||
tinyint
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'smallint'">
|
||||
smallint
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'int'">
|
||||
int
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'bigint'">
|
||||
bigint
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'float'">
|
||||
float
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'double'">
|
||||
double
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'binary'">
|
||||
binary
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'nchar'">
|
||||
nchar
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'bool'">
|
||||
bool
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'json'">
|
||||
json
|
||||
</when>
|
||||
</choose>
|
||||
</if>
|
||||
<if test="fieldsVo.size != null">
|
||||
(
|
||||
#{fieldsVo.size}
|
||||
)
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<update id="dropColumnForSuperTable">
|
||||
ALTER
|
||||
STABLE
|
||||
#{superTableName}
|
||||
DROP
|
||||
COLUMN
|
||||
<if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
|
||||
#{fieldsVo.fieldName}
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<update id="addTagForSuperTable">
|
||||
ALTER
|
||||
STABLE
|
||||
#{superTableName}
|
||||
ADD
|
||||
TAG
|
||||
<if test="fieldsVo.fieldName != null || fieldsVo.fieldName != ''">
|
||||
#{fieldsVo.fieldName}
|
||||
</if>
|
||||
<if test="fieldsVo.dataType != null || fieldsVo.dataType != ''">
|
||||
<choose>
|
||||
<when test="fieldsVo.dataType == 'timestamp'">
|
||||
timestamp
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'tinyint'">
|
||||
tinyint
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'smallint'">
|
||||
smallint
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'int'">
|
||||
int
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'bigint'">
|
||||
bigint
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'float'">
|
||||
float
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'double'">
|
||||
double
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'binary'">
|
||||
binary
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'nchar'">
|
||||
nchar
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'bool'">
|
||||
bool
|
||||
</when>
|
||||
<when test="fieldsVo.dataType == 'json'">
|
||||
json
|
||||
</when>
|
||||
</choose>
|
||||
</if>
|
||||
<if test="fieldsVo.size != null">
|
||||
(
|
||||
#{fieldsVo.size}
|
||||
)
|
||||
</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="checkTableExists" resultType="java.lang.Integer">
|
||||
SELECT COUNT(0)
|
||||
FROM information_schema.ins_tables
|
||||
WHERE db_name = #{dataBaseName}
|
||||
AND table_name = #{tableName}
|
||||
</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}, ts
|
||||
FROM #{dataBaseName}.#{tableName} WHERE ts BETWEEN #{startTime} AND #{endTime}
|
||||
LIMIT #{num}
|
||||
</select>
|
||||
<select id="getRealtimeData" resultType="java.util.Map"
|
||||
parameterType="cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto">
|
||||
SELECT #{fieldName}, ts
|
||||
FROM #{dataBaseName}.#{tableName} LIMIT #{num}
|
||||
</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>
|
||||
|
||||
|
||||
<insert id="createSuperTableDevice">
|
||||
${sql}
|
||||
</insert>
|
||||
|
||||
</mapper>
|
Loading…
Reference in New Issue