【代码评审】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
private IotDeviceDataService deviceDataService;
// TODO @haohao是不是叫 get-latest 就好了。因为 data 已经在 url 里了哈
@GetMapping("/latest-data")
@Operation(summary = "获取设备属性最新数据")
public CommonResult<List<IotDeviceDataRespVO>> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
@@ -38,6 +39,7 @@ public class IotDeviceDataController {
return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class));
}
// TODO @haohao是不是叫 /history-data => page
@GetMapping("/history-data")
@Operation(summary = "获取设备属性历史数据")
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;
// TODO @haohaoIotDeviceDataPageReqVO
@Schema(description = "管理后台 - IoT 设备数据 Request VO")
@Data
public class IotDeviceDataReqVO extends PageParam {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -43,7 +43,7 @@ public interface IotThinkModelFunctionMapper extends BaseMapperX<IotThinkModelFu
default List<IotThinkModelFunctionDO> selectListByProductIdAndIdentifiersAndTypes(Long productId,
List<String> identifiers,
List<Integer> types){
List<Integer> types) {
return selectList(new LambdaQueryWrapperX<IotThinkModelFunctionDO>()
.eq(IotThinkModelFunctionDO::getProductId, productId)
.in(IotThinkModelFunctionDO::getIdentifier, identifiers)
@@ -55,7 +55,8 @@ public interface IotThinkModelFunctionMapper extends BaseMapperX<IotThinkModelFu
IotThinkModelFunctionDO::getName, name);
}
default List<IotThinkModelFunctionDO> selectListByProductKey(String productKey){
default List<IotThinkModelFunctionDO> selectListByProductKey(String 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.Param;
// TODO @haohaoInterceptorIgnore 忽略租户,可以在每个方法上,添加 @TenantIgnore 哈。
/**
* TD 引擎的数据库 Mapper
*/

View File

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

View File

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

View File

@@ -213,31 +213,30 @@ public class IotDeviceServiceImpl implements IotDeviceService {
@Override
public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
// 校验存在
// 1. 校验存在
// TODO @haohao这里的 iotDeviceDO => device。一个是去掉 iot一个是去掉 DO 后缀。这样,简洁一点。
IotDeviceDO iotDeviceDO = 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());
}
// 设置状态更新时间
// 2.3 设置状态更新时间
updateObj.setStatusLastUpdateTime(LocalDateTime.now());
// 2.4 更新到数据库
deviceMapper.updateById(updateObj);
}

View File

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

View File

@@ -2,7 +2,6 @@
<!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">
<!-- 插入数据 -->

View File

@@ -2,7 +2,6 @@
<!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">
<!-- 创建数据库 -->

View File

@@ -2,7 +2,6 @@
<!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">
<!-- 根据时间戳查询数据 -->

View File

@@ -2,7 +2,6 @@
<!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">
<!-- 创建超级表 -->

View File

@@ -2,7 +2,6 @@
<!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">
<!-- 创建子表 -->