This commit is contained in:
2026-09-18 09:59:04 +08:00
106 changed files with 5113 additions and 471 deletions
@@ -7,14 +7,17 @@ import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.dto.WarehousePalletResolveResponse;
import com.baiyue.warehouse.security.InternalServiceTokenGuard;
import com.baiyue.warehouse.service.WarehouseResourceService;
import com.baiyue.warehouse.service.PalletLabelPrintService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/internal/v1/warehouse-pallets")
public class InternalWarehousePalletController {
@Resource private WarehouseResourceService service;
@Resource private PalletLabelPrintService palletLabels;
@Resource private InternalServiceTokenGuard tokenGuard;
@PostMapping("/ensure-dropship")
@@ -49,4 +52,14 @@ public class InternalWarehousePalletController {
return ApiResponse.success(service.resolvePublicPallet(palletId));
}
@GetMapping("/{warehouseId}/{palletId}/label")
public ResponseEntity<byte[]> label(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@RequestHeader(value = "X-Request-Id", required = false) String requestId,
@PathVariable long warehouseId, @PathVariable long palletId,
@RequestParam String transferNo) {
tokenGuard.verify(token);
return palletLabels.printTransfer(warehouseId, palletId, transferNo, requestId);
}
}
@@ -25,6 +25,16 @@ public class PalletLabelPrintService {
private String internalToken;
public ResponseEntity<byte[]> print(long warehouseId, long palletId, String requestId) {
return print(warehouseId, palletId, requestId, "PALLET");
}
public ResponseEntity<byte[]> printTransfer(long warehouseId, long palletId, String transferNo,
String requestId) {
String reference = transferNo == null || transferNo.isBlank() ? "转运单" : "转运单 " + transferNo.trim();
return print(warehouseId, palletId, requestId, reference);
}
private ResponseEntity<byte[]> print(long warehouseId, long palletId, String requestId, String reference) {
var pallet = resources.pallet(WarehouseResourceService.PUBLIC_RESOURCE_SCOPE, warehouseId, palletId);
String code = pallet.getPalletCode();
String fbaCode = pallet.getFbaWarehouseCode();
@@ -37,8 +47,9 @@ public class PalletLabelPrintService {
}
var warehouse = warehouses.required(warehouseId);
String fileName = code.replaceAll("[\\\\/:*?\"<>|]", "-") + "-托盘码.pdf";
var label = new PrintPdfClient.Label(code, fbaCode == null || fbaCode.isBlank() ? "—" : fbaCode,
"PALLET", warehouse.warehouseName(), code);
var label = new PrintPdfClient.Label(code,
fbaCode == null || fbaCode.isBlank() ? "目的仓待定" : fbaCode,
reference, warehouse.warehouseName(), code);
var request = new PrintPdfClient.Request("INBOUND_MARK_V1",
new PrintPdfClient.PageSpec(new BigDecimal("100"), new BigDecimal("100")), fileName, List.of(label));
try {
@@ -386,9 +386,11 @@ public class WarehouseResourceService {
}
private static String generatedPalletCode(String warehouseCode, long palletId) {
String boundedWarehouseCode = warehouseCode.length() <= 30 ? warehouseCode : warehouseCode.substring(0, 30);
return "PLT-" + boundedWarehouseCode + "-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
+ "-" + Long.toUnsignedString(palletId);
String compactWarehouseCode = warehouseCode.replaceAll("[^A-Za-z0-9]", "").toUpperCase(java.util.Locale.ROOT);
if (compactWarehouseCode.length() > 9) compactWarehouseCode = compactWarehouseCode.substring(0, 9);
return "PLT-" + compactWarehouseCode + "-"
+ LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd")) + "-"
+ Long.toUnsignedString(palletId, 36).toUpperCase(java.util.Locale.ROOT);
}
private static String defaultLocationCode(String warehouseCode) {
@@ -0,0 +1,20 @@
package com.baiyue.warehouse.controller;
import org.junit.jupiter.api.Test;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import static org.assertj.core.api.Assertions.assertThat;
class InternalWarehousePalletLabelContractTest {
@Test
void labelEndpointIsInternalAndRequiresTheServiceToken() throws Exception {
var method = InternalWarehousePalletController.class.getMethod("label",
String.class, String.class, long.class, long.class, String.class);
assertThat(method.getAnnotation(GetMapping.class).value())
.containsExactly("/{warehouseId}/{palletId}/label");
assertThat(method.getParameters()[0].getAnnotation(RequestHeader.class).value())
.isEqualTo("X-Internal-Service-Token");
}
}
@@ -59,6 +59,26 @@ class PalletLabelPrintServiceTest {
verify(print, never()).generateInboundMarks(any(), any(), any());
}
@Test
void transferLabelIncludesTransferNumberAndMakesAnUnknownDestinationExplicit() {
pallet.setPalletCode("ABCD1234567-ABE3-003");
pallet.setFbaWarehouseCode(null);
byte[] pdf = "%PDF-transfer-label".getBytes(java.nio.charset.StandardCharsets.US_ASCII);
when(print.generateInboundMarks(anyString(), anyString(), any()))
.thenReturn(ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(pdf));
service.printTransfer(200L, 9L, "WTR-20260916-1", "request-transfer");
var request = ArgumentCaptor.forClass(PrintPdfClient.Request.class);
verify(print).generateInboundMarks(eq("test-token"), eq("request-transfer"), request.capture());
assertThat(request.getValue().labels()).singleElement().satisfies(label -> {
assertThat(label.topCode()).isEqualTo("ABCD1234567-ABE3-003");
assertThat(label.cartonText()).isEqualTo("转运单 WTR-20260916-1");
assertThat(label.warehouseText()).isEqualTo("目的仓待定");
assertThat(label.routeText()).isEqualTo("洛杉矶仓");
});
}
@Test
void refusesWrongWarehouseBeforeCallingPrint() {
when(resources.pallet(0L, 201L, 9L)).thenThrow(new BizException(ResultCode.NOT_FOUND, "托盘不存在"));
@@ -262,7 +262,8 @@ class WarehouseResourceServiceTest {
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
verify(repository).insertPallet(captor.capture());
assertThat(captor.getValue().getPalletCode()).matches("PLT-US-LAX-01-[0-9]{8}-[0-9]+");
assertThat(captor.getValue().getPalletCode()).matches("PLT-USLAX01-[0-9]{6}-[0-9A-Z]+");
assertThat(captor.getValue().getPalletCode()).hasSizeLessThanOrEqualTo(34);
assertThat(captor.getValue().getLocationId()).isEqualTo(30003L);
assertThat(captor.getValue().getId()).isNotNull();
}
@@ -283,7 +284,7 @@ class WarehouseResourceServiceTest {
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
verify(repository).insertPallet(captor.capture());
assertThat(captor.getValue().getPalletCode()).hasSizeLessThanOrEqualTo(64);
assertThat(captor.getValue().getPalletCode()).hasSizeLessThanOrEqualTo(34);
}
@Test