package com.usky.vpp.util; import com.usky.common.core.exception.BusinessException; import org.springframework.util.StringUtils; import java.math.BigDecimal; /** * 响应量系数区间:结构化边界校验、匹配、重叠判断与展示串拼装。 */ public final class VppResponseCoefficientHelper { private VppResponseCoefficientHelper() { } /** * 由结构化边界构建区间并校验。 */ public static Range of(BigDecimal lower, BigDecimal upper, Boolean lowerInclusive, Boolean upperInclusive) { if (lower == null && upper == null) { throw new BusinessException("响应量系数下限与上限不能同时为空"); } boolean lowerInc = lower != null && Boolean.TRUE.equals(lowerInclusive); boolean upperInc = upper != null && Boolean.TRUE.equals(upperInclusive); if (lower != null && upper != null && lower.compareTo(upper) >= 0) { throw new BusinessException("响应量系数区间下限必须小于上限"); } return new Range(lower, upper, lowerInc, upperInc); } public static boolean matches(BigDecimal lower, BigDecimal upper, Boolean lowerInclusive, Boolean upperInclusive, BigDecimal coefficient) { return coefficient != null && of(lower, upper, lowerInclusive, upperInclusive).contains(coefficient); } public static boolean matches(Range range, BigDecimal coefficient) { return coefficient != null && range != null && range.contains(coefficient); } public static boolean overlaps(Range left, Range right) { return left != null && right != null && left.overlaps(right); } /** 拼装展示串,如 [0.6,0.8)、<0.6、>1.4。 */ public static String format(BigDecimal lower, BigDecimal upper, Boolean lowerInclusive, Boolean upperInclusive) { return of(lower, upper, lowerInclusive, upperInclusive).format(); } public static String format(Range range) { return range == null ? null : range.format(); } /** * 解析历史字符串区间,仅供数据迁移使用。 */ public static Range parse(String expression) { if (!StringUtils.hasText(expression)) { throw new BusinessException("响应量系数不能为空"); } String value = expression.trim().replace(',', ',').replace(" ", ""); try { if (value.startsWith("<=")) { return new Range(null, new BigDecimal(value.substring(2)), false, true); } if (value.startsWith("<")) { return new Range(null, new BigDecimal(value.substring(1)), false, false); } if (value.startsWith(">=")) { return new Range(new BigDecimal(value.substring(2)), null, true, false); } if (value.startsWith(">")) { return new Range(new BigDecimal(value.substring(1)), null, false, false); } if ((value.startsWith("[") || value.startsWith("(")) && (value.endsWith("]") || value.endsWith(")"))) { String[] bounds = value.substring(1, value.length() - 1).split(",", -1); if (bounds.length != 2) { throw new IllegalArgumentException(); } BigDecimal lower = new BigDecimal(bounds[0]); BigDecimal upper = new BigDecimal(bounds[1]); if (lower.compareTo(upper) >= 0) { throw new BusinessException("响应量系数区间下限必须小于上限"); } return new Range(lower, upper, value.startsWith("["), value.endsWith("]")); } } catch (NumberFormatException ex) { throw new BusinessException("响应量系数格式无效"); } throw new BusinessException("响应量系数格式无效,示例:[0.6,0.8)、<0.6、>1.4"); } public static final class Range { private final BigDecimal lower; private final BigDecimal upper; private final boolean lowerInclusive; private final boolean upperInclusive; private Range(BigDecimal lower, BigDecimal upper, boolean lowerInclusive, boolean upperInclusive) { this.lower = lower; this.upper = upper; this.lowerInclusive = lowerInclusive; this.upperInclusive = upperInclusive; } public BigDecimal getLower() { return lower; } public BigDecimal getUpper() { return upper; } public boolean isLowerInclusive() { return lowerInclusive; } public boolean isUpperInclusive() { return upperInclusive; } public boolean contains(BigDecimal value) { if (lower != null) { int comparison = value.compareTo(lower); if (comparison < 0 || (comparison == 0 && !lowerInclusive)) { return false; } } if (upper != null) { int comparison = value.compareTo(upper); if (comparison > 0 || (comparison == 0 && !upperInclusive)) { return false; } } return true; } public boolean overlaps(Range other) { return upperAllows(other.lower, other.lowerInclusive) && other.upperAllows(lower, lowerInclusive); } public String format() { if (lower == null && upper != null) { return (upperInclusive ? "<=" : "<") + upper.stripTrailingZeros().toPlainString(); } if (upper == null && lower != null) { return (lowerInclusive ? ">=" : ">") + lower.stripTrailingZeros().toPlainString(); } String left = lowerInclusive ? "[" : "("; String right = upperInclusive ? "]" : ")"; return left + lower.stripTrailingZeros().toPlainString() + "," + upper.stripTrailingZeros().toPlainString() + right; } private boolean upperAllows(BigDecimal otherLower, boolean otherLowerInclusive) { if (upper == null || otherLower == null) { return true; } int comparison = upper.compareTo(otherLower); return comparison > 0 || (comparison == 0 && upperInclusive && otherLowerInclusive); } } }