feat:【IoT 物联网】设备消息统计的代码优化
This commit is contained in:
@@ -16,6 +16,7 @@ import java.util.Arrays;
|
||||
@AllArgsConstructor
|
||||
public enum DateIntervalEnum implements ArrayValuable<Integer> {
|
||||
|
||||
HOUR(0, "小时"), // 特殊:字典里,暂时不会有这个枚举!!!因为大多数情况下,用不到这个间隔
|
||||
DAY(1, "天"),
|
||||
WEEK(2, "周"),
|
||||
MONTH(3, "月"),
|
||||
|
||||
@@ -8,6 +8,7 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
@@ -16,8 +17,7 @@ import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
|
||||
import static cn.hutool.core.date.DatePattern.createFormatter;
|
||||
import static cn.hutool.core.date.DatePattern.*;
|
||||
|
||||
/**
|
||||
* 时间工具类,用于 {@link java.time.LocalDateTime}
|
||||
@@ -82,6 +82,21 @@ public class LocalDateTimeUtils {
|
||||
return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
|
||||
}
|
||||
|
||||
/**
|
||||
* 判指定断时间,是否在该时间范围内
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param time 指定时间
|
||||
* @return 是否
|
||||
*/
|
||||
public static boolean isBetween(LocalDateTime startTime, LocalDateTime endTime, Timestamp time) {
|
||||
if (startTime == null || endTime == null || time == null) {
|
||||
return false;
|
||||
}
|
||||
return LocalDateTimeUtil.isIn(LocalDateTimeUtil.of(time), startTime, endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判指定断时间,是否在该时间范围内
|
||||
*
|
||||
@@ -234,6 +249,11 @@ public class LocalDateTimeUtils {
|
||||
// 2. 循环,生成时间范围
|
||||
List<LocalDateTime[]> timeRanges = new ArrayList<>();
|
||||
switch (intervalEnum) {
|
||||
case HOUR:
|
||||
while (startTime.isBefore(endTime)) {
|
||||
timeRanges.add(new LocalDateTime[]{startTime, startTime.plusHours(1).minusNanos(1)});
|
||||
startTime = startTime.plusHours(1);
|
||||
}
|
||||
case DAY:
|
||||
while (startTime.isBefore(endTime)) {
|
||||
timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
|
||||
@@ -297,6 +317,8 @@ public class LocalDateTimeUtils {
|
||||
|
||||
// 2. 循环,生成时间范围
|
||||
switch (intervalEnum) {
|
||||
case HOUR:
|
||||
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
|
||||
case DAY:
|
||||
return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
|
||||
case WEEK:
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
### 请求 /iot/statistics/get-device-message-summary-by-date 接口(小时)
|
||||
GET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=0×[0]=2025-06-13 00:00:00×[1]=2025-06-14 23:59:59
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenantId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
### 请求 /iot/statistics/get-device-message-summary-by-date 接口(天)
|
||||
GET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=1×[0]=2025-06-13 00:00:00×[1]=2025-06-14 23:59:59
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenantId}}
|
||||
Authorization: Bearer {{token}}
|
||||
@@ -1,12 +1,13 @@
|
||||
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.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.property.IotDeviceLogService;
|
||||
import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
|
||||
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;
|
||||
@@ -19,9 +20,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.*;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - IoT 数据统计")
|
||||
@RestController
|
||||
@@ -36,24 +38,23 @@ public class IotStatisticsController {
|
||||
@Resource
|
||||
private IotProductService productService;
|
||||
@Resource
|
||||
private IotDeviceLogService deviceLogService;
|
||||
private IotDeviceMessageService deviceMessageService;
|
||||
|
||||
@GetMapping("/get-summary")
|
||||
@Operation(summary = "获取 IoT 数据统计")
|
||||
public CommonResult<IotStatisticsSummaryRespVO> getIotStatisticsSummary(){
|
||||
@Operation(summary = "获取全局的数据统计")
|
||||
public CommonResult<IotStatisticsSummaryRespVO> getStatisticsSummary(){
|
||||
IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO();
|
||||
// 1.1 获取总数
|
||||
respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null));
|
||||
respVO.setProductCount(productService.getProductCount(null));
|
||||
respVO.setDeviceCount(deviceService.getDeviceCount(null));
|
||||
respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null));
|
||||
respVO.setDeviceMessageCount(deviceMessageService.getDeviceMessageCount(null));
|
||||
// 1.2 获取今日新增数量
|
||||
// TODO @super:使用 LocalDateTimeUtils.getToday()
|
||||
LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
|
||||
LocalDateTime todayStart = LocalDateTimeUtils.getToday();
|
||||
respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart));
|
||||
respVO.setProductTodayCount(productService.getProductCount(todayStart));
|
||||
respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart));
|
||||
respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart));
|
||||
respVO.setDeviceMessageTodayCount(deviceMessageService.getDeviceMessageCount(todayStart));
|
||||
|
||||
// 2. 获取各个品类下设备数量统计
|
||||
respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap());
|
||||
@@ -66,14 +67,11 @@ public class IotStatisticsController {
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
// TODO @super:要不干掉 IotStatisticsReqVO 参数,直接使用 @RequestParam 接收,简单一些。
|
||||
@GetMapping("/get-log-summary")
|
||||
@Operation(summary = "获取 IoT 设备上下行消息数据统计")
|
||||
public CommonResult<IotStatisticsDeviceMessageSummaryRespVO> getIotStatisticsDeviceMessageSummary(
|
||||
@Valid IotStatisticsReqVO reqVO) {
|
||||
return success(new IotStatisticsDeviceMessageSummaryRespVO()
|
||||
.setDownstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()))
|
||||
.setDownstreamCounts((deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()))));
|
||||
@GetMapping("/get-device-message-summary-by-date")
|
||||
@Operation(summary = "获取设备消息的数据统计")
|
||||
public CommonResult<List<IotStatisticsDeviceMessageSummaryByDateRespVO>> getDeviceMessageSummaryByDate(
|
||||
@Valid IotStatisticsDeviceMessageReqVO reqVO) {
|
||||
return success(deviceMessageService.getDeviceMessageSummaryByDate(reqVO));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO")
|
||||
@Data
|
||||
public class IotStatisticsDeviceMessageReqVO {
|
||||
|
||||
@Schema(description = "时间间隔类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@InEnum(value = DateIntervalEnum.class, message = "时间间隔类型,必须是 {value}")
|
||||
private Integer interval;
|
||||
|
||||
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Size(min = 2, max = 2, message = "请选择时间范围")
|
||||
private LocalDateTime[] times;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@Schema(description = "管理后台 - IoT 设备消息数量统计 Response VO")
|
||||
@Data
|
||||
public class IotStatisticsDeviceMessageSummaryByDateRespVO {
|
||||
|
||||
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer upstreamCount;
|
||||
|
||||
@Schema(description = "上行消息数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
|
||||
private Integer downstreamCount;
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
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 {
|
||||
|
||||
// TODO @super:前端传递的时候,还是通过 startTime 和 endTime 传递。后端转成 Long
|
||||
|
||||
@Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1658486600000")
|
||||
@NotNull(message = "查询起始时间不能为空")
|
||||
private Long startTime;
|
||||
|
||||
@Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1758486600000")
|
||||
@NotNull(message = "查询结束时间不能为空")
|
||||
private Long endTime;
|
||||
|
||||
}
|
||||
@@ -57,20 +57,10 @@ public interface IotDeviceMessageMapper {
|
||||
*/
|
||||
Long selectCountByCreateTime(@Param("createTime") Long createTime);
|
||||
|
||||
// TODO @super:1)上行、下行,不写在 mapper 里,而是通过参数传递,这样,selectDeviceLogUpCountByHour、selectDeviceLogDownCountByHour 可以合并;
|
||||
// TODO @super:2)不能只基于 identifier 来计算,而是要 type + identifier 成对
|
||||
/**
|
||||
* 查询每个小时设备上行消息数量
|
||||
* 按照时间范围(小时),统计设备的消息数量
|
||||
*/
|
||||
List<Map<String, Object>> selectDeviceLogUpCountByHour(@Param("deviceId") Long deviceId,
|
||||
@Param("startTime") Long startTime,
|
||||
@Param("endTime") Long endTime);
|
||||
|
||||
/**
|
||||
* 查询每个小时设备下行消息数量
|
||||
*/
|
||||
List<Map<String, Object>> selectDeviceLogDownCountByHour(@Param("deviceId") Long deviceId,
|
||||
@Param("startTime") Long startTime,
|
||||
@Param("endTime") Long endTime);
|
||||
List<Map<String, Object>> selectDeviceMessageCountGroupByDate(@Param("startTime") Long startTime,
|
||||
@Param("endTime") Long endTime);
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.device;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
@@ -15,6 +14,7 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
|
||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
|
||||
@@ -22,7 +22,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;
|
||||
import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -321,15 +320,6 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
return deviceMapper.selectCountByGroupId(groupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 deviceKey
|
||||
*
|
||||
* @return 生成的 deviceKey
|
||||
*/
|
||||
private String generateDeviceKey() {
|
||||
return RandomUtil.randomString(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 deviceSecret
|
||||
*
|
||||
|
||||
@@ -2,10 +2,16 @@ package cn.iocoder.yudao.module.iot.service.device.message;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessagePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IoT 设备消息 Service 接口
|
||||
*
|
||||
@@ -57,4 +63,21 @@ public interface IotDeviceMessageService {
|
||||
*/
|
||||
PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得设备消息数量
|
||||
*
|
||||
* @param createTime 创建时间,如果为空,则统计所有消息数量
|
||||
* @return 消息数量
|
||||
*/
|
||||
Long getDeviceMessageCount(@Nullable LocalDateTime createTime);
|
||||
|
||||
/**
|
||||
* 获取设备消息的数据统计
|
||||
*
|
||||
* @param reqVO 统计请求
|
||||
* @return 设备消息的数据统计
|
||||
*/
|
||||
List<IotStatisticsDeviceMessageSummaryByDateRespVO> getDeviceMessageSummaryByDate(
|
||||
IotStatisticsDeviceMessageReqVO reqVO);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package cn.iocoder.yudao.module.iot.service.device.message;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
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.message.IotDeviceMessagePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
@@ -26,9 +31,13 @@ import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL;
|
||||
|
||||
/**
|
||||
@@ -47,19 +56,19 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
|
||||
private IotDevicePropertyService devicePropertyService;
|
||||
|
||||
@Resource
|
||||
private IotDeviceMessageMapper deviceLogMapper;
|
||||
private IotDeviceMessageMapper deviceMessageMapper;
|
||||
|
||||
@Resource
|
||||
private IotDeviceMessageProducer deviceMessageProducer;
|
||||
|
||||
@Override
|
||||
public void defineDeviceMessageStable() {
|
||||
if (StrUtil.isNotEmpty(deviceLogMapper.showSTable())) {
|
||||
if (StrUtil.isNotEmpty(deviceMessageMapper.showSTable())) {
|
||||
log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]");
|
||||
return;
|
||||
}
|
||||
log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]");
|
||||
deviceLogMapper.createSTable();
|
||||
deviceMessageMapper.createSTable();
|
||||
log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]");
|
||||
}
|
||||
|
||||
@@ -74,7 +83,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
|
||||
if (messageDO.getData() != null) {
|
||||
messageDO.setData(JsonUtils.toJsonString(messageDO.getData()));
|
||||
}
|
||||
deviceLogMapper.insert(messageDO);
|
||||
deviceMessageMapper.insert(messageDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,7 +201,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
|
||||
@Override
|
||||
public PageResult<IotDeviceMessageDO> getDeviceMessagePage(IotDeviceMessagePageReqVO pageReqVO) {
|
||||
try {
|
||||
IPage<IotDeviceMessageDO> page = deviceLogMapper.selectPage(
|
||||
IPage<IotDeviceMessageDO> page = deviceMessageMapper.selectPage(
|
||||
new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO);
|
||||
return new PageResult<>(page.getRecords(), page.getTotal());
|
||||
} catch (Exception exception) {
|
||||
@@ -203,6 +212,33 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDeviceMessageCount(LocalDateTime createTime) {
|
||||
return deviceMessageMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IotStatisticsDeviceMessageSummaryByDateRespVO> getDeviceMessageSummaryByDate(
|
||||
IotStatisticsDeviceMessageReqVO reqVO) {
|
||||
// 1. 按小时统计,获取分项统计数据
|
||||
List<Map<String, Object>> countList = deviceMessageMapper.selectDeviceMessageCountGroupByDate(
|
||||
LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[0]), LocalDateTimeUtil.toEpochMilli(reqVO.getTimes()[1]));
|
||||
|
||||
// 2. 按照日期间隔,合并数据
|
||||
List<LocalDateTime[]> timeRanges = LocalDateTimeUtils.getDateRangeList(reqVO.getTimes()[0], reqVO.getTimes()[1], reqVO.getInterval());
|
||||
return convertList(timeRanges, times -> {
|
||||
Integer upstreamCount = countList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time")))
|
||||
.mapToInt(value -> MapUtil.getInt(value, "upstream_count")).sum();
|
||||
Integer downstreamCount = countList.stream()
|
||||
.filter(vo -> LocalDateTimeUtils.isBetween(times[0], times[1], (Timestamp) vo.get("time")))
|
||||
.mapToInt(value -> MapUtil.getInt(value, "downstream_count")).sum();
|
||||
return new IotStatisticsDeviceMessageSummaryByDateRespVO()
|
||||
.setTime(LocalDateTimeUtils.formatDateRange(times[0], times[1], reqVO.getInterval()))
|
||||
.setUpstreamCount(upstreamCount).setDownstreamCount(downstreamCount);
|
||||
});
|
||||
}
|
||||
|
||||
private IotDeviceMessageServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.device.property;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* IoT 设备消息数据 Service 接口
|
||||
*
|
||||
* @author alwayssuper
|
||||
*/
|
||||
public interface IotDeviceLogService {
|
||||
|
||||
/**
|
||||
* 获得设备消息数量
|
||||
*
|
||||
* @param createTime 创建时间,如果为空,则统计所有消息数量
|
||||
* @return 消息数量
|
||||
*/
|
||||
Long getDeviceLogCount(@Nullable LocalDateTime createTime);
|
||||
|
||||
// TODO @super:deviceKey 是不是用不上哈?
|
||||
/**
|
||||
* 获得每个小时设备上行消息数量统计
|
||||
*
|
||||
* @param deviceKey 设备标识
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return key: 时间戳, value: 消息数量
|
||||
*/
|
||||
List<Map<Long, Integer>> getDeviceLogUpCountByHour(@Nullable String deviceKey,
|
||||
@Nullable Long startTime,
|
||||
@Nullable Long endTime);
|
||||
|
||||
/**
|
||||
* 获得每个小时设备下行消息数量统计
|
||||
*
|
||||
* @param deviceKey 设备标识
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return key: 时间戳, value: 消息数量
|
||||
*/
|
||||
List<Map<Long, Integer>> getDeviceLogDownCountByHour(@Nullable String deviceKey,
|
||||
@Nullable Long startTime,
|
||||
@Nullable Long endTime);
|
||||
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package cn.iocoder.yudao.module.iot.service.device.property;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
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.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* IoT 设备日志数据 Service 实现类
|
||||
*
|
||||
* @author alwayssuper
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@Validated
|
||||
public class IotDeviceLogServiceImpl implements IotDeviceLogService {
|
||||
|
||||
@Resource
|
||||
private IotDeviceMessageMapper deviceLogMapper;
|
||||
|
||||
@Override
|
||||
public Long getDeviceLogCount(LocalDateTime createTime) {
|
||||
return deviceLogMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null);
|
||||
}
|
||||
|
||||
// TODO @super:加一个参数,Boolean upstream:true 上行,false 下行,null 不过滤
|
||||
@Override
|
||||
public List<Map<Long, Integer>> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) {
|
||||
// TODO @super:不能只基于数据库统计。因为有一些小时,可能出现没数据的情况,导致前端展示的图是不全的。可以参考 CrmStatisticsCustomerService 来实现
|
||||
// TODO @芋艿:这里实现,需要调整
|
||||
List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogUpCountByHour(0L, 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 MapUtil.of(timeMillis, count);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// TODO @super:getDeviceLogDownCountByHour 融合到 getDeviceLogUpCountByHour
|
||||
@Override
|
||||
public List<Map<Long, Integer>> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) {
|
||||
// TODO @芋艿:这里实现,需要调整
|
||||
List<Map<String, Object>> list = deviceLogMapper.selectDeviceLogDownCountByHour(0L, startTime, endTime);
|
||||
return list.stream()
|
||||
.map(map -> {
|
||||
Timestamp timestamp = (Timestamp) map.get("time");
|
||||
Long timeMillis = timestamp.getTime();
|
||||
Integer count = ((Number) map.get("data")).intValue();
|
||||
return MapUtil.of(timeMillis, count);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,19 +69,12 @@
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectDeviceLogUpCountByHour" resultType="java.util.Map">
|
||||
<select id="selectDeviceMessageCountGroupByDate" resultType="java.util.Map">
|
||||
SELECT
|
||||
TIMETRUNCATE(ts, 1h) as time,
|
||||
COUNT(*) as data
|
||||
FROM
|
||||
<choose>
|
||||
<when test="deviceId != null">
|
||||
device_message_${deviceId}
|
||||
</when>
|
||||
<otherwise>
|
||||
device_message
|
||||
</otherwise>
|
||||
</choose>
|
||||
TIMETRUNCATE(ts, 1h) AS time,
|
||||
SUM(CASE WHEN upstream = true THEN 1 ELSE 0 END) AS upstream_count,
|
||||
SUM(CASE WHEN upstream = false THEN 1 ELSE 0 END) AS downstream_count
|
||||
FROM device_message
|
||||
<where>
|
||||
<if test="startTime != null">
|
||||
AND ts >= #{startTime}
|
||||
@@ -89,36 +82,8 @@
|
||||
<if test="endTime != null">
|
||||
AND ts <= #{endTime}
|
||||
</if>
|
||||
AND upstream = true
|
||||
</where>
|
||||
GROUP BY TIMETRUNCATE(ts, 1h)
|
||||
ORDER BY time ASC
|
||||
</select>
|
||||
|
||||
<select id="selectDeviceLogDownCountByHour" resultType="java.util.Map">
|
||||
SELECT
|
||||
TIMETRUNCATE(ts, 1h) as time,
|
||||
COUNT(*) as data
|
||||
FROM
|
||||
<choose>
|
||||
<when test="deviceId != null">
|
||||
device_message_${deviceId}
|
||||
</when>
|
||||
<otherwise>
|
||||
device_message
|
||||
</otherwise>
|
||||
</choose>
|
||||
<where>
|
||||
<if test="startTime != null">
|
||||
AND ts >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND ts <= #{endTime}
|
||||
</if>
|
||||
AND upstream = false
|
||||
</where>
|
||||
GROUP BY TIMETRUNCATE(ts, 1h)
|
||||
ORDER BY time ASC
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user