feat:【IoT 物联网】设备消息统计的代码优化

This commit is contained in:
YunaiV
2025-06-14 17:15:00 +08:00
parent 6a06f520fb
commit d70c6986d5
15 changed files with 173 additions and 247 deletions

View File

@@ -16,6 +16,7 @@ import java.util.Arrays;
@AllArgsConstructor
public enum DateIntervalEnum implements ArrayValuable<Integer> {
HOUR(0, "小时"), // 特殊:字典里,暂时不会有这个枚举!!!因为大多数情况下,用不到这个间隔
DAY(1, ""),
WEEK(2, ""),
MONTH(3, ""),

View File

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

View File

@@ -0,0 +1,11 @@
### 请求 /iot/statistics/get-device-message-summary-by-date 接口(小时)
GET {{baseUrl}}/iot/statistics/get-device-message-summary-by-date?interval=0&times[0]=2025-06-13 00:00:00&times[1]=2025-06-14 23:59:59
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&times[0]=2025-06-13 00:00:00&times[1]=2025-06-14 23:59:59
Content-Type: application/json
tenant-id: {{adminTenantId}}
Authorization: Bearer {{token}}

View File

@@ -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));
}
}

View File

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

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

View File

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

View File

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

View File

@@ -57,20 +57,10 @@ public interface IotDeviceMessageMapper {
*/
Long selectCountByCreateTime(@Param("createTime") Long createTime);
// TODO @super1上行、下行不写在 mapper 里而是通过参数传递这样selectDeviceLogUpCountByHour、selectDeviceLogDownCountByHour 可以合并;
// TODO @super2不能只基于 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);
}

View File

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

View File

@@ -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);
}

View File

@@ -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());
}

View File

@@ -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 @superdeviceKey 是不是用不上哈?
/**
* 获得每个小时设备上行消息数量统计
*
* @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);
}

View File

@@ -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 upstreamtrue 上行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 @supergetDeviceLogDownCountByHour 融合到 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());
}
}

View File

@@ -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 &lt;= #{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 &lt;= #{endTime}
</if>
AND upstream = false
</where>
GROUP BY TIMETRUNCATE(ts, 1h)
ORDER BY time ASC
GROUP BY time
</select>
</mapper>