yeg
2026-09-05 a5487f4ce3594611eaffed5ed14bb2ec6b88adf0
整理Ticket08验收后注释
3个文件已修改
9 ■■■■■ 已修改文件
battery-device-api/src/main/java/com/battery/device/api/TestDataObservationAdapter.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/db/migration-final/V3__test_data_persistence.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
battery-device-api/src/main/java/com/battery/device/api/TestDataObservationAdapter.java
@@ -9,14 +9,19 @@
/** 供业务持久化使用的厂商无关测试数据观测扩展。 */
public interface TestDataObservationAdapter extends DeviceObservationAdapter
{
    /** 返回指定检测柜通道最近一次实时观测;没有成功观测时返回空值。 */
    CabinetRealtime latestCabinet(ChannelIdentity channel);
    /** 返回采集器独立实时快照;不得通过历史下载补采。 */
    CollectorRealtime latestCollector(long collectorId);
    /** 返回设备直接提供的工步统计,不从实时采样推导。 */
    List<StepRecord> stepRecords(ChannelIdentity channel);
    /** 返回设备直接提供的循环统计,不补齐设备未返回的记录。 */
    List<CycleRecord> cycleRecords(ChannelIdentity channel);
    /** 返回适配器定义的实时持久化槽位间隔。 */
    long persistenceIntervalMillis(ChannelIdentity channel);
    record CabinetRealtime(long sequenceId, Instant deviceTime, double currentA, double voltageV,
ruoyi-admin/src/main/resources/db/migration-final/V3__test_data_persistence.sql
@@ -1,3 +1,4 @@
-- Ticket08:补齐三类测试事实的绑定、接收时间、来源编号和幂等键;原始事实只追加不覆盖。
ALTER TABLE t_test_realtime_record
    ADD COLUMN source_identity VARCHAR(256) NULL AFTER adapter_code,
    ADD COLUMN source_record_id VARCHAR(128) NULL AFTER source_identity;
@@ -35,6 +36,7 @@
    MODIFY COLUMN dedupe_key VARCHAR(512) NOT NULL,
    MODIFY COLUMN server_received_at_utc DATETIME(3) NOT NULL,
    DROP INDEX uk_step_identity,
    -- 前缀索引控制 InnoDB 索引长度,同时保留来源身份和幂等键的稳定区分。
    ADD UNIQUE KEY uk_step_dedupe (test_battery_id, source_identity(128), dedupe_key(128)),
    ADD KEY idx_step_binding_time (binding_id, server_received_at_utc);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImpl.java
@@ -100,6 +100,7 @@
    private void saveRealtime(TTest test, TTestBattery battery)
    {
        // 柜端和采集器端分别判断成功;同一槽位只写一行,失败来源的身份、时间和指标保持为空。
        if (adapter == null) return;
        TTestChannelBinding binding = battery.getBinding(); if (binding == null || binding.getDevid() == null || binding.getChlid() == null) return;
        Instant now = clock.instant(); long interval = persistenceInterval(binding); String key = test.getTestId() + ":" + battery.getTestBatteryId();
@@ -141,6 +142,7 @@
    private void saveDeviceAggregates(TTest test, TTestBattery battery)
    {
        // 工步、循环只接受设备直接返回的统计记录,并以来源编号或明确的时间组合执行幂等。
        if (adapter == null) return; TTestChannelBinding binding = battery.getBinding(); if (binding == null || binding.getDevid() == null || binding.getChlid() == null) return;
        ChannelIdentity channel = new ChannelIdentity(binding.getAdapterCode(), binding.getDevtype(), binding.getDevid(), binding.getSubdevid(), binding.getChlid());
        List<StepRecord> steps; List<CycleRecord> cycles; try { steps = adapter.stepRecords(channel); cycles = adapter.cycleRecords(channel); } catch (RuntimeException ex) { return; }