diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/ReceivingContainerController.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/ReceivingContainerController.java index d80e315d5..c6ddb022d 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/ReceivingContainerController.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/controller/ReceivingContainerController.java @@ -95,6 +95,19 @@ public class ReceivingContainerController { .contentLength(download.content().length).body(download.content()); } + @GetMapping("/{containerNo}/unload-photos/files/{fileId}/download") + public ResponseEntity downloadContainerPhoto(@AuthenticationPrincipal AuthenticatedUser user, + @PathVariable String containerNo, + @PathVariable String fileId) { + PdaWarehouseIdentity identity = identity(user); + var download = service.downloadContainerPhoto(identity.overseasWarehouseId(), containerNo, fileId); + return ResponseEntity.ok().cacheControl(CacheControl.noStore()) + .header(HttpHeaders.CONTENT_DISPOSITION, + ContentDisposition.inline().filename(download.fileName(), StandardCharsets.UTF_8).build().toString()) + .contentType(MediaType.parseMediaType(download.contentType())) + .contentLength(download.content().length).body(download.content()); + } + @GetMapping("/current/domestic-loading-photos/{evidenceType}/content") public ResponseEntity downloadDomesticLoadingPhoto(@AuthenticationPrincipal AuthenticatedUser user, @PathVariable String evidenceType) { @@ -107,6 +120,19 @@ public class ReceivingContainerController { .contentLength(download.content().length).body(download.content()); } + @GetMapping("/{containerNo}/domestic-loading-photos/{evidenceType}/content") + public ResponseEntity downloadContainerDomesticLoadingPhoto(@AuthenticationPrincipal AuthenticatedUser user, + @PathVariable String containerNo, + @PathVariable String evidenceType) { + PdaWarehouseIdentity identity = identity(user); + var download = service.downloadContainerDomesticLoadingPhoto(identity.overseasWarehouseId(), containerNo, evidenceType); + return ResponseEntity.ok().cacheControl(CacheControl.noStore()) + .header(HttpHeaders.CONTENT_DISPOSITION, + ContentDisposition.inline().filename(download.fileName(), StandardCharsets.UTF_8).build().toString()) + .contentType(MediaType.parseMediaType(download.contentType())) + .contentLength(download.content().length).body(download.content()); + } + @PostMapping("/current/sign") public ApiResponse sign(@AuthenticationPrincipal AuthenticatedUser user, @Valid @RequestBody ReceivingContainerRequests.Sign request) { diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/mapper/ReceivingContainerSessionMapper.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/mapper/ReceivingContainerSessionMapper.java index 0cfc25de9..4c6697f14 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/mapper/ReceivingContainerSessionMapper.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/mapper/ReceivingContainerSessionMapper.java @@ -22,6 +22,10 @@ public interface ReceivingContainerSessionMapper extends BaseMapper findActiveContainers(); diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java index 2c3b85714..63ed8f26a 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/service/ReceivingContainerService.java @@ -70,8 +70,12 @@ public class ReceivingContainerService { private static final String PLATFORM_SCOPE = "0"; private static final String PHOTO_DOWNLOAD_PATH = "/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/{fileId}/download"; + private static final String CONTAINER_PHOTO_DOWNLOAD_PATH = + "/api/pda/v1/last-leg/receiving-containers/{containerNo}/unload-photos/files/{fileId}/download"; private static final String DOMESTIC_PHOTO_DOWNLOAD_PATH = "/api/pda/v1/last-leg/receiving-containers/current/domestic-loading-photos/{type}/content"; + private static final String CONTAINER_DOMESTIC_PHOTO_DOWNLOAD_PATH = + "/api/pda/v1/last-leg/receiving-containers/{containerNo}/domestic-loading-photos/{type}/content"; private static final String PALLET_MERGE_HINT = "该入库计划可选择合托:合托时扫描同目的仓已使用的托盘码,独立收货时扫描新托盘码;不强制合托"; private static final int MAX_PACKAGES_PER_PALLET = 66; @@ -275,6 +279,39 @@ public class ReceivingContainerService { return domesticEvidenceFiles.download(bookingId, evidence.getSourceFileId()); } + public UnloadPhotoFileGateway.DownloadedFile downloadContainerPhoto( + long overseasWarehouseId, String containerNo, String fileId) { + ReceivingContainerSession session = requireContainerPhotoSession(overseasWarehouseId, containerNo); + long parsedFileId = positiveFileId(fileId); + if (signedPhotos(session).stream().noneMatch(photo -> fileId.equals(photo.fileId()))) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "该柜没有对应的卸柜照片"); + } + return unloadPhotos.download(PLATFORM_SCOPE, session.getId(), parsedFileId); + } + + public DomesticLoadingEvidenceGateway.DownloadedFile downloadContainerDomesticLoadingPhoto( + long overseasWarehouseId, String containerNo, String evidenceType) { + ReceivingContainerSession session = requireContainerPhotoSession(overseasWarehouseId, containerNo); + String type = requiredEvidenceType(evidenceType); + var evidence = domesticEvidence.find(session.getSourceContainerBookingId(), type); + if (evidence == null) throw notFound("当前柜没有该国内装柜照片"); + long bookingId; + try { bookingId = Long.parseLong(session.getSourceContainerBookingId()); } + catch (NumberFormatException exception) { throw new ResponseStatusException( + HttpStatus.SERVICE_UNAVAILABLE, "当前柜国内照片来源无效", exception); } + return domesticEvidenceFiles.download(bookingId, evidence.getSourceFileId()); + } + + private ReceivingContainerSession requireContainerPhotoSession(long overseasWarehouseId, String containerNo) { + String value = trim(containerNo); + if (value == null) throw notFound("柜号不能为空"); + ReceivingContainerSession session = sessions.findLatestByContainerNo(value); + if (session == null || !Objects.equals(session.getOverseasWarehouseId(), overseasWarehouseId)) { + throw notFound("当前仓没有该柜的照片"); + } + return session; + } + public void requireCurrentWarehouse(String operatorKey, long overseasWarehouseId) { requireSessionWarehouse(sessions.findActiveByOperator(activeOperatorKey(operatorKey)), overseasWarehouseId); } @@ -1050,7 +1087,8 @@ public class ReceivingContainerService { if (bookingId == null) return List.of(); return domesticEvidence.listByContainer(bookingId).stream() .map(value -> new ReceivingContainerResponses.DomesticLoadingPhoto(value.getEvidenceType(), - evidenceLabel(value.getEvidenceType()), DOMESTIC_PHOTO_DOWNLOAD_PATH.replace("{type}", value.getEvidenceType()))) + evidenceLabel(value.getEvidenceType()), CONTAINER_DOMESTIC_PHOTO_DOWNLOAD_PATH + .replace("{containerNo}", session.getContainerNo()).replace("{type}", value.getEvidenceType()))) .toList(); } @@ -1384,7 +1422,8 @@ public class ReceivingContainerService { private List signPhotos(ReceivingContainerSession session) { return signedPhotos(session).stream() .map(photo -> new ReceivingContainerResponses.SignedPhoto(photo.role(), photo.fileId(), - PHOTO_DOWNLOAD_PATH.replace("{fileId}", photo.fileId()))) + CONTAINER_PHOTO_DOWNLOAD_PATH.replace("{containerNo}", session.getContainerNo()) + .replace("{fileId}", photo.fileId()))) .toList(); } diff --git a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml index dbdbcfe8b..0af7a9337 100644 --- a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml +++ b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml @@ -84,6 +84,17 @@ paths: application/octet-stream: { schema: { type: string, format: binary } } '403': { description: fileId 未绑定到当前操作员的当前柜会话三类签收照片 } '404': { description: 当前柜会话或照片不存在 } + /api/pda/v1/last-leg/receiving-containers/{containerNo}/unload-photos/files/{fileId}/download: + get: + summary: 按柜号下载卸柜签收照片 + description: 权限 lastleg:pda:operate;按当前 PDA 绑定仓和柜号读取历史或当前柜的签收照片,不要求存在未完成接柜会话。 + security: [{ bearerAuth: [] }] + parameters: + - { name: containerNo, in: path, required: true, schema: { type: string } } + - { name: fileId, in: path, required: true, schema: { type: string, pattern: '^[1-9][0-9]{0,18}$' } } + responses: + '200': { description: 签收照片字节流, content: { image/jpeg: { schema: { type: string, format: binary } }, image/png: { schema: { type: string, format: binary } } } } + '404': { description: 当前仓没有该柜或照片不存在 } /api/pda/v1/last-leg/receiving-containers/current/domestic-loading-photos/{evidenceType}/content: get: summary: 查看当前柜国内装柜照片 @@ -100,6 +111,17 @@ paths: application/octet-stream: { schema: { type: string, format: binary } } '404': { description: 当前柜或指定类型照片不存在 } '503': { description: 国内照片服务暂不可用 } + /api/pda/v1/last-leg/receiving-containers/{containerNo}/domestic-loading-photos/{evidenceType}/content: + get: + summary: 按柜号查看国内装柜照片 + description: 权限 lastleg:pda:operate;按当前 PDA 绑定仓和柜号读取历史或当前柜的国内装柜照片,不要求存在未完成接柜会话。 + security: [{ bearerAuth: [] }] + parameters: + - { name: containerNo, in: path, required: true, schema: { type: string } } + - { name: evidenceType, in: path, required: true, schema: { type: string, enum: [PHOTO_STEP_1, PHOTO_STEP_2, PHOTO_STEP_3, PHOTO_STEP_4, SEAL] } } + responses: + '200': { description: 国内装柜照片字节流, content: { image/jpeg: { schema: { type: string, format: binary } }, image/png: { schema: { type: string, format: binary } } } } + '404': { description: 当前仓没有该柜或指定类型照片不存在 } /api/pda/v1/last-leg/receiving-containers/current/sign: post: summary: 签收当前柜 diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/ReceivingContainerContractTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/ReceivingContainerContractTest.java index 8a53f13d8..0f959bfa8 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/ReceivingContainerContractTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/pda/ReceivingContainerContractTest.java @@ -140,6 +140,8 @@ class ReceivingContainerContractTest { assertTrue(yaml.contains(prefix + "/current/receipts/pallet:")); assertTrue(yaml.contains(prefix + "/current/complete:")); assertTrue(yaml.contains(prefix + "/current/unload-photos/files/{fileId}/download:")); + assertTrue(yaml.contains(prefix + "/{containerNo}/unload-photos/files/{fileId}/download:")); + assertTrue(yaml.contains(prefix + "/{containerNo}/domestic-loading-photos/{evidenceType}/content:")); assertFalse(yaml.contains("/api/pda/v1/last-leg/receiving-containers/{planNo}")); String receivingPaths = yaml.substring(yaml.indexOf(prefix), yaml.indexOf("/api/platform/v1/last-leg/orders:")); List successResponses = receivingPaths.lines().map(String::trim)