优化
This commit is contained in:
@@ -3,7 +3,6 @@ package com.baiyue.firstleg.client;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
@@ -30,12 +29,6 @@ public interface LoadingDocumentFileClient {
|
||||
ResponseEntity<byte[]> download(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@PathVariable long tenantId,@PathVariable String businessType,@PathVariable String businessId,@PathVariable long fileId);
|
||||
|
||||
@DeleteMapping("/{tenantId}/{businessType}/{businessId}/{fileId}")
|
||||
ApiResponse<Void> delete(@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@RequestHeader("X-Operator-Id") long operatorId,
|
||||
@PathVariable long tenantId, @PathVariable String businessType,
|
||||
@PathVariable String businessId, @PathVariable long fileId);
|
||||
|
||||
record Metadata(Long fileId, Long tenantId, String businessType, String businessId,
|
||||
String fileName, String sha256, String contentType, Long sizeBytes) {}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
@@ -52,7 +50,6 @@ import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class LoadingListService {
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadingListService.class);
|
||||
private static final Set<String> PRELOAD_STATUSES = Set.of("EDITING", "LOADING");
|
||||
public static final String DESTINATION_CLEARANCE = "DESTINATION_CLEARANCE";
|
||||
public static final String CUSTOMS_DECLARATION = "CUSTOMS_DECLARATION";
|
||||
@@ -321,16 +318,8 @@ public class LoadingListService {
|
||||
requireReplaceableDocument(current, expectedVersion);
|
||||
LoadingDocumentFileClient.Metadata metadata = uploadDocumentFile(
|
||||
businessType, businessId, operatorId, file, fileHash);
|
||||
try {
|
||||
return persistDocument(plan, documentType, current, metadata, operatorId, operatorName,
|
||||
operationId, "UPLOAD_" + documentType, requestHash, operationReason);
|
||||
} catch (RuntimeException exception) {
|
||||
compensateUploadedFile(businessType, businessId, metadata.fileId(), operatorId);
|
||||
if (exception instanceof DataIntegrityViolationException) {
|
||||
throw conflict("资料状态或数据库约束已变化,请刷新后重试");
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
return persistDocument(plan, documentType, current, metadata, operatorId, operatorName,
|
||||
operationId, "UPLOAD_" + documentType, requestHash, operationReason);
|
||||
}
|
||||
|
||||
private BusinessDocument persistDocument(AggregateRow plan, String documentType,
|
||||
@@ -432,19 +421,6 @@ public class LoadingListService {
|
||||
}
|
||||
}
|
||||
|
||||
private void compensateUploadedFile(String businessType, String businessId, long fileId, long operatorId) {
|
||||
try {
|
||||
var response = documentFiles.delete(internalToken, operatorId, 0, businessType, businessId, fileId);
|
||||
if (response == null || !response.successful()) {
|
||||
log.error("Business file compensation failed: type={}, businessId={}, fileId={}",
|
||||
businessType, businessId, fileId);
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
log.error("Business file compensation request failed: type={}, businessId={}, fileId={}",
|
||||
businessType, businessId, fileId, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void requireReplaceableDocument(ContainerDocumentEntity current, int expectedVersion) {
|
||||
if (current == null && expectedVersion != 0) throw conflict("资料版本已变化,请刷新后重试");
|
||||
if (current != null && !java.util.Objects.equals(current.getVersion(), expectedVersion)) {
|
||||
|
||||
@@ -625,7 +625,6 @@ class LoadingListServiceTest {
|
||||
.containsExactly(LoadingListService.DELIVERY_ORDER, 101L, 0);
|
||||
verify(repository).appendHistory(eq(10L), anyString(), eq("UPLOAD_DELIVERY_ORDER"),
|
||||
eq("COMPLETED"), eq("COMPLETED"), anyString(), anyString(), eq(9L), eq("operator"), isNull());
|
||||
verify(documentFiles, never()).delete(anyString(), anyLong(), anyLong(), anyString(), anyString(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -694,33 +693,6 @@ class LoadingListServiceTest {
|
||||
verify(documentFiles, never()).upload(anyString(), anyLong(), anyLong(), anyString(), anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void databaseFailureAfterFileUploadTriggersExactOwnershipCompensationAndBusinessConflict() {
|
||||
AggregateRow plan = row("COMPLETED", 4);
|
||||
byte[] content = "delivery-order".getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
var file = new MockMultipartFile("file", "do.pdf", "application/pdf", content);
|
||||
String contentHash = sha256Bytes(content);
|
||||
var metadata = new LoadingDocumentFileClient.Metadata(103L, 0L,
|
||||
"CONTAINER_DELIVERY_ORDER", "30", "do.pdf", contentHash, "application/pdf", (long) content.length);
|
||||
when(repository.aggregateByBooking(30L)).thenReturn(plan);
|
||||
when(repository.history(anyString())).thenReturn(null);
|
||||
when(repository.document(30L, LoadingListService.DELIVERY_ORDER)).thenReturn(null);
|
||||
when(documentFiles.upload(anyString(), eq(9L), eq(0L), eq("CONTAINER_DELIVERY_ORDER"),
|
||||
eq("30"), eq(file))).thenReturn(ApiResponse.success(metadata));
|
||||
when(documentFiles.metadata(anyString(), eq(0L), eq("CONTAINER_DELIVERY_ORDER"),
|
||||
eq("30"), eq(103L))).thenReturn(ApiResponse.success(metadata));
|
||||
when(repository.createDocument(any())).thenThrow(
|
||||
new org.springframework.dao.DataIntegrityViolationException("trigger rejected"));
|
||||
when(documentFiles.delete(anyString(), eq(9L), eq(0L), eq("CONTAINER_DELIVERY_ORDER"),
|
||||
eq("30"), eq(103L))).thenReturn(ApiResponse.success(null));
|
||||
|
||||
assertThatThrownBy(() -> service.uploadContainerLogisticsDocument(30L,
|
||||
LoadingListService.DELIVERY_ORDER, 9L, "operator", "do-db-fail", 0, null, file))
|
||||
.isInstanceOf(BizException.class)
|
||||
.extracting(error -> ((BizException) error).getResultCode()).isEqualTo(ResultCode.CONFLICT);
|
||||
verify(documentFiles).delete(anyString(), eq(9L), eq(0L), eq("CONTAINER_DELIVERY_ORDER"),
|
||||
eq("30"), eq(103L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void confirmsEveryLinkedPreloadAndCreatesSinglePdaTaskAtomically() {
|
||||
|
||||
Reference in New Issue
Block a user