From e8890389ff1a7ead2b9482c053f1fd3d25853060 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Thu, 10 Sep 2026 11:37:51 +0800 Subject: [PATCH] fix(firstleg): keep PDA photos accessible after departure --- .../PlatformContainerController.java | 1 - .../openapi/phase3-container-overview-v1.yaml | 2 +- .../ContainerControllerContractTest.java | 33 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java index e4e43d904..d7bdccc2c 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java @@ -92,7 +92,6 @@ public class PlatformContainerController { @PathVariable @Positive long id, @PathVariable @Positive long fileId) { FirstlegScopeGuard.platform(user); - service.requireDomestic(id); var value = overview.downloadPhoto(id, fileId); return ResponseEntity.ok().cacheControl(CacheControl.noStore()) .header(HttpHeaders.CONTENT_DISPOSITION, org.springframework.http.ContentDisposition.inline() diff --git a/firstleg/src/main/resources/openapi/phase3-container-overview-v1.yaml b/firstleg/src/main/resources/openapi/phase3-container-overview-v1.yaml index e89283d7e..4c4a1797b 100644 --- a/firstleg/src/main/resources/openapi/phase3-container-overview-v1.yaml +++ b/firstleg/src/main/resources/openapi/phase3-container-overview-v1.yaml @@ -27,7 +27,7 @@ paths: /api/platform/v1/containers/{id}/pda/photos/{fileId}/download: get: operationId: downloadContainerPdaPhoto - description: '权限:platform:firstleg:container:list;Firstleg按柜次、装柜计划、入库单和文件归属代理下载,不暴露内部服务令牌' + description: '权限:platform:firstleg:container:list;PDA完成及国内离仓后仍可读取ACTIVE历史照片;Firstleg按柜次、装柜计划、入库单和文件归属代理下载,不暴露内部服务令牌' parameters: [{$ref: '#/components/parameters/ContainerId'}, {name: fileId, in: path, required: true, schema: {type: integer, format: int64}}] responses: {'200': {description: photo bytes, content: {application/octet-stream: {schema: {type: string, format: binary}}}}, '404': {description: photo missing or not owned by the container}} /api/platform/v1/containers/{id}/documents/{documentType}: diff --git a/firstleg/src/test/java/com/baiyue/firstleg/controller/ContainerControllerContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/controller/ContainerControllerContractTest.java index c272ef9e4..080a3a86e 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/controller/ContainerControllerContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/controller/ContainerControllerContractTest.java @@ -1,20 +1,52 @@ package com.baiyue.firstleg.controller; +import com.baiyue.common.security.AuthenticatedUser; +import com.baiyue.firstleg.service.ContainerOverviewService; +import com.baiyue.firstleg.service.ContainerService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.core.io.ClassPathResource; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; @DisplayName("货柜基础资料接口契约") class ContainerControllerContractTest { + @Test + @DisplayName("PDA照片作为历史证据下载时不受国内工作台分区限制") + void downloadsPdaPhotoWithoutDomesticWorkbenchGate() { + ContainerService containers = mock(ContainerService.class); + ContainerOverviewService overview = mock(ContainerOverviewService.class); + PlatformContainerController controller = new PlatformContainerController(); + ReflectionTestUtils.setField(controller, "service", containers); + ReflectionTestUtils.setField(controller, "overview", overview); + when(overview.downloadPhoto(41L, 101L)).thenReturn(new ContainerOverviewService.PhotoDownload( + "door.jpg", MediaType.IMAGE_JPEG, new byte[]{1, 2, 3})); + AuthenticatedUser user = new AuthenticatedUser(7L, 0L, 0L, 8L, + "admin", "PLATFORM", "WEB", Set.of("platform:firstleg:container:list")); + + var response = controller.downloadPdaPhoto(user, 41L, 101L); + + assertThat(response.getStatusCode().is2xxSuccessful()).isTrue(); + assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.IMAGE_JPEG); + assertThat(response.getBody()).containsExactly(1, 2, 3); + verify(overview).downloadPhoto(41L, 101L); + verifyNoInteractions(containers); + } + @Test @DisplayName("查询、新增和编辑按钮权限彼此独立") void separatesButtonPermissions() { @@ -50,6 +82,7 @@ class ContainerControllerContractTest { "/pda/photos/{fileId}/download:", "downloadPath:", "workflowStatus:", "AWAITING_REVIEW", "version: {type: integer, minimum: 0}", "downloadPath: {type: string, nullable: true", + "PDA完成及国内离仓后仍可读取ACTIVE历史照片", "仅 ACTIVE 照片提供鉴权下载路径;VOID 历史照片为空") .doesNotContain("?containerNo", "containerNo in: query"); }