From d70c6986d588f09e2c4784c2e3dbbc6714f299bb Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Jun 2025 17:15:00 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E3=80=90IoT=20=E7=89=A9=E8=81=94?= =?UTF-8?q?=E7=BD=91=E3=80=91=E8=AE=BE=E5=A4=87=E6=B6=88=E6=81=AF=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E7=9A=84=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/enums/DateIntervalEnum.java | 1 + .../common/util/date/LocalDateTimeUtils.java | 26 ++++++- .../statistics/IotStatisticsController.http | 11 +++ .../statistics/IotStatisticsController.java | 36 +++++----- .../vo/IotStatisticsDeviceMessageReqVO.java | 27 ++++++++ ...sticsDeviceMessageSummaryByDateRespVO.java | 19 ++++++ ...tStatisticsDeviceMessageSummaryRespVO.java | 19 ------ .../statistics/vo/IotStatisticsReqVO.java | 21 ------ .../dal/tdengine/IotDeviceMessageMapper.java | 16 +---- .../service/device/IotDeviceServiceImpl.java | 12 +--- .../message/IotDeviceMessageService.java | 23 +++++++ .../message/IotDeviceMessageServiceImpl.java | 46 +++++++++++-- .../device/property/IotDeviceLogService.java | 48 ------------- .../property/IotDeviceLogServiceImpl.java | 68 ------------------- .../mapper/device/IotDeviceMessageMapper.xml | 47 ++----------- 15 files changed, 173 insertions(+), 247 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java index 8d6a791784..d266eadc6a 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java @@ -16,6 +16,7 @@ import java.util.Arrays; @AllArgsConstructor public enum DateIntervalEnum implements ArrayValuable { + HOUR(0, "小时"), // 特殊:字典里,暂时不会有这个枚举!!!因为大多数情况下,用不到这个间隔 DAY(1, "天"), WEEK(2, "周"), MONTH(3, "月"), diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java index cc2d4e204d..f40758334d 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java @@ -8,6 +8,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; +import java.sql.Timestamp; import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters; import java.util.ArrayList; import java.util.List; -import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN; -import static cn.hutool.core.date.DatePattern.createFormatter; +import static cn.hutool.core.date.DatePattern.*; /** * 时间工具类,用于 {@link java.time.LocalDateTime} @@ -82,6 +82,21 @@ public class LocalDateTimeUtils { return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)}; } + /** + * 判指定断时间,是否在该时间范围内 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param time 指定时间 + * @return 是否 + */ + public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, Timestamp time) { + if (startTime == null || endTime == null || time == null) { + return false; + } + return LocalDateTimeUtil.isIn(LocalDateTimeUtil.of(time), startTime, endTime); + } + /** * 判指定断时间,是否在该时间范围内 * @@ -234,6 +249,11 @@ public class LocalDateTimeUtils { // 2. 循环,生成时间范围 List timeRanges = new ArrayList<>(); switch (intervalEnum) { + case HOUR: + while (startTime.isBefore(endTime)) { + timeRanges.add(new LocalDateTime[]{startTime, startTime.plusHours(1).minusNanos(1)}); + startTime = startTime.plusHours(1); + } case DAY: while (startTime.isBefore(endTime)) { timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); @@ -297,6 +317,8 @@ public class LocalDateTimeUtils { // 2. 循环,生成时间范围 switch (intervalEnum) { + case HOUR: + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN); case DAY: return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); case WEEK: diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http new file mode 100644 index 0000000000..b8cb6b544f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http @@ -0,0 +1,11 @@ +### 请求 /iot/statistics/get-device-message-summary-by-date 接口(小时) +GET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=0×[0]=2025-06-13 00:00:00×[1]=2025-06-14 23:59:59 +Content-Type: application/json +tenant-id: {{adminTenantId}} +Authorization: Bearer {{token}} + +### 请求 /iot/statistics/get-device-message-summary-by-date 接口(天) +GET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=1×[0]=2025-06-13 00:00:00×[1]=2025-06-14 23:59:59 +Content-Type: application/json +tenant-id: {{adminTenantId}} +Authorization: Bearer {{token}} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java index d623846109..22837c48ba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -1,12 +1,13 @@ package cn.iocoder.yudao.module.iot.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsReqVO; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.property.IotDeviceLogService; +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import io.swagger.v3.oas.annotations.Operation; @@ -19,9 +20,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; +import java.util.List; import java.util.Map; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.*; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管理后台 - IoT 数据统计") @RestController @@ -36,24 +38,23 @@ public class IotStatisticsController { @Resource private IotProductService productService; @Resource - private IotDeviceLogService deviceLogService; + private IotDeviceMessageService deviceMessageService; @GetMapping("/get-summary") - @Operation(summary = "获取 IoT 数据统计") - public CommonResult getIotStatisticsSummary(){ + @Operation(summary = "获取全局的数据统计") + public CommonResult getStatisticsSummary(){ IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO(); // 1.1 获取总数 respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null)); respVO.setProductCount(productService.getProductCount(null)); respVO.setDeviceCount(deviceService.getDeviceCount(null)); - respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null)); + respVO.setDeviceMessageCount(deviceMessageService.getDeviceMessageCount(null)); // 1.2 获取今日新增数量 - // TODO @super:使用 LocalDateTimeUtils.getToday() - LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); + LocalDateTime todayStart = LocalDateTimeUtils.getToday(); respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart)); respVO.setProductTodayCount(productService.getProductCount(todayStart)); respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart)); - respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart)); + respVO.setDeviceMessageTodayCount(deviceMessageService.getDeviceMessageCount(todayStart)); // 2. 获取各个品类下设备数量统计 respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap()); @@ -66,14 +67,11 @@ public class IotStatisticsController { return success(respVO); } - // TODO @super:要不干掉 IotStatisticsReqVO 参数,直接使用 @RequestParam 接收,简单一些。 - @GetMapping("/get-log-summary") - @Operation(summary = "获取 IoT 设备上下行消息数据统计") - public CommonResult getIotStatisticsDeviceMessageSummary( - @Valid IotStatisticsReqVO reqVO) { - return success(new IotStatisticsDeviceMessageSummaryRespVO() - .setDownstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())) - .setDownstreamCounts((deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())))); + @GetMapping("/get-device-message-summary-by-date") + @Operation(summary = "获取设备消息的数据统计") + public CommonResult> getDeviceMessageSummaryByDate( + @Valid IotStatisticsDeviceMessageReqVO reqVO) { + return success(deviceMessageService.getDeviceMessageSummaryByDate(reqVO)); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java new file mode 100644 index 0000000000..73f83e70cf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; + +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO") +@Data +public class IotStatisticsDeviceMessageReqVO { + + @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") + private Integer interval; + + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Size(min = 2, max = 2, message = "请选择时间范围") + private LocalDateTime[] times; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java new file mode 100644 index 0000000000..9c605dd341 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO") +@Data +public class IotStatisticsDeviceMessageSummaryByDateRespVO { + + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") + private String time; + + @Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer upstreamCount; + + @Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private Integer downstreamCount; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java deleted file mode 100644 index 15d2abccc6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - IoT 设备上下行消息数量统计 Response VO") -@Data -public class IotStatisticsDeviceMessageSummaryRespVO { - - @Schema(description = "每小时上行数据数量统计") - private List> upstreamCounts; - - @Schema(description = "每小时下行数据数量统计") - private List> downstreamCounts; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java deleted file mode 100644 index 741f77f3a4..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 统计 Request VO") -@Data -public class IotStatisticsReqVO { - - // TODO @super:前端传递的时候,还是通过 startTime 和 endTime 传递。后端转成 Long - - @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1658486600000") - @NotNull(message = "查询起始时间不能为空") - private Long startTime; - - @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1758486600000") - @NotNull(message = "查询结束时间不能为空") - private Long endTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java index 1b1dba12ae..9c35269113 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java @@ -57,20 +57,10 @@ public interface IotDeviceMessageMapper { */ Long selectCountByCreateTime(@Param("createTime") Long createTime); - // TODO @super:1)上行、下行,不写在 mapper 里,而是通过参数传递,这样,selectDeviceLogUpCountByHour、selectDeviceLogDownCountByHour 可以合并; - // TODO @super:2)不能只基于 identifier 来计算,而是要 type + identifier 成对 /** - * 查询每个小时设备上行消息数量 + * 按照时间范围(小时),统计设备的消息数量 */ - List> selectDeviceLogUpCountByHour(@Param("deviceId") Long deviceId, - @Param("startTime") Long startTime, - @Param("endTime") Long endTime); - - /** - * 查询每个小时设备下行消息数量 - */ - List> selectDeviceLogDownCountByHour(@Param("deviceId") Long deviceId, - @Param("startTime") Long startTime, - @Param("endTime") Long endTime); + List> selectDeviceMessageCountGroupByDate(@Param("startTime") Long startTime, + @Param("endTime") Long endTime); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index a14b905835..a9ffbf3644 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; @@ -15,6 +14,7 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; @@ -22,7 +22,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import jakarta.annotation.Resource; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; @@ -321,15 +320,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectCountByGroupId(groupId); } - /** - * 生成 deviceKey - * - * @return 生成的 deviceKey - */ - private String generateDeviceKey() { - return RandomUtil.randomString(16); - } - /** * 生成 deviceSecret * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java index 51d3124f98..4e0d761299 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java @@ -2,10 +2,16 @@ package cn.iocoder.yudao.module.iot.service.device.message; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; +import javax.annotation.Nullable; +import java.time.LocalDateTime; +import java.util.List; + /** * IoT 设备消息 Service 接口 * @@ -57,4 +63,21 @@ public interface IotDeviceMessageService { */ PageResult getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO); + /** + * 获得设备消息数量 + * + * @param createTime 创建时间,如果为空,则统计所有消息数量 + * @return 消息数量 + */ + Long getDeviceMessageCount(@Nullable LocalDateTime createTime); + + /** + * 获取设备消息的数据统计 + * + * @param reqVO 统计请求 + * @return 设备消息的数据统计 + */ + List getDeviceMessageSummaryByDate( + IotStatisticsDeviceMessageReqVO reqVO); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java index e3230c147b..1ae04e291a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java @@ -1,12 +1,17 @@ package cn.iocoder.yudao.module.iot.service.device.message; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -26,9 +31,13 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.sql.Timestamp; import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL; /** @@ -47,19 +56,19 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { private IotDevicePropertyService devicePropertyService; @Resource - private IotDeviceMessageMapper deviceLogMapper; + private IotDeviceMessageMapper deviceMessageMapper; @Resource private IotDeviceMessageProducer deviceMessageProducer; @Override public void defineDeviceMessageStable() { - if (StrUtil.isNotEmpty(deviceLogMapper.showSTable())) { + if (StrUtil.isNotEmpty(deviceMessageMapper.showSTable())) { log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]"); return; } log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]"); - deviceLogMapper.createSTable(); + deviceMessageMapper.createSTable(); log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]"); } @@ -74,7 +83,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { if (messageDO.getData() != null) { messageDO.setData(JsonUtils.toJsonString(messageDO.getData())); } - deviceLogMapper.insert(messageDO); + deviceMessageMapper.insert(messageDO); } @Override @@ -192,7 +201,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @Override public PageResult getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO) { try { - IPage page = deviceLogMapper.selectPage( + IPage page = deviceMessageMapper.selectPage( new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); return new PageResult<>(page.getRecords(), page.getTotal()); } catch (Exception exception) { @@ -203,6 +212,33 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { } } + @Override + public Long getDeviceMessageCount(LocalDateTime createTime) { + return deviceMessageMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); + } + + @Override + public List getDeviceMessageSummaryByDate( + IotStatisticsDeviceMessageReqVO reqVO) { + // 1. 按小时统计,获取分项统计数据 + List> countList = deviceMessageMapper.selectDeviceMessageCountGroupByDate( + LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[0]), LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[1])); + + // 2. 按照日期间隔,合并数据 + List timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); + return convertList(timeRanges, times -> { + Integer upstreamCount = countList.stream() + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time"))) + .mapToInt(value -> MapUtil.getInt(value, "upstream_count")).sum(); + Integer downstreamCount = countList.stream() + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time"))) + .mapToInt(value -> MapUtil.getInt(value, "downstream_count")).sum(); + return new IotStatisticsDeviceMessageSummaryByDateRespVO() + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) + .setUpstreamCount(upstreamCount).setDownstreamCount(downstreamCount); + }); + } + private IotDeviceMessageServiceImpl getSelf() { return SpringUtil.getBean(getClass()); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java deleted file mode 100644 index 42785e6399..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.property; - -import javax.annotation.Nullable; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -/** - * IoT 设备消息数据 Service 接口 - * - * @author alwayssuper - */ -public interface IotDeviceLogService { - - /** - * 获得设备消息数量 - * - * @param createTime 创建时间,如果为空,则统计所有消息数量 - * @return 消息数量 - */ - Long getDeviceLogCount(@Nullable LocalDateTime createTime); - - // TODO @super:deviceKey 是不是用不上哈? - /** - * 获得每个小时设备上行消息数量统计 - * - * @param deviceKey 设备标识 - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return key: 时间戳, value: 消息数量 - */ - List> getDeviceLogUpCountByHour(@Nullable String deviceKey, - @Nullable Long startTime, - @Nullable Long endTime); - - /** - * 获得每个小时设备下行消息数量统计 - * - * @param deviceKey 设备标识 - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return key: 时间戳, value: 消息数量 - */ - List> getDeviceLogDownCountByHour(@Nullable String deviceKey, - @Nullable Long startTime, - @Nullable Long endTime); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java deleted file mode 100644 index 311a3bb988..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.property; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.sql.Timestamp; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * IoT 设备日志数据 Service 实现类 - * - * @author alwayssuper - */ -@Service -@Slf4j -@Validated -public class IotDeviceLogServiceImpl implements IotDeviceLogService { - - @Resource - private IotDeviceMessageMapper deviceLogMapper; - - @Override - public Long getDeviceLogCount(LocalDateTime createTime) { - return deviceLogMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); - } - - // TODO @super:加一个参数,Boolean upstream:true 上行,false 下行,null 不过滤 - @Override - public List> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { - // TODO @super:不能只基于数据库统计。因为有一些小时,可能出现没数据的情况,导致前端展示的图是不全的。可以参考 CrmStatisticsCustomerService 来实现 - // TODO @芋艿:这里实现,需要调整 - List> list = deviceLogMapper.selectDeviceLogUpCountByHour(0L, startTime, endTime); - return list.stream() - .map(map -> { - // 从Timestamp获取时间戳 - Timestamp timestamp = (Timestamp) map.get("time"); - Long timeMillis = timestamp.getTime(); - // 消息数量转换 - Integer count = ((Number) map.get("data")).intValue(); - return MapUtil.of(timeMillis, count); - }) - .collect(Collectors.toList()); - } - - // TODO @super:getDeviceLogDownCountByHour 融合到 getDeviceLogUpCountByHour - @Override - public List> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) { - // TODO @芋艿:这里实现,需要调整 - List> list = deviceLogMapper.selectDeviceLogDownCountByHour(0L, startTime, endTime); - return list.stream() - .map(map -> { - Timestamp timestamp = (Timestamp) map.get("time"); - Long timeMillis = timestamp.getTime(); - Integer count = ((Number) map.get("data")).intValue(); - return MapUtil.of(timeMillis, count); - }) - .collect(Collectors.toList()); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml index 6c4c032e18..11da5cda8c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml @@ -69,19 +69,12 @@ - SELECT - TIMETRUNCATE(ts, 1h) as time, - COUNT(*) as data - FROM - - - device_message_${deviceId} - - - device_message - - + TIMETRUNCATE(ts, 1h) AS time, + SUM(CASE WHEN upstream = true THEN 1 ELSE 0 END) AS upstream_count, + SUM(CASE WHEN upstream = false THEN 1 ELSE 0 END) AS downstream_count + FROM device_message AND ts >= #{startTime} @@ -89,36 +82,8 @@ AND ts <= #{endTime} - AND upstream = true - GROUP BY TIMETRUNCATE(ts, 1h) - ORDER BY time ASC - - - \ No newline at end of file