yeg
2026-09-08 05fe40d73ad556f4f3ec4a6571008502c2fb2fdd
集成对比来源详情与曲线显示契约
2个文件已修改
56 ■■■■■ 已修改文件
battery-business/src/main/java/com/battery/business/report/CompareReportRequest.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
battery-business/src/main/java/com/battery/business/report/CompareReportService.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
battery-business/src/main/java/com/battery/business/report/CompareReportRequest.java
@@ -23,11 +23,16 @@
            @NotNull List<String> batteryTemplateFields, @NotNull List<String> specialFields,
            @NotNull Map<String, @NotNull Map<String, String>> detailFieldValues,
            List<@NotNull @Valid CompareField> fieldStructure,
            List<@NotNull String> hiddenSeriesKeys)
            List<@NotNull String> hiddenSeriesKeys,
            Map<String, @NotNull @Valid SourceDetailOverride> sourceDetailOverrides)
    {
        /** 展示设置不能夹带测量快照或其他未支持的修改。 */
        @com.fasterxml.jackson.annotation.JsonAnySetter
        public void rejectUnknown(String field, Object value)
        { throw new com.ruoyi.common.exception.ServiceException("报表设置不允许提交字段:" + field); }
    }
    /** 只记录报表中明确修改的格;缺失与空字符串分别表示未修改和人工清空。 */
    public record SourceDetailOverride(@Size(max = 500) String experimentName,
            Map<String, @Size(max = 500) String> fieldValues) { }
}
battery-business/src/main/java/com/battery/business/report/CompareReportService.java
@@ -97,9 +97,13 @@
        validateHiddenSeries(request.settings(), request.selections());
        List<Item> items = preview(request.selections()).stream().map(item -> new Item(item.source(), item.zeroTime(), item.records(), item.logicalMapping(),
                item.businessFields().stream().filter(field -> selectedBusinessField(field, request.settings())).toList())).toList();
        validateSourceOverrides(request.settings(), items);
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put("name", request.name().trim()); snapshot.put("description", request.description());
        snapshot.put("experimentName", request.experimentName()); snapshot.put("items", items); snapshot.put("settings", snapshotSettings(request.settings()));
        snapshot.put("experimentName", request.experimentName()); snapshot.put("items", items);
        var settings = snapshotSettings(request.settings());
        settings.putIfAbsent("sourceDetailOverrides", Map.of());
        snapshot.put("settings", settings);
        snapshot.put("units", Map.of("voltage", "V", "current", "A", "temperature", "℃", "positiveVoltage", "V", "negativeVoltage", "V", "capacity", "Ah", "energy", "Wh"));
        snapshot.put("displayUnits", Map.of("voltage", "V", "current", "mA", "temperature", "℃", "positiveVoltage", "V", "negativeVoltage", "V", "capacity", "mAh", "energy", "mWh"));
        snapshot.put("timeUnit", "epoch-milliseconds");
@@ -131,6 +135,7 @@
        validateSettings(request.settings());
        var items = JSON.parseArray(JSON.toJSONString(snapshot.get("items")), Item.class);
        validateHiddenSeries(request.settings(), items.stream().map(item -> item.source().selection()).toList());
        validateSourceOverrides(request.settings(), items);
        snapshot.put("name", request.name().trim()); snapshot.put("description", request.description());
        snapshot.put("experimentName", request.experimentName()); snapshot.put("settings", snapshotSettings(request.settings()));
        reports.append(id, request.baseVersionNo() + 1, request.name().trim(), JSON.toJSONString(snapshot, JSONWriter.Feature.WriteMapNullValue),
@@ -149,6 +154,8 @@
                .map(item -> new Item(item.source(), item.zeroTime(), item.records(), item.logicalMapping(),
                        item.businessFields().stream().filter(field -> selectedBusinessField(field, settings)).toList())).toList();
        snapshot.put("items", updated);
        // 旧版仅在明确更新来源时取得新详情;原有按范围保存的人工值不迁移或合并。
        snapshot.getJSONObject("settings").putIfAbsent("sourceDetailOverrides", Map.of());
        reports.append(id, request.baseVersionNo() + 1, snapshot.getString("name"), JSON.toJSONString(snapshot, JSONWriter.Feature.WriteMapNullValue),
                operatorId, operatorName, "按最新来源生成:" + request.changeSummary().trim());
        return get(id);
@@ -220,7 +227,25 @@
        if (settings.hiddenSeriesKeys() != null) result.put("hiddenSeriesKeys", settings.hiddenSeriesKeys());
        // 独立复制结构而非保存模板引用,重开、编辑与打印均不重新读取模板或共享定义。
        if (settings.fieldStructure() != null) result.put("fieldStructure", settings.fieldStructure());
        if (settings.sourceDetailOverrides() != null) result.put("sourceDetailOverrides", settings.sourceDetailOverrides());
        return result;
    }
    private void validateSourceOverrides(CompareReportRequest.Settings settings, List<Item> items)
    {
        if (settings.sourceDetailOverrides() == null) return;
        Set<String> identities = new HashSet<>();
        for (var item : items) identities.add(item.source().selection().testId() + "|" + item.source().selection().testBatteryId());
        for (var entry : settings.sourceDetailOverrides().entrySet())
        {
            // 报表人工值只能归入已冻结的来源电池,不接受电池号或列位置作为身份。
            if (!identities.contains(entry.getKey()) || entry.getValue() == null)
                throw new ServiceException("测试详情来源不属于当前报表");
            var fields = entry.getValue().fieldValues();
            if (fields != null && fields.entrySet().stream().anyMatch(field -> field.getKey() == null
                    || !field.getKey().matches("(?:battery|special)\\|.{1,100}") || field.getValue() == null || field.getValue().length() > 500))
                throw new ServiceException("测试详情字段或填写值无效");
        }
    }
    private List<Candidate> candidates(TTest test, List<Map<String, Object>> records)
@@ -249,7 +274,27 @@
    {
        return new Candidate(new Selection(test.getTestId(), battery.getTestBatteryId(), cycle, step, logical),
                test.getTestName(), test.getTestTypeName(), battery.getGroupId(), battery.getGroupName(), battery.getBatteryNo(), process,
                test.getConsoleDetail() == null ? null : test.getConsoleDetail().getExperimentName(), test.getConsoleDetail());
                test.getConsoleDetail() == null ? null : test.getConsoleDetail().getExperimentName(), selectedConsoleDetail(test, battery));
    }
    private TTestConsoleDetail selectedConsoleDetail(TTest test, TTestBattery battery)
    {
        var original = test.getConsoleDetail();
        if (original == null) return null;
        var detail = new TTestConsoleDetail();
        detail.setExperimentName(original.getExperimentName());
        detail.setBatteryFields(original.getBatteryFields());
        detail.setSpecialFields(original.getSpecialFields());
        // 快照只冻结所选电池的已保存字符串;不带入其他电池,也不由当前电池组值补缺。
        original.getFieldValues().forEach((field, values) -> {
            if (values.containsKey(battery.getBatteryNo()))
            {
                var selected = new LinkedHashMap<String, String>();
                selected.put(battery.getBatteryNo(), values.get(battery.getBatteryNo()));
                detail.getFieldValues().put(field, selected);
            }
        });
        return detail;
    }
    private int number(Map<String, Object> row, String key) { return row.get(key) instanceof Number value ? value.intValue() : 0; }