Commit d70c6986d588f09e2c4784c2e3dbbc6714f299bb

Authored by YunaiV
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
@@ -16,6 +16,7 @@ import java.util.Arrays; @@ -16,6 +16,7 @@ import java.util.Arrays;
16 @AllArgsConstructor 16 @AllArgsConstructor
17 public enum DateIntervalEnum implements ArrayValuable<Integer> { 17 public enum DateIntervalEnum implements ArrayValuable<Integer> {
18 18
  19 + HOUR(0, "小时"), // 特殊:字典里,暂时不会有这个枚举!!!因为大多数情况下,用不到这个间隔
19 DAY(1, "天"), 20 DAY(1, "天"),
20 WEEK(2, "周"), 21 WEEK(2, "周"),
21 MONTH(3, "月"), 22 MONTH(3, "月"),
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,6 +8,7 @@ import cn.hutool.core.lang.Assert;
8 import cn.hutool.core.util.StrUtil; 8 import cn.hutool.core.util.StrUtil;
9 import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum; 9 import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
10 10
  11 +import java.sql.Timestamp;
11 import java.time.*; 12 import java.time.*;
12 import java.time.format.DateTimeFormatter; 13 import java.time.format.DateTimeFormatter;
13 import java.time.format.DateTimeParseException; 14 import java.time.format.DateTimeParseException;
@@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters; @@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters;
16 import java.util.ArrayList; 17 import java.util.ArrayList;
17 import java.util.List; 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 * 时间工具类,用于 {@link java.time.LocalDateTime} 23 * 时间工具类,用于 {@link java.time.LocalDateTime}
@@ -90,6 +90,21 @@ public class LocalDateTimeUtils { @@ -90,6 +90,21 @@ public class LocalDateTimeUtils {
90 * @param time 指定时间 90 * @param time 指定时间
91 * @return 是否 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 public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) { 108 public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, String time) {
94 if (startTime == null || endTime == null || time == null) { 109 if (startTime == null || endTime == null || time == null) {
95 return false; 110 return false;
@@ -234,6 +249,11 @@ public class LocalDateTimeUtils { @@ -234,6 +249,11 @@ public class LocalDateTimeUtils {
234 // 2. 循环,生成时间范围 249 // 2. 循环,生成时间范围
235 List<LocalDateTime[]> timeRanges = new ArrayList<>(); 250 List<LocalDateTime[]> timeRanges = new ArrayList<>();
236 switch (intervalEnum) { 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 case DAY: 257 case DAY:
238 while (startTime.isBefore(endTime)) { 258 while (startTime.isBefore(endTime)) {
239 timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)}); 259 timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
@@ -297,6 +317,8 @@ public class LocalDateTimeUtils { @@ -297,6 +317,8 @@ public class LocalDateTimeUtils {
297 317
298 // 2. 循环,生成时间范围 318 // 2. 循环,生成时间范围
299 switch (intervalEnum) { 319 switch (intervalEnum) {
  320 + case HOUR:
  321 + return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
300 case DAY: 322 case DAY:
301 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN); 323 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
302 case WEEK: 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&times[0]=2025-06-13 00:00:00&times[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&times[0]=2025-06-13 00:00:00&times[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 package cn.iocoder.yudao.module.iot.controller.admin.statistics; 1 package cn.iocoder.yudao.module.iot.controller.admin.statistics;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 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 import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO;
7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 8 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
8 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 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 import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; 11 import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;
11 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 12 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
12 import io.swagger.v3.oas.annotations.Operation; 13 import io.swagger.v3.oas.annotations.Operation;
@@ -19,9 +20,10 @@ import org.springframework.web.bind.annotation.RequestMapping; @@ -19,9 +20,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
19 import org.springframework.web.bind.annotation.RestController; 20 import org.springframework.web.bind.annotation.RestController;
20 21
21 import java.time.LocalDateTime; 22 import java.time.LocalDateTime;
  23 +import java.util.List;
22 import java.util.Map; 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 @Tag(name = "管理后台 - IoT 数据统计") 28 @Tag(name = "管理后台 - IoT 数据统计")
27 @RestController 29 @RestController
@@ -36,24 +38,23 @@ public class IotStatisticsController { @@ -36,24 +38,23 @@ public class IotStatisticsController {
36 @Resource 38 @Resource
37 private IotProductService productService; 39 private IotProductService productService;
38 @Resource 40 @Resource
39 - private IotDeviceLogService deviceLogService; 41 + private IotDeviceMessageService deviceMessageService;
40 42
41 @GetMapping("/get-summary") 43 @GetMapping("/get-summary")
42 - @Operation(summary = "获取 IoT 数据统计")  
43 - public CommonResult<IotStatisticsSummaryRespVO> getIotStatisticsSummary(){ 44 + @Operation(summary = "获取全局的数据统计")
  45 + public CommonResult<IotStatisticsSummaryRespVO> getStatisticsSummary(){
44 IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO(); 46 IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO();
45 // 1.1 获取总数 47 // 1.1 获取总数
46 respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null)); 48 respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null));
47 respVO.setProductCount(productService.getProductCount(null)); 49 respVO.setProductCount(productService.getProductCount(null));
48 respVO.setDeviceCount(deviceService.getDeviceCount(null)); 50 respVO.setDeviceCount(deviceService.getDeviceCount(null));
49 - respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null)); 51 + respVO.setDeviceMessageCount(deviceMessageService.getDeviceMessageCount(null));
50 // 1.2 获取今日新增数量 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 respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart)); 54 respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart));
54 respVO.setProductTodayCount(productService.getProductCount(todayStart)); 55 respVO.setProductTodayCount(productService.getProductCount(todayStart));
55 respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart)); 56 respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart));
56 - respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart)); 57 + respVO.setDeviceMessageTodayCount(deviceMessageService.getDeviceMessageCount(todayStart));
57 58
58 // 2. 获取各个品类下设备数量统计 59 // 2. 获取各个品类下设备数量统计
59 respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap()); 60 respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap());
@@ -66,14 +67,11 @@ public class IotStatisticsController { @@ -66,14 +67,11 @@ public class IotStatisticsController {
66 return success(respVO); 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,20 +57,10 @@ public interface IotDeviceMessageMapper {
57 */ 57 */
58 Long selectCountByCreateTime(@Param("createTime") Long createTime); 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,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.device;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.IdUtil; 4 import cn.hutool.core.util.IdUtil;
5 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
6 -import cn.hutool.core.util.RandomUtil;  
7 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
8 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
9 import cn.iocoder.yudao.framework.common.exception.ServiceException; 8 import cn.iocoder.yudao.framework.common.exception.ServiceException;
@@ -15,6 +14,7 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; @@ -15,6 +14,7 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
15 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 14 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
16 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 15 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
17 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 16 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
  17 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
18 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 18 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
19 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; 19 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
20 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 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,7 +22,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;
22 import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; 22 import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
23 import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; 23 import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
24 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 24 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
25 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;  
26 import jakarta.annotation.Resource; 25 import jakarta.annotation.Resource;
27 import jakarta.validation.ConstraintViolationException; 26 import jakarta.validation.ConstraintViolationException;
28 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
@@ -322,15 +321,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -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 * 生成 deviceSecret 324 * 生成 deviceSecret
335 * 325 *
336 * @return 生成的 deviceSecret 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,10 +2,16 @@ package cn.iocoder.yudao.module.iot.service.device.message;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; 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 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; 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 * IoT 设备消息 Service 接口 16 * IoT 设备消息 Service 接口
11 * 17 *
@@ -57,4 +63,21 @@ public interface IotDeviceMessageService { @@ -57,4 +63,21 @@ public interface IotDeviceMessageService {
57 */ 63 */
58 PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO); 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 package cn.iocoder.yudao.module.iot.service.device.message; 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 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.spring.SpringUtil; 6 import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.framework.common.exception.ServiceException; 7 import cn.iocoder.yudao.framework.common.exception.ServiceException;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 10 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO; 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 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 15 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
11 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 16 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
12 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 17 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
@@ -26,9 +31,13 @@ import org.springframework.scheduling.annotation.Async; @@ -26,9 +31,13 @@ import org.springframework.scheduling.annotation.Async;
26 import org.springframework.stereotype.Service; 31 import org.springframework.stereotype.Service;
27 import org.springframework.validation.annotation.Validated; 32 import org.springframework.validation.annotation.Validated;
28 33
  34 +import java.sql.Timestamp;
29 import java.time.LocalDateTime; 35 import java.time.LocalDateTime;
  36 +import java.util.List;
  37 +import java.util.Map;
30 38
31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL; 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,19 +56,19 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
47 private IotDevicePropertyService devicePropertyService; 56 private IotDevicePropertyService devicePropertyService;
48 57
49 @Resource 58 @Resource
50 - private IotDeviceMessageMapper deviceLogMapper; 59 + private IotDeviceMessageMapper deviceMessageMapper;
51 60
52 @Resource 61 @Resource
53 private IotDeviceMessageProducer deviceMessageProducer; 62 private IotDeviceMessageProducer deviceMessageProducer;
54 63
55 @Override 64 @Override
56 public void defineDeviceMessageStable() { 65 public void defineDeviceMessageStable() {
57 - if (StrUtil.isNotEmpty(deviceLogMapper.showSTable())) { 66 + if (StrUtil.isNotEmpty(deviceMessageMapper.showSTable())) {
58 log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]"); 67 log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]");
59 return; 68 return;
60 } 69 }
61 log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]"); 70 log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]");
62 - deviceLogMapper.createSTable(); 71 + deviceMessageMapper.createSTable();
63 log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]"); 72 log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]");
64 } 73 }
65 74
@@ -74,7 +83,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -74,7 +83,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
74 if (messageDO.getData() != null) { 83 if (messageDO.getData() != null) {
75 messageDO.setData(JsonUtils.toJsonString(messageDO.getData())); 84 messageDO.setData(JsonUtils.toJsonString(messageDO.getData()));
76 } 85 }
77 - deviceLogMapper.insert(messageDO); 86 + deviceMessageMapper.insert(messageDO);
78 } 87 }
79 88
80 @Override 89 @Override
@@ -192,7 +201,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -192,7 +201,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
192 @Override 201 @Override
193 public PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO) { 202 public PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO) {
194 try { 203 try {
195 - IPage<IotDeviceMessageDO> page = deviceLogMapper.selectPage( 204 + IPage<IotDeviceMessageDO> page = deviceMessageMapper.selectPage(
196 new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); 205 new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO);
197 return new PageResult<>(page.getRecords(), page.getTotal()); 206 return new PageResult<>(page.getRecords(), page.getTotal());
198 } catch (Exception exception) { 207 } catch (Exception exception) {
@@ -203,6 +212,33 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -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 private IotDeviceMessageServiceImpl getSelf() { 242 private IotDeviceMessageServiceImpl getSelf() {
207 return SpringUtil.getBean(getClass()); 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,45 +69,12 @@
69 </where> 69 </where>
70 </select> 70 </select>
71 71
72 - <select id="selectDeviceLogUpCountByHour" resultType="java.util.Map"> 72 + <select id="selectDeviceMessageCountGroupByDate" resultType="java.util.Map">
73 SELECT 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 &lt;= #{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 <where> 78 <where>
112 <if test="startTime != null"> 79 <if test="startTime != null">
113 AND ts >= #{startTime} 80 AND ts >= #{startTime}
@@ -115,10 +82,8 @@ @@ -115,10 +82,8 @@
115 <if test="endTime != null"> 82 <if test="endTime != null">
116 AND ts &lt;= #{endTime} 83 AND ts &lt;= #{endTime}
117 </if> 84 </if>
118 - AND upstream = false  
119 </where> 85 </where>
120 - GROUP BY TIMETRUNCATE(ts, 1h)  
121 - ORDER BY time ASC 86 + GROUP BY time
122 </select> 87 </select>
123 88
124 </mapper> 89 </mapper>
125 \ No newline at end of file 90 \ No newline at end of file