This commit is contained in:
2026-09-15 14:18:18 +08:00
parent 6a2075fe4b
commit a474ca4d93
35 changed files with 751 additions and 228 deletions
@@ -0,0 +1,24 @@
package com.baiyue.warehouse.client;
import java.math.BigDecimal;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
/** Reuses the Print service's existing label renderer; no local PDF template. */
@FeignClient(name = "print-service", contextId = "overseasWarehousePrintPdfClient",
url = "${baiyue.clients.print.url:}", path = "/internal/print/v1/pdfs")
public interface PrintPdfClient {
@PostMapping(value = "/inbound-marks", consumes = "application/json")
ResponseEntity<byte[]> generateInboundMarks(
@RequestHeader("X-Internal-Service-Token") String token,
@RequestHeader(value = "X-Request-Id", required = false) String requestId,
@RequestBody Request request);
record Request(String templateCode, PageSpec pageSpec, String downloadFileName, List<Label> labels) {}
record PageSpec(BigDecimal widthMm, BigDecimal heightMm) {}
record Label(String topCode, String warehouseText, String cartonText, String routeText, String qrContent) {}
}
@@ -10,6 +10,9 @@ import com.baiyue.warehouse.dto.PalletRequests;
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.service.WarehouseResourceService;
import com.baiyue.warehouse.service.PalletLabelPrintService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestHeader;
import com.baiyue.warehouse.security.OverseasWarehousePermissions;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -34,6 +37,7 @@ import org.springframework.web.bind.annotation.RestController;
@Tag(name = "Platform overseas warehouse resources")
public class PlatformWarehouseResourceController {
@Resource private WarehouseResourceService service;
@Resource private PalletLabelPrintService palletLabels;
@PostMapping("/locations")
@PreAuthorize("hasAuthority('" + OverseasWarehousePermissions.LOCATION_CREATE + "')")
@@ -134,6 +138,16 @@ public class PlatformWarehouseResourceController {
return ApiResponse.success(service.pallet(WarehouseResourceService.PUBLIC_RESOURCE_SCOPE, warehouseId, id));
}
@GetMapping("/pallets/{id}/label")
@PreAuthorize("hasAuthority('" + OverseasWarehousePermissions.PALLET_LIST + "')")
@Operation(summary = "Print one existing pallet label", description = "Required authority: " + OverseasWarehousePermissions.PALLET_LIST)
public ResponseEntity<byte[]> palletLabel(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable long warehouseId, @PathVariable long id,
@RequestHeader(value = "X-Request-Id", required = false) String requestId) {
requirePlatform(user);
return palletLabels.print(warehouseId, id, requestId);
}
@DeleteMapping("/pallets/{id}")
@PreAuthorize("hasAuthority('" + OverseasWarehousePermissions.PALLET_DELETE + "')")
@Operation(summary = "Delete a warehouse pallet", description = "Required authority: " + OverseasWarehousePermissions.PALLET_DELETE)
@@ -0,0 +1,56 @@
package com.baiyue.warehouse.service;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.client.PrintPdfClient;
import feign.FeignException;
import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
@Service
public class PalletLabelPrintService {
@Resource private WarehouseResourceService resources;
@Resource private DomesticWarehouseDirectory warehouses;
@Resource private PrintPdfClient print;
@Value("${baiyue.security.internal-service-token:${baiyue.internal-service.token}}")
private String internalToken;
public ResponseEntity<byte[]> print(long warehouseId, long palletId, String requestId) {
var pallet = resources.pallet(WarehouseResourceService.PUBLIC_RESOURCE_SCOPE, warehouseId, palletId);
String code = pallet.getPalletCode();
// Keep the stored business code intact, including the QR payload.
if (code == null || !code.matches("[\\x20-\\x7E]{1,34}") || code.isBlank()) {
throw new BizException(ResultCode.VALIDATION_FAILED, "当前标签模板要求托盘编码为 1–34 位可打印 ASCII 字符,请核对托盘编码");
}
var warehouse = warehouses.required(warehouseId);
String fbaCode = pallet.getFbaWarehouseCode();
String fileName = code.replaceAll("[\\\\/:*?\"<>|]", "-") + "-托盘码.pdf";
var label = new PrintPdfClient.Label(code, fbaCode == null || fbaCode.isBlank() ? "—" : fbaCode,
"PALLET", 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 {
var response = print.generateInboundMarks(internalToken, requestId, request);
byte[] body = response == null ? null : response.getBody();
MediaType type = response == null ? null : response.getHeaders().getContentType();
if (response == null || !response.getStatusCode().is2xxSuccessful() || body == null || body.length == 0
|| type == null || !MediaType.APPLICATION_PDF.isCompatibleWith(type)) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "打印服务未返回有效 PDF,请稍后重试");
}
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).contentLength(body.length)
.header(HttpHeaders.CONTENT_DISPOSITION,
ContentDisposition.attachment().filename(fileName, StandardCharsets.UTF_8).build().toString())
.header(HttpHeaders.CACHE_CONTROL, "no-store").body(body);
} catch (FeignException exception) {
throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "打印服务暂不可用,请稍后重试");
}
}
}