mirror of
https://gitee.com/myxzgzs/boyue-vue-pro.git
synced 2025-08-08 16:32:46 +08:00
【优化功能】 优化 tdengine 操作数据库相关代码
This commit is contained in:
parent
9b30d5d355
commit
89fb71e857
@ -0,0 +1,38 @@
|
||||
package cn.iocoder.yudao.module.iot.enums;
|
||||
|
||||
/**
|
||||
* Iot 常量
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IotConstants {
|
||||
|
||||
/**
|
||||
* 获取设备表名
|
||||
* <p>
|
||||
* 格式为 device_{productKey}_{deviceName}
|
||||
*/
|
||||
String DEVICE_TABLE_NAME_FORMAT = "device_%s_%s";
|
||||
|
||||
/**
|
||||
* 获取产品属性超级表名 - 网关子设备
|
||||
* <p>
|
||||
* 格式为 gateway_sub_{productKey}
|
||||
*/
|
||||
String GATEWAY_SUB_STABLE_NAME_FORMAT = "gateway_sub_%s";
|
||||
|
||||
/**
|
||||
* 获取产品属性超级表名 - 网关
|
||||
* <p>
|
||||
* 格式为 gateway_{productKey}
|
||||
*/
|
||||
String GATEWAY_STABLE_NAME_FORMAT = "gateway_%s";
|
||||
|
||||
/**
|
||||
* 获取产品属性超级表名 - 设备
|
||||
* <p>
|
||||
* 格式为 device_{productKey}
|
||||
*/
|
||||
String DEVICE_STABLE_NAME_FORMAT = "device_%s";
|
||||
|
||||
}
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.device;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO;
|
||||
@ -31,19 +31,17 @@ public class IotDeviceDataController {
|
||||
@Resource
|
||||
private IotDeviceDataService deviceDataService;
|
||||
|
||||
// TODO @haohao:是不是叫 get-latest 就好了。因为 data 已经在 url 里了哈
|
||||
@GetMapping("/latest-data")
|
||||
@GetMapping("/latest")
|
||||
@Operation(summary = "获取设备属性最新数据")
|
||||
public CommonResult<List<IotDeviceDataRespVO>> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
|
||||
List<IotDeviceDataDO> list = deviceDataService.getDevicePropertiesLatestData(deviceDataReqVO);
|
||||
public CommonResult<List<IotDeviceDataRespVO>> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {
|
||||
List<IotDeviceDataDO> list = deviceDataService.getLatestDeviceProperties(deviceDataReqVO);
|
||||
return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class));
|
||||
}
|
||||
|
||||
// TODO @haohao:是不是叫 /history-data => page
|
||||
@GetMapping("/history-data")
|
||||
@GetMapping("/history")
|
||||
@Operation(summary = "获取设备属性历史数据")
|
||||
public CommonResult<PageResult<IotTimeDataRespVO>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
|
||||
PageResult<Map<String, Object>> list = deviceDataService.getDevicePropertiesHistoryData(deviceDataReqVO);
|
||||
public CommonResult<PageResult<IotTimeDataRespVO>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {
|
||||
PageResult<Map<String, Object>> list = deviceDataService.getHistoryDeviceProperties(deviceDataReqVO);
|
||||
return success(BeanUtils.toBean(list, IotTimeDataRespVO.class));
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,9 @@ import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
// TODO @haohao:IotDeviceDataPageReqVO
|
||||
@Schema(description = "管理后台 - IoT 设备数据 Request VO")
|
||||
@Data
|
||||
public class IotDeviceDataReqVO extends PageParam {
|
||||
public class IotDeviceDataPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
|
||||
private Long deviceId;
|
@ -1,9 +1,6 @@
|
||||
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
@ -1,15 +1,12 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.dataobject.device;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
@ -23,39 +20,52 @@ import java.time.LocalDateTime;
|
||||
@AllArgsConstructor
|
||||
public class IotDeviceDataDO {
|
||||
|
||||
// TODO @haohao:每个字段的关联关系,可以 @ 下哈。
|
||||
/**
|
||||
* 设备编号
|
||||
* <p>
|
||||
* 关联 {@link IotDeviceDO#getId()}
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 物模型编号
|
||||
* <p>
|
||||
* 关联 {@link IotThinkModelFunctionDO#getId()}
|
||||
*/
|
||||
private Long thinkModelFunctionId;
|
||||
|
||||
/**
|
||||
* 产品标识
|
||||
* <p>
|
||||
* 关联 {@link IotProductDO#getProductKey()}
|
||||
*/
|
||||
private String productKey;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
* <p>
|
||||
* 冗余 {@link IotDeviceDO#getDeviceName()}
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 属性标识符
|
||||
* <p>
|
||||
* 关联 {@link IotThinkModelFunctionDO#getIdentifier()}
|
||||
*/
|
||||
private String identifier;
|
||||
|
||||
/**
|
||||
* 属性名称
|
||||
* <p>
|
||||
* 关联 {@link IotThinkModelFunctionDO#getName()}
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 数据类型
|
||||
* <p>
|
||||
* 关联 {@link IotThinkModelFunctionDO#getProperty()#getDataType()}
|
||||
*/
|
||||
private String dataType;
|
||||
|
||||
|
@ -42,12 +42,6 @@ public class SelectDO {
|
||||
*/
|
||||
private String type;
|
||||
|
||||
// TODO @haohao:这个字段,是啥哈?
|
||||
/**
|
||||
* 查询条件
|
||||
*/
|
||||
private Set<Integer> orgIds;
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
|
@ -27,14 +27,17 @@ public class SelectVisualDO {
|
||||
* 查询类型,0历史数据,1实时数据,2聚合数据
|
||||
*/
|
||||
private int type;
|
||||
|
||||
/**
|
||||
* 查询的数据量
|
||||
*/
|
||||
private int num;
|
||||
|
||||
/**
|
||||
* 聚合函数
|
||||
*/
|
||||
private String aggregate;
|
||||
|
||||
/**
|
||||
* 统计间隔数字+s/m/h/d
|
||||
* 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天
|
||||
|
@ -1,159 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// TODO @haohao:这个还有用哇?
|
||||
/**
|
||||
* TableManager 类用于管理 TDengine 表的创建、删除和结构信息获取
|
||||
*/
|
||||
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<TdFieldDO> fields, TdFieldDO... tags) {
|
||||
if (fields.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 生成字段片段
|
||||
StringBuilder sbField = new StringBuilder("time TIMESTAMP,");
|
||||
|
||||
for (TdFieldDO field : fields) {
|
||||
sbField.append(FieldParser.getFieldDefine(field));
|
||||
sbField.append(",");
|
||||
}
|
||||
sbField.deleteCharAt(sbField.length() - 1);
|
||||
|
||||
String fieldFrag = sbField.toString();
|
||||
|
||||
// 生成tag
|
||||
StringBuilder sbTag = new StringBuilder();
|
||||
for (TdFieldDO 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<TdFieldDO> fields) {
|
||||
if (fields.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//生成字段片段
|
||||
StringBuilder sbField = new StringBuilder("time timestamp,");
|
||||
|
||||
for (TdFieldDO 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<TdFieldDO> fields) {
|
||||
StringBuilder sbAdd = new StringBuilder();
|
||||
for (TdFieldDO 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<TdFieldDO> fields) {
|
||||
StringBuilder sbModify = new StringBuilder();
|
||||
for (TdFieldDO 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<TdFieldDO> fields) {
|
||||
StringBuilder sbDrop = new StringBuilder();
|
||||
for (TdFieldDO field : fields) {
|
||||
sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL,
|
||||
tbName,
|
||||
field.getFieldName()
|
||||
));
|
||||
}
|
||||
return sbDrop.toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,30 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TD 引擎的超级表 Mapper
|
||||
* 专门处理 DDL(数据定义语言)操作,包含所有的数据库和表的定义操作,例如创建数据库、创建超级表、创建子表等
|
||||
*/
|
||||
@Mapper
|
||||
@DS("tdengine")
|
||||
public interface TdEngineSuperTableMapper {
|
||||
public interface TdEngineDDLMapper {
|
||||
|
||||
/**
|
||||
* 创建数据库
|
||||
* SQL:CREATE DATABASE [IF NOT EXISTS] db_name [database_options];
|
||||
*
|
||||
* @param dataBaseName 数据库名称
|
||||
*/
|
||||
@TenantIgnore
|
||||
void createDatabase(@Param("dataBaseName") String dataBaseName);
|
||||
|
||||
/**
|
||||
* 创建超级表
|
||||
@ -25,7 +36,7 @@ public interface TdEngineSuperTableMapper {
|
||||
* columns 列信息
|
||||
* tags 标签信息
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
void createSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
@ -36,7 +47,7 @@ public interface TdEngineSuperTableMapper {
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
List<Map<String, Object>> showSuperTables(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
@ -47,7 +58,7 @@ public interface TdEngineSuperTableMapper {
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
List<Map<String, Object>> describeSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
@ -59,7 +70,7 @@ public interface TdEngineSuperTableMapper {
|
||||
* superTableName 超级表名称
|
||||
* column 列信息
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
void addColumnForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
@ -71,7 +82,7 @@ public interface TdEngineSuperTableMapper {
|
||||
* superTableName 超级表名称
|
||||
* column 列信息
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
void dropColumnForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
@ -83,7 +94,7 @@ public interface TdEngineSuperTableMapper {
|
||||
* superTableName 超级表名称
|
||||
* column 列信息
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
void modifyColumnWidthForSuperTable(TdTableDO superTable);
|
||||
|
||||
|
||||
@ -95,7 +106,7 @@ public interface TdEngineSuperTableMapper {
|
||||
* superTableName 超级表名称
|
||||
* tag 标签信息
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
void addTagForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
@ -106,6 +117,33 @@ public interface TdEngineSuperTableMapper {
|
||||
* superTableName 超级表名称
|
||||
* tag 标签信息
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@TenantIgnore
|
||||
void dropTagForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 创建子表 - 创建子表
|
||||
* SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...);
|
||||
*
|
||||
* @param table 表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* tableName 子表名称
|
||||
* tags TAG 字段
|
||||
*/
|
||||
@TenantIgnore
|
||||
void createTable(TdTableDO table);
|
||||
|
||||
/**
|
||||
* 创建子表 - 创建子表并指定标签的值
|
||||
* SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...);
|
||||
*
|
||||
* @param table 表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* tableName 子表名称
|
||||
* tags TAG 字段
|
||||
*/
|
||||
@TenantIgnore
|
||||
void createTableWithTags(TdTableDO table);
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 专门处理 TD Engine 的 DML(数据操作语言)操作,处理所有的数据查询和写入操作,如插入数据、查询数据等
|
||||
*/
|
||||
@Mapper
|
||||
@DS("tdengine")
|
||||
public interface TdEngineDMLMapper {
|
||||
|
||||
/**
|
||||
* 插入数据 - 指定列插入数据
|
||||
*
|
||||
* @param table 数据
|
||||
* dataBaseName 数据库名
|
||||
* tableName 表名
|
||||
* columns 列
|
||||
*/
|
||||
@TenantIgnore
|
||||
void insertData(TdTableDO table);
|
||||
|
||||
/**
|
||||
* 根据时间戳查询数据
|
||||
*
|
||||
* @param selectDO 查询条件
|
||||
* @return 查询结果列表
|
||||
*/
|
||||
@TenantIgnore
|
||||
List<Map<String, Object>> selectByTimestamp(SelectDO selectDO);
|
||||
|
||||
/**
|
||||
* 根据时间戳获取数据条数
|
||||
*
|
||||
* @param selectDO 查询条件
|
||||
* @return 数据条数
|
||||
*/
|
||||
@TenantIgnore
|
||||
Map<String, Long> selectCountByTimestamp(SelectDO selectDO);
|
||||
|
||||
/**
|
||||
* 获取最新数据
|
||||
*
|
||||
* @param selectDO 查询条件
|
||||
* @return 最新数据
|
||||
*/
|
||||
@TenantIgnore
|
||||
Map<String, Object> selectOneLastData(SelectDO selectDO);
|
||||
|
||||
/**
|
||||
* 获取历史数据列表
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 历史数据列表
|
||||
*/
|
||||
@TenantIgnore
|
||||
List<Map<String, Object>> selectHistoryDataList(SelectVisualDO selectVisualDO);
|
||||
|
||||
/**
|
||||
* 获取实时数据列表
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 实时数据列表
|
||||
*/
|
||||
@TenantIgnore
|
||||
List<Map<String, Object>> selectRealtimeDataList(SelectVisualDO selectVisualDO);
|
||||
|
||||
/**
|
||||
* 获取聚合数据列表
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 聚合数据列表
|
||||
*/
|
||||
@TenantIgnore
|
||||
List<Map<String, Object>> selectAggregateDataList(SelectVisualDO selectVisualDO);
|
||||
|
||||
/**
|
||||
* 根据标签获取最新数据列表
|
||||
*
|
||||
* @param tagsSelectDO 查询条件
|
||||
* @return 最新数据列表
|
||||
*/
|
||||
@TenantIgnore
|
||||
List<Map<String, Object>> selectLastDataListByTags(TagsSelectDO tagsSelectDO);
|
||||
|
||||
/**
|
||||
* 获取历史数据条数
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 数据条数
|
||||
*/
|
||||
@TenantIgnore
|
||||
Long selectHistoryCount(SelectVisualDO selectVisualDO);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* TD 引擎的数据写入 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
@DS("tdengine")
|
||||
public interface TdEngineDataWriterMapper {
|
||||
|
||||
/**
|
||||
* 插入数据 - 指定列插入数据
|
||||
*
|
||||
* @param table 数据
|
||||
* dataBaseName 数据库名
|
||||
* tableName 表名
|
||||
* columns 列
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
void insertData(TdTableDO table);
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.tdengine;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
// TODO @haohao:InterceptorIgnore 忽略租户,可以在每个方法上,添加 @TenantIgnore 哈。
|
||||
/**
|
||||
* TD 引擎的数据库 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
@DS("tdengine")
|
||||
public interface TdEngineDatabaseMapper {
|
||||
|
||||
/**
|
||||
* 创建数据库
|
||||
* SQL:CREATE DATABASE [IF NOT EXISTS] db_name [database_options];
|
||||
*
|
||||
* @param dataBaseName 数据库名称
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
void createDatabase(@Param("dataBaseName") String dataBaseName);
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TD 引擎的查询 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
@DS("tdengine")
|
||||
public interface TdEngineQueryMapper {
|
||||
|
||||
/**
|
||||
* 根据时间戳查询数据
|
||||
*
|
||||
* @param selectDO 查询条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<Map<String, Object>> selectByTimestamp(SelectDO selectDO);
|
||||
|
||||
// TODO @haohao:最好方法的命名,和数据库操作的保持一直。get => select。然后 selectList or selectOne
|
||||
/**
|
||||
* 根据时间戳获取数据条数
|
||||
*
|
||||
* @param selectDO 查询条件
|
||||
* @return 数据条数
|
||||
*/
|
||||
Map<String, Long> getCountByTimestamp(SelectDO selectDO);
|
||||
|
||||
/**
|
||||
* 获取最新数据
|
||||
*
|
||||
* @param selectDO 查询条件
|
||||
* @return 最新数据
|
||||
*/
|
||||
Map<String, Object> getLastData(SelectDO selectDO);
|
||||
|
||||
/**
|
||||
* 获取历史数据
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 历史数据列表
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
List<Map<String, Object>> getHistoryData(SelectVisualDO selectVisualDO);
|
||||
|
||||
/**
|
||||
* 获取实时数据
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 实时数据列表
|
||||
*/
|
||||
List<Map<String, Object>> getRealtimeData(SelectVisualDO selectVisualDO);
|
||||
|
||||
/**
|
||||
* 获取聚合数据
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 聚合数据列表
|
||||
*/
|
||||
List<Map<String, Object>> getAggregateData(SelectVisualDO selectVisualDO);
|
||||
|
||||
/**
|
||||
* 根据标签获取最新数据
|
||||
*
|
||||
* @param tagsSelectDO 查询条件
|
||||
* @return 最新数据列表
|
||||
*/
|
||||
List<Map<String, Object>> getLastDataByTags(TagsSelectDO tagsSelectDO);
|
||||
|
||||
/**
|
||||
* 获取历史数据条数
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 数据条数
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
Long getHistoryCount(SelectVisualDO selectVisualDO);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.dal.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* TD 引擎的表 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
@DS("tdengine")
|
||||
public interface TdEngineTableMapper {
|
||||
|
||||
/**
|
||||
* 创建子表 - 创建子表
|
||||
* SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...);
|
||||
*
|
||||
* @param table 表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* tableName 子表名称
|
||||
* tags TAG 字段
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
void createTable(TdTableDO table);
|
||||
|
||||
/**
|
||||
* 创建子表 - 创建子表并指定标签的值
|
||||
* SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...);
|
||||
*
|
||||
* @param table 表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* tableName 子表名称
|
||||
* tags TAG 字段
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
void createTableWithTags(TdTableDO table);
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@ -31,7 +31,7 @@ public interface IotDeviceDataService {
|
||||
* @param deviceId 设备编号
|
||||
* @return 设备属性最新数据
|
||||
*/
|
||||
List<IotDeviceDataDO> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId);
|
||||
List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceId);
|
||||
|
||||
/**
|
||||
* 获得设备属性历史数据
|
||||
@ -39,5 +39,5 @@ public interface IotDeviceDataService {
|
||||
* @param deviceDataReqVO 设备属性历史数据 Request VO
|
||||
* @return 设备属性历史数据
|
||||
*/
|
||||
PageResult<Map<String, Object>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO);
|
||||
PageResult<Map<String, Object>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO);
|
||||
}
|
@ -1,18 +1,20 @@
|
||||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.IotConstants;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
|
||||
import cn.iocoder.yudao.module.iot.service.tdengine.TdEngineQueryService;
|
||||
import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
@ -40,7 +42,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
|
||||
@Resource
|
||||
private IotThinkModelFunctionService thinkModelFunctionService;
|
||||
@Resource
|
||||
private TdEngineQueryService tdEngineQueryService;
|
||||
private TdEngineDMLMapper tdEngineDMLMapper;
|
||||
|
||||
@Resource
|
||||
private DeviceDataRedisDAO deviceDataRedisDAO;
|
||||
@ -66,7 +68,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IotDeviceDataDO> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
|
||||
public List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {
|
||||
List<IotDeviceDataDO> list = new ArrayList<>();
|
||||
// 1. 获取设备信息
|
||||
IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
|
||||
@ -106,7 +108,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<Map<String, Object>> getDevicePropertiesHistoryData(IotDeviceDataReqVO deviceDataReqVO) {
|
||||
public PageResult<Map<String, Object>> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) {
|
||||
PageResult<Map<String, Object>> pageResult = new PageResult<>();
|
||||
// 1. 获取设备信息
|
||||
IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
|
||||
@ -121,17 +123,17 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
|
||||
params.put("rows", deviceDataReqVO.getPageSize());
|
||||
params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
|
||||
selectVisualDO.setParams(params);
|
||||
pageResult.setList(tdEngineQueryService.getHistoryData(selectVisualDO));
|
||||
pageResult.setTotal(tdEngineQueryService.getHistoryCount(selectVisualDO));
|
||||
pageResult.setList(tdEngineDMLMapper.selectHistoryDataList(selectVisualDO));
|
||||
pageResult.setTotal(tdEngineDMLMapper.selectHistoryCount(selectVisualDO));
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
private String getDatabaseName() {
|
||||
return url.substring(url.lastIndexOf("/") + 1);
|
||||
return StrUtil.subAfter(url, "/", true);
|
||||
}
|
||||
|
||||
private static String getDeviceTableName(String productKey, String deviceName) {
|
||||
return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase());
|
||||
return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName);
|
||||
}
|
||||
|
||||
}
|
@ -19,7 +19,9 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Base64;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -137,10 +139,13 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
* @return 生成的 MQTT Password
|
||||
*/
|
||||
private String generateMqttPassword() {
|
||||
// TODO @haohao:【后续优化】在实际应用中,建议使用更安全的方法生成 MQTT Password,如加密或哈希
|
||||
return UUID.randomUUID().toString();
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
byte[] passwordBytes = new byte[32]; // 256 位的随机数
|
||||
secureRandom.nextBytes(passwordBytes);
|
||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成唯一的 DeviceName
|
||||
*
|
||||
@ -214,30 +219,28 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
@Override
|
||||
public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
|
||||
// 1. 校验存在
|
||||
// TODO @haohao:这里的 iotDeviceDO => device。一个是去掉 iot,一个是去掉 DO 后缀。这样,简洁一点。
|
||||
IotDeviceDO iotDeviceDO = validateDeviceExists(updateReqVO.getId());
|
||||
IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
|
||||
|
||||
// 2.1 更新状态和更新时间
|
||||
IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
|
||||
// TODO @haohao:下面几个状态的处理,可以考虑 if else if。这样,看起来会有层次感哈
|
||||
// 2.2.1 以前是未激活,现在是上线,设置设备激活时间
|
||||
// TODO @haohao:这里可以使用 ObjectUtils.equalsAny 类似这种哈。
|
||||
if (Objects.equals(iotDeviceDO.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
|
||||
&& Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
updateObj.setActiveTime(LocalDateTime.now());
|
||||
}
|
||||
// 2.2.2 如果是上线,设置上线时间
|
||||
if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
updateObj.setLastOnlineTime(LocalDateTime.now());
|
||||
}
|
||||
// 2.2.3 如果是离线,设置离线时间
|
||||
if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) {
|
||||
updateObj.setLastOfflineTime(LocalDateTime.now());
|
||||
IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
|
||||
// 2.2 更新状态相关时间
|
||||
if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
|
||||
&& Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
// 从未激活到在线,设置激活时间和最后上线时间
|
||||
updateDevice.setActiveTime(LocalDateTime.now());
|
||||
updateDevice.setLastOnlineTime(LocalDateTime.now());
|
||||
} else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
|
||||
// 如果是上线,设置最后上线时间
|
||||
updateDevice.setLastOnlineTime(LocalDateTime.now());
|
||||
} else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) {
|
||||
// 如果是离线,设置最后离线时间
|
||||
updateDevice.setLastOfflineTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 2.3 设置状态更新时间
|
||||
updateObj.setStatusLastUpdateTime(LocalDateTime.now());
|
||||
updateDevice.setStatusLastUpdateTime(LocalDateTime.now());
|
||||
// 2.4 更新到数据库
|
||||
deviceMapper.updateById(updateObj);
|
||||
deviceMapper.updateById(updateDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -26,7 +28,7 @@ import java.util.stream.Collectors;
|
||||
public class IotSuperTableServiceImpl implements IotSuperTableService {
|
||||
|
||||
@Resource
|
||||
private TdEngineSuperTableService tdEngineSuperTableService;
|
||||
private TdEngineDDLMapper tdEngineDDLMapper;
|
||||
|
||||
@Value("${spring.datasource.dynamic.datasource.tdengine.url}")
|
||||
private String url;
|
||||
@ -42,9 +44,10 @@ public class IotSuperTableServiceImpl implements IotSuperTableService {
|
||||
|
||||
String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey());
|
||||
String databaseName = getDatabaseName();
|
||||
Integer tableExists = tdEngineSuperTableService.checkSuperTableExists(new TdTableDO(databaseName, superTableName));
|
||||
|
||||
if (tableExists != null && tableExists > 0) {
|
||||
List<Map<String, Object>> results = tdEngineDDLMapper.showSuperTables(new TdTableDO(databaseName, superTableName));
|
||||
int tableExists = results == null || results.isEmpty() ? 0 : results.size();
|
||||
if (tableExists > 0) {
|
||||
updateSuperTable(thingModel, product.getDeviceType());
|
||||
} else {
|
||||
createSuperTable(thingModel, product.getDeviceType());
|
||||
@ -76,7 +79,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService {
|
||||
String databaseName = getDatabaseName();
|
||||
|
||||
// 创建超级表
|
||||
tdEngineSuperTableService.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));
|
||||
tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,7 +101,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService {
|
||||
* 获取表的字段信息
|
||||
*/
|
||||
private List<TdFieldDO> getTableFields(String tableName) {
|
||||
List<Map<String, Object>> tableDescription = tdEngineSuperTableService.describeSuperTable(new TdTableDO(getDatabaseName(), tableName));
|
||||
List<Map<String, Object>> tableDescription = tdEngineDDLMapper.describeSuperTable(new TdTableDO(getDatabaseName(), tableName));
|
||||
if (CollUtil.isEmpty(tableDescription)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@ -127,34 +130,40 @@ public class IotSuperTableServiceImpl implements IotSuperTableService {
|
||||
|
||||
// 添加新增字段
|
||||
if (CollUtil.isNotEmpty(addFields)) {
|
||||
tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder()
|
||||
for (TdFieldDO addField : addFields) {
|
||||
tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder()
|
||||
.dataBaseName(databaseName)
|
||||
.superTableName(tableName)
|
||||
.columns(addFields)
|
||||
.column(addField)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
// 删除旧字段
|
||||
if (CollUtil.isNotEmpty(dropFields)) {
|
||||
tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder()
|
||||
for (TdFieldDO dropField : dropFields) {
|
||||
tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder()
|
||||
.dataBaseName(databaseName)
|
||||
.superTableName(tableName)
|
||||
.columns(dropFields)
|
||||
.column(dropField)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
// 修改字段(先删除再添加)
|
||||
if (CollUtil.isNotEmpty(modifyFields)) {
|
||||
tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder()
|
||||
for (TdFieldDO modifyField : modifyFields) {
|
||||
tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder()
|
||||
.dataBaseName(databaseName)
|
||||
.superTableName(tableName)
|
||||
.columns(modifyFields)
|
||||
.column(modifyField)
|
||||
.build());
|
||||
tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder()
|
||||
tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder()
|
||||
.dataBaseName(databaseName)
|
||||
.superTableName(tableName)
|
||||
.columns(addFields)
|
||||
.column(modifyField)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需要新增的字段
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
@ -11,6 +13,9 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.IotConstants;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
@ -30,6 +35,14 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class IotThingModelMessageServiceImpl implements IotThingModelMessageService {
|
||||
|
||||
private static final String TAG_NOTE = "TAG";
|
||||
private static final String NOTE = "note";
|
||||
private static final String TIME = "time";
|
||||
private static final String DEVICE_KEY = "device_key";
|
||||
private static final String DEVICE_NAME = "device_name";
|
||||
private static final String PRODUCT_KEY = "product_key";
|
||||
private static final String DEVICE_TYPE = "device_type";
|
||||
|
||||
@Value("${spring.datasource.dynamic.datasource.tdengine.url}")
|
||||
private String url;
|
||||
|
||||
@ -38,11 +51,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
|
||||
@Resource
|
||||
private IotDeviceService iotDeviceService;
|
||||
@Resource
|
||||
private TdEngineTableService tdEngineTableService;
|
||||
private TdEngineDDLMapper tdEngineDDLMapper;
|
||||
@Resource
|
||||
private TdEngineSuperTableService tdEngineSuperTableService;
|
||||
@Resource
|
||||
private TdEngineDataWriterService tdEngineDataWriterService;
|
||||
private TdEngineDMLMapper tdEngineDMLMapper;
|
||||
|
||||
@Resource
|
||||
private DeviceDataRedisDAO deviceDataRedisDAO;
|
||||
@ -51,62 +62,64 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
|
||||
@Override
|
||||
@TenantIgnore
|
||||
public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) {
|
||||
// 判断设备状态,如果为未激活状态,创建数据表
|
||||
// 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态
|
||||
if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) {
|
||||
// 创建设备数据表
|
||||
createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
|
||||
// 更新设备状态
|
||||
// TODO @haohao:下面可以考虑,链式调用。iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO().setid().setstatus())
|
||||
IotDeviceStatusUpdateReqVO updateReqVO = new IotDeviceStatusUpdateReqVO();
|
||||
updateReqVO.setId(device.getId());
|
||||
updateReqVO.setStatus(IotDeviceStatusEnum.ONLINE.getStatus());
|
||||
iotDeviceService.updateDeviceStatus(updateReqVO);
|
||||
iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO()
|
||||
.setId(device.getId())
|
||||
.setStatus(IotDeviceStatusEnum.ONLINE.getStatus()));
|
||||
}
|
||||
|
||||
// TODO @haohao:这个变量,可以和 “过滤并收集有效的属性字段” 那块,因为关联度高一点。
|
||||
// 获取设备属性
|
||||
// 2. 获取设备属性并进行物模型校验,过滤非物模型属性
|
||||
Map<String, Object> params = thingModelMessage.dataToMap();
|
||||
|
||||
// 物模型校验,过滤非物模型属性
|
||||
List<IotThinkModelFunctionDO> functionList = iotThinkModelFunctionService
|
||||
.getThinkModelFunctionListByProductKey(thingModelMessage.getProductKey())
|
||||
.stream()
|
||||
.filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType()))
|
||||
.toList();
|
||||
List<IotThinkModelFunctionDO> functionList = getValidFunctionList(thingModelMessage.getProductKey());
|
||||
if (functionList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取属性标识符集合
|
||||
// TODO @haohao:这个变量,可以和 “过滤并收集有效的属性字段” 那块,因为关联度高一点。另外,可以使用 CollectionUtils。convertSet
|
||||
Set<String> propertyIdentifiers = functionList.stream()
|
||||
.map(IotThinkModelFunctionDO::getIdentifier)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 3. 过滤并收集有效的属性字段,缓存设备属性
|
||||
List<TdFieldDO> schemaFieldValues = filterAndCollectValidFields(params, functionList, device, thingModelMessage.getTime());
|
||||
if (schemaFieldValues.size() == 1) { // 仅有时间字段,无需保存
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 构建并保存设备属性数据
|
||||
tdEngineDMLMapper.insertData(TdTableDO.builder()
|
||||
.dataBaseName(getDatabaseName())
|
||||
.tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()))
|
||||
.columns(schemaFieldValues)
|
||||
.build());
|
||||
}
|
||||
|
||||
private List<IotThinkModelFunctionDO> getValidFunctionList(String productKey) {
|
||||
return iotThinkModelFunctionService
|
||||
.getThinkModelFunctionListByProductKey(productKey)
|
||||
.stream()
|
||||
.filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotThinkModelFunctionDO> functionList, IotDeviceDO device, Long time) {
|
||||
// 1. 获取属性标识符集合
|
||||
Set<String> propertyIdentifiers = CollectionUtils.convertSet(functionList, IotThinkModelFunctionDO::getIdentifier);
|
||||
|
||||
// 2. 构建属性标识符和属性的映射
|
||||
Map<String, IotThinkModelFunctionDO> functionMap = functionList.stream()
|
||||
.collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, function -> function));
|
||||
|
||||
// 过滤并收集有效的属性字段
|
||||
// 3. 过滤并收集有效的属性字段
|
||||
List<TdFieldDO> schemaFieldValues = new ArrayList<>();
|
||||
schemaFieldValues.add(new TdFieldDO("time", thingModelMessage.getTime()));
|
||||
schemaFieldValues.add(new TdFieldDO(TIME, time));
|
||||
params.forEach((key, val) -> {
|
||||
if (propertyIdentifiers.contains(key)) {
|
||||
schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val));
|
||||
// 缓存设备属性
|
||||
// TODO @haohao:这个缓存的写入,可以使用的时候 cache 么?被动读
|
||||
setDeviceDataCache(device, functionMap.get(key), val, thingModelMessage.getTime());
|
||||
setDeviceDataCache(device, functionMap.get(key), val, time);
|
||||
}
|
||||
});
|
||||
// TODO @haohao:疑问,为什么 1 不继续哈?
|
||||
if (schemaFieldValues.size() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建并保存设备属性数据
|
||||
tdEngineDataWriterService.insertData(TdTableDO.builder().build()
|
||||
.setDataBaseName(getDatabaseName())
|
||||
.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()))
|
||||
.setColumns(schemaFieldValues));
|
||||
return schemaFieldValues;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,7 +145,6 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
|
||||
deviceDataRedisDAO.set(deviceData);
|
||||
}
|
||||
|
||||
// TODO @haohao:实现没问题哈。这个方法的空行有点多,逻辑分块上没这么明显。看看能不能改下。
|
||||
/**
|
||||
* 创建设备数据表
|
||||
*
|
||||
@ -142,36 +154,37 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
|
||||
* @param deviceKey 设备 Key
|
||||
*/
|
||||
private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) {
|
||||
// 1. 获取超级表名和数据库名
|
||||
String superTableName = getProductPropertySTableName(deviceType, productKey);
|
||||
String dataBaseName = getDatabaseName();
|
||||
|
||||
List<Map<String, Object>> maps = tdEngineSuperTableService.describeSuperTable(new TdTableDO(dataBaseName, superTableName));
|
||||
// 2. 获取超级表的结构信息
|
||||
List<Map<String, Object>> maps = tdEngineDDLMapper.describeSuperTable(new TdTableDO(dataBaseName, superTableName));
|
||||
List<TdFieldDO> tagsFieldValues = new ArrayList<>();
|
||||
|
||||
if (maps != null) {
|
||||
// TODO @haohao:一些字符串,是不是可以枚举起来哈。
|
||||
// TODO @haohao:这种过滤的,常用的,可以考虑用 CollectionUtils.filterList。一些常用的 stream 操作,适合封装哈
|
||||
List<Map<String, Object>> taggedNotesList = maps.stream()
|
||||
.filter(map -> "TAG".equals(map.get("note")))
|
||||
.toList();
|
||||
// 2.1 过滤出 TAG 类型的字段
|
||||
List<Map<String, Object>> taggedNotesList = CollectionUtils.filterList(maps, map -> TAG_NOTE.equals(map.get(NOTE)));
|
||||
|
||||
|
||||
// 2.2 解析字段信息
|
||||
tagsFieldValues = FieldParser.parse(taggedNotesList.stream()
|
||||
.map(map -> List.of(map.get("field"), map.get("type"), map.get("length")))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
// 2.3 设置 TAG 字段的值
|
||||
for (TdFieldDO tagsFieldValue : tagsFieldValues) {
|
||||
switch (tagsFieldValue.getFieldName()) {
|
||||
case "product_key" -> tagsFieldValue.setFieldValue(productKey);
|
||||
case "device_key" -> tagsFieldValue.setFieldValue(deviceKey);
|
||||
case "device_name" -> tagsFieldValue.setFieldValue(deviceName);
|
||||
case "device_type" -> tagsFieldValue.setFieldValue(deviceType);
|
||||
case PRODUCT_KEY -> tagsFieldValue.setFieldValue(productKey);
|
||||
case DEVICE_KEY -> tagsFieldValue.setFieldValue(deviceKey);
|
||||
case DEVICE_NAME -> tagsFieldValue.setFieldValue(deviceName);
|
||||
case DEVICE_TYPE -> tagsFieldValue.setFieldValue(deviceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建设备数据表
|
||||
// 3. 创建设备数据表
|
||||
String tableName = getDeviceTableName(productKey, deviceName);
|
||||
tdEngineTableService.createTable(TdTableDO.builder().build()
|
||||
tdEngineDDLMapper.createTable(TdTableDO.builder().build()
|
||||
.setDataBaseName(dataBaseName)
|
||||
.setSuperTableName(superTableName)
|
||||
.setTableName(tableName)
|
||||
@ -184,8 +197,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
|
||||
* @return 数据库名称
|
||||
*/
|
||||
private String getDatabaseName() {
|
||||
// TODO @haohao:可以使用 StrUtil.subAftetLast 这种方法
|
||||
return url.substring(url.lastIndexOf("/") + 1);
|
||||
return StrUtil.subAfter(url, "/", true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,9 +210,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
|
||||
private static String getProductPropertySTableName(Integer deviceType, String productKey) {
|
||||
// TODO @haohao:枚举下,会好点哈。
|
||||
return switch (deviceType) {
|
||||
case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase();
|
||||
case 2 -> String.format("gateway_%s", productKey).toLowerCase();
|
||||
default -> String.format("device_%s", productKey).toLowerCase();
|
||||
case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase();
|
||||
case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase();
|
||||
default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
@ -212,7 +224,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
|
||||
* @return 设备表名
|
||||
*/
|
||||
private static String getDeviceTableName(String productKey, String deviceName) {
|
||||
return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase());
|
||||
return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase());
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
|
||||
/**
|
||||
* TD 引擎的数据写入 Service 接口
|
||||
*/
|
||||
public interface TdEngineDataWriterService {
|
||||
|
||||
/**
|
||||
* 插入数据 - 指定列插入数据
|
||||
*
|
||||
* @param table 数据
|
||||
* dataBaseName 数据库名
|
||||
* tableName 表名
|
||||
* columns 列
|
||||
*/
|
||||
void insertData(TdTableDO table);
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* TD 引擎的数据写入 Service 实现类
|
||||
*/
|
||||
@Service
|
||||
public class TdEngineDataWriterServiceImpl implements TdEngineDataWriterService {
|
||||
|
||||
@Resource
|
||||
private TdEngineDataWriterMapper tdEngineDataWriterMapper;
|
||||
|
||||
@Override
|
||||
public void insertData(TdTableDO table) {
|
||||
tdEngineDataWriterMapper.insertData(table);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
/**
|
||||
* TD 引擎的数据库 Service 接口
|
||||
*/
|
||||
public interface TdEngineDatabaseService {
|
||||
|
||||
/**
|
||||
* 创建数据库
|
||||
*
|
||||
* @param dataBaseName 数据库名称
|
||||
*/
|
||||
void createDatabase(String dataBaseName);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* TD 引擎的数据库 Service 实现类
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TdEngineDatabaseServiceImpl implements TdEngineDatabaseService {
|
||||
|
||||
@Resource
|
||||
private TdEngineDatabaseMapper tdEngineDatabaseMapper;
|
||||
|
||||
@Override
|
||||
public void createDatabase(String dataBaseName) {
|
||||
tdEngineDatabaseMapper.createDatabase(dataBaseName);
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TD 引擎的查询 Service 接口
|
||||
*/
|
||||
public interface TdEngineQueryService {
|
||||
|
||||
/**
|
||||
* 获取历史数据
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 历史数据列表
|
||||
*/
|
||||
List<Map<String, Object>> getHistoryData(SelectVisualDO selectVisualDO);
|
||||
|
||||
/**
|
||||
* 获取历史数据条数
|
||||
*
|
||||
* @param selectVisualDO 查询条件
|
||||
* @return 数据条数
|
||||
*/
|
||||
Long getHistoryCount(SelectVisualDO selectVisualDO);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineQueryMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class TdEngineQueryServiceImpl implements TdEngineQueryService {
|
||||
|
||||
@Resource
|
||||
private TdEngineQueryMapper tdEngineQueryMapper;
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> getHistoryData(SelectVisualDO selectVisualDO) {
|
||||
return tdEngineQueryMapper.getHistoryData(selectVisualDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getHistoryCount(SelectVisualDO selectVisualDO) {
|
||||
return tdEngineQueryMapper.getHistoryCount(selectVisualDO);
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TD 引擎的超级表 Service 接口
|
||||
*/
|
||||
public interface TdEngineSuperTableService {
|
||||
|
||||
/**
|
||||
* 创建超级表
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* columns 列信息
|
||||
* tags 标签信息
|
||||
*/
|
||||
void createSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 查看超级表 - 显示当前数据库下的所有超级表信息
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
*/
|
||||
|
||||
List<Map<String, Object>> showSuperTables(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 查看超级表 - 获取超级表的结构信息
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
*/
|
||||
List<Map<String, Object>> describeSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 修改超级表 - 增加列
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* column 列信息
|
||||
*/
|
||||
void addColumnForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 修改超级表 - 删除列
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* column 列信息
|
||||
*/
|
||||
void dropColumnForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 修改超级表 - 修改列宽
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* column 列信息
|
||||
*/
|
||||
void modifyColumnWidthForSuperTable(TdTableDO superTable);
|
||||
|
||||
|
||||
/**
|
||||
* 修改超级表 - 为超级表添加标签
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* tag 标签信息
|
||||
*/
|
||||
void addTagForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 修改超级表 - 为超级表删除标签
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* tag 标签信息
|
||||
*/
|
||||
void dropTagForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 检查超级表是否存在
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* @return 超级表数量
|
||||
*/
|
||||
Integer checkSuperTableExists(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 为超级表添加列
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* columns 列信息
|
||||
*/
|
||||
void addColumnsForSuperTable(TdTableDO superTable);
|
||||
|
||||
/**
|
||||
* 为超级表删除列
|
||||
*
|
||||
* @param superTable 超级表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* columns 列信息
|
||||
*/
|
||||
void dropColumnsForSuperTable(TdTableDO superTable);
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineSuperTableMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TD 引擎的超级表 Service 实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TdEngineSuperTableServiceImpl implements TdEngineSuperTableService {
|
||||
|
||||
@Resource
|
||||
private TdEngineSuperTableMapper tdEngineSuperTableMapper;
|
||||
|
||||
@Override
|
||||
public void createSuperTable(TdTableDO superTable) {
|
||||
tdEngineSuperTableMapper.createSuperTable(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> showSuperTables(TdTableDO superTable) {
|
||||
return tdEngineSuperTableMapper.showSuperTables(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> describeSuperTable(TdTableDO superTable) {
|
||||
return tdEngineSuperTableMapper.describeSuperTable(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addColumnForSuperTable(TdTableDO superTable) {
|
||||
tdEngineSuperTableMapper.addColumnForSuperTable(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropColumnForSuperTable(TdTableDO superTable) {
|
||||
tdEngineSuperTableMapper.dropColumnForSuperTable(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyColumnWidthForSuperTable(TdTableDO superTable) {
|
||||
tdEngineSuperTableMapper.modifyColumnWidthForSuperTable(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTagForSuperTable(TdTableDO superTable) {
|
||||
tdEngineSuperTableMapper.addTagForSuperTable(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropTagForSuperTable(TdTableDO superTable) {
|
||||
tdEngineSuperTableMapper.dropTagForSuperTable(superTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer checkSuperTableExists(TdTableDO superTable) {
|
||||
List<Map<String, Object>> results = tdEngineSuperTableMapper.showSuperTables(superTable);
|
||||
return results == null || results.isEmpty() ? 0 : results.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addColumnsForSuperTable(TdTableDO superTable) {
|
||||
for (TdFieldDO column : superTable.getColumns()) {
|
||||
tdEngineSuperTableMapper.addColumnForSuperTable(TdTableDO.builder()
|
||||
.dataBaseName(superTable.getDataBaseName())
|
||||
.superTableName(superTable.getSuperTableName())
|
||||
.column(column)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropColumnsForSuperTable(TdTableDO superTable) {
|
||||
for (TdFieldDO column : superTable.getColumns()) {
|
||||
tdEngineSuperTableMapper.dropColumnForSuperTable(TdTableDO.builder()
|
||||
.dataBaseName(superTable.getDataBaseName())
|
||||
.superTableName(superTable.getSuperTableName())
|
||||
.column(column)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
|
||||
/**
|
||||
* TD 引擎的表 Service 接口
|
||||
*/
|
||||
public interface TdEngineTableService {
|
||||
|
||||
/**
|
||||
* 创建表 - 创建超级表的子表
|
||||
*
|
||||
* @param table 表信息
|
||||
* dataBaseName 数据库名称
|
||||
* superTableName 超级表名称
|
||||
* tableName 子表名称
|
||||
* tags TAG 字段
|
||||
*/
|
||||
void createTable(TdTableDO table);
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.tdengine;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* TD 引擎的表 Service 实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TdEngineTableServiceImpl implements TdEngineTableService {
|
||||
|
||||
@Resource
|
||||
private TdEngineTableMapper tdEngineTableMapper;
|
||||
|
||||
@Override
|
||||
public void createTable(TdTableDO table) {
|
||||
tdEngineTableMapper.createTable(table);
|
||||
}
|
||||
}
|
@ -2,7 +2,12 @@
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineSuperTableMapper">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper">
|
||||
|
||||
<!-- 创建数据库 -->
|
||||
<update id="createDatabase" parameterType="String">
|
||||
CREATE DATABASE IF NOT EXISTS ${dataBaseName}
|
||||
</update>
|
||||
|
||||
<!-- 创建超级表 -->
|
||||
<update id="createSuperTable">
|
||||
@ -68,4 +73,29 @@
|
||||
ALTER STABLE ${dataBaseName}.${superTableName} DROP TAG ${tag.fieldName}
|
||||
</update>
|
||||
|
||||
<!-- 创建子表 -->
|
||||
<update id="createTable">
|
||||
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
|
||||
USING ${dataBaseName}.${superTableName}
|
||||
TAGS
|
||||
<foreach item="item" collection="tags" separator=","
|
||||
open="(" close=")">
|
||||
#{item.fieldValue}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<!-- 创建子表,带有 TAGS -->
|
||||
<update id="createTableWithTags">
|
||||
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
|
||||
USING ${dataBaseName}.${superTableName}
|
||||
<foreach item="item" collection="tags" separator="," open="(" close=")">
|
||||
#{item.fieldName}
|
||||
</foreach>
|
||||
TAGS
|
||||
<foreach item="item" collection="tags" separator=","
|
||||
open="(" close=")">
|
||||
#{item.fieldValue}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
</mapper>
|
@ -2,7 +2,21 @@
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineQueryMapper">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper">
|
||||
|
||||
<!-- 插入数据 -->
|
||||
<insert id="insertData">
|
||||
INSERT INTO ${dataBaseName}.${tableName}
|
||||
<foreach item="item" collection="columns" separator=","
|
||||
open="(" close=")">
|
||||
${item.fieldName}
|
||||
</foreach>
|
||||
VALUES
|
||||
<foreach item="item" collection="columns" separator=","
|
||||
open="(" close=")">
|
||||
#{item.fieldValue}
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 根据时间戳查询数据 -->
|
||||
<select id="selectByTimestamp" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO"
|
||||
@ -12,7 +26,7 @@
|
||||
</select>
|
||||
|
||||
<!-- 获取时间范围内的数据条数 -->
|
||||
<select id="getCountByTimestamp" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO"
|
||||
<select id="selectCountByTimestamp" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO"
|
||||
resultType="java.util.Map">
|
||||
SELECT COUNT(0) AS count
|
||||
FROM ${dataBaseName}.${tableName}
|
||||
@ -20,14 +34,14 @@
|
||||
</select>
|
||||
|
||||
<!-- 获取最新数据 -->
|
||||
<select id="getLastData" resultType="java.util.Map">
|
||||
<select id="selectOneLastData" resultType="java.util.Map">
|
||||
SELECT LAST(time), *
|
||||
FROM ${tableName}
|
||||
WHERE device_id = #{deviceId}
|
||||
</select>
|
||||
|
||||
<!-- 根据标签获取最新数据 -->
|
||||
<select id="getLastDataByTags" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO"
|
||||
<select id="selectLastDataListByTags" parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO"
|
||||
resultType="Map">
|
||||
SELECT LAST(*)
|
||||
FROM ${dataBaseName}.${stableName}
|
||||
@ -35,7 +49,7 @@
|
||||
</select>
|
||||
|
||||
<!-- 获取历史数据 -->
|
||||
<select id="getHistoryData" resultType="java.util.Map"
|
||||
<select id="selectHistoryDataList" resultType="java.util.Map"
|
||||
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO">
|
||||
SELECT ${fieldName} AS data, time
|
||||
FROM ${dataBaseName}.${tableName}
|
||||
@ -46,14 +60,14 @@
|
||||
</select>
|
||||
|
||||
<!-- 获取实时数据 -->
|
||||
<select id="getRealtimeData" resultType="java.util.Map"
|
||||
<select id="selectRealtimeDataList" resultType="java.util.Map"
|
||||
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO">
|
||||
SELECT ${fieldName}, time
|
||||
FROM ${dataBaseName}.${tableName}
|
||||
</select>
|
||||
|
||||
<!-- 获取聚合数据 -->
|
||||
<select id="getAggregateData" resultType="java.util.Map"
|
||||
<select id="selectAggregateDataList" resultType="java.util.Map"
|
||||
parameterType="cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO">
|
||||
SELECT ${aggregate}(${fieldName})
|
||||
FROM ${dataBaseName}.${tableName}
|
||||
@ -62,7 +76,7 @@
|
||||
</select>
|
||||
|
||||
<!-- 获取历史数据条数 -->
|
||||
<select id="getHistoryCount" resultType="java.lang.Long">
|
||||
<select id="selectHistoryCount" resultType="java.lang.Long">
|
||||
SELECT COUNT(time)
|
||||
FROM ${dataBaseName}.${tableName}
|
||||
WHERE time BETWEEN #{startTime} AND #{endTime}
|
@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper">
|
||||
|
||||
<!-- 插入数据 -->
|
||||
<insert id="insertData">
|
||||
INSERT INTO ${dataBaseName}.${tableName}
|
||||
<foreach item="item" collection="columns" separator=","
|
||||
open="(" close=")">
|
||||
${item.fieldName}
|
||||
</foreach>
|
||||
VALUES
|
||||
<foreach item="item" collection="columns" separator=","
|
||||
open="(" close=")">
|
||||
#{item.fieldValue}
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper">
|
||||
|
||||
<!-- 创建数据库 -->
|
||||
<update id="createDatabase" parameterType="String">
|
||||
CREATE DATABASE IF NOT EXISTS ${dataBaseName}
|
||||
</update>
|
||||
|
||||
</mapper>
|
@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper">
|
||||
|
||||
<!-- 创建子表 -->
|
||||
<update id="createTable">
|
||||
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
|
||||
USING ${dataBaseName}.${superTableName}
|
||||
TAGS
|
||||
<foreach item="item" collection="tags" separator=","
|
||||
open="(" close=")">
|
||||
#{item.fieldValue}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<!-- 创建子表,带有 TAGS -->
|
||||
<update id="createTableWithTags">
|
||||
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
|
||||
USING ${dataBaseName}.${superTableName}
|
||||
<foreach item="item" collection="tags" separator="," open="(" close=")">
|
||||
#{item.fieldName}
|
||||
</foreach>
|
||||
TAGS
|
||||
<foreach item="item" collection="tags" separator=","
|
||||
open="(" close=")">
|
||||
#{item.fieldValue}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
</mapper>
|
Loading…
x
Reference in New Issue
Block a user