!1266 对IOT首页功能的code review的修改

Merge pull request !1266 from alwayssuper/feature/iot
This commit is contained in:
芋道源码 2025-03-01 14:40:50 +00:00 committed by Gitee
commit 8bcfd40847
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
17 changed files with 217 additions and 209 deletions

View File

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

View File

@ -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 这个键值对
}

View File

@ -11,12 +11,12 @@ public class IotStatisticsReqVO {
// TODO @suppertimes 直接传递哈 // TODO @suppertimes 直接传递哈
// TODO 2superprivate 不要丢了 // TODO 2superprivate 不要丢了
@Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1658486600000")
@NotNull(message = "查询起始时间不能为空") @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 = "查询结束时间不能为空") @NotNull(message = "查询结束时间不能为空")
Long endTime; private Long endTime;
} }

View File

@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.Map;
// TODO @superTotal 全部改成 Count // TODO @superTotal 全部改成 Count
// TODO @superIotStatisticsSummaryRespVO // TODO @superIotStatisticsSummaryRespVO
@ -12,90 +12,58 @@ import java.util.List;
*/ */
@Schema(description = "管理后台 - Iot 统计 Response VO") @Schema(description = "管理后台 - Iot 统计 Response VO")
@Data @Data
public class IotStatisticsRespVO { public class IotStatisticsSummaryRespVO {
// TODO @superproductCategory // TODO @superproductCategory
@Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private long categoryTotal; private long productCategoryCount;
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private long productTotal; private long productCount;
@Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private long deviceTotal; private long deviceCount;
// TODO @superdeviceMessageCount设备消息数量 // TODO @superdeviceMessageCount设备消息数量
@Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
private long reportTotal; private long deviceMessageCount;
// TODO @superproductCategory // TODO @superproductCategory
@Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private long categoryTodayTotal; private long productCategoryTodayCount;
@Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") @Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private long productTodayTotal; private long productTodayCount;
@Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private long deviceTodayTotal; private long deviceTodayCount;
// TODO @superdeviceMessageCount今日设备消息数量 // TODO @superdeviceMessageCount今日设备消息数量
@Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
private long reportTodayTotal; private long deviceMessageTodayCount;
// TODO @superdeviceOnlineCount // TODO @superdeviceOnlineCount
@Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80")
private long onlineTotal; private long deviceOnlineCount;
// TODO @superdeviceOfflineCount // TODO @superdeviceOfflineCount
@Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15") @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15")
private long offlineTotal; private long deviceOfflineCount;
// TODO @superdeviceInactivECount // TODO @superdeviceInactivECount
@Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private long neverOnlineTotal; private long deviceInactiveCount;
// TODO @super1类型改成 Mapkey 分类名value 设备数量2deviceStatsOfCategory => productCategoryDeviceCounts // TODO @super1类型改成 Mapkey 分类名value 设备数量2deviceStatsOfCategory => productCategoryDeviceCounts
@Schema(description = "按品类统计的设备数量") @Schema(description = "按品类统计的设备数量")
private List<DataItem> deviceStatsOfCategory; private Map<String, Integer> productCategoryDeviceCounts;
// TODO @super貌似界面里用不到这个字段 // TODO @super貌似界面里用不到这个字段
@Schema(description = "上报数据数量统计")
private List<TimeData> reportDataStats;
// TODO @superdeviceUpMessageStatsdeviceDownMessageStats 单独抽到 IotStatisticsDeviceMessageSummaryRespVO然后里面属性就是 upstreamCountsdownstreamCounts // TODO @superdeviceUpMessageStatsdeviceDownMessageStats 单独抽到 IotStatisticsDeviceMessageSummaryRespVO然后里面属性就是 upstreamCountsdownstreamCounts
@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;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -35,6 +35,7 @@ import org.springframework.validation.annotation.Validated;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; 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.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 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 处理下 // TODO @super是不是 groupby 查询更高效不过 controller还是要考虑 null 的情况不过可以直接枚举 foreach 处理下
@Override
public Long getDeviceCountByState(Integer state) {
return deviceMapper.selectCountByState(state);
}
@Override @Override
public List<IotDeviceDO> getDeviceList() { public List<IotDeviceDO> getDeviceList() {
return deviceMapper.selectList(); 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())
));
}
} }

View File

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

View File

@ -1,12 +1,13 @@
package cn.iocoder.yudao.module.iot.service.device.data; 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.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 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.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.dataobject.device.IotDeviceLogDO;
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper;
import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; 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.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.sql.Timestamp;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** /**
* IoT 设备日志数据 Service 实现类 * IoT 设备日志数据 Service 实现类
@ -71,37 +73,39 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService {
@Override @Override
public Long getDeviceLogCount(LocalDateTime createTime) { public Long getDeviceLogCount(LocalDateTime createTime) {
Long time = null;
if (createTime != null) {
// todo @super1LocalDateTimeUtil.toEpochMilli(createTime);2直接表达式更简洁 time != null ? createTime.toInstant(ZoneOffset.UTC).toEpochMilli() : null; // todo @super1LocalDateTimeUtil.toEpochMilli(createTime);2直接表达式更简洁 time != null ? createTime.toInstant(ZoneOffset.UTC).toEpochMilli() : null;
time = createTime.toInstant(ZoneOffset.UTC).toEpochMilli(); return deviceLogMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null);
}
return deviceLogMapper.selectCountByCreateTime(time);
} }
// TODO @super加一个参数Boolean upstreamtrue 上行false 下行null 不过滤 // TODO @super加一个参数Boolean upstreamtrue 上行false 下行null 不过滤
@Override @Override
public List<IotStatisticsRespVO.TimeData> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { public List<Map<Long, Integer>> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) {
try { List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime);
return deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime); return list.stream()
} catch (Exception exception) { .map(map -> {
if (exception.getMessage().contains("Table does not exist")) { // 从Timestamp获取时间戳
return new ArrayList<>(); Timestamp timestamp = (Timestamp) map.get("time");
} Long timeMillis = timestamp.getTime();
throw exception; // 消息数量转换
} Integer count = ((Number) map.get("data")).intValue();
return Map.of(timeMillis, count);
})
.collect(Collectors.toList());
} }
@Override @Override
public List<IotStatisticsRespVO.TimeData> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) { public List<Map<Long, Integer>> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) {
try { List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime);
return deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime); return list.stream()
} catch (Exception exception) { .map(map -> {
if (exception.getMessage().contains("Table does not exist")) { // 从Timestamp获取时间戳
return new ArrayList<>(); Timestamp timestamp = (Timestamp) map.get("time");
} Long timeMillis = timestamp.getTime();
throw exception; // 消息数量转换
} Integer count = ((Number) map.get("data")).intValue();
return Map.of(timeMillis, count);
})
.collect(Collectors.toList());
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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>