fix: close phase4 overseas acceptance gaps

This commit is contained in:
2026-08-24 21:00:22 +08:00
parent 005c3f54e2
commit 3607bebbfd
42 changed files with 860 additions and 203 deletions
@@ -34,7 +34,7 @@ public class MerchantWarehouseController {
@Operation(
summary = "List available warehouses / 查询可用仓库",
description = "Tenant comes only from the authenticated JWT; clients cannot override it. / 租户仅从认证 JWT 获取,客户端不可覆盖。"
description = "Customer scope comes only from the authenticated JWT; any customerId query value is ignored. / 客户范围仅从认证 JWT 获取,查询中的 customerId 不生效。"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Query succeeded / 查询成功"),
@@ -91,7 +91,8 @@ public class PlatformWarehouseController {
@Operation(
summary = "List warehouses / 查询仓库",
description = "Required authority: " + OverseasWarehousePermissions.WAREHOUSE_LIST + ". Returns all warehouses with optional filters."
description = "Required authority: " + OverseasWarehousePermissions.WAREHOUSE_LIST
+ ". Returns warehouses across customers, or only the optional customerId scope when supplied."
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Query succeeded / 查询成功"),
@@ -8,6 +8,7 @@ import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
@@ -94,6 +95,8 @@ public final class WarehouseRequests {
@Schema(name = "WarehouseQueryRequest", description = "Warehouse page query / 仓库分页查询条件")
public record QueryRequest(
@Schema(description = "Optional target customer ID for platform queries; merchant queries always use the authenticated customer / 平台查询可选目标客户 ID,商户查询始终使用认证客户", example = "10001", nullable = true)
@Positive Long customerId,
@Schema(description = "Page number, starting at 1 / 页码,从 1 开始", example = "1", defaultValue = "1", minimum = "1")
@Min(1) long page,
@Schema(description = "Page size / 每页条数", example = "20", defaultValue = "20", minimum = "1", maximum = "200")
@@ -87,6 +87,7 @@ public class WarehouseRepository {
public PageResult<WarehouseEntity> pageAll(WarehouseRequests.QueryRequest query) {
LambdaQueryWrapper<WarehouseEntity> wrapper = new LambdaQueryWrapper<WarehouseEntity>()
.eq(query.customerId() != null, WarehouseEntity::getCustomerId, query.customerId())
.eq(StringUtils.hasText(query.status()), WarehouseEntity::getStatus, query.status())
.eq(StringUtils.hasText(query.countryCode()), WarehouseEntity::getCountryCode, query.countryCode())
.orderByDesc(WarehouseEntity::getCreatedAt);
@@ -94,6 +95,7 @@ public class WarehouseRepository {
if (StringUtils.hasText(query.capabilityCode())) {
List<Long> ids = capabilities.selectList(new LambdaQueryWrapper<ServiceCapabilityEntity>()
.select(ServiceCapabilityEntity::getWarehouseId)
.eq(query.customerId() != null, ServiceCapabilityEntity::getCustomerId, query.customerId())
.eq(ServiceCapabilityEntity::getCapabilityCode, query.capabilityCode())
.eq(ServiceCapabilityEntity::getEnabled, true))
.stream()
@@ -0,0 +1,36 @@
package com.baiyue.warehouse.controller;
import com.baiyue.common.core.api.PageResult;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.warehouse.dto.WarehouseRequests;
import com.baiyue.warehouse.service.WarehouseApplicationService;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import java.util.Set;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class MerchantWarehouseControllerTest {
@Test
void merchantPageIgnoresRequestedCustomerAndUsesAuthenticatedCustomer() {
WarehouseApplicationService service = mock(WarehouseApplicationService.class);
MerchantWarehouseController controller = new MerchantWarehouseController();
ReflectionTestUtils.setField(controller, "service", service);
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(
99999L, 1, 20, "US", null, "ACTIVE");
when(service.page(10001L, query)).thenReturn(new PageResult<>(List.of(), 1, 20, 0, 0));
controller.page(merchant(10001L), query);
verify(service).page(10001L, query);
}
private static AuthenticatedUser merchant(long customerId) {
return new AuthenticatedUser(1L, customerId, customerId, 1L,
"merchant", "MERCHANT", "WEB", Set.of());
}
}
@@ -5,6 +5,8 @@ import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.warehouse.dto.WarehouseRequests;
import com.baiyue.warehouse.security.OverseasWarehousePermissions;
import com.baiyue.warehouse.service.WarehouseApplicationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import org.junit.jupiter.api.Test;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.test.util.ReflectionTestUtils;
@@ -16,6 +18,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PlatformWarehouseControllerTest {
@Test
@@ -30,11 +33,11 @@ class PlatformWarehouseControllerTest {
}
@Test
void platformPageQueriesAllWarehousesWithoutCustomerId() {
void platformPagePassesOptionalCustomerFilterToApplicationService() {
WarehouseApplicationService service = mock(WarehouseApplicationService.class);
PlatformWarehouseController controller = new PlatformWarehouseController();
ReflectionTestUtils.setField(controller, "service", service);
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(1, 20, "ACTIVE", null, null);
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(10001L, 1, 20, null, null, "ACTIVE");
when(service.pageAll(query)).thenReturn(new PageResult<>(List.of(), 1, 20, 0, 0));
controller.page(platform(), query);
@@ -42,6 +45,22 @@ class PlatformWarehouseControllerTest {
verify(service).pageAll(query);
}
@Test
void platformPageOpenApiExposesOptionalCustomerScope() throws Exception {
var customerId = WarehouseRequests.QueryRequest.class.getRecordComponents()[0];
Schema schema = WarehouseRequests.QueryRequest.class.getDeclaredMethod("customerId")
.getAnnotation(Schema.class);
Operation operation = PlatformWarehouseController.class
.getMethod("page", AuthenticatedUser.class, WarehouseRequests.QueryRequest.class)
.getAnnotation(Operation.class);
assertEquals("customerId", customerId.getName());
assertEquals(Long.class, customerId.getType());
assertTrue(schema.nullable());
assertTrue(schema.description().contains("platform"));
assertTrue(operation.description().contains("customerId"));
}
private static AuthenticatedUser platform() {
return new AuthenticatedUser(1L, 0L, 0L, 1L, "operator", "PLATFORM", "WEB", Set.of());
}
@@ -0,0 +1,42 @@
package com.baiyue.warehouse.repository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baiyue.warehouse.dto.WarehouseRequests;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.mapper.ServiceCapabilityMapper;
import com.baiyue.warehouse.mapper.WarehouseMapper;
import org.junit.jupiter.api.Test;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class WarehouseRepositoryTest {
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
void platformPageAllFiltersByOptionalCustomerId() {
WarehouseMapper warehouses = mock(WarehouseMapper.class);
WarehouseRepository repository = new WarehouseRepository();
ReflectionTestUtils.setField(repository, "warehouses", warehouses);
ReflectionTestUtils.setField(repository, "capabilities", mock(ServiceCapabilityMapper.class));
TableInfoHelper.initTableInfo(
new MapperBuilderAssistant(new MybatisConfiguration(), "warehouse-overseas-query-test"),
WarehouseEntity.class);
when(warehouses.selectPage(any(Page.class), any(LambdaQueryWrapper.class)))
.thenReturn(new Page<WarehouseEntity>(1, 20));
repository.pageAll(new WarehouseRequests.QueryRequest(10001L, 1, 20, "US", null, "ACTIVE"));
var queryCaptor = org.mockito.ArgumentCaptor.forClass(LambdaQueryWrapper.class);
org.mockito.Mockito.verify(warehouses).selectPage(any(Page.class), queryCaptor.capture());
LambdaQueryWrapper<WarehouseEntity> query = queryCaptor.getValue();
assertThat(query.getSqlSegment()).contains("customer_id");
assertThat(query.getParamNameValuePairs()).containsValue(10001L);
}
}
@@ -50,8 +50,8 @@ class WarehouseApplicationServiceTest {
}
@Test
void platformWarehousePageUsesGlobalRepositoryQuery() {
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(1, 20, "ACTIVE", "US", null);
void platformWarehousePagePassesCustomerScopedQueryToRepository() {
WarehouseRequests.QueryRequest query = new WarehouseRequests.QueryRequest(10001L, 1, 20, "US", null, "ACTIVE");
when(repository.pageAll(query)).thenReturn(new com.baiyue.common.core.api.PageResult<>(List.of(), 1, 20, 0, 0));
service.pageAll(query);