| | |
| | | import com.battery.business.domain.TBatteryGroupField; |
| | | import com.battery.business.domain.TTest; |
| | | import com.battery.business.domain.TTestBattery; |
| | | import com.battery.business.domain.TTestConsoleDetail; |
| | | import com.battery.business.report.FormationReportRequest.Selection; |
| | | import com.battery.business.report.FormationReportRequest.Settings; |
| | | import com.battery.business.service.ITBatteryGroupFieldService; |
| | |
| | | FormationReportRepository reports, ITBatteryGroupFieldService definitions, StepDirectionResolver directions) |
| | | { this.directions = directions; this.tests = tests; this.data = data; this.reports = reports; this.definitions = definitions; } |
| | | |
| | | public record Candidate(Selection selection, String name, String type, long groupId, String batteryGroup, String batteryNo) { } |
| | | public record Candidate(Selection selection, String name, String type, long groupId, String batteryGroup, String batteryNo, TTestConsoleDetail consoleDetail) { } |
| | | public record ChargeStep(int stepNo, String name, String stepType, BigDecimal endVoltage, Direction direction) { } |
| | | public record Curve(Long zeroTime, List<Map<String, Object>> records) { } |
| | | public record Item(Candidate source, List<Integer> cycleNos, List<Map<String, Object>> cycles, |
| | |
| | | Settings settings = new Settings(validateFields(request.settings().fields(), true), request.settings().values(), |
| | | request.settings().chargeStepSelections() == null ? Map.of() : request.settings().chargeStepSelections(), |
| | | request.settings().metrics(), request.settings().seriesColors(), request.settings().manualChargeSteps(), |
| | | request.settings().curveStepSelections(), request.settings().hiddenSeriesKeys(), request.settings().activeCycle()); |
| | | request.settings().curveStepSelections(), request.settings().hiddenSeriesKeys(), request.settings().activeCycle(), request.settings().sourceDetailOverrides()); |
| | | Matrix matrix = freeze(preview(request.selections()), settings.fields()); |
| | | settings = stepSettings(settings, matrix.items(), false); |
| | | validateValues(settings, matrix.items()); validateChargeSelections(settings, matrix.items()); |
| | |
| | | public Map<String, Object> edit(long id, FormationReportRequest.Edit request, long userId, String userName) |
| | | { |
| | | var snapshot = current(id, request.baseVersionNo()); |
| | | validateFields(request.settings().fields(), false); |
| | | var settings = "actual-steps".equals(snapshot.getString("curveMode")) ? stepSettings(request.settings(), items(snapshot), false) : request.settings(); |
| | | var submitted = request.settings(); |
| | | var settings = new Settings(validateFields(submitted.fields(), false), submitted.values(), submitted.chargeStepSelections(), |
| | | submitted.metrics(), submitted.seriesColors(), submitted.manualChargeSteps(), submitted.curveStepSelections(), |
| | | submitted.hiddenSeriesKeys(), submitted.activeCycle(), submitted.sourceDetailOverrides()); |
| | | if ("actual-steps".equals(snapshot.getString("curveMode"))) settings = stepSettings(settings, items(snapshot), false); |
| | | validateValues(settings, items(snapshot)); validateChargeSelections(settings, items(snapshot)); |
| | | snapshot.put("name", request.name().trim()); snapshot.put("description", request.description()); snapshot.put("settings", settings); |
| | | reports.append(id, request.baseVersionNo() + 1, request.name().trim(), json(snapshot), userId, userName, request.changeSummary().trim()); |
| | |
| | | Set<String> allowed = valueKeys(settings.fields(), matrix.items()); |
| | | Map<String, String> values = new LinkedHashMap<>(); |
| | | settings.values().forEach((key, value) -> { if (allowed.contains(key)) values.put(key, value); }); |
| | | var refreshedSettings = new Settings(settings.fields(), values, |
| | | var refreshedSettings = new Settings(validateFields(settings.fields(), false), values, |
| | | settings.chargeStepSelections() == null ? Map.of() : settings.chargeStepSelections(), settings.metrics(), settings.seriesColors(), |
| | | settings.manualChargeSteps(), settings.curveStepSelections(), settings.hiddenSeriesKeys(), settings.activeCycle()); |
| | | settings.manualChargeSteps(), settings.curveStepSelections(), settings.hiddenSeriesKeys(), settings.activeCycle(), settings.sourceDetailOverrides()); |
| | | snapshot.put("curveMode", "actual-steps"); |
| | | snapshot.put("items", matrix.items()); snapshot.put("cycleNos", matrix.cycleNos()); |
| | | snapshot.put("settings", stepSettings(refreshedSettings, matrix.items(), true)); |
| | |
| | | var current = resolveDefinitions ? definitions.selectBatteryGroupFieldList(new TBatteryGroupField()) : List.<TBatteryGroupField>of(); |
| | | return fields.stream().map(field -> { |
| | | if (!names.add(field.scope() + "|" + field.key())) throw new ServiceException("化成字段不能重复"); |
| | | if (!"battery".equals(field.kind())) return field; |
| | | if (!"battery".equals(field.kind())) { |
| | | // 仅新生成的字段结构纠正旧误写,读取历史快照时不改名。 |
| | | String corrected = Map.of("chargePerGram", "每克N充电量", "chargePerGramTotal", "每克F充电量", |
| | | "capacity1v", "1.0V容量", "capacity1vf", "1.0VF克容量", "dischargeInflection", "拐点容量", |
| | | "dischargeInflectionTotal", "拐点F克容量").get(field.key()); |
| | | return corrected == null ? field : new FormationField(field.key(), field.scope(), field.kind(), corrected, field.unit(), field.fieldDefinitionId()); |
| | | } |
| | | Long id = field.fieldDefinitionId(); |
| | | if (resolveDefinitions) |
| | | { |
| | |
| | | if (settings.metrics().stream().anyMatch(value -> value == null || !Set.of("voltage", "current", "positiveVoltage", "negativeVoltage", "temperature").contains(value)) |
| | | || new HashSet<>(settings.metrics()).size() != settings.metrics().size()) throw new ServiceException("化成曲线指标无效或重复"); |
| | | Set<String> sources = new HashSet<>(items.stream().map(item -> sourceKey(item)).toList()); |
| | | if (settings.sourceDetailOverrides() != null) { |
| | | // 详情只接受测试电池身份;曲线工步身份不能作为详情列提交。 |
| | | if (!sources.containsAll(settings.sourceDetailOverrides().keySet())) throw new ServiceException("详情调整不属于所选测试电池"); |
| | | for (var override : settings.sourceDetailOverrides().values()) { |
| | | if (override == null || (override.experimentName() != null && override.experimentName().length() > 500)) throw new ServiceException("详情调整无效"); |
| | | if (override.fieldValues() != null && override.fieldValues().entrySet().stream().anyMatch(entry -> |
| | | entry.getKey() == null || !entry.getKey().matches("(?:battery|special)\\|.{1,100}") |
| | | || entry.getValue() == null || entry.getValue().length() > 500)) throw new ServiceException("详情字段名称或值无效"); |
| | | } |
| | | } |
| | | for (Item item : items) if (item.stepCurves() != null) item.stepCurves().forEach((cycle, ranges) -> ranges.keySet().forEach(step -> sources.add(sourceKey(item) + "|" + cycle + "|" + step))); |
| | | if (settings.hiddenSeriesKeys() != null && !sources.containsAll(settings.hiddenSeriesKeys())) throw new ServiceException("隐藏曲线不属于当前来源工步"); |
| | | if (!sources.containsAll(settings.seriesColors().keySet()) || settings.seriesColors().values().stream() |
| | |
| | | var colors = new LinkedHashMap<>(settings.seriesColors()); |
| | | var hidden = settings.hiddenSeriesKeys() == null ? List.<String>of() : settings.hiddenSeriesKeys(); |
| | | if (refresh) { colors.keySet().retainAll(series); hidden = hidden.stream().filter(series::contains).toList(); } |
| | | return new Settings(settings.fields(), settings.values(), settings.chargeStepSelections(), settings.metrics(), colors, manual, selected, hidden, active); |
| | | return new Settings(settings.fields(), settings.values(), settings.chargeStepSelections(), settings.metrics(), colors, manual, selected, hidden, active, settings.sourceDetailOverrides()); |
| | | } |
| | | |
| | | private void validateChargeSelections(Settings settings, List<Item> items) |
| | |
| | | |
| | | private boolean eligible(TTest test) { return "COMPLETED".equals(test.getTestStatus()) && "化成".equals(test.getTestTypeName()); } |
| | | private Candidate candidate(TTest test, TTestBattery battery) |
| | | { return new Candidate(new Selection(test.getTestId(), battery.getTestBatteryId()), test.getTestName(), test.getTestTypeName(), battery.getGroupId(), battery.getGroupName(), battery.getBatteryNo()); } |
| | | { return new Candidate(new Selection(test.getTestId(), battery.getTestBatteryId()), test.getTestName(), test.getTestTypeName(), battery.getGroupId(), battery.getGroupName(), battery.getBatteryNo(), consoleDetail(test, battery)); } |
| | | private TTestConsoleDetail consoleDetail(TTest test, TTestBattery battery) |
| | | { |
| | | var original = test.getConsoleDetail(); |
| | | if (original == null) return null; |
| | | var result = new TTestConsoleDetail(); |
| | | result.setExperimentName(original.getExperimentName()); result.setBatteryFields(original.getBatteryFields()); result.setSpecialFields(original.getSpecialFields()); |
| | | // 只冻结所选电池的人工详情,同名字段不推断共享身份或业务单位。 |
| | | Map<String, Map<String, String>> values = new LinkedHashMap<>(); |
| | | original.getFieldValues().forEach((field, byBattery) -> { |
| | | if (byBattery.containsKey(battery.getBatteryNo())) values.put(field, Map.of(battery.getBatteryNo(), byBattery.get(battery.getBatteryNo()))); |
| | | }); |
| | | result.setFieldValues(values); return result; |
| | | } |
| | | private List<Map<String, Object>> batteryRows(List<Map<String, Object>> rows, TTestBattery battery) |
| | | { return rows.stream().filter(row -> Objects.equals(battery.getBatteryNo(), row.get("batteryNo"))).toList(); } |
| | | private int cycleNo(Map<String, Object> row) { return row.get("cycleNo") instanceof Number number ? number.intValue() : 0; } |