yeg
2026-09-06 ac360fe22ae21722c3af52ef727c8456f5f100e7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package com.battery.business.web;
 
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import com.alibaba.fastjson2.JSON;
import com.battery.business.domain.TTest;
import com.battery.business.mapper.TTestMapper;
import com.battery.business.service.impl.TestConsoleDataServiceImpl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.test.util.ReflectionTestUtils;
 
/** 从控制台提交入口验证单页导入,数据库使用边界替身。 */
class ConsoleSingleSheetImportTest
{
    private TTestMapper mapper;
    private TTestController controller;
    private final List<Map<String, Object>> saved = new ArrayList<>();
 
    @BeforeEach void setUp()
    {
        mapper = mock(TTestMapper.class);
        TTest test = new TTest(); test.setTestStatus("COMPLETED");
        when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test);
        when(mapper.selectImportBinding(1L, "BAT-1")).thenReturn(Map.of(
                "test_battery_id", 4L, "binding_id", 5L, "adapter_code", "adapter-a",
                "devtype", 27, "devid", 1, "subdevid", 1, "chlid", 1));
        when(mapper.selectImportBinding(1L, "BAT-2")).thenReturn(Map.of(
                "test_battery_id", 6L, "binding_id", 7L, "adapter_code", "adapter-a",
                "devtype", 27, "devid", 1, "subdevid", 1, "chlid", 2));
        when(mapper.selectImportDataRecords(eq(1L), anyString())).thenAnswer(call -> new ArrayList<>(saved));
        when(mapper.insertImportedDataRecord(anyString(), anyMap())).thenAnswer(call -> {
            saved.add(new LinkedHashMap<>(call.getArgument(1))); return 1;
        });
        controller = new TTestController() {
            @Override public Long getUserId() { return 1L; }
            @Override public String getUsername() { return "tester"; }
        };
        ReflectionTestUtils.setField(controller, "consoleDataService", new TestConsoleDataServiceImpl(mapper));
    }
 
    @Test void identicalRowsInOneSheetAreSkipped()
    {
        var response = submit("REALTIME", List.of(row(), row()));
        assertEquals(1, response.get("importedCount"));
        assertEquals(1, response.get("skippedCount"));
    }
 
    @Test void allThreeTypesSkipRetryButRejectChangedContent()
    {
        for (String type : List.of("REALTIME", "STEP", "CYCLE"))
        {
            saved.clear();
            var row = row();
            row.put("deviceTimeRaw", "2023-05-08 11:42:11"); row.put("adapterTimezoneId", "Asia/Hong_Kong");
            row.put("capacity", 8.5122); row.put("dischargeCapacity", 8.5122);
            assertEquals(1, submit(type, List.of(row)).get("importedCount"));
            row.put("sourceRow", 19);
            row.put("capacity", new java.math.BigDecimal("8.512200"));
            var repeated = submit(type, List.of(row));
            assertEquals(0, repeated.get("importedCount"));
            assertEquals(1, repeated.get("skippedCount"));
            assertNull(repeated.get("actionId"));
            row.put("cycleNo", 2);
            var failure = assertThrows(ServiceException.class, () -> submit(type, List.of(row)));
            assertTrue(failure.getMessage().contains("第19行,列「记录编号」"));
            assertEquals(1, saved.size());
        }
    }
 
    @Test void sameRecordNumberForDifferentBatteriesHasIndependentIdentity()
    {
        var other = row(); other.put("batteryNo", "BAT-2");
        assertEquals(2, submit("REALTIME", List.of(row(), other)).get("importedCount"));
        assertEquals("adapter-a:27:1:1:1", saved.get(0).get("source_identity"));
        assertEquals("adapter-a:27:1:1:2", saved.get(1).get("source_identity"));
    }
 
    @Test void pollingSlotIsNotPartOfImportedDeviceContent()
    {
        submit("REALTIME", List.of(row()));
        saved.get(0).put("capture_slot_utc", new java.util.Date(1683517380000L));
        assertEquals(1, submit("REALTIME", List.of(row())).get("skippedCount"));
    }
 
    @Test void anyInvalidRowLeavesWholeSheetAndRevisionsUnwritten()
    {
        for (Map.Entry<String, Object> invalid : Map.<String, Object>of("cycleNo", 0, "recordedAt", "2023-05-08",
                "voltage", "NaN", "batteryNo", "OTHER").entrySet())
        {
            var bad = row(); bad.put(invalid.getKey(), invalid.getValue()); bad.put("sourceRow", 23);
            var failure = assertThrows(ServiceException.class, () -> submit("REALTIME", List.of(row(), bad)));
            assertTrue(failure.getMessage().contains("第23行,列「"));
            assertTrue(saved.isEmpty());
        }
        verify(mapper, never()).insertDataRevisionAction(any());
    }
 
    @Test void conflictAfterValidRowDoesNotWriteValidPrefixOrAudit()
    {
        submit("REALTIME", List.of(row()));
        clearInvocations(mapper);
        var next = row(); next.put("recordNo", "18"); next.put("recordedAt", 1683517391000L);
        var conflict = row(); conflict.put("voltage", 9);
        assertThrows(ServiceException.class, () -> submit("REALTIME", List.of(next, conflict)));
        assertEquals(1, saved.size());
        verify(mapper, never()).insertDataRevisionAction(any());
        var sameSecond = row(); sameSecond.put("recordNo", "19"); sameSecond.put("sequenceId", 19);
        assertEquals(1, submit("REALTIME", List.of(sameSecond)).get("importedCount"));
    }
 
    private Map<String, Object> row()
    {
        return new LinkedHashMap<>(Map.of("recordNo", "17", "batteryNo", "BAT-1", "recordedAt", 1683517331000L,
                "cycleNo", 1, "stepNo", 1, "stepName", "恒流放电", "sequenceId", 17, "voltage", 1.3349));
    }
 
    private com.ruoyi.common.core.domain.AjaxResult submit(String type, List<Map<String, Object>> rows)
    {
        return controller.dataAction(1L, new TTestController.ConsoleDataActionRequest(type, "IMPORT",
                "单页导入", "[]", JSON.toJSONString(rows), "开发测试", List.of()));
    }
}