This commit is contained in:
2026-08-28 10:38:58 +08:00
parent b34fbb2bd1
commit 1e5ce54858
37 changed files with 256 additions and 159 deletions
@@ -85,7 +85,7 @@ class LoadingCompletedOutboxServiceTest {
.hasMessageContaining("缺少 SKU");
}
@Test void rejectsMissingTrustedBookingCustomerName() {
@Test void rejectsMissingTrustedPlanItemCustomerName() {
Row value = row(11, 100, 101);
value.setCustomerName(" ");
when(snapshots.rows(90L)).thenReturn(List.of(value));
@@ -93,12 +93,19 @@ class LoadingCompletedOutboxServiceTest {
assertThatThrownBy(() -> service.publish(90L)).hasMessageContaining("缺少客户名称快照");
}
@Test void rejectsBookingCustomerIdThatDoesNotMatchAccountEvent() {
@Test void publishesFrozenPlanItemCustomerSnapshotWithoutDependingOnBookingOwner() throws Exception {
Row value = row(11, 100, 101);
value.setBookingCustomerId(22L);
value.setCustomerName("南玻万");
value.setDestinationType("FBA");
when(snapshots.rows(90L)).thenReturn(List.of(value));
assertThatThrownBy(() -> service.publish(90L)).hasMessageContaining("客户与柜次客户快照不一致");
service.publish(90L);
ArgumentCaptor<InboundOutboxEntity> captor = ArgumentCaptor.forClass(InboundOutboxEntity.class);
verify(outbox).insert(captor.capture());
EventEnvelope event = mapper.readValue(captor.getValue().getPayloadJson(), EventEnvelope.class);
assertThat(event.tenantId()).isEqualTo("11");
assertThat(event.payload().at("/orders/0/customerName").asText()).isEqualTo("南玻万");
}
@Test void fbaFreezesPackageCountWithoutRequiringCartonLabels() throws Exception {
@@ -209,8 +216,9 @@ class LoadingCompletedOutboxServiceTest {
assertThat(rowsSql).contains("p.plan_status = 'COMPLETED'")
.contains("o.process_status = 'LOADED'")
.contains("b.customer_id AS booking_customer_id")
.contains("b.customer_name")
.contains("i.customer_name")
.doesNotContain("b.customer_id AS booking_customer_id")
.doesNotContain("b.customer_name")
.contains("b.estimated_port_arrival_at", "b.actual_port_arrival_at")
.contains("b.estimated_warehouse_arrival_at");
for (String sql : List.of(rowsSql, palletsSql)) {
@@ -288,7 +296,7 @@ class LoadingCompletedOutboxServiceTest {
private Row row(long account,long inbound,long detail) {
Row r=new Row(); r.setLoadingPlanId(90L); r.setPlanNo("P90"); r.setContainerBookingId(91L);
r.setAggregateVersion(8); r.setContainerNo("CONT"); r.setCabinetNo("CAB"); r.setWarehouseId(92L);
r.setWarehouseName("WH"); r.setBookingCustomerId(account); r.setCustomerName("客户-" + account);
r.setWarehouseName("WH"); r.setCustomerName("客户-" + account);
r.setEstimatedPortArrivalAt(java.time.LocalDateTime.of(2026, 9, 30, 12, 0));
r.setActualPortArrivalAt(java.time.LocalDateTime.of(2026, 10, 1, 8, 30));
r.setEstimatedWarehouseArrivalAt(java.time.LocalDateTime.of(2026, 10, 3, 9, 0));
@@ -63,6 +63,24 @@ class LoadingCompletionWorkerTest {
verify(picks).failCompletion(20L);
}
@Test
void completedEventBusinessFailureIsTerminalOnFirstAttempt() {
LoadingCompletionCommandEntity command = command(0);
when(commands.due(10)).thenReturn(List.of(command));
when(commands.claim(eq(900L), any(String.class), any(LocalDateTime.class))).thenReturn(true);
doThrow(new LoadingPickService.CompletionProcessingException("COMPLETED_EVENT",
new BizException(ResultCode.CONFLICT, "customer snapshot conflict")))
.when(loadingPicks).processCompletion(eq(command), any(String.class));
when(commands.fail(eq(900L), any(String.class), eq("FAILED"), any(LocalDateTime.class),
eq("COMPLETED_EVENT"), eq(ResultCode.CONFLICT.code()), eq(null), eq(null))).thenReturn(true);
worker.dispatch();
verify(commands).fail(eq(900L), any(String.class), eq("FAILED"), any(LocalDateTime.class),
eq("COMPLETED_EVENT"), eq(ResultCode.CONFLICT.code()), eq(null), eq(null));
verify(picks).failCompletion(20L);
}
private static LoadingCompletionCommandEntity command(int attemptCount) {
LoadingCompletionCommandEntity command = new LoadingCompletionCommandEntity();
command.setId(900L);