格式修改

This commit is contained in:
2026-08-05 10:35:07 +08:00
parent 81c9c92f5d
commit 496fc58a90
241 changed files with 5600 additions and 2088 deletions
@@ -1,5 +1,8 @@
package com.baiyue.order.controller;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
@@ -19,13 +22,13 @@ import java.security.MessageDigest;
@RestController
@RequestMapping("/internal/order/v1/cargoes")
public class InternalOrderCargoController {
private final OrderRepository orders;
private final byte[] expectedToken;
@Resource private OrderRepository orders;
@Value("${baiyue.security.internal-service-token}") private String internalToken;
private byte[] expectedToken;
public InternalOrderCargoController(OrderRepository orders,
@Value("${baiyue.security.internal-service-token}") String token) {
this.orders = orders;
this.expectedToken = token.getBytes(StandardCharsets.UTF_8);
@PostConstruct
void initializeToken() {
expectedToken = internalToken.getBytes(StandardCharsets.UTF_8);
}
@GetMapping("/{tenantId}/{orderId}/{cargoId}")
@@ -1,5 +1,7 @@
package com.baiyue.order.repository;
import jakarta.annotation.Resource;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.order.dto.OrderRequests;
import com.baiyue.order.dto.MerchantInboundOrderQuery;
@@ -22,28 +24,14 @@ import java.time.LocalDateTime;
*/
@Repository
public class OrderRepository {
private final OrderMapper orderMapper;
private final OrderCargoMapper cargoMapper;
private final OrderPartyMapper partyMapper;
private final OrderInternalNoteMapper noteMapper;
private final OrderStatusHistoryMapper historyMapper;
private final OutboxEventMapper outboxMapper;
private final OrderChildPurgeMapper childPurgeMapper;
private final OrderIdempotencyRecordMapper idempotencyMapper;
public OrderRepository(OrderMapper orderMapper, OrderCargoMapper cargoMapper, OrderPartyMapper partyMapper,
OrderInternalNoteMapper noteMapper, OrderStatusHistoryMapper historyMapper,
OutboxEventMapper outboxMapper, OrderChildPurgeMapper childPurgeMapper,
OrderIdempotencyRecordMapper idempotencyMapper) {
this.orderMapper = orderMapper;
this.cargoMapper = cargoMapper;
this.partyMapper = partyMapper;
this.noteMapper = noteMapper;
this.historyMapper = historyMapper;
this.outboxMapper = outboxMapper;
this.childPurgeMapper = childPurgeMapper;
this.idempotencyMapper = idempotencyMapper;
}
@Resource private OrderMapper orderMapper;
@Resource private OrderCargoMapper cargoMapper;
@Resource private OrderPartyMapper partyMapper;
@Resource private OrderInternalNoteMapper noteMapper;
@Resource private OrderStatusHistoryMapper historyMapper;
@Resource private OutboxEventMapper outboxMapper;
@Resource private OrderChildPurgeMapper childPurgeMapper;
@Resource private OrderIdempotencyRecordMapper idempotencyMapper;
public void insertOrder(OrderEntity order) { orderMapper.insert(order); }
public int saveOrder(OrderEntity order) { return orderMapper.updateById(order); }
@@ -1,5 +1,7 @@
package com.baiyue.order.service;
import jakarta.annotation.Resource;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
@@ -24,18 +26,9 @@ import java.util.stream.Collectors;
@Service
public class MerchantInboundOrderListService {
private final OrderRepository repository;
private final FirstlegInboundClient firstleg;
private final String internalToken;
public MerchantInboundOrderListService(
OrderRepository repository,
FirstlegInboundClient firstleg,
@Value("${baiyue.security.internal-service-token}") String internalToken) {
this.repository = repository;
this.firstleg = firstleg;
this.internalToken = internalToken;
}
@Resource private OrderRepository repository;
@Resource private FirstlegInboundClient firstleg;
@Value("${baiyue.security.internal-service-token}") private String internalToken;
public PageResult<MerchantInboundOrderRow> page(long tenantId, MerchantInboundOrderQuery query) {
PageResult<OrderEntity> source = repository.merchantInboundPage(tenantId, query);
@@ -1,5 +1,7 @@
package com.baiyue.order.service;
import jakarta.annotation.Resource;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.order.client.FirstlegInboundClient;
import com.baiyue.order.entity.OutboxEventEntity;
@@ -14,19 +16,10 @@ import java.time.LocalDateTime;
/** 可靠投递订单审核事件;接收端以订单唯一键保证重复投递安全。 */
@Component
public class OrderInboundOutboxPublisher {
private final OrderRepository repository;
private final FirstlegInboundClient client;
private final ObjectMapper json;
private final String internalToken;
public OrderInboundOutboxPublisher(OrderRepository repository, FirstlegInboundClient client,
ObjectMapper json,
@Value("${baiyue.security.internal-service-token}") String internalToken) {
this.repository = repository;
this.client = client;
this.json = json;
this.internalToken = internalToken;
}
@Resource private OrderRepository repository;
@Resource private FirstlegInboundClient client;
@Resource private ObjectMapper json;
@Value("${baiyue.security.internal-service-token}") private String internalToken;
@Scheduled(fixedDelayString = "${baiyue.order.inbound-outbox.fixed-delay-ms:3000}")
public void publishApprovedOrders() {
@@ -1,5 +1,7 @@
package com.baiyue.order.service;
import jakarta.annotation.Resource;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.exception.BizException;
@@ -18,15 +20,11 @@ import org.slf4j.LoggerFactory;
@Service
public class OrderMasterDataGuard {
private static final Logger log = LoggerFactory.getLogger(OrderMasterDataGuard.class);
private final WarehouseClient warehouses;
private final PartnerClient partners;
private final String token;
@Resource private WarehouseClient warehouses;
@Resource private PartnerClient partners;
@Value("${baiyue.security.internal-service-token}") private String token;
@Value("${baiyue.warehouse.public-tenant-id:10001}")
private long publicTenantId = 10001L;
public OrderMasterDataGuard(WarehouseClient warehouses, PartnerClient partners,
@Value("${baiyue.security.internal-service-token}") String token) {
this.warehouses=warehouses; this.partners=partners; this.token=token;
}
public Snapshots validate(long tenantId, OrderRequests.SaveRequest request) {
try {
PartnerClient.Partner partner=request.customerId()==null ? null : loadPartner(tenantId,request.customerId());
+1 -1
View File
@@ -41,6 +41,6 @@ logging:
rollingpolicy:
file-name-pattern: "${LOGGING_ROLLING_FILE_NAME_PATTERN:${LOG_PATH:./logs}/${spring.application.name}.%d{yyyy-MM-dd}.%i.log.gz}"
max-file-size: "${LOGGING_ROLLING_MAX_FILE_SIZE:100MB}"
max-history: "${LOGGING_ROLLING_MAX_HISTORY:30}"
max-history: "${LOGGING_ROLLING_MAX_HISTORY:7}"
total-size-cap: "${LOGGING_ROLLING_TOTAL_SIZE_CAP:5GB}"
clean-history-on-start: "${LOGGING_ROLLING_CLEAN_HISTORY_ON_START:false}"
@@ -4,6 +4,10 @@ import com.baiyue.common.core.exception.BizException;
import com.baiyue.order.entity.OrderCargoEntity;
import com.baiyue.order.repository.OrderRepository;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -11,13 +15,14 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(InternalOrderCargoController.class)
@TestPropertySource(properties = "baiyue.security.internal-service-token=internal-token")
class InternalOrderCargoControllerTest {
private final OrderRepository orders = mock(OrderRepository.class);
private final InternalOrderCargoController controller =
new InternalOrderCargoController(orders, "internal-token");
@MockBean private OrderRepository orders;
@Resource private InternalOrderCargoController controller;
@Test
void 只返回指定租户订单下的已有货物明细() {
void shouldHandleCase01() {
OrderCargoEntity cargo = new OrderCargoEntity();
cargo.setId(31L);
cargo.setOrderId(21L);
@@ -35,7 +40,7 @@ class InternalOrderCargoControllerTest {
}
@Test
void 内部令牌错误时拒绝查询() {
void shouldHandleCase02() {
assertThatThrownBy(() -> controller.detail("bad-token", 11L, 21L, 31L))
.isInstanceOf(BizException.class)
.hasMessageContaining("凭证无效");
@@ -5,6 +5,9 @@ import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.order.dto.OrderRequests;
import com.baiyue.order.service.OrderApplicationService;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.Set;
@@ -14,14 +17,15 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@SpringJUnitConfig(PlatformFirstLegOrderController.class)
class PlatformOrderPageControllerTest {
@MockBean private OrderApplicationService service;
@Resource private PlatformFirstLegOrderController controller;
private final OrderRequests.QueryRequest query = new OrderRequests.QueryRequest(
null, null, null, null, null, null, 1, 20);
@Test
void omittedTenantQueriesAcrossAllTenantsForPlatformUser() {
OrderApplicationService service = mock(OrderApplicationService.class);
PlatformFirstLegOrderController controller = controller(service);
controller.page(user("PLATFORM"), null, query);
@@ -30,8 +34,6 @@ class PlatformOrderPageControllerTest {
@Test
void suppliedTenantFiltersOneTenantForPlatformUser() {
OrderApplicationService service = mock(OrderApplicationService.class);
PlatformFirstLegOrderController controller = controller(service);
controller.page(user("PLATFORM"), 2084455952401629185L, query);
@@ -40,8 +42,6 @@ class PlatformOrderPageControllerTest {
@Test
void invalidTenantFilterIsRejectedBeforeQuery() {
OrderApplicationService service = mock(OrderApplicationService.class);
PlatformFirstLegOrderController controller = controller(service);
assertThatThrownBy(() -> controller.page(user("PLATFORM"), 0L, query))
.isInstanceOf(BizException.class)
@@ -51,8 +51,6 @@ class PlatformOrderPageControllerTest {
@Test
void merchantCannotUseCrossTenantPlatformList() {
OrderApplicationService service = mock(OrderApplicationService.class);
PlatformFirstLegOrderController controller = controller(service);
assertThatThrownBy(() -> controller.page(user("MERCHANT"), null, query))
.isInstanceOf(BizException.class)
@@ -60,12 +58,6 @@ class PlatformOrderPageControllerTest {
verifyNoInteractions(service);
}
private PlatformFirstLegOrderController controller(OrderApplicationService service) {
PlatformFirstLegOrderController controller = new PlatformFirstLegOrderController();
ReflectionTestUtils.setField(controller, "service", service);
return controller;
}
private AuthenticatedUser user(String appCode) {
return new AuthenticatedUser(1L, null, null, null, "operator", appCode, "WEB", Set.of());
}
@@ -18,6 +18,9 @@ import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -25,20 +28,24 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(OrderRepository.class)
class MerchantInboundOrderQueryRepositoryTest {
@MockBean private OrderMapper mapper;
@MockBean private OrderCargoMapper cargoMapper;
@MockBean private OrderPartyMapper partyMapper;
@MockBean private OrderInternalNoteMapper noteMapper;
@MockBean private OrderStatusHistoryMapper historyMapper;
@MockBean private OutboxEventMapper outboxMapper;
@MockBean private OrderChildPurgeMapper purgeMapper;
@MockBean private OrderIdempotencyRecordMapper idempotencyMapper;
@Resource private OrderRepository repository;
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
void 查询始终带租户并覆盖页面筛选条件() {
void shouldHandleCase01() {
TableInfoHelper.initTableInfo(
new MapperBuilderAssistant(new MybatisConfiguration(), "merchant-inbound-query-test"),
OrderEntity.class);
OrderMapper mapper = mock(OrderMapper.class);
when(mapper.selectPage(any(Page.class), any(Wrapper.class))).thenReturn(new Page<>());
OrderRepository repository = new OrderRepository(
mapper, mock(OrderCargoMapper.class), mock(OrderPartyMapper.class),
mock(OrderInternalNoteMapper.class), mock(OrderStatusHistoryMapper.class),
mock(OutboxEventMapper.class), mock(OrderChildPurgeMapper.class),
mock(OrderIdempotencyRecordMapper.class));
MerchantInboundOrderQuery query = new MerchantInboundOrderQuery(
"客户", "FL-", 11L, "美森", "13800000000", 30001L, null, OrderStatus.APPROVED,
"FBA", 22L, null, null, 1, 20);
@@ -14,6 +14,9 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -21,17 +24,24 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(OrderRepository.class)
class PlatformOrderPageRepositoryTest {
@MockBean private OrderMapper mapper;
@MockBean private OrderCargoMapper cargoMapper;
@MockBean private OrderPartyMapper partyMapper;
@MockBean private OrderInternalNoteMapper noteMapper;
@MockBean private OrderStatusHistoryMapper historyMapper;
@MockBean private OutboxEventMapper outboxMapper;
@MockBean private OrderChildPurgeMapper purgeMapper;
@MockBean private OrderIdempotencyRecordMapper idempotencyMapper;
@Resource private OrderRepository repository;
private final OrderRequests.QueryRequest query = new OrderRequests.QueryRequest(
null, null, null, null, null, null, 1, 20);
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
void tenantPredicateIsAddedOnlyWhenFilterIsSupplied() {
OrderMapper mapper = mock(OrderMapper.class);
when(mapper.selectPage(any(Page.class), any(Wrapper.class))).thenReturn(new Page<>());
OrderRepository repository = repository(mapper);
repository.page(null, query);
repository.page(20002L, query);
@@ -41,16 +51,4 @@ class PlatformOrderPageRepositoryTest {
assertThat(wrapperCaptor.getAllValues().get(1).getExpression().getNormal()).isNotEmpty();
}
private OrderRepository repository(OrderMapper mapper) {
return new OrderRepository(
mapper,
mock(OrderCargoMapper.class),
mock(OrderPartyMapper.class),
mock(OrderInternalNoteMapper.class),
mock(OrderStatusHistoryMapper.class),
mock(OutboxEventMapper.class),
mock(OrderChildPurgeMapper.class),
mock(OrderIdempotencyRecordMapper.class)
);
}
}
@@ -11,6 +11,10 @@ import com.baiyue.order.entity.OrderStatus;
import com.baiyue.order.entity.OrderStatusHistoryEntity;
import com.baiyue.order.repository.OrderRepository;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.math.BigDecimal;
import java.util.List;
@@ -20,14 +24,15 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(MerchantInboundOrderListService.class)
@TestPropertySource(properties = "baiyue.security.internal-service-token=internal-token")
class MerchantInboundOrderListServiceTest {
private final OrderRepository repository = mock(OrderRepository.class);
private final FirstlegInboundClient firstleg = mock(FirstlegInboundClient.class);
private final MerchantInboundOrderListService service =
new MerchantInboundOrderListService(repository, firstleg, "internal-token");
@MockBean private OrderRepository repository;
@MockBean private FirstlegInboundClient firstleg;
@Resource private MerchantInboundOrderListService service;
@Test
void 聚合客户端列表所需的订单货物收货方和入仓编码() {
void shouldHandleCase01() {
MerchantInboundOrderQuery query = new MerchantInboundOrderQuery(
null, null, null, null, null, null, null, null, null, null, null, null, 1, 20);
OrderEntity order = new OrderEntity();
@@ -12,6 +12,12 @@ import com.baiyue.order.repository.OrderRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.util.ReflectionTestUtils;
import java.math.BigDecimal;
@@ -22,7 +28,18 @@ import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
@DisplayName("订单状态流转")
@SpringJUnitConfig(classes = {OrderApplicationService.class, OrderApplicationServiceStateTest.TestConfig.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class OrderApplicationServiceStateTest {
@MockBean private OrderRepository contextRepository;
@MockBean private OrderEligibilityGuard contextEligibility;
@MockBean private OrderMasterDataGuard contextMasterData;
@Resource private OrderApplicationService springService;
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean ObjectMapper objectMapper() { return new ObjectMapper(); }
}
@Test
void unapprovedTenantCanCreateDraft() {
OrderRepository repository = mock(OrderRepository.class);
@@ -89,7 +106,7 @@ class OrderApplicationServiceStateTest {
OrderEntity order=new OrderEntity(); order.setId(1L); order.setTenantId(10001L); order.setStatus(OrderStatus.SUBMITTED);
when(repository.findOwned(10001,1)).thenReturn(order);
OrderEligibilityGuard eligibility=mock(OrderEligibilityGuard.class);
OrderApplicationService service=new OrderApplicationService();
OrderApplicationService service=springService;
ReflectionTestUtils.setField(service,"repository",repository);
ReflectionTestUtils.setField(service,"eligibilityGuard",eligibility);
ReflectionTestUtils.setField(service,"masterDataGuard",mock(OrderMasterDataGuard.class));
@@ -163,7 +180,7 @@ class OrderApplicationServiceStateTest {
private OrderApplicationService service(OrderRepository repository, OrderEligibilityGuard eligibility,
OrderMasterDataGuard masterData) {
OrderApplicationService service = new OrderApplicationService();
OrderApplicationService service = springService;
ReflectionTestUtils.setField(service, "repository", repository);
ReflectionTestUtils.setField(service, "eligibilityGuard", eligibility);
ReflectionTestUtils.setField(service, "masterDataGuard", masterData);
@@ -11,6 +11,12 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.test.util.ReflectionTestUtils;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;
@@ -27,7 +33,18 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@DisplayName("订单幂等处理")
@SpringJUnitConfig(classes = {OrderApplicationService.class, OrderIdempotencyServiceTest.TestConfig.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class OrderIdempotencyServiceTest {
@MockBean private OrderRepository contextRepository;
@MockBean private OrderEligibilityGuard contextEligibility;
@MockBean private OrderMasterDataGuard contextMasterData;
@Resource private OrderApplicationService springService;
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean ObjectMapper objectMapper() { return new ObjectMapper(); }
}
@Test
void sameKeyAndRequestReturnsFirstCreatedOrderId() {
OrderRepository repository = mock(OrderRepository.class);
@@ -155,7 +172,7 @@ class OrderIdempotencyServiceTest {
}
private OrderApplicationService service(OrderRepository repository) {
OrderApplicationService service = new OrderApplicationService();
OrderApplicationService service = springService;
ReflectionTestUtils.setField(service, "repository", repository);
ReflectionTestUtils.setField(service, "json", new ObjectMapper());
ReflectionTestUtils.setField(service, "eligibilityGuard", mock(OrderEligibilityGuard.class));
@@ -11,6 +11,10 @@ import feign.Request;
import feign.Response;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
@@ -20,36 +24,38 @@ import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
@DisplayName("订单主数据校验")
@SpringJUnitConfig(OrderMasterDataGuard.class)
@TestPropertySource(properties = "baiyue.security.internal-service-token=token")
class OrderMasterDataGuardTest {
@MockBean private WarehouseClient warehouses;
@MockBean private PartnerClient partners;
@Resource private OrderMasterDataGuard guard;
private static final long PUBLIC_TENANT_ID = 10001L;
@Test
void validMasterDataReturnsTrustedServerSnapshot() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
var partner=new PartnerClient.Partner(7L,10001L,"服务端客户名","ACTIVE","SHIPPER",List.of("SHIPPER"));
var address=new WarehouseClient.Address("CN","广东省","深圳市","宝安区","518000","示例路1号",null);
var capability=new WarehouseClient.Capability("RECEIVING",true,java.time.LocalTime.of(16,0));
var wh=new WarehouseClient.Detail(new WarehouseClient.Warehouse(8L,10001L,"CN-SZX-01","深圳仓","DOMESTIC","CN","ACTIVE"),List.of(address),List.of(capability));
when(partners.active("token",10001,7)).thenReturn(ApiResponse.success(partner));
when(warehouses.active("token",10001,8)).thenReturn(ApiResponse.success(wh));
var result=new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L));
var result=guard.validate(10001,request(7L,8L));
assertThat(result.partner().partnerName()).isEqualTo("服务端客户名");
assertThat(result.receiving().warehouse().warehouseName()).isEqualTo("深圳仓");
}
@Test
void inactiveOrCrossTenantMasterDataIsRejected() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10002,7)).thenReturn(ApiResponse.success(null));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10002,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10002,request(7L,8L)))
.isInstanceOf(BizException.class).hasMessageContaining("服务暂时不可用");
}
@Test
void downstreamHttp200BusinessFailureKeepsBusinessCodeAndMessage() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenReturn(
ApiResponse.<PartnerClient.Partner>failure(ResultCode.NOT_FOUND,"往来单位不存在",null,null));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.hasMessage("往来单位不存在")
.extracting(exception -> ((BizException) exception).getResultCode())
@@ -58,24 +64,22 @@ class OrderMasterDataGuardTest {
}
@Test
void masterDataResponseWithNullElementReturnsControlledBusinessError() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
var partner=new PartnerClient.Partner(7L,10001L,"服务端客户名","ACTIVE","SHIPPER",List.of("SHIPPER"));
var wh=new WarehouseClient.Detail(new WarehouseClient.Warehouse(8L,10001L,"CN-SZX-01","深圳仓","DOMESTIC","CN","ACTIVE"),
Collections.singletonList(null), Collections.singletonList(null));
when(partners.active("token",10001,7)).thenReturn(ApiResponse.success(partner));
when(warehouses.active("token",10001,8)).thenReturn(ApiResponse.success(wh));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.VALIDATION_FAILED);
}
@Test
void masterDataResponseMissingSnapshotNameIsServiceUnavailable() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenReturn(ApiResponse.success(new PartnerClient.Partner(7L,10001L,null,"ACTIVE","SHIPPER",List.of("SHIPPER"))));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
@@ -83,30 +87,27 @@ class OrderMasterDataGuardTest {
}
@Test
void downstreamNonFeignRuntimeExceptionMapsToServiceUnavailable() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(new IllegalStateException("解码失败"));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
}
@Test
void downstreamForbiddenMapsToServiceUnavailable() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(feign(403));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
}
@Test
void downstreamHttpNotFoundReturnsUnboundCustomerMessage() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(feign(404));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.hasMessage("\u672a\u7ed1\u5b9a\u53ef\u7528\u5f80\u6765\u5ba2\u6237")
.extracting(exception -> ((BizException) exception).getResultCode())
@@ -114,42 +115,38 @@ class OrderMasterDataGuardTest {
}
@Test
void wrappedDownstreamHttpConflictMapsToServiceUnavailable() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(new IllegalStateException("wrapped", feign(409)));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
}
@Test
void downstreamServerErrorMapsToServiceUnavailable() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenThrow(feign(500));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.SERVICE_UNAVAILABLE);
}
@Test
void inactiveMasterDataInSuccessfulResponseMapsToConflict() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7))
.thenReturn(ApiResponse.success(new PartnerClient.Partner(7L,10001L,"客户","INACTIVE","SHIPPER",List.of("SHIPPER"))));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.CONFLICT);
}
@Test
void partnerWithoutShipperTypeIsRejected() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",10001,7)).thenReturn(ApiResponse.success(
new PartnerClient.Partner(7L,10001L,"收货单位","ACTIVE","CONSIGNEE",List.of("CONSIGNEE"))));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token").validate(10001,request(7L,8L)))
assertThatThrownBy(() -> guard.validate(10001,request(7L,8L)))
.isInstanceOf(BizException.class)
.extracting(exception -> ((BizException) exception).getResultCode())
.isEqualTo(ResultCode.VALIDATION_FAILED);
@@ -157,23 +154,21 @@ class OrderMasterDataGuardTest {
}
@Test
void customerTenantCanUseDomesticWarehouseFromPublicTenant() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",20002,7)).thenReturn(ApiResponse.success(
new PartnerClient.Partner(7L,20002L,"客户","ACTIVE","SHIPPER",List.of("SHIPPER"))));
when(warehouses.active("token",PUBLIC_TENANT_ID,8)).thenReturn(ApiResponse.success(
warehouse(8L,"DOMESTIC",true)));
new OrderMasterDataGuard(warehouses,partners,"token").validate(20002,request(7L,8L));
guard.validate(20002,request(7L,8L));
verify(warehouses).active("token",PUBLIC_TENANT_ID,8);
}
@Test
void orderWithoutPartnerUsesPublicWarehouseWithoutPartnerLookup() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(warehouses.active("token",PUBLIC_TENANT_ID,8)).thenReturn(ApiResponse.success(
warehouse(8L,"DOMESTIC",true)));
var result=new OrderMasterDataGuard(warehouses,partners,"token")
var result=guard
.validate(20002,request(null,8L));
assertThat(result.partner()).isNull();
@@ -182,7 +177,6 @@ class OrderMasterDataGuardTest {
}
@Test
void fbaDestinationMustReferenceMatchingPublicWarehouse() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",20002,7)).thenReturn(ApiResponse.success(
new PartnerClient.Partner(7L,20002L,"客户","ACTIVE","SHIPPER",List.of("SHIPPER"))));
when(warehouses.active("token",PUBLIC_TENANT_ID,8)).thenReturn(ApiResponse.success(
@@ -190,20 +184,19 @@ class OrderMasterDataGuardTest {
when(warehouses.active("token",PUBLIC_TENANT_ID,9)).thenReturn(ApiResponse.success(
warehouse(9L,"OVERSEAS",false)));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token")
assertThatThrownBy(() -> guard
.validate(20002,request(7L,8L,"FBA",9L)))
.isInstanceOf(BizException.class)
.hasMessageContaining("目的仓库类型必须为 FBA");
}
@Test
void privateDestinationCannotReferencePublicWarehouse() {
WarehouseClient warehouses=mock(WarehouseClient.class); PartnerClient partners=mock(PartnerClient.class);
when(partners.active("token",20002,7)).thenReturn(ApiResponse.success(
new PartnerClient.Partner(7L,20002L,"客户","ACTIVE","SHIPPER",List.of("SHIPPER"))));
when(warehouses.active("token",PUBLIC_TENANT_ID,8)).thenReturn(ApiResponse.success(
warehouse(8L,"DOMESTIC",true)));
assertThatThrownBy(() -> new OrderMasterDataGuard(warehouses,partners,"token")
assertThatThrownBy(() -> guard
.validate(20002,request(7L,8L,"PRIVATE",9L)))
.isInstanceOf(BizException.class)
.hasMessageContaining("私人地址不能引用公共仓库");
@@ -3,21 +3,31 @@ package com.baiyue.order.service;
import com.baiyue.order.dto.OrderRequests;
import com.baiyue.order.repository.OrderRepository;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@SpringJUnitConfig(classes = {OrderApplicationService.class, PlatformOrderPageServiceTest.TestConfig.class})
class PlatformOrderPageServiceTest {
@MockBean private OrderRepository repository;
@MockBean private OrderEligibilityGuard eligibilityGuard;
@MockBean private OrderMasterDataGuard masterDataGuard;
@Resource private OrderApplicationService service;
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean ObjectMapper objectMapper() { return new ObjectMapper(); }
}
private final OrderRequests.QueryRequest query = new OrderRequests.QueryRequest(
null, null, null, null, null, null, 1, 20);
@Test
void passesOptionalTenantScopeToRepository() {
OrderRepository repository = mock(OrderRepository.class);
OrderApplicationService service = new OrderApplicationService();
ReflectionTestUtils.setField(service, "repository", repository);
service.page(null, query);
service.page(20002L, query);
@@ -2,7 +2,12 @@ package com.baiyue.order.service;
import com.baiyue.order.repository.OrderRepository;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import jakarta.annotation.Resource;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Map;
@@ -11,14 +16,19 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SpringJUnitConfig(classes = {OrderApplicationService.class, TenantOrderCountServiceTest.TestConfig.class})
class TenantOrderCountServiceTest {
@MockBean private OrderRepository repository;
@MockBean private OrderEligibilityGuard eligibilityGuard;
@MockBean private OrderMasterDataGuard masterDataGuard;
@Resource private OrderApplicationService service;
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean ObjectMapper objectMapper() { return new ObjectMapper(); }
}
@Test
void returnsZeroForTenantsWithoutOrdersAndDeduplicatesRequestedIds() {
OrderRepository repository = mock(OrderRepository.class);
when(repository.countByTenantIds(List.of(8L, 9L, 8L))).thenReturn(Map.of(8L, 12L));
OrderApplicationService service = new OrderApplicationService();
ReflectionTestUtils.setField(service, "repository", repository);
assertThat(service.countByTenantIds(List.of(8L, 9L, 8L)))
.extracting("tenantId", "orderCount")
.containsExactly(