优化
This commit is contained in:
+81
@@ -0,0 +1,81 @@
|
||||
package com.baiyue.lastleg.customervisit.service;
|
||||
|
||||
import com.baiyue.lastleg.customervisit.dto.CustomerVisitRequests;
|
||||
import com.baiyue.lastleg.customervisit.entity.CustomerVisit;
|
||||
import com.baiyue.lastleg.customervisit.mapper.CustomerVisitMapper;
|
||||
import com.baiyue.lastleg.delivery.entity.DeliveryOrder;
|
||||
import com.baiyue.lastleg.delivery.mapper.DeliveryOperationMapper;
|
||||
import com.baiyue.lastleg.delivery.mapper.DeliveryOrderMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CustomerVisitServiceTest {
|
||||
@Mock private CustomerVisitMapper visits;
|
||||
@Mock private DeliveryOrderMapper deliveryOrders;
|
||||
@Mock private DeliveryOperationMapper deliveryOperations;
|
||||
private CustomerVisitService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new CustomerVisitService();
|
||||
ReflectionTestUtils.setField(service, "visits", visits);
|
||||
ReflectionTestUtils.setField(service, "deliveryOrders", deliveryOrders);
|
||||
ReflectionTestUtils.setField(service, "deliveryOperations", deliveryOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsRollbackFromDeliveredToWaitDeliveryWithoutWritingHistory() {
|
||||
DeliveryOrder delivery = delivery("DELIVERED");
|
||||
when(deliveryOrders.findByNo("1001", "DL-1")).thenReturn(delivery);
|
||||
|
||||
assertThatThrownBy(() -> service.updateDeliveryStatus("1001", "9001", "DL-1",
|
||||
new CustomerVisitRequests.DeliveryStatus("WAIT_DELIVERY", "错误倒退")))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.satisfies(error -> assertThat(((ResponseStatusException) error).getStatusCode().value()).isEqualTo(409))
|
||||
.hasMessageContaining("DELIVERED").hasMessageContaining("WAIT_DELIVERY");
|
||||
|
||||
verify(deliveryOrders, never()).updateById(delivery);
|
||||
verifyNoInteractions(deliveryOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaysDeliveredToDeliveredWithoutChangingDeliveryOrAddingHistory() {
|
||||
DeliveryOrder delivery = delivery("DELIVERED");
|
||||
CustomerVisit visit = new CustomerVisit();
|
||||
visit.setId(33L);
|
||||
visit.setStatus("PENDING");
|
||||
when(deliveryOrders.findByNo("1001", "DL-1")).thenReturn(delivery);
|
||||
when(visits.findByDelivery("1001", 11L)).thenReturn(visit);
|
||||
|
||||
var result = service.updateDeliveryStatus("1001", "9001", "DL-1",
|
||||
new CustomerVisitRequests.DeliveryStatus("DELIVERED", null));
|
||||
|
||||
assertThat(result.replayed()).isTrue();
|
||||
assertThat(result.deliveryStatus()).isEqualTo("DELIVERED");
|
||||
assertThat(result.visitId()).isEqualTo(33L);
|
||||
verify(deliveryOrders, never()).updateById(delivery);
|
||||
verifyNoInteractions(deliveryOperations);
|
||||
}
|
||||
|
||||
private DeliveryOrder delivery(String status) {
|
||||
DeliveryOrder delivery = new DeliveryOrder();
|
||||
delivery.setId(11L);
|
||||
delivery.setCustomerId("1001");
|
||||
delivery.setDeliveryNo("DL-1");
|
||||
delivery.setStatus(status);
|
||||
return delivery;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -66,6 +66,6 @@ class DeliveryLongIdJsonContractTest {
|
||||
DELIVERY_ID,
|
||||
null, null, "PS-1", null, null, null, null, "CREATED", null,
|
||||
null, null, null, null, null, null, null, null, null, null, null,
|
||||
0, 0, null, null, null, null, null, null, null, null);
|
||||
0, 0, null, null, null, null, null, null, null, null, java.util.List.of());
|
||||
}
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.baiyue.lastleg.delivery;
|
||||
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipOrderProfileMapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class DropshipOutboundCandidateMapperContractTest {
|
||||
@Test
|
||||
void candidateQueryIsSingleStatementAndFailsClosedOnEveryEligibilityFact() throws Exception {
|
||||
String sql = select("listOutboundCandidates", String.class, String.class);
|
||||
for (String token : new String[] {"p.customer_id=#{customerId}", "p.status='ACTIVE'",
|
||||
"o.order_type='ONE_CLICK_DROPSHIP'", "COALESCE(o.detained,0)=0", "pos.status='OCCUPIED'",
|
||||
"pos.available_packages>0", "s.package_count>0", "c.status='AVAILABLE'",
|
||||
"ll_dispatch_pallet_lock", "ll_warehouse_transfer_inventory_allocation",
|
||||
"ll_outbound_pallet_item", "pos.overseas_warehouse_id=#{warehouseId}", "LIMIT 500"}) {
|
||||
assertTrue(sql.contains(token), token);
|
||||
}
|
||||
assertTrue(sql.contains("LEAST(pos.available_packages,s.package_count"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void creationLockUsesTheSameEligibilityContractAndLocksOneSkuDimension() throws Exception {
|
||||
String sql = select("lockOutboundCandidate", String.class, Long.class, String.class);
|
||||
assertTrue(sql.contains("AND pos.id=#{positionId}"));
|
||||
assertTrue(sql.contains("AND s.sku_code=#{skuCode}"));
|
||||
assertTrue(sql.contains("LIMIT 1 FOR UPDATE"));
|
||||
assertTrue(sql.contains("ll_dispatch_pallet_lock"));
|
||||
assertTrue(sql.contains("ll_outbound_pallet_item"));
|
||||
}
|
||||
|
||||
private String select(String method, Class<?>... parameterTypes) throws Exception {
|
||||
Select annotation = DropshipOrderProfileMapper.class.getMethod(method, parameterTypes)
|
||||
.getAnnotation(Select.class);
|
||||
assertNotNull(annotation);
|
||||
return String.join("", annotation.value());
|
||||
}
|
||||
}
|
||||
+8
@@ -90,15 +90,20 @@ class PlatformDispatchOutboundContractTest {
|
||||
void oneClickRequiresCustomerWhileOutboundListKeepsOptionalFilter() throws Exception {
|
||||
Method create = DeliveryTransferController.class.getMethod("createOneClick", AuthenticatedUser.class,
|
||||
String.class, DeliveryRequests.OneClickOutbound.class);
|
||||
Method candidates = DeliveryTransferController.class.getMethod("oneClickCandidates", AuthenticatedUser.class,
|
||||
String.class, String.class);
|
||||
Method page = DeliveryTransferController.class.getMethod("outboundPage", AuthenticatedUser.class,
|
||||
String.class, DeliveryRequests.Query.class);
|
||||
|
||||
assertPermission(create, OverseasPermissions.OUTBOUND_CREATE);
|
||||
assertPermission(candidates, OverseasPermissions.OUTBOUND_CREATE);
|
||||
assertPermission(page, OverseasPermissions.OUTBOUND_LIST);
|
||||
RequestParam createCustomer = create.getParameters()[1].getAnnotation(RequestParam.class);
|
||||
RequestParam pageCustomer = page.getParameters()[1].getAnnotation(RequestParam.class);
|
||||
assertNotNull(createCustomer);
|
||||
assertTrue(createCustomer.required());
|
||||
assertTrue(candidates.getParameters()[1].getAnnotation(RequestParam.class).required());
|
||||
assertFalse(candidates.getParameters()[2].getAnnotation(RequestParam.class).required());
|
||||
assertNotNull(pageCustomer);
|
||||
assertFalse(pageCustomer.required());
|
||||
|
||||
@@ -167,6 +172,9 @@ class PlatformDispatchOutboundContractTest {
|
||||
assertFalse(action.contains("name: customerId"));
|
||||
}
|
||||
assertTrue(outboundDetail.contains("pallets[].palletNo"));
|
||||
assertTrue(yaml.contains("/api/platform/v1/last-leg/outbound-orders/one-click-dropship/candidates:"));
|
||||
assertTrue(yaml.contains("OneClickOutboundCandidate"));
|
||||
assertTrue(yaml.contains("单次 SQL 返回,不逐行补查"));
|
||||
assertTrue(yaml.contains("platform:overseas-warehouse:dispatch:edit"));
|
||||
assertTrue(yaml.contains("platform:overseas-warehouse:dispatch:cancel"));
|
||||
assertTrue(yaml.contains("platform:overseas-warehouse:dispatch:transfer-delivery"));
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.baiyue.lastleg.delivery.model;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DeliveryStatusPolicyTest {
|
||||
@Test
|
||||
void exposesOnlyServerAuthorizedActionsForEachState() {
|
||||
assertThat(DeliveryStatusPolicy.allowedActions("WAIT_DELIVERY"))
|
||||
.containsExactly("START_DELIVERY", "COMPLETE_DELIVERY", "REJECT_DELIVERY", "CANCEL_DELIVERY", "MARK_RETURN");
|
||||
assertThat(DeliveryStatusPolicy.allowedActions("DELIVERING"))
|
||||
.containsExactly("COMPLETE_DELIVERY", "REJECT_DELIVERY", "MARK_RETURN");
|
||||
assertThat(DeliveryStatusPolicy.allowedActions("DELIVERED")).containsExactly("MARK_RETURN");
|
||||
assertThat(DeliveryStatusPolicy.allowedActions("RETURN_PENDING")).isEmpty();
|
||||
assertThat(DeliveryStatusPolicy.allowedActions("CANCELLED")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void terminalStatesCannotReenterTheDeliveryFlow() {
|
||||
assertThat(DeliveryStatusPolicy.canTransition("DELIVERED", "WAIT_DELIVERY")).isFalse();
|
||||
assertThat(DeliveryStatusPolicy.canTransition("DELIVERED", "DELIVERING")).isFalse();
|
||||
assertThat(DeliveryStatusPolicy.canTransition("REJECTED", "DELIVERING")).isFalse();
|
||||
assertThat(DeliveryStatusPolicy.canTransition("CANCELLED", "DELIVERED")).isFalse();
|
||||
}
|
||||
}
|
||||
+48
-6
@@ -34,6 +34,8 @@ import com.baiyue.lastleg.dropship.entity.DropshipOrderProfile;
|
||||
import com.baiyue.lastleg.dropship.entity.DropshipCarton;
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipOrderProfileMapper;
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipCartonMapper;
|
||||
import com.baiyue.lastleg.dropship.mapper.DropshipSkuInventoryMapper;
|
||||
import com.baiyue.lastleg.dropship.model.DropshipOutboundCandidate;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -78,6 +80,7 @@ class DeliveryTransferServiceTest {
|
||||
@Mock InventoryBalanceMapper inventoryBalances;
|
||||
@Mock DropshipOrderProfileMapper dropshipProfiles;
|
||||
@Mock DropshipCartonMapper dropshipCartons;
|
||||
@Mock DropshipSkuInventoryMapper dropshipSkuInventory;
|
||||
|
||||
private DeliveryTransferService service;
|
||||
|
||||
@@ -103,6 +106,7 @@ class DeliveryTransferServiceTest {
|
||||
ReflectionTestUtils.setField(service, "inventoryBalances", inventoryBalances);
|
||||
ReflectionTestUtils.setField(service, "dropshipProfiles", dropshipProfiles);
|
||||
ReflectionTestUtils.setField(service, "dropshipCartons", dropshipCartons);
|
||||
ReflectionTestUtils.setField(service, "dropshipSkuInventory", dropshipSkuInventory);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -398,12 +402,8 @@ class DeliveryTransferServiceTest {
|
||||
|
||||
@Test
|
||||
void oneClickOutboundRejectsSummarySkuWithoutAvailableDropshipCartons() {
|
||||
InventoryPosition position = new InventoryPosition();
|
||||
position.setId(901L); position.setCustomerId("TENANT-A"); position.setOverseasWarehouseId("WH-1");
|
||||
position.setPalletId(501L); position.setPalletNo("PLT-501"); position.setAvailablePackages(1L);
|
||||
DropshipOrderProfile profile = new DropshipOrderProfile(); profile.setId(1001L);
|
||||
when(inventoryPositions.selectById(901L)).thenReturn(position);
|
||||
when(dropshipProfiles.findPosition("TENANT-A", 901L)).thenReturn(profile);
|
||||
DropshipOutboundCandidate candidate = dropshipCandidate(901L, 1001L, "WH-1", "PLT-501", "MIXED", 1L);
|
||||
when(dropshipProfiles.lockOutboundCandidate("TENANT-A", 901L, "MIXED")).thenReturn(candidate);
|
||||
when(dropshipCartons.lockAvailable("TENANT-A", 1001L, "MIXED", 1L)).thenReturn(List.of());
|
||||
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class, () -> service.createOneClick(
|
||||
@@ -411,9 +411,43 @@ class DeliveryTransferServiceTest {
|
||||
new DeliveryRequests.OneClickItem(901L, 1L, "MIXED")), null, "one-click-mixed")));
|
||||
|
||||
assertEquals(409, exception.getStatusCode().value());
|
||||
assertTrue(exception.getReason().contains("一件代发可用箱数不足"));
|
||||
verify(outboundOrders, never()).insert(any(OutboundOrder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneClickCandidatesUseOneAuthoritativeQueryAndExposeStableBusinessFields() {
|
||||
DropshipOutboundCandidate candidate = dropshipCandidate(901L, 1001L, "WH-1", "DS-A002", "SKU-1", 3L);
|
||||
candidate.setOverseasWarehouseName("Los Angeles"); candidate.setInboundCode("IN-1");
|
||||
candidate.setProductName("Product 1");
|
||||
when(dropshipProfiles.listOutboundCandidates("TENANT-A", "WH-1")).thenReturn(List.of(candidate));
|
||||
|
||||
List<DeliveryResponses.OneClickCandidate> result = service.oneClickCandidates("TENANT-A", " WH-1 ");
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(901L, result.get(0).inventoryPositionId());
|
||||
assertEquals("DS-A002", result.get(0).palletNo());
|
||||
assertEquals("SKU-1", result.get(0).skuCode());
|
||||
assertEquals(3L, result.get(0).availablePackages());
|
||||
verify(dropshipProfiles).listOutboundCandidates("TENANT-A", "WH-1");
|
||||
verify(inventoryPositions, never()).selectById(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneClickCreationRejectsOrdinaryInventoryBeforeAnyWrite() {
|
||||
when(dropshipProfiles.lockOutboundCandidate("TENANT-A", 901L, "MIXED")).thenReturn(null);
|
||||
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class, () -> service.createOneClick(
|
||||
"TENANT-A", "U-1", new DeliveryRequests.OneClickOutbound("recipient", List.of(
|
||||
new DeliveryRequests.OneClickItem(901L, 1L, "MIXED")), null, "ordinary-stock")));
|
||||
|
||||
assertEquals(409, exception.getStatusCode().value());
|
||||
assertEquals("所选库存不是可用的一件代发库存,或已被其他作业占用", exception.getReason());
|
||||
verify(outboundOrders, never()).insert(any(OutboundOrder.class));
|
||||
verify(outboundPallets, never()).insert(any(OutboundPalletItem.class));
|
||||
verify(pdaTasks, never()).insert(any(OutboundPdaTask.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void outboundPdaListAndDetailUseOnlyBoundWarehouse() throws Exception {
|
||||
assertNotNull(DeliveryTransferService.class.getMethod("pdaPick", String.class, String.class,
|
||||
@@ -652,4 +686,12 @@ class DeliveryTransferServiceTest {
|
||||
value.setInboundCode(inboundCode); value.setOrderRefs("LMO-1"); value.setPackageCount(packages);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static DropshipOutboundCandidate dropshipCandidate(Long positionId, Long profileId, String warehouseId,
|
||||
String palletNo, String sku, Long packages) {
|
||||
DropshipOutboundCandidate value = new DropshipOutboundCandidate();
|
||||
value.setInventoryPositionId(positionId); value.setProfileId(profileId); value.setWarehousePalletId(501L);
|
||||
value.setOverseasWarehouseId(warehouseId); value.setPalletNo(palletNo); value.setSkuCode(sku);
|
||||
value.setAvailablePackages(packages); return value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user