优化
This commit is contained in:
+3
@@ -128,6 +128,7 @@ class ReceivingContainerContractTest {
|
||||
assertTrue(yaml.contains(prefix + "/current/skus/resolve:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/receipts/carton:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/complete:"));
|
||||
assertTrue(yaml.contains(prefix + "/current/unload-photos/files/{fileId}/download:"));
|
||||
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<String> successResponses = receivingPaths.lines().map(String::trim)
|
||||
@@ -139,6 +140,8 @@ class ReceivingContainerContractTest {
|
||||
assertTrue(receivingPaths.contains("仅 PLATFORM WAREHOUSE_PICKER 可用"));
|
||||
assertFalse(receivingPaths.contains("客户与操作账号均从 PDA Token 获取"));
|
||||
assertTrue(yaml.contains("enum: [LICENSE_PLATE, CONTAINER_SEAL, DOOR_OPEN]"));
|
||||
assertTrue(yaml.contains("photos: { type: array, minItems: 3, maxItems: 3"));
|
||||
assertTrue(yaml.contains("required: [role, fileId, downloadPath]"));
|
||||
assertTrue(yaml.contains("overseasWarehouseId: { type: string"));
|
||||
assertTrue(yaml.contains("overseasWarehouseName: { type: string"));
|
||||
assertFalse(receivingPaths.contains("photoFileIds"));
|
||||
|
||||
+22
@@ -2,6 +2,7 @@ package com.baiyue.lastleg.inboundplan.pda.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.lastleg.inboundplan.pda.dto.ReceivingContainerResponses;
|
||||
import com.baiyue.lastleg.inboundplan.pda.gateway.UnloadPhotoFileGateway;
|
||||
import com.baiyue.lastleg.inboundplan.pda.service.ReceivingContainerService;
|
||||
import com.baiyue.lastleg.pda.security.PdaWarehouseIdentity;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -9,12 +10,16 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -55,6 +60,23 @@ class ReceivingContainerControllerTest {
|
||||
.hasMessageContaining("403");
|
||||
}
|
||||
|
||||
@Test
|
||||
void downloadCurrentUnloadPhotoDelegatesToService() {
|
||||
var user = picker();
|
||||
when(service.downloadCurrentPhoto("42", "9999")).thenReturn(
|
||||
new UnloadPhotoFileGateway.DownloadedFile("abc".getBytes(), "photo.jpg", MediaType.IMAGE_JPEG_VALUE));
|
||||
|
||||
ResponseEntity<byte[]> response = controller.downloadCurrentPhoto(user, "9999");
|
||||
|
||||
assertThat(response.getStatusCodeValue()).isEqualTo(200);
|
||||
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.IMAGE_JPEG);
|
||||
assertThat(response.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION))
|
||||
.startsWith("inline").contains("photo.jpg");
|
||||
assertThat(response.getHeaders().getCacheControl()).isEqualTo("no-store");
|
||||
assertThat(response.getBody()).containsExactly("abc".getBytes());
|
||||
verify(service).downloadCurrentPhoto("42", "9999");
|
||||
}
|
||||
|
||||
@Test
|
||||
void pickerAuthorityWithoutWarehouseBindingIsForbidden() {
|
||||
AuthenticatedUser unbound = new AuthenticatedUser(42L, 0L, 0L, 8L, "receiver", "PLATFORM", "PDA",
|
||||
|
||||
+32
@@ -2,6 +2,8 @@ package com.baiyue.lastleg.inboundplan.pda.gateway;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
@@ -11,8 +13,10 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
|
||||
|
||||
class HttpUnloadPhotoFileGatewayTest {
|
||||
private HttpUnloadPhotoFileGateway gateway;
|
||||
@@ -51,4 +55,32 @@ class HttpUnloadPhotoFileGatewayTest {
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(400));
|
||||
server.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void downloadUsesBusinessOwnershipPathAndPreservesBlobMetadata() {
|
||||
server.expect(requestTo(
|
||||
"http://file-service/internal/file/v1/business-files/100/OVERSEAS_UNLOAD_PHOTO/8/9/content"))
|
||||
.andExpect(header("X-Internal-Service-Token", "internal-token"))
|
||||
.andRespond(withSuccess("photo-bytes".getBytes(), MediaType.IMAGE_JPEG)
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=door.jpg"));
|
||||
|
||||
UnloadPhotoFileGateway.DownloadedFile result = gateway.download("100", 8L, 9L);
|
||||
|
||||
assertThat(result.content()).containsExactly("photo-bytes".getBytes());
|
||||
assertThat(result.fileName()).isEqualTo("door.jpg");
|
||||
assertThat(result.contentType()).isEqualTo(MediaType.IMAGE_JPEG_VALUE);
|
||||
server.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingOwnedPhotoMapsToNotFound() {
|
||||
server.expect(requestTo(
|
||||
"http://file-service/internal/file/v1/business-files/100/OVERSEAS_UNLOAD_PHOTO/8/9/content"))
|
||||
.andRespond(withStatus(HttpStatus.NOT_FOUND));
|
||||
|
||||
assertThatThrownBy(() -> gateway.download("100", 8L, 9L))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(404));
|
||||
server.verify();
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -315,6 +315,11 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
ReceivingPhotoRole.DOOR_OPEN);
|
||||
org.assertj.core.api.Assertions.assertThat(response.photos()).extracting("fileId")
|
||||
.containsExactly("2092476334483931138", "2", "3");
|
||||
org.assertj.core.api.Assertions.assertThat(response.photos()).extracting("downloadPath")
|
||||
.containsExactly(
|
||||
"/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/2092476334483931138/download",
|
||||
"/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/2/download",
|
||||
"/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/3/download");
|
||||
verify(unloadPhotos).validate("100", 8L, 2092476334483931138L);
|
||||
verify(unloadPhotos).validate("100", 8L, 2L);
|
||||
verify(unloadPhotos).validate("100", 8L, 3L);
|
||||
@@ -356,6 +361,35 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
verifyNoInteractions(unloadPhotos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void currentSignedPhotoCanBeDownloadedOnlyThroughItsOperatorCustomerAndSessionScope() {
|
||||
ReceivingContainerSession current = session("CONT-A", "SIGNED");
|
||||
current.setSignPhotos("[{\"role\":\"LICENSE_PLATE\",\"fileId\":\"9\"},"
|
||||
+ "{\"role\":\"CONTAINER_SEAL\",\"fileId\":\"10\"},"
|
||||
+ "{\"role\":\"DOOR_OPEN\",\"fileId\":\"11\"}]");
|
||||
when(sessions.findActiveByOperator("OPERATOR:7")).thenReturn(current);
|
||||
UnloadPhotoFileGateway.DownloadedFile expected =
|
||||
new UnloadPhotoFileGateway.DownloadedFile("image".getBytes(), "plate.jpg", "image/jpeg");
|
||||
when(unloadPhotos.download("100", 8L, 9L)).thenReturn(expected);
|
||||
|
||||
assertThat(service.downloadCurrentPhoto("7", "9")).isSameAs(expected);
|
||||
verify(unloadPhotos).download("100", 8L, 9L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void photoOutsideCurrentSignedRoleBindingsIsForbiddenBeforeFileServiceCall() {
|
||||
ReceivingContainerSession current = session("CONT-A", "SIGNED");
|
||||
current.setSignPhotos("[{\"role\":\"LICENSE_PLATE\",\"fileId\":\"9\"},"
|
||||
+ "{\"role\":\"CONTAINER_SEAL\",\"fileId\":\"10\"},"
|
||||
+ "{\"role\":\"DOOR_OPEN\",\"fileId\":\"11\"}]");
|
||||
when(sessions.findActiveByOperator("OPERATOR:7")).thenReturn(current);
|
||||
|
||||
assertThatThrownBy(() -> service.downloadCurrentPhoto("7", "12"))
|
||||
.isInstanceOfSatisfying(ResponseStatusException.class,
|
||||
error -> assertThat(error.getStatusCode().value()).isEqualTo(403));
|
||||
verifyNoInteractions(unloadPhotos);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bulkRejectsSkuOverReceiptBeforeWarehouseLookup() {
|
||||
stubCurrent("FBA", "CONT-A");
|
||||
|
||||
Reference in New Issue
Block a user