diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index 0f11f7930..2a4a51e1b 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -7942,7 +7942,7 @@ CREATE TABLE IF NOT EXISTS baiyue_firstleg.fl_customer_dimension_config ( inner_length_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内长(厘米)', inner_width_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内宽(厘米)', inner_height_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内高(厘米)', - edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '每个尺寸方向的单侧加宽厘米数', + edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '长宽高各自增加一次的厘米数', default_calculation_mode VARCHAR(16) NOT NULL DEFAULT 'ADD' COMMENT '订单外箱规默认计算方式:ADD或MULTIPLY', remark VARCHAR(500) NULL COMMENT '备注', initialization_key VARCHAR(128) NOT NULL COMMENT '创建流程幂等键及补偿归属', @@ -12992,7 +12992,7 @@ CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'bo CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'created_at', '创建时间'); CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'created_by', '创建人ID'); CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'deleted', '逻辑删除'); -CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'edge_addition_cm', '每个尺寸方向的单侧加宽厘米数'); +CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'edge_addition_cm', '长宽高各自增加一次的厘米数'); CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'default_calculation_mode', '订单外箱规默认计算方式:ADD或MULTIPLY'); CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'id', '配置ID'); CALL queue_column_comment('baiyue_firstleg', 'fl_customer_dimension_config', 'initialization_key', '创建流程幂等键及补偿归属'); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/entity/CustomerDimensionConfigEntity.java b/firstleg/src/main/java/com/baiyue/firstleg/entity/CustomerDimensionConfigEntity.java index 4ed2769d6..3633b203a 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/entity/CustomerDimensionConfigEntity.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/entity/CustomerDimensionConfigEntity.java @@ -10,7 +10,7 @@ import lombok.Setter; import java.math.BigDecimal; -/** 客户固定箱规的内径和每侧加宽配置。 */ +/** 客户固定箱规的内径和长、宽、高尺寸增加值配置。 */ @Getter @Setter @TableName("fl_customer_dimension_config") diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionConfigService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionConfigService.java index 6dd1b7f84..7a7311f88 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionConfigService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionConfigService.java @@ -27,7 +27,6 @@ import java.util.Map; public class CustomerDimensionConfigService { public static final BigDecimal DEFAULT_EDGE_ADDITION_CM = new BigDecimal("2.000"); public static final String DEFAULT_CALCULATION_MODE = "ADD"; - private static final BigDecimal TWO = new BigDecimal("2"); private static final BigDecimal CUBIC_CENTIMETERS_PER_CBM = new BigDecimal("1000000"); @Resource @@ -116,7 +115,7 @@ public class CustomerDimensionConfigService { requirePositive(rule.innerLengthCm(), "箱规内长"); requirePositive(rule.innerWidthCm(), "箱规内宽"); requirePositive(rule.innerHeightCm(), "箱规内高"); - requirePositive(rule.edgeAdditionCm(), "箱规单侧加宽"); + requirePositive(rule.edgeAdditionCm(), "箱规尺寸增加值"); if (rule.version() == null || rule.version() < 0) { throw new BizException(ResultCode.BAD_REQUEST, "箱规版本号无效"); } @@ -223,10 +222,10 @@ public class CustomerDimensionConfigService { } private static RuleResponse ruleResponse(CustomerDimensionConfigEntity value) { - BigDecimal edgePair = value.getEdgeAdditionCm().multiply(TWO); - BigDecimal outerLength = value.getInnerLengthCm().add(edgePair); - BigDecimal outerWidth = value.getInnerWidthCm().add(edgePair); - BigDecimal outerHeight = value.getInnerHeightCm().add(edgePair); + BigDecimal addition = value.getEdgeAdditionCm(); + BigDecimal outerLength = value.getInnerLengthCm().add(addition); + BigDecimal outerWidth = value.getInnerWidthCm().add(addition); + BigDecimal outerHeight = value.getInnerHeightCm().add(addition); BigDecimal volume = outerLength.multiply(outerWidth).multiply(outerHeight) .divide(CUBIC_CENTIMETERS_PER_CBM) .setScale(3, RoundingMode.HALF_UP); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionWeightingService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionWeightingService.java index d829a013e..2fbc2782b 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionWeightingService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/CustomerDimensionWeightingService.java @@ -13,10 +13,9 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -/** Resolves measured carton dimensions to the customer's smallest containing dimension rule. */ +/** Resolves dimensions to the smallest containing rule, capped at the largest configured tier. */ @Service public class CustomerDimensionWeightingService { - private static final BigDecimal TWO = new BigDecimal("2"); private static final BigDecimal CUBIC_CENTIMETERS_PER_CBM = new BigDecimal("1000000"); private final CustomerDimensionConfigRepository repository; @@ -62,16 +61,8 @@ public class CustomerDimensionWeightingService { .filter(rule -> rule.contains(measured)) .findFirst() .map(Rule::weighted) - .orElseGet(() -> actualDimensions(lengthCm, widthCm, heightCm)); - } - - private WeightedDimensions actualDimensions(BigDecimal lengthCm, BigDecimal widthCm, - BigDecimal heightCm) { - BigDecimal rawVolume = lengthCm.multiply(widthCm).multiply(heightCm) - .divide(CUBIC_CENTIMETERS_PER_CBM, 6, RoundingMode.HALF_UP); - return new WeightedDimensions(null, null, null, lengthCm, widthCm, heightCm, - rawVolume, rawVolume.setScale(3, RoundingMode.HALF_UP), - "ACTUAL_DIMENSION_V1"); + .orElseGet(() -> rules.get(rules.size() - 1) + .weightedActualDimensions(lengthCm, widthCm, heightCm)); } } @@ -113,10 +104,21 @@ public class CustomerDimensionWeightingService { } private WeightedDimensions weighted() { - BigDecimal edgePair = edgeAdditionCm.multiply(TWO); - BigDecimal outerLength = innerLengthCm.add(edgePair); - BigDecimal outerWidth = innerWidthCm.add(edgePair); - BigDecimal outerHeight = innerHeightCm.add(edgePair); + BigDecimal outerLength = innerLengthCm.add(edgeAdditionCm); + BigDecimal outerWidth = innerWidthCm.add(edgeAdditionCm); + BigDecimal outerHeight = innerHeightCm.add(edgeAdditionCm); + BigDecimal rawVolume = outerLength.multiply(outerWidth).multiply(outerHeight) + .divide(CUBIC_CENTIMETERS_PER_CBM, 6, RoundingMode.HALF_UP); + BigDecimal unitVolume = rawVolume.setScale(3, RoundingMode.HALF_UP); + return new WeightedDimensions(boxType, version, edgeAdditionCm, outerLength, outerWidth, outerHeight, + rawVolume, unitVolume, "CUSTOMER_DIMENSION_" + boxType.name() + "_V" + version); + } + + private WeightedDimensions weightedActualDimensions(BigDecimal lengthCm, BigDecimal widthCm, + BigDecimal heightCm) { + BigDecimal outerLength = lengthCm.add(edgeAdditionCm); + BigDecimal outerWidth = widthCm.add(edgeAdditionCm); + BigDecimal outerHeight = heightCm.add(edgeAdditionCm); BigDecimal rawVolume = outerLength.multiply(outerWidth).multiply(outerHeight) .divide(CUBIC_CENTIMETERS_PER_CBM, 6, RoundingMode.HALF_UP); BigDecimal unitVolume = rawVolume.setScale(3, RoundingMode.HALF_UP); diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementBatchService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementBatchService.java index e78048358..f7be95ab3 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementBatchService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/MeasurementBatchService.java @@ -147,8 +147,20 @@ public class MeasurementBatchService { if (!itemNumbers.add(source.itemNo())) { throw new BizException(ResultCode.VALIDATION_FAILED, "测量明细号不能重复"); } + if ((source.outerLengthCm() != null && source.outerLengthCm().signum() <= 0) + || (source.outerWidthCm() != null && source.outerWidthCm().signum() <= 0) + || (source.outerHeightCm() != null && source.outerHeightCm().signum() <= 0)) { + throw new BizException(ResultCode.VALIDATION_FAILED, + "仓库修改外箱尺寸必须大于0,明细 " + source.itemNo()); + } + boolean hasOuterOverride = source.outerLengthCm() != null + || source.outerWidthCm() != null + || source.outerHeightCm() != null; + BigDecimal baseLength = source.outerLengthCm() != null ? source.outerLengthCm() : source.lengthCm(); + BigDecimal baseWidth = source.outerWidthCm() != null ? source.outerWidthCm() : source.widthCm(); + BigDecimal baseHeight = source.outerHeightCm() != null ? source.outerHeightCm() : source.heightCm(); CustomerDimensionWeightingService.WeightedDimensions weighted = dimensionRules.resolve( - source.lengthCm(), source.widthCm(), source.heightCm()); + baseLength, baseWidth, baseHeight); String requestedMode = normalizeMode(source.coefficientMode()); CustomerChargeRuleEntity coefficientRule = "MULTIPLY".equals(requestedMode) ? requireCoefficientRule(command.tenantId(), customerId, source.chargeRuleId()) @@ -159,32 +171,16 @@ public class MeasurementBatchService { boolean hasMatchedAddition = useDimensionRule && weighted.boxType() != null; // ADD 只从匹配箱规取得加边值,外尺寸仍以本次实测尺寸为基准, // 不能把箱规边界尺寸当成本次货物的实际外尺寸。 - BigDecimal outerLength = hasMatchedAddition ? source.lengthCm().add(addition) : source.lengthCm(); - BigDecimal outerWidth = hasMatchedAddition ? source.widthCm().add(addition) : source.widthCm(); - BigDecimal outerHeight = hasMatchedAddition ? source.heightCm().add(addition) : source.heightCm(); - boolean hasOuterOverride = source.outerLengthCm() != null - || source.outerWidthCm() != null - || source.outerHeightCm() != null; - if ((source.outerLengthCm() != null && source.outerLengthCm().signum() <= 0) - || (source.outerWidthCm() != null && source.outerWidthCm().signum() <= 0) - || (source.outerHeightCm() != null && source.outerHeightCm().signum() <= 0)) { - throw new BizException(ResultCode.VALIDATION_FAILED, - "仓库修改外箱尺寸必须大于0,明细 " + source.itemNo()); - } + BigDecimal outerLength = hasMatchedAddition ? baseLength.add(addition) : baseLength; + BigDecimal outerWidth = hasMatchedAddition ? baseWidth.add(addition) : baseWidth; + BigDecimal outerHeight = hasMatchedAddition ? baseHeight.add(addition) : baseHeight; if (coefficientRule != null) { BigDecimal specificationCoefficient = resolveSpecificationCoefficient( - coefficientRule, source.lengthCm(), source.widthCm(), source.heightCm()); + coefficientRule, baseLength, baseWidth, baseHeight); outerLength = applyCoefficient(outerLength, specificationCoefficient); outerWidth = applyCoefficient(outerWidth, specificationCoefficient); outerHeight = applyCoefficient(outerHeight, specificationCoefficient); } - if (hasOuterOverride) { - // Each warehouse-confirmed dimension is authoritative on its own. - // Unspecified dimensions keep their strategy-calculated values. - if (source.outerLengthCm() != null) outerLength = source.outerLengthCm(); - if (source.outerWidthCm() != null) outerWidth = source.outerWidthCm(); - if (source.outerHeightCm() != null) outerHeight = source.outerHeightCm(); - } BigDecimal unitVolumeRaw = outerLength.multiply(outerWidth).multiply(outerHeight) .divide(new BigDecimal("1000000"), 6, RoundingMode.HALF_UP); BigDecimal unitVolume = hasMatchedAddition && !hasOuterOverride @@ -226,9 +222,13 @@ public class MeasurementBatchService { : useDimensionRule ? weighted.calculationRuleVersion() : "ACTUAL_DIMENSION_V1"); item.setSourceImageFileId(source.intelligentImageFileId()); item.setOverLimitWarning(source.overLimitWarning()); - item.setOuterLengthCm(outerLength.setScale(3, RoundingMode.HALF_UP)); - item.setOuterWidthCm(outerWidth.setScale(3, RoundingMode.HALF_UP)); - item.setOuterHeightCm(outerHeight.setScale(3, RoundingMode.HALF_UP)); + // Override fields retain the operator's base values so repeated saves do not compound the strategy. + item.setOuterLengthCm((source.outerLengthCm() != null ? source.outerLengthCm() : outerLength) + .setScale(3, RoundingMode.HALF_UP)); + item.setOuterWidthCm((source.outerWidthCm() != null ? source.outerWidthCm() : outerWidth) + .setScale(3, RoundingMode.HALF_UP)); + item.setOuterHeightCm((source.outerHeightCm() != null ? source.outerHeightCm() : outerHeight) + .setScale(3, RoundingMode.HALF_UP)); item.setRemark(source.remark()); item.setCreatedAt(LocalDateTime.now()); return item; diff --git a/firstleg/src/main/resources/db/customer-dimension-config.sql b/firstleg/src/main/resources/db/customer-dimension-config.sql index 6b6edc7d4..42cae340c 100644 --- a/firstleg/src/main/resources/db/customer-dimension-config.sql +++ b/firstleg/src/main/resources/db/customer-dimension-config.sql @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS fl_customer_dimension_config ( inner_length_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内长(厘米)', inner_width_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内宽(厘米)', inner_height_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内高(厘米)', - edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '每个尺寸方向的单侧加宽厘米数', + edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '长宽高各自增加一次的厘米数', default_calculation_mode VARCHAR(16) NOT NULL DEFAULT 'ADD' COMMENT '订单外箱规默认计算方式:ADD或MULTIPLY', remark VARCHAR(500) NULL COMMENT '备注', initialization_key VARCHAR(128) NOT NULL COMMENT '创建流程幂等键及补偿归属', diff --git a/firstleg/src/main/resources/db/firstleg.sql b/firstleg/src/main/resources/db/firstleg.sql index e21360d74..c99f8df9b 100644 --- a/firstleg/src/main/resources/db/firstleg.sql +++ b/firstleg/src/main/resources/db/firstleg.sql @@ -56,7 +56,7 @@ CREATE TABLE IF NOT EXISTS fl_customer_dimension_config ( inner_length_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内长(厘米)', inner_width_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内宽(厘米)', inner_height_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内高(厘米)', - edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '每个尺寸方向的单侧加宽厘米数', + edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '长宽高各自增加一次的厘米数', default_calculation_mode VARCHAR(16) NOT NULL DEFAULT 'ADD' COMMENT '订单外箱规默认计算方式:ADD或MULTIPLY', remark VARCHAR(500) NULL COMMENT '备注', initialization_key VARCHAR(128) NOT NULL COMMENT '创建流程幂等键及补偿归属', diff --git a/firstleg/src/main/resources/db/phase2-inbound-schema.sql b/firstleg/src/main/resources/db/phase2-inbound-schema.sql index a95e9d2b8..ce3ed7e0d 100644 --- a/firstleg/src/main/resources/db/phase2-inbound-schema.sql +++ b/firstleg/src/main/resources/db/phase2-inbound-schema.sql @@ -859,7 +859,7 @@ CREATE TABLE IF NOT EXISTS fl_customer_dimension_config ( inner_length_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内长(厘米)', inner_width_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内宽(厘米)', inner_height_cm DECIMAL(9,3) NOT NULL COMMENT '箱规内高(厘米)', - edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '每个尺寸方向的单侧加宽厘米数', + edge_addition_cm DECIMAL(6,3) NOT NULL DEFAULT 2.000 COMMENT '长宽高各自增加一次的厘米数', default_calculation_mode VARCHAR(16) NOT NULL DEFAULT 'ADD' COMMENT '订单外箱规默认计算方式:ADD或MULTIPLY', remark VARCHAR(500) NULL COMMENT '备注', initialization_key VARCHAR(128) NOT NULL COMMENT '创建流程幂等键及补偿归属', diff --git a/firstleg/src/main/resources/openapi/firstleg-v1.yaml b/firstleg/src/main/resources/openapi/firstleg-v1.yaml index 7cea2e977..0dca695be 100644 --- a/firstleg/src/main/resources/openapi/firstleg-v1.yaml +++ b/firstleg/src/main/resources/openapi/firstleg-v1.yaml @@ -1443,7 +1443,7 @@ components: minimum: 0.001 maximum: 100 multipleOf: 0.001 - description: 箱规长、宽、高三个方向分别使用的每侧加宽厘米数 + description: 箱规长、宽、高三个方向分别增加一次的厘米数 example: 2.000 remark: {type: string, maxLength: 500, nullable: true} version: {type: integer, minimum: 0, description: 乐观锁版本号} @@ -1469,9 +1469,9 @@ components: innerWidthCm: {type: number, minimum: 0.001, multipleOf: 0.001} innerHeightCm: {type: number, minimum: 0.001, multipleOf: 0.001} edgeAdditionCm: {type: number, minimum: 0.001, maximum: 100, example: 2.000} - outerLengthCm: {type: number, minimum: 0.001, description: innerLengthCm + edgeAdditionCm * 2} - outerWidthCm: {type: number, minimum: 0.001, description: innerWidthCm + edgeAdditionCm * 2} - outerHeightCm: {type: number, minimum: 0.001, description: innerHeightCm + edgeAdditionCm * 2} + outerLengthCm: {type: number, minimum: 0.001, description: innerLengthCm + edgeAdditionCm} + outerWidthCm: {type: number, minimum: 0.001, description: innerWidthCm + edgeAdditionCm} + outerHeightCm: {type: number, minimum: 0.001, description: innerHeightCm + edgeAdditionCm} volumeCbm: {type: number, multipleOf: 0.001, description: 使用未舍入外径完整计算后四舍五入保留3位} remark: {type: string, nullable: true} version: {type: integer, minimum: 0} diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/CartonMeasurementServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/CartonMeasurementServiceTest.java index 39fea23eb..2070eeea6 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/CartonMeasurementServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/CartonMeasurementServiceTest.java @@ -90,10 +90,10 @@ class CartonMeasurementServiceTest { assertThat(result.scannedShipmentId()).isEqualTo("FBA-SCAN-002"); assertThat(insertedItem.get().getScannedShipmentId()).isEqualTo("FBA-SCAN-002"); assertThat(insertedItem.get().getMeasurementId()).isEqualTo(30001L); - assertThat(insertedItem.get().getTotalVolumeCbm()).isEqualByComparingTo("0.1870"); - assertThat(insertedItem.get().getOuterLengthCm()).isEqualByComparingTo("64.000"); - assertThat(insertedItem.get().getOuterWidthCm()).isEqualByComparingTo("54.000"); - assertThat(insertedItem.get().getOuterHeightCm()).isEqualByComparingTo("54.000"); + assertThat(insertedItem.get().getTotalVolumeCbm()).isEqualByComparingTo("0.1680"); + assertThat(insertedItem.get().getOuterLengthCm()).isEqualByComparingTo("62.000"); + assertThat(insertedItem.get().getOuterWidthCm()).isEqualByComparingTo("52.000"); + assertThat(insertedItem.get().getOuterHeightCm()).isEqualByComparingTo("52.000"); assertThat(measurement.getSourceType()).isEqualTo(MeasurementBatchService.SOURCE_THIRD_PARTY); assertThat(measurement.getSourceCode()).isEqualTo("DEVICE"); } @@ -155,9 +155,9 @@ class CartonMeasurementServiceTest { assertThat(result.created()).isFalse(); assertThat(result.confirmedPackageCount()).isEqualTo(4); assertThat(existing.getLengthCm()).isEqualByComparingTo("59.500"); - assertThat(existing.getUnitVolumeCbm()).isEqualByComparingTo("0.187"); - assertThat(existing.getTotalVolumeCbm()).isEqualByComparingTo("0.7480"); - assertThat(existing.getVolumeWeightKg()).isEqualByComparingTo("124.916"); + assertThat(existing.getUnitVolumeCbm()).isEqualByComparingTo("0.168"); + assertThat(existing.getTotalVolumeCbm()).isEqualByComparingTo("0.6720"); + assertThat(existing.getVolumeWeightKg()).isEqualByComparingTo("112.224"); assertThat(existing.getSourceImageFileId()).isEqualTo(99001L); assertThat(existing.getScannedShipmentId()).isEqualTo("FBA-SCAN-002"); verify(images).delete("internal-token", 90001L, 10001L, 70001L, 88001L); diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionConfigServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionConfigServiceTest.java index bd6179c64..8913594b6 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionConfigServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionConfigServiceTest.java @@ -95,10 +95,10 @@ class CustomerDimensionConfigServiceTest { assertThat(result.rules()).extracting(rule -> rule.boxType()) .containsExactly(CustomerDimensionBoxType.values()); assertThat(result.rules().get(0).boxTypeName()).isEqualTo("小箱"); - assertThat(result.rules().get(0).outerLengthCm()).isEqualByComparingTo("34.000"); - assertThat(result.rules().get(0).outerWidthCm()).isEqualByComparingTo("24.000"); - assertThat(result.rules().get(0).outerHeightCm()).isEqualByComparingTo("24.000"); - assertThat(result.rules().get(0).volumeCbm()).isEqualByComparingTo("0.020"); + assertThat(result.rules().get(0).outerLengthCm()).isEqualByComparingTo("32.000"); + assertThat(result.rules().get(0).outerWidthCm()).isEqualByComparingTo("22.000"); + assertThat(result.rules().get(0).outerHeightCm()).isEqualByComparingTo("22.000"); + assertThat(result.rules().get(0).volumeCbm()).isEqualByComparingTo("0.015"); } @Test diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionPricingFlowTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionPricingFlowTest.java index 33616c9f5..cd59ff673 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionPricingFlowTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionPricingFlowTest.java @@ -32,7 +32,7 @@ class CustomerDimensionPricingFlowTest { var kgEstimate = pricing.estimate(rate("KG", "8.700000"), actualWeight, twoCartonsVolume); var cbmEstimate = pricing.estimate(rate("CBM", "1400.000000"), actualWeight, twoCartonsVolume); - assertThat(twoCartonsVolume).isEqualByComparingTo("0.040"); + assertThat(twoCartonsVolume).isEqualByComparingTo("0.030"); assertThat(kgEstimate.quantity()).isEqualByComparingTo("24.000000"); assertThat(kgEstimate.amount()).isEqualByComparingTo("208.80"); assertThat(cbmEstimate.quantity()).isEqualByComparingTo("0.066116"); diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingServiceTest.java index c2d696991..423983954 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingServiceTest.java @@ -26,16 +26,16 @@ class CustomerDimensionWeightingServiceTest { } @Test - void measuredDimensionsUseSmallestContainingRuleAndAddEachEdgeTwice() { + void measuredDimensionsUseSmallestContainingRuleAndAddConfiguredValueOncePerDimension() { var weighted = service.load(10001L).resolve( new BigDecimal("10"), new BigDecimal("10"), new BigDecimal("10")); assertThat(weighted.boxType()).isEqualTo(CustomerDimensionBoxType.SMALL); - assertThat(weighted.outerLengthCm()).isEqualByComparingTo("34.000"); - assertThat(weighted.outerWidthCm()).isEqualByComparingTo("24.000"); - assertThat(weighted.outerHeightCm()).isEqualByComparingTo("24.000"); - assertThat(weighted.rawVolumeCbm()).isEqualByComparingTo("0.019584"); - assertThat(weighted.unitVolumeCbm()).isEqualByComparingTo("0.020"); + assertThat(weighted.outerLengthCm()).isEqualByComparingTo("32.000"); + assertThat(weighted.outerWidthCm()).isEqualByComparingTo("22.000"); + assertThat(weighted.outerHeightCm()).isEqualByComparingTo("22.000"); + assertThat(weighted.rawVolumeCbm()).isEqualByComparingTo("0.015488"); + assertThat(weighted.unitVolumeCbm()).isEqualByComparingTo("0.015"); } @Test @@ -52,19 +52,20 @@ class CustomerDimensionWeightingServiceTest { new BigDecimal("30.001"), new BigDecimal("20"), new BigDecimal("20")); assertThat(weighted.boxType()).isEqualTo(CustomerDimensionBoxType.MEDIUM); - assertThat(weighted.unitVolumeCbm()).isEqualByComparingTo("0.051"); + assertThat(weighted.unitVolumeCbm()).isEqualByComparingTo("0.043"); } @Test - void dimensionsOutsideConfiguredWeightingRulesUseActualDimensions() { + void dimensionsOutsideConfiguredWeightingRulesUseLargestTierAdditionOnActualDimensions() { var weighted = service.load(10001L).resolve( new BigDecimal("60.001"), new BigDecimal("50"), new BigDecimal("50")); - assertThat(weighted.boxType()).isNull(); - assertThat(weighted.outerLengthCm()).isEqualByComparingTo("60.001"); - assertThat(weighted.outerWidthCm()).isEqualByComparingTo("50"); - assertThat(weighted.outerHeightCm()).isEqualByComparingTo("50"); - assertThat(weighted.calculationRuleVersion()).isEqualTo("ACTUAL_DIMENSION_V1"); + assertThat(weighted.boxType()).isEqualTo(CustomerDimensionBoxType.EXTRA_LARGE); + assertThat(weighted.edgeAdditionCm()).isEqualByComparingTo("2.000"); + assertThat(weighted.outerLengthCm()).isEqualByComparingTo("62.001"); + assertThat(weighted.outerWidthCm()).isEqualByComparingTo("52.000"); + assertThat(weighted.outerHeightCm()).isEqualByComparingTo("52.000"); + assertThat(weighted.calculationRuleVersion()).isEqualTo("CUSTOMER_DIMENSION_EXTRA_LARGE_V3"); } @Test diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingTerminalRulesTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingTerminalRulesTest.java index 46f8d80f3..8408df171 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingTerminalRulesTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/CustomerDimensionWeightingTerminalRulesTest.java @@ -24,13 +24,13 @@ class CustomerDimensionWeightingTerminalRulesTest { var ruleSet = service.load(10001L); assertWeighted(ruleSet, "10", "10", "10", CustomerDimensionBoxType.SMALL, - "34", "24", "24", "0.020"); + "32", "22", "22", "0.015"); assertWeighted(ruleSet, "35", "25", "25", CustomerDimensionBoxType.MEDIUM, - "44", "34", "34", "0.051"); + "42", "32", "32", "0.043"); assertWeighted(ruleSet, "45", "35", "35", CustomerDimensionBoxType.LARGE, - "54", "44", "44", "0.105"); + "52", "42", "42", "0.092"); assertWeighted(ruleSet, "55", "45", "45", CustomerDimensionBoxType.EXTRA_LARGE, - "64", "54", "54", "0.187"); + "62", "52", "52", "0.168"); } private static void assertWeighted(CustomerDimensionWeightingService.RuleSet rules, diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementBatchServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementBatchServiceTest.java index 45bf02aad..3bc37d323 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementBatchServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementBatchServiceTest.java @@ -152,8 +152,8 @@ class MeasurementBatchServiceTest { + "LARGE:50,40,40;系数:1.3;大箱:—;EXTRA_LARGE:60,50,50;系数:1.4;特大箱:—"); when(pricingRepository.findDefaultRule(1L, 1L)).thenReturn(rule); MeasurementBatchCommand.Item small = new MeasurementBatchCommand.Item( - 80001L, "001", "A", 1, new BigDecimal("30"), new BigDecimal("20"), - new BigDecimal("20"), 500L, null, null, null, null, null, null, "小箱"); + 80001L, "001", "A", 1, new BigDecimal("10"), new BigDecimal("10"), + new BigDecimal("10"), 500L, null, null, null, null, null, null, "低于最小档"); MeasurementBatchCommand.Item medium = new MeasurementBatchCommand.Item( 80002L, "002", "B", 1, new BigDecimal("31"), new BigDecimal("20"), new BigDecimal("20"), 501L, null, null, null, null, null, null, "中箱"); @@ -166,9 +166,9 @@ class MeasurementBatchServiceTest { ArgumentCaptor> items = ArgumentCaptor.forClass(List.class); verify(repository).replaceItems(org.mockito.ArgumentMatchers.eq(1L), org.mockito.ArgumentMatchers.eq(100L), items.capture()); - assertThat(items.getValue().get(0).getOuterLengthCm()).isEqualByComparingTo("33.000"); - assertThat(items.getValue().get(0).getOuterWidthCm()).isEqualByComparingTo("22.000"); - assertThat(items.getValue().get(0).getOuterHeightCm()).isEqualByComparingTo("22.000"); + assertThat(items.getValue().get(0).getOuterLengthCm()).isEqualByComparingTo("11.000"); + assertThat(items.getValue().get(0).getOuterWidthCm()).isEqualByComparingTo("11.000"); + assertThat(items.getValue().get(0).getOuterHeightCm()).isEqualByComparingTo("11.000"); assertThat(items.getValue().get(1).getOuterLengthCm()).isEqualByComparingTo("37.200"); assertThat(items.getValue().get(1).getOuterWidthCm()).isEqualByComparingTo("24.000"); assertThat(items.getValue().get(1).getOuterHeightCm()).isEqualByComparingTo("24.000"); @@ -221,7 +221,32 @@ class MeasurementBatchServiceTest { } @Test - void warehouseOuterDimensionsOverrideCalculatedCartonAndAllDerivedValues() { + void dimensionsAboveLargestTierAddLargestTierValueToMeasuredDimensions() { + MeasurementBatchCommand.Item source = new MeasurementBatchCommand.Item( + 80001L, "001", "A", 2, new BigDecimal("60.001"), new BigDecimal("50"), + new BigDecimal("50"), 500L, null, null, null, null, null, "ADD", "超过最大箱规"); + MeasurementBatchCommand command = new MeasurementBatchCommand( + 1L, 10L, null, 0, "MANUAL", "PLATFORM", null, null, null, + LocalDateTime.now(), null, null, null, null, null, 9L, List.of(source)); + + service.saveBatch(command); + + ArgumentCaptor> items = ArgumentCaptor.forClass(List.class); + verify(repository).replaceItems(org.mockito.ArgumentMatchers.eq(1L), + org.mockito.ArgumentMatchers.eq(100L), items.capture()); + InboundMeasurementItemEntity calculated = items.getValue().get(0); + assertThat(calculated.getOuterLengthCm()).isEqualByComparingTo("62.001"); + assertThat(calculated.getOuterWidthCm()).isEqualByComparingTo("52.000"); + assertThat(calculated.getOuterHeightCm()).isEqualByComparingTo("52.000"); + assertThat(calculated.getGirthCm()).isEqualByComparingTo("270.001"); + assertThat(calculated.getUnitVolumeCbm()).isEqualByComparingTo("0.168"); + assertThat(calculated.getTotalVolumeCbm()).isEqualByComparingTo("0.3360"); + assertThat(calculated.getCalculationRuleVersion()) + .isEqualTo("CUSTOMER_DIMENSION_EXTRA_LARGE_V0"); + } + + @Test + void warehouseOuterDimensionsBecomeBaseValuesBeforeAddStrategy() { MeasurementBatchCommand.Item source = new MeasurementBatchCommand.Item( 80001L, "001", "A", 2, new BigDecimal("40"), new BigDecimal("30"), new BigDecimal("20"), 500L, null, new BigDecimal("50"), @@ -239,10 +264,10 @@ class MeasurementBatchServiceTest { assertThat(calculated.getOuterLengthCm()).isEqualByComparingTo("50.000"); assertThat(calculated.getOuterWidthCm()).isEqualByComparingTo("40.000"); assertThat(calculated.getOuterHeightCm()).isEqualByComparingTo("30.000"); - assertThat(calculated.getGirthCm()).isEqualByComparingTo("190.000"); - assertThat(calculated.getUnitVolumeCbm()).isEqualByComparingTo("0.0600"); - assertThat(calculated.getTotalVolumeCbm()).isEqualByComparingTo("0.1200"); - assertThat(calculated.getVolumeWeightKg()).isEqualByComparingTo("20.040"); + assertThat(calculated.getGirthCm()).isEqualByComparingTo("200.000"); + assertThat(calculated.getUnitVolumeCbm()).isEqualByComparingTo("0.0699"); + assertThat(calculated.getTotalVolumeCbm()).isEqualByComparingTo("0.1398"); + assertThat(calculated.getVolumeWeightKg()).isEqualByComparingTo("23.347"); assertThat(calculated.getCalculationRuleVersion()).isEqualTo("WAREHOUSE_OUTER_OVERRIDE_V1"); } @@ -265,7 +290,47 @@ class MeasurementBatchServiceTest { assertThat(calculated.getOuterLengthCm()).isEqualByComparingTo("50.000"); assertThat(calculated.getOuterWidthCm()).isEqualByComparingTo("32.000"); assertThat(calculated.getOuterHeightCm()).isEqualByComparingTo("22.000"); - assertThat(calculated.getUnitVolumeCbm()).isEqualByComparingTo("0.0352"); + assertThat(calculated.getUnitVolumeCbm()).isEqualByComparingTo("0.0367"); + assertThat(calculated.getTotalVolumeCbm()).isEqualByComparingTo("0.0734"); + assertThat(calculated.getGirthCm()).isEqualByComparingTo("160.000"); + assertThat(calculated.getCalculationRuleVersion()).isEqualTo("WAREHOUSE_OUTER_OVERRIDE_L_V1"); + } + + @Test + void warehouseOverrideUsesTheTierMatchedByItsBaseDimensionsBeforeMultiplyStrategy() { + CustomerChargeRuleEntity rule = new CustomerChargeRuleEntity(); + rule.setId(79L); + rule.setTenantId(1L); + rule.setCustomerId(1L); + rule.setRuleCode("TIERED-OVERRIDE"); + rule.setRuleVersion(1); + rule.setStatus("ACTIVE"); + rule.setDefaultRule(true); + rule.setLengthFactor(new BigDecimal("1.1")); + rule.setWidthFactor(new BigDecimal("1.1")); + rule.setHeightFactor(new BigDecimal("1.1")); + rule.setRemark("SMALL:30,20,20;系数:1.1;小箱:—;MEDIUM:40,30,30;系数:1.2;中箱:—"); + when(pricingRepository.findDefaultRule(1L, 1L)).thenReturn(rule); + MeasurementBatchCommand.Item source = new MeasurementBatchCommand.Item( + 80001L, "001", "A", 1, new BigDecimal("30"), new BigDecimal("20"), + new BigDecimal("20"), 500L, null, new BigDecimal("31"), + null, null, null, "MULTIPLY", "仅修改长度"); + MeasurementBatchCommand command = new MeasurementBatchCommand( + 1L, 10L, null, 0, "MANUAL", "PLATFORM", null, null, null, + LocalDateTime.now(), null, null, null, null, null, 9L, List.of(source)); + + service.saveBatch(command); + + ArgumentCaptor> items = ArgumentCaptor.forClass(List.class); + verify(repository).replaceItems(org.mockito.ArgumentMatchers.eq(1L), + org.mockito.ArgumentMatchers.eq(100L), items.capture()); + InboundMeasurementItemEntity calculated = items.getValue().get(0); + assertThat(calculated.getOuterLengthCm()).isEqualByComparingTo("31.000"); + assertThat(calculated.getOuterWidthCm()).isEqualByComparingTo("24.000"); + assertThat(calculated.getOuterHeightCm()).isEqualByComparingTo("24.000"); + assertThat(calculated.getUnitVolumeCbm()).isEqualByComparingTo("0.0215"); + assertThat(calculated.getGirthCm()).isEqualByComparingTo("133.200"); + assertThat(calculated.getChargeRuleId()).isEqualTo(79L); assertThat(calculated.getCalculationRuleVersion()).isEqualTo("WAREHOUSE_OUTER_OVERRIDE_L_V1"); }