AnalysisScoreDetail.java
package com.taxonomy.dto;
/**
* Immutable interpretation of one raw analysis score.
*
* @param nodeCode node identity
* @param kind semantic meaning of {@code rawScore}
* @param rawScore original 0–100 score returned by the analysis
* @param effectiveRelevance comparable 0–100 relevance used by generic downstream consumers
* @param parentCode direct hierarchical parent, when known
* @param parentScore direct parent relevance used to derive a product's effective relevance
*/
public record AnalysisScoreDetail(
String nodeCode,
AnalysisScoreKind kind,
int rawScore,
int effectiveRelevance,
String parentCode,
Integer parentScore) {
public AnalysisScoreDetail {
if (nodeCode == null || nodeCode.isBlank()) {
throw new IllegalArgumentException("nodeCode must not be blank");
}
nodeCode = nodeCode.strip();
kind = kind == null ? AnalysisScoreKind.HIERARCHICAL_RELEVANCE : kind;
if (rawScore < 0 || rawScore > 100) {
throw new IllegalArgumentException("rawScore must be between 0 and 100");
}
if (effectiveRelevance < 0 || effectiveRelevance > 100) {
throw new IllegalArgumentException("effectiveRelevance must be between 0 and 100");
}
parentCode = parentCode == null || parentCode.isBlank() ? null : parentCode.strip();
if (parentScore != null && (parentScore < 0 || parentScore > 100)) {
throw new IllegalArgumentException("parentScore must be between 0 and 100");
}
}
public boolean isProductSuitability() {
return kind == AnalysisScoreKind.PRODUCT_SUITABILITY;
}
}