| | |
| | | return result.stream().sorted(Comparator.comparingLong(Candidate::startedAt) |
| | | .thenComparingLong(value -> value.selection().testId())).toList(); |
| | | } |
| | | /** 校验至少两个同组测试选择相同的参与电池集合,按提交顺序接续真实循环。 */ |
| | | /** 校验每个测试电池来源并按提交顺序逐段接续真实循环。 */ |
| | | public Matrix preview(List<SpliceReportRequest.Selection> selections) { |
| | | if (selections == null || selections.size() < 2 || selections.contains(null) |
| | | || new HashSet<>(selections).size() != selections.size()) |
| | | throw new ServiceException("请选择至少两个不重复的检测完成测试电池"); |
| | | Map<Long, TTest> loaded = new LinkedHashMap<>(); |
| | | Map<Long, List<TTestBattery>> selected = new LinkedHashMap<>(); |
| | | Long group = null; |
| | | Map<SpliceReportRequest.Selection, TTestBattery> selected = new LinkedHashMap<>(); |
| | | for (var selection : selections) { |
| | | if (selection.testId() == null || selection.testBatteryId() == null) throw new ServiceException("测试和电池身份不能为空"); |
| | | TTest test = loaded.computeIfAbsent(selection.testId(), tests::selectTestById); |
| | | if (test == null || !"COMPLETED".equals(test.getTestStatus())) throw new ServiceException("仅可选择检测完成测试"); |
| | | TTestBattery battery = test.getBatteries().stream().filter(row -> selection.testBatteryId().equals(row.getTestBatteryId())) |
| | | .findFirst().orElseThrow(() -> new ServiceException("电池不属于所选测试")); |
| | | if (group == null) group = battery.getGroupId(); |
| | | if (!Objects.equals(group, battery.getGroupId())) throw new ServiceException("拼接测试必须属于同一电池组"); |
| | | selected.computeIfAbsent(selection.testId(), key -> new ArrayList<>()).add(battery); |
| | | } |
| | | if (selected.size() < 2) throw new ServiceException("请至少选择两个不同测试"); |
| | | Set<String> common = null; |
| | | for (var batteries : selected.values()) { |
| | | Set<String> names = new HashSet<>(batteries.stream().map(TTestBattery::getBatteryNo).toList()); |
| | | if (common == null) common = names; |
| | | if (names.size() != batteries.size() || !common.equals(names)) throw new ServiceException("拼接测试必须选择相同的共同参与电池集合"); |
| | | selected.put(selection, battery); |
| | | } |
| | | List<Map<String, Object>> segments = new ArrayList<>(); |
| | | Set<Integer> mappedCycles = new TreeSet<>(); |
| | | Integer previousEnd = null; |
| | | for (var entry : selected.entrySet()) { |
| | | List<Map<String, Object>> cycles = data.cycles(entry.getKey()), steps = data.steps(entry.getKey()), records = data.records(entry.getKey()); |
| | | long testId = entry.getKey().testId(); |
| | | List<Map<String, Object>> cycles = data.cycles(testId), steps = data.steps(testId), records = data.records(testId); |
| | | List<Map<String, Object>> current = new ArrayList<>(); |
| | | Set<Integer> sourceCycles = new TreeSet<>(); |
| | | for (TTestBattery battery : entry.getValue()) { |
| | | for (TTestBattery battery : List.of(entry.getValue())) { |
| | | var rows = batteryRows(cycles, battery); |
| | | var stepRows = batteryRows(steps, battery); |
| | | var nos = java.util.stream.Stream.of(rows, stepRows, batteryRows(records, battery)).flatMap(List::stream) |
| | | .map(this::cycle).filter(no -> no > 0).distinct().sorted().toList(); |
| | | Map<String, Object> segment = new LinkedHashMap<>(); |
| | | segment.put("source", candidate(loaded.get(entry.getKey()), battery)); |
| | | segment.put("source", candidate(loaded.get(testId), battery)); |
| | | segment.put("cycles", rows); segment.put("steps", stepRows); |
| | | segment.put("businessFields", battery.getFields()); |
| | | segment.put("originalCycleNos", nos); segment.put("statistics", statistics(rows, stepRows, nos)); |
| | | current.add(segment); sourceCycles.addAll(nos); |
| | | } |
| | | // 同一测试的全部电池共用偏移;首个非空段保留原号,后段首循环紧接前段末循环。 |
| | | // 每个测试电池独立计算偏移;空段不占用循环,各指标共用同一定位。 |
| | | int offset = previousEnd == null || sourceCycles.isEmpty() ? 0 : previousEnd + 1 - sourceCycles.iterator().next(); |
| | | current.forEach(segment -> segment.put("offset", offset)); |
| | | for (int no : sourceCycles) mappedCycles.add(no + offset); |