feat(warehouse): provide bounded internal historical metadata batch

This commit is contained in:
2026-10-10 11:52:29 +08:00
parent 46ae8ab1c6
commit 83049ed9e5
10 changed files with 177 additions and 2 deletions
@@ -28,6 +28,14 @@ public class InternalWarehouseController {
private String internalToken;
private byte[] expected;
@PostMapping("/platform-metadata/batch")
public ApiResponse<com.baiyue.warehouse.dto.WarehouseMetadataBatch.Response> platformMetadata(
@RequestHeader(value="X-Internal-Service-Token",required=false) String token,
@jakarta.validation.Valid @RequestBody com.baiyue.warehouse.dto.WarehouseMetadataBatch.Request request) {
verifyToken(token);
return ApiResponse.success(service.platformMetadata(request.warehouseIds()));
}
@PostConstruct
void initializeToken() {
this.expected = internalToken.getBytes(StandardCharsets.UTF_8);
@@ -81,7 +89,7 @@ public class InternalWarehouseController {
private void verifyToken(String token) {
byte[] supplied = token == null ? new byte[0] : token.getBytes(StandardCharsets.UTF_8);
if (!MessageDigest.isEqual(expected, supplied)) {
if (expected == null || expected.length == 0 || !MessageDigest.isEqual(expected, supplied)) {
throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效");
}
}
@@ -0,0 +1,13 @@
package com.baiyue.warehouse.dto;
import jakarta.validation.constraints.*;
import java.util.List;
/** Minimal public warehouse facts for internal historical read projections. */
public final class WarehouseMetadataBatch {
private WarehouseMetadataBatch() { }
public record Request(@NotEmpty @Size(max=2000) List<@NotNull @Positive Long> warehouseIds) { }
public record Item(Long id,String warehouseCode,String warehouseName,String warehouseType,
String countryCode,String timezone,String status) { }
public record Response(List<Item> records,List<Long> missingIds) { }
}
@@ -34,7 +34,7 @@ public class WarehouseEntity extends BaseEntity {
private String warehouseType;
@Schema(description = "ISO two-letter country code / ISO 两位国家代码", example = "US")
private String countryCode;
@Schema(description = "UTC offset / UTC 偏移量", example = "UTC-8")
@Schema(description = "IANA location time zone / 地点时区,未核实为空", example = "America/Los_Angeles")
private String timezone;
@Schema(description = "Contact name / 联系人", example = "Alex Chen")
private String contactName;
@@ -46,6 +46,14 @@ public class WarehouseRepository {
.eq(WarehouseEntity::getId, id));
}
public List<WarehouseEntity> platformMetadata(List<Long> ids) {
return warehouses.selectList(new LambdaQueryWrapper<WarehouseEntity>()
.select(WarehouseEntity::getId,WarehouseEntity::getTenantId,WarehouseEntity::getWarehouseCode,
WarehouseEntity::getWarehouseName,WarehouseEntity::getWarehouseType,WarehouseEntity::getCountryCode,
WarehouseEntity::getTimezone,WarehouseEntity::getStatus)
.eq(WarehouseEntity::getTenantId,0L).in(WarehouseEntity::getId,ids));
}
public WarehouseEntity findByCode(long tenantId, String code) {
return warehouses.selectOne(new LambdaQueryWrapper<WarehouseEntity>()
.eq(WarehouseEntity::getTenantId, tenantId)
@@ -28,6 +28,26 @@ public class WarehouseApplicationService {
@Resource
private WarehouseRepository repository;
@Transactional(readOnly=true)
public com.baiyue.warehouse.dto.WarehouseMetadataBatch.Response platformMetadata(List<Long> ids) {
if(ids==null || ids.isEmpty() || ids.size()>2000 || ids.stream().anyMatch(id->id==null || id<=0)
|| new java.util.HashSet<>(ids).size()!=ids.size())
throw new BizException(ResultCode.VALIDATION_FAILED,"每次需提供1至2000个不重复的有效仓库标识");
var byId=new java.util.HashMap<Long,WarehouseEntity>();
for(var row:repository.platformMetadata(ids)) {
if(Long.valueOf(0L).equals(row.getTenantId()) && ids.contains(row.getId())) byId.put(row.getId(),row);
}
var records=new java.util.ArrayList<com.baiyue.warehouse.dto.WarehouseMetadataBatch.Item>();
var missing=new java.util.ArrayList<Long>();
for(var id:ids) {
var row=byId.get(id);
if(row==null) missing.add(id);
else records.add(new com.baiyue.warehouse.dto.WarehouseMetadataBatch.Item(row.getId(),row.getWarehouseCode(),
row.getWarehouseName(),row.getWarehouseType(),row.getCountryCode(),row.getTimezone(),row.getStatus()));
}
return new com.baiyue.warehouse.dto.WarehouseMetadataBatch.Response(List.copyOf(records),List.copyOf(missing));
}
@Transactional
@BusinessAudit(
@@ -3,6 +3,49 @@ info:
title: Warehouse catalog and receiving options
version: 1.0.0
paths:
/internal/warehouses/platform-metadata/batch:
post:
summary: 内部批量读取公共仓库历史展示资料
description: 只读;必须验证X-Internal-Service-Token,不经公网Gateway。固定tenant_id=0,包含ACTIVE和INACTIVE,已删除、非公共和不存在的ID统一返回missingIds。不返回联系人、电话、地址或客户资料。原timezone保留原值/空值,不猜测或修复;消费者必须自行核对地点时区和国家。每批最多2000个不重复正数ID,一次SQL查询。
parameters:
- {name: X-Internal-Service-Token, in: header, required: true, schema: {type: string}}
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [warehouseIds]
properties:
warehouseIds: {type: array, minItems: 1, maxItems: 2000, uniqueItems: true, items: {type: string, pattern: '^[1-9][0-9]*$'}}
responses:
'200':
description: 成功业务响应code为200,按请求顺序返回可读记录及缺失ID
content:
application/json:
schema:
type: object
properties:
code: {type: string}
data:
type: object
required: [records, missingIds]
properties:
missingIds: {type: array, items: {type: string}}
records:
type: array
items:
type: object
properties:
id: {type: string}
warehouseCode: {type: string}
warehouseName: {type: string}
warehouseType: {type: string}
countryCode: {type: string, nullable: true}
timezone: {type: string, nullable: true}
status: {type: string}
'403': {description: 内部服务凭据错误或缺失}
'422': {description: ID为空、非正数、重复或超过2000个}
/api/platform/v1/warehouse-admin/overseas-receiving-options:
get:
summary: 分页查询启用的公共海外收货仓