This commit is contained in:
2026-08-04 11:14:42 +08:00
parent 0fe129bba7
commit da9aa9515e
14 changed files with 135 additions and 28 deletions
@@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -20,11 +21,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 商户端仓库查询接口,仅允许在认证令牌所属租户范围内读取仓库列表和详情。
* 商户端公共仓库查询接口。认证用户共享平台默认租户维护的仓库配置。
*/
@Tag(
name = "Merchant warehouses / 商户仓库",
description = "Merchant read APIs restricted to the tenant in the authenticated JWT. / 商户端只读接口,租户范围取自认证 JWT。"
description = "Merchant read APIs for shared public warehouses. / 商户端只读公共仓库接口。"
)
@RestController
@RequestMapping("/api/merchant/v1/warehouses")
@@ -32,9 +33,12 @@ public class MerchantWarehouseController {
@Resource
private WarehouseApplicationService service;
@Value("${baiyue.warehouse.public-tenant-id:10001}")
private long publicTenantId;
@Operation(
summary = "List available warehouses / 查询可用仓库",
description = "Tenant comes only from the authenticated JWT; clients cannot override it. / 租户仅从认证 JWT 获取,客户端不可覆盖。"
description = "Returns active public warehouses maintained under the configured public tenant. / 返回平台默认租户维护的可用公共仓库。"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Query succeeded / 查询成功"),
@@ -44,12 +48,12 @@ public class MerchantWarehouseController {
public ApiResponse<PageResult<WarehouseEntity>> page(
@AuthenticationPrincipal AuthenticatedUser user,
@Valid WarehouseRequests.QueryRequest query) {
return ApiResponse.success(service.merchantPage(user.tenantId(), query));
return ApiResponse.success(service.merchantPage(publicTenantId, query));
}
@Operation(
summary = "Get warehouse details / 查询仓库详情",
description = "Returns warehouse details only within the authenticated merchant tenant. / 仅返回当前认证商户租户内的仓库详情。"
description = "Returns a public warehouse maintained under the configured public tenant. / 返回平台默认租户维护的公共仓库详情。"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "Query succeeded / 查询成功"),
@@ -60,6 +64,6 @@ public class MerchantWarehouseController {
@AuthenticationPrincipal AuthenticatedUser user,
@Parameter(description = "Warehouse ID / 仓库 ID", example = "190000000000000001", required = true)
@PathVariable long id) {
return ApiResponse.success(service.activeDetail(user.tenantId(), id));
return ApiResponse.success(service.activeDetail(publicTenantId, id));
}
}
@@ -29,8 +29,8 @@ public final class WarehouseRequests {
@NotBlank @Size(max = 50) String warehouseCode,
@Schema(description = "Warehouse name / 仓库名称", example = "Los Angeles Main Warehouse / 洛杉矶主仓", maxLength = 120, requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank @Size(max = 120) String warehouseName,
@Schema(description = "Warehouse type / 仓库类型", example = "OVERSEAS", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank String warehouseType,
@Schema(description = "Warehouse type / 仓库类型", example = "OVERSEAS", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS"}, requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank @Pattern(regexp = "DOMESTIC|FBA|OVERSEAS") String warehouseType,
@Schema(description = "ISO 3166-1 alpha-2 country code / ISO 两位国家代码", example = "US", pattern = "[A-Z]{2}", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank @Pattern(regexp = "[A-Z]{2}") String countryCode,
@Schema(description = "IANA timezone / IANA 时区", example = "America/Los_Angeles", requiredMode = Schema.RequiredMode.REQUIRED)
@@ -102,6 +102,8 @@ public final class WarehouseRequests {
String countryCode,
@Schema(description = "Enabled capability code filter / 已启用能力编码筛选", example = "STORAGE")
String capabilityCode,
@Schema(description = "Warehouse type filter / 仓库类型筛选", example = "DOMESTIC", allowableValues = {"DOMESTIC", "FBA", "OVERSEAS"})
@Pattern(regexp = "DOMESTIC|FBA|OVERSEAS") String warehouseType,
@Schema(description = "Status filter / 状态筛选", example = "ACTIVE", allowableValues = {"ACTIVE", "INACTIVE"})
String status
) {
@@ -64,6 +64,7 @@ public class WarehouseRepository {
LambdaQueryWrapper<WarehouseEntity> wrapper = new LambdaQueryWrapper<WarehouseEntity>()
.eq(WarehouseEntity::getTenantId, tenantId)
.eq(StringUtils.hasText(query.status()), WarehouseEntity::getStatus, query.status())
.eq(StringUtils.hasText(query.warehouseType()), WarehouseEntity::getWarehouseType, query.warehouseType())
.eq(StringUtils.hasText(query.countryCode()), WarehouseEntity::getCountryCode, query.countryCode())
.orderByDesc(WarehouseEntity::getCreatedAt);
@@ -87,7 +88,7 @@ public class WarehouseRepository {
public PageResult<WarehouseEntity> activePage(long tenantId, WarehouseRequests.QueryRequest query) {
return page(tenantId, new WarehouseRequests.QueryRequest(query.page(), query.size(),
query.countryCode(), query.capabilityCode(), "ACTIVE"));
query.countryCode(), query.capabilityCode(), query.warehouseType(), "ACTIVE"));
}
public List<WarehouseAddressEntity> findAddresses(long tenantId, long warehouseId) {
@@ -126,6 +126,9 @@ SET @schema_name = DATABASE();
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_warehouse' AND index_name = 'idx_wh_tenant_status') = 0,
'ALTER TABLE wh_warehouse ADD KEY idx_wh_tenant_status (tenant_id, status, deleted)', 'SELECT 1');
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_warehouse' AND index_name = 'idx_wh_tenant_type_status') = 0,
'ALTER TABLE wh_warehouse ADD KEY idx_wh_tenant_type_status (tenant_id, warehouse_type, status, deleted)', 'SELECT 1');
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
SET @sql = IF((SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = @schema_name AND table_name = 'wh_warehouse_address' AND index_name = 'idx_address_warehouse') = 0,
'ALTER TABLE wh_warehouse_address ADD KEY idx_address_warehouse (tenant_id, warehouse_id, deleted)', 'SELECT 1');
PREPARE statement FROM @sql; EXECUTE statement; DEALLOCATE PREPARE statement;
@@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS wh_warehouse (
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '仓库所属租户ID',
warehouse_code VARCHAR(50) NOT NULL COMMENT '租户内唯一仓库编码',
warehouse_name VARCHAR(120) NOT NULL COMMENT '仓库名称',
warehouse_type VARCHAR(32) NOT NULL DEFAULT 'OVERSEAS' COMMENT '仓库类型',
warehouse_type VARCHAR(32) NOT NULL DEFAULT 'DOMESTIC' COMMENT '公共仓库类型:DOMESTIC、FBA、OVERSEAS',
country_code CHAR(2) NOT NULL COMMENT '所在国家二字码',
timezone VARCHAR(64) NOT NULL COMMENT '仓库所在地IANA时区',
contact_name VARCHAR(64) DEFAULT NULL COMMENT '仓库联系人',
@@ -25,6 +25,7 @@ CREATE TABLE IF NOT EXISTS wh_warehouse (
PRIMARY KEY (id),
UNIQUE KEY uk_wh_tenant_code (tenant_id, warehouse_code, deleted),
KEY idx_wh_tenant_status (tenant_id, status, deleted),
KEY idx_wh_tenant_type_status (tenant_id, warehouse_type, status, deleted),
KEY idx_wh_country (tenant_id, country_code, deleted)
) ENGINE=InnoDB COMMENT='仓库主数据表:以租户为边界维护仓库编码、名称、国家、时区和启停状态';