Bläddra i källkod

!1266 对IOT首页功能的code review的修改
Merge pull request !1266 from alwayssuper/feature/iot

芋道源码 5 månader sedan
förälder
incheckning
8bcfd40847
17 ändrade filer med 217 tillägg och 209 borttagningar
  1. 28 23
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java
  2. 19 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java
  3. 4 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java
  4. 15 47
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java
  5. 17 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
  6. 0 5
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
  7. 9 19
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java
  8. 18 8
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
  9. 21 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
  10. 16 15
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java
  11. 30 26
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java
  12. 1 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java
  13. 12 42
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java
  14. 0 8
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
  15. 0 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
  16. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml
  17. 25 0
      yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml

+ 28 - 23
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java

@@ -1,8 +1,9 @@
 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.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
 import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
@@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.time.LocalDateTime;
+import java.util.Map;
 
 @Tag(name = "管理后台 - IoT 数据统计")
 @RestController
@@ -35,38 +37,41 @@ public class IotStatisticsController {
     private IotDeviceLogService deviceLogService;
 
     // TODO @super:description 非必要,可以不写哈
-    @GetMapping("/main")
-    @Operation(summary = "获取首页的数据统计", description = "用于IOT首页的数据统计")
-    public CommonResult<IotStatisticsRespVO> getIotMainStats(@Valid IotStatisticsReqVO reqVO){
-        // TODO @super:新增 get-summary 接口,返回:总数、今日新增、数量、状态
-        IotStatisticsRespVO iotStatisticsRespVO = new IotStatisticsRespVO();
+    @GetMapping("/get-summary")
+    @Operation(summary = "获取IOT数据统计")
+    public CommonResult<IotStatisticsSummaryRespVO> getIotStatisticsSummary(){
+        IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO();
         // 获取总数
-        iotStatisticsRespVO.setCategoryTotal(productCategoryService.getProductCategoryCount(null));
-        iotStatisticsRespVO.setProductTotal(productService.getProductCount(null));
-        iotStatisticsRespVO.setDeviceTotal(deviceService.getDeviceCount(null));
-        iotStatisticsRespVO.setReportTotal(deviceLogService.getDeviceLogCount(null));
+        respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null));
+        respVO.setProductCount(productService.getProductCount(null));
+        respVO.setDeviceCount(deviceService.getDeviceCount(null));
+        respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null));
 
         // 获取今日新增数量
         LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
-        iotStatisticsRespVO.setCategoryTodayTotal(productCategoryService.getProductCategoryCount(todayStart));
-        iotStatisticsRespVO.setProductTodayTotal(productService.getProductCount(todayStart));
-        iotStatisticsRespVO.setDeviceTodayTotal(deviceService.getDeviceCount(todayStart));
-        iotStatisticsRespVO.setReportTodayTotal(deviceLogService.getDeviceLogCount(todayStart));
+        respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart));
+        respVO.setProductTodayCount(productService.getProductCount(todayStart));
+        respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart));
+        respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart));
 
         // 获取各个品类下设备数量统计
-        iotStatisticsRespVO.setDeviceStatsOfCategory(
-            productCategoryService.getDeviceCountsOfProductCategory()
-        );
+        respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap());
 
         // 获取设备状态数量统计
-        iotStatisticsRespVO.setOnlineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.ONLINE.getState()));
-        iotStatisticsRespVO.setOfflineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.OFFLINE.getState()));
-        iotStatisticsRespVO.setNeverOnlineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.INACTIVE.getState()));
+        Map<Integer, Long> deviceCountMap = deviceService.getDeviceCountMapByState();
+        respVO.setDeviceOnlineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.ONLINE.getState(), 0L));
+        respVO.setDeviceOfflineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.OFFLINE.getState(), 0L));
+        respVO.setDeviceInactiveCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.INACTIVE.getState(), 0L));
 
-        // TODO @super:新增 get-log-summary 接口,返回
+        return CommonResult.success(respVO);
+    }
+    @GetMapping("/get-log-summary")
+    @Operation(summary = "获取IOT上下行消息数据统计")
+    public CommonResult<IotStatisticsDeviceMessageSummaryRespVO> getIotStatisticsDeviceMessageSummary(@Valid IotStatisticsReqVO reqVO){
         // 根据传入时间范围获取设备上下行消息数量统计
-        iotStatisticsRespVO.setDeviceUpMessageStats(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()));
-        iotStatisticsRespVO.setDeviceDownMessageStats(deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()));
+        IotStatisticsDeviceMessageSummaryRespVO iotStatisticsRespVO = new IotStatisticsDeviceMessageSummaryRespVO();
+        iotStatisticsRespVO.setUpstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()));
+        iotStatisticsRespVO.setDownstreamCounts(deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()));
 
         return CommonResult.success(iotStatisticsRespVO);
     }

+ 19 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.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;
+
+import java.util.List;
+import java.util.Map;
+
+@Schema(description = "管理后台 - Iot 上下行消息数量统计 Response VO")
+@Data
+public class IotStatisticsDeviceMessageSummaryRespVO {
+    @Schema(description = "每小时上行数据数量统计")
+    private List<Map<Long, Integer>> upstreamCounts;
+
+    @Schema(description = "每小时下行数据数量统计")
+    private List<Map<Long, Integer>> downstreamCounts;
+
+    // TODO @super:如果只有这两个字段,使用 KeyValue 这个键值对
+}

+ 4 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java

@@ -11,12 +11,12 @@ public class IotStatisticsReqVO {
     // TODO @supper:times 直接传递哈;
     // TODO 2super:private 不要丢了
 
-    @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
+    @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1658486600000")
     @NotNull(message = "查询起始时间不能为空")
-    Long startTime;
+    private Long startTime;
 
-    @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
+    @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1758486600000")
     @NotNull(message = "查询结束时间不能为空")
-    Long endTime;
+    private Long endTime;
 
 }

+ 15 - 47
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java

@@ -3,7 +3,7 @@ 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;
 
 // TODO @super:Total 全部改成 Count
 // TODO @super:IotStatisticsSummaryRespVO
@@ -12,90 +12,58 @@ import java.util.List;
  */
 @Schema(description = "管理后台 - Iot 统计 Response VO")
 @Data
-public class IotStatisticsRespVO {
+public class IotStatisticsSummaryRespVO {
 
     // TODO @super:productCategory 哈
     @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
-    private long categoryTotal;
+    private long productCategoryCount;
 
     @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
-    private long productTotal;
+    private long productCount;
 
     @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
-    private long deviceTotal;
+    private long deviceCount;
 
     // TODO @super:deviceMessageCount;设备消息数量
     @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
-    private long reportTotal;
+    private long deviceMessageCount;
 
     // TODO @super:productCategory 哈
     @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
-    private long categoryTodayTotal;
+    private long productCategoryTodayCount;
 
     @Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
-    private long productTodayTotal;
+    private long productTodayCount;
 
     @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
-    private long deviceTodayTotal;
+    private long deviceTodayCount;
 
     // TODO @super:deviceMessageCount;今日设备消息数量
     @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
-    private long reportTodayTotal;
+    private long deviceMessageTodayCount;
 
     // TODO @super:deviceOnlineCount
 
     @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80")
-    private long onlineTotal;
+    private long deviceOnlineCount;
 
     // TODO @super:deviceOfflineCount
 
     @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15")
-    private long offlineTotal;
+    private long deviceOfflineCount;
 
     // TODO @super:deviceInactivECount
 
     @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
-    private long neverOnlineTotal;
+    private long deviceInactiveCount;
 
     // TODO @super:1)类型改成 Map,key 分类名、value 设备数量;2)deviceStatsOfCategory => productCategoryDeviceCounts
     @Schema(description = "按品类统计的设备数量")
-    private List<DataItem> deviceStatsOfCategory;
+    private Map<String, Integer> productCategoryDeviceCounts;
 
     // TODO @super:貌似界面里,用不到这个字段???
-    @Schema(description = "上报数据数量统计")
-    private List<TimeData> reportDataStats;
 
-    // TODO @super:deviceUpMessageStats、deviceDownMessageStats 单独抽到 IotStatisticsDeviceMessageSummaryRespVO,然后里面属性就是 upstreamCounts、downstreamCounts
-
-    @Schema(description = "上行数据数量统计")
-    private List<TimeData> deviceUpMessageStats;
-
-    @Schema(description = "下行数据数量统计")
-    private List<TimeData> deviceDownMessageStats;
-
-    // TODO @super:如果只有这两个字段,使用 KeyValue 这个键值对
-    @Schema(description = "时间数据")
-    @Data
-    public static class TimeData {
-
-        @Schema(description = "时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1646092800000")
-        private long time;
-
-        @Schema(description = "数据值", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
-        private Object data;
 
-    }
-
-    @Schema(description = "数据项")
-    @Data
-    public static class DataItem {
-
-        @Schema(description = "数据项名", requiredMode = Schema.RequiredMode.REQUIRED, example = "智能家居")
-        private String name;
-
-        @Schema(description = "数据项值", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
-        private Object value;
-
-    }
+    // TODO @super:deviceUpMessageStats、deviceDownMessageStats 单独抽到 IotStatisticsDeviceMessageSummaryRespVO,然后里面属性就是 upstreamCounts、downstreamCounts
 
 }

+ 17 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java

@@ -8,10 +8,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePa
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 import javax.annotation.Nullable;
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * IoT 设备 Mapper
@@ -81,4 +84,18 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
                 .eqIfPresent(IotDeviceDO::getState, state));
     }
 
+    /**
+     * 查询指定产品下各状态的设备数量
+     *
+     * @return 设备数量统计列表
+     */
+    List<Map<String, Object>> selectDeviceCountMapByProductId();
+
+    /**
+     * 查询各个状态下的设备数量
+     *
+     * @return 设备数量统计列表
+     */
+    List<Map<String, Object>> selectDeviceCountGroupByState();
+
 }

+ 0 - 5
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java

@@ -37,10 +37,5 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> {
                 .geIfPresent(IotProductDO::getCreateTime, createTime));
     }
 
-    default List<IotProductDO> selectListByCategoryId(Long categoryId) {
-        return selectList(new LambdaQueryWrapperX<IotProductDO>()
-                .eq(IotProductDO::getCategoryId, categoryId)
-                .orderByDesc(IotProductDO::getId));
-    }
 
 }

+ 9 - 19
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.iot.dal.tdengine;
 
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
 import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;
 import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
@@ -10,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 设备日志 {@link IotDeviceLogDO} Mapper 接口
@@ -60,27 +60,17 @@ public interface IotDeviceLogMapper {
     // TODO @super:1)上行、下行,不写在 mapper 里,而是通过参数传递,这样,selectDeviceLogUpCountByHour、selectDeviceLogDownCountByHour 可以合并;
     //  TODO @super:2)不能只基于 identifier 来计算,而是要 type + identifier 成对
     /**
-     * 获得每个小时设备上行消息数量统计
-     *
-     * @param deviceKey 设备标识
-     * @param startTime 开始时间
-     * @param endTime 结束时间
-     * @return 每小时消息数量统计
+     * 查询每个小时设备上行消息数量
      */
-    List<IotStatisticsRespVO.TimeData> selectDeviceLogUpCountByHour(@Param("deviceKey") String deviceKey,
-                                                                    @Param("startTime") Long startTime,
-                                                                    @Param("endTime") Long endTime);
+    List<Map<String, Object>> selectDeviceLogUpCountByHour(@Param("deviceKey") String deviceKey,
+                                                          @Param("startTime") Long startTime,
+                                                          @Param("endTime") Long endTime);
 
     /**
-     * 获得每个小时设备下行消息数量统计
-     *
-     * @param deviceKey 设备标识
-     * @param startTime 开始时间
-     * @param endTime 结束时间
-     * @return 每小时消息数量统计
+     * 查询每个小时设备下行消息数量
      */
-    List<IotStatisticsRespVO.TimeData> selectDeviceLogDownCountByHour(@Param("deviceKey") String deviceKey,
-                                                                      @Param("startTime") Long startTime,
-                                                                      @Param("endTime") Long endTime);
+    List<Map<String, Object>> selectDeviceLogDownCountByHour(@Param("deviceKey") String deviceKey,
+                                                            @Param("startTime") Long startTime,
+                                                            @Param("endTime") Long endTime);
 
 }

+ 18 - 8
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
 import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotEmpty;
 
@@ -10,6 +11,7 @@ import javax.annotation.Nullable;
 import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 /**
  * IoT 设备 Service 接口
@@ -111,7 +113,7 @@ public interface IotDeviceService {
     IotDeviceDO getDeviceByDeviceKey(String deviceKey);
 
     /**
-     * ��得设备分页
+     * 得设备分页
      *
      * @param pageReqVO 分页查询
      * @return IoT 设备分页
@@ -194,13 +196,6 @@ public interface IotDeviceService {
      */
     Long getDeviceCount(@Nullable LocalDateTime createTime);
 
-    /**
-     * 获得设备数量,基于状态
-     *
-     * @param state 状态
-     * @return 设备数量
-     */
-    Long getDeviceCountByState(Integer state);
 
     /**
      * 获得所有设备列表
@@ -217,4 +212,19 @@ public interface IotDeviceService {
      */
     IotDeviceMqttConnectionParamsRespVO getMqttConnectionParams(Long deviceId);
 
+    /**
+     * 获得各个产品下的设备数量 Map
+     *
+     * @return key: 产品编号, value: 设备数量
+     */
+    Map<Long, Integer> getDeviceCountMapByProductId();
+
+    /**
+     * 获得各个状态下的设备数量 Map
+     *
+     * @return key: 设备状态枚举 {@link IotDeviceStateEnum}
+     *         value: 设备数量
+     */
+    Map<Integer, Long> getDeviceCountMapByState();
+
 }

+ 21 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java

@@ -35,6 +35,7 @@ import org.springframework.validation.annotation.Validated;
 import javax.annotation.Nullable;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -430,14 +431,30 @@ public class IotDeviceServiceImpl implements IotDeviceService {
     }
 
     // TODO @super:是不是 groupby 查询,更高效;不过 controller,还是要考虑 null 的情况;不过可以直接枚举 foreach 处理下
-    @Override
-    public Long getDeviceCountByState(Integer state) {
-        return deviceMapper.selectCountByState(state);
-    }
 
     @Override
     public List<IotDeviceDO> getDeviceList() {
         return deviceMapper.selectList();
     }
 
+    @Override
+    public Map<Long, Integer> getDeviceCountMapByProductId() {
+        // 查询结果转换成Map
+        List<Map<String, Object>> list = deviceMapper.selectDeviceCountMapByProductId();
+        return list.stream().collect(Collectors.toMap(
+            map -> Long.valueOf(map.get("key").toString()),
+            map -> Integer.valueOf(map.get("value").toString())
+        ));
+    }
+
+    @Override
+    public Map<Integer, Long> getDeviceCountMapByState() {
+        // 查询结果转换成Map
+        List<Map<String, Object>> list = deviceMapper.selectDeviceCountGroupByState();
+        return list.stream().collect(Collectors.toMap(
+            map -> Integer.valueOf(map.get("key").toString()),
+            map -> Long.valueOf(map.get("value").toString())
+        ));
+    }
+
 }

+ 16 - 15
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java

@@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.iot.service.device.data;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 
 import javax.annotation.Nullable;
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Map;
 
 /**
  * IoT 设备日志数据 Service 接口
@@ -50,25 +51,25 @@ public interface IotDeviceLogService {
     /**
      * 获得每个小时设备上行消息数量统计
      *
-     * @param deviceKey 设备标识,如果为空,则统计所有设备
-     * @param startTime 开始时间,如果为空,则不限制开始时间
-     * @param endTime 结束时间,如果为空,则不限制结束时间
-     * @return 每小时消息数量统计列表
+     * @param deviceKey 设备标识
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return key: 时间戳, value: 消息数量
      */
-    List<IotStatisticsRespVO.TimeData> getDeviceLogUpCountByHour(@Nullable String deviceKey,
-                                                                 @Nullable Long startTime,
-                                                                 @Nullable Long endTime);
+    List<Map<Long, Integer>> getDeviceLogUpCountByHour(@Nullable String deviceKey,
+                                                                                     @Nullable Long startTime,
+                                                                                     @Nullable Long endTime);
 
     /**
      * 获得每个小时设备下行消息数量统计
      *
-     * @param deviceKey 设备标识,如果为空,则统计所有设备
-     * @param startTime 开始时间,如果为空,则不限制开始时间
-     * @param endTime 结束时间,如果为空,则不限制结束时间
-     * @return 每小时消息数量统计列表
+     * @param deviceKey 设备标识
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return key: 时间戳, value: 消息数量
      */
-    List<IotStatisticsRespVO.TimeData> getDeviceLogDownCountByHour(@Nullable String deviceKey,
-                                                                   @Nullable Long startTime,
-                                                                   @Nullable Long endTime);
+    List<Map<Long, Integer>> getDeviceLogDownCountByHour(@Nullable String deviceKey,
+                                                  @Nullable Long startTime,
+                                                  @Nullable Long endTime);
 
 }

+ 30 - 26
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java

@@ -1,12 +1,13 @@
 package cn.iocoder.yudao.module.iot.service.device.data;
 
+import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 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.data.IotDeviceLogPageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
 import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
@@ -17,10 +18,11 @@ 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.time.ZoneOffset;
-import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * IoT 设备日志数据 Service 实现类
@@ -71,37 +73,39 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService {
 
     @Override
     public Long getDeviceLogCount(LocalDateTime createTime) {
-        Long time = null;
-        if (createTime != null) {
             // todo @super:1)LocalDateTimeUtil.toEpochMilli(createTime);2)直接表达式,更简洁 time != null ? createTime.toInstant(ZoneOffset.UTC).toEpochMilli() : null;
-            time = createTime.toInstant(ZoneOffset.UTC).toEpochMilli();
-        }
-        return deviceLogMapper.selectCountByCreateTime(time);
+        return deviceLogMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null);
     }
 
     // TODO @super:加一个参数,Boolean upstream:true 上行,false 下行,null 不过滤
     @Override
-    public List<IotStatisticsRespVO.TimeData> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) {
-        try {
-            return deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime);
-        } catch (Exception exception) {
-            if (exception.getMessage().contains("Table does not exist")) {
-                return new ArrayList<>();
-            }
-            throw exception;
-        }
+    public List<Map<Long, Integer>> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) {
+        List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, 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 Map.of(timeMillis, count);
+                })
+                .collect(Collectors.toList());
     }
 
     @Override
-    public List<IotStatisticsRespVO.TimeData> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) {
-        try {
-            return deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime);
-        } catch (Exception exception) {
-            if (exception.getMessage().contains("Table does not exist")) {
-                return new ArrayList<>();
-            }
-            throw exception;
-        }
+    public List<Map<Long, Integer>> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) {
+        List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, 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 Map.of(timeMillis, count);
+                })
+                .collect(Collectors.toList());
     }
 
 }

+ 1 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.product;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;
 import jakarta.validation.Valid;
 
@@ -100,6 +99,6 @@ public interface IotProductCategoryService {
      *
      * @return 品类设备统计列表
      */
-    List<IotStatisticsRespVO.DataItem> getDeviceCountsOfProductCategory();
+    Map<String, Integer> getProductCategoryDeviceCountMap();
 
 }

+ 12 - 42
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java

@@ -5,8 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO;
-import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductCategoryMapper;
@@ -16,11 +14,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.time.LocalDateTime;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS;
@@ -103,54 +97,30 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService
     }
 
     @Override
-    public List<IotStatisticsRespVO.DataItem> getDeviceCountsOfProductCategory() {
+    public Map<String, Integer> getProductCategoryDeviceCountMap() {
         // 1. 获取所有数据
         List<IotProductCategoryDO> categoryList = productCategoryMapper.selectList();
         List<IotProductDO> productList = productService.getProductList();
         // TODO @super:不要 list 查询,返回内存,而是查询一个 Map<productId, count>
-        List<IotDeviceDO> deviceList = deviceService.getDeviceList();
+        Map<Long, Integer> deviceCountMapByProductId = deviceService.getDeviceCountMapByProductId();
 
         // 2. 统计每个分类下的设备数量
         Map<String, Integer> categoryDeviceCountMap = new HashMap<>();
-
+        
         // 2.1 初始化所有分类的计数为0
         for (IotProductCategoryDO category : categoryList) {
             categoryDeviceCountMap.put(category.getName(), 0);
-            // TODO @super:直接这里面计算,不用多个循环。产品本身也不多,不用构建 Map,直接 filter 就好了
-        }
-
-        // 2.2 构建产品ID到分类的映射
-        Map<Long, IotProductCategoryDO> productCategoryMap = new HashMap<>();
-        for (IotProductDO product : productList) {
-            Long categoryId = product.getCategoryId();
-            IotProductCategoryDO category = categoryList.stream()
-                    .filter(c -> c.getId().equals(categoryId))
-                    .findFirst()
-                    .orElse(null);
-            if (category != null) {
-                productCategoryMap.put(product.getId(), category);
-            }
-        }
-
-        // 2.3 统计每个分类下的设备数量
-        for (IotDeviceDO device : deviceList) {
-            Long productId = device.getProductId();
-            IotProductCategoryDO category = productCategoryMap.get(productId);
-            if (category != null) {
-                String categoryName = category.getName();
-                categoryDeviceCountMap.merge(categoryName, 1, Integer::sum);
+            
+            // 2.2 找到该分类下的所有产品,累加设备数量
+            for (IotProductDO product : productList) {
+                if (Objects.equals(product.getCategoryId(), category.getId())) {
+                    Integer deviceCount = deviceCountMapByProductId.getOrDefault(product.getId(), 0);
+                    categoryDeviceCountMap.merge(category.getName(), deviceCount, Integer::sum);
+                }
             }
         }
 
-        // 3. 转换为 DataItem 列表
-        return categoryDeviceCountMap.entrySet().stream()
-                .map(entry -> {
-                    IotStatisticsRespVO.DataItem dataItem = new IotStatisticsRespVO.DataItem();
-                    dataItem.setName(entry.getKey());
-                    dataItem.setValue(entry.getValue());
-                    return dataItem;
-                })
-                .collect(Collectors.toList());
+        return categoryDeviceCountMap;
     }
 
 }

+ 0 - 8
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java

@@ -102,13 +102,5 @@ public interface IotProductService {
      */
     Long getProductCount(@Nullable LocalDateTime createTime);
 
-    // TODO @super:用不到的,删除下哈
-    /**
-     * 获得产品列表,基于分类编号
-     *
-     * @param categoryId 分类编号
-     * @return 产品列表
-     */
-    List<IotProductDO> getProductListByCategoryId(Long categoryId);
 
 }

+ 0 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java

@@ -143,9 +143,5 @@ public class IotProductServiceImpl implements IotProductService {
         return productMapper.selectCountByCreateTime(createTime);
     }
 
-    @Override
-    public List<IotProductDO> getProductListByCategoryId(Long categoryId) {
-        return productMapper.selectListByCategoryId(categoryId);
-    }
 
 }

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml

@@ -65,7 +65,7 @@
         </where>
     </select>
 
-    <select id="selectDeviceLogUpCountByHour" resultType="cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO$TimeData">
+    <select id="selectDeviceLogUpCountByHour" resultType="java.util.Map">
         SELECT 
             TIMETRUNCATE(ts, 1h) as time,
             COUNT(*) as data 
@@ -93,7 +93,7 @@
         ORDER BY time ASC
     </select>
 
-    <select id="selectDeviceLogDownCountByHour" resultType="cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO$TimeData">
+    <select id="selectDeviceLogDownCountByHour" resultType="java.util.Map">
         SELECT 
             TIMETRUNCATE(ts, 1h) as time,
             COUNT(*) as data 

+ 25 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper">
+
+    <select id="selectDeviceCountGroupByState" resultType="java.util.Map">
+        SELECT 
+            state AS `key`,
+            COUNT(1) AS `value`
+        FROM iot_device
+        WHERE deleted = 0
+        GROUP BY state
+    </select>
+
+    <select id="selectDeviceCountMapByProductId" resultType="java.util.Map">
+        SELECT
+            product_id AS `key`,
+            COUNT(1) AS `value`
+        FROM iot_device
+        WHERE deleted = 0
+        GROUP BY product_id
+    </select>
+
+</mapper>