yeg
2026-09-03 76cd5948e95ddbde9dd4411b466ee75ba20b562e
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImpl.java
@@ -11,6 +11,7 @@
import java.util.List;
import com.alibaba.fastjson2.JSON;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.device.bts.BtsDevelopmentSimulator;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsHistoryRecord;
@@ -23,22 +24,30 @@
import com.ruoyi.system.service.ITTestService;
import com.ruoyi.system.service.ITestDataReceptionService;
/** 接收时确定测试归属,并以现有采样表追加保存检测柜事实。 */
/** 接收时确定测试归属,分别保留检测柜、采集器原始事实,并形成统一实时快照。 */
@Service
public class TestDataReceptionServiceImpl implements ITestDataReceptionService
{
    private final TTestMapper mapper;
    private final ITTestService tests;
    private final BtsDevelopmentSimulator simulator;
    private final TestWarningExceptionService warningExceptions;
    private final Map<String, Instant> lastSnapshotAt = new java.util.concurrent.ConcurrentHashMap<>();
    private final Map<Long, Instant> lastRawCaptureAt = new java.util.concurrent.ConcurrentHashMap<>();
    public TestDataReceptionServiceImpl(TTestMapper mapper, ITTestService tests, BtsDevelopmentSimulator simulator)
    @Autowired
    public TestDataReceptionServiceImpl(TTestMapper mapper, ITTestService tests, BtsDevelopmentSimulator simulator,
            TestWarningExceptionService warningExceptions)
    {
        this.mapper = mapper;
        this.tests = tests;
        this.simulator = simulator;
        this.warningExceptions = warningExceptions;
    }
    /** 保留单元测试及旧调用方构造契约。 */
    public TestDataReceptionServiceImpl(TTestMapper mapper, ITTestService tests, BtsDevelopmentSimulator simulator)
    { this(mapper, tests, simulator, new TestWarningExceptionService(mapper)); }
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -169,7 +178,15 @@
            {
                // 模拟器保留通道历史;只接收本次测试开始后产生的设备时间记录,避免顺序测试串入旧事实。
                if (startedAt != null && record.deviceTime().isBefore(startedAt.toInstant())) continue;
                save(test, binding, record);
                if (save(test, binding, record)) {
                    String source = String.join(":", java.util.Objects.toString(binding.getAdapterCode(), ""),
                            java.util.Objects.toString(binding.getDevtype(), ""), java.util.Objects.toString(binding.getDevid(), ""),
                            java.util.Objects.toString(binding.getSubdevid(), ""), java.util.Objects.toString(binding.getChlid(), ""));
                    warningExceptions.evaluate(test, "CABINET", source, "VOLTAGE", BigDecimal.valueOf(record.voltageV()), "V", Date.from(record.deviceTime()));
                    warningExceptions.evaluate(test, "CABINET", source, "CURRENT", BigDecimal.valueOf(record.currentA()), "A", Date.from(record.deviceTime()));
                    warningExceptions.evaluate(test, "CABINET", source, "CAPACITY", BigDecimal.valueOf(record.capacityAh()), "Ah", Date.from(record.deviceTime()));
                    warningExceptions.evaluate(test, "CABINET", source, "ENERGY", BigDecimal.valueOf(record.energyWh()), "Wh", Date.from(record.deviceTime()));
                }
            }
        }
    }
@@ -192,11 +209,12 @@
                    java.util.Objects.equals(mapping.getChannelCode(), measurement.channelCode())
                    && metricMatches(mapping.getMeasurementCode(), measurement.metricCode()));
            if (!mapped) continue;
            saveCollector(test, battery, measurement);
            if (saveCollector(test, battery, measurement))
                warningExceptions.evaluate(test, "COLLECTOR", measurement.adapterCode() + ":" + battery.getCollectorId() + ":" + measurement.channelCode(), measurement.metricCode(), measurement.value(), measurement.unit(), Date.from(measurement.deviceTime()));
        }
    }
    private void saveCollector(TTest test, TTestBattery battery, CollectorRealtimeMeasurement measurement)
    private boolean saveCollector(TTest test, TTestBattery battery, CollectorRealtimeMeasurement measurement)
    {
        byte[] hash = hash(measurement);
        String old = mapper.selectCollectorSampleContentHash(battery.getCollectorId(), measurement.channelCode(),
@@ -209,7 +227,7 @@
                        "采集器实时接收内容冲突 collector=" + battery.getCollectorId() + ", channel="
                                + measurement.channelCode() + ", metric=" + measurement.metricCode()
                                + ", seqid=" + measurement.sequenceId());
            return;
            return false;
        }
        Date receivedAt = new Date();
        int inserted = mapper.insertCollectorSample(test.getTestId(), battery.getBinding().getBindingId(), battery.getCollectorId(),
@@ -228,9 +246,10 @@
                                + measurement.channelCode() + ", metric=" + measurement.metricCode()
                                + ", seqid=" + measurement.sequenceId());
        }
        return inserted > 0;
    }
    private void save(TTest test, TTestChannelBinding binding,
    private boolean save(TTest test, TTestChannelBinding binding,
            BtsHistoryRecord record)
    {
        // 模拟器历史 testid 按通道固定;接收事实必须再带测试作用域,避免顺序测试共用全局幂等键。
@@ -242,7 +261,7 @@
            if (!old.equalsIgnoreCase(HexFormat.of().formatHex(hash)))
                mapper.insertSampleConflict(test.getTestId(), binding.getChannelId(),
                        "实时接收内容冲突 testid=" + btsTestId + ", seqid=" + record.sequenceId());
            return;
            return false;
        }
        int inserted = mapper.insertBtsSample(test.getTestId(), binding.getBindingId(), binding.getChannelId(),
                btsTestId, record.sequenceId(), Date.from(record.deviceTime()),
@@ -257,6 +276,7 @@
                mapper.insertSampleConflict(test.getTestId(), binding.getChannelId(),
                        "实时接收并发内容冲突 testid=" + btsTestId + ", seqid=" + record.sequenceId());
        }
        return inserted > 0;
    }
    private byte[] hash(BtsHistoryRecord record)