feat(order): add customer order foundations
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
package com.baiyue.order.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class OrderCustomerFoundationOpenApiContractTest {
|
||||
@Test
|
||||
void publishesAddressBatchAndWoodInspectionContracts() throws Exception {
|
||||
try (var input = getClass().getResourceAsStream("/openapi/order-v1.yaml")) {
|
||||
assertThat(input).isNotNull();
|
||||
String yaml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
assertThat(yaml)
|
||||
.contains("/api/merchant/v1/order-addresses:")
|
||||
.contains("/api/merchant/v1/orders/first-leg/batch-imports:")
|
||||
.contains("senderAddressId:")
|
||||
.contains("privateDestinationAddressId:")
|
||||
.contains("woodenProduct:")
|
||||
.contains("ORDER_BATCH_IMPORT_SOURCE")
|
||||
.contains("ATOMIC 在任一行失败时整批回滚")
|
||||
.doesNotContain("发货单位和收货单位仅通过 sender/receiver");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.baiyue.order.service;
|
||||
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.order.dto.CustomerAddressDtos;
|
||||
import com.baiyue.order.entity.CustomerAddressEntity;
|
||||
import com.baiyue.order.mapper.CustomerAddressMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class CustomerAddressServiceTest {
|
||||
@Test
|
||||
void createsCustomerOwnedPrivateAddressWithoutCallingWarehouse() {
|
||||
CustomerAddressMapper mapper = mock(CustomerAddressMapper.class);
|
||||
CustomerAddressService service = new CustomerAddressService();
|
||||
ReflectionTestUtils.setField(service, "mapper", mapper);
|
||||
var request = new CustomerAddressDtos.SaveRequest("PRIVATE_DESTINATION", "洛杉矶私人仓", "客户海外仓",
|
||||
"Alex", "+1-213-555-0100", null, "US", "CA", "Los Angeles", null,
|
||||
"123 Private Ave", "90001", false);
|
||||
|
||||
service.create(10001L, 20001L, request);
|
||||
|
||||
verify(mapper).insert(any(CustomerAddressEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectingAnotherCustomersAddressIsReportedAsNotFound() {
|
||||
CustomerAddressMapper mapper = mock(CustomerAddressMapper.class);
|
||||
when(mapper.selectOne(any())).thenReturn(null);
|
||||
CustomerAddressService service = new CustomerAddressService();
|
||||
ReflectionTestUtils.setField(service, "mapper", mapper);
|
||||
|
||||
assertThatThrownBy(() -> service.required(10001L, 90001L, "PICKUP"))
|
||||
.isInstanceOf(BizException.class).hasMessage("客户地址不存在或已停用");
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringJUnitConfig(MerchantBatchDraftService.class)
|
||||
class MerchantBatchDraftServiceTest {
|
||||
@@ -85,6 +86,21 @@ class MerchantBatchDraftServiceTest {
|
||||
assertThat(anotherRow).isNotEqualTo(first);
|
||||
}
|
||||
|
||||
@Test
|
||||
void atomicModeValidatesEveryRowBeforeCreatingJoinedTransactions() {
|
||||
when(itemService.createJoined(anyLong(), anyLong(), isNull(), any(), anyString()))
|
||||
.thenReturn(order(101L, "FL101", null), order(102L, "FL102", null));
|
||||
|
||||
var response = service.createAtomic(10L, 20L, null, "atomic-key",
|
||||
List.of(item("row-1"), item("row-2")));
|
||||
|
||||
assertThat(response.succeeded()).isEqualTo(2);
|
||||
verify(itemService, org.mockito.Mockito.times(2)).validate(any());
|
||||
verify(itemService, org.mockito.Mockito.times(2))
|
||||
.createJoined(anyLong(), anyLong(), isNull(), any(), anyString());
|
||||
verify(itemService, never()).create(anyLong(), anyLong(), isNull(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsInvalidBatchBeforeCreatingRows() {
|
||||
assertThatThrownBy(() -> service.create(10L, 20L, " ", List.of(item("row-1"))))
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.baiyue.order.service;
|
||||
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class MerchantBatchImportServiceTest {
|
||||
@Test
|
||||
void templateUsesStableRowIdentityAndAuthoritativeDraftSchema() throws Exception {
|
||||
byte[] content = new MerchantBatchImportService().template();
|
||||
try (var workbook = new XSSFWorkbook(new ByteArrayInputStream(content))) {
|
||||
var sheet = workbook.getSheetAt(0);
|
||||
assertThat(sheet.getRow(0).getCell(0).getStringCellValue()).isEqualTo("clientRowId");
|
||||
assertThat(sheet.getRow(0).getCell(11).getStringCellValue()).isEqualTo("senderAddressId");
|
||||
assertThat(sheet.getRow(0).getCell(21).getStringCellValue()).isEqualTo("privateDestinationAddressId");
|
||||
assertThat(sheet.getRow(0).getCell(32).getStringCellValue()).isEqualTo("cargo.woodenProduct");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,29 +476,31 @@ class OrderApplicationServiceStateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void pickupDraftAllowsMissingSender() {
|
||||
void pickupDraftRequiresSelectedAddress() {
|
||||
OrderRepository repository = mock(OrderRepository.class);
|
||||
OrderMasterDataGuard masterData = mock(OrderMasterDataGuard.class);
|
||||
OrderRequests.SaveRequest request = request(null, "PICKUP", null, null, "OVERSEAS");
|
||||
when(masterData.validate(20002L, request)).thenReturn(snapshots(null));
|
||||
when(repository.completeIdempotency(anyLong(), any(), anyLong())).thenReturn(1);
|
||||
|
||||
service(repository, mock(OrderEligibilityGuard.class), masterData)
|
||||
.createDraft(20002L, 2L, request, "pickup-without-sender");
|
||||
verify(repository).insertOrder(any());
|
||||
assertThatThrownBy(() -> service(repository, mock(OrderEligibilityGuard.class), masterData)
|
||||
.createDraft(20002L, 2L, request, "pickup-without-sender"))
|
||||
.isInstanceOf(BizException.class).hasMessage("上门提货时必须选择取货地址");
|
||||
verify(repository, never()).insertOrder(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateDraftAllowsMissingReceiver() {
|
||||
void privateDraftRequiresSelectedAddress() {
|
||||
OrderRepository repository = mock(OrderRepository.class);
|
||||
OrderMasterDataGuard masterData = mock(OrderMasterDataGuard.class);
|
||||
OrderRequests.SaveRequest request = request(null, "SELF_EXPRESS", null, null, "PRIVATE");
|
||||
|
||||
when(masterData.validate(20002L, request)).thenReturn(snapshots(null));
|
||||
when(repository.completeIdempotency(anyLong(), any(), anyLong())).thenReturn(1);
|
||||
service(repository, mock(OrderEligibilityGuard.class), masterData)
|
||||
.createDraft(20002L, 2L, request, "private-without-receiver");
|
||||
verify(repository).insertOrder(any());
|
||||
assertThatThrownBy(() -> service(repository, mock(OrderEligibilityGuard.class), masterData)
|
||||
.createDraft(20002L, 2L, request, "private-without-receiver"))
|
||||
.isInstanceOf(BizException.class).hasMessage("私人目的仓必须选择客户地址");
|
||||
verify(repository, never()).insertOrder(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.baiyue.order.service;
|
||||
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.order.dto.OrderRequests;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class OrderWoodInspectionRuleTest {
|
||||
@Test
|
||||
void woodProductRequiresInspectionWithStableMessage() {
|
||||
OrderRequests.Cargo cargo = mock(OrderRequests.Cargo.class);
|
||||
OrderRequests.SaveRequest request = mock(OrderRequests.SaveRequest.class);
|
||||
when(cargo.woodenProduct()).thenReturn(true);
|
||||
when(request.cargoes()).thenReturn(List.of(cargo));
|
||||
when(request.customsInspection()).thenReturn(null);
|
||||
|
||||
assertThatThrownBy(() -> OrderApplicationService.validateWoodInspection(request))
|
||||
.isInstanceOf(BizException.class).hasMessage("木制品必须选择商检");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonWoodProductAllowsInspectionToBeOmitted() {
|
||||
OrderRequests.Cargo cargo = mock(OrderRequests.Cargo.class);
|
||||
OrderRequests.SaveRequest request = mock(OrderRequests.SaveRequest.class);
|
||||
when(cargo.woodenProduct()).thenReturn(false);
|
||||
when(request.cargoes()).thenReturn(List.of(cargo));
|
||||
when(request.customsInspection()).thenReturn(null);
|
||||
|
||||
assertThatCode(() -> OrderApplicationService.validateWoodInspection(request)).doesNotThrowAnyException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user