【代码评审】IoT:tdengine 封装的 review

This commit is contained in:
YunaiV 2024-11-09 13:39:51 +08:00
parent e3dcea9cb3
commit 9b30d5d355
21 changed files with 43 additions and 19 deletions

View File

@ -31,6 +31,7 @@ public class IotDeviceDataController {
@Resource @Resource
private IotDeviceDataService deviceDataService; private IotDeviceDataService deviceDataService;
// TODO @haohao是不是叫 get-latest 就好了因为 data 已经在 url 里了哈
@GetMapping("/latest-data") @GetMapping("/latest-data")
@Operation(summary = "获取设备属性最新数据") @Operation(summary = "获取设备属性最新数据")
public CommonResult<List<IotDeviceDataRespVO>> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) { public CommonResult<List<IotDeviceDataRespVO>> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
@ -38,6 +39,7 @@ public class IotDeviceDataController {
return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class));
} }
// TODO @haohao是不是叫 /history-data => page
@GetMapping("/history-data") @GetMapping("/history-data")
@Operation(summary = "获取设备属性历史数据") @Operation(summary = "获取设备属性历史数据")
public CommonResult<PageResult<IotTimeDataRespVO>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) { public CommonResult<PageResult<IotTimeDataRespVO>> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) {

View File

@ -10,6 +10,7 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @haohaoIotDeviceDataPageReqVO
@Schema(description = "管理后台 - IoT 设备数据 Request VO") @Schema(description = "管理后台 - IoT 设备数据 Request VO")
@Data @Data
public class IotDeviceDataReqVO extends PageParam { public class IotDeviceDataReqVO extends PageParam {

View File

@ -23,6 +23,7 @@ import java.time.LocalDateTime;
@AllArgsConstructor @AllArgsConstructor
public class IotDeviceDataDO { public class IotDeviceDataDO {
// TODO @haohao每个字段的关联关系可以 @ 下哈
/** /**
* 设备编号 * 设备编号
*/ */

View File

@ -3,12 +3,15 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import lombok.Data; import lombok.Data;
import java.util.Set; import java.util.Set;
// TODO @haohao类似这个其实可以参考 mybatis plusquerywrapper搞个 TdEngineQueryWrapper这样看起来会更好懂
/** /**
* 查询DO * 查询DO
*/ */
@Data @Data
public class SelectDO { public class SelectDO {
// TODO @haohadatabase 是个单词
/** /**
* 数据库名称 * 数据库名称
*/ */
@ -39,6 +42,7 @@ public class SelectDO {
*/ */
private String type; private String type;
// TODO @haohao这个字段是啥哈
/** /**
* 查询条件 * 查询条件
*/ */

View File

@ -4,6 +4,7 @@ import lombok.Data;
import java.util.Map; import java.util.Map;
// TODO @haohao类似 SelectDO 的想法只是它是返回ps貌似可以在 tdengine 里面创建一个 query 放这种比较特殊的查询和结果对象dataobject 更多还是实际存储的结构化的 do
@Data @Data
public class SelectVisualDO { public class SelectVisualDO {

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
import java.util.List; import java.util.List;
// TODO @haohao这个还有用哇
/** /**
* TableManager 类用于管理 TDengine 表的创建删除和结构信息获取 * TableManager 类用于管理 TDengine 表的创建删除和结构信息获取
*/ */

View File

@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
// TODO @haohao有部分非实体的部分是不是可以搞到 iot framework 包下搞个 tdengine 作为框架级的封装放在 dataobject感觉不是很合理哈可以微信讨论下
/** /**
* TdRestApi 类用于处理 TDengine REST API 请求 * TdRestApi 类用于处理 TDengine REST API 请求
*/ */

View File

@ -21,6 +21,7 @@ public class TdTableDO {
*/ */
private String dataBaseName; private String dataBaseName;
// TODO @haohaosuperTableName tableName 是不是合并因为每个 mapper 操作的时候有且只会使用到其中一个
/** /**
* 超级表名称 * 超级表名称
*/ */

View File

@ -15,6 +15,7 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> { public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
// TODO @haohao可能多余的查询条件要去掉哈
default PageResult<IotDeviceDO> selectPage(IotDevicePageReqVO reqVO) { default PageResult<IotDeviceDO> selectPage(IotDevicePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>() return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>()
.eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey()) .eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey())

View File

@ -43,7 +43,7 @@ public interface IotThinkModelFunctionMapper extends BaseMapperX<IotThinkModelFu
default List<IotThinkModelFunctionDO> selectListByProductIdAndIdentifiersAndTypes(Long productId, default List<IotThinkModelFunctionDO> selectListByProductIdAndIdentifiersAndTypes(Long productId,
List<String> identifiers, List<String> identifiers,
List<Integer> types){ List<Integer> types) {
return selectList(new LambdaQueryWrapperX<IotThinkModelFunctionDO>() return selectList(new LambdaQueryWrapperX<IotThinkModelFunctionDO>()
.eq(IotThinkModelFunctionDO::getProductId, productId) .eq(IotThinkModelFunctionDO::getProductId, productId)
.in(IotThinkModelFunctionDO::getIdentifier, identifiers) .in(IotThinkModelFunctionDO::getIdentifier, identifiers)
@ -55,7 +55,8 @@ public interface IotThinkModelFunctionMapper extends BaseMapperX<IotThinkModelFu
IotThinkModelFunctionDO::getName, name); IotThinkModelFunctionDO::getName, name);
} }
default List<IotThinkModelFunctionDO> selectListByProductKey(String productKey){ default List<IotThinkModelFunctionDO> selectListByProductKey(String productKey) {
return selectList(IotThinkModelFunctionDO::getProductKey, productKey); return selectList(IotThinkModelFunctionDO::getProductKey, productKey);
} }
} }

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
// TODO @haohaoInterceptorIgnore 忽略租户可以在每个方法上添加 @TenantIgnore
/** /**
* TD 引擎的数据库 Mapper * TD 引擎的数据库 Mapper
*/ */

View File

@ -25,6 +25,7 @@ public interface TdEngineQueryMapper {
*/ */
List<Map<String, Object>> selectByTimestamp(SelectDO selectDO); List<Map<String, Object>> selectByTimestamp(SelectDO selectDO);
// TODO @haohao最好方法的命名和数据库操作的保持一直get => select然后 selectList or selectOne
/** /**
* 根据时间戳获取数据条数 * 根据时间戳获取数据条数
* *

View File

@ -31,6 +31,7 @@ public class EmqxServiceImpl implements EmqxService {
// 根据不同的主题处理不同的业务逻辑 // 根据不同的主题处理不同的业务逻辑
if (topic.contains("/property/post")) { if (topic.contains("/property/post")) {
// 设备上报数据 topic /sys/f13f57c63e9/dianbiao1/thing/event/property/post // 设备上报数据 topic /sys/f13f57c63e9/dianbiao1/thing/event/property/post
// TODO @hao这块未来可能搞个 IotTopicUrls 之类把拼接和解析的逻辑收敛
String productKey = topic.split("/")[2]; String productKey = topic.split("/")[2];
String deviceName = topic.split("/")[3]; String deviceName = topic.split("/")[3];
String message = new String(mqttMessage.getPayload()); String message = new String(mqttMessage.getPayload());
@ -48,4 +49,5 @@ public class EmqxServiceImpl implements EmqxService {
log.error("订阅默认主题失败", e); log.error("订阅默认主题失败", e);
} }
} }
} }

View File

@ -133,4 +133,5 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
private static String getDeviceTableName(String productKey, String deviceName) { private static String getDeviceTableName(String productKey, String deviceName) {
return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase());
} }
} }

View File

@ -213,31 +213,30 @@ public class IotDeviceServiceImpl implements IotDeviceService {
@Override @Override
public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
// 校验存在 // 1. 校验存在
// TODO @haohao这里的 iotDeviceDO => device一个是去掉 iot一个是去掉 DO 后缀这样简洁一点
IotDeviceDO iotDeviceDO = validateDeviceExists(updateReqVO.getId()); IotDeviceDO iotDeviceDO = validateDeviceExists(updateReqVO.getId());
// 更新状态和更新时间 // 2.1 更新状态和更新时间
IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); 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()) if (Objects.equals(iotDeviceDO.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
&& Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { && Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
updateObj.setActiveTime(LocalDateTime.now()); updateObj.setActiveTime(LocalDateTime.now());
} }
// 2.2.2 如果是上线设置上线时间
// 如果是上线设置上线时间
if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
updateObj.setLastOnlineTime(LocalDateTime.now()); updateObj.setLastOnlineTime(LocalDateTime.now());
} }
// 2.2.3 如果是离线设置离线时间
// 如果是离线设置离线时间
if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) { if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) {
updateObj.setLastOfflineTime(LocalDateTime.now()); updateObj.setLastOfflineTime(LocalDateTime.now());
} }
// 2.3 设置状态更新时间
// 设置状态更新时间
updateObj.setStatusLastUpdateTime(LocalDateTime.now()); updateObj.setStatusLastUpdateTime(LocalDateTime.now());
// 2.4 更新到数据库
deviceMapper.updateById(updateObj); deviceMapper.updateById(updateObj);
} }

View File

@ -47,6 +47,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
@Resource @Resource
private DeviceDataRedisDAO deviceDataRedisDAO; private DeviceDataRedisDAO deviceDataRedisDAO;
// TODO @haohao这个方法可以考虑加下 1. 2. 3. 更有层次感
@Override @Override
@TenantIgnore @TenantIgnore
public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) {
@ -55,12 +56,14 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
// 创建设备数据表 // 创建设备数据表
createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
// 更新设备状态 // 更新设备状态
// TODO @haohao下面可以考虑链式调用iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO().setid().setstatus())
IotDeviceStatusUpdateReqVO updateReqVO = new IotDeviceStatusUpdateReqVO(); IotDeviceStatusUpdateReqVO updateReqVO = new IotDeviceStatusUpdateReqVO();
updateReqVO.setId(device.getId()); updateReqVO.setId(device.getId());
updateReqVO.setStatus(IotDeviceStatusEnum.ONLINE.getStatus()); updateReqVO.setStatus(IotDeviceStatusEnum.ONLINE.getStatus());
iotDeviceService.updateDeviceStatus(updateReqVO); iotDeviceService.updateDeviceStatus(updateReqVO);
} }
// TODO @haohao这个变量可以和 过滤并收集有效的属性字段 那块因为关联度高一点
// 获取设备属性 // 获取设备属性
Map<String, Object> params = thingModelMessage.dataToMap(); Map<String, Object> params = thingModelMessage.dataToMap();
@ -70,12 +73,12 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
.stream() .stream()
.filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType())) .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType()))
.toList(); .toList();
if (functionList.isEmpty()) { if (functionList.isEmpty()) {
return; return;
} }
// 获取属性标识符集合 // 获取属性标识符集合
// TODO @haohao这个变量可以和 过滤并收集有效的属性字段 那块因为关联度高一点另外可以使用 CollectionUtilsconvertSet
Set<String> propertyIdentifiers = functionList.stream() Set<String> propertyIdentifiers = functionList.stream()
.map(IotThinkModelFunctionDO::getIdentifier) .map(IotThinkModelFunctionDO::getIdentifier)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -90,9 +93,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
if (propertyIdentifiers.contains(key)) { if (propertyIdentifiers.contains(key)) {
schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val));
// 缓存设备属性 // 缓存设备属性
// TODO @haohao这个缓存的写入可以使用的时候 cache 被动读
setDeviceDataCache(device, functionMap.get(key), val, thingModelMessage.getTime()); setDeviceDataCache(device, functionMap.get(key), val, thingModelMessage.getTime());
} }
}); });
// TODO @haohao疑问为什么 1 不继续哈
if (schemaFieldValues.size() == 1) { if (schemaFieldValues.size() == 1) {
return; return;
} }
@ -127,6 +132,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
deviceDataRedisDAO.set(deviceData); deviceDataRedisDAO.set(deviceData);
} }
// TODO @haohao实现没问题哈这个方法的空行有点多逻辑分块上没这么明显看看能不能改下
/** /**
* 创建设备数据表 * 创建设备数据表
* *
@ -143,6 +149,8 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
List<TdFieldDO> tagsFieldValues = new ArrayList<>(); List<TdFieldDO> tagsFieldValues = new ArrayList<>();
if (maps != null) { if (maps != null) {
// TODO @haohao一些字符串是不是可以枚举起来哈
// TODO @haohao这种过滤的常用的可以考虑用 CollectionUtils.filterList一些常用的 stream 操作适合封装哈
List<Map<String, Object>> taggedNotesList = maps.stream() List<Map<String, Object>> taggedNotesList = maps.stream()
.filter(map -> "TAG".equals(map.get("note"))) .filter(map -> "TAG".equals(map.get("note")))
.toList(); .toList();
@ -176,6 +184,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
* @return 数据库名称 * @return 数据库名称
*/ */
private String getDatabaseName() { private String getDatabaseName() {
// TODO @haohao可以使用 StrUtil.subAftetLast 这种方法
return url.substring(url.lastIndexOf("/") + 1); return url.substring(url.lastIndexOf("/") + 1);
} }
@ -187,6 +196,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
* @return 产品属性表名 * @return 产品属性表名
*/ */
private static String getProductPropertySTableName(Integer deviceType, String productKey) { private static String getProductPropertySTableName(Integer deviceType, String productKey) {
// TODO @haohao枚举下会好点哈
return switch (deviceType) { return switch (deviceType) {
case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase();
case 2 -> String.format("gateway_%s", productKey).toLowerCase(); case 2 -> String.format("gateway_%s", productKey).toLowerCase();
@ -204,4 +214,5 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
private static String getDeviceTableName(String productKey, String deviceName) { private static String getDeviceTableName(String productKey, String deviceName) {
return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase());
} }
} }

View File

@ -2,7 +2,6 @@
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper"> <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper">
<!-- 插入数据 --> <!-- 插入数据 -->

View File

@ -2,7 +2,6 @@
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper"> <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper">
<!-- 创建数据库 --> <!-- 创建数据库 -->

View File

@ -2,7 +2,6 @@
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "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.TdEngineQueryMapper">
<!-- 根据时间戳查询数据 --> <!-- 根据时间戳查询数据 -->

View File

@ -2,7 +2,6 @@
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "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.TdEngineSuperTableMapper">
<!-- 创建超级表 --> <!-- 创建超级表 -->

View File

@ -2,7 +2,6 @@
<!DOCTYPE mapper <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper"> <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper">
<!-- 创建子表 --> <!-- 创建子表 -->