Commit d70c6986d588f09e2c4784c2e3dbbc6714f299bb
1 parent
6a06f520
feat:【IoT 物联网】设备消息统计的代码优化
Showing
15 changed files
with
173 additions
and
247 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/DateIntervalEnum.java
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; |
| 8 | 8 | import cn.hutool.core.util.StrUtil; |
| 9 | 9 | import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; |
| 10 | 10 | |
| 11 | +import java.sql.Timestamp; | |
| 11 | 12 | import java.time.*; |
| 12 | 13 | import java.time.format.DateTimeFormatter; |
| 13 | 14 | import java.time.format.DateTimeParseException; |
| ... | ... | @@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters; |
| 16 | 17 | import java.util.ArrayList; |
| 17 | 18 | import java.util.List; |
| 18 | 19 | |
| 19 | -import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN; | |
| 20 | -import static cn.hutool.core.date.DatePattern.createFormatter; | |
| 20 | +import static cn.hutool.core.date.DatePattern.*; | |
| 21 | 21 | |
| 22 | 22 | /** |
| 23 | 23 | * 时间工具类,用于 {@link java.time.LocalDateTime} |
| ... | ... | @@ -90,6 +90,21 @@ public class LocalDateTimeUtils { |
| 90 | 90 | * @param time 指定时间 |
| 91 | 91 | * @return 是否 |
| 92 | 92 | */ |
| 93 | + public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, Timestamp time) { | |
| 94 | + if (startTime == null || endTime == null || time == null) { | |
| 95 | + return false; | |
| 96 | + } | |
| 97 | + return LocalDateTimeUtil.isIn(LocalDateTimeUtil.of(time), startTime, endTime); | |
| 98 | + } | |
| 99 | + | |
| 100 | + /** | |
| 101 | + * 判指定断时间,是否在该时间范围内 | |
| 102 | + * | |
| 103 | + * @param startTime 开始时间 | |
| 104 | + * @param endTime 结束时间 | |
| 105 | + * @param time 指定时间 | |
| 106 | + * @return 是否 | |
| 107 | + */ | |
| 93 | 108 | public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { |
| 94 | 109 | if (startTime == null || endTime == null || time == null) { |
| 95 | 110 | return false; |
| ... | ... | @@ -234,6 +249,11 @@ public class LocalDateTimeUtils { |
| 234 | 249 | // 2. 循环,生成时间范围 |
| 235 | 250 | List<LocalDateTime[]> timeRanges = new ArrayList<>(); |
| 236 | 251 | switch (intervalEnum) { |
| 252 | + case HOUR: | |
| 253 | + while (startTime.isBefore(endTime)) { | |
| 254 | + timeRanges.add(new LocalDateTime[]{startTime, startTime.plusHours(1).minusNanos(1)}); | |
| 255 | + startTime = startTime.plusHours(1); | |
| 256 | + } | |
| 237 | 257 | case DAY: |
| 238 | 258 | while (startTime.isBefore(endTime)) { |
| 239 | 259 | timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); |
| ... | ... | @@ -297,6 +317,8 @@ public class LocalDateTimeUtils { |
| 297 | 317 | |
| 298 | 318 | // 2. 循环,生成时间范围 |
| 299 | 319 | switch (intervalEnum) { |
| 320 | + case HOUR: | |
| 321 | + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN); | |
| 300 | 322 | case DAY: |
| 301 | 323 | return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); |
| 302 | 324 | case WEEK: | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.http
0 → 100644
| 1 | +### 请求 /iot/statistics/get-device-message-summary-by-date 接口(小时) | |
| 2 | +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 | |
| 3 | +Content-Type: application/json | |
| 4 | +tenant-id: {{adminTenantId}} | |
| 5 | +Authorization: Bearer {{token}} | |
| 6 | + | |
| 7 | +### 请求 /iot/statistics/get-device-message-summary-by-date 接口(天) | |
| 8 | +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 | |
| 9 | +Content-Type: application/json | |
| 10 | +tenant-id: {{adminTenantId}} | |
| 11 | +Authorization: Bearer {{token}} | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.controller.admin.statistics; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO; | |
| 5 | -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsReqVO; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | |
| 5 | +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; | |
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; | |
| 6 | 7 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; |
| 7 | 8 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; |
| 8 | 9 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 9 | -import cn.iocoder.yudao.module.iot.service.device.property.IotDeviceLogService; | |
| 10 | +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; | |
| 10 | 11 | import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; |
| 11 | 12 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 12 | 13 | import io.swagger.v3.oas.annotations.Operation; |
| ... | ... | @@ -19,9 +20,10 @@ import org.springframework.web.bind.annotation.RequestMapping; |
| 19 | 20 | import org.springframework.web.bind.annotation.RestController; |
| 20 | 21 | |
| 21 | 22 | import java.time.LocalDateTime; |
| 23 | +import java.util.List; | |
| 22 | 24 | import java.util.Map; |
| 23 | 25 | |
| 24 | -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.*; | |
| 26 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 25 | 27 | |
| 26 | 28 | @Tag(name = "管理后台 - IoT 数据统计") |
| 27 | 29 | @RestController |
| ... | ... | @@ -36,24 +38,23 @@ public class IotStatisticsController { |
| 36 | 38 | @Resource |
| 37 | 39 | private IotProductService productService; |
| 38 | 40 | @Resource |
| 39 | - private IotDeviceLogService deviceLogService; | |
| 41 | + private IotDeviceMessageService deviceMessageService; | |
| 40 | 42 | |
| 41 | 43 | @GetMapping("/get-summary") |
| 42 | - @Operation(summary = "获取 IoT 数据统计") | |
| 43 | - public CommonResult<IotStatisticsSummaryRespVO> getIotStatisticsSummary(){ | |
| 44 | + @Operation(summary = "获取全局的数据统计") | |
| 45 | + public CommonResult<IotStatisticsSummaryRespVO> getStatisticsSummary(){ | |
| 44 | 46 | IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO(); |
| 45 | 47 | // 1.1 获取总数 |
| 46 | 48 | respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null)); |
| 47 | 49 | respVO.setProductCount(productService.getProductCount(null)); |
| 48 | 50 | respVO.setDeviceCount(deviceService.getDeviceCount(null)); |
| 49 | - respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null)); | |
| 51 | + respVO.setDeviceMessageCount(deviceMessageService.getDeviceMessageCount(null)); | |
| 50 | 52 | // 1.2 获取今日新增数量 |
| 51 | - // TODO @super:使用 LocalDateTimeUtils.getToday() | |
| 52 | - LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); | |
| 53 | + LocalDateTime todayStart = LocalDateTimeUtils.getToday(); | |
| 53 | 54 | respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart)); |
| 54 | 55 | respVO.setProductTodayCount(productService.getProductCount(todayStart)); |
| 55 | 56 | respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart)); |
| 56 | - respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart)); | |
| 57 | + respVO.setDeviceMessageTodayCount(deviceMessageService.getDeviceMessageCount(todayStart)); | |
| 57 | 58 | |
| 58 | 59 | // 2. 获取各个品类下设备数量统计 |
| 59 | 60 | respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap()); |
| ... | ... | @@ -66,14 +67,11 @@ public class IotStatisticsController { |
| 66 | 67 | return success(respVO); |
| 67 | 68 | } |
| 68 | 69 | |
| 69 | - // TODO @super:要不干掉 IotStatisticsReqVO 参数,直接使用 @RequestParam 接收,简单一些。 | |
| 70 | - @GetMapping("/get-log-summary") | |
| 71 | - @Operation(summary = "获取 IoT 设备上下行消息数据统计") | |
| 72 | - public CommonResult<IotStatisticsDeviceMessageSummaryRespVO> getIotStatisticsDeviceMessageSummary( | |
| 73 | - @Valid IotStatisticsReqVO reqVO) { | |
| 74 | - return success(new IotStatisticsDeviceMessageSummaryRespVO() | |
| 75 | - .setDownstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())) | |
| 76 | - .setDownstreamCounts((deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())))); | |
| 70 | + @GetMapping("/get-device-message-summary-by-date") | |
| 71 | + @Operation(summary = "获取设备消息的数据统计") | |
| 72 | + public CommonResult<List<IotStatisticsDeviceMessageSummaryByDateRespVO>> getDeviceMessageSummaryByDate( | |
| 73 | + @Valid IotStatisticsDeviceMessageReqVO reqVO) { | |
| 74 | + return success(deviceMessageService.getDeviceMessageSummaryByDate(reqVO)); | |
| 77 | 75 | } |
| 78 | 76 | |
| 79 | 77 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import jakarta.validation.constraints.Size; | |
| 7 | +import lombok.Data; | |
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 9 | + | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | + | |
| 12 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 13 | + | |
| 14 | +@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO") | |
| 15 | +@Data | |
| 16 | +public class IotStatisticsDeviceMessageReqVO { | |
| 17 | + | |
| 18 | + @Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 19 | + @InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}") | |
| 20 | + private Integer interval; | |
| 21 | + | |
| 22 | + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) | |
| 23 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 24 | + @Size(min = 2, max = 2, message = "请选择时间范围") | |
| 25 | + private LocalDateTime[] times; | |
| 26 | + | |
| 27 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryByDateRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO") | |
| 7 | +@Data | |
| 8 | +public class IotStatisticsDeviceMessageSummaryByDateRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401") | |
| 11 | + private String time; | |
| 12 | + | |
| 13 | + @Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | |
| 14 | + private Integer upstreamCount; | |
| 15 | + | |
| 16 | + @Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") | |
| 17 | + private Integer downstreamCount; | |
| 18 | + | |
| 19 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; | |
| 2 | - | |
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | -import lombok.Data; | |
| 5 | - | |
| 6 | -import java.util.List; | |
| 7 | -import java.util.Map; | |
| 8 | - | |
| 9 | -@Schema(description = "管理后台 - IoT 设备上下行消息数量统计 Response VO") | |
| 10 | -@Data | |
| 11 | -public class IotStatisticsDeviceMessageSummaryRespVO { | |
| 12 | - | |
| 13 | - @Schema(description = "每小时上行数据数量统计") | |
| 14 | - private List<Map<Long, Integer>> upstreamCounts; | |
| 15 | - | |
| 16 | - @Schema(description = "每小时下行数据数量统计") | |
| 17 | - private List<Map<Long, Integer>> downstreamCounts; | |
| 18 | - | |
| 19 | -} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; | |
| 2 | - | |
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | -import jakarta.validation.constraints.NotNull; | |
| 5 | -import lombok.Data; | |
| 6 | - | |
| 7 | -@Schema(description = "管理后台 - IoT 统计 Request VO") | |
| 8 | -@Data | |
| 9 | -public class IotStatisticsReqVO { | |
| 10 | - | |
| 11 | - // TODO @super:前端传递的时候,还是通过 startTime 和 endTime 传递。后端转成 Long | |
| 12 | - | |
| 13 | - @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1658486600000") | |
| 14 | - @NotNull(message = "查询起始时间不能为空") | |
| 15 | - private Long startTime; | |
| 16 | - | |
| 17 | - @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1758486600000") | |
| 18 | - @NotNull(message = "查询结束时间不能为空") | |
| 19 | - private Long endTime; | |
| 20 | - | |
| 21 | -} |
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 { |
| 57 | 57 | */ |
| 58 | 58 | Long selectCountByCreateTime(@Param("createTime") Long createTime); |
| 59 | 59 | |
| 60 | - // TODO @super:1)上行、下行,不写在 mapper 里,而是通过参数传递,这样,selectDeviceLogUpCountByHour、selectDeviceLogDownCountByHour 可以合并; | |
| 61 | - // TODO @super:2)不能只基于 identifier 来计算,而是要 type + identifier 成对 | |
| 62 | 60 | /** |
| 63 | - * 查询每个小时设备上行消息数量 | |
| 61 | + * 按照时间范围(小时),统计设备的消息数量 | |
| 64 | 62 | */ |
| 65 | - List<Map<String, Object>> selectDeviceLogUpCountByHour(@Param("deviceId") Long deviceId, | |
| 66 | - @Param("startTime") Long startTime, | |
| 67 | - @Param("endTime") Long endTime); | |
| 68 | - | |
| 69 | - /** | |
| 70 | - * 查询每个小时设备下行消息数量 | |
| 71 | - */ | |
| 72 | - List<Map<String, Object>> selectDeviceLogDownCountByHour(@Param("deviceId") Long deviceId, | |
| 73 | - @Param("startTime") Long startTime, | |
| 74 | - @Param("endTime") Long endTime); | |
| 63 | + List<Map<String, Object>> selectDeviceMessageCountGroupByDate(@Param("startTime") Long startTime, | |
| 64 | + @Param("endTime") Long endTime); | |
| 75 | 65 | |
| 76 | 66 | } | ... | ... |
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; |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.IdUtil; |
| 5 | 5 | import cn.hutool.core.util.ObjUtil; |
| 6 | -import cn.hutool.core.util.RandomUtil; | |
| 7 | 6 | import cn.hutool.core.util.StrUtil; |
| 8 | 7 | import cn.hutool.extra.spring.SpringUtil; |
| 9 | 8 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| ... | ... | @@ -15,6 +14,7 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 15 | 14 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; |
| 16 | 15 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 17 | 16 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; |
| 17 | +import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; | |
| 18 | 18 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 19 | 19 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; |
| 20 | 20 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| ... | ... | @@ -22,7 +22,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; |
| 22 | 22 | import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; |
| 23 | 23 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 24 | 24 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 25 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; | |
| 26 | 25 | import jakarta.annotation.Resource; |
| 27 | 26 | import jakarta.validation.ConstraintViolationException; |
| 28 | 27 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -322,15 +321,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 322 | 321 | } |
| 323 | 322 | |
| 324 | 323 | /** |
| 325 | - * 生成 deviceKey | |
| 326 | - * | |
| 327 | - * @return 生成的 deviceKey | |
| 328 | - */ | |
| 329 | - private String generateDeviceKey() { | |
| 330 | - return RandomUtil.randomString(16); | |
| 331 | - } | |
| 332 | - | |
| 333 | - /** | |
| 334 | 324 | * 生成 deviceSecret |
| 335 | 325 | * |
| 336 | 326 | * @return 生成的 deviceSecret | ... | ... |
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; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; |
| 5 | +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; | |
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; | |
| 5 | 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 7 | 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; |
| 8 | 10 | |
| 11 | +import javax.annotation.Nullable; | |
| 12 | +import java.time.LocalDateTime; | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 9 | 15 | /** |
| 10 | 16 | * IoT 设备消息 Service 接口 |
| 11 | 17 | * |
| ... | ... | @@ -57,4 +63,21 @@ public interface IotDeviceMessageService { |
| 57 | 63 | */ |
| 58 | 64 | PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO); |
| 59 | 65 | |
| 66 | + /** | |
| 67 | + * 获得设备消息数量 | |
| 68 | + * | |
| 69 | + * @param createTime 创建时间,如果为空,则统计所有消息数量 | |
| 70 | + * @return 消息数量 | |
| 71 | + */ | |
| 72 | + Long getDeviceMessageCount(@Nullable LocalDateTime createTime); | |
| 73 | + | |
| 74 | + /** | |
| 75 | + * 获取设备消息的数据统计 | |
| 76 | + * | |
| 77 | + * @param reqVO 统计请求 | |
| 78 | + * @return 设备消息的数据统计 | |
| 79 | + */ | |
| 80 | + List<IotStatisticsDeviceMessageSummaryByDateRespVO> getDeviceMessageSummaryByDate( | |
| 81 | + IotStatisticsDeviceMessageReqVO reqVO); | |
| 82 | + | |
| 60 | 83 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.service.device.message; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 4 | +import cn.hutool.core.map.MapUtil; | |
| 3 | 5 | import cn.hutool.core.util.StrUtil; |
| 4 | 6 | import cn.hutool.extra.spring.SpringUtil; |
| 5 | 7 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 6 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | |
| 7 | 10 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 8 | 11 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 9 | 12 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; |
| 13 | +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; | |
| 14 | +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; | |
| 10 | 15 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 11 | 16 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; |
| 12 | 17 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| ... | ... | @@ -26,9 +31,13 @@ import org.springframework.scheduling.annotation.Async; |
| 26 | 31 | import org.springframework.stereotype.Service; |
| 27 | 32 | import org.springframework.validation.annotation.Validated; |
| 28 | 33 | |
| 34 | +import java.sql.Timestamp; | |
| 29 | 35 | import java.time.LocalDateTime; |
| 36 | +import java.util.List; | |
| 37 | +import java.util.Map; | |
| 30 | 38 | |
| 31 | 39 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 40 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 32 | 41 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL; |
| 33 | 42 | |
| 34 | 43 | /** |
| ... | ... | @@ -47,19 +56,19 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { |
| 47 | 56 | private IotDevicePropertyService devicePropertyService; |
| 48 | 57 | |
| 49 | 58 | @Resource |
| 50 | - private IotDeviceMessageMapper deviceLogMapper; | |
| 59 | + private IotDeviceMessageMapper deviceMessageMapper; | |
| 51 | 60 | |
| 52 | 61 | @Resource |
| 53 | 62 | private IotDeviceMessageProducer deviceMessageProducer; |
| 54 | 63 | |
| 55 | 64 | @Override |
| 56 | 65 | public void defineDeviceMessageStable() { |
| 57 | - if (StrUtil.isNotEmpty(deviceLogMapper.showSTable())) { | |
| 66 | + if (StrUtil.isNotEmpty(deviceMessageMapper.showSTable())) { | |
| 58 | 67 | log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]"); |
| 59 | 68 | return; |
| 60 | 69 | } |
| 61 | 70 | log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]"); |
| 62 | - deviceLogMapper.createSTable(); | |
| 71 | + deviceMessageMapper.createSTable(); | |
| 63 | 72 | log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]"); |
| 64 | 73 | } |
| 65 | 74 | |
| ... | ... | @@ -74,7 +83,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { |
| 74 | 83 | if (messageDO.getData() != null) { |
| 75 | 84 | messageDO.setData(JsonUtils.toJsonString(messageDO.getData())); |
| 76 | 85 | } |
| 77 | - deviceLogMapper.insert(messageDO); | |
| 86 | + deviceMessageMapper.insert(messageDO); | |
| 78 | 87 | } |
| 79 | 88 | |
| 80 | 89 | @Override |
| ... | ... | @@ -192,7 +201,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { |
| 192 | 201 | @Override |
| 193 | 202 | public PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO) { |
| 194 | 203 | try { |
| 195 | - IPage<IotDeviceMessageDO> page = deviceLogMapper.selectPage( | |
| 204 | + IPage<IotDeviceMessageDO> page = deviceMessageMapper.selectPage( | |
| 196 | 205 | new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); |
| 197 | 206 | return new PageResult<>(page.getRecords(), page.getTotal()); |
| 198 | 207 | } catch (Exception exception) { |
| ... | ... | @@ -203,6 +212,33 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { |
| 203 | 212 | } |
| 204 | 213 | } |
| 205 | 214 | |
| 215 | + @Override | |
| 216 | + public Long getDeviceMessageCount(LocalDateTime createTime) { | |
| 217 | + return deviceMessageMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); | |
| 218 | + } | |
| 219 | + | |
| 220 | + @Override | |
| 221 | + public List<IotStatisticsDeviceMessageSummaryByDateRespVO> getDeviceMessageSummaryByDate( | |
| 222 | + IotStatisticsDeviceMessageReqVO reqVO) { | |
| 223 | + // 1. 按小时统计,获取分项统计数据 | |
| 224 | + List<Map<String, Object>> countList = deviceMessageMapper.selectDeviceMessageCountGroupByDate( | |
| 225 | + LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[0]), LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[1])); | |
| 226 | + | |
| 227 | + // 2. 按照日期间隔,合并数据 | |
| 228 | + List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval()); | |
| 229 | + return convertList(timeRanges, times -> { | |
| 230 | + Integer upstreamCount = countList.stream() | |
| 231 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time"))) | |
| 232 | + .mapToInt(value -> MapUtil.getInt(value, "upstream_count")).sum(); | |
| 233 | + Integer downstreamCount = countList.stream() | |
| 234 | + .filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time"))) | |
| 235 | + .mapToInt(value -> MapUtil.getInt(value, "downstream_count")).sum(); | |
| 236 | + return new IotStatisticsDeviceMessageSummaryByDateRespVO() | |
| 237 | + .setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval())) | |
| 238 | + .setUpstreamCount(upstreamCount).setDownstreamCount(downstreamCount); | |
| 239 | + }); | |
| 240 | + } | |
| 241 | + | |
| 206 | 242 | private IotDeviceMessageServiceImpl getSelf() { |
| 207 | 243 | return SpringUtil.getBean(getClass()); |
| 208 | 244 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.device.property; | |
| 2 | - | |
| 3 | -import javax.annotation.Nullable; | |
| 4 | -import java.time.LocalDateTime; | |
| 5 | -import java.util.List; | |
| 6 | -import java.util.Map; | |
| 7 | - | |
| 8 | -/** | |
| 9 | - * IoT 设备消息数据 Service 接口 | |
| 10 | - * | |
| 11 | - * @author alwayssuper | |
| 12 | - */ | |
| 13 | -public interface IotDeviceLogService { | |
| 14 | - | |
| 15 | - /** | |
| 16 | - * 获得设备消息数量 | |
| 17 | - * | |
| 18 | - * @param createTime 创建时间,如果为空,则统计所有消息数量 | |
| 19 | - * @return 消息数量 | |
| 20 | - */ | |
| 21 | - Long getDeviceLogCount(@Nullable LocalDateTime createTime); | |
| 22 | - | |
| 23 | - // TODO @super:deviceKey 是不是用不上哈? | |
| 24 | - /** | |
| 25 | - * 获得每个小时设备上行消息数量统计 | |
| 26 | - * | |
| 27 | - * @param deviceKey 设备标识 | |
| 28 | - * @param startTime 开始时间 | |
| 29 | - * @param endTime 结束时间 | |
| 30 | - * @return key: 时间戳, value: 消息数量 | |
| 31 | - */ | |
| 32 | - List<Map<Long, Integer>> getDeviceLogUpCountByHour(@Nullable String deviceKey, | |
| 33 | - @Nullable Long startTime, | |
| 34 | - @Nullable Long endTime); | |
| 35 | - | |
| 36 | - /** | |
| 37 | - * 获得每个小时设备下行消息数量统计 | |
| 38 | - * | |
| 39 | - * @param deviceKey 设备标识 | |
| 40 | - * @param startTime 开始时间 | |
| 41 | - * @param endTime 结束时间 | |
| 42 | - * @return key: 时间戳, value: 消息数量 | |
| 43 | - */ | |
| 44 | - List<Map<Long, Integer>> getDeviceLogDownCountByHour(@Nullable String deviceKey, | |
| 45 | - @Nullable Long startTime, | |
| 46 | - @Nullable Long endTime); | |
| 47 | - | |
| 48 | -} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.device.property; | |
| 2 | - | |
| 3 | -import cn.hutool.core.date.LocalDateTimeUtil; | |
| 4 | -import cn.hutool.core.map.MapUtil; | |
| 5 | -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper; | |
| 6 | -import jakarta.annotation.Resource; | |
| 7 | -import lombok.extern.slf4j.Slf4j; | |
| 8 | -import org.springframework.stereotype.Service; | |
| 9 | -import org.springframework.validation.annotation.Validated; | |
| 10 | - | |
| 11 | -import java.sql.Timestamp; | |
| 12 | -import java.time.LocalDateTime; | |
| 13 | -import java.util.List; | |
| 14 | -import java.util.Map; | |
| 15 | -import java.util.stream.Collectors; | |
| 16 | - | |
| 17 | -/** | |
| 18 | - * IoT 设备日志数据 Service 实现类 | |
| 19 | - * | |
| 20 | - * @author alwayssuper | |
| 21 | - */ | |
| 22 | -@Service | |
| 23 | -@Slf4j | |
| 24 | -@Validated | |
| 25 | -public class IotDeviceLogServiceImpl implements IotDeviceLogService { | |
| 26 | - | |
| 27 | - @Resource | |
| 28 | - private IotDeviceMessageMapper deviceLogMapper; | |
| 29 | - | |
| 30 | - @Override | |
| 31 | - public Long getDeviceLogCount(LocalDateTime createTime) { | |
| 32 | - return deviceLogMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); | |
| 33 | - } | |
| 34 | - | |
| 35 | - // TODO @super:加一个参数,Boolean upstream:true 上行,false 下行,null 不过滤 | |
| 36 | - @Override | |
| 37 | - public List<Map<Long, Integer>> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { | |
| 38 | - // TODO @super:不能只基于数据库统计。因为有一些小时,可能出现没数据的情况,导致前端展示的图是不全的。可以参考 CrmStatisticsCustomerService 来实现 | |
| 39 | - // TODO @芋艿:这里实现,需要调整 | |
| 40 | - List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogUpCountByHour(0L, startTime, endTime); | |
| 41 | - return list.stream() | |
| 42 | - .map(map -> { | |
| 43 | - // 从Timestamp获取时间戳 | |
| 44 | - Timestamp timestamp = (Timestamp) map.get("time"); | |
| 45 | - Long timeMillis = timestamp.getTime(); | |
| 46 | - // 消息数量转换 | |
| 47 | - Integer count = ((Number) map.get("data")).intValue(); | |
| 48 | - return MapUtil.of(timeMillis, count); | |
| 49 | - }) | |
| 50 | - .collect(Collectors.toList()); | |
| 51 | - } | |
| 52 | - | |
| 53 | - // TODO @super:getDeviceLogDownCountByHour 融合到 getDeviceLogUpCountByHour | |
| 54 | - @Override | |
| 55 | - public List<Map<Long, Integer>> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) { | |
| 56 | - // TODO @芋艿:这里实现,需要调整 | |
| 57 | - List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogDownCountByHour(0L, startTime, endTime); | |
| 58 | - return list.stream() | |
| 59 | - .map(map -> { | |
| 60 | - Timestamp timestamp = (Timestamp) map.get("time"); | |
| 61 | - Long timeMillis = timestamp.getTime(); | |
| 62 | - Integer count = ((Number) map.get("data")).intValue(); | |
| 63 | - return MapUtil.of(timeMillis, count); | |
| 64 | - }) | |
| 65 | - .collect(Collectors.toList()); | |
| 66 | - } | |
| 67 | - | |
| 68 | -} |
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml
| ... | ... | @@ -69,45 +69,12 @@ |
| 69 | 69 | </where> |
| 70 | 70 | </select> |
| 71 | 71 | |
| 72 | - <select id="selectDeviceLogUpCountByHour" resultType="java.util.Map"> | |
| 72 | + <select id="selectDeviceMessageCountGroupByDate" resultType="java.util.Map"> | |
| 73 | 73 | SELECT |
| 74 | - TIMETRUNCATE(ts, 1h) as time, | |
| 75 | - COUNT(*) as data | |
| 76 | - FROM | |
| 77 | - <choose> | |
| 78 | - <when test="deviceId != null"> | |
| 79 | - device_message_${deviceId} | |
| 80 | - </when> | |
| 81 | - <otherwise> | |
| 82 | - device_message | |
| 83 | - </otherwise> | |
| 84 | - </choose> | |
| 85 | - <where> | |
| 86 | - <if test="startTime != null"> | |
| 87 | - AND ts >= #{startTime} | |
| 88 | - </if> | |
| 89 | - <if test="endTime != null"> | |
| 90 | - AND ts <= #{endTime} | |
| 91 | - </if> | |
| 92 | - AND upstream = true | |
| 93 | - </where> | |
| 94 | - GROUP BY TIMETRUNCATE(ts, 1h) | |
| 95 | - ORDER BY time ASC | |
| 96 | - </select> | |
| 97 | - | |
| 98 | - <select id="selectDeviceLogDownCountByHour" resultType="java.util.Map"> | |
| 99 | - SELECT | |
| 100 | - TIMETRUNCATE(ts, 1h) as time, | |
| 101 | - COUNT(*) as data | |
| 102 | - FROM | |
| 103 | - <choose> | |
| 104 | - <when test="deviceId != null"> | |
| 105 | - device_message_${deviceId} | |
| 106 | - </when> | |
| 107 | - <otherwise> | |
| 108 | - device_message | |
| 109 | - </otherwise> | |
| 110 | - </choose> | |
| 74 | + TIMETRUNCATE(ts, 1h) AS time, | |
| 75 | + SUM(CASE WHEN upstream = true THEN 1 ELSE 0 END) AS upstream_count, | |
| 76 | + SUM(CASE WHEN upstream = false THEN 1 ELSE 0 END) AS downstream_count | |
| 77 | + FROM device_message | |
| 111 | 78 | <where> |
| 112 | 79 | <if test="startTime != null"> |
| 113 | 80 | AND ts >= #{startTime} |
| ... | ... | @@ -115,10 +82,8 @@ |
| 115 | 82 | <if test="endTime != null"> |
| 116 | 83 | AND ts <= #{endTime} |
| 117 | 84 | </if> |
| 118 | - AND upstream = false | |
| 119 | 85 | </where> |
| 120 | - GROUP BY TIMETRUNCATE(ts, 1h) | |
| 121 | - ORDER BY time ASC | |
| 86 | + GROUP BY time | |
| 122 | 87 | </select> |
| 123 | 88 | |
| 124 | 89 | </mapper> |
| 125 | 90 | \ No newline at end of file | ... | ... |