yeg
2026-09-01 0fcd0642efdff8d4e0584fa991aaea2178bc932b
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestBackfillServiceImpl.java
@@ -190,6 +190,15 @@
        byte[] hash = hash(sample.sequenceId() + "|" + sample.stepNo() + "|" + sample.cycleNo() + "|"
                + sample.stepType() + "|" + sample.deviceTimeRaw() + "|" + sample.voltageV() + "|"
                + sample.currentA() + "|" + sample.capacityAh() + "|" + sample.energyWh());
        String testScoped = mapper.selectTestSampleContentHash(test.getTestId(), binding.getChannelId(),
                sample.sequenceId());
        if (testScoped != null)
        {
            if (!testScoped.equalsIgnoreCase(HexFormat.of().formatHex(hash)))
                mapper.insertBackfillConflict(test.getTestId(), binding.getChannelId(),
                        "测试内采样内容冲突 seqid=" + sample.sequenceId());
            return;
        }
        String old = mapper.selectSampleContentHash(binding.getChannelId(), btsTestId, sample.sequenceId());
        if (old != null && !old.equalsIgnoreCase(HexFormat.of().formatHex(hash)))
        {
@@ -199,7 +208,8 @@
        }
        mapper.insertBtsSample(test.getTestId(), binding.getBindingId(), binding.getChannelId(),
                intervalId(intervals, sample.deviceTime()), btsTestId, sample.sequenceId(), Date.from(sample.deviceTime()),
                sample.deviceTimeRaw(), new Date(), sample.cycleNo(), sample.stepNo(), sample.stepType(), sample.testTimeMs(),
                sample.deviceTimeRaw(), new Date(), sample.cycleNo(), sample.stepNo(), sample.stepType(),
                stepNameForType(sample.stepType()), sample.testTimeMs(),
                sample.voltageV(), sample.currentA(), sample.capacityAh(), sample.energyWh(), hash);
        Long sampleId = mapper.selectBtsSampleId(binding.getChannelId(), btsTestId, sample.sequenceId());
        if (sampleId != null)
@@ -255,6 +265,18 @@
                Date.from(record.serverReceivedAt()), record.metricCode(), record.value(), record.unit(), hash);
    }
    /** BTS download 协议只提供类型码;演示协议按已确认的类型语义补出名称。 */
    private String stepNameForType(String stepType)
    {
        return switch (stepType == null ? "" : stepType.toLowerCase(java.util.Locale.ROOT))
        {
            case "cc" -> "恒流充电";
            case "dc" -> "恒流放电";
            case "rest" -> "搁置";
            default -> stepType;
        };
    }
    private boolean checkIntegrity(TTest test, Date checkedAt)
    {
        // 序号只在同一来源、同一接收区间内比较;区间边界和暂停间隔不构成缺口。