This commit is contained in:
2026-08-06 18:01:59 +08:00
parent 584f1519d1
commit 6e9739826c
24 changed files with 549 additions and 62 deletions
@@ -13,20 +13,26 @@ import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("FBA计费OpenAPI契约")
class FbaPricingContractTest {
@Test
@DisplayName("只有基础资料列表允许省略企业参数")
void onlyMasterDataListsAllowOptionalTenant() {
for (String listMethod : new String[] {"rules", "rates"}) {
Method method = method(listMethod);
assertThat(method.getParameterTypes()[1]).isEqualTo(Long.class);
assertThat(method.getParameters()[1].getAnnotation(RequestParam.class).required()).isFalse();
}
@DisplayName("客户规则保留客户参数,船司价格使用平台公共作用域")
void keepsCustomerRuleScopeAndRemovesCarrierRateTenantScope() {
Method rules = method("rules");
assertThat(rules.getParameterTypes()[1]).isEqualTo(Long.class);
assertThat(rules.getParameters()[1].getAnnotation(RequestParam.class).required()).isFalse();
for (String scopedMethod : new String[] {"rule", "createRule", "retireRule",
"rate", "createRate", "retireRate"}) {
Method rates = method("rates");
assertThat(rates.getParameterTypes()).doesNotContain(Long.class);
for (String scopedMethod : new String[] {"rule", "createRule", "retireRule"}) {
Method method = method(scopedMethod);
assertThat(method.getParameterTypes()[1]).isEqualTo(long.class);
assertThat(method.getParameters()[1].getAnnotation(RequestParam.class).required()).isTrue();
}
for (String publicMethod : new String[] {"rate", "createRate", "retireRate"}) {
assertThat(java.util.Arrays.stream(method(publicMethod).getParameters())
.map(parameter -> parameter.getAnnotation(RequestParam.class)))
.allMatch(annotation -> annotation == null);
}
}
@Test
@@ -62,7 +68,7 @@ class FbaPricingContractTest {
openapi.indexOf("/api/platform/v1/fba-pricing/carrier-rates:"),
openapi.indexOf("/api/platform/v1/fba-pricing/carrier-rates/versions:"));
assertThat(carrierRates)
.contains("OptionalTenantId")
.doesNotContain("OptionalTenantId")
.contains("name: rateCode");
assertThat(openapi)
.contains("OptionalTenantId: {name: tenantId, in: query, required: false")
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baiyue.firstleg.entity.CustomerChargeRuleEntity;
import com.baiyue.firstleg.entity.CarrierFbaRateEntity;
import com.baiyue.firstleg.entity.InboundPricingIdempotencyEntity;
import com.baiyue.firstleg.entity.InboundPricingEntity;
import com.baiyue.firstleg.mapper.CarrierFbaRateMapper;
import com.baiyue.firstleg.mapper.CustomerChargeRuleMapper;
import com.baiyue.firstleg.mapper.InboundFeeItemMapper;
@@ -48,6 +49,8 @@ class FbaPricingRepositoryRuleListTest {
CustomerChargeRuleEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "test"),
CarrierFbaRateEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new Configuration(), "test"),
InboundPricingEntity.class);
}
@Test
@@ -61,14 +64,52 @@ class FbaPricingRepositoryRuleListTest {
}
@Test
void platformRateListOmitsTenantConditionWhenTenantIsAbsent() {
assertThat(rateQueryValues(null, null)).isEmpty();
void platformRateListAlwaysUsesPublicScope() {
assertThat(rateQueryValues(null)).containsExactly(CarrierFbaRateEntity.PLATFORM_SCOPE);
}
@Test
void platformRateListKeepsTenantAndRateCodeFiltersWhenProvided() {
assertThat(rateQueryValues(10001L, "RATE-1"))
.containsExactlyInAnyOrder(10001L, "RATE-1");
void platformRateListKeepsPublicScopeAndRateCodeFiltersWhenProvided() {
assertThat(rateQueryValues("RATE-1"))
.containsExactlyInAnyOrder(CarrierFbaRateEntity.PLATFORM_SCOPE, "RATE-1");
}
@Test
void platformRateCreateQueryLocksPublicScope() {
when(rates.selectList(org.mockito.ArgumentMatchers.any())).thenReturn(List.of());
repository.listRatesForUpdate(null);
@SuppressWarnings("unchecked")
ArgumentCaptor<LambdaQueryWrapper<CarrierFbaRateEntity>> wrapper =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
verify(rates).selectList(wrapper.capture());
assertThat(wrapper.getValue().getCustomSqlSegment()).contains("FOR UPDATE");
assertThat(wrapper.getValue().getParamNameValuePairs().values())
.containsExactly(CarrierFbaRateEntity.PLATFORM_SCOPE);
}
@Test
void carrierRateMatchingAlwaysUsesPlatformScope() {
InboundPricingEntity inbound = new InboundPricingEntity();
inbound.setTenantId(10001L);
inbound.setWarehouseId(21L);
inbound.setBusinessType("FBA_SEA");
inbound.setChannelCode("SEA-TAX");
inbound.setCarrierId(31L);
inbound.setDestinationCode("ONT8");
when(rates.selectList(org.mockito.ArgumentMatchers.any())).thenReturn(List.of());
repository.matchRates(inbound, "KG", java.time.LocalDateTime.of(2026, 8, 1, 0, 0));
@SuppressWarnings("unchecked")
ArgumentCaptor<LambdaQueryWrapper<CarrierFbaRateEntity>> wrapper =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
verify(rates).selectList(wrapper.capture());
wrapper.getValue().getSqlSegment();
assertThat(wrapper.getValue().getParamNameValuePairs().values())
.contains(CarrierFbaRateEntity.PLATFORM_SCOPE)
.doesNotContain(10001L);
}
@Test
@@ -122,10 +163,10 @@ class FbaPricingRepositoryRuleListTest {
return wrapper.getValue().getParamNameValuePairs().values();
}
private Collection<Object> rateQueryValues(Long tenantId, String rateCode) {
private Collection<Object> rateQueryValues(String rateCode) {
when(rates.selectList(org.mockito.ArgumentMatchers.any())).thenReturn(List.of());
repository.listRates(tenantId, rateCode);
repository.listRates(rateCode);
@SuppressWarnings("unchecked")
ArgumentCaptor<LambdaQueryWrapper<CarrierFbaRateEntity>> wrapper =
@@ -38,4 +38,18 @@ class InboundWorkflowSqlContractTest {
.contains("SET warehouse_resource_tenant_id = 0")
.contains("MODIFY COLUMN warehouse_resource_tenant_id BIGINT UNSIGNED NOT NULL");
}
@Test
@DisplayName("船司价格存量迁移固定到平台作用域并拒绝冲突")
void shouldMigrateCarrierRatesToPublicScopeWithoutOverwritingConflicts() throws Exception {
String migration = new ClassPathResource("db/phase2-fba-pricing-migration.sql")
.getContentAsString(StandardCharsets.UTF_8);
assertThat(migration)
.contains("phase2_fba_migrate_carrier_rate_scope")
.contains("GROUP BY rate_code, rate_version")
.contains("公共匹配维度存在有效期重叠")
.contains("SET tenant_id = 0")
.contains("WHERE tenant_id <> 0");
}
}
@@ -2,6 +2,8 @@ package com.baiyue.firstleg.service;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.dto.FbaPricingDtos.CustomerRuleVersionRequest;
import com.baiyue.firstleg.dto.FbaPricingDtos.CarrierRateVersionRequest;
import com.baiyue.firstleg.entity.CarrierFbaRateEntity;
import com.baiyue.firstleg.entity.CustomerChargeRuleEntity;
import com.baiyue.firstleg.repository.FbaPricingRepository;
import jakarta.annotation.Resource;
@@ -73,11 +75,33 @@ class FbaMasterDataServiceTest {
.hasMessageContaining("有效期重叠");
}
@Test
@DisplayName("船司价格版本固定写入平台公共作用域")
void createsCarrierRateInPlatformScope() {
when(repository.listRatesForUpdate(null)).thenReturn(List.of());
when(repository.nextRateVersion("RATE-1")).thenReturn(1);
doAnswer(invocation -> { ((CarrierFbaRateEntity) invocation.getArgument(0)).setId(20L); return null; })
.when(repository).insertRate(any());
CarrierFbaRateEntity created = service.createRateVersion(2L, carrierRateRequest());
assertThat(created.getTenantId()).isEqualTo(CarrierFbaRateEntity.PLATFORM_SCOPE);
verify(repository).listRatesForUpdate(null);
verify(repository).nextRateVersion("RATE-1");
verify(repository).insertRate(any());
}
private CustomerRuleVersionRequest request(LocalDateTime from, LocalDateTime to) {
return new CustomerRuleVersionRequest("CR-1", 100L, "客户A", "FBA_SEA", "KG",
"TAX_INCLUDED", new BigDecimal("167"), 3, "CEILING", "USD", from, to, null);
}
private CarrierRateVersionRequest carrierRateRequest() {
return new CarrierRateVersionRequest("RATE-1", 21L, "义乌仓", "FBA_SEA", "SEA-TAX",
31L, "船司A", "ONT8", "KG", new BigDecimal("2.50"), "USD", new BigDecimal("10"),
AUGUST, null, null);
}
private CustomerChargeRuleEntity activeRule(LocalDateTime from, LocalDateTime to) {
CustomerChargeRuleEntity value = new CustomerChargeRuleEntity();
value.setRuleCode("CR-1"); value.setRuleVersion(1); value.setCustomerId(100L);