This commit is contained in:
2026-08-26 16:50:22 +08:00
parent fd1a81594a
commit 6c4543c18c
25 changed files with 1704 additions and 9 deletions
@@ -47,6 +47,14 @@ public class ReceivingContainerController {
return ApiResponse.success(service.candidates(identity.operatorKey(), identity.overseasWarehouseId()));
}
@GetMapping("/warehouse-profile")
@Operation(summary = "Get the authenticated warehouse picker's current Chinese warehouse master name")
public ApiResponse<ReceivingContainerResponses.WarehouseProfile> warehouseProfile(
@AuthenticationPrincipal AuthenticatedUser user) {
PdaWarehouseIdentity identity = identity(user);
return ApiResponse.success(service.warehouseProfile(identity.overseasWarehouseId()));
}
@GetMapping("/{containerNo}")
@Operation(summary = "Get one receiving container without exposing plan or allocation IDs")
public ApiResponse<ReceivingContainerResponses.Session> detail(@AuthenticationPrincipal AuthenticatedUser user,
@@ -10,6 +10,8 @@ public final class ReceivingContainerResponses {
List<String> destinationCodes, int plannedPalletCount,
int totalPackages, int receivedPackages, boolean currentAccount) { }
public record WarehouseProfile(String warehouseId, String warehouseCode, String warehouseName) { }
public record Session(String containerNo, Long overseasWarehouseId, String overseasWarehouseName,
String status, String operatorName, int version,
List<SignedPhoto> photos, LocalDateTime preparedAt, LocalDateTime signedAt,
@@ -87,6 +87,12 @@ public class ReceivingContainerService {
.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
}
public ReceivingContainerResponses.WarehouseProfile warehouseProfile(long overseasWarehouseId) {
WarehouseResourceGateway.WarehouseReference value = warehouse.requireActiveSharedWarehouse(overseasWarehouseId);
return new ReceivingContainerResponses.WarehouseProfile(
Long.toString(value.warehouseId()), value.warehouseCode(), value.warehouseName());
}
public List<ReceivingContainerResponses.Candidate> candidates(String operatorKey, long overseasWarehouseId) {
ReceivingContainerSession current = sessions.findActiveByOperator(activeOperatorKey(operatorKey));
requireSessionWarehouse(current, overseasWarehouseId);
@@ -9,6 +9,12 @@ paths:
description: 权限 lastleg:pda:operate;仅 PLATFORM WAREHOUSE_PICKER 可用;仅返回已单调消费 PICKUP_READY(80) 或更高实际船节点、状态为 RECEIVING_AVAILABLE 的柜,不接收 customerId。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 柜号候选及箱数进度, content: { application/json: { schema: { $ref: '#/components/schemas/ReceivingCandidateListApiResponse' } } } }, '403': { description: 缺少 PDA 操作权限 } }
/api/pda/v1/last-leg/receiving-containers/warehouse-profile:
get:
summary: 查询当前PDA账号绑定的海外仓主档
description: 权限 lastleg:pda:operate;仓库ID只取Token,名称与编码由共享仓库主档实时解析,不接收warehouseId参数。
security: [{ bearerAuth: [] }]
responses: { '200': { description: 当前绑定仓库中文名称与编码, content: { application/json: { schema: { $ref: '#/components/schemas/ReceivingWarehouseProfileApiResponse' } } } }, '403': { description: 账号未绑定有效海外仓 } }
/api/pda/v1/last-leg/receiving-containers/{containerNo}:
get:
summary: 查询待签收柜号详情
@@ -691,6 +697,13 @@ components:
totalPackages: { type: integer }
receivedPackages: { type: integer }
pendingPackages: { type: integer }
ReceivingWarehouseProfile:
type: object
required: [warehouseId, warehouseCode, warehouseName]
properties:
warehouseId: { type: string, pattern: '^\d+$' }
warehouseCode: { type: string }
warehouseName: { type: string, description: 国内Warehouse Service主档维护的当前中文名称 }
ReceivingCandidate:
type: object
required: [containerNo, status, planCount, inboundCodeCount, destinationCodes, plannedPalletCount, totalPackages, receivedPackages, currentAccount]
@@ -777,6 +790,12 @@ components:
- type: object
required: [data]
properties: { data: { type: array, items: { $ref: '#/components/schemas/ReceivingCandidate' } } }
ReceivingWarehouseProfileApiResponse:
allOf:
- { $ref: '#/components/schemas/ReceivingApiResponseBase' }
- type: object
required: [data]
properties: { data: { $ref: '#/components/schemas/ReceivingWarehouseProfile' } }
ReceivingSessionApiResponse:
allOf:
- { $ref: '#/components/schemas/ReceivingApiResponseBase' }
@@ -124,6 +124,7 @@ class ReceivingContainerContractTest {
String yaml = resource("openapi/overseas-business-v1.yaml");
String prefix = "/api/pda/v1/last-leg/receiving-containers";
assertTrue(yaml.contains(prefix + "/candidates:"));
assertTrue(yaml.contains(prefix + "/warehouse-profile:"));
assertTrue(yaml.contains(prefix + "/current/receipts/bulk:"));
assertTrue(yaml.contains(prefix + "/current/skus/resolve:"));
assertTrue(yaml.contains(prefix + "/current/receipts/carton:"));
@@ -133,8 +134,8 @@ class ReceivingContainerContractTest {
String receivingPaths = yaml.substring(yaml.indexOf(prefix), yaml.indexOf("/api/platform/v1/last-leg/orders:"));
List<String> successResponses = receivingPaths.lines().map(String::trim)
.filter(line -> line.startsWith("responses: { '200':")).toList();
assertEquals(16, successResponses.size());
assertEquals(12, successResponses.stream().filter(line -> line.contains("/schemas/Receiving")).count());
assertEquals(17, successResponses.size());
assertEquals(13, successResponses.stream().filter(line -> line.contains("/schemas/Receiving")).count());
assertEquals(4, successResponses.stream().filter(line -> line.contains("/schemas/Pda")).count());
assertTrue(receivingPaths.contains("返回 WAIT_SIGN、SIGNED 或 RECEIVING 当前柜"));
assertTrue(receivingPaths.contains("仅 PLATFORM WAREHOUSE_PICKER 可用"));
@@ -45,6 +45,17 @@ class ReceivingContainerControllerTest {
verify(service).candidates("42", 900L);
}
@Test
void warehouseProfileUsesOnlyTheTokenBoundWarehouse() {
when(service.warehouseProfile(900L)).thenReturn(
new ReceivingContainerResponses.WarehouseProfile("900", "US-LAX-01", "洛杉矶一号仓"));
var response = controller.warehouseProfile(picker());
assertThat(response.data().warehouseName()).isEqualTo("洛杉矶一号仓");
verify(service).warehouseProfile(900L);
}
@Test
void merchantIdentityCannotEnterPdaReceivingApiEvenWithForgedAuthority() {
AuthenticatedUser merchant = new AuthenticatedUser(7L, 100L, 100L, 8L, "merchant", "MERCHANT", "WEB",