Merge branch 'dev-lyhfive' of http://git.buggoglobal.com/java-project/baiyue-firstleg into dev
This commit is contained in:
+7
@@ -24,6 +24,13 @@ class PlatformLoadingPdaControllerContractTest {
|
||||
authority("review", "platform:firstleg:pda:review", AuthenticatedUser.class, long.class, ReviewRequest.class);
|
||||
authority("complete", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class, CompleteLoadingRequest.class);
|
||||
authority("operations", "platform:firstleg:pda:view", AuthenticatedUser.class, long.class);
|
||||
authority("pallets", "platform:firstleg:pda:view", AuthenticatedUser.class, long.class, long.class);
|
||||
authority("openPallet", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class,
|
||||
com.baiyue.firstleg.dto.LoadingPalletDtos.OpenRequest.class);
|
||||
authority("scanCarton", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class, long.class,
|
||||
com.baiyue.firstleg.dto.LoadingPalletDtos.ScanRequest.class);
|
||||
authority("sealPallet", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class, long.class,
|
||||
com.baiyue.firstleg.dto.LoadingPalletDtos.SealRequest.class);
|
||||
}
|
||||
|
||||
private void authority(String name, String expected, Class<?>... parameterTypes) throws Exception {
|
||||
|
||||
+31
@@ -290,6 +290,30 @@ class LoadingCompletedOutboxServiceTest {
|
||||
assertThat(event.payload().at("/orders/0/palletCount").asInt()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test void publishesExactPdaCartonToPalletBindingsWithoutSequentialRedistribution() throws Exception {
|
||||
Row first = row(11, 100, 101); first.setFulfillmentType("ONE_CLICK_DROPSHIP");
|
||||
first.setPackageCount(2); first.setProductQuantity(20); first.setSkuCode("SKU-A");
|
||||
Row second = row(11, 100, 102); second.setFulfillmentType("ONE_CLICK_DROPSHIP");
|
||||
second.setPackageCount(2); second.setProductQuantity(10); second.setSkuCode("SKU-B");
|
||||
when(snapshots.rows(90L)).thenReturn(List.of(first, second));
|
||||
when(snapshots.workPalletCount(90L)).thenReturn(2);
|
||||
when(snapshots.actualPalletRows(90L)).thenReturn(List.of(
|
||||
actualPallet(11,100,"PLT-01",101,"SKU-A","BOX-A2"),
|
||||
actualPallet(11,100,"PLT-01",102,"SKU-B","BOX-B1"),
|
||||
actualPallet(11,100,"PLT-02",101,"SKU-A","BOX-A1"),
|
||||
actualPallet(11,100,"PLT-02",102,"SKU-B","BOX-B2")));
|
||||
|
||||
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.payload().at("/orders/0/pallets/0/skuPackages/0/cartonMarks/0").asText())
|
||||
.isEqualTo("BOX-A2");
|
||||
assertThat(event.payload().at("/orders/0/pallets/1/skuPackages/0/cartonMarks/0").asText())
|
||||
.isEqualTo("BOX-A1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void pricingBusinessTypeCannotTurnAStandardOrderIntoDropship() throws Exception {
|
||||
Row value = row(11, 100, 101);
|
||||
@@ -347,6 +371,13 @@ class LoadingCompletedOutboxServiceTest {
|
||||
row.setPackageCount(packages); row.setProductQuantity(quantity); row.setBoxCode(boxCode); return row;
|
||||
}
|
||||
|
||||
private PalletRow actualPallet(long account,long inbound,String palletNo,long detail,
|
||||
String sku,String boxCode) {
|
||||
PalletRow row = pallet(account,inbound,0,palletNo,1,detail,sku,1,1,boxCode);
|
||||
row.setActualBinding(true);
|
||||
return row;
|
||||
}
|
||||
|
||||
private LabelRow label(long detailId, String boxCode) {
|
||||
LabelRow row = new LabelRow(); row.setInboundDetailId(detailId); row.setBoxCode(boxCode); return row;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.firstleg.dto.LoadingPalletDtos.ScanRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingPalletDtos.SealRequest;
|
||||
import com.baiyue.firstleg.entity.LoadingWorkPalletCartonEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingWorkPalletEntity;
|
||||
import com.baiyue.firstleg.mapper.LoadingWorkPalletCartonMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingWorkPalletCartonMapper.CartonCandidate;
|
||||
import com.baiyue.firstleg.mapper.LoadingWorkPalletMapper;
|
||||
import com.baiyue.firstleg.mapper.LoadingWorkPalletMapper.TaskContext;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringJUnitConfig(LoadingWorkPalletService.class)
|
||||
class LoadingWorkPalletServiceTest {
|
||||
@MockBean private LoadingWorkPalletMapper pallets;
|
||||
@MockBean private LoadingWorkPalletCartonMapper cartons;
|
||||
@Resource private LoadingWorkPalletService service;
|
||||
|
||||
@Test
|
||||
void scansDropshipCartonOntoMixedCustomerWorkPalletWithoutChangingPickWorkflow() {
|
||||
when(pallets.selectTaskContext(10L, 20L)).thenReturn(context());
|
||||
LoadingWorkPalletEntity pallet = pallet(30L, "OPEN");
|
||||
when(pallets.selectScoped(10L, 30L, 20L)).thenReturn(pallet);
|
||||
CartonCandidate candidate = candidate();
|
||||
when(cartons.selectCandidates(10L, 20L, "BOX-A")).thenReturn(List.of(candidate));
|
||||
doAnswer(invocation -> {
|
||||
LoadingWorkPalletCartonEntity value = invocation.getArgument(0);
|
||||
value.setId(50L);
|
||||
return 1;
|
||||
}).when(cartons).insert(any(LoadingWorkPalletCartonEntity.class));
|
||||
when(cartons.selectPalletCartons(30L)).thenAnswer(invocation -> List.of());
|
||||
|
||||
service.scan(10L, 30L, new ScanRequest(20L, "BOX-A", "SCAN-1"), 99L, "作业员");
|
||||
|
||||
var captor = org.mockito.ArgumentCaptor.forClass(LoadingWorkPalletCartonEntity.class);
|
||||
verify(cartons).insert(captor.capture());
|
||||
assertThat(captor.getValue().getTenantId()).isEqualTo(88L);
|
||||
assertThat(captor.getValue().getLoadingPlanItemId()).isEqualTo(41L);
|
||||
assertThat(captor.getValue().getSkuCode()).isEqualTo("SKU-A");
|
||||
}
|
||||
|
||||
@Test
|
||||
void refusesToSealUntilScannedCartonsAreReconciledByExistingPickSubmission() {
|
||||
when(pallets.selectTaskContext(10L, 20L)).thenReturn(context());
|
||||
when(pallets.selectScoped(10L, 30L, 20L)).thenReturn(pallet(30L, "OPEN"));
|
||||
when(cartons.countPalletCartons(30L)).thenReturn(2);
|
||||
when(cartons.countUnreconciled(10L)).thenReturn(1);
|
||||
|
||||
assertThatThrownBy(() -> service.seal(10L, 30L,
|
||||
new SealRequest(20L, 0, "SEAL-1"), 99L, "作业员"))
|
||||
.hasMessageContaining("原PDA拣货提交");
|
||||
}
|
||||
|
||||
private static TaskContext context() {
|
||||
return new TaskContext(10L, 11L, "LOADING", 20L, 99L, 2);
|
||||
}
|
||||
|
||||
private static LoadingWorkPalletEntity pallet(long id, String status) {
|
||||
LoadingWorkPalletEntity value = new LoadingWorkPalletEntity();
|
||||
value.setId(id); value.setPickTaskId(10L); value.setLoadingPlanId(11L);
|
||||
value.setWarehouseId(20L); value.setPalletCode("PLT-01");
|
||||
value.setPalletStatus(status); value.setVersion(0);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static CartonCandidate candidate() {
|
||||
CartonCandidate value = new CartonCandidate();
|
||||
value.setCartonLabelId(40L); value.setTenantId(88L); value.setInboundId(81L);
|
||||
value.setInboundDetailId(82L); value.setBoxCode("BOX-A");
|
||||
value.setLoadingPlanItemId(41L); value.setPickItemId(42L);
|
||||
value.setSkuCode("SKU-A"); value.setUnitsPerCarton(20);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.common.event.EventPayloadCanonicalizer;
|
||||
import com.baiyue.common.event.EventPayloadHash;
|
||||
import com.baiyue.common.event.EventTypes;
|
||||
import com.baiyue.common.event.OverseasInboundOrderDetainedChangedV1;
|
||||
import com.baiyue.common.event.RegionCode;
|
||||
import com.baiyue.firstleg.entity.InboundHoldRecordEntity;
|
||||
import com.baiyue.firstleg.entity.InboundOrderEntity;
|
||||
import com.baiyue.firstleg.entity.ContainerEventInboxEntity;
|
||||
import com.baiyue.firstleg.mapper.ContainerEventInboxMapper;
|
||||
import com.baiyue.firstleg.mapper.InboundHoldRecordMapper;
|
||||
import com.baiyue.firstleg.mapper.InboundOrderMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class OverseasInboundOrderDetainedConsumerTest {
|
||||
@Mock InboundOrderMapper inboundOrders;
|
||||
@Mock InboundHoldRecordMapper holdRecords;
|
||||
@Mock ContainerEventInboxMapper inbox;
|
||||
ObjectMapper objectMapper;
|
||||
OverseasInboundOrderDetainedConsumer consumer;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
|
||||
consumer = new OverseasInboundOrderDetainedConsumer(inboundOrders, holdRecords, inbox, objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void appliesHoldBySourceInboundIdAndWritesHistory() {
|
||||
InboundOrderEntity inbound = inbound(null);
|
||||
when(inboundOrders.selectActiveByIdForUpdate(1001L)).thenReturn(inbound);
|
||||
when(inboundOrders.applyOverseasHold(88L, 1001L, true, "等待查验", 7L, 66L)).thenReturn(1);
|
||||
|
||||
consumer.consume(event(7, true, "等待查验"));
|
||||
|
||||
verify(inboundOrders).applyOverseasHold(88L, 1001L, true, "等待查验", 7L, 66L);
|
||||
ArgumentCaptor<InboundHoldRecordEntity> history = ArgumentCaptor.forClass(InboundHoldRecordEntity.class);
|
||||
verify(holdRecords).insert(history.capture());
|
||||
assertThat(history.getValue().getInboundId()).isEqualTo(1001L);
|
||||
assertThat(history.getValue().getHoldAction()).isEqualTo("HOLD");
|
||||
verify(inbox).updateById(argThat((ContainerEventInboxEntity row) -> "PROCESSED".equals(row.getStatus())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void staleReleaseCannotOverwriteNewerHoldState() {
|
||||
when(inboundOrders.selectActiveByIdForUpdate(1001L)).thenReturn(inbound(9L));
|
||||
|
||||
consumer.consume(event(7, false, "解除"));
|
||||
|
||||
verify(inboundOrders, never()).applyOverseasHold(anyLong(), anyLong(), anyBoolean(), any(), anyLong(), anyLong());
|
||||
verifyNoInteractions(holdRecords);
|
||||
verify(inbox).updateById(argThat((ContainerEventInboxEntity row) -> "PROCESSED".equals(row.getStatus())));
|
||||
}
|
||||
|
||||
private InboundOrderEntity inbound(Long sourceVersion) {
|
||||
InboundOrderEntity value = new InboundOrderEntity();
|
||||
value.setId(1001L); value.setTenantId(88L); value.setInboundCode("IN-1001");
|
||||
value.setLastOverseasHoldVersion(sourceVersion);
|
||||
return value;
|
||||
}
|
||||
|
||||
private EventEnvelope event(long version, boolean detained, String reason) {
|
||||
var payload = new OverseasInboundOrderDetainedChangedV1("1001", "IN-1001", "9001",
|
||||
"LM-9001", detained, reason, "66", "海外操作员", Instant.parse("2026-08-31T06:00:00Z"));
|
||||
var node = objectMapper.valueToTree(payload);
|
||||
return new EventEnvelope(String.format("00000000-0000-4000-8000-%012d", version),
|
||||
EventTypes.OVERSEAS_INBOUND_ORDER_DETAINED_CHANGED, 1, payload.changedAt(),
|
||||
RegionCode.OVERSEAS, RegionCode.CN, "88", "INBOUND_ORDER", "1001", version, 1L,
|
||||
"00000000-0000-4000-8000-000000000099", "hold-1001-" + version, null, node,
|
||||
EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(node)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user