【功能优化】IoT:
1. DeviceDataApi => IotDeviceUpstreamApi,并新建 upstream 包 2. ThingModelMessage => IotDeviceMessage 设备消息 3. 基于 spring event 异步消费 IotDeviceMessage,并实现 IotDeviceLogMessageConsumer 记录日志
This commit is contained in:
parent
f4ad3e9d2d
commit
8089f3a319
|
@ -13,7 +13,7 @@ import java.util.Set;
|
|||
/**
|
||||
* 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能
|
||||
*
|
||||
* @author
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class TenantDatabaseInterceptor implements TenantLineHandler {
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ public class TenantUtils {
|
|||
*
|
||||
* @param tenantId 租户编号
|
||||
* @param callable 逻辑
|
||||
* @return 结果
|
||||
*/
|
||||
public static <V> V execute(Long tenantId, Callable<V> callable) {
|
||||
Long oldTenantId = TenantContextHolder.getTenantId();
|
||||
|
@ -78,6 +79,25 @@ public class TenantUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略租户,执行对应的逻辑
|
||||
*
|
||||
* @param callable 逻辑
|
||||
* @return 结果
|
||||
*/
|
||||
public static <V> V executeIgnore(Callable<V> callable) {
|
||||
Boolean oldIgnore = TenantContextHolder.isIgnore();
|
||||
try {
|
||||
TenantContextHolder.setIgnore(true);
|
||||
// 执行逻辑
|
||||
return callable.call();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
TenantContextHolder.setIgnore(oldIgnore);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将多租户编号,添加到 header 中
|
||||
*
|
||||
|
|
|
@ -5,49 +5,44 @@ import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
|
|||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.enums.ApiConstants;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
// TODO 芋艿:名字可能看情况改下
|
||||
/**
|
||||
* 设备数据 API
|
||||
* 设备数据 Upstream 上行 API
|
||||
*
|
||||
* 目的:设备 -> 插件 -> 服务端
|
||||
*
|
||||
* @author haohao
|
||||
*/
|
||||
public interface DeviceDataApi {
|
||||
public interface IotDeviceUpstreamApi {
|
||||
|
||||
// TODO @芋艿:可能会调整
|
||||
String PREFIX = ApiConstants.PREFIX + "/device-data";
|
||||
String PREFIX = ApiConstants.PREFIX + "/device/upstream";
|
||||
|
||||
/**
|
||||
* 更新设备状态
|
||||
*
|
||||
* @param updateReqDTO 更新请求
|
||||
* @param updateReqDTO 更新设备状态 DTO
|
||||
*/
|
||||
@PutMapping(PREFIX + "/update-status")
|
||||
@PermitAll // TODO 芋艿:后续看看怎么优化下
|
||||
CommonResult<Boolean> updateDeviceStatus(@Valid @RequestBody IotDeviceStatusUpdateReqDTO updateReqDTO);
|
||||
|
||||
/**
|
||||
* 上报设备属性数据
|
||||
*
|
||||
* @param reportReqDTO 上报设备属性数据 DTO
|
||||
*/
|
||||
@PostMapping(PREFIX + "/report-property")
|
||||
CommonResult<Boolean> reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO);
|
||||
|
||||
/**
|
||||
* 上报设备事件数据
|
||||
*
|
||||
* @param reportReqDTO 设备事件
|
||||
*/
|
||||
@PostMapping(PREFIX + "/report-event")
|
||||
@PermitAll // TODO 芋艿:后续看看怎么优化下
|
||||
CommonResult<Boolean> reportDeviceEventData(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO);
|
||||
|
||||
/**
|
||||
* 上报设备属性数据
|
||||
*
|
||||
* @param reportReqDTO 设备数据
|
||||
*/
|
||||
@PostMapping(PREFIX + "/report-property")
|
||||
@PermitAll // TODO 芋艿:后续看看怎么优化下
|
||||
CommonResult<Boolean> reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO);
|
||||
|
||||
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
package cn.iocoder.yudao.module.iot.api.device.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -12,24 +11,9 @@ import java.util.Map;
|
|||
* IoT 设备【事件】数据上报 Request DTO
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class IotDeviceEventReportReqDTO {
|
||||
|
||||
// TODO 芋艿:要不要 id
|
||||
// TODO 芋艿:要不要 time
|
||||
|
||||
/**
|
||||
* 产品标识
|
||||
*/
|
||||
@NotEmpty(message = "产品标识不能为空")
|
||||
private String productKey;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
@NotEmpty(message = "设备名称不能为空")
|
||||
private String deviceName;
|
||||
public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||
|
||||
/**
|
||||
* 事件标识
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package cn.iocoder.yudao.module.iot.api.device.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -12,28 +11,14 @@ import java.util.Map;
|
|||
* IoT 设备【属性】数据上报 Request DTO
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class IotDevicePropertyReportReqDTO {
|
||||
public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||
|
||||
// TODO 芋艿:要不要 id
|
||||
// TODO 芋艿:要不要 time
|
||||
|
||||
/**
|
||||
* 产品标识
|
||||
*/
|
||||
@NotEmpty(message = "产品标识不能为空")
|
||||
private String productKey;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
@NotEmpty(message = "设备名称不能为空")
|
||||
private String deviceName;
|
||||
/**
|
||||
* 属性参数
|
||||
*/
|
||||
@NotEmpty(message = "属性参数不能为空")
|
||||
private Map<String, Object> params;
|
||||
private Map<String, Object> properties;
|
||||
|
||||
}
|
|
@ -3,33 +3,18 @@ package cn.iocoder.yudao.module.iot.api.device.dto;
|
|||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* IoT 设备状态更新 Request DTO
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class IotDeviceStatusUpdateReqDTO {
|
||||
public class IotDeviceStatusUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||
|
||||
// TODO 芋艿:要不要 id
|
||||
// TODO 芋艿:要不要 time
|
||||
|
||||
/**
|
||||
* 产品标识
|
||||
*/
|
||||
@NotEmpty(message = "产品标识不能为空")
|
||||
private String productKey;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
@NotEmpty(message = "设备名称不能为空")
|
||||
private String deviceName;
|
||||
/**
|
||||
* 设备状态
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package cn.iocoder.yudao.module.iot.api.device.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* IoT 设备上行的抽象 Request DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
public abstract class IotDeviceUpstreamAbstractReqDTO {
|
||||
|
||||
/**
|
||||
* 请求编号
|
||||
*/
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 插件标识
|
||||
*/
|
||||
private String pluginKey;
|
||||
|
||||
/**
|
||||
* 产品标识
|
||||
*/
|
||||
@NotEmpty(message = "产品标识不能为空")
|
||||
private String productKey;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
@NotEmpty(message = "设备名称不能为空")
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 上报时间
|
||||
*/
|
||||
private LocalDateTime reportTime;
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.iot.enums.device;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* IoT 设备消息标识符枚举
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum IotDeviceMessageIdentifierEnum {
|
||||
|
||||
PROPERTY_GET("get"),
|
||||
PROPERTY_SET("set"),
|
||||
PROPERTY_REPORT("report");
|
||||
|
||||
/**
|
||||
* 标志符
|
||||
*/
|
||||
private final String identifier;
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.iot.enums.device;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* IoT 设备消息类型枚举
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum IotDeviceMessageTypeEnum {
|
||||
|
||||
STATE("state"), // 设备状态
|
||||
PROPERTY("property"), // 设备属性
|
||||
EVENT("event"); // 设备事件
|
||||
|
||||
/**
|
||||
* 属性
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.upstream.IotDeviceUpstreamService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
@ -13,28 +13,30 @@ import javax.annotation.Resource;
|
|||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 设备数据 API 实现类
|
||||
* * 设备数据 Upstream 上行 API 实现类
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
public class DeviceDataApiImpl implements DeviceDataApi {
|
||||
public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
|
||||
|
||||
@Resource
|
||||
private IotDevicePropertyDataService deviceDataService;
|
||||
private IotDeviceUpstreamService deviceUpstreamService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
|
||||
deviceUpstreamService.updateDeviceStatus(updateReqDTO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
|
||||
deviceDataService.saveDeviceData(reportReqDTO);
|
||||
deviceUpstreamService.reportDevicePropertyData(reportReqDTO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
|
||||
deviceUpstreamService.reportDeviceEventData(reportReqDTO);
|
||||
return success(true);
|
||||
}
|
||||
|
|
@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
|
@ -27,13 +27,13 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
|||
public class IotDeviceDataController {
|
||||
|
||||
@Resource
|
||||
private IotDevicePropertyDataService deviceDataService;
|
||||
private IotDevicePropertyService deviceDataService;
|
||||
|
||||
@Resource
|
||||
private IotDeviceLogDataService iotDeviceLogDataService;
|
||||
private IotDeviceLogService iotDeviceLogDataService;
|
||||
|
||||
@Resource // TODO @super:service 之间,不用空行;原因是,这样更简洁;空行,主要是为了“间隔”,提升可读性
|
||||
private IotDeviceLogDataService deviceLogDataService;
|
||||
private IotDeviceLogService deviceLogDataService;
|
||||
|
||||
// TODO @浩浩:这里的 /latest-list,包括方法名。
|
||||
@GetMapping("/latest")
|
||||
|
|
|
@ -36,6 +36,8 @@ public class IotDeviceDO extends BaseDO {
|
|||
private Long id;
|
||||
/**
|
||||
* 设备唯一标识符,全局唯一,用于识别设备
|
||||
*
|
||||
* 类似阿里云 <a href="https://help.aliyun.com/zh/iot/developer-reference/api-querydeviceinfo">QueryDeviceInfo</a> 的 IotInstanceId
|
||||
*/
|
||||
private String deviceKey;
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.device;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
@ -19,37 +23,51 @@ import lombok.NoArgsConstructor;
|
|||
@AllArgsConstructor
|
||||
public class IotDeviceLogDO {
|
||||
|
||||
// TODO @芋艿:消息 ID 的生成逻辑
|
||||
/**
|
||||
* 消息 ID
|
||||
* 日志编号
|
||||
*
|
||||
* 通过 {@link IdUtil#fastSimpleUUID()} 生成
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 请求编号
|
||||
*
|
||||
* 对应 {@link IotDeviceMessage#getRequestId()} 字段
|
||||
*/
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 产品标识
|
||||
* <p>
|
||||
* 关联 {@link IotProductDO#getProductKey()}
|
||||
*/
|
||||
private String productKey;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*
|
||||
* 关联 {@link IotDeviceDO#getDeviceName()}
|
||||
*/
|
||||
private String deviceName;
|
||||
/**
|
||||
* 设备标识
|
||||
* <p>
|
||||
* 关联 {@link IotDeviceDO#getDeviceKey()}}
|
||||
*/
|
||||
private String deviceKey;
|
||||
private String deviceKey; // 非存储字段,用于 TDengine 的 TAG
|
||||
|
||||
// TODO @super:枚举类
|
||||
/**
|
||||
* 日志类型
|
||||
*
|
||||
* 枚举 {@link IotDeviceMessageTypeEnum}
|
||||
*/
|
||||
private String type;
|
||||
|
||||
// TODO @super:枚举类
|
||||
/**
|
||||
* 标识符:用于标识具体的属性、事件或服务
|
||||
* 标识符
|
||||
*
|
||||
* 枚举 {@link IotDeviceMessageIdentifierEnum}
|
||||
*/
|
||||
private String subType;
|
||||
private String identifier;
|
||||
|
||||
/**
|
||||
* 数据内容
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 物模型消息
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ThingModelMessage {
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 扩展功能的参数
|
||||
*/
|
||||
private Object sys;
|
||||
|
||||
/**
|
||||
* 请求方法 例如:thing.event.property.post
|
||||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
private Object params;
|
||||
|
||||
/**
|
||||
* 属性上报时间戳
|
||||
*/
|
||||
private Long time;
|
||||
|
||||
/**
|
||||
* 设备信息
|
||||
*/
|
||||
private String productKey;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 设备 key
|
||||
*/
|
||||
private String deviceKey;
|
||||
|
||||
/**
|
||||
* 转换为 Map 类型
|
||||
*/
|
||||
public Map<String, Object> dataToMap() {
|
||||
Map<String, Object> mapData = new HashMap<>();
|
||||
if (params instanceof Map) {
|
||||
((Map<?, ?>) params).forEach((key, value) -> mapData.put(key.toString(), value));
|
||||
}
|
||||
return mapData;
|
||||
}
|
||||
}
|
|
@ -22,21 +22,13 @@ public interface IotDeviceLogDataMapper {
|
|||
*/
|
||||
void createDeviceLogSTable();
|
||||
|
||||
// TODO @super:单个参数,不用加 @Param
|
||||
// TODO @芋艿:在瞅瞅
|
||||
//讨论:艿菇这里有些特殊情况,我也学习了一下这块知识:
|
||||
// 如果使用的是Java 8及以上版本,并且编译器保留了参数名(通过编译器选项-parameters启用),则可以去掉@Param注解。MyBatis会自动使用参数的实际名称
|
||||
// 但在TDengine中 @Param去掉后TDengine会报错,以下是大模型的回答:
|
||||
// 不用加 @Param在普通的 MySQL 场景下是正确的 - 对于 MyBatis,当方法只有一个参数时,确实可以不用添加 @Param 注解。
|
||||
//但是在 TDengine 的场景下,情况不同:
|
||||
//TDengine 的特殊性:
|
||||
//TDengine 使用特殊的 SQL 语法
|
||||
//需要处理超级表(STable)和子表的概念
|
||||
//参数绑定的方式与普通 MySQL 不同
|
||||
//为什么这里必须要 @Param:
|
||||
//XML 中使用了 ${log.deviceKey} 这样的参数引用方式
|
||||
//需要在 SQL 中动态构建表名(device_log_${log.deviceKey})
|
||||
//没有 @Param("log") 的话,MyBatis 无法正确解析参数
|
||||
/**
|
||||
* 查询设备日志表是否存在
|
||||
*
|
||||
* @return 存在则返回表名;不存在则返回 null
|
||||
*/
|
||||
String showDeviceLogSTable();
|
||||
|
||||
/**
|
||||
* 插入设备日志数据
|
||||
*
|
||||
|
@ -44,7 +36,7 @@ public interface IotDeviceLogDataMapper {
|
|||
*
|
||||
* @param log 设备日志数据
|
||||
*/
|
||||
void insert(@Param("log") IotDeviceLogDO log);
|
||||
void insert(IotDeviceLogDO log);
|
||||
|
||||
/**
|
||||
* 获得设备日志分页
|
||||
|
@ -62,12 +54,4 @@ public interface IotDeviceLogDataMapper {
|
|||
*/
|
||||
Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO);
|
||||
|
||||
// TODO @芋艿:这个方法名,后续看看叫啥好
|
||||
/**
|
||||
* 查询设备日志表是否存在
|
||||
*
|
||||
* @return 不存在返回 null
|
||||
*/
|
||||
Object checkDeviceLogSTableExists();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package cn.iocoder.yudao.module.iot.emq.service;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
|
@ -20,7 +20,7 @@ import org.springframework.scheduling.annotation.Async;
|
|||
public class EmqxServiceImpl implements EmqxService {
|
||||
|
||||
@Resource
|
||||
private IotDevicePropertyDataService iotDeviceDataService;
|
||||
private IotDevicePropertyService iotDeviceDataService;
|
||||
|
||||
// TODO 多线程处理消息
|
||||
@Override
|
||||
|
@ -35,8 +35,8 @@ public class EmqxServiceImpl implements EmqxService {
|
|||
String deviceName = topic.split("/")[3];
|
||||
String message = new String(mqttMessage.getPayload());
|
||||
IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder()
|
||||
.productKey(productKey)
|
||||
.deviceName(deviceName)
|
||||
// .productKey(productKey)
|
||||
// .deviceName(deviceName)
|
||||
// .properties(message) // TODO 芋艿:临时去掉,看看
|
||||
.build();
|
||||
iotDeviceDataService.saveDeviceData(createDTO);
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.springframework.stereotype.Component;
|
|||
import java.sql.Timestamp;
|
||||
import java.util.Map;
|
||||
|
||||
// TODO @haohao:这个还需要的么?
|
||||
/**
|
||||
* TaosAspect 是一个处理 Taos 数据库返回值的切面。
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package cn.iocoder.yudao.module.iot.framework.security.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
|
||||
import cn.iocoder.yudao.module.iot.enums.ApiConstants;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||
|
||||
/**
|
||||
* IoT 模块的 Security 配置
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false, value = "iotSecurityConfiguration")
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Bean("iotAuthorizeRequestsCustomizer")
|
||||
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
|
||||
return new AuthorizeRequestsCustomizer() {
|
||||
|
||||
@Override
|
||||
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
|
||||
// RPC 服务的安全配置
|
||||
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.iot.framework.security.core;
|
|
@ -1,6 +1,6 @@
|
|||
package cn.iocoder.yudao.module.iot.framework.tdengine.config;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
|
@ -17,7 +17,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
@RequiredArgsConstructor
|
||||
public class TDengineTableInitConfiguration implements ApplicationRunner {
|
||||
|
||||
private final IotDeviceLogDataService deviceLogService;
|
||||
private final IotDeviceLogService deviceLogService;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.springframework.stereotype.Component;
|
|||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
// TODO 芋艿:后续再看看
|
||||
/**
|
||||
* 插件实例 Job
|
||||
*
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package cn.iocoder.yudao.module.iot.mq.consumer.device;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 针对 {@link IotDeviceMessage} 的消费者,记录设备日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class IotDeviceLogMessageConsumer {
|
||||
|
||||
@Resource
|
||||
private IotDeviceLogService deviceLogService;
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
public void onMessage(IotDeviceMessage message) {
|
||||
log.info("[onMessage][消息内容({})]", message);
|
||||
deviceLogService.createDeviceLog(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cn.iocoder.yudao.module.iot.mq.consumer.device;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 针对 {@link IotDeviceMessage} 的消费者,记录设备属性
|
||||
*
|
||||
* @author alwayssuper
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class IotDevicePropertyMessageConsumer {
|
||||
|
||||
@Resource
|
||||
private IotDevicePropertyService deviceDataService;
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
public void onMessage(IotDeviceMessage message) {
|
||||
log.info("[onMessage][消息内容({})]", message);
|
||||
|
||||
// 设备日志记录
|
||||
// TODO @芋艿:重新写下
|
||||
// deviceLogDataService.createDeviceLog(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package cn.iocoder.yudao.module.iot.mq.consumer.deviceconsumer;
|
||||
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 针对 {@link ThingModelMessage} 的消费者
|
||||
*
|
||||
* @author alwayssuper
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DeviceConsumer {
|
||||
|
||||
@Resource
|
||||
private IotDeviceLogDataService deviceLogDataService;
|
||||
@Resource
|
||||
private IotDevicePropertyDataService deviceDataService;
|
||||
|
||||
// TODO @芋艿:这块先用ThingModelMessage,后续看看用啥替代
|
||||
@EventListener
|
||||
@Async
|
||||
public void onMessage(ThingModelMessage message) {
|
||||
log.info("[onMessage][消息内容({})]", message);
|
||||
//TODO:数据插入这块整体写的比较混乱,整体借鉴了浩浩哥之前写的逻辑,目前是通过模拟设备科插入数据了,但之前的逻辑有大量弃用的部分,后续看看怎么完善
|
||||
|
||||
// 设备数据记录
|
||||
deviceDataService.saveDeviceDataTest(message);
|
||||
// 设备日志记录
|
||||
deviceLogDataService.saveDeviceLog(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* TODO 芋艿:未来实现一个 IotRuleMessageConsumer
|
||||
*/
|
||||
package cn.iocoder.yudao.module.iot.mq.consumer.rule;
|
|
@ -0,0 +1,66 @@
|
|||
package cn.iocoder.yudao.module.iot.mq.message;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 设备消息
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class IotDeviceMessage {
|
||||
|
||||
/**
|
||||
* 请求编号
|
||||
*/
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 设备信息
|
||||
*/
|
||||
private String productKey;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String deviceName;
|
||||
/**
|
||||
* 设备标识
|
||||
*/
|
||||
private String deviceKey;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*
|
||||
* 枚举 {@link IotDeviceMessageTypeEnum}
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 标识符
|
||||
*
|
||||
* 枚举 {@link IotDeviceMessageIdentifierEnum}
|
||||
*/
|
||||
private String identifier;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*
|
||||
* 例如说:属性上报的 properties、事件上报的 params
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
/**
|
||||
* 上报时间
|
||||
*/
|
||||
private LocalDateTime reportTime;
|
||||
|
||||
// TODO @芋艿 code;
|
||||
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* 消息队列的消息
|
||||
*/
|
||||
package cn.iocoder.yudao.module.iot.mq.message;
|
|
@ -1,31 +1,30 @@
|
|||
package cn.iocoder.yudao.module.iot.mq.producer.simulatesend;
|
||||
package cn.iocoder.yudao.module.iot.mq.producer.device;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
|
||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
// TODO @芋艿:@alwayssuper:是不是还没用起来哈?Producer 最好属于某个模块;
|
||||
/**
|
||||
* SimulateSend 模拟设备上报的 Producer
|
||||
* Iot 设备相关消息的 Producer
|
||||
*
|
||||
* @author alwayssuper
|
||||
* @since 2024/12/17 16:35
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SimulateSendProducer {
|
||||
public class IotDeviceProducer {
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 发送 {@link ThingModelMessage} 消息
|
||||
* 发送 {@link IotDeviceMessage} 消息
|
||||
*
|
||||
* @param thingModelMessage 物模型消息
|
||||
*/
|
||||
public void sendSimulateMessage(ThingModelMessage thingModelMessage) {
|
||||
public void sendDeviceMessage(IotDeviceMessage thingModelMessage) {
|
||||
applicationContext.publishEvent(thingModelMessage);
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* TODO 芋艿:临时占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.iot.mq.producer;
|
|
@ -1,77 +0,0 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
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.IotDeviceDataSimulatorSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IoT 设备日志数据 Service 实现了
|
||||
*
|
||||
* @author alwayssuper
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@Validated
|
||||
public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{
|
||||
|
||||
@Resource
|
||||
private IotDeviceLogDataMapper deviceLogDataMapper;
|
||||
|
||||
@Override
|
||||
public void defineDeviceLog() {
|
||||
if (deviceLogDataMapper.checkDeviceLogSTableExists() != null) {
|
||||
log.info("[defineDeviceLog][设备日志超级表已存在,跳过创建]");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("[defineDeviceLog][设备日志超级表不存在,开始创建]");
|
||||
deviceLogDataMapper.createDeviceLogSTable();
|
||||
log.info("[defineDeviceLog][设备日志超级表不存在,创建完成]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) {
|
||||
// 1. 转换请求对象为 DO
|
||||
IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class);
|
||||
|
||||
// 2. 处理时间字段
|
||||
// iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now()
|
||||
|
||||
// 3. 插入数据
|
||||
deviceLogDataMapper.insert(iotDeviceLogDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) {
|
||||
// TODO @芋艿:增加一个表不存在的 try catch
|
||||
List<IotDeviceLogDO> list = deviceLogDataMapper.selectPage(pageReqVO);
|
||||
Long total = deviceLogDataMapper.selectCount(pageReqVO);
|
||||
return new PageResult<>(list, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveDeviceLog(ThingModelMessage message) {
|
||||
IotDeviceLogDO log = IotDeviceLogDO.builder()
|
||||
.id(message.getId())
|
||||
.deviceKey(message.getDeviceKey())
|
||||
.productKey(message.getProductKey())
|
||||
.type(message.getMethod()) // 消息类型,使用method作为类型 TODO 芋艿:在看看
|
||||
.subType("property") // TODO 芋艿:这块先写死,后续优化
|
||||
.content(JSONUtil.toJsonStr(message)) // TODO 芋艿:后续优化
|
||||
.reportTime(message.getTime()) // 上报时间 TODO 芋艿:在想想时间
|
||||
.build();
|
||||
deviceLogDataMapper.insert(log);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +1,16 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
package cn.iocoder.yudao.module.iot.service.device.data;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
|
||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||
|
||||
/**
|
||||
* IoT 设备日志数据 Service 接口
|
||||
*
|
||||
* @author alwayssuper
|
||||
*/
|
||||
public interface IotDeviceLogDataService {
|
||||
public interface IotDeviceLogService {
|
||||
|
||||
/**
|
||||
* 初始化 TDengine 超级表
|
||||
|
@ -23,11 +22,9 @@ public interface IotDeviceLogDataService {
|
|||
/**
|
||||
* 插入设备日志
|
||||
*
|
||||
* 当该设备第一次插入日志时,自动创建该设备的设备日志子表
|
||||
*
|
||||
* @param simulatorReqVO 设备日志模拟数据
|
||||
* @param message 设备数据
|
||||
*/
|
||||
void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO);
|
||||
void createDeviceLog(IotDeviceMessage message);
|
||||
|
||||
/**
|
||||
* 获得设备日志分页
|
||||
|
@ -37,11 +34,4 @@ public interface IotDeviceLogDataService {
|
|||
*/
|
||||
PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 插入设备日志
|
||||
*
|
||||
* @param message 设备数据
|
||||
*/
|
||||
void saveDeviceLog(ThingModelMessage message);
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device.data;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper;
|
||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IoT 设备日志数据 Service 实现类
|
||||
*
|
||||
* @author alwayssuper
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@Validated
|
||||
public class IotDeviceLogServiceImpl implements IotDeviceLogService {
|
||||
|
||||
@Resource
|
||||
private IotDeviceLogDataMapper deviceLogDataMapper;
|
||||
|
||||
@Override
|
||||
public void defineDeviceLog() {
|
||||
if (StrUtil.isNotEmpty(deviceLogDataMapper.showDeviceLogSTable())) {
|
||||
log.info("[defineDeviceLog][设备日志超级表已存在,创建跳过]");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("[defineDeviceLog][设备日志超级表不存在,创建开始...]");
|
||||
deviceLogDataMapper.createDeviceLogSTable();
|
||||
log.info("[defineDeviceLog][设备日志超级表不存在,创建成功]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDeviceLog(IotDeviceMessage message) {
|
||||
IotDeviceLogDO log = BeanUtils.toBean(message, IotDeviceLogDO.class)
|
||||
.setId(IdUtil.fastSimpleUUID())
|
||||
.setContent(JsonUtils.toJsonString(message.getData()));
|
||||
deviceLogDataMapper.insert(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) {
|
||||
// TODO @芋艿:增加一个表不存在的 try catch
|
||||
List<IotDeviceLogDO> list = deviceLogDataMapper.selectPage(pageReqVO);
|
||||
Long total = deviceLogDataMapper.selectCount(pageReqVO);
|
||||
return new PageResult<>(list, total);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
package cn.iocoder.yudao.module.iot.service.device.data;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -16,7 +15,7 @@ import java.util.Map;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IotDevicePropertyDataService {
|
||||
public interface IotDevicePropertyService {
|
||||
|
||||
/**
|
||||
* 定义设备属性数据的结构
|
||||
|
@ -32,13 +31,6 @@ public interface IotDevicePropertyDataService {
|
|||
*/
|
||||
void saveDeviceData(IotDevicePropertyReportReqDTO createDTO);
|
||||
|
||||
/**
|
||||
* 保存设备数据
|
||||
*
|
||||
* @param thingModelMessage 设备数据
|
||||
*/
|
||||
void saveDeviceDataTest(ThingModelMessage thingModelMessage);
|
||||
|
||||
/**
|
||||
* 模拟设备
|
||||
*
|
|
@ -1,9 +1,8 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device;
|
||||
package cn.iocoder.yudao.module.iot.service.device.data;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
@ -16,18 +15,15 @@ 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.product.IotProductDO;
|
||||
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.thingmodel.IotThingModelDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.IotConstants;
|
||||
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
|
||||
import cn.iocoder.yudao.module.iot.mq.producer.simulatesend.SimulateSendProducer;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||
import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
|
||||
import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
|
@ -53,7 +49,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_C
|
|||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService {
|
||||
public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
|
||||
|
||||
/**
|
||||
* 物模型的数据类型,与 TDengine 数据类型的映射关系
|
||||
|
@ -76,25 +72,16 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
|
|||
@Resource
|
||||
private IotDeviceService deviceService;
|
||||
@Resource
|
||||
private IotThingModelMessageService thingModelMessageService;
|
||||
@Resource
|
||||
private IotThingModelService thingModelService;
|
||||
@Resource
|
||||
private IotProductService productService;
|
||||
|
||||
@Resource
|
||||
private SimulateSendProducer simulateSendProducer;
|
||||
|
||||
@Resource
|
||||
private TdEngineDMLMapper tdEngineDMLMapper;
|
||||
|
||||
@Resource
|
||||
private DeviceDataRedisDAO deviceDataRedisDAO;
|
||||
|
||||
@Resource
|
||||
private IotDevicePropertyDataMapper devicePropertyDataMapper;
|
||||
|
||||
|
||||
@Override
|
||||
public void defineDevicePropertyData(Long productId) {
|
||||
// 1.1 查询产品和物模型
|
||||
|
@ -144,28 +131,8 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
|
|||
// 1. 根据产品 key 和设备名称,获得设备信息
|
||||
IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName());
|
||||
// 2. 解析消息,保存数据
|
||||
JSONObject jsonObject = new JSONObject(createDTO.getParams());
|
||||
JSONObject jsonObject = new JSONObject(createDTO.getProperties());
|
||||
log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject);
|
||||
ThingModelMessage thingModelMessage = ThingModelMessage.builder()
|
||||
.id(jsonObject.getStr("id"))
|
||||
.sys(jsonObject.get("sys"))
|
||||
.method(jsonObject.getStr("method"))
|
||||
.params(jsonObject.get("params"))
|
||||
.time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time"))
|
||||
.productKey(createDTO.getProductKey())
|
||||
.deviceName(createDTO.getDeviceName())
|
||||
.deviceKey(device.getDeviceKey())
|
||||
.build();
|
||||
thingModelMessageService.saveThingModelMessage(device, thingModelMessage);
|
||||
}
|
||||
|
||||
//TODO @芋艿:后续捋一捋这块逻辑,先借鉴一下目前的代码
|
||||
@Override
|
||||
public void saveDeviceDataTest(ThingModelMessage thingModelMessage) {
|
||||
// 1. 根据产品 key 和设备名称,获得设备信息
|
||||
IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(thingModelMessage.getProductKey(), thingModelMessage.getDeviceName());
|
||||
// 2. 保存数据
|
||||
thingModelMessageService.saveThingModelMessage(device, thingModelMessage);
|
||||
}
|
||||
|
||||
//TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化
|
||||
|
@ -182,20 +149,17 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
|
|||
throw exception(DEVICE_DATA_CONTENT_JSON_PARSE_ERROR);
|
||||
}
|
||||
|
||||
// TODO @芋艿:后续优化
|
||||
// 3. 构建物模型消息
|
||||
ThingModelMessage thingModelMessage = ThingModelMessage.builder()
|
||||
.id(IdUtil.fastSimpleUUID()) // TODO:后续优化
|
||||
.sys(null)// TODO:这块先写死,后续优化
|
||||
.method("thing.event.property.post") // TODO:这块先写死,后续优化
|
||||
.params(contentJson) // 将 content 作为 params
|
||||
.time(simulatorReqVO.getReportTime()) // 使用上报时间
|
||||
.productKey(simulatorReqVO.getProductKey())
|
||||
.deviceName(device.getDeviceName())
|
||||
.deviceKey(device.getDeviceKey())
|
||||
.build();
|
||||
// IotDeviceMessage thingModelMessage = IotDeviceMessage.builder()
|
||||
// .params(contentJson) // 将 content 作为 params
|
||||
// .time(simulatorReqVO.getReportTime()) // 使用上报时间
|
||||
// .productKey(simulatorReqVO.getProductKey())
|
||||
// .deviceName(device.getDeviceName())
|
||||
// .build();
|
||||
|
||||
// 4. 发送模拟消息
|
||||
simulateSendProducer.sendSimulateMessage(thingModelMessage);
|
||||
// simulateSendProducer.sendDeviceMessage(thingModelMessage);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device.upstream;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
|
||||
|
||||
/**
|
||||
* 设备上行 Service 接口
|
||||
*
|
||||
* 目的:设备 -> 插件 -> 服务端
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface IotDeviceUpstreamService {
|
||||
|
||||
/**
|
||||
* 更新设备状态
|
||||
*
|
||||
* @param updateReqDTO 更新设备状态 DTO
|
||||
*/
|
||||
void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO);
|
||||
|
||||
/**
|
||||
* 上报设备属性数据
|
||||
*
|
||||
* @param reportReqDTO 上报设备属性数据 DTO
|
||||
*/
|
||||
void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO);
|
||||
|
||||
/**
|
||||
* 上报设备事件数据
|
||||
*
|
||||
* @param reportReqDTO 设备事件
|
||||
*/
|
||||
void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO);
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package cn.iocoder.yudao.module.iot.service.device.upstream;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceUpstreamAbstractReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 设备上行 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
|
||||
|
||||
@Resource
|
||||
private IotDeviceService deviceService;
|
||||
|
||||
@Resource
|
||||
private IotDeviceProducer deviceProducer;
|
||||
|
||||
@Override
|
||||
public void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
|
||||
log.info("[updateDeviceStatus][更新设备状态: {}]", updateReqDTO);
|
||||
// TODO 芋艿:插件状态
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
|
||||
// 1.1 获得设备
|
||||
log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO);
|
||||
IotDeviceDO device = getDevice(reportReqDTO);
|
||||
if (device == null) {
|
||||
log.error("[reportDevicePropertyData][设备({}/{})不存在]",
|
||||
reportReqDTO.getProductKey(), reportReqDTO.getDeviceName());
|
||||
return;
|
||||
}
|
||||
// 1.2 记录设备的最后时间
|
||||
updateDeviceLastTime(device, reportReqDTO);
|
||||
|
||||
// 2. 发送设备消息
|
||||
IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class)
|
||||
.setType(IotDeviceMessageTypeEnum.PROPERTY.getType())
|
||||
.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())
|
||||
.setData(reportReqDTO.getProperties());
|
||||
sendDeviceMessage(message, device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
|
||||
log.info("[reportDeviceEventData][上报设备事件数据: {}]", reportReqDTO);
|
||||
|
||||
// TODO 芋艿:待实现
|
||||
}
|
||||
|
||||
private IotDeviceDO getDevice(IotDeviceUpstreamAbstractReqDTO reqDTO) {
|
||||
return TenantUtils.executeIgnore(() -> // 需要忽略租户,因为请求时,未带租户编号
|
||||
deviceService.getDeviceByProductKeyAndDeviceName(reqDTO.getProductKey(), reqDTO.getDeviceName()));
|
||||
}
|
||||
|
||||
private void updateDeviceLastTime(IotDeviceDO deviceDO, IotDeviceUpstreamAbstractReqDTO reqDTO) {
|
||||
// TODO 芋艿:插件状态
|
||||
// TODO 芋艿:操作时间
|
||||
}
|
||||
|
||||
private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
|
||||
// 1. 完善消息
|
||||
message.setDeviceKey(device.getDeviceKey());
|
||||
if (StrUtil.isEmpty(message.getRequestId())) {
|
||||
message.setRequestId(IdUtil.fastSimpleUUID());
|
||||
}
|
||||
if (message.getReportTime() == null) {
|
||||
message.setReportTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 2. 发送消息
|
||||
try {
|
||||
deviceProducer.sendDeviceMessage(message);
|
||||
log.info("[sendDeviceMessage][message({}) 发送消息成功]", message);
|
||||
} catch (Exception e) {
|
||||
log.error("[sendDeviceMessage][message({}) 发送消息失败]", message, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ 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.mysql.product.IotProductMapper;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
@ -35,7 +35,7 @@ public class IotProductServiceImpl implements IotProductService {
|
|||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,解决循环依赖
|
||||
private IotDevicePropertyDataService devicePropertyDataService;
|
||||
private IotDevicePropertyService devicePropertyDataService;
|
||||
|
||||
@Override
|
||||
public Long createProduct(IotProductSaveReqVO createReqVO) {
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
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.tdengine.ThingModelMessage;
|
||||
|
||||
/**
|
||||
* 物模型消息 Service
|
||||
*/
|
||||
public interface IotThingModelMessageService {
|
||||
|
||||
/**
|
||||
* 保存物模型消息
|
||||
*
|
||||
* @param device 设备
|
||||
* @param thingModelMessage 物模型消息
|
||||
*/
|
||||
void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage);
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
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;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
|
||||
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.tdengine.ThingModelMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper;
|
||||
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.thingmodel.IotThingModelTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||
import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
|
||||
/**
|
||||
* 物模型消息 Service 实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@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;
|
||||
|
||||
@Resource
|
||||
private IotThingModelService iotThingModelService;
|
||||
@Resource
|
||||
private IotDeviceService iotDeviceService;
|
||||
@Resource
|
||||
private IotProductService productService;
|
||||
|
||||
@Resource
|
||||
private TdEngineDDLMapper tdEngineDDLMapper;
|
||||
@Resource
|
||||
private TdEngineDMLMapper tdEngineDMLMapper;
|
||||
|
||||
@Resource
|
||||
private IotDevicePropertyDataMapper iotDevicePropertyDataMapper;
|
||||
|
||||
@Resource
|
||||
private DeviceDataRedisDAO deviceDataRedisDAO;
|
||||
|
||||
// TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感
|
||||
@Override
|
||||
@TenantIgnore
|
||||
public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) {
|
||||
// 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态
|
||||
if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) {
|
||||
// 1.1 创建设备表
|
||||
// createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
|
||||
iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO()
|
||||
.setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus()));
|
||||
}
|
||||
|
||||
// 2. 获取设备属性并进行物模型校验,过滤非物模型属性
|
||||
Map<String, Object> params = thingModelMessage.dataToMap();
|
||||
List<IotThingModelDO> thingModelList = getValidThingModelList(thingModelMessage.getProductKey());
|
||||
if (thingModelList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 过滤并收集有效的属性字段,缓存设备属性
|
||||
List<TdFieldDO> schemaFieldValues = filterAndCollectValidFields(params, thingModelList, device, thingModelMessage.getTime());
|
||||
if (schemaFieldValues.size() == 0) { // 没有字段,无需保存
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 构建并保存设备属性数据
|
||||
// tdEngineDMLMapper.insertData(TdTableDO.builder()
|
||||
// .dataBaseName(getDatabaseName())
|
||||
// .tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()))
|
||||
// .columns(schemaFieldValues)
|
||||
// .build());
|
||||
// TODO:复用了旧逻辑,先过渡一下
|
||||
iotDevicePropertyDataMapper.insertDevicePropertyData(TdTableDO.builder()
|
||||
.productKey(device.getProductKey())
|
||||
.deviceKey(device.getDeviceKey())
|
||||
.columns(schemaFieldValues)
|
||||
.build());
|
||||
}
|
||||
|
||||
private List<IotThingModelDO> getValidThingModelList(String productKey) {
|
||||
return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey),
|
||||
thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));
|
||||
}
|
||||
|
||||
// @Override
|
||||
// @TenantIgnore
|
||||
// public void createSuperTable(Long productId) {
|
||||
// // 1. 查询产品
|
||||
// IotProductDO product = productService.getProduct(productId);
|
||||
// // 2. 创建日志超级表
|
||||
// tdThingModelMessageMapper.createSuperTable(product.getProductKey());
|
||||
//
|
||||
// // 2. 获取超级表的名称和数据库名称
|
||||
// // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法
|
||||
//// String databaseName = IotTdDatabaseUtils.getDatabaseName(url);
|
||||
//// String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey());
|
||||
////
|
||||
//// // 解析物模型,获取字段列表
|
||||
//// List<TdFieldDO> schemaFields = List.of(
|
||||
//// TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(),
|
||||
//// TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(),
|
||||
//// TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(),
|
||||
//// TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(),
|
||||
//// TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build()
|
||||
//// );
|
||||
//// // 设置超级表的标签
|
||||
//// List<TdFieldDO> tagsFields = List.of(
|
||||
//// TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build()
|
||||
//// );
|
||||
//// // 3. 创建超级表
|
||||
//// tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));
|
||||
// }
|
||||
|
||||
private List<IotThingModelDO> getValidFunctionList(String productKey) {
|
||||
return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey),
|
||||
thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));
|
||||
}
|
||||
|
||||
private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotThingModelDO> thingModelList, IotDeviceDO device, Long time) {
|
||||
// 1. 获取属性标识符集合
|
||||
Set<String> propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier);
|
||||
|
||||
// 2. 构建属性标识符和属性的映射
|
||||
Map<String, IotThingModelDO> thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier);
|
||||
|
||||
// 3. 过滤并收集有效的属性字段
|
||||
List<TdFieldDO> schemaFieldValues = new ArrayList<>();
|
||||
//TODO:新版本是使用ts字段
|
||||
// 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, thingModelMap.get(key), val, time);
|
||||
}
|
||||
});
|
||||
return schemaFieldValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存设备属性
|
||||
*
|
||||
* @param device 设备信息
|
||||
* @param iotThingModelDO 物模型属性
|
||||
* @param val 属性值
|
||||
* @param time 时间
|
||||
*/
|
||||
private void setDeviceDataCache(IotDeviceDO device, IotThingModelDO iotThingModelDO, Object val, Long time) {
|
||||
IotDeviceDataDO deviceData = IotDeviceDataDO.builder()
|
||||
.productKey(device.getProductKey())
|
||||
.deviceName(device.getDeviceName())
|
||||
.identifier(iotThingModelDO.getIdentifier())
|
||||
.value(val != null ? val.toString() : null)
|
||||
.updateTime(DateUtil.toLocalDateTime(new Date(time)))
|
||||
.deviceId(device.getId())
|
||||
.thingModelId(iotThingModelDO.getId())
|
||||
.name(iotThingModelDO.getName())
|
||||
.dataType(iotThingModelDO.getProperty().getDataType())
|
||||
.build();
|
||||
deviceDataRedisDAO.set(deviceData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备数据表
|
||||
*
|
||||
* @param deviceType 设备类型
|
||||
* @param productKey 产品 Key
|
||||
* @param deviceName 设备名称
|
||||
* @param deviceKey 设备 Key
|
||||
*/
|
||||
private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) {
|
||||
// 1. 获取超级表名和数据库名
|
||||
String superTableName = getProductPropertySTableName(deviceType, productKey);
|
||||
String dataBaseName = getDatabaseName();
|
||||
|
||||
// 2. 获取超级表的结构信息
|
||||
List<Map<String, Object>> maps = tdEngineDDLMapper.describeSuperTable(new TdTableDO(dataBaseName, superTableName));
|
||||
List<TdFieldDO> tagsFieldValues = new ArrayList<>();
|
||||
if (maps != null) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 创建设备数据表
|
||||
String tableName = getDeviceTableName(productKey, deviceName);
|
||||
tdEngineDDLMapper.createTable(TdTableDO.builder().build()
|
||||
.setDataBaseName(dataBaseName)
|
||||
.setSuperTableName(superTableName)
|
||||
.setTableName(tableName)
|
||||
.setTags(tagsFieldValues));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取数据库名称
|
||||
*
|
||||
* @return 数据库名称
|
||||
*/
|
||||
private String getDatabaseName() {
|
||||
return StrUtil.subAfter(url, "/", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品属性表名
|
||||
*
|
||||
* @param deviceType 设备类型
|
||||
* @param productKey 产品 Key
|
||||
* @return 产品属性表名
|
||||
*/
|
||||
private static String getProductPropertySTableName(Integer deviceType, String productKey) {
|
||||
// TODO @haohao:枚举下,会好点哈。
|
||||
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
|
||||
* @param deviceName 设备名称
|
||||
* @return 设备表名
|
||||
*/
|
||||
private static String getDeviceTableName(String productKey, String deviceName) {
|
||||
return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase());
|
||||
}
|
||||
|
||||
}
|
|
@ -9,8 +9,9 @@
|
|||
ts TIMESTAMP,
|
||||
id NCHAR(50),
|
||||
product_key NCHAR(50),
|
||||
device_name NCHAR(50),
|
||||
type NCHAR(50),
|
||||
sub_type NCHAR(50),
|
||||
identifier NCHAR(255),
|
||||
content NCHAR(1024),
|
||||
report_time TIMESTAMP
|
||||
) TAGS (
|
||||
|
@ -18,18 +19,23 @@
|
|||
)
|
||||
</update>
|
||||
|
||||
<select id="showDeviceLogSTable" resultType="String">
|
||||
SHOW STABLES LIKE 'device_log'
|
||||
</select>
|
||||
|
||||
<insert id="insert">
|
||||
INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time)
|
||||
INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, report_time)
|
||||
USING device_log
|
||||
TAGS ('${log.deviceKey}')
|
||||
TAGS ('${deviceKey}')
|
||||
VALUES (
|
||||
NOW,
|
||||
#{log.id},
|
||||
#{log.productKey},
|
||||
#{log.type},
|
||||
#{log.subType},
|
||||
#{log.content},
|
||||
#{log.reportTime}
|
||||
#{id},
|
||||
#{productKey},
|
||||
#{deviceName},
|
||||
#{type},
|
||||
#{identifier},
|
||||
#{content},
|
||||
#{reportTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
|
@ -51,6 +57,7 @@
|
|||
LIMIT #{reqVO.pageSize} OFFSET #{reqVO.pageNo}
|
||||
</select>
|
||||
|
||||
<!-- TODO 芋艿:看看能不能复用 mybatis-plus 的 selectCount 方法 -->
|
||||
<select id="selectCount" resultType="Long">
|
||||
SELECT COUNT(*)
|
||||
FROM device_log_${reqVO.deviceKey}
|
||||
|
@ -67,12 +74,4 @@
|
|||
</where>
|
||||
</select>
|
||||
|
||||
<select id="checkDeviceLogSTableExists" resultType="Object">
|
||||
SHOW STABLES LIKE 'device_log'
|
||||
</select>
|
||||
|
||||
<select id="checkDeviceLogTableExists" resultType="Object">
|
||||
SHOW TABLES LIKE 'device_log_${deviceKey}'
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -1,99 +1,62 @@
|
|||
package cn.iocoder.yudao.module.iot.plugin.common.api;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
* 用于通过 {@link RestTemplate} 向远程 IoT 服务发送设备数据相关的请求,
|
||||
* 包括设备状态更新、事件数据上报、属性数据上报等操作。
|
||||
*/
|
||||
// TODO @haohao:类注释,写一下,比较好
|
||||
// TODO @haohao:类名要改下
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class DeviceDataApiClient implements DeviceDataApi {
|
||||
public class DeviceDataApiClient implements IotDeviceUpstreamApi {
|
||||
|
||||
public static final String URL_PREFIX = "/rpc-api/iot/device/upstream";
|
||||
|
||||
/**
|
||||
* 用于发送 HTTP 请求的工具
|
||||
*/
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
/**
|
||||
* 远程 IoT 服务的基础 URL
|
||||
* 例如:http://127.0.0.1:8080
|
||||
*/
|
||||
private final String deviceDataUrl;
|
||||
|
||||
// 可以通过构造器把 RestTemplate 和 baseUrl 注入进来
|
||||
// TODO @haohao:可以用 lombok 简化
|
||||
public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) {
|
||||
this.restTemplate = restTemplate;
|
||||
this.deviceDataUrl = deviceDataUrl;
|
||||
}
|
||||
|
||||
// TODO @haohao:返回结果,不用 CommonResult 哈。
|
||||
@Override
|
||||
public CommonResult<Boolean> updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
|
||||
String url = deviceDataUrl + "/rpc-api/iot/device-data/update-status";
|
||||
String url = deviceDataUrl + URL_PREFIX + "/update-status";
|
||||
return doPost(url, updateReqDTO, "updateDeviceStatus");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
|
||||
String url = deviceDataUrl + "/rpc-api/iot/device-data/report-event";
|
||||
String url = deviceDataUrl + URL_PREFIX + "/report-event";
|
||||
return doPost(url, reportReqDTO, "reportDeviceEventData");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
|
||||
String url = deviceDataUrl + "/rpc-api/iot/device-data/report-property";
|
||||
String url = deviceDataUrl + URL_PREFIX + "/report-property";
|
||||
return doPost(url, reportReqDTO, "reportDevicePropertyData");
|
||||
}
|
||||
|
||||
|
||||
// TODO @haohao:未来可能有 get 类型哈
|
||||
/**
|
||||
* 发送 GET 请求
|
||||
*
|
||||
* @param <T> 请求体类型
|
||||
* @param url 请求 URL
|
||||
* @param requestBody 请求体
|
||||
* @param actionName 操作名称
|
||||
* @return 响应结果
|
||||
*/
|
||||
private <T> CommonResult<Boolean> doGet(String url, T requestBody, String actionName) {
|
||||
log.info("[{}] Sending request to URL: {}", actionName, url);
|
||||
try {
|
||||
CommonResult<?> response = restTemplate.getForObject(url, CommonResult.class);
|
||||
if (response != null && response.isSuccess()) {
|
||||
return success(true);
|
||||
} else {
|
||||
log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response);
|
||||
return CommonResult.error(500, "Request failed");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[{}] Error sending request to URL: {}", actionName, url, e);
|
||||
return CommonResult.error(400, "Request error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 POST 请求
|
||||
*
|
||||
* @param <T> 请求体类型
|
||||
* @param url 请求 URL
|
||||
* @param requestBody 请求体
|
||||
* @param actionName 操作名称
|
||||
* @return 响应结果
|
||||
* 将与远程服务交互的通用逻辑抽取成一个私有方法
|
||||
*/
|
||||
private <T> CommonResult<Boolean> doPost(String url, T requestBody, String actionName) {
|
||||
log.info("[{}] Sending request to URL: {}", actionName, url);
|
||||
try {
|
||||
CommonResult<?> response = restTemplate.postForObject(url, requestBody, CommonResult.class);
|
||||
if (response != null && response.isSuccess()) {
|
||||
return success(true);
|
||||
} else {
|
||||
log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response);
|
||||
return CommonResult.error(500, "Request failed");
|
||||
}
|
||||
// 这里指定返回类型为 CommonResult<?>,根据后台服务返回的实际结构做调整
|
||||
restTemplate.postForObject(url, requestBody, CommonResult.class);
|
||||
// TODO @haohao:check 结果,是否成功
|
||||
return success(true);
|
||||
} catch (Exception e) {
|
||||
log.error("[{}] Error sending request to URL: {}", actionName, url, e);
|
||||
return CommonResult.error(400, "Request error: " + e.getMessage());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package cn.iocoder.yudao.module.iot.plugin.common.config;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
||||
import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
|
@ -43,7 +43,7 @@ public class YudaoDeviceDataApiAutoConfiguration {
|
|||
* @return DeviceDataApi 实例
|
||||
*/
|
||||
@Bean
|
||||
public DeviceDataApi deviceDataApi(RestTemplate restTemplate) {
|
||||
public IotDeviceUpstreamApi deviceDataApi(RestTemplate restTemplate) {
|
||||
return new DeviceDataApiClient(restTemplate, deviceDataUrl);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package cn.iocoder.yudao.module.iot.plugin;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.pf4j.Plugin;
|
||||
import org.pf4j.PluginWrapper;
|
||||
|
@ -27,7 +27,7 @@ public class EmqxPlugin extends Plugin {
|
|||
executorService = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
|
||||
IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class);
|
||||
if (deviceDataApi == null) {
|
||||
log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!");
|
||||
return;
|
||||
|
|
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.config;
|
|||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.pf4j.spring.SpringPlugin;
|
||||
|
@ -64,7 +64,7 @@ public class HttpVertxPlugin extends SpringPlugin {
|
|||
protected void prepareRefresh() {
|
||||
// 在刷新容器前注册主程序中的 Bean
|
||||
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
|
||||
DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
|
||||
IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class);
|
||||
beanFactory.registerSingleton("deviceDataApi", deviceDataApi);
|
||||
super.prepareRefresh();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package cn.iocoder.yudao.module.iot.plugin.http.config;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
||||
import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.ext.web.Router;
|
||||
|
@ -61,7 +61,7 @@ public class HttpVertxPluginConfiguration {
|
|||
* @return HttpVertxHandler 实例
|
||||
*/
|
||||
@Bean
|
||||
public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) {
|
||||
public HttpVertxHandler httpVertxHandler(IotDeviceUpstreamApi deviceDataApi) {
|
||||
return new HttpVertxHandler(deviceDataApi);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,21 @@ package cn.iocoder.yudao.module.iot.plugin.http.service;
|
|||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
||||
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RequestBody;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class HttpVertxHandler implements Handler<RoutingContext> {
|
||||
|
||||
private final DeviceDataApi deviceDataApi;
|
||||
private final IotDeviceUpstreamApi deviceDataApi;
|
||||
|
||||
public HttpVertxHandler(DeviceDataApi deviceDataApi) {
|
||||
public HttpVertxHandler(IotDeviceUpstreamApi deviceDataApi) {
|
||||
this.deviceDataApi = deviceDataApi;
|
||||
}
|
||||
|
||||
|
@ -23,6 +25,7 @@ public class HttpVertxHandler implements Handler<RoutingContext> {
|
|||
String productKey = ctx.pathParam("productKey");
|
||||
String deviceName = ctx.pathParam("deviceName");
|
||||
|
||||
// TODO @haohao:requestBody.asJsonObject() 貌似天然就是 json 对象哈?
|
||||
RequestBody requestBody = ctx.body();
|
||||
JSONObject jsonData;
|
||||
try {
|
||||
|
@ -43,7 +46,7 @@ public class HttpVertxHandler implements Handler<RoutingContext> {
|
|||
IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder()
|
||||
.productKey(productKey)
|
||||
.deviceName(deviceName)
|
||||
.params(jsonData)
|
||||
.properties((Map<String, Object>) requestBody.asJsonObject().getMap().get("properties"))
|
||||
.build();
|
||||
|
||||
deviceDataApi.reportDevicePropertyData(reportReqDTO);
|
||||
|
|
Loading…
Reference in New Issue