Commit 8089f3a319efb919fce809b8e144c3fcc0694b4f
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
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
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
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
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
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
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<RoutingContext> { |
| 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<RoutingContext> { |
| 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); | ... | ... |