liu
20 小时以前 f57a9de2dbe69e7f867b39c25abdab58b401a2df
fix(mes): 过滤异常未来遥测时间
已添加1个文件
已修改2个文件
58 ■■■■■ 文件已修改
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/dv/telemetry/MesDvTelemetryMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryServiceImplTest.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/dv/telemetry/MesDvTelemetryMapper.java
@@ -8,6 +8,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.telemetry.MesDvTelemetryDO;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -26,6 +27,7 @@
                .eqIfPresent(MesDvTelemetryDO::getWhetherAnomaly, reqVO.getWhetherAnomaly())
                .eqIfPresent(MesDvTelemetryDO::getShiftName, reqVO.getShiftName())
                .betweenIfPresent(MesDvTelemetryDO::getTelemetryDataTime, reqVO.getTelemetryDataTime())
                .le(MesDvTelemetryDO::getTelemetryDataTime, LocalDateTime.now().plusMinutes(5))
                .orderByDesc(MesDvTelemetryDO::getTelemetryDataTime)
                .orderByDesc(MesDvTelemetryDO::getId));
    }
@@ -34,6 +36,7 @@
        return selectList(new LambdaQueryWrapperX<MesDvTelemetryDO>()
                .eqIfPresent(MesDvTelemetryDO::getTbDeviceId, reqVO.getTbDeviceId())
                .likeIfPresent(MesDvTelemetryDO::getDeviceName, reqVO.getDeviceName())
                .le(MesDvTelemetryDO::getTelemetryDataTime, LocalDateTime.now().plusMinutes(5))
                .orderByDesc(MesDvTelemetryDO::getTelemetryDataTime)
                .orderByDesc(MesDvTelemetryDO::getId)
                .last("LIMIT " + limit));
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryServiceImpl.java
@@ -38,6 +38,8 @@
@Slf4j
public class MesDvTelemetryServiceImpl implements MesDvTelemetryService {
    static final long MAX_FUTURE_SKEW_MINUTES = 5;
    @Resource
    private MesDvTelemetryMapper telemetryMapper;
    @Resource
@@ -53,7 +55,12 @@
        // å–最近一批数据,按设备+信号去重保留最新,用于实时监控视图
        List<MesDvTelemetryDO> list = telemetryMapper.selectLatest(reqVO, 5000);
        Map<String, MesDvTelemetryDO> latestMap = new LinkedHashMap<>();
        LocalDateTime queryTime = LocalDateTime.now();
        for (MesDvTelemetryDO row : list) {
            // åŒé‡é˜²æŠ¤ï¼šå³ä½¿æ•°æ®åº“中已有未来时间的脏数据,也不能作为实时数据返回
            if (!isAcceptableTelemetryTime(row.getTelemetryDataTime(), queryTime)) {
                continue;
            }
            String key = StrUtil.blankToDefault(row.getTbDeviceId(), "")
                    + ":" + StrUtil.blankToDefault(row.getParamKeyName(), "");
            latestMap.putIfAbsent(key, row);
@@ -149,10 +156,17 @@
                continue;
            }
            MesDvTelemetryDO data = convert((Map<?, ?>) rowObj);
            if (!isAcceptableTelemetryTime(data.getTelemetryDataTime(), pullTime)) {
                log.warn("[pullTelemetry][忽略异常遥测时间,deviceId({}),telemetryDataTime({}),pullTime({})]",
                        data.getTbDeviceId(), data.getTelemetryDataTime(), pullTime);
                continue;
            }
            data.setPullTime(pullTime);
            saveList.add(data);
        }
        telemetryMapper.insertBatch(saveList);
        if (CollUtil.isNotEmpty(saveList)) {
            telemetryMapper.insertBatch(saveList);
        }
        // 6. è¿”回统计
        result.setRecordCount(saveList.size());
        result.setDeviceCount((int) saveList.stream()
@@ -204,4 +218,13 @@
        }
    }
    /**
     * é¥æµ‹æ—¶é—´å¿…须存在,且不能明显晚于本次拉取/查询时间。
     * ä¿ç•™ 5 åˆ†é’Ÿå®¹å·®ï¼Œå…¼å®¹è®¾å¤‡æ—¶é’Ÿä¸Žåº”用服务器之间的轻微偏差。
     */
    static boolean isAcceptableTelemetryTime(LocalDateTime telemetryTime, LocalDateTime referenceTime) {
        return telemetryTime != null && referenceTime != null
                && !telemetryTime.isAfter(referenceTime.plusMinutes(MAX_FUTURE_SKEW_MINUTES));
    }
}
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/dv/telemetry/MesDvTelemetryServiceImplTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.service.dv.telemetry;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class MesDvTelemetryServiceImplTest {
    private static final LocalDateTime REFERENCE_TIME = LocalDateTime.of(2026, 11, 7, 15, 30);
    @Test
    void isAcceptableTelemetryTime_acceptsCurrentTimeAndClockSkew() {
        assertTrue(MesDvTelemetryServiceImpl.isAcceptableTelemetryTime(REFERENCE_TIME, REFERENCE_TIME));
        assertTrue(MesDvTelemetryServiceImpl.isAcceptableTelemetryTime(
                REFERENCE_TIME.plusMinutes(5), REFERENCE_TIME));
    }
    @Test
    void isAcceptableTelemetryTime_rejectsMissingAndFutureTime() {
        assertFalse(MesDvTelemetryServiceImpl.isAcceptableTelemetryTime(null, REFERENCE_TIME));
        assertFalse(MesDvTelemetryServiceImpl.isAcceptableTelemetryTime(
                LocalDateTime.of(2026, 12, 24, 0, 0), REFERENCE_TIME));
        assertFalse(MesDvTelemetryServiceImpl.isAcceptableTelemetryTime(
                REFERENCE_TIME.plusMinutes(5).plusNanos(1), REFERENCE_TIME));
    }
}