fix(lastleg): prevent transfer business number collisions

This commit is contained in:
2026-10-06 00:20:43 +08:00
parent 4bde1a8352
commit 60543adf63
3 changed files with 18 additions and 3 deletions
@@ -47,6 +47,11 @@ public class LastlegOrderNoGenerator {
// Return numbers are independent business identifiers, not truncated database IDs.
// Keep every UUID bit; the longest result (RETU + 25 base-36 digits) fits VARCHAR(32).
if (returnPrefix != null) return returnPrefix + "-" + encodeReturnUuid(UUID.randomUUID());
// Transfer numbers also identify synthetic inbound events globally. Truncating
// a database ID to six digits can collide between unrelated fresh transfers.
if ("TRANSFER".equals(code) || "WAREHOUSE_TRANSFER".equals(code)) {
return "WT-" + encodeReturnUuid(UUID.randomUUID());
}
String type = normalizeType(businessType);
return type + "-" + warehouseCode(warehouse) + "-"
+ LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE) + "-"
@@ -81,4 +81,14 @@ class LastlegOrderNoGeneratorTest {
assertTrue(value.matches("OB-LAX-\\d{8}-000123"), value);
assertTrue(value.length() <= 24);
}
@Test void transferNumbersDoNotCollideWhenDatabaseIdSuffixesRepeat() {
String first = generator.next("TRANSFER", "warehouse-LAX-01", 123L);
String second = generator.next("TRANSFER", "warehouse-LAX-01", 1_000_123L);
String repeatedId = generator.next("WAREHOUSE_TRANSFER", "warehouse-LAX-01", 123L);
assertTrue(first.matches("WT-[0-9A-Z]{25}"), first);
assertTrue(first.length() <= 32);
assertNotEquals(first, second);
assertNotEquals(first, repeatedId);
}
}
@@ -231,7 +231,7 @@ class WarehouseTransferServiceTest {
ReflectionTestUtils.setField(service, "relabelService", relabelService);
ReflectionTestUtils.setField(service, "objectMapper", new ObjectMapper().findAndRegisterModules());
lenient().when(orderNoGenerator.next(anyString(), any(), anyLong()))
.thenReturn("WT-WH-20260901-000001");
.thenReturn("WT-0000000000000000000000001");
lenient().when(inventoryAllocations.selectList(any())).thenReturn(List.of());
lenient().when(transferReceipts.selectList(any())).thenReturn(List.of());
lenient().when(transferCartons.listOrder(anyString(), anyLong())).thenReturn(List.of());
@@ -386,7 +386,7 @@ class WarehouseTransferServiceTest {
assertEquals(WarehouseTransferService.ORDER_TYPE, tailOrder.getValue().getOrderType());
assertEquals(WarehouseTransferService.DRAFT, tailOrder.getValue().getCurrentStatus());
assertEquals("WAREHOUSE_TRANSFER:DRAFT;RELABEL:PENDING_BINDING", tailOrder.getValue().getSubOrderStatus());
assertTrue(tailOrder.getValue().getOrderNo().matches("WT-WH-\\d{8}-\\d{6}"));
assertTrue(tailOrder.getValue().getOrderNo().matches("WT-[0-9A-Z]{25}"));
assertNull(tailOrder.getValue().getTransferWarehouseId());
verify(sourceOrders).insert(any(WarehouseTransferSourceOrder.class));
verify(relabelApplications, never()).insert(any(RelabelApplication.class));
@@ -544,7 +544,7 @@ class WarehouseTransferServiceTest {
assertEquals(List.of(1, 2, 3, 4, 5), cartons.getAllValues().stream()
.map(WarehouseTransferCarton::getCartonIndex).toList());
assertTrue(cartons.getAllValues().stream().allMatch(value -> value.getCartonMark()
.matches("WT-WH-\\d{8}-\\d{6}#5-[1-5]")));
.matches("WT-[0-9A-Z]{25}#5-[1-5]")));
}
@Test