From f38ca8a9f8454ac2eebdc268c89efd0e436b3cf2 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期三, 09 九月 2026 19:58:40 +0800
Subject: [PATCH] 修复化成报表漏识别导入充放电工步

---
 ruoyi-admin/src/test/java/com/battery/business/report/FormationRealReportTest.java        |   35 +++++++++++++++++
 battery-device-bts/src/main/java/com/battery/device/bts/BtsStepDirectionResolver.java     |    1 
 battery-device-api/src/main/java/com/battery/device/api/StepDirectionResolver.java        |    4 +-
 battery-business/src/main/java/com/battery/business/report/FormationReportService.java    |   22 +++++++++--
 battery-device-bts/src/test/java/com/battery/device/bts/BtsStepDirectionResolverTest.java |    3 +
 5 files changed, 58 insertions(+), 7 deletions(-)

diff --git a/battery-business/src/main/java/com/battery/business/report/FormationReportService.java b/battery-business/src/main/java/com/battery/business/report/FormationReportService.java
index 9fc3810..d54a620 100644
--- a/battery-business/src/main/java/com/battery/business/report/FormationReportService.java
+++ b/battery-business/src/main/java/com/battery/business/report/FormationReportService.java
@@ -353,18 +353,32 @@
             Map<Integer, ChargeStep> found = new java.util.TreeMap<>();
             // 实时记录只补充实际范围身份,不代替缺失的工步终止电压。
             for (var row : realtime) if (cycleNo(row) == cycle && number(row.get("stepNo")) > 0) {
-                String type = String.valueOf(row.getOrDefault("stepType", ""));
-                found.putIfAbsent(number(row.get("stepNo")), new ChargeStep(number(row.get("stepNo")), String.valueOf(row.getOrDefault("stepName", "")), type, null, directions.resolve(type), null));
+                String type = Objects.toString(row.get("stepType"), "");
+                String name = Objects.toString(row.get("stepName"), "");
+                found.putIfAbsent(number(row.get("stepNo")), new ChargeStep(number(row.get("stepNo")), name, type, null, stepDirection(type, name), null));
             }
             if (rows != null) for (var row : rows) if (Objects.equals(battery.getBatteryNo(), row.get("batteryNo")) && cycleNo(row) == cycle && number(row.get("stepNo")) > 0) {
-                String type = String.valueOf(row.getOrDefault("stepType", ""));
-                found.put(number(row.get("stepNo")), new ChargeStep(number(row.get("stepNo")), String.valueOf(row.getOrDefault("stepName", "")), type, decimal(row.get("endVoltage")), directions.resolve(type), decimal(row.get("capacity"))));
+                String type = Objects.toString(row.get("stepType"), "");
+                String name = Objects.toString(row.get("stepName"), "");
+                found.put(number(row.get("stepNo")), new ChargeStep(number(row.get("stepNo")), name, type, decimal(row.get("endVoltage")), stepDirection(type, name), decimal(row.get("capacity"))));
             }
             result.put(cycle, new ArrayList<>(found.values()));
         }
         return result;
     }
     private int number(Object value) { return value instanceof Number n ? n.intValue() : 0; }
+
+    private Direction stepDirection(String type, String name)
+    {
+        // 甲方 Excel 只提供工步名称;有协议类型时禁止用名称覆盖未知/混合类型。
+        if (!type.isBlank()) return directions.resolve(type);
+        return switch (name.trim()) {
+            case "恒流充电" -> Direction.CHARGE;
+            case "恒流放电" -> Direction.DISCHARGE;
+            case "搁置", "静置" -> Direction.REST;
+            default -> Direction.UNKNOWN;
+        };
+    }
     private BigDecimal decimal(Object value) { return value instanceof Number n ? new BigDecimal(n.toString()) : null; }
 
     private Set<String> valueKeys(List<FormationField> fields, List<Item> items)
diff --git a/battery-device-api/src/main/java/com/battery/device/api/StepDirectionResolver.java b/battery-device-api/src/main/java/com/battery/device/api/StepDirectionResolver.java
index 00efdf9..a0d57a4 100644
--- a/battery-device-api/src/main/java/com/battery/device/api/StepDirectionResolver.java
+++ b/battery-device-api/src/main/java/com/battery/device/api/StepDirectionResolver.java
@@ -1,9 +1,9 @@
 package com.battery.device.api;
 
-/** 将已保存的实际工步类型解释为充电、放电或未知,不依据测量值猜测。 */
+/** 将已保存的实际工步类型解释为充电、放电、静置或未知,不依据测量值猜测。 */
 public interface StepDirectionResolver
 {
-    enum Direction { CHARGE, DISCHARGE, UNKNOWN }
+    enum Direction { CHARGE, DISCHARGE, REST, UNKNOWN }
 
     /** 返回有明确协议依据的方向;未定义或混合类型保持未知。 */
     Direction resolve(String stepType);
diff --git a/battery-device-bts/src/main/java/com/battery/device/bts/BtsStepDirectionResolver.java b/battery-device-bts/src/main/java/com/battery/device/bts/BtsStepDirectionResolver.java
index 87bd68a..dce72c4 100644
--- a/battery-device-bts/src/main/java/com/battery/device/bts/BtsStepDirectionResolver.java
+++ b/battery-device-bts/src/main/java/com/battery/device/bts/BtsStepDirectionResolver.java
@@ -12,6 +12,7 @@
     public Direction resolve(String stepType)
     {
         String type = stepType == null ? "" : stepType.trim().toLowerCase(Locale.ROOT);
+        if ("rest".equals(type)) return Direction.REST;
         if (Set.of("cc", "cv", "cccv", "cp", "cr", "charge", "charging").contains(type)) return Direction.CHARGE;
         if (Set.of("dc", "dp", "dr", "dv", "cccd", "discharge", "discharging").contains(type)) return Direction.DISCHARGE;
         return Direction.UNKNOWN;
diff --git a/battery-device-bts/src/test/java/com/battery/device/bts/BtsStepDirectionResolverTest.java b/battery-device-bts/src/test/java/com/battery/device/bts/BtsStepDirectionResolverTest.java
index 7fffffb..fe131bd 100644
--- a/battery-device-bts/src/test/java/com/battery/device/bts/BtsStepDirectionResolverTest.java
+++ b/battery-device-bts/src/test/java/com/battery/device/bts/BtsStepDirectionResolverTest.java
@@ -15,7 +15,8 @@
             assertEquals(Direction.CHARGE, resolver.resolve(type));
         for (String type : List.of("dc", "dp", "dr", "dv", "cccd", "DISCHARGE"))
             assertEquals(Direction.DISCHARGE, resolver.resolve(type));
-        for (String type : List.of("pcccv", "pulse", "sim", "rest", "恒流充电", "0.1C", ""))
+        assertEquals(Direction.REST, resolver.resolve(" rest "));
+        for (String type : List.of("pcccv", "pulse", "sim", "恒流充电", "0.1C", ""))
             assertEquals(Direction.UNKNOWN, resolver.resolve(type));
         assertEquals(Direction.UNKNOWN, resolver.resolve(null));
     }
diff --git a/ruoyi-admin/src/test/java/com/battery/business/report/FormationRealReportTest.java b/ruoyi-admin/src/test/java/com/battery/business/report/FormationRealReportTest.java
index 29922a8..7b6a3a2 100644
--- a/ruoyi-admin/src/test/java/com/battery/business/report/FormationRealReportTest.java
+++ b/ruoyi-admin/src/test/java/com/battery/business/report/FormationRealReportTest.java
@@ -108,6 +108,41 @@
     }
 
     @Test
+    void importedStepNamesSupplyDirectionWithoutInventingProtocolTypes() throws Exception
+    {
+        var discharge = step(1, null, "1", "0.25"); discharge.put("stepName", "恒流放电");
+        var rest = step(2, null, "1", null); rest.put("stepName", "搁置");
+        var charge = step(3, null, "4.2", "0.2"); charge.put("stepName", "恒流充电");
+        var ambiguous = step(4, "pulse", "99", "99"); ambiguous.put("stepName", "恒流充电");
+        when(data.steps(1L)).thenReturn(List.of(discharge, rest, charge, ambiguous));
+        var saved = create(request());
+        var choices = saved.getJSONObject("settings").getJSONObject("curveStepSelections").getJSONObject("1|11|1");
+        assertEquals(List.of(3), choices.getList("charge", Integer.class));
+        assertEquals(List.of(1), choices.getList("discharge", Integer.class));
+        assertEquals(250, saved.getJSONObject("calculatedValues").getBigDecimal("1|11|1|capacity1v").intValueExact());
+        var steps = saved.getJSONArray("items").getJSONObject(0).getJSONObject("chargeSteps").getJSONArray("1");
+        assertEquals("REST", steps.getJSONObject(1).getString("direction"));
+        assertEquals("UNKNOWN", steps.getJSONObject(3).getString("direction"));
+    }
+
+    @Test
+    void explicitSourceRefreshKeepsManualValuesAndHistoricalClassification() throws Exception
+    {
+        var imported = step(1, null, "1", "0.25");
+        when(data.steps(1L)).thenReturn(List.of(imported));
+        var first = create(request());
+        long id = first.getLongValue("id");
+        assertNull(first.getJSONObject("calculatedValues").get("1|11|1|capacity1v"));
+        imported.put("stepName", "恒流放电");
+        var refreshed = JSON.parseObject(JSON.toJSONString(service.refresh(id, new CompareReportEditRequest.Refresh(1, "重新读取实际工步"), 1, "操作员")));
+        assertEquals(250, refreshed.getJSONObject("calculatedValues").getBigDecimal("1|11|1|capacity1v").intValueExact());
+        assertEquals("2", refreshed.getJSONObject("settings").getJSONObject("values").getString("1|11|1|manual"));
+        var historical = JSON.parseObject(JSON.toJSONString(service.get(id, 1)));
+        assertNull(historical.getJSONObject("calculatedValues").get("1|11|1|capacity1v"));
+        assertEquals("UNKNOWN", historical.getJSONArray("items").getJSONObject(0).getJSONObject("chargeSteps").getJSONArray("1").getJSONObject(0).getString("direction"));
+    }
+
+    @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")));

--
Gitblit v1.7.1