yeg
2026-09-09 403eed51f3687479448ef31028004ee6f04e7bdc
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
package com.battery.business.report;
 
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.math.BigDecimal;
import java.util.*;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.battery.business.domain.*;
import com.battery.business.service.*;
import com.battery.device.api.StepDirectionResolver.Direction;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
 
class FormationRealReportTest
{
    private final ObjectMapper mapper = new ObjectMapper();
    private final ITTestService tests = mock(ITTestService.class);
    private final ITestDataQueryService data = mock(ITestDataQueryService.class);
    private final FormationReportService service;
 
    FormationRealReportTest()
    {
        var db = new JdbcDataSource();
        db.setURL("jdbc:h2:mem:formation_" + UUID.randomUUID() + ";MODE=MySQL;DB_CLOSE_DELAY=-1");
        var jdbc = new JdbcTemplate(db);
        jdbc.execute("CREATE TABLE t_formation_report(report_id BIGINT AUTO_INCREMENT PRIMARY KEY,report_name VARCHAR(100),creator_id BIGINT,creator_name VARCHAR(100),created_at_utc TIMESTAMP)");
        jdbc.execute("CREATE TABLE t_formation_report_version(report_id BIGINT,version_no INT,snapshot_json CLOB,operator_id BIGINT,operator_name VARCHAR(100),created_at_utc TIMESTAMP,change_summary VARCHAR(500),PRIMARY KEY(report_id,version_no))");
        service = new FormationReportService(tests, data, new FormationReportRepository(db), mock(ITBatteryGroupFieldService.class),
                type -> "cc".equals(type) ? Direction.CHARGE : "dc".equals(type) ? Direction.DISCHARGE : Direction.UNKNOWN);
        var battery = new TTestBattery(); battery.setTestBatteryId(11L); battery.setBatteryNo("电池1"); battery.setGroupId(1L); battery.setGroupName("组1");
        battery.setFields(new ArrayList<>());
        Map.of("H", "2", "Z", "3", "A", "4", "F", "5", "B", "6").forEach((name, value) -> {
            var field = new CurrentBatteryField(); field.setFieldName(name); field.setFieldValue(value); battery.getFields().add(field);
        });
        var test = new TTest(); test.setTestId(1L); test.setTestName("化成甲"); test.setTestStatus("COMPLETED"); test.setTestTypeName("化成"); test.setBatteries(List.of(battery));
        when(tests.selectTestById(1L)).thenReturn(test);
        when(data.records(1L)).thenReturn(List.of()); when(data.cycles(1L)).thenReturn(List.of());
        when(data.steps(1L)).thenReturn(List.of(step(1, "cc", "4.1", "0.1"), step(2, "cc", "4.2", "0.2"), step(3, "dc", "1", "0.25")));
    }
 
    private Map<String, Object> step(int no, String type, String voltage, String capacity)
    {
        var row = new LinkedHashMap<String, Object>(); row.put("batteryNo", "电池1"); row.put("cycleNo", 1); row.put("stepNo", no); row.put("stepType", type);
        row.put("endVoltage", new BigDecimal(voltage)); row.put("capacity", capacity == null ? null : new BigDecimal(capacity)); return row;
    }
 
    private JSONObject request()
    {
        return JSON.parseObject("""
            {"name":"化成真实报表","description":"","selections":[{"testId":1,"testBatteryId":11}],
             "settings":{"fields":[{"key":"manual","scope":"cycle","kind":"manual","displayName":"数字","unit":""}],
              "values":{"1|11|1|manual":"2"},"chargeStepSelections":{}}}
            """);
    }
 
    private JSONObject create(JSONObject request) throws Exception
    { return JSON.parseObject(JSON.toJSONString(service.create(mapper.readValue(request.toJSONString(), FormationReportRequest.class), 1, "操作员"))); }
 
    @Test
    void freezesFormulaInputsAndLastDischargeCapacityWithoutDependingOnDisplayedFields() throws Exception
    {
        var saved = create(request());
        var values = saved.getJSONObject("calculatedValues");
        assertNotNull(values, "公式结果必须随版本保存");
        assertEquals(720, values.getBigDecimal("1|11|0|C").intValueExact());
        assertEquals(240, values.getBigDecimal("1|11|1|chargePerGram").intValueExact());
        assertEquals(288, values.getBigDecimal("1|11|1|chargePerGramTotal").intValueExact());
        assertEquals(250, values.getBigDecimal("1|11|1|capacity1v").intValueExact());
        assertEquals(50, values.getBigDecimal("1|11|1|capacity1vf").intValueExact());
        assertEquals(5, saved.getJSONArray("items").getJSONObject(0).getJSONArray("businessFields").size());
        assertEquals(1, service.list().get(0).versionNo());
        clearInvocations(tests, data);
        assertEquals(values, JSON.parseObject(JSON.toJSONString(service.get(saved.getLongValue("id"), null))).getJSONObject("calculatedValues"));
        verifyNoInteractions(tests, data);
    }
 
    @Test
    void reportFieldEditsRecalculateWithoutReadingOrChangingDeletedSource() throws Exception
    {
        var request = request();
        request.getJSONObject("settings").getJSONArray("fields").add(JSON.parseObject("{\"key\":\"F\",\"scope\":\"detail\",\"kind\":\"battery\",\"displayName\":\"F\",\"unit\":\"\"}"));
        var first = create(request); long id = first.getLongValue("id");
        clearInvocations(tests, data);
        when(tests.selectTestById(1L)).thenThrow(new IllegalStateException("来源已删除"));
        request.remove("selections"); request.put("baseVersionNo", 1); request.put("changeSummary", "修改报表 F");
        request.getJSONObject("settings").getJSONObject("values").put("1|11|0|F", "10");
        var second = JSON.parseObject(JSON.toJSONString(service.edit(id, mapper.readValue(request.toJSONString(), FormationReportRequest.Edit.class), 1, "操作员")));
        assertEquals(1440, second.getJSONObject("calculatedValues").getBigDecimal("1|11|0|C").intValueExact());
        assertEquals(480, second.getJSONObject("calculatedValues").getBigDecimal("1|11|1|chargePerGram").intValueExact());
        assertEquals(25, second.getJSONObject("calculatedValues").getBigDecimal("1|11|1|capacity1vf").intValueExact());
        assertEquals(720, JSON.parseObject(JSON.toJSONString(service.get(id, 1))).getJSONObject("calculatedValues").getBigDecimal("1|11|0|C").intValueExact());
        verifyNoInteractions(tests, data);
    }
 
    @Test
    void missingLastDischargeDoesNotBorrowEarlierCapacityAndManualMustBeNonnegative() throws Exception
    {
        when(data.steps(1L)).thenReturn(List.of(step(1,"dc","1","0.25"),step(2,"dc","1",null)));
        var saved=create(request());
        assertNull(saved.getJSONObject("calculatedValues").get("1|11|1|capacity1v"));
        var bad=request();bad.getJSONObject("settings").getJSONObject("values").put("1|11|1|manual","-1");
        assertThrows(com.ruoyi.common.exception.ServiceException.class,()->create(bad));
        bad.getJSONObject("settings").getJSONObject("values").put("1|11|1|manual","电量");
        assertThrows(com.ruoyi.common.exception.ServiceException.class,()->create(bad));
    }
 
    @Test
    void automaticDirectionSelectionExcludesUnknownAndKeepsAllRealChargeSteps() throws Exception
    {
        when(data.steps(1L)).thenReturn(List.of(step(1,"cc","4.1","0.1"),step(2,"unknown","99","99"),step(3,"cc","4.2","0.2"),step(4,"dc","1","0.25")));
        var request=request();
        request.getJSONObject("settings").put("manualChargeSteps",Map.of("1|11|1",List.of(2)));
        request.getJSONObject("settings").put("curveStepSelections",Map.of("1|11|1",Map.of("charge",List.of(1,2),"discharge",List.of(4))));
        var saved=create(request);
        var selected=saved.getJSONObject("settings").getJSONObject("curveStepSelections").getJSONObject("1|11|1");
        assertEquals(List.of(1,3),selected.getList("charge",Integer.class));
        assertEquals(List.of(4),selected.getList("discharge",Integer.class));
        assertEquals(250,saved.getJSONObject("calculatedValues").getBigDecimal("1|11|1|capacity1v").intValueExact());
    }
}