优化
This commit is contained in:
+13
-2
@@ -5,6 +5,8 @@ import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.ConfirmedQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.TransportUpdateRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentAttachRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentCompleteRequest;
|
||||
import com.baiyue.firstleg.service.LoadingListService;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -32,6 +34,11 @@ class LoadingListControllerContractTest {
|
||||
authority("confirmedPage", "platform:firstleg:loading-list:list", AuthenticatedUser.class,
|
||||
ConfirmedQuery.class);
|
||||
authority("confirmedDetail", "platform:firstleg:loading-list:view", AuthenticatedUser.class, long.class, long.class);
|
||||
authority("declaration", "platform:firstleg:loading-list:view", AuthenticatedUser.class, long.class, long.class);
|
||||
authority("attachDeclaration", "platform:firstleg:customs:manage", AuthenticatedUser.class,
|
||||
long.class, long.class, String.class, DocumentAttachRequest.class);
|
||||
authority("completeDeclaration", "platform:firstleg:customs:manage", AuthenticatedUser.class,
|
||||
long.class, long.class, String.class, DocumentCompleteRequest.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,13 +98,17 @@ class LoadingListControllerContractTest {
|
||||
"/api/platform/v1/first-leg/loading-plans/{planId}",
|
||||
"/api/platform/v1/first-leg/loading-plans/{planId}/transport",
|
||||
"/api/platform/v1/first-leg/loading-lists",
|
||||
"/api/platform/v1/first-leg/loading-lists/{planId}");
|
||||
"/api/platform/v1/first-leg/loading-lists/{planId}",
|
||||
"/api/platform/v1/first-leg/loading-lists/{planId}/customs",
|
||||
"/api/platform/v1/first-leg/loading-lists/{planId}/customs/document",
|
||||
"/api/platform/v1/first-leg/loading-lists/{planId}/customs/complete");
|
||||
Map<String, Object> components = stringMap(document.get("components"));
|
||||
Map<String, Object> schemas = stringMap(components.get("schemas"));
|
||||
assertThat(schemas).containsKeys(
|
||||
"LongId", "PreloadPage", "ConfirmedPage", "Customer", "PreloadSummary",
|
||||
"ConfirmedSummary", "LoadingItem", "OperationHistory", "PreloadDetail",
|
||||
"ConfirmedDetail", "TransportUpdateRequest");
|
||||
"ConfirmedDetail", "TransportUpdateRequest", "BusinessDocumentEnvelope",
|
||||
"DocumentAttachRequest", "DocumentCompleteRequest", "BusinessDocument");
|
||||
assertThat(paths)
|
||||
.doesNotContainKey("/api/platform/v1/first-leg/loading-plans/{planId}/complete");
|
||||
assertThat(stringMap(schemas.get("LongId"))).containsEntry("type", "string");
|
||||
|
||||
@@ -6,6 +6,11 @@ import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.ConfirmedQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.PreloadQuery;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.TransportUpdateRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentAttachRequest;
|
||||
import com.baiyue.firstleg.dto.LoadingListDtos.DocumentCompleteRequest;
|
||||
import com.baiyue.firstleg.client.LoadingDocumentFileClient;
|
||||
import com.baiyue.firstleg.entity.ContainerDocumentEntity;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.AggregateRow;
|
||||
import com.baiyue.firstleg.mapper.LoadingListQueryMapper.HistoryRow;
|
||||
import com.baiyue.firstleg.repository.LoadingListRepository;
|
||||
@@ -38,6 +43,8 @@ class LoadingListServiceTest {
|
||||
private LoadingListRepository repository;
|
||||
@MockBean
|
||||
private LoadingPickRepository pickRepository;
|
||||
@MockBean
|
||||
private LoadingDocumentFileClient documentFiles;
|
||||
@Resource
|
||||
private LoadingListService service;
|
||||
|
||||
@@ -189,6 +196,116 @@ class LoadingListServiceTest {
|
||||
.isEqualTo(ResultCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void attachesRealContainerClearanceFileAndWritesImmutableHistory() {
|
||||
AggregateRow plan = row("LOADING", 2);
|
||||
plan.setContainerBookingId(30L);
|
||||
when(repository.aggregateByBooking(30L)).thenReturn(plan);
|
||||
when(repository.history(anyString())).thenReturn(null);
|
||||
when(repository.document(30L, LoadingListService.DESTINATION_CLEARANCE))
|
||||
.thenReturn(null, document(40L, LoadingListService.DESTINATION_CLEARANCE, 0, false));
|
||||
when(documentFiles.metadata(anyString(), eq(0L), eq("CONTAINER_CLEARANCE_DOCUMENT"),
|
||||
eq("30"), eq(99L))).thenReturn(ApiResponse.success(new LoadingDocumentFileClient.Metadata(
|
||||
99L, 0L, "CONTAINER_CLEARANCE_DOCUMENT", "30", "clearance.pdf", "abc", "application/pdf", 20L)));
|
||||
when(repository.createDocument(org.mockito.ArgumentMatchers.any())).thenReturn(1);
|
||||
|
||||
var result = service.attachContainerClearance(30L, 9L, "operator", "key-clearance",
|
||||
new DocumentAttachRequest(99L, 0, "上传清关资料"));
|
||||
|
||||
assertThat(result.fileName()).isEqualTo("clearance.pdf");
|
||||
verify(repository).appendHistory(eq(10L), anyString(), eq("ATTACH_DESTINATION_CLEARANCE"),
|
||||
eq("LOADING"), eq("LOADING"), anyString(), anyString(), eq(9L), eq("operator"),
|
||||
eq("上传清关资料"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsDeclarationBeforeLoadingListIsCompleted() {
|
||||
AggregateRow plan = row("LOADING", 2);
|
||||
plan.setContainerBookingId(30L);
|
||||
when(repository.aggregate(10L)).thenReturn(plan);
|
||||
|
||||
assertThatThrownBy(() -> service.attachLoadingDeclaration(10L, 100L, 9L, "operator", "key",
|
||||
new DocumentAttachRequest(99L, 0, null)))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode())
|
||||
.isEqualTo(ResultCode.CONFLICT);
|
||||
verify(documentFiles, never()).metadata(anyString(), eq(0L), anyString(), anyString(), eq(99L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsFileMetadataFromAnotherBusinessResource() {
|
||||
AggregateRow plan = row("LOADING", 2);
|
||||
plan.setContainerBookingId(30L);
|
||||
when(repository.aggregateByBooking(30L)).thenReturn(plan);
|
||||
when(repository.history(anyString())).thenReturn(null);
|
||||
when(repository.document(30L, LoadingListService.DESTINATION_CLEARANCE)).thenReturn(null);
|
||||
when(documentFiles.metadata(anyString(), eq(0L), eq("CONTAINER_CLEARANCE_DOCUMENT"),
|
||||
eq("30"), eq(99L))).thenReturn(ApiResponse.success(new LoadingDocumentFileClient.Metadata(
|
||||
99L, 0L, "CONTAINER_CLEARANCE_DOCUMENT", "different-booking", "clearance.pdf", "abc",
|
||||
"application/pdf", 20L)));
|
||||
|
||||
assertThatThrownBy(() -> service.attachContainerClearance(30L, 9L, "operator", "key",
|
||||
new DocumentAttachRequest(99L, 0, null)))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode())
|
||||
.isEqualTo(ResultCode.BAD_REQUEST);
|
||||
verify(repository, never()).createDocument(org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void completesDeclarationOnlyWhenDocumentVersionMatches() {
|
||||
AggregateRow plan = row("COMPLETED", 2);
|
||||
plan.setContainerBookingId(30L);
|
||||
ContainerDocumentEntity before = document(40L, LoadingListService.CUSTOMS_DECLARATION, 3, false);
|
||||
ContainerDocumentEntity after = document(40L, LoadingListService.CUSTOMS_DECLARATION, 4, true);
|
||||
when(repository.aggregate(10L)).thenReturn(plan);
|
||||
when(repository.history(anyString())).thenReturn(null);
|
||||
when(repository.document(30L, LoadingListService.CUSTOMS_DECLARATION)).thenReturn(before, after);
|
||||
when(repository.completeDocument(40L, 3, 9L, "operator")).thenReturn(1);
|
||||
|
||||
var result = service.completeLoadingDeclaration(10L, 100L, 9L, "operator", "complete-key",
|
||||
new DocumentCompleteRequest(3, "点击报关"));
|
||||
|
||||
assertThat(result.completedAt()).isNotNull();
|
||||
verify(repository).appendHistory(eq(10L), anyString(), eq("COMPLETE_CUSTOMS_DECLARATION"),
|
||||
eq("COMPLETED"), eq("COMPLETED"), anyString(), anyString(), eq(9L), eq("operator"),
|
||||
eq("点击报关"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsCompleteWithoutUploadedFile() {
|
||||
AggregateRow plan = row("LOADING", 2);
|
||||
plan.setContainerBookingId(30L);
|
||||
when(repository.aggregateByBooking(30L)).thenReturn(plan);
|
||||
when(repository.history(anyString())).thenReturn(null);
|
||||
when(repository.document(30L, LoadingListService.DESTINATION_CLEARANCE)).thenReturn(null);
|
||||
|
||||
assertThatThrownBy(() -> service.completeContainerClearance(30L, 9L, "operator", "key",
|
||||
new DocumentCompleteRequest(0, null)))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode())
|
||||
.isEqualTo(ResultCode.CONFLICT);
|
||||
verify(repository, never()).completeDocument(org.mockito.ArgumentMatchers.anyLong(),
|
||||
org.mockito.ArgumentMatchers.anyInt(), eq(9L), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void repeatedCompleteWithCurrentVersionIsNaturallyIdempotent() {
|
||||
AggregateRow plan = row("LOADING", 2);
|
||||
plan.setContainerBookingId(30L);
|
||||
ContainerDocumentEntity completed = document(40L, LoadingListService.DESTINATION_CLEARANCE, 4, true);
|
||||
when(repository.aggregateByBooking(30L)).thenReturn(plan);
|
||||
when(repository.history(anyString())).thenReturn(null);
|
||||
when(repository.document(30L, LoadingListService.DESTINATION_CLEARANCE)).thenReturn(completed);
|
||||
|
||||
var result = service.completeContainerClearance(30L, 9L, "operator", "new-key",
|
||||
new DocumentCompleteRequest(4, null));
|
||||
|
||||
assertThat(result.completedAt()).isNotNull();
|
||||
verify(repository, never()).completeDocument(org.mockito.ArgumentMatchers.anyLong(),
|
||||
org.mockito.ArgumentMatchers.anyInt(), eq(9L), anyString());
|
||||
}
|
||||
|
||||
private static AggregateRow row(String status, int version) {
|
||||
AggregateRow row = new AggregateRow();
|
||||
row.setPlanId(10L);
|
||||
@@ -198,4 +315,14 @@ class LoadingListServiceTest {
|
||||
row.setVersion(version);
|
||||
return row;
|
||||
}
|
||||
|
||||
private static ContainerDocumentEntity document(long id, String type, int version, boolean completed) {
|
||||
ContainerDocumentEntity value = new ContainerDocumentEntity();
|
||||
value.setId(id); value.setContainerBookingId(30L); value.setDocumentType(type);
|
||||
value.setFileId(99L); value.setFileName("clearance.pdf"); value.setFileHash("abc");
|
||||
value.setUploadedBy(9L); value.setUploadedByName("operator"); value.setVersion(version);
|
||||
value.setCreatedAt(java.time.LocalDateTime.now());
|
||||
if (completed) value.setCompletedAt(java.time.LocalDateTime.now());
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user