Commit 8089f3a319efb919fce809b8e144c3fcc0694b4f

Authored by YunaiV
1 parent f4ad3e9d

【功能优化】IoT:

1. DeviceDataApi => IotDeviceUpstreamApi,并新建 upstream 包
2. ThingModelMessage => IotDeviceMessage 设备消息
3. 基于 spring event 异步消费 IotDeviceMessage,并实现 IotDeviceLogMessageConsumer 记录日志
Showing 42 changed files with 538 additions and 680 deletions
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java
... ... @@ -13,7 +13,7 @@ import java.util.Set;
13 13 /**
14 14 * 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能
15 15 *
16   - * @author
  16 + * @author 芋道源码
17 17 */
18 18 public class TenantDatabaseInterceptor implements TenantLineHandler {
19 19  
... ...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
... ... @@ -45,6 +45,7 @@ public class TenantUtils {
45 45 *
46 46 * @param tenantId 租户编号
47 47 * @param callable 逻辑
  48 + * @return 结果
48 49 */
49 50 public static <V> V execute(Long tenantId, Callable<V> callable) {
50 51 Long oldTenantId = TenantContextHolder.getTenantId();
... ... @@ -79,6 +80,25 @@ public class TenantUtils {
79 80 }
80 81  
81 82 /**
  83 + * 忽略租户,执行对应的逻辑
  84 + *
  85 + * @param callable 逻辑
  86 + * @return 结果
  87 + */
  88 + public static <V> V executeIgnore(Callable<V> callable) {
  89 + Boolean oldIgnore = TenantContextHolder.isIgnore();
  90 + try {
  91 + TenantContextHolder.setIgnore(true);
  92 + // 执行逻辑
  93 + return callable.call();
  94 + } catch (Exception e) {
  95 + throw new RuntimeException(e);
  96 + } finally {
  97 + TenantContextHolder.setIgnore(oldIgnore);
  98 + }
  99 + }
  100 +
  101 + /**
82 102 * 将多租户编号,添加到 header 中
83 103 *
84 104 * @param headers HTTP 请求 headers
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java renamed to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java
... ... @@ -5,49 +5,44 @@ import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
5 5 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
6 6 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
7 7 import cn.iocoder.yudao.module.iot.enums.ApiConstants;
8   -import jakarta.annotation.security.PermitAll;
9 8 import jakarta.validation.Valid;
10 9 import org.springframework.web.bind.annotation.PostMapping;
11 10 import org.springframework.web.bind.annotation.PutMapping;
12 11 import org.springframework.web.bind.annotation.RequestBody;
13 12  
14   -// TODO 芋艿:名字可能看情况改下
15 13 /**
16   - * 设备数据 API
  14 + * 设备数据 Upstream 上行 API
  15 + *
  16 + * 目的:设备 -> 插件 -> 服务端
17 17 *
18 18 * @author haohao
19 19 */
20   -public interface DeviceDataApi {
  20 +public interface IotDeviceUpstreamApi {
21 21  
22   - // TODO @芋艿:可能会调整
23   - String PREFIX = ApiConstants.PREFIX + "/device-data";
  22 + String PREFIX = ApiConstants.PREFIX + "/device/upstream";
24 23  
25 24 /**
26 25 * 更新设备状态
27 26 *
28   - * @param updateReqDTO 更新请求
  27 + * @param updateReqDTO 更新设备状态 DTO
29 28 */
30 29 @PutMapping(PREFIX + "/update-status")
31   - @PermitAll // TODO 芋艿:后续看看怎么优化下
32 30 CommonResult<Boolean> updateDeviceStatus(@Valid @RequestBody IotDeviceStatusUpdateReqDTO updateReqDTO);
33 31  
34 32 /**
35   - * 上报设备事件数据
36   - *
37   - * @param reportReqDTO 设备事件
38   - */
39   - @PostMapping(PREFIX + "/report-event")
40   - @PermitAll // TODO 芋艿:后续看看怎么优化下
41   - CommonResult<Boolean> reportDeviceEventData(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO);
42   -
43   - /**
44 33 * 上报设备属性数据
45 34 *
46   - * @param reportReqDTO 设备数据
  35 + * @param reportReqDTO 上报设备属性数据 DTO
47 36 */
48 37 @PostMapping(PREFIX + "/report-property")
49   - @PermitAll // TODO 芋艿:后续看看怎么优化下
50 38 CommonResult<Boolean> reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO);
51 39  
  40 + /**
  41 + * 上报设备事件数据
  42 + *
  43 + * @param reportReqDTO 设备事件
  44 + */
  45 + @PostMapping(PREFIX + "/report-event")
  46 + CommonResult<Boolean> reportDeviceEventData(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO);
52 47  
53 48 }
54 49 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java
1 1 package cn.iocoder.yudao.module.iot.api.device.dto;
2 2  
3 3 import jakarta.validation.constraints.NotEmpty;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Builder;
6 4 import lombok.Data;
7 5 import lombok.NoArgsConstructor;
  6 +import lombok.experimental.SuperBuilder;
8 7  
9 8 import java.util.Map;
10 9  
... ... @@ -12,24 +11,9 @@ import java.util.Map;
12 11 * IoT 设备【事件】数据上报 Request DTO
13 12 */
14 13 @Data
  14 +@SuperBuilder
15 15 @NoArgsConstructor
16   -@AllArgsConstructor
17   -@Builder
18   -public class IotDeviceEventReportReqDTO {
19   -
20   - // TODO 芋艿:要不要 id
21   - // TODO 芋艿:要不要 time
22   -
23   - /**
24   - * 产品标识
25   - */
26   - @NotEmpty(message = "产品标识不能为空")
27   - private String productKey;
28   - /**
29   - * 设备名称
30   - */
31   - @NotEmpty(message = "设备名称不能为空")
32   - private String deviceName;
  16 +public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
33 17  
34 18 /**
35 19 * 事件标识
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java
1 1 package cn.iocoder.yudao.module.iot.api.device.dto;
2 2  
3 3 import jakarta.validation.constraints.NotEmpty;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Builder;
6 4 import lombok.Data;
7 5 import lombok.NoArgsConstructor;
  6 +import lombok.experimental.SuperBuilder;
8 7  
9 8 import java.util.Map;
10 9  
... ... @@ -12,28 +11,14 @@ import java.util.Map;
12 11 * IoT 设备【属性】数据上报 Request DTO
13 12 */
14 13 @Data
  14 +@SuperBuilder
15 15 @NoArgsConstructor
16   -@AllArgsConstructor
17   -@Builder
18   -public class IotDevicePropertyReportReqDTO {
  16 +public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
19 17  
20   - // TODO 芋艿:要不要 id
21   - // TODO 芋艿:要不要 time
22   -
23   - /**
24   - * 产品标识
25   - */
26   - @NotEmpty(message = "产品标识不能为空")
27   - private String productKey;
28   - /**
29   - * 设备名称
30   - */
31   - @NotEmpty(message = "设备名称不能为空")
32   - private String deviceName;
33 18 /**
34 19 * 属性参数
35 20 */
36 21 @NotEmpty(message = "属性参数不能为空")
37   - private Map<String, Object> params;
  22 + private Map<String, Object> properties;
38 23  
39 24 }
40 25 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java
... ... @@ -3,33 +3,18 @@ package cn.iocoder.yudao.module.iot.api.device.dto;
3 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 4 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
5 5 import jakarta.validation.constraints.NotEmpty;
6   -import lombok.AllArgsConstructor;
7   -import lombok.Builder;
8 6 import lombok.Data;
9 7 import lombok.NoArgsConstructor;
  8 +import lombok.experimental.SuperBuilder;
10 9  
11 10 /**
12 11 * IoT 设备状态更新 Request DTO
13 12 */
14 13 @Data
  14 +@SuperBuilder
15 15 @NoArgsConstructor
16   -@AllArgsConstructor
17   -@Builder
18   -public class IotDeviceStatusUpdateReqDTO {
  16 +public class IotDeviceStatusUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO {
19 17  
20   - // TODO 芋艿:要不要 id
21   - // TODO 芋艿:要不要 time
22   -
23   - /**
24   - * 产品标识
25   - */
26   - @NotEmpty(message = "产品标识不能为空")
27   - private String productKey;
28   - /**
29   - * 设备名称
30   - */
31   - @NotEmpty(message = "设备名称不能为空")
32   - private String deviceName;
33 18 /**
34 19 * 设备状态
35 20 */
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.api.device.dto;
  2 +
  3 +import jakarta.validation.constraints.NotEmpty;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +import lombok.experimental.SuperBuilder;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +/**
  11 + * IoT 设备上行的抽象 Request DTO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Data
  16 +@SuperBuilder
  17 +@NoArgsConstructor
  18 +public abstract class IotDeviceUpstreamAbstractReqDTO {
  19 +
  20 + /**
  21 + * 请求编号
  22 + */
  23 + private String requestId;
  24 +
  25 + /**
  26 + * 插件标识
  27 + */
  28 + private String pluginKey;
  29 +
  30 + /**
  31 + * 产品标识
  32 + */
  33 + @NotEmpty(message = "产品标识不能为空")
  34 + private String productKey;
  35 + /**
  36 + * 设备名称
  37 + */
  38 + @NotEmpty(message = "设备名称不能为空")
  39 + private String deviceName;
  40 +
  41 + /**
  42 + * 上报时间
  43 + */
  44 + private LocalDateTime reportTime;
  45 +
  46 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.device;
  2 +
  3 +import lombok.Getter;
  4 +import lombok.RequiredArgsConstructor;
  5 +
  6 +/**
  7 + * IoT 设备消息标识符枚举
  8 + */
  9 +@Getter
  10 +@RequiredArgsConstructor
  11 +public enum IotDeviceMessageIdentifierEnum {
  12 +
  13 + PROPERTY_GET("get"),
  14 + PROPERTY_SET("set"),
  15 + PROPERTY_REPORT("report");
  16 +
  17 + /**
  18 + * 标志符
  19 + */
  20 + private final String identifier;
  21 +
  22 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.device;
  2 +
  3 +import lombok.Getter;
  4 +import lombok.RequiredArgsConstructor;
  5 +
  6 +/**
  7 + * IoT 设备消息类型枚举
  8 + */
  9 +@Getter
  10 +@RequiredArgsConstructor
  11 +public enum IotDeviceMessageTypeEnum {
  12 +
  13 + STATE("state"), // 设备状态
  14 + PROPERTY("property"), // 设备属性
  15 + EVENT("event"); // 设备事件
  16 +
  17 + /**
  18 + * 属性
  19 + */
  20 + private final String type;
  21 +
  22 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
5 5 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
6 6 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
7   -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
  7 +import cn.iocoder.yudao.module.iot.service.device.upstream.IotDeviceUpstreamService;
8 8 import org.springframework.validation.annotation.Validated;
9 9 import org.springframework.web.bind.annotation.RestController;
10 10  
... ... @@ -13,28 +13,30 @@ import javax.annotation.Resource;
13 13 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
14 14  
15 15 /**
16   - * 设备数据 API 实现类
  16 + * * 设备数据 Upstream 上行 API 实现类
17 17 */
18 18 @RestController
19 19 @Validated
20   -public class DeviceDataApiImpl implements DeviceDataApi {
  20 +public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
21 21  
22 22 @Resource
23   - private IotDevicePropertyDataService deviceDataService;
  23 + private IotDeviceUpstreamService deviceUpstreamService;
24 24  
25 25 @Override
26 26 public CommonResult<Boolean> updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
  27 + deviceUpstreamService.updateDeviceStatus(updateReqDTO);
27 28 return success(true);
28 29 }
29 30  
30 31 @Override
31   - public CommonResult<Boolean> reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
  32 + public CommonResult<Boolean> reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
  33 + deviceUpstreamService.reportDevicePropertyData(reportReqDTO);
32 34 return success(true);
33 35 }
34 36  
35 37 @Override
36   - public CommonResult<Boolean> reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
37   - deviceDataService.saveDeviceData(reportReqDTO);
  38 + public CommonResult<Boolean> reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
  39 + deviceUpstreamService.reportDeviceEventData(reportReqDTO);
38 40 return success(true);
39 41 }
40 42  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java
... ... @@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*;
7 7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
8 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
9   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
10   -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
  9 +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
  10 +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
11 11 import io.swagger.v3.oas.annotations.Operation;
12 12 import io.swagger.v3.oas.annotations.tags.Tag;
13 13 import jakarta.annotation.Resource;
... ... @@ -27,13 +27,13 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
27 27 public class IotDeviceDataController {
28 28  
29 29 @Resource
30   - private IotDevicePropertyDataService deviceDataService;
  30 + private IotDevicePropertyService deviceDataService;
31 31  
32 32 @Resource
33   - private IotDeviceLogDataService iotDeviceLogDataService;
  33 + private IotDeviceLogService iotDeviceLogDataService;
34 34  
35 35 @Resource // TODO @super:service 之间,不用空行;原因是,这样更简洁;空行,主要是为了“间隔”,提升可读性
36   - private IotDeviceLogDataService deviceLogDataService;
  36 + private IotDeviceLogService deviceLogDataService;
37 37  
38 38 // TODO @浩浩:这里的 /latest-list,包括方法名。
39 39 @GetMapping("/latest")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
... ... @@ -36,6 +36,8 @@ public class IotDeviceDO extends BaseDO {
36 36 private Long id;
37 37 /**
38 38 * 设备唯一标识符,全局唯一,用于识别设备
  39 + *
  40 + * 类似阿里云 <a href="https://help.aliyun.com/zh/iot/developer-reference/api-querydeviceinfo">QueryDeviceInfo</a> 的 IotInstanceId
39 41 */
40 42 private String deviceKey;
41 43 /**
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java
1 1 package cn.iocoder.yudao.module.iot.dal.dataobject.device;
2 2  
  3 +import cn.hutool.core.util.IdUtil;
3 4 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  5 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
  6 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  7 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
4 8 import lombok.AllArgsConstructor;
5 9 import lombok.Builder;
6 10 import lombok.Data;
... ... @@ -19,37 +23,51 @@ import lombok.NoArgsConstructor;
19 23 @AllArgsConstructor
20 24 public class IotDeviceLogDO {
21 25  
22   - // TODO @芋艿:消息 ID 的生成逻辑
23 26 /**
24   - * 消息 ID
  27 + * 日志编号
  28 + *
  29 + * 通过 {@link IdUtil#fastSimpleUUID()} 生成
25 30 */
26 31 private String id;
27 32  
28 33 /**
  34 + * 请求编号
  35 + *
  36 + * 对应 {@link IotDeviceMessage#getRequestId()} 字段
  37 + */
  38 + private String requestId;
  39 +
  40 + /**
29 41 * 产品标识
30 42 * <p>
31 43 * 关联 {@link IotProductDO#getProductKey()}
32 44 */
33 45 private String productKey;
34   -
  46 + /**
  47 + * 设备名称
  48 + *
  49 + * 关联 {@link IotDeviceDO#getDeviceName()}
  50 + */
  51 + private String deviceName;
35 52 /**
36 53 * 设备标识
37 54 * <p>
38 55 * 关联 {@link IotDeviceDO#getDeviceKey()}}
39 56 */
40   - private String deviceKey;
  57 + private String deviceKey; // 非存储字段,用于 TDengine 的 TAG
41 58  
42   - // TODO @super:枚举类
43 59 /**
44 60 * 日志类型
  61 + *
  62 + * 枚举 {@link IotDeviceMessageTypeEnum}
45 63 */
46 64 private String type;
47   -
48   - // TODO @super:枚举类
49 65 /**
50   - * 标识符:用于标识具体的属性、事件或服务
  66 + * 标识符
  67 + *
  68 + * 枚举 {@link IotDeviceMessageIdentifierEnum}
51 69 */
52   - private String subType;
  70 + private String identifier;
53 71  
54 72 /**
55 73 * 数据内容
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java
... ... @@ -22,21 +22,13 @@ public interface IotDeviceLogDataMapper {
22 22 */
23 23 void createDeviceLogSTable();
24 24  
25   - // TODO @super:单个参数,不用加 @Param
26   - // TODO @芋艿:在瞅瞅
27   - //讨论:艿菇这里有些特殊情况,我也学习了一下这块知识:
28   - // 如果使用的是Java 8及以上版本,并且编译器保留了参数名(通过编译器选项-parameters启用),则可以去掉@Param注解。MyBatis会自动使用参数的实际名称
29   - // 但在TDengine中 @Param去掉后TDengine会报错,以下是大模型的回答:
30   - // 不用加 @Param在普通的 MySQL 场景下是正确的 - 对于 MyBatis,当方法只有一个参数时,确实可以不用添加 @Param 注解。
31   - //但是在 TDengine 的场景下,情况不同:
32   - //TDengine 的特殊性:
33   - //TDengine 使用特殊的 SQL 语法
34   - //需要处理超级表(STable)和子表的概念
35   - //参数绑定的方式与普通 MySQL 不同
36   - //为什么这里必须要 @Param:
37   - //XML 中使用了 ${log.deviceKey} 这样的参数引用方式
38   - //需要在 SQL 中动态构建表名(device_log_${log.deviceKey})
39   - //没有 @Param("log") 的话,MyBatis 无法正确解析参数
  25 + /**
  26 + * 查询设备日志表是否存在
  27 + *
  28 + * @return 存在则返回表名;不存在则返回 null
  29 + */
  30 + String showDeviceLogSTable();
  31 +
40 32 /**
41 33 * 插入设备日志数据
42 34 *
... ... @@ -44,7 +36,7 @@ public interface IotDeviceLogDataMapper {
44 36 *
45 37 * @param log 设备日志数据
46 38 */
47   - void insert(@Param("log") IotDeviceLogDO log);
  39 + void insert(IotDeviceLogDO log);
48 40  
49 41 /**
50 42 * 获得设备日志分页
... ... @@ -62,12 +54,4 @@ public interface IotDeviceLogDataMapper {
62 54 */
63 55 Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO);
64 56  
65   - // TODO @芋艿:这个方法名,后续看看叫啥好
66   - /**
67   - * 查询设备日志表是否存在
68   - *
69   - * @return 不存在返回 null
70   - */
71   - Object checkDeviceLogSTableExists();
72   -
73 57 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.emq.service;
2 2  
3 3 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
4   -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
  4 +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
5 5 import jakarta.annotation.Resource;
6 6 import lombok.extern.slf4j.Slf4j;
7 7 import org.eclipse.paho.client.mqttv3.MqttClient;
... ... @@ -20,7 +20,7 @@ import org.springframework.scheduling.annotation.Async;
20 20 public class EmqxServiceImpl implements EmqxService {
21 21  
22 22 @Resource
23   - private IotDevicePropertyDataService iotDeviceDataService;
  23 + private IotDevicePropertyService iotDeviceDataService;
24 24  
25 25 // TODO 多线程处理消息
26 26 @Override
... ... @@ -35,8 +35,8 @@ public class EmqxServiceImpl implements EmqxService {
35 35 String deviceName = topic.split("/")[3];
36 36 String message = new String(mqttMessage.getPayload());
37 37 IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder()
38   - .productKey(productKey)
39   - .deviceName(deviceName)
  38 +// .productKey(productKey)
  39 +// .deviceName(deviceName)
40 40 // .properties(message) // TODO 芋艿:临时去掉,看看
41 41 .build();
42 42 iotDeviceDataService.saveDeviceData(createDTO);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java
... ... @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component;
9 9 import java.sql.Timestamp;
10 10 import java.util.Map;
11 11  
  12 +// TODO @haohao:这个还需要的么?
12 13 /**
13 14 * TaosAspect 是一个处理 Taos 数据库返回值的切面。
14 15 */
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.security.config;
  2 +
  3 +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
  4 +import cn.iocoder.yudao.module.iot.enums.ApiConstants;
  5 +import org.springframework.context.annotation.Bean;
  6 +import org.springframework.context.annotation.Configuration;
  7 +import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  8 +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
  9 +
  10 +/**
  11 + * IoT 模块的 Security 配置
  12 + */
  13 +@Configuration(proxyBeanMethods = false, value = "iotSecurityConfiguration")
  14 +public class SecurityConfiguration {
  15 +
  16 + @Bean("iotAuthorizeRequestsCustomizer")
  17 + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
  18 + return new AuthorizeRequestsCustomizer() {
  19 +
  20 + @Override
  21 + public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
  22 + // RPC 服务的安全配置
  23 + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
  24 + }
  25 +
  26 + };
  27 + }
  28 +
  29 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java
1 1 /**
2   - * 消息队列的消息
  2 + * 占位
3 3 */
4   -package cn.iocoder.yudao.module.iot.mq.message;
  4 +package cn.iocoder.yudao.module.iot.framework.security.core;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java
1 1 package cn.iocoder.yudao.module.iot.framework.tdengine.config;
2 2  
3   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
  3 +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
4 4 import lombok.RequiredArgsConstructor;
5 5 import lombok.extern.slf4j.Slf4j;
6 6 import org.springframework.boot.ApplicationArguments;
... ... @@ -17,7 +17,7 @@ import org.springframework.context.annotation.Configuration;
17 17 @RequiredArgsConstructor
18 18 public class TDengineTableInitConfiguration implements ApplicationRunner {
19 19  
20   - private final IotDeviceLogDataService deviceLogService;
  20 + private final IotDeviceLogService deviceLogService;
21 21  
22 22 @Override
23 23 public void run(ApplicationArguments args) {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java
... ... @@ -8,6 +8,7 @@ import org.springframework.stereotype.Component;
8 8 import javax.annotation.Resource;
9 9 import java.util.concurrent.TimeUnit;
10 10  
  11 +// TODO 芋艿:后续再看看
11 12 /**
12 13 * 插件实例 Job
13 14 *
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.mq.consumer.device;
  2 +
  3 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
  5 +import jakarta.annotation.Resource;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.context.event.EventListener;
  8 +import org.springframework.scheduling.annotation.Async;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +/**
  12 + * 针对 {@link IotDeviceMessage} 的消费者,记录设备日志
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Component
  17 +@Slf4j
  18 +public class IotDeviceLogMessageConsumer {
  19 +
  20 + @Resource
  21 + private IotDeviceLogService deviceLogService;
  22 +
  23 + @EventListener
  24 + @Async
  25 + public void onMessage(IotDeviceMessage message) {
  26 + log.info("[onMessage][消息内容({})]", message);
  27 + deviceLogService.createDeviceLog(message);
  28 + }
  29 +
  30 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java
1   -package cn.iocoder.yudao.module.iot.mq.consumer.deviceconsumer;
  1 +package cn.iocoder.yudao.module.iot.mq.consumer.device;
2 2  
3   -
4   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
5   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService;
6   -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
  3 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  4 +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
7 5 import lombok.extern.slf4j.Slf4j;
8 6 import org.springframework.context.event.EventListener;
9 7 import org.springframework.scheduling.annotation.Async;
... ... @@ -12,30 +10,25 @@ import org.springframework.stereotype.Component;
12 10 import javax.annotation.Resource;
13 11  
14 12 /**
15   - * 针对 {@link ThingModelMessage} 的消费者
  13 + * 针对 {@link IotDeviceMessage} 的消费者,记录设备属性
16 14 *
17 15 * @author alwayssuper
18 16 */
19 17 @Component
20 18 @Slf4j
21   -public class DeviceConsumer {
  19 +public class IotDevicePropertyMessageConsumer {
22 20  
23 21 @Resource
24   - private IotDeviceLogDataService deviceLogDataService;
25   - @Resource
26   - private IotDevicePropertyDataService deviceDataService;
  22 + private IotDevicePropertyService deviceDataService;
27 23  
28   - // TODO @芋艿:这块先用ThingModelMessage,后续看看用啥替代
29 24 @EventListener
30 25 @Async
31   - public void onMessage(ThingModelMessage message) {
  26 + public void onMessage(IotDeviceMessage message) {
32 27 log.info("[onMessage][消息内容({})]", message);
33   - //TODO:数据插入这块整体写的比较混乱,整体借鉴了浩浩哥之前写的逻辑,目前是通过模拟设备科插入数据了,但之前的逻辑有大量弃用的部分,后续看看怎么完善
34 28  
35   - // 设备数据记录
36   - deviceDataService.saveDeviceDataTest(message);
37 29 // 设备日志记录
38   - deviceLogDataService.saveDeviceLog(message);
  30 + // TODO @芋艿:重新写下
  31 +// deviceLogDataService.createDeviceLog(message);
39 32 }
40 33  
41 34 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java 0 → 100644
  1 +/**
  2 + * TODO 芋艿:未来实现一个 IotRuleMessageConsumer
  3 + */
  4 +package cn.iocoder.yudao.module.iot.mq.consumer.rule;
0 5 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java
1   -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
  1 +package cn.iocoder.yudao.module.iot.mq.message;
2 2  
  3 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  4 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
3 5 import lombok.AllArgsConstructor;
4 6 import lombok.Builder;
5 7 import lombok.Data;
6 8 import lombok.NoArgsConstructor;
7 9  
8   -import java.util.HashMap;
9   -import java.util.Map;
  10 +import java.time.LocalDateTime;
10 11  
11 12 /**
12   - * 物模型消息
  13 + * 设备消息
13 14 */
14 15 @Data
15 16 @NoArgsConstructor
16 17 @AllArgsConstructor
17 18 @Builder
18   -public class ThingModelMessage {
  19 +public class IotDeviceMessage {
19 20  
20 21 /**
21   - * 消息ID
  22 + * 请求编号
22 23 */
23   - private String id;
  24 + private String requestId;
24 25  
25 26 /**
26   - * 扩展功能的参数
  27 + * 设备信息
27 28 */
28   - private Object sys;
29   -
  29 + private String productKey;
30 30 /**
31   - * 请求方法 例如:thing.event.property.post
  31 + * 设备名称
32 32 */
33   - private String method;
34   -
  33 + private String deviceName;
35 34 /**
36   - * 请求参数
  35 + * 设备标识
37 36 */
38   - private Object params;
  37 + private String deviceKey;
39 38  
40 39 /**
41   - * 属性上报时间戳
  40 + * 消息类型
  41 + *
  42 + * 枚举 {@link IotDeviceMessageTypeEnum}
42 43 */
43   - private Long time;
44   -
  44 + private String type;
45 45 /**
46   - * 设备信息
  46 + * 标识符
  47 + *
  48 + * 枚举 {@link IotDeviceMessageIdentifierEnum}
47 49 */
48   - private String productKey;
  50 + private String identifier;
49 51  
50 52 /**
51   - * 设备名称
  53 + * 请求参数
  54 + *
  55 + * 例如说:属性上报的 properties、事件上报的 params
52 56 */
53   - private String deviceName;
  57 + private Object data;
54 58  
55 59 /**
56   - * 设备 key
  60 + * 上报时间
57 61 */
58   - private String deviceKey;
  62 + private LocalDateTime reportTime;
  63 +
  64 + // TODO @芋艿 code;
59 65  
60   - /**
61   - * 转换为 Map 类型
62   - */
63   - public Map<String, Object> dataToMap() {
64   - Map<String, Object> mapData = new HashMap<>();
65   - if (params instanceof Map) {
66   - ((Map<?, ?>) params).forEach((key, value) -> mapData.put(key.toString(), value));
67   - }
68   - return mapData;
69   - }
70 66 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java
1   -package cn.iocoder.yudao.module.iot.mq.producer.simulatesend;
  1 +package cn.iocoder.yudao.module.iot.mq.producer.device;
2 2  
3   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
  3 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
4 4 import jakarta.annotation.Resource;
5 5 import lombok.extern.slf4j.Slf4j;
6 6 import org.springframework.context.ApplicationContext;
7 7 import org.springframework.stereotype.Component;
8 8  
9   -// TODO @芋艿:@alwayssuper:是不是还没用起来哈?Producer 最好属于某个模块;
10 9 /**
11   - * SimulateSend 模拟设备上报的 Producer
  10 + * Iot 设备相关消息的 Producer
12 11 *
13 12 * @author alwayssuper
14 13 * @since 2024/12/17 16:35
15 14 */
16 15 @Slf4j
17 16 @Component
18   -public class SimulateSendProducer {
  17 +public class IotDeviceProducer {
19 18  
20 19 @Resource
21 20 private ApplicationContext applicationContext;
22 21  
23 22 /**
24   - * 发送 {@link ThingModelMessage} 消息
  23 + * 发送 {@link IotDeviceMessage} 消息
25 24 *
26 25 * @param thingModelMessage 物模型消息
27 26 */
28   - public void sendSimulateMessage(ThingModelMessage thingModelMessage) {
  27 + public void sendDeviceMessage(IotDeviceMessage thingModelMessage) {
29 28 applicationContext.publishEvent(thingModelMessage);
30 29 }
31 30  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java 0 → 100644
  1 +/**
  2 + * TODO 芋艿:临时占位
  3 + */
  4 +package cn.iocoder.yudao.module.iot.mq.producer;
0 5 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java
1   -package cn.iocoder.yudao.module.iot.service.device;
  1 +package cn.iocoder.yudao.module.iot.service.device.data;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
5 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
6 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
7   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
  6 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
8 7  
9 8 /**
10 9 * IoT 设备日志数据 Service 接口
11 10 *
12 11 * @author alwayssuper
13 12 */
14   -public interface IotDeviceLogDataService {
  13 +public interface IotDeviceLogService {
15 14  
16 15 /**
17 16 * 初始化 TDengine 超级表
... ... @@ -23,11 +22,9 @@ public interface IotDeviceLogDataService {
23 22 /**
24 23 * 插入设备日志
25 24 *
26   - * 当该设备第一次插入日志时,自动创建该设备的设备日志子表
27   - *
28   - * @param simulatorReqVO 设备日志模拟数据
  25 + * @param message 设备数据
29 26 */
30   - void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO);
  27 + void createDeviceLog(IotDeviceMessage message);
31 28  
32 29 /**
33 30 * 获得设备日志分页
... ... @@ -37,11 +34,4 @@ public interface IotDeviceLogDataService {
37 34 */
38 35 PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO);
39 36  
40   - /**
41   - * 插入设备日志
42   - *
43   - * @param message 设备数据
44   - */
45   - void saveDeviceLog(ThingModelMessage message);
46   -
47 37 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java
1   -package cn.iocoder.yudao.module.iot.service.device;
  1 +package cn.iocoder.yudao.module.iot.service.device.data;
2 2  
3   -import cn.hutool.json.JSONUtil;
  3 +import cn.hutool.core.util.IdUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
7 8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO;
8 9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
9   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
10 10 import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper;
  11 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
11 12 import jakarta.annotation.Resource;
12 13 import lombok.extern.slf4j.Slf4j;
13 14 import org.springframework.stereotype.Service;
... ... @@ -16,40 +17,36 @@ import org.springframework.validation.annotation.Validated;
16 17 import java.util.List;
17 18  
18 19 /**
19   - * IoT 设备日志数据 Service 实现了
  20 + * IoT 设备日志数据 Service 实现类
20 21 *
21 22 * @author alwayssuper
22 23 */
23 24 @Service
24 25 @Slf4j
25 26 @Validated
26   -public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{
  27 +public class IotDeviceLogServiceImpl implements IotDeviceLogService {
27 28  
28 29 @Resource
29 30 private IotDeviceLogDataMapper deviceLogDataMapper;
30 31  
31 32 @Override
32 33 public void defineDeviceLog() {
33   - if (deviceLogDataMapper.checkDeviceLogSTableExists() != null) {
34   - log.info("[defineDeviceLog][设备日志超级表已存在,跳过创建]");
  34 + if (StrUtil.isNotEmpty(deviceLogDataMapper.showDeviceLogSTable())) {
  35 + log.info("[defineDeviceLog][设备日志超级表已存在,创建跳过]");
35 36 return;
36 37 }
37 38  
38   - log.info("[defineDeviceLog][设备日志超级表不存在,开始创建]");
  39 + log.info("[defineDeviceLog][设备日志超级表不存在,创建开始...]");
39 40 deviceLogDataMapper.createDeviceLogSTable();
40   - log.info("[defineDeviceLog][设备日志超级表不存在,创建完成]");
  41 + log.info("[defineDeviceLog][设备日志超级表不存在,创建成功]");
41 42 }
42 43  
43 44 @Override
44   - public void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) {
45   - // 1. 转换请求对象为 DO
46   - IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class);
47   -
48   - // 2. 处理时间字段
49   -// iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now()
50   -
51   - // 3. 插入数据
52   - deviceLogDataMapper.insert(iotDeviceLogDO);
  45 + public void createDeviceLog(IotDeviceMessage message) {
  46 + IotDeviceLogDO log = BeanUtils.toBean(message, IotDeviceLogDO.class)
  47 + .setId(IdUtil.fastSimpleUUID())
  48 + .setContent(JsonUtils.toJsonString(message.getData()));
  49 + deviceLogDataMapper.insert(log);
53 50 }
54 51  
55 52 @Override
... ... @@ -60,18 +57,4 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{
60 57 return new PageResult<>(list, total);
61 58 }
62 59  
63   - @Override
64   - public void saveDeviceLog(ThingModelMessage message) {
65   - IotDeviceLogDO log = IotDeviceLogDO.builder()
66   - .id(message.getId())
67   - .deviceKey(message.getDeviceKey())
68   - .productKey(message.getProductKey())
69   - .type(message.getMethod()) // 消息类型,使用method作为类型 TODO 芋艿:在看看
70   - .subType("property") // TODO 芋艿:这块先写死,后续优化
71   - .content(JSONUtil.toJsonStr(message)) // TODO 芋艿:后续优化
72   - .reportTime(message.getTime()) // 上报时间 TODO 芋艿:在想想时间
73   - .build();
74   - deviceLogDataMapper.insert(log);
75   - }
76   -
77 60 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java
1   -package cn.iocoder.yudao.module.iot.service.device;
  1 +package cn.iocoder.yudao.module.iot.service.device.data;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
5 5 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
6 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
7 7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
8   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
9 8 import jakarta.validation.Valid;
10 9  
11 10 import java.util.List;
... ... @@ -16,7 +15,7 @@ import java.util.Map;
16 15 *
17 16 * @author 芋道源码
18 17 */
19   -public interface IotDevicePropertyDataService {
  18 +public interface IotDevicePropertyService {
20 19  
21 20 /**
22 21 * 定义设备属性数据的结构
... ... @@ -33,13 +32,6 @@ public interface IotDevicePropertyDataService {
33 32 void saveDeviceData(IotDevicePropertyReportReqDTO createDTO);
34 33  
35 34 /**
36   - * 保存设备数据
37   - *
38   - * @param thingModelMessage 设备数据
39   - */
40   - void saveDeviceDataTest(ThingModelMessage thingModelMessage);
41   -
42   - /**
43 35 * 模拟设备
44 36 *
45 37 * @param simulatorReqVO 设备数据
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java
1   -package cn.iocoder.yudao.module.iot.service.device;
  1 +package cn.iocoder.yudao.module.iot.service.device.data;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.date.DateUtil;
5 5 import cn.hutool.core.map.MapUtil;
6   -import cn.hutool.core.util.IdUtil;
7 6 import cn.hutool.core.util.StrUtil;
8 7 import cn.hutool.json.JSONObject;
9 8 import cn.hutool.json.JSONUtil;
... ... @@ -16,18 +15,15 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
16 15 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
17 16 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
18 17 import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
19   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
20 18 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
21 19 import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
22 20 import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper;
23   -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
24 21 import cn.iocoder.yudao.module.iot.enums.IotConstants;
25 22 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
26 23 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
27 24 import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
28   -import cn.iocoder.yudao.module.iot.mq.producer.simulatesend.SimulateSendProducer;
  25 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
29 26 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
30   -import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
31 27 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
32 28 import jakarta.annotation.Resource;
33 29 import jakarta.validation.Valid;
... ... @@ -53,7 +49,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_C
53 49 */
54 50 @Service
55 51 @Slf4j
56   -public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService {
  52 +public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
57 53  
58 54 /**
59 55 * 物模型的数据类型,与 TDengine 数据类型的映射关系
... ... @@ -76,25 +72,16 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
76 72 @Resource
77 73 private IotDeviceService deviceService;
78 74 @Resource
79   - private IotThingModelMessageService thingModelMessageService;
80   - @Resource
81 75 private IotThingModelService thingModelService;
82 76 @Resource
83 77 private IotProductService productService;
84 78  
85 79 @Resource
86   - private SimulateSendProducer simulateSendProducer;
87   -
88   - @Resource
89   - private TdEngineDMLMapper tdEngineDMLMapper;
90   -
91   - @Resource
92 80 private DeviceDataRedisDAO deviceDataRedisDAO;
93 81  
94 82 @Resource
95 83 private IotDevicePropertyDataMapper devicePropertyDataMapper;
96 84  
97   -
98 85 @Override
99 86 public void defineDevicePropertyData(Long productId) {
100 87 // 1.1 查询产品和物模型
... ... @@ -144,28 +131,8 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
144 131 // 1. 根据产品 key 和设备名称,获得设备信息
145 132 IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName());
146 133 // 2. 解析消息,保存数据
147   - JSONObject jsonObject = new JSONObject(createDTO.getParams());
  134 + JSONObject jsonObject = new JSONObject(createDTO.getProperties());
148 135 log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject);
149   - ThingModelMessage thingModelMessage = ThingModelMessage.builder()
150   - .id(jsonObject.getStr("id"))
151   - .sys(jsonObject.get("sys"))
152   - .method(jsonObject.getStr("method"))
153   - .params(jsonObject.get("params"))
154   - .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time"))
155   - .productKey(createDTO.getProductKey())
156   - .deviceName(createDTO.getDeviceName())
157   - .deviceKey(device.getDeviceKey())
158   - .build();
159   - thingModelMessageService.saveThingModelMessage(device, thingModelMessage);
160   - }
161   -
162   - //TODO @芋艿:后续捋一捋这块逻辑,先借鉴一下目前的代码
163   - @Override
164   - public void saveDeviceDataTest(ThingModelMessage thingModelMessage) {
165   - // 1. 根据产品 key 和设备名称,获得设备信息
166   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(thingModelMessage.getProductKey(), thingModelMessage.getDeviceName());
167   - // 2. 保存数据
168   - thingModelMessageService.saveThingModelMessage(device, thingModelMessage);
169 136 }
170 137  
171 138 //TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化
... ... @@ -182,20 +149,17 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe
182 149 throw exception(DEVICE_DATA_CONTENT_JSON_PARSE_ERROR);
183 150 }
184 151  
  152 + // TODO @芋艿:后续优化
185 153 // 3. 构建物模型消息
186   - ThingModelMessage thingModelMessage = ThingModelMessage.builder()
187   - .id(IdUtil.fastSimpleUUID()) // TODO:后续优化
188   - .sys(null)// TODO:这块先写死,后续优化
189   - .method("thing.event.property.post") // TODO:这块先写死,后续优化
190   - .params(contentJson) // 将 content 作为 params
191   - .time(simulatorReqVO.getReportTime()) // 使用上报时间
192   - .productKey(simulatorReqVO.getProductKey())
193   - .deviceName(device.getDeviceName())
194   - .deviceKey(device.getDeviceKey())
195   - .build();
  154 +// IotDeviceMessage thingModelMessage = IotDeviceMessage.builder()
  155 +// .params(contentJson) // 将 content 作为 params
  156 +// .time(simulatorReqVO.getReportTime()) // 使用上报时间
  157 +// .productKey(simulatorReqVO.getProductKey())
  158 +// .deviceName(device.getDeviceName())
  159 +// .build();
196 160  
197 161 // 4. 发送模拟消息
198   - simulateSendProducer.sendSimulateMessage(thingModelMessage);
  162 +// simulateSendProducer.sendDeviceMessage(thingModelMessage);
199 163 }
200 164  
201 165 @Override
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device.upstream;
  2 +
  3 +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
  4 +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
  5 +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
  6 +
  7 +/**
  8 + * 设备上行 Service 接口
  9 + *
  10 + * 目的:设备 -> 插件 -> 服务端
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface IotDeviceUpstreamService {
  15 +
  16 + /**
  17 + * 更新设备状态
  18 + *
  19 + * @param updateReqDTO 更新设备状态 DTO
  20 + */
  21 + void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO);
  22 +
  23 + /**
  24 + * 上报设备属性数据
  25 + *
  26 + * @param reportReqDTO 上报设备属性数据 DTO
  27 + */
  28 + void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO);
  29 +
  30 + /**
  31 + * 上报设备事件数据
  32 + *
  33 + * @param reportReqDTO 设备事件
  34 + */
  35 + void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO);
  36 +
  37 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device.upstream;
  2 +
  3 +import cn.hutool.core.util.IdUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  7 +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
  8 +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
  9 +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
  10 +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceUpstreamAbstractReqDTO;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  12 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
  13 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
  14 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  15 +import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer;
  16 +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
  17 +import jakarta.annotation.Resource;
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.stereotype.Service;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import java.time.LocalDateTime;
  23 +
  24 +/**
  25 + * 设备上行 Service 实现类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@Service
  30 +@Validated
  31 +@Slf4j
  32 +public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
  33 +
  34 + @Resource
  35 + private IotDeviceService deviceService;
  36 +
  37 + @Resource
  38 + private IotDeviceProducer deviceProducer;
  39 +
  40 + @Override
  41 + public void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
  42 + log.info("[updateDeviceStatus][更新设备状态: {}]", updateReqDTO);
  43 + // TODO 芋艿:插件状态
  44 + }
  45 +
  46 + @Override
  47 + public void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
  48 + // 1.1 获得设备
  49 + log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO);
  50 + IotDeviceDO device = getDevice(reportReqDTO);
  51 + if (device == null) {
  52 + log.error("[reportDevicePropertyData][设备({}/{})不存在]",
  53 + reportReqDTO.getProductKey(), reportReqDTO.getDeviceName());
  54 + return;
  55 + }
  56 + // 1.2 记录设备的最后时间
  57 + updateDeviceLastTime(device, reportReqDTO);
  58 +
  59 + // 2. 发送设备消息
  60 + IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class)
  61 + .setType(IotDeviceMessageTypeEnum.PROPERTY.getType())
  62 + .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())
  63 + .setData(reportReqDTO.getProperties());
  64 + sendDeviceMessage(message, device);
  65 + }
  66 +
  67 + @Override
  68 + public void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
  69 + log.info("[reportDeviceEventData][上报设备事件数据: {}]", reportReqDTO);
  70 +
  71 + // TODO 芋艿:待实现
  72 + }
  73 +
  74 + private IotDeviceDO getDevice(IotDeviceUpstreamAbstractReqDTO reqDTO) {
  75 + return TenantUtils.executeIgnore(() -> // 需要忽略租户,因为请求时,未带租户编号
  76 + deviceService.getDeviceByProductKeyAndDeviceName(reqDTO.getProductKey(), reqDTO.getDeviceName()));
  77 + }
  78 +
  79 + private void updateDeviceLastTime(IotDeviceDO deviceDO, IotDeviceUpstreamAbstractReqDTO reqDTO) {
  80 + // TODO 芋艿:插件状态
  81 + // TODO 芋艿:操作时间
  82 + }
  83 +
  84 + private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {
  85 + // 1. 完善消息
  86 + message.setDeviceKey(device.getDeviceKey());
  87 + if (StrUtil.isEmpty(message.getRequestId())) {
  88 + message.setRequestId(IdUtil.fastSimpleUUID());
  89 + }
  90 + if (message.getReportTime() == null) {
  91 + message.setReportTime(LocalDateTime.now());
  92 + }
  93 +
  94 + // 2. 发送消息
  95 + try {
  96 + deviceProducer.sendDeviceMessage(message);
  97 + log.info("[sendDeviceMessage][message({}) 发送消息成功]", message);
  98 + } catch (Exception e) {
  99 + log.error("[sendDeviceMessage][message({}) 发送消息失败]", message, e);
  100 + }
  101 + }
  102 +
  103 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
... ... @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc
8 8 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
9 9 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
10 10 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
11   -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
  11 +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
12 12 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
13 13 import jakarta.annotation.Resource;
14 14 import org.springframework.context.annotation.Lazy;
... ... @@ -35,7 +35,7 @@ public class IotProductServiceImpl implements IotProductService {
35 35  
36 36 @Resource
37 37 @Lazy // 延迟加载,解决循环依赖
38   - private IotDevicePropertyDataService devicePropertyDataService;
  38 + private IotDevicePropertyService devicePropertyDataService;
39 39  
40 40 @Override
41 41 public Long createProduct(IotProductSaveReqVO createReqVO) {
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java deleted
1   -package cn.iocoder.yudao.module.iot.service.tdengine;
2   -
3   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
4   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
5   -
6   -/**
7   - * 物模型消息 Service
8   - */
9   -public interface IotThingModelMessageService {
10   -
11   - /**
12   - * 保存物模型消息
13   - *
14   - * @param device 设备
15   - * @param thingModelMessage 物模型消息
16   - */
17   - void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage);
18   -
19   -
20   -
21   -}
22 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.iot.service.tdengine;
2   -
3   -import cn.hutool.core.date.DateUtil;
4   -import cn.hutool.core.util.StrUtil;
5   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6   -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
7   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
8   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
9   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
10   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;
11   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
12   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;
13   -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
14   -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
15   -import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
16   -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper;
17   -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper;
18   -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
19   -import cn.iocoder.yudao.module.iot.enums.IotConstants;
20   -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
21   -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
22   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
23   -import cn.iocoder.yudao.module.iot.service.product.IotProductService;
24   -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
25   -import jakarta.annotation.Resource;
26   -import lombok.extern.slf4j.Slf4j;
27   -import org.springframework.beans.factory.annotation.Value;
28   -import org.springframework.stereotype.Service;
29   -
30   -import java.util.*;
31   -import java.util.stream.Collectors;
32   -
33   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
34   -
35   -/**
36   - * 物模型消息 Service 实现类
37   - */
38   -@Slf4j
39   -@Service
40   -public class IotThingModelMessageServiceImpl implements IotThingModelMessageService {
41   -
42   - private static final String TAG_NOTE = "TAG";
43   - private static final String NOTE = "note";
44   - private static final String TIME = "time";
45   - private static final String DEVICE_KEY = "device_key";
46   - private static final String DEVICE_NAME = "device_name";
47   - private static final String PRODUCT_KEY = "product_key";
48   - private static final String DEVICE_TYPE = "device_type";
49   -
50   - @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
51   - private String url;
52   -
53   - @Resource
54   - private IotThingModelService iotThingModelService;
55   - @Resource
56   - private IotDeviceService iotDeviceService;
57   - @Resource
58   - private IotProductService productService;
59   -
60   - @Resource
61   - private TdEngineDDLMapper tdEngineDDLMapper;
62   - @Resource
63   - private TdEngineDMLMapper tdEngineDMLMapper;
64   -
65   - @Resource
66   - private IotDevicePropertyDataMapper iotDevicePropertyDataMapper;
67   -
68   - @Resource
69   - private DeviceDataRedisDAO deviceDataRedisDAO;
70   -
71   - // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感
72   - @Override
73   - @TenantIgnore
74   - public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) {
75   - // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态
76   - if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) {
77   - // 1.1 创建设备表
78   -// createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
79   - iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO()
80   - .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus()));
81   - }
82   -
83   - // 2. 获取设备属性并进行物模型校验,过滤非物模型属性
84   - Map<String, Object> params = thingModelMessage.dataToMap();
85   - List<IotThingModelDO> thingModelList = getValidThingModelList(thingModelMessage.getProductKey());
86   - if (thingModelList.isEmpty()) {
87   - return;
88   - }
89   -
90   - // 3. 过滤并收集有效的属性字段,缓存设备属性
91   - List<TdFieldDO> schemaFieldValues = filterAndCollectValidFields(params, thingModelList, device, thingModelMessage.getTime());
92   - if (schemaFieldValues.size() == 0) { // 没有字段,无需保存
93   - return;
94   - }
95   -
96   - // 4. 构建并保存设备属性数据
97   -// tdEngineDMLMapper.insertData(TdTableDO.builder()
98   -// .dataBaseName(getDatabaseName())
99   -// .tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()))
100   -// .columns(schemaFieldValues)
101   -// .build());
102   - // TODO:复用了旧逻辑,先过渡一下
103   - iotDevicePropertyDataMapper.insertDevicePropertyData(TdTableDO.builder()
104   - .productKey(device.getProductKey())
105   - .deviceKey(device.getDeviceKey())
106   - .columns(schemaFieldValues)
107   - .build());
108   - }
109   -
110   - private List<IotThingModelDO> getValidThingModelList(String productKey) {
111   - return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey),
112   - thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));
113   - }
114   -
115   -// @Override
116   -// @TenantIgnore
117   -// public void createSuperTable(Long productId) {
118   -// // 1. 查询产品
119   -// IotProductDO product = productService.getProduct(productId);
120   -// // 2. 创建日志超级表
121   -// tdThingModelMessageMapper.createSuperTable(product.getProductKey());
122   -//
123   -// // 2. 获取超级表的名称和数据库名称
124   -// // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法
125   -//// String databaseName = IotTdDatabaseUtils.getDatabaseName(url);
126   -//// String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey());
127   -////
128   -//// // 解析物模型,获取字段列表
129   -//// List<TdFieldDO> schemaFields = List.of(
130   -//// TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(),
131   -//// TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(),
132   -//// TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(),
133   -//// TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(),
134   -//// TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build()
135   -//// );
136   -//// // 设置超级表的标签
137   -//// List<TdFieldDO> tagsFields = List.of(
138   -//// TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build()
139   -//// );
140   -//// // 3. 创建超级表
141   -//// tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));
142   -// }
143   -
144   - private List<IotThingModelDO> getValidFunctionList(String productKey) {
145   - return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey),
146   - thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));
147   - }
148   -
149   - private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotThingModelDO> thingModelList, IotDeviceDO device, Long time) {
150   - // 1. 获取属性标识符集合
151   - Set<String> propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier);
152   -
153   - // 2. 构建属性标识符和属性的映射
154   - Map<String, IotThingModelDO> thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier);
155   -
156   - // 3. 过滤并收集有效的属性字段
157   - List<TdFieldDO> schemaFieldValues = new ArrayList<>();
158   - //TODO:新版本是使用ts字段
159   -// schemaFieldValues.add(new TdFieldDO(TIME, time));
160   - params.forEach((key, val) -> {
161   - if (propertyIdentifiers.contains(key)) {
162   - schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val));
163   - // 缓存设备属性
164   - // TODO @haohao:这个缓存的写入,可以使用的时候 cache 么?被动读
165   - setDeviceDataCache(device, thingModelMap.get(key), val, time);
166   - }
167   - });
168   - return schemaFieldValues;
169   - }
170   -
171   - /**
172   - * 缓存设备属性
173   - *
174   - * @param device 设备信息
175   - * @param iotThingModelDO 物模型属性
176   - * @param val 属性值
177   - * @param time 时间
178   - */
179   - private void setDeviceDataCache(IotDeviceDO device, IotThingModelDO iotThingModelDO, Object val, Long time) {
180   - IotDeviceDataDO deviceData = IotDeviceDataDO.builder()
181   - .productKey(device.getProductKey())
182   - .deviceName(device.getDeviceName())
183   - .identifier(iotThingModelDO.getIdentifier())
184   - .value(val != null ? val.toString() : null)
185   - .updateTime(DateUtil.toLocalDateTime(new Date(time)))
186   - .deviceId(device.getId())
187   - .thingModelId(iotThingModelDO.getId())
188   - .name(iotThingModelDO.getName())
189   - .dataType(iotThingModelDO.getProperty().getDataType())
190   - .build();
191   - deviceDataRedisDAO.set(deviceData);
192   - }
193   -
194   - /**
195   - * 创建设备数据表
196   - *
197   - * @param deviceType 设备类型
198   - * @param productKey 产品 Key
199   - * @param deviceName 设备名称
200   - * @param deviceKey 设备 Key
201   - */
202   - private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) {
203   - // 1. 获取超级表名和数据库名
204   - String superTableName = getProductPropertySTableName(deviceType, productKey);
205   - String dataBaseName = getDatabaseName();
206   -
207   - // 2. 获取超级表的结构信息
208   - List<Map<String, Object>> maps = tdEngineDDLMapper.describeSuperTable(new TdTableDO(dataBaseName, superTableName));
209   - List<TdFieldDO> tagsFieldValues = new ArrayList<>();
210   - if (maps != null) {
211   - // 2.1 过滤出 TAG 类型的字段
212   - List<Map<String, Object>> taggedNotesList = CollectionUtils.filterList(maps, map -> TAG_NOTE.equals(map.get(NOTE)));
213   -
214   - // 2.2 解析字段信息
215   - tagsFieldValues = FieldParser.parse(taggedNotesList.stream()
216   - .map(map -> List.of(map.get("field"), map.get("type"), map.get("length")))
217   - .collect(Collectors.toList()));
218   -
219   - // 2.3 设置 TAG 字段的值
220   - for (TdFieldDO tagsFieldValue : tagsFieldValues) {
221   - switch (tagsFieldValue.getFieldName()) {
222   - case PRODUCT_KEY -> tagsFieldValue.setFieldValue(productKey);
223   - case DEVICE_KEY -> tagsFieldValue.setFieldValue(deviceKey);
224   - case DEVICE_NAME -> tagsFieldValue.setFieldValue(deviceName);
225   - case DEVICE_TYPE -> tagsFieldValue.setFieldValue(deviceType);
226   - }
227   - }
228   - }
229   -
230   - // 3. 创建设备数据表
231   - String tableName = getDeviceTableName(productKey, deviceName);
232   - tdEngineDDLMapper.createTable(TdTableDO.builder().build()
233   - .setDataBaseName(dataBaseName)
234   - .setSuperTableName(superTableName)
235   - .setTableName(tableName)
236   - .setTags(tagsFieldValues));
237   - }
238   -
239   -
240   -
241   - /**
242   - * 获取数据库名称
243   - *
244   - * @return 数据库名称
245   - */
246   - private String getDatabaseName() {
247   - return StrUtil.subAfter(url, "/", true);
248   - }
249   -
250   - /**
251   - * 获取产品属性表名
252   - *
253   - * @param deviceType 设备类型
254   - * @param productKey 产品 Key
255   - * @return 产品属性表名
256   - */
257   - private static String getProductPropertySTableName(Integer deviceType, String productKey) {
258   - // TODO @haohao:枚举下,会好点哈。
259   - return switch (deviceType) {
260   - case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase();
261   - case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase();
262   - default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase();
263   - };
264   - }
265   -
266   - /**
267   - * 获取设备表名
268   - *
269   - * @param productKey 产品 Key
270   - * @param deviceName 设备名称
271   - * @return 设备表名
272   - */
273   - private static String getDeviceTableName(String productKey, String deviceName) {
274   - return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase());
275   - }
276   -
277   -}
278 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml
... ... @@ -9,8 +9,9 @@
9 9 ts TIMESTAMP,
10 10 id NCHAR(50),
11 11 product_key NCHAR(50),
  12 + device_name NCHAR(50),
12 13 type NCHAR(50),
13   - sub_type NCHAR(50),
  14 + identifier NCHAR(255),
14 15 content NCHAR(1024),
15 16 report_time TIMESTAMP
16 17 ) TAGS (
... ... @@ -18,18 +19,23 @@
18 19 )
19 20 </update>
20 21  
  22 + <select id="showDeviceLogSTable" resultType="String">
  23 + SHOW STABLES LIKE 'device_log'
  24 + </select>
  25 +
21 26 <insert id="insert">
22   - INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time)
  27 + INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, report_time)
23 28 USING device_log
24   - TAGS ('${log.deviceKey}')
  29 + TAGS ('${deviceKey}')
25 30 VALUES (
26 31 NOW,
27   - #{log.id},
28   - #{log.productKey},
29   - #{log.type},
30   - #{log.subType},
31   - #{log.content},
32   - #{log.reportTime}
  32 + #{id},
  33 + #{productKey},
  34 + #{deviceName},
  35 + #{type},
  36 + #{identifier},
  37 + #{content},
  38 + #{reportTime}
33 39 )
34 40 </insert>
35 41  
... ... @@ -51,6 +57,7 @@
51 57 LIMIT #{reqVO.pageSize} OFFSET #{reqVO.pageNo}
52 58 </select>
53 59  
  60 + <!-- TODO 芋艿:看看能不能复用 mybatis-plus 的 selectCount 方法 -->
54 61 <select id="selectCount" resultType="Long">
55 62 SELECT COUNT(*)
56 63 FROM device_log_${reqVO.deviceKey}
... ... @@ -67,12 +74,4 @@
67 74 </where>
68 75 </select>
69 76  
70   - <select id="checkDeviceLogSTableExists" resultType="Object">
71   - SHOW STABLES LIKE 'device_log'
72   - </select>
73   -
74   - <select id="checkDeviceLogTableExists" resultType="Object">
75   - SHOW TABLES LIKE 'device_log_${deviceKey}'
76   - </select>
77   -
78 77 </mapper>
79 78 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java
1 1 package cn.iocoder.yudao.module.iot.plugin.common.api;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  4 +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
5 5 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
6 6 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
7 7 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
8   -import lombok.RequiredArgsConstructor;
9 8 import lombok.extern.slf4j.Slf4j;
10 9 import org.springframework.web.client.RestTemplate;
11 10  
12 11 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
13 12  
14   -/**
15   - * 用于通过 {@link RestTemplate} 向远程 IoT 服务发送设备数据相关的请求,
16   - * 包括设备状态更新、事件数据上报、属性数据上报等操作。
17   - */
  13 +// TODO @haohao:类注释,写一下,比较好
  14 +// TODO @haohao:类名要改下
18 15 @Slf4j
19   -@RequiredArgsConstructor
20   -public class DeviceDataApiClient implements DeviceDataApi {
  16 +public class DeviceDataApiClient implements IotDeviceUpstreamApi {
21 17  
22   - /**
23   - * 用于发送 HTTP 请求的工具
24   - */
25   - private final RestTemplate restTemplate;
  18 + public static final String URL_PREFIX = "/rpc-api/iot/device/upstream";
26 19  
27   - /**
28   - * 远程 IoT 服务的基础 URL
29   - * 例如:http://127.0.0.1:8080
30   - */
  20 + private final RestTemplate restTemplate;
31 21 private final String deviceDataUrl;
32 22  
  23 + // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来
  24 + // TODO @haohao:可以用 lombok 简化
  25 + public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) {
  26 + this.restTemplate = restTemplate;
  27 + this.deviceDataUrl = deviceDataUrl;
  28 + }
  29 +
33 30 // TODO @haohao:返回结果,不用 CommonResult 哈。
34 31 @Override
35 32 public CommonResult<Boolean> updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
36   - String url = deviceDataUrl + "/rpc-api/iot/device-data/update-status";
  33 + String url = deviceDataUrl + URL_PREFIX + "/update-status";
37 34 return doPost(url, updateReqDTO, "updateDeviceStatus");
38 35 }
39 36  
40 37 @Override
41 38 public CommonResult<Boolean> reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
42   - String url = deviceDataUrl + "/rpc-api/iot/device-data/report-event";
  39 + String url = deviceDataUrl + URL_PREFIX + "/report-event";
43 40 return doPost(url, reportReqDTO, "reportDeviceEventData");
44 41 }
45 42  
46 43 @Override
47 44 public CommonResult<Boolean> reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
48   - String url = deviceDataUrl + "/rpc-api/iot/device-data/report-property";
  45 + String url = deviceDataUrl + URL_PREFIX + "/report-property";
49 46 return doPost(url, reportReqDTO, "reportDevicePropertyData");
50 47 }
51 48  
52   -
53   - /**
54   - * 发送 GET 请求
55   - *
56   - * @param <T> 请求体类型
57   - * @param url 请求 URL
58   - * @param requestBody 请求体
59   - * @param actionName 操作名称
60   - * @return 响应结果
61   - */
62   - private <T> CommonResult<Boolean> doGet(String url, T requestBody, String actionName) {
63   - log.info("[{}] Sending request to URL: {}", actionName, url);
64   - try {
65   - CommonResult<?> response = restTemplate.getForObject(url, CommonResult.class);
66   - if (response != null && response.isSuccess()) {
67   - return success(true);
68   - } else {
69   - log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response);
70   - return CommonResult.error(500, "Request failed");
71   - }
72   - } catch (Exception e) {
73   - log.error("[{}] Error sending request to URL: {}", actionName, url, e);
74   - return CommonResult.error(400, "Request error: " + e.getMessage());
75   - }
76   - }
77   -
  49 + // TODO @haohao:未来可能有 get 类型哈
78 50 /**
79   - * 发送 POST 请求
80   - *
81   - * @param <T> 请求体类型
82   - * @param url 请求 URL
83   - * @param requestBody 请求体
84   - * @param actionName 操作名称
85   - * @return 响应结果
  51 + * 将与远程服务交互的通用逻辑抽取成一个私有方法
86 52 */
87 53 private <T> CommonResult<Boolean> doPost(String url, T requestBody, String actionName) {
88 54 log.info("[{}] Sending request to URL: {}", actionName, url);
89 55 try {
90   - CommonResult<?> response = restTemplate.postForObject(url, requestBody, CommonResult.class);
91   - if (response != null && response.isSuccess()) {
92   - return success(true);
93   - } else {
94   - log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response);
95   - return CommonResult.error(500, "Request failed");
96   - }
  56 + // 这里指定返回类型为 CommonResult<?>,根据后台服务返回的实际结构做调整
  57 + restTemplate.postForObject(url, requestBody, CommonResult.class);
  58 + // TODO @haohao:check 结果,是否成功
  59 + return success(true);
97 60 } catch (Exception e) {
98 61 log.error("[{}] Error sending request to URL: {}", actionName, url, e);
99 62 return CommonResult.error(400, "Request error: " + e.getMessage());
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java
1 1 package cn.iocoder.yudao.module.iot.plugin.common.config;
2 2  
3   -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  3 +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
4 4 import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient;
5 5 import org.springframework.beans.factory.annotation.Value;
6 6 import org.springframework.boot.autoconfigure.AutoConfiguration;
... ... @@ -43,7 +43,7 @@ public class YudaoDeviceDataApiAutoConfiguration {
43 43 * @return DeviceDataApi 实例
44 44 */
45 45 @Bean
46   - public DeviceDataApi deviceDataApi(RestTemplate restTemplate) {
  46 + public IotDeviceUpstreamApi deviceDataApi(RestTemplate restTemplate) {
47 47 return new DeviceDataApiClient(restTemplate, deviceDataUrl);
48 48 }
49 49  
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java
1 1 package cn.iocoder.yudao.module.iot.plugin;
2 2  
3 3 import cn.hutool.extra.spring.SpringUtil;
4   -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  4 +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
5 5 import lombok.extern.slf4j.Slf4j;
6 6 import org.pf4j.Plugin;
7 7 import org.pf4j.PluginWrapper;
... ... @@ -27,7 +27,7 @@ public class EmqxPlugin extends Plugin {
27 27 executorService = Executors.newSingleThreadExecutor();
28 28 }
29 29  
30   - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
  30 + IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class);
31 31 if (deviceDataApi == null) {
32 32 log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!");
33 33 return;
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java
... ... @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.config;
2 2  
3 3 import cn.hutool.core.lang.Assert;
4 4 import cn.hutool.extra.spring.SpringUtil;
5   -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  5 +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
6 6 import lombok.extern.slf4j.Slf4j;
7 7 import org.pf4j.PluginWrapper;
8 8 import org.pf4j.spring.SpringPlugin;
... ... @@ -64,7 +64,7 @@ public class HttpVertxPlugin extends SpringPlugin {
64 64 protected void prepareRefresh() {
65 65 // 在刷新容器前注册主程序中的 Bean
66 66 ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
67   - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
  67 + IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class);
68 68 beanFactory.registerSingleton("deviceDataApi", deviceDataApi);
69 69 super.prepareRefresh();
70 70 }
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java
1 1 package cn.iocoder.yudao.module.iot.plugin.http.config;
2 2  
3   -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  3 +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
4 4 import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler;
5 5 import io.vertx.core.Vertx;
6 6 import io.vertx.ext.web.Router;
... ... @@ -61,7 +61,7 @@ public class HttpVertxPluginConfiguration {
61 61 * @return HttpVertxHandler 实例
62 62 */
63 63 @Bean
64   - public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) {
  64 + public HttpVertxHandler httpVertxHandler(IotDeviceUpstreamApi deviceDataApi) {
65 65 return new HttpVertxHandler(deviceDataApi);
66 66 }
67 67  
... ...
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java
... ... @@ -2,19 +2,21 @@ package cn.iocoder.yudao.module.iot.plugin.http.service;
2 2  
3 3 import cn.hutool.json.JSONObject;
4 4 import cn.hutool.json.JSONUtil;
5   -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  5 +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
6 6 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
7 7 import io.vertx.core.Handler;
8 8 import io.vertx.ext.web.RequestBody;
9 9 import io.vertx.ext.web.RoutingContext;
10 10 import lombok.extern.slf4j.Slf4j;
11 11  
  12 +import java.util.Map;
  13 +
12 14 @Slf4j
13 15 public class HttpVertxHandler implements Handler<RoutingContext> {
14 16  
15   - private final DeviceDataApi deviceDataApi;
  17 + private final IotDeviceUpstreamApi deviceDataApi;
16 18  
17   - public HttpVertxHandler(DeviceDataApi deviceDataApi) {
  19 + public HttpVertxHandler(IotDeviceUpstreamApi deviceDataApi) {
18 20 this.deviceDataApi = deviceDataApi;
19 21 }
20 22  
... ... @@ -23,6 +25,7 @@ public class HttpVertxHandler implements Handler&lt;RoutingContext&gt; {
23 25 String productKey = ctx.pathParam("productKey");
24 26 String deviceName = ctx.pathParam("deviceName");
25 27  
  28 + // TODO @haohao:requestBody.asJsonObject() 貌似天然就是 json 对象哈?
26 29 RequestBody requestBody = ctx.body();
27 30 JSONObject jsonData;
28 31 try {
... ... @@ -43,7 +46,7 @@ public class HttpVertxHandler implements Handler&lt;RoutingContext&gt; {
43 46 IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder()
44 47 .productKey(productKey)
45 48 .deviceName(deviceName)
46   - .params(jsonData)
  49 + .properties((Map<String, Object>) requestBody.asJsonObject().getMap().get("properties"))
47 50 .build();
48 51  
49 52 deviceDataApi.reportDevicePropertyData(reportReqDTO);
... ...