优化
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
| 托盘解析与业务绑定 | `controller/InternalWarehousePalletController.java`、`dto/WarehousePalletResolveResponse.java`、`entity/WarehousePalletBindingEntity.java` |
|
||||
| 柜级入库计划批量托盘 | `controller/InternalWarehousePalletBatchController.java`、`service/WarehousePalletBatchService.java`、`warehouse-pallet-batch-internal-v1.yaml`;以稳定批次键幂等执行 `ensure-reserved → confirm`,权威托盘码已存在且资源可预留时必须复用,未提供或不存在时才创建;Lastleg 本地计划事务失败时仅补偿仍为 `RESERVED` 的未确认资源,已 `CONFIRMED` 批次不得回退,同柜重放不得产生第二批资源 |
|
||||
| 仓储资源服务 | `service/WarehouseResourceService.java`、`WarehouseResourceLockService.java` |
|
||||
| 单张托盘标签 PDF | `controller/PlatformWarehouseResourceController.java` 的 `/pallets/{id}/label`、`service/PalletLabelPrintService.java`、`client/PrintPdfClient.java`;契约 `src/main/resources/openapi/pallet-label-v1.yaml`。使用公共托盘真实编码并复用 Print `INBOUND_MARK_V1`,模板仍只在 Print 维护;沿用托盘列表权限与海外 Gateway 的 `warehouse-admin/**` 路由。`baiyue.clients.print.url` 对应 `PRINT_SERVICE_URL`,海外 Compose 必须与 BOL 一样显式注入国内 Print 地址,不能依赖海外 Nacos 发现国内服务;Feign 及熔断包装异常统一返回 `S0503` |
|
||||
| 单张托盘标签 PDF | `controller/PlatformWarehouseResourceController.java` 的 `/pallets/{id}/label`、`service/PalletLabelPrintService.java`、`client/PrintPdfClient.java`;契约 `src/main/resources/openapi/pallet-label-v1.yaml`。有 FBA 仓码的托盘与国内统一调用 Print `/internal/print/v1/pdfs/fba-pallet-labels`、`FBA_PALLET_A4_LANDSCAPE_V1`:297×210 mm,顶部 SCK8 等仓码,完整托盘号作为 Code 128 与底部文字,141 等内部编号原样保留;其他托盘使用 `INBOUND_MARK_V1`。模板只在 Print 维护;沿用托盘列表权限与海外 Gateway 的 `warehouse-admin/**` 路由。`baiyue.clients.print.url` 对应 `PRINT_SERVICE_URL`,海外 Compose 必须与 BOL 一样显式注入国内 Print 地址,不能依赖海外 Nacos 发现国内服务;Feign 及熔断包装异常统一返回 `S0503` |
|
||||
| 内部服务令牌 | `security/InternalServiceTokenGuard.java` |
|
||||
| 数据库基线与中文注释契约 | `src/main/resources/db/warehouse.sql`、`src/test/java/com/baiyue/warehouse/schema/SchemaChineseCommentContractTest.java` |
|
||||
| 测试 | `src/test/java/com/baiyue/warehouse/` |
|
||||
|
||||
@@ -7,7 +7,7 @@ paths:
|
||||
get:
|
||||
operationId: printOverseasPalletLabel
|
||||
summary: 下载指定托盘的一页标签 PDF
|
||||
description: 平台公共资源,要求 platform:overseas-warehouse:pallet:list。校验仓库和托盘归属后复用 Print 的 INBOUND_MARK_V1(100×100mm),原样使用托盘编码作为二维码,不创建或修改托盘。当前模板要求编码为 1–34 位可打印 ASCII 字符。
|
||||
description: 平台公共资源,要求 platform:overseas-warehouse:pallet:list。校验仓库和托盘归属后,有 FBA 仓码的托盘统一调用 Print FBA_PALLET_A4_LANDSCAPE_V1(297×210mm),顶部显示 SCK8 等 FBA 仓码,托盘完整编码同时作为 Code 128 内容和底部文字;内部编号如 141 原样保留。FBA 编码限 1–80 位可打印 ASCII,禁止首尾空格。不创建或修改托盘。其他托盘继续使用其现有 INBOUND_MARK_V1(100×100mm,编码限 1–34 位)。
|
||||
security:
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
|
||||
+27
-16
@@ -30,34 +30,33 @@ class PalletLabelPrintServiceTest {
|
||||
ReflectionTestUtils.setField(service, "warehouses", warehouses);
|
||||
ReflectionTestUtils.setField(service, "print", print);
|
||||
ReflectionTestUtils.setField(service, "internalToken", "test-token");
|
||||
pallet.setPalletCode("ABCD1234567-ABE3-003");
|
||||
pallet.setFbaWarehouseCode("ABE3");
|
||||
pallet.setPalletCode("MSCU6639870-141-001");
|
||||
pallet.setFbaWarehouseCode("SCK8");
|
||||
when(resources.pallet(0L, 200L, 9L)).thenReturn(pallet);
|
||||
when(warehouses.required(200L)).thenReturn(new SharedWarehouseReference(200L, "LAX", "洛杉矶仓", "ACTIVE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reusesExistingRendererWithExactlyTheSelectedCode() {
|
||||
void usesSharedFbaRendererWithExactlyTheSelectedCode() {
|
||||
byte[] pdf = "%PDF-label".getBytes(java.nio.charset.StandardCharsets.US_ASCII);
|
||||
when(print.generateInboundMarks(anyString(), anyString(), any()))
|
||||
when(print.generateFbaPalletLabels(anyString(), anyString(), any()))
|
||||
.thenReturn(ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(pdf));
|
||||
|
||||
var response = service.print(200L, 9L, "request-1");
|
||||
|
||||
var request = ArgumentCaptor.forClass(PrintPdfClient.Request.class);
|
||||
verify(print).generateInboundMarks(eq("test-token"), eq("request-1"), request.capture());
|
||||
assertThat(request.getValue().templateCode()).isEqualTo("INBOUND_MARK_V1");
|
||||
assertThat(request.getValue().pageSpec().widthMm()).isEqualByComparingTo("100");
|
||||
assertThat(request.getValue().pageSpec().heightMm()).isEqualByComparingTo("100");
|
||||
var request = ArgumentCaptor.forClass(PrintPdfClient.FbaPalletRequest.class);
|
||||
verify(print).generateFbaPalletLabels(eq("test-token"), eq("request-1"), request.capture());
|
||||
assertThat(request.getValue().templateCode()).isEqualTo("FBA_PALLET_A4_LANDSCAPE_V1");
|
||||
assertThat(request.getValue().pageSpec().widthMm()).isEqualByComparingTo("297");
|
||||
assertThat(request.getValue().pageSpec().heightMm()).isEqualByComparingTo("210");
|
||||
assertThat(request.getValue().labels()).singleElement().satisfies(label -> {
|
||||
assertThat(label.topCode()).isEqualTo("ABCD1234567-ABE3-003");
|
||||
assertThat(label.qrContent()).isEqualTo(label.topCode());
|
||||
assertThat(label.warehouseText()).isEqualTo("ABE3");
|
||||
assertThat(label.routeText()).isEqualTo("洛杉矶仓");
|
||||
assertThat(label.palletCode()).isEqualTo("MSCU6639870-141-001");
|
||||
assertThat(label.fbaWarehouseCode()).isEqualTo("SCK8");
|
||||
});
|
||||
assertThat(response.getBody()).isEqualTo(pdf);
|
||||
verify(resources).pallet(0L, 200L, 9L);
|
||||
verifyNoMoreInteractions(resources);
|
||||
verify(print, never()).generateInboundMarks(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,7 +68,7 @@ class PalletLabelPrintServiceTest {
|
||||
|
||||
@Test
|
||||
void mapsCircuitBreakerWrappedPrintFailureToServiceUnavailable() {
|
||||
when(print.generateInboundMarks(anyString(), anyString(), any()))
|
||||
when(print.generateFbaPalletLabels(anyString(), anyString(), any()))
|
||||
.thenThrow(new NoFallbackAvailableException("No fallback available.",
|
||||
new IllegalStateException("No instances available for print-service")));
|
||||
|
||||
@@ -82,7 +81,7 @@ class PalletLabelPrintServiceTest {
|
||||
|
||||
@Test
|
||||
void rejectsJsonErrorInsteadOfDownloadingItAsPdf() {
|
||||
when(print.generateInboundMarks(anyString(), anyString(), any()))
|
||||
when(print.generateFbaPalletLabels(anyString(), anyString(), any()))
|
||||
.thenReturn(ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body("{}".getBytes()));
|
||||
assertThatThrownBy(() -> service.print(200L, 9L, "request-1"))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("有效 PDF");
|
||||
@@ -90,11 +89,23 @@ class PalletLabelPrintServiceTest {
|
||||
|
||||
@Test
|
||||
void neverTruncatesOrSubstitutesUnsupportedPalletCodes() {
|
||||
for (String code : new String[] { "", "托盘001", "A".repeat(35) }) {
|
||||
for (String code : new String[] { "", "托盘001", "A".repeat(81), " PALLET" }) {
|
||||
pallet.setPalletCode(code);
|
||||
assertThatThrownBy(() -> service.print(200L, 9L, "request-1"))
|
||||
.isInstanceOf(BizException.class).hasMessageContaining("托盘编码");
|
||||
}
|
||||
verifyNoInteractions(print);
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsNonFbaLabelsOnTheirOwnTemplate() {
|
||||
pallet.setFbaWarehouseCode(null);
|
||||
when(print.generateInboundMarks(anyString(), anyString(), any()))
|
||||
.thenReturn(ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body("%PDF-other".getBytes()));
|
||||
service.print(200L, 9L, "request-1");
|
||||
var captor = ArgumentCaptor.forClass(PrintPdfClient.Request.class);
|
||||
verify(print).generateInboundMarks(anyString(), anyString(), captor.capture());
|
||||
assertThat(captor.getValue().templateCode()).isEqualTo("INBOUND_MARK_V1");
|
||||
verify(print, never()).generateFbaPalletLabels(any(), any(), any());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user