fix(loading): remove ISO container validation across PDA fulfillment
This commit is contained in:
@@ -2,15 +2,11 @@ package com.baiyue.common.event;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/** Canonical physical container number used by cross-region business facts. */
|
||||
/** 跨服务业务事实使用的真实集装箱号,只规范化空白和大小写,不校验 ISO 格式。 */
|
||||
public final class ContainerNumbers {
|
||||
private static final int[] LETTER_VALUES = {
|
||||
10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38
|
||||
};
|
||||
|
||||
private ContainerNumbers() { }
|
||||
|
||||
/** 柜号必须非空;统一移除格式空白并转大写,保留业务录入值。 */
|
||||
public static String normalizeRequired(String value) {
|
||||
if (value == null || value.isBlank()) {
|
||||
throw new IllegalArgumentException("containerNo is required");
|
||||
@@ -19,21 +15,13 @@ public final class ContainerNumbers {
|
||||
value.codePoints().filter(codePoint -> !Character.isWhitespace(codePoint)
|
||||
&& !Character.isSpaceChar(codePoint)).forEach(compact::appendCodePoint);
|
||||
String normalized = compact.toString().toUpperCase(Locale.ROOT);
|
||||
if (!normalized.matches("[A-Z]{3}[UJZ][0-9]{7}")) {
|
||||
throw new IllegalArgumentException("containerNo must comply with ISO 6346");
|
||||
}
|
||||
int sum = 0;
|
||||
for (int index = 0; index < 10; index++) {
|
||||
char current = normalized.charAt(index);
|
||||
int number = Character.isDigit(current) ? current - '0' : LETTER_VALUES[current - 'A'];
|
||||
sum += number * (1 << index);
|
||||
}
|
||||
if (normalized.charAt(10) - '0' != sum % 11 % 10) {
|
||||
throw new IllegalArgumentException("containerNo must comply with ISO 6346 check digit");
|
||||
if (normalized.isEmpty()) {
|
||||
throw new IllegalArgumentException("containerNo is required");
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/** 冻结事实必须已规范化,避免生产与消费端的快照或哈希发生漂移。 */
|
||||
public static String requireCanonical(String value) {
|
||||
String normalized = normalizeRequired(value);
|
||||
if (!normalized.equals(value)) {
|
||||
|
||||
+27
-6
@@ -1,20 +1,41 @@
|
||||
package com.baiyue.common.event;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class ContainerNumbersTest {
|
||||
/** 规范化仍保持确定性,事件只能接受已冻结的规范值。 */
|
||||
@Test
|
||||
void normalizesWhitespaceAndCaseButCanonicalFactsRequireExactIso6346Value() {
|
||||
void normalizesWhitespaceAndCaseButCanonicalFactsRequireExactValue() {
|
||||
assertThat(ContainerNumbers.normalizeRequired(" zcsu 7881970 ")).isEqualTo("ZCSU7881970");
|
||||
assertThat(ContainerNumbers.requireCanonical("ZCSU7881970")).isEqualTo("ZCSU7881970");
|
||||
assertThatThrownBy(() -> ContainerNumbers.requireCanonical("zcsu7881970"))
|
||||
.hasMessageContaining("normalized");
|
||||
assertThatThrownBy(() -> ContainerNumbers.normalizeRequired("ZCSU7881971"))
|
||||
.hasMessageContaining("check digit");
|
||||
assertThatThrownBy(() -> ContainerNumbers.normalizeRequired("CAB-260831-0001"))
|
||||
.hasMessageContaining("ISO 6346");
|
||||
assertThatThrownBy(() -> ContainerNumbers.requireCanonical(" JZXH10231043 "))
|
||||
.hasMessageContaining("normalized");
|
||||
}
|
||||
|
||||
/** 非 ISO 柜号可以作为事件事实,不在下游重新阻断。 */
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"JZXH10231043", "ZCSU7881971", "MSCA6639870", "ABC", "ABCU123456789012", "LOCAL-2026-001"})
|
||||
void acceptsNonIsoNumber(String containerNo) {
|
||||
assertThat(ContainerNumbers.normalizeRequired(" " + containerNo.toLowerCase(java.util.Locale.ROOT) + " "))
|
||||
.isEqualTo(containerNo);
|
||||
assertThat(ContainerNumbers.requireCanonical(containerNo)).isEqualTo(containerNo);
|
||||
}
|
||||
|
||||
/** 规范化前后均不得将空白柜号作为业务事实。 */
|
||||
@ParameterizedTest
|
||||
@NullAndEmptySource
|
||||
@ValueSource(strings = {" ", "\u00a0\u3000"})
|
||||
void rejectsMissingNumber(String containerNo) {
|
||||
assertThatThrownBy(() -> ContainerNumbers.normalizeRequired(containerNo))
|
||||
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining("required");
|
||||
assertThatThrownBy(() -> ContainerNumbers.requireCanonical(containerNo))
|
||||
.isInstanceOf(IllegalArgumentException.class).hasMessageContaining("required");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user