优化
This commit is contained in:
+3
-1
@@ -3,6 +3,7 @@ package com.baiyue.lastleg.inboundplan.pda.dto;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@@ -14,7 +15,8 @@ public final class ReceivingContainerRequests {
|
||||
public record Action(@NotBlank @Size(max = 255) String idempotencyKey) { }
|
||||
public record Sign(@NotNull @Size(min = 3, max = 3) List<@Valid SignedPhoto> photos,
|
||||
@NotBlank @Size(max = 255) String idempotencyKey) { }
|
||||
public record SignedPhoto(@NotNull ReceivingPhotoRole role, @Positive long fileId) { }
|
||||
public record SignedPhoto(@NotNull ReceivingPhotoRole role,
|
||||
@NotBlank @Pattern(regexp = "^[1-9][0-9]{0,18}$") String fileId) { }
|
||||
public record CartonResolve(@NotBlank String cartonMark) { }
|
||||
public record SkuResolve(@NotBlank @Size(max = 128) String skuCode) { }
|
||||
public record PalletResolve(@NotBlank String palletCode, @NotBlank String inboundCode) { }
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ public final class ReceivingContainerResponses {
|
||||
int totalPackages, int receivedPackages, int pendingPackages,
|
||||
List<Inbound> inbounds) { }
|
||||
|
||||
public record SignedPhoto(ReceivingPhotoRole role, long fileId) { }
|
||||
public record SignedPhoto(ReceivingPhotoRole role, String fileId) { }
|
||||
|
||||
public record Inbound(String inboundCode, String destinationType,
|
||||
String overseasWarehouseId, String overseasWarehouseName,
|
||||
@@ -48,5 +48,5 @@ public final class ReceivingContainerResponses {
|
||||
String destinationType, String palletCode, int acceptedPackages,
|
||||
int receivedPackages, int pendingPackages, String sessionStatus) { }
|
||||
|
||||
public record Photo(long fileId, String fileName, String contentType, long sizeBytes) { }
|
||||
public record Photo(String fileId, String fileName, String contentType, long sizeBytes) { }
|
||||
}
|
||||
|
||||
+14
-4
@@ -169,7 +169,7 @@ public class ReceivingContainerService {
|
||||
String customerId = session.getCustomerId();
|
||||
if (!"WAIT_SIGN".equals(session.getStatus())) throw conflict("Unload photos can only be uploaded before signing");
|
||||
UnloadPhotoFileGateway.UploadedFile uploaded = unloadPhotos.upload(customerId, session.getId(), operatorId, file);
|
||||
return new ReceivingContainerResponses.Photo(uploaded.fileId(), uploaded.fileName(), uploaded.contentType(), uploaded.sizeBytes());
|
||||
return new ReceivingContainerResponses.Photo(Long.toString(uploaded.fileId()), uploaded.fileName(), uploaded.contentType(), uploaded.sizeBytes());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -181,7 +181,7 @@ public class ReceivingContainerService {
|
||||
String customerId = session.getCustomerId();
|
||||
if (!"WAIT_SIGN".equals(session.getStatus())) throw conflict("Container has already been signed");
|
||||
List<ReceivingContainerResponses.SignedPhoto> photos = validateSignPhotos(request);
|
||||
List<Long> photoFileIds = photos.stream().map(ReceivingContainerResponses.SignedPhoto::fileId).toList();
|
||||
List<Long> photoFileIds = photos.stream().map(photo -> positiveFileId(photo.fileId())).toList();
|
||||
photoFileIds.forEach(fileId -> unloadPhotos.validate(customerId, session.getId(), fileId));
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
session.setSignPhotos(write(photos)); session.setStatus("SIGNED"); session.setSignedAt(now);
|
||||
@@ -513,9 +513,9 @@ public class ReceivingContainerService {
|
||||
Set<com.baiyue.lastleg.inboundplan.pda.dto.ReceivingPhotoRole> roles = request.photos().stream()
|
||||
.map(ReceivingContainerRequests.SignedPhoto::role).collect(Collectors.toSet());
|
||||
if (roles.size() != 3 || roles.contains(null)) throw conflict("Each unload photo role is required exactly once");
|
||||
Set<Long> fileIds = request.photos().stream().map(ReceivingContainerRequests.SignedPhoto::fileId)
|
||||
Set<Long> fileIds = request.photos().stream().map(value -> positiveFileId(value.fileId()))
|
||||
.collect(Collectors.toSet());
|
||||
if (fileIds.size() != 3 || fileIds.stream().anyMatch(value -> value == null || value <= 0)) {
|
||||
if (fileIds.size() != 3) {
|
||||
throw conflict("Unload photo file IDs must be positive and unique");
|
||||
}
|
||||
return request.photos().stream()
|
||||
@@ -523,6 +523,16 @@ public class ReceivingContainerService {
|
||||
.map(value -> new ReceivingContainerResponses.SignedPhoto(value.role(), value.fileId())).toList();
|
||||
}
|
||||
|
||||
private long positiveFileId(String value) {
|
||||
try {
|
||||
long fileId = Long.parseLong(value);
|
||||
if (fileId > 0) return fileId;
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Validation normally rejects this before the service; keep the domain fail-closed.
|
||||
}
|
||||
throw conflict("Unload photo file ID must be a positive decimal string");
|
||||
}
|
||||
|
||||
private SkuRef requireSku(InboundRef ref, String skuCode) {
|
||||
SkuRef result = skuRef(ref, skuCode);
|
||||
if (result == null) throw notFound("SKU is not part of the selected inbound code");
|
||||
|
||||
@@ -623,7 +623,7 @@ components:
|
||||
required: [role, fileId]
|
||||
properties:
|
||||
role: { type: string, enum: [LICENSE_PLATE, CONTAINER_SEAL, DOOR_OPEN] }
|
||||
fileId: { type: integer, format: int64, minimum: 1 }
|
||||
fileId: { type: string, pattern: '^[1-9][0-9]{0,18}$', description: Snowflake file ID kept as a decimal string }
|
||||
ReceivingBulk:
|
||||
type: object
|
||||
required: [inboundCode, skuCode, palletCode, packageCount, idempotencyKey]
|
||||
@@ -701,7 +701,7 @@ components:
|
||||
type: object
|
||||
required: [fileId, fileName, contentType, sizeBytes]
|
||||
properties:
|
||||
fileId: { type: integer, format: int64 }
|
||||
fileId: { type: string, pattern: '^[1-9][0-9]{0,18}$', description: Snowflake file ID kept as a decimal string }
|
||||
fileName: { type: string }
|
||||
contentType: { type: string }
|
||||
sizeBytes: { type: integer, format: int64 }
|
||||
|
||||
+12
-10
@@ -304,16 +304,18 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
when(sessions.updateById(any(ReceivingContainerSession.class))).thenReturn(1);
|
||||
when(operations.insert(any(ReceivingContainerOperation.class))).thenReturn(1);
|
||||
ReceivingContainerRequests.Sign request = new ReceivingContainerRequests.Sign(List.of(
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.DOOR_OPEN, 3L),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, 1L),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.CONTAINER_SEAL, 2L)), "sign-key");
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.DOOR_OPEN, "3"),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, "2092476334483931138"),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.CONTAINER_SEAL, "2")), "sign-key");
|
||||
|
||||
var response = service.sign("7", "receiver", request);
|
||||
|
||||
org.assertj.core.api.Assertions.assertThat(response.photos()).extracting("role")
|
||||
.containsExactly(ReceivingPhotoRole.LICENSE_PLATE, ReceivingPhotoRole.CONTAINER_SEAL,
|
||||
ReceivingPhotoRole.DOOR_OPEN);
|
||||
verify(unloadPhotos).validate("100", 8L, 1L);
|
||||
org.assertj.core.api.Assertions.assertThat(response.photos()).extracting("fileId")
|
||||
.containsExactly("2092476334483931138", "2", "3");
|
||||
verify(unloadPhotos).validate("100", 8L, 2092476334483931138L);
|
||||
verify(unloadPhotos).validate("100", 8L, 2L);
|
||||
verify(unloadPhotos).validate("100", 8L, 3L);
|
||||
}
|
||||
@@ -322,9 +324,9 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
void signRejectsDuplicateRolesBeforeFileValidation() {
|
||||
when(sessions.lockActiveByOperator("OPERATOR:7")).thenReturn(session("CONT-A", "WAIT_SIGN"));
|
||||
ReceivingContainerRequests.Sign request = new ReceivingContainerRequests.Sign(List.of(
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, 1L),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, 2L),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.DOOR_OPEN, 3L)), "invalid-sign");
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, "1"),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, "2"),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.DOOR_OPEN, "3")), "invalid-sign");
|
||||
|
||||
assertThatThrownBy(() -> service.sign("7", "receiver", request))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
@@ -345,9 +347,9 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
void signRejectsDuplicateFileIdsBeforeFileValidation() {
|
||||
when(sessions.lockActiveByOperator("OPERATOR:7")).thenReturn(session("CONT-A", "WAIT_SIGN"));
|
||||
ReceivingContainerRequests.Sign request = new ReceivingContainerRequests.Sign(List.of(
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, 1L),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.CONTAINER_SEAL, 1L),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.DOOR_OPEN, 3L)), "invalid-files");
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.LICENSE_PLATE, "1"),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.CONTAINER_SEAL, "1"),
|
||||
new ReceivingContainerRequests.SignedPhoto(ReceivingPhotoRole.DOOR_OPEN, "3")), "invalid-files");
|
||||
|
||||
assertThatThrownBy(() -> service.sign("7", "receiver", request))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
|
||||
Reference in New Issue
Block a user