Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into feature/iot

# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java
This commit is contained in:
YunaiV 2024-12-19 21:07:25 +08:00
commit 92de5b1f09
10 changed files with 308 additions and 9 deletions

View File

@ -35,4 +35,11 @@ public interface IotConstants {
*/ */
String DEVICE_STABLE_NAME_FORMAT = "device_%s"; String DEVICE_STABLE_NAME_FORMAT = "device_%s";
/**
* 获取物模型消息记录设备名
* <p>
* 格式为 thing_model_message_{productKey}_{deviceName}
*/
String THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT = "thing_model_message_%s_%s";
} }

View File

@ -1,10 +1,13 @@
package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType;
import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty;
import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data; import lombok.Data;
@Data @Data
public class ThingModelArgument { @JsonIgnoreProperties(ignoreUnknown = true)
public class ThinkModelArgument {
public static final String DIRECTION_INPUT = "input"; public static final String DIRECTION_INPUT = "input";
public static final String DIRECTION_OUTPUT = "output"; public static final String DIRECTION_OUTPUT = "output";

View File

@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.xmlbeans.impl.xb.xsdschema.Public;
/**
* TD 物模型消息日志的数据库
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ThinkModelMessageDO {
/**
* 数据库名称
*/
private String dataBaseName;
// TODO @haohaosuperTableName tableName 是不是合并因为每个 mapper 操作的时候有且只会使用到其中一个
/**
* 超级表名称
*/
private String superTableName;
/**
* 表名称
*/
private String tableName;
/**
* 消息ID
*/
private String id;
/**
* 扩展功能的参数
*/
private Object sys;
/**
* 请求方法 例如thing.event.property.post
*/
private String method;
/**
* 请求参数
*/
private Object params;
/**
* 属性上报时间戳
*/
private Long time;
/**
* 设备 key
*/
private String deviceKey;
}

View File

@ -0,0 +1,29 @@
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 cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessageDO;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.apache.ibatis.annotations.Mapper;
/**
* 处理 TD 中物模型消息日志的操作
*/
@Mapper
@DS("tdengine")
public interface TdThinkModelMessageMapper {
/**
* 创建物模型消息日志超级表超级表
*
*/
@TenantIgnore
void createSuperTable(ThinkModelMessageDO superTable);
/**
* 创建子表
*
*/
@TenantIgnore
void createTableWithTag(ThinkModelMessageDO table);
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.iot.mq.producer.simulatesend;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* SimulateSend 模拟设备上报的 Producer
*
* @author alwayssuper
* @since 2024/12/17 16:35
*/
@Slf4j
@Component
public class SimulateSendProducer {
@Resource
private ApplicationContext applicationContext;
}

View File

@ -7,7 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; 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.enums.product.IotProductStatusEnum;
import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThinkModelMessageService;
import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService;
import com.baomidou.dynamic.datasource.annotation.DSTransactional; import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -33,8 +34,11 @@ public class IotProductServiceImpl implements IotProductService {
private IotProductMapper productMapper; private IotProductMapper productMapper;
@Resource @Resource
@Lazy @Lazy // 延迟加载解决循环依赖
private IotProductThingModelService thingModelService; private IotProductThinkModelService thinkModelFunctionService;
@Resource
@Lazy // 延迟加载解决循环依赖
private IotThinkModelMessageService thinkModelMessageService;
@Override @Override
public Long createProduct(IotProductSaveReqVO createReqVO) { public Long createProduct(IotProductSaveReqVO createReqVO) {
@ -114,8 +118,10 @@ public class IotProductServiceImpl implements IotProductService {
IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build();
// 3. 产品是发布状态 // 3. 产品是发布状态
if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) {
// 3.1 创建超级表数据模型 // 3.1 创建产品超级表数据模型
thingModelService.createSuperTableDataModel(id); thinkModelFunctionService.createSuperTableDataModel(id);
// 3.2 创建物模型日志超级表数据模型
thinkModelMessageService.createSuperTable(id);
} }
productMapper.updateById(updateObj); productMapper.updateById(updateObj);
} }

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.iot.service.tdengine; package cn.iocoder.yudao.module.iot.service.tdengine;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage;
/** /**
* 物模型消息 Service * 物模型消息 Service
@ -14,5 +14,13 @@ public interface IotThingModelMessageService {
* @param device 设备 * @param device 设备
* @param thingModelMessage 物模型消息 * @param thingModelMessage 物模型消息
*/ */
void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage);
/**
* 创建物模型消息日志超级表
*
* @param productId 产品编号
*/
void createSuperTable(Long productId);
} }

View File

@ -12,14 +12,21 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO;
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.thinkmodel.IotProductThinkModelDO;
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
import cn.iocoder.yudao.module.iot.dal.tdengine.TdThinkModelMessageMapper;
import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.IotConstants;
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService;
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService;
import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -56,19 +63,29 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
private TdEngineDDLMapper tdEngineDDLMapper; private TdEngineDDLMapper tdEngineDDLMapper;
@Resource @Resource
private TdEngineDMLMapper tdEngineDMLMapper; private TdEngineDMLMapper tdEngineDMLMapper;
@Resource
private IotProductService productService;
@Resource @Resource
private DeviceDataRedisDAO deviceDataRedisDAO; private DeviceDataRedisDAO deviceDataRedisDAO;
@Resource
private IotTdDatabaseUtils iotTdDatabaseUtils;
@Resource
private TdThinkModelMessageMapper tdThinkModelMessageMapper;
// TODO @haohao这个方法可以考虑加下 1. 2. 3. 更有层次感 // TODO @haohao这个方法可以考虑加下 1. 2. 3. 更有层次感
@Override @Override
@TenantIgnore @TenantIgnore
public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) {
// 1. 判断设备状态如果为未激活状态创建数据表并更新设备状态 // 1. 判断设备状态如果为未激活状态创建数据表并更新设备状态
if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) {
// 1.1 创建设备表
createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO()
.setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus()));
// 1.2 创建物模型日志设备表
createThinkModelMessageDeviceTable(device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
} }
// 2. 获取设备属性并进行物模型校验过滤非物模型属性 // 2. 获取设备属性并进行物模型校验过滤非物模型属性
@ -95,6 +112,28 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
private List<IotProductThingModelDO> getValidThingModelList(String productKey) { private List<IotProductThingModelDO> getValidThingModelList(String productKey) {
return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey),
thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));
@Override
@TenantIgnore
public void createSuperTable(Long productId) {
// 1. 查询产品
IotProductDO product = productService.getProduct(productId);
// 2. 获取超级表的名称和数据库名称
String databaseName = iotTdDatabaseUtils.getDatabaseName();
String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(product.getProductKey());
// 3. 创建超级表
tdThinkModelMessageMapper.createSuperTable(ThinkModelMessageDO.builder().build()
.setDataBaseName(databaseName)
.setSuperTableName(superTableName));
}
private List<IotProductThinkModelDO> getValidFunctionList(String productKey) {
return iotProductThinkModelService
.getProductThinkModelListByProductKey(productKey)
.stream()
.filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType().equals(function.getType()))
.toList();
} }
private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotProductThingModelDO> thingModelList, IotDeviceDO device, Long time) { private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotProductThingModelDO> thingModelList, IotDeviceDO device, Long time) {
@ -186,6 +225,30 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
.setTags(tagsFieldValues)); .setTags(tagsFieldValues));
} }
/**
* 创建物模型日志设备数据表
*
* @param productKey 产品 Key
* @param deviceName 设备名称
* @param deviceKey 设备 Key
*
*/
private void createThinkModelMessageDeviceTable(String productKey, String deviceName, String deviceKey){
// 1. 获取超级表的名称数据库名称设备日志表名称
String databaseName = iotTdDatabaseUtils.getDatabaseName();
String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(productKey);
String thinkModelMessageDeviceTableName = IotTdDatabaseUtils.getThinkModelMessageDeviceTableName(productKey, deviceName);
// 2. 创建物模型日志设备数据表
tdThinkModelMessageMapper.createTableWithTag(ThinkModelMessageDO.builder().build()
.setDataBaseName(databaseName)
.setSuperTableName(superTableName)
.setTableName(thinkModelMessageDeviceTableName)
.setDeviceKey(deviceKey));
}
/** /**
* 获取数据库名称 * 获取数据库名称
* *

View File

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.iot.util;
import cn.iocoder.yudao.module.iot.enums.IotConstants;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* TD数据库工具类
*
* @author AlwaysSuper
*/
@Component
public class IotTdDatabaseUtils {
@Value("${spring.datasource.dynamic.datasource.tdengine.url}")
private String url;
/**
* 获取数据库名称
*/
public String getDatabaseName() {
int index = url.lastIndexOf("/");
return index != -1 ? url.substring(index + 1) : url;
}
/**
* 获取产品超级表表名
*
* @param deviceType 设备类型
* @param productKey 产品 Key
* @return 产品超级表表名
*/
public static String getProductSuperTableName(Integer deviceType, String productKey) {
return switch (deviceType) {
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();
};
}
/**
* 获取物模型日志超级表表名
*
* @param productKey 产品 Key
* @return 物模型日志超级表表名
*
*/
public static String getThinkModelMessageSuperTableName(String productKey) {
return String.format("thing_model_message_", productKey).toLowerCase();
}
/**
* 获取物模型日志设备表名
*
* @param productKey 产品 Key
* @param deviceName 设备名称
* @return 物模型日志设备表名
*/
public static String getThinkModelMessageDeviceTableName(String productKey, String deviceName) {
return String.format(IotConstants.THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase());
}
}

View File

@ -0,0 +1,34 @@
<?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.TdThinkModelMessageMapper">
<!-- 创建物模型消息日志超级表 -->
<update id="createSuperTable">
CREATE STABLE ${dataBaseName}.${superTableName}(
ts TIMESTAMP,
id VARCHAR(255),
sys VARCHAR(2048),
method VARCHAR(255),
params VARCHAR(2048)
)TAGS (
deviceKey VARCHAR(255)
)
</update>
<!-- 创建物模型消息日志子表带有deviceKey的TAG -->
<update id="createTableWithTag">
CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
USING ${dataBaseName}.${superTableName}(
ts,
id ,
sys ,
method ,
params
)TAGS(
#{deviceKey}
)
</update>
</mapper>