yeg
2026-09-02 4220194f54535388daad46136f8da0452441b806
补齐控制台来源身份与时间字段

查询返回适配器和设备通道完整身份以及工步循环服务器接收时间,避免不同来源碰撞。
4个文件已修改
73 ■■■■■ 已修改文件
ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TestDataQueryServiceEffectiveDataTest.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestConsoleDataServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataQueryServiceImpl.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TTestMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TestDataQueryServiceEffectiveDataTest.java
@@ -28,12 +28,12 @@
    void appliesRealtimeEditAndFiltersDeletedRow()
    {
        when(tests.selectTestById(7L)).thenReturn(new TTest());
        Map<String, Object> edited = row("deviceId", 22L, "channelId", 8L, "sequenceId", 3L,
        Map<String, Object> edited = row("adapterId", 1L, "devtype", 22, "deviceId", 22L, "subdevid", 0, "channelId", 8L, "sequenceId", 3L,
                "voltage", 3.7d);
        Map<String, Object> deleted = row("deviceId", 22L, "channelId", 8L, "sequenceId", 4L,
        Map<String, Object> deleted = row("adapterId", 1L, "devtype", 22, "deviceId", 22L, "subdevid", 0, "channelId", 8L, "sequenceId", 4L,
                "voltage", 3.8d);
        TTestDataOverride edit = override("22:8:3", "{\"voltage\":\"4.1000\"}", false);
        TTestDataOverride remove = override("22:8:4", null, true);
        TTestDataOverride edit = override("1:22:22:0:8:3", "{\"voltage\":\"4.1000\"}", false);
        TTestDataOverride remove = override("1:22:22:0:8:4", null, true);
        when(mapper.selectPersistedRecords(7L)).thenReturn(List.of(edited, deleted));
        when(mapper.selectDataOverrides(7L, "REALTIME")).thenReturn(List.of(edit, remove));
@@ -48,15 +48,15 @@
    void usesStepAndCycleStableIdentities()
    {
        when(tests.selectTestById(8L)).thenReturn(new TTest());
        Map<String, Object> step = row("deviceId", 22L, "channelId", 8L, "cycleNo", 2, "stepNo", 4,
        Map<String, Object> step = row("adapterId", 1L, "devtype", 22, "deviceId", 22L, "subdevid", 0, "channelId", 8L, "cycleNo", 2, "stepNo", 4,
                "capacity", 1d);
        Map<String, Object> cycle = row("deviceId", 22L, "channelId", 8L, "cycleNo", 2,
        Map<String, Object> cycle = row("adapterId", 1L, "devtype", 22, "deviceId", 22L, "subdevid", 0, "channelId", 8L, "cycleNo", 2,
                "efficiency", 90d);
        when(mapper.selectPersistedSteps(8L)).thenReturn(List.of(step));
        when(mapper.selectPersistedCycles(8L)).thenReturn(List.of(cycle));
        when(mapper.selectDataOverrides(8L, "STEP")).thenReturn(List.of(override("step:22:8:2:4",
        when(mapper.selectDataOverrides(8L, "STEP")).thenReturn(List.of(override("step:1:22:22:0:8:2:4",
                "{\"capacity\":\"2.0000\"}", false)));
        when(mapper.selectDataOverrides(8L, "CYCLE")).thenReturn(List.of(override("cycle:22:8:2",
        when(mapper.selectDataOverrides(8L, "CYCLE")).thenReturn(List.of(override("cycle:1:22:22:0:8:2",
                "{\"efficiency\":\"95.00\"}", false)));
        TestDataQueryServiceImpl service = new TestDataQueryServiceImpl(mapper, tests);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestConsoleDataServiceImpl.java
@@ -227,16 +227,25 @@
    private Long[] parseDeleteIdentity(String value, String dataType)
    {
        String[] parts = value.split(":", -1);
        int expectedLength = "STEP".equals(dataType) ? 5 : "CYCLE".equals(dataType) ? 4 : 3;
        if (parts.length != expectedLength) return null;
        int expectedLength = "STEP".equals(dataType) ? 8 : "CYCLE".equals(dataType) ? 7 : 6;
        int legacyLength = "STEP".equals(dataType) ? 5 : "CYCLE".equals(dataType) ? 4 : 3;
        if (parts.length != expectedLength && parts.length != legacyLength) return null;
        int offset = "REALTIME".equals(dataType) ? 0 : 1;
        try
        {
            if (parts.length == legacyLength && "STEP".equals(dataType))
                return new Long[] { Long.valueOf(parts[1]), Long.valueOf(parts[2]),
                        Long.valueOf(parts[3]), Long.valueOf(parts[4]) };
            if (parts.length == legacyLength)
                return new Long[] { Long.valueOf(parts[0]), Long.valueOf(parts[1]), Long.valueOf(parts[2]) };
            if ("STEP".equals(dataType))
                return new Long[] { Long.valueOf(parts[offset]), Long.valueOf(parts[offset + 1]),
                        Long.valueOf(parts[offset + 2]), Long.valueOf(parts[offset + 3]) };
                        Long.valueOf(parts[offset + 2]), Long.valueOf(parts[offset + 3]),
                        Long.valueOf(parts[offset + 4]), Long.valueOf(parts[offset + 5]),
                        Long.valueOf(parts[offset + 6]) };
            return new Long[] { Long.valueOf(parts[offset]), Long.valueOf(parts[offset + 1]),
                    Long.valueOf(parts[offset + 2]) };
                    Long.valueOf(parts[offset + 2]), Long.valueOf(parts[offset + 3]),
                    Long.valueOf(parts[offset + 4]), Long.valueOf(parts[offset + 5]) };
        }
        catch (NumberFormatException exception) { return null; }
    }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TestDataQueryServiceImpl.java
@@ -81,18 +81,27 @@
    private String stableIdentity(String dataType, Map<String, Object> row)
    {
        String deviceId = value(row, "deviceId");
        String channelId = value(row, "channelId");
        String adapterId = identityPart(row, "adapterId");
        String devtype = identityPart(row, "devtype");
        String deviceId = identityPart(row, "deviceId");
        String subdevid = identityPart(row, "subdevid");
        String channelId = identityPart(row, "channelId");
        String cycleNo = value(row, "cycleNo");
        if ("REALTIME".equals(dataType)) return deviceId + ":" + channelId + ":" + value(row, "sequenceId");
        if ("STEP".equals(dataType)) return "step:" + deviceId + ":" + channelId + ":" + cycleNo + ":" + value(row, "stepNo");
        return "cycle:" + deviceId + ":" + channelId + ":" + cycleNo;
        String channelIdentity = String.join(":", adapterId, devtype, deviceId, subdevid, channelId);
        if ("REALTIME".equals(dataType)) return channelIdentity + ":" + value(row, "sequenceId");
        if ("STEP".equals(dataType)) return "step:" + channelIdentity + ":" + cycleNo + ":" + value(row, "stepNo");
        return "cycle:" + channelIdentity + ":" + cycleNo;
    }
    private String identityPart(Map<String, Object> row, String key)
    {
        Object value = row == null ? null : row.get(key);
        return value == null ? "" : String.valueOf(value);
    }
    private String value(Map<String, Object> row, String key)
    {
        Object value = row == null ? null : row.get(key);
        return value == null ? "" : String.valueOf(value);
        return identityPart(row, key);
    }
    private void requireTest(Long testId)
ruoyi-system/src/main/resources/mapper/system/TTestMapper.xml
@@ -326,7 +326,8 @@
               sample.device_time_utc recordedAt, sample.device_time_utc absoluteTime,
               sample.device_time_utc deviceTime, sample.received_at_utc serverReceivedAt,
               sample.voltage_v voltage, sample.current_a current, sample.capacity_ah capacity,
               sample.energy_wh energy, channel.devid deviceId, channel.chlid channelId
               sample.energy_wh energy, channel.adapter_id adapterId, channel.adapter_code adapterCode,
               channel.devtype devtype, channel.devid deviceId, channel.subdevid subdevid, channel.chlid channelId
        from t_test_sample sample
        join t_test_battery battery on battery.relation_id = sample.binding_id and battery.del_flag = 0
        join t_detection_channel channel on channel.channel_id = sample.channel_id and channel.del_flag = 0
@@ -341,18 +342,21 @@
               sample.step_type stepType, coalesce(sample.step_name, sample.step_type) actualStepLabel,
               count(*) recordCount,
               min(sample.device_time_utc) recordedAt, min(sample.device_time_utc) absoluteTime,
               max(sample.received_at_utc) serverReceivedAt,
               timestampdiff(second, min(sample.device_time_utc), max(sample.device_time_utc)) stepDuration,
               max(sample.capacity_ah) capacity, max(sample.energy_wh) energy,
               min(sample.voltage_v) startVoltage,
               max(sample.voltage_v) endVoltage,
               max(case when sample.current_a &gt;= 0 then sample.voltage_v end) chargePeakVoltage,
               min(case when sample.current_a &lt; 0 then sample.voltage_v end) dischargeValleyVoltage,
               channel.devid deviceId, channel.chlid channelId
               channel.adapter_id adapterId, channel.adapter_code adapterCode, channel.devtype devtype,
               channel.devid deviceId, channel.subdevid subdevid, channel.chlid channelId
        from t_test_sample sample
        join t_test_battery battery on battery.relation_id = sample.binding_id and battery.del_flag = 0
        join t_detection_channel channel on channel.channel_id = sample.channel_id and channel.del_flag = 0
        where sample.test_id = #{testId}
        group by sample.binding_id, battery.battery_no, channel.devid, channel.chlid, sample.cycle_no, sample.step_no,
        group by sample.binding_id, battery.battery_no, channel.adapter_id, channel.adapter_code, channel.devtype,
                 channel.devid, channel.subdevid, channel.chlid, sample.cycle_no, sample.step_no,
                 sample.step_type, sample.step_name
        order by sample.cycle_no, sample.step_no, battery.battery_no
    </select>
@@ -361,6 +365,7 @@
               concat('CYCLE-', channel.devid, '-', channel.chlid, '-', sample.cycle_no) recordNo,
               battery.battery_no batteryNo, sample.cycle_no cycleNo, count(*) recordCount,
               min(sample.device_time_utc) recordedAt, min(sample.device_time_utc) absoluteTime,
               max(sample.received_at_utc) serverReceivedAt,
               greatest(max(case when sample.current_a &gt;= 0 then sample.capacity_ah end), 0) chargeCapacity,
               greatest(max(case when sample.current_a &lt; 0 then sample.capacity_ah end), 0) dischargeCapacity,
               greatest(max(case when sample.current_a &gt;= 0 then sample.energy_wh end), 0) chargeEnergy,
@@ -369,12 +374,14 @@
                    then greatest(max(case when sample.current_a &lt; 0 then sample.capacity_ah end), 0)
                         / max(case when sample.current_a &gt;= 0 then sample.capacity_ah end) * 100
                    else 0 end efficiency,
               channel.devid deviceId, channel.chlid channelId
               channel.adapter_id adapterId, channel.adapter_code adapterCode, channel.devtype devtype,
               channel.devid deviceId, channel.subdevid subdevid, channel.chlid channelId
        from t_test_sample sample
        join t_test_battery battery on battery.relation_id = sample.binding_id and battery.del_flag = 0
        join t_detection_channel channel on channel.channel_id = sample.channel_id and channel.del_flag = 0
        where sample.test_id = #{testId}
        group by sample.binding_id, battery.battery_no, channel.devid, channel.chlid, sample.cycle_no
        group by sample.binding_id, battery.battery_no, channel.adapter_id, channel.adapter_code, channel.devtype,
                 channel.devid, channel.subdevid, channel.chlid, sample.cycle_no
        order by sample.cycle_no, battery.battery_no
    </select>
    <select id="selectTestResults" parameterType="TTestResult" resultMap="TTestResultResultMap">