Browse Source

Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot

YunaiV 5 months ago
parent
commit
2a65e3bd2e
20 changed files with 586 additions and 4 deletions
  1. 75 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java
  2. 16 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java
  3. 78 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java
  4. 23 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
  5. 13 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java
  6. 26 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
  7. 12 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java
  8. 36 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java
  9. 24 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
  10. 15 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
  11. 33 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java
  12. 39 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java
  13. 17 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java
  14. 69 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java
  15. 17 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
  16. 11 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
  17. 9 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java
  18. 6 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
  19. 64 0
      yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml
  20. 3 3
      yudao-server/src/main/resources/application-local.yaml

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

@@ -0,0 +1,75 @@
+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.IotStatisticsReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO;
+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;
+import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;
+import cn.iocoder.yudao.module.iot.service.product.IotProductService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.LocalDateTime;
+
+
+@Tag(name = "管理后台 - IoT 数据统计")
+@RestController
+@RequestMapping("/iot/statistics")
+@Validated
+public class IotStatisticsController {
+
+    @Resource
+    private IotDeviceService iotDeviceService;
+
+    @Resource
+    private IotProductCategoryService iotProductCategoryService;
+
+    @Resource
+    private IotProductService iotProductService;
+
+    @Resource
+    private IotDeviceLogService iotDeviceLogService;
+
+
+    @GetMapping("/main")
+    @Operation(summary = "获取IOT首页的数据统计", description = "用于IOT首页的数据统计")
+    public CommonResult<IotStatisticsRespVO> getIotMainStats(@Valid IotStatisticsReqVO reqVO){
+        IotStatisticsRespVO iotStatisticsRespVO = new IotStatisticsRespVO();
+        // 获取总数
+        iotStatisticsRespVO.setCategoryTotal(iotProductCategoryService.getProductCategoryCount(null));
+        iotStatisticsRespVO.setProductTotal(iotProductService.getProductCount(null));
+        iotStatisticsRespVO.setDeviceTotal(iotDeviceService.getDeviceCount(null));
+        iotStatisticsRespVO.setReportTotal(iotDeviceLogService.getDeviceLogCount(null));
+
+        // 获取今日新增数量
+        LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
+        iotStatisticsRespVO.setCategoryTodayTotal(iotProductCategoryService.getProductCategoryCount(todayStart));
+        iotStatisticsRespVO.setProductTodayTotal(iotProductService.getProductCount(todayStart));
+        iotStatisticsRespVO.setDeviceTodayTotal(iotDeviceService.getDeviceCount(todayStart));
+        iotStatisticsRespVO.setReportTodayTotal(iotDeviceLogService.getDeviceLogCount(todayStart));
+
+        // 获取各个品类下设备数量统计
+        iotStatisticsRespVO.setDeviceStatsOfCategory(
+            iotProductCategoryService.getDeviceCountsOfProductCategory()
+        );
+
+        // 获取设备状态数量统计
+        iotStatisticsRespVO.setOnlineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.ONLINE.getState()));
+        iotStatisticsRespVO.setOfflineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.OFFLINE.getState()));
+        iotStatisticsRespVO.setNeverOnlineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.INACTIVE.getState()));
+
+        // 根据传入时间范围获取设备上下行消息数量统计
+        iotStatisticsRespVO.setDeviceUpMessageStats(iotDeviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()));
+        iotStatisticsRespVO.setDeviceDownMessageStats(iotDeviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()));
+
+        return CommonResult.success(iotStatisticsRespVO);
+    }
+}

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

@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - Iot统计 Request VO")
+@Data
+public class IotStatisticsReqVO {
+    @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
+    @NotNull(message = "查询起始时间不能为空")
+    Long startTime;
+    @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
+    @NotNull(message = "查询结束时间不能为空")
+    Long endTime;
+}

+ 78 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java

@@ -0,0 +1,78 @@
+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;
+
+@Schema(description = "管理后台 - Iot统计 Response VO")
+@Data
+public class IotStatisticsRespVO {
+
+    @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private long categoryTotal;
+
+    @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+    private long productTotal;
+
+    @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
+    private long deviceTotal;
+
+    @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
+    private long reportTotal;
+
+    @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private long categoryTodayTotal;
+
+    @Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+    private long productTodayTotal;
+
+    @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
+    private long deviceTodayTotal;
+
+    @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
+    private long reportTodayTotal;
+
+    @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80")
+    private long onlineTotal;
+
+    @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15")
+    private long offlineTotal;
+
+    @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    private long neverOnlineTotal;
+
+    @Schema(description = "上报数据数量统计")
+    private List<TimeData> reportDataStats;
+
+    @Schema(description = "上行数据数量统计")
+    private List<TimeData> deviceUpMessageStats;
+
+    @Schema(description = "下行数据数量统计")
+    private List<TimeData> deviceDownMessageStats;
+
+    @Schema(description = "按品类统计的设备数量")
+    private List<DataItem> deviceStatsOfCategory;
+
+    @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;
+    }
+}

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

@@ -9,6 +9,7 @@ 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 java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -69,4 +70,26 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
                 .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0"));
     }
 
+    /**
+     * 统计设备数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有设备数量
+     * @return 设备数量
+     */
+    default Long selectCountByCreateTime(LocalDateTime createTime) {
+        return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()
+                .geIfPresent(IotDeviceDO::getCreateTime, createTime));
+    }
+
+    /**
+     * 统计指定状态的设备数量
+     *
+     * @param state 状态
+     * @return 设备数量
+     */
+    default Long selectCountByState(Integer state) {
+        return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()
+                .eqIfPresent(IotDeviceDO::getState, state));
+    }
+
 }

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

@@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -28,4 +30,15 @@ public interface IotProductCategoryMapper extends BaseMapperX<IotProductCategory
         return selectList(IotProductCategoryDO::getStatus, status);
     }
 
+    /**
+     * 统计产品分类数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有分类数量
+     * @return 产品分类数量
+     */
+    default Long selectCountByCreateTime(LocalDateTime createTime) {
+        return selectCount(new LambdaQueryWrapperX<IotProductCategoryDO>()
+                .geIfPresent(IotProductCategoryDO::getCreateTime, createTime));
+    }
+
 }

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

@@ -8,6 +8,9 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDateTime;
+import java.util.List;
+
 /**
  * IoT 产品 Mapper
  *
@@ -28,4 +31,27 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> {
                 .apply("LOWER(product_key) = {0}", productKey.toLowerCase()));
     }
 
+    /**
+     * 统计产品数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有产品数量
+     * @return 产品数量
+     */
+    default Long selectCountByCreateTime(LocalDateTime createTime) {
+        return selectCount(new LambdaQueryWrapperX<IotProductDO>()
+                .geIfPresent(IotProductDO::getCreateTime, createTime));
+    }
+
+    /**
+     * 获得产品列表,基于分类编号
+     *
+     * @param categoryId 分类编号
+     * @return 产品列表
+     */
+    default List<IotProductDO> selectListByCategoryId(Long categoryId) {
+        return selectList(new LambdaQueryWrapperX<IotProductDO>()
+                .eq(IotProductDO::getCategoryId, categoryId)
+                .orderByDesc(IotProductDO::getId));
+    }
+
 }

+ 12 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java

@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelP
 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -72,4 +73,15 @@ public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> {
                 IotThingModelDO::getName, name);
     }
 
+    /**
+     * 统计物模型数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有物模型数量
+     * @return 物模型数量
+     */
+    default Long selectCountByCreateTime(LocalDateTime createTime) {
+        return selectCount(new LambdaQueryWrapperX<IotThingModelDO>()
+                .geIfPresent(IotThingModelDO::getCreateTime, createTime));
+    }
+
 }

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

@@ -1,6 +1,7 @@
 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;
@@ -8,6 +9,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDateTime;
+import java.util.List;
+
 /**
  * 设备日志 {@link IotDeviceLogDO} Mapper 接口
  */
@@ -46,4 +50,36 @@ public interface IotDeviceLogMapper {
     IPage<IotDeviceLogDO> selectPage(IPage<IotDeviceLogDO> page,
                                      @Param("reqVO") IotDeviceLogPageReqVO reqVO);
 
+    /**
+     * 统计设备日志数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有日志数量
+     * @return 日志数量
+     */
+    Long selectCountByCreateTime(@Param("createTime") Long createTime);
+
+    /**
+     * 获得每个小时设备上行消息数量统计
+     *
+     * @param deviceKey 设备标识
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 每小时消息数量统计
+     */
+    List<IotStatisticsRespVO.TimeData> 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);
+
 }

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

@@ -7,6 +7,7 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotEmpty;
 
 import javax.annotation.Nullable;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 
@@ -185,6 +186,29 @@ public interface IotDeviceService {
      */
     IotDeviceImportRespVO importDevice(List<IotDeviceImportExcelVO> importDevices, boolean updateSupport);
 
+    /**
+     * 获得设备数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有设备数量
+     * @return 设备数量
+     */
+    Long getDeviceCount(LocalDateTime createTime);
+
+    /**
+     * 获得设备数量,基于状态
+     *
+     * @param state 状态
+     * @return 设备数量
+     */
+    Long getDeviceCountByState(Integer state);
+
+    /**
+     * 获得所有设备列表
+     *
+     * @return 设备列表
+     */
+    List<IotDeviceDO> getDeviceList();
+
     /**
      * 获取 MQTT 连接参数
      *

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

@@ -424,4 +424,19 @@ public class IotDeviceServiceImpl implements IotDeviceService {
         return SpringUtil.getBean(getClass());
     }
 
+    @Override
+    public Long getDeviceCount(LocalDateTime createTime) {
+        return deviceMapper.selectCountByCreateTime(createTime);
+    }
+
+    @Override
+    public Long getDeviceCountByState(Integer state) {
+        return deviceMapper.selectCountByState(state);
+    }
+
+    @Override
+    public List<IotDeviceDO> getDeviceList() {
+        return deviceMapper.selectList();
+    }
+
 }

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

@@ -2,8 +2,13 @@ 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.dal.dataobject.device.IotDeviceLogDO;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDateTime;
+import java.util.List;
 
 /**
  * IoT 设备日志数据 Service 接口
@@ -34,4 +39,32 @@ public interface IotDeviceLogService {
      */
     PageResult<IotDeviceLogDO> getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO);
 
+    /**
+     * 获得设备日志数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有日志数量
+     * @return 日志数量
+     */
+    Long getDeviceLogCount(LocalDateTime createTime);
+
+    /**
+     * 获得每个小时设备上行消息数量统计
+     *
+     * @param deviceKey 设备标识,如果为空,则统计所有设备
+     * @param startTime 开始时间,如果为空,则不限制开始时间
+     * @param endTime 结束时间,如果为空,则不限制结束时间
+     * @return 每小时消息数量统计列表
+     */
+    List<IotStatisticsRespVO.TimeData> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime);
+
+    /**
+     * 获得每个小时设备下行消息数量统计
+     *
+     * @param deviceKey 设备标识,如果为空,则统计所有设备
+     * @param startTime 开始时间,如果为空,则不限制开始时间
+     * @param endTime 结束时间,如果为空,则不限制结束时间
+     * @return 每小时消息数量统计列表
+     */
+    List<IotStatisticsRespVO.TimeData> getDeviceLogDownCountByHour( String deviceKey, Long startTime, Long endTime);
+
 }

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

@@ -6,6 +6,7 @@ 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.dal.dataobject.device.IotDeviceLogDO;
 import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
@@ -16,6 +17,11 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * IoT 设备日志数据 Service 实现类
  *
@@ -63,4 +69,37 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService {
         }
     }
 
+    @Override
+    public Long getDeviceLogCount(LocalDateTime createTime) {
+        Long time = null;
+        if (createTime != null) {
+            time = createTime.toInstant(ZoneOffset.UTC).toEpochMilli();
+        }
+        return deviceLogMapper.selectCountByCreateTime(time);
+    }
+
+    @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;
+        }
+    }
+
+    @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;
+        }
+    }
+
 }

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

@@ -3,9 +3,11 @@ 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;
 
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -83,4 +85,19 @@ public interface IotProductCategoryService {
      */
     List<IotProductCategoryDO> getProductCategoryListByStatus(Integer status);
 
+    /**
+     * 获得产品分类数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有分类数量
+     * @return 产品分类数量
+     */
+    Long getProductCategoryCount(LocalDateTime createTime);
+
+    /**
+     * 获得各个品类下设备数量统计
+     *
+     * @return 品类设备统计列表
+     */
+    List<IotStatisticsRespVO.DataItem> getDeviceCountsOfProductCategory();
+
 }

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

@@ -5,14 +5,23 @@ 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;
+import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
+import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;
 import jakarta.annotation.Resource;
 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 static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS;
@@ -29,7 +38,12 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService
     @Resource
     private IotProductCategoryMapper productCategoryMapper;
 
-    @Override
+    @Resource
+    private IotProductService productService;
+
+    @Resource
+    private IotDeviceService deviceService;
+
     public Long createProductCategory(IotProductCategorySaveReqVO createReqVO) {
         // 插入
         IotProductCategoryDO productCategory = BeanUtils.toBean(createReqVO, IotProductCategoryDO.class);
@@ -84,4 +98,58 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService
         return productCategoryMapper.selectListByStatus(status);
     }
 
+    @Override
+    public Long getProductCategoryCount(LocalDateTime createTime) {
+        return productCategoryMapper.selectCountByCreateTime(createTime);
+    }
+
+    @Override
+    public List<IotStatisticsRespVO.DataItem> getDeviceCountsOfProductCategory() {
+        // 1. 获取所有数据
+        List<IotProductCategoryDO> categoryList = productCategoryMapper.selectList();
+        List<IotProductDO> productList = productService.getProductList();
+        List<IotDeviceDO> deviceList = deviceService.getDeviceList();
+
+        // 2. 统计每个分类下的设备数量
+        Map<String, Integer> categoryDeviceCountMap = new HashMap<>();
+        
+        // 2.1 初始化所有分类的计数为0
+        for (IotProductCategoryDO category : categoryList) {
+            categoryDeviceCountMap.put(category.getName(), 0);
+        }
+        
+        // 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);
+            }
+        }
+
+        // 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());
+    }
+
 }

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

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
 import jakarta.validation.Valid;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -92,4 +93,20 @@ public interface IotProductService {
      */
     List<IotProductDO> getProductList();
 
+    /**
+     * 获得产品数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有产品数量
+     * @return 产品数量
+     */
+    Long getProductCount(LocalDateTime createTime);
+
+    /**
+     * 获得产品列表,基于分类编号
+     *
+     * @param categoryId 分类编号
+     * @return 产品列表
+     */
+    List<IotProductDO> getProductListByCategoryId(Long categoryId);
+
 }

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

@@ -15,6 +15,7 @@ import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Objects;
 
@@ -137,4 +138,14 @@ public class IotProductServiceImpl implements IotProductService {
         return productMapper.selectList();
     }
 
+    @Override
+    public Long getProductCount(LocalDateTime createTime) {
+        return productMapper.selectCountByCreateTime(createTime);
+    }
+
+    @Override
+    public List<IotProductDO> getProductListByCategoryId(Long categoryId) {
+        return productMapper.selectListByCategoryId(categoryId);
+    }
+
 }

+ 9 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelS
 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
 import jakarta.validation.Valid;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -80,4 +81,12 @@ public interface IotThingModelService {
      */
     List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO);
 
+    /**
+     * 获得物模型数量
+     *
+     * @param createTime 创建时间,如果为空,则统计所有物模型数量
+     * @return 物模型数量
+     */
+    Long getThingModelCount(LocalDateTime createTime);
+
 }

+ 6 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java

@@ -29,6 +29,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
+import java.time.LocalDateTime;
 import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -363,4 +364,9 @@ public class IotThingModelServiceImpl implements IotThingModelService {
         return SpringUtil.getBean(getClass());
     }
 
+    @Override
+    public Long getThingModelCount(LocalDateTime createTime) {
+        return thingModelMapper.selectCountByCreateTime(createTime);
+    }
+
 }

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

@@ -55,4 +55,68 @@
         ORDER BY ts DESC
     </select>
 
+    <select id="selectCountByCreateTime" resultType="Long">
+        SELECT COUNT(*)
+        FROM device_log
+        <where>
+            <if test="createTime != null">
+                AND ts >= #{createTime}
+            </if>
+        </where>
+    </select>
+
+    <select id="selectDeviceLogUpCountByHour" resultType="cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO$TimeData">
+        SELECT 
+            TIMETRUNCATE(ts, 1h) as time,
+            COUNT(*) as data 
+        FROM 
+        <choose>
+            <when test="deviceKey != null and deviceKey != ''">
+                device_log_${deviceKey}
+            </when>
+            <otherwise>
+                device_log
+            </otherwise>
+        </choose>
+        <where>
+            <if test="startTime != null">
+                AND ts >= #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND ts &lt;= #{endTime}
+            </if>
+            AND (
+                 identifier IN ('online', 'offline', 'pull', 'progress', 'report', 'register', 'register_sub')
+            )
+        </where>
+        GROUP BY TIMETRUNCATE(ts, 1h)
+        ORDER BY time ASC
+    </select>
+
+    <select id="selectDeviceLogDownCountByHour" resultType="cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO$TimeData">
+        SELECT 
+            TIMETRUNCATE(ts, 1h) as time,
+            COUNT(*) as data 
+        FROM 
+        <choose>
+            <when test="deviceKey != null and deviceKey != ''">
+                device_log_${deviceKey}
+            </when>
+            <otherwise>
+                device_log
+            </otherwise>
+        </choose>
+        <where>
+            <if test="startTime != null">
+                AND ts >= #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND ts &lt;= #{endTime}
+            </if>
+            AND identifier IN ('set', 'get', 'upgrade', 'unregister_sub', 'topology_add')
+        </where>
+        GROUP BY TIMETRUNCATE(ts, 1h)
+        ORDER BY time ASC
+    </select>
+
 </mapper>

+ 3 - 3
yudao-server/src/main/resources/application-local.yaml

@@ -78,10 +78,10 @@ spring:
   # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
   data:
     redis:
-      host: 127.0.0.1 # 地址
+      host: chaojiniu.top # 地址
       port: 6379 # 端口
-      database: 1 # 数据库索引
-  #    password: 123456 # 密码,建议生产环境开启
+      database: 15 # 数据库索引
+      password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启
 
 --- #################### 定时任务相关配置 ####################