From 5c094fd0fd921bfa358e9c5549b504d2533254c5 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Fri, 4 Sep 2026 12:23:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/LoadingDeliveryService.java | 6 +++ .../firstleg/service/FbaPricingService.java | 17 ++++++++ .../service/LoadingDeliveryServiceTest.java | 4 ++ .../service/FbaPricingServiceTest.java | 43 +++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java b/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java index 1222debdf..7ad30b3c8 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/loading/service/LoadingDeliveryService.java @@ -15,6 +15,7 @@ import com.baiyue.firstleg.loading.mapper.LoadingInboundMapper.CompletionTarget; import com.baiyue.firstleg.loading.bridge.service.LoadingModelBridgeService; import com.baiyue.firstleg.loading.repository.LoadingRepository; import com.baiyue.firstleg.repository.LoadingPickRepository; +import com.baiyue.firstleg.service.FbaPricingService; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; @@ -40,6 +41,7 @@ public class LoadingDeliveryService { @Resource private LoadingCompletionGateService completionGate; @Resource private InboundStatusHistoryMapper histories; @Resource private CabinetLoadingEvidenceService cabinetEvidenceService; + @Resource private FbaPricingService pricingService; @Resource private ObjectMapper objectMapper; @Transactional @@ -64,6 +66,7 @@ public class LoadingDeliveryService { } if ("LOADED".equals(inbound.processStatus()) || "DELIVERED".equals(inbound.processStatus())) { requireLoadingPlanGate(tenantId, inboundId, acceptedPdaCompletion); + pricingService.autoConfirmAfterLoading(tenantId, inboundId); return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, new LoadingActionResponse(inbound.processStatus())); } @@ -74,6 +77,7 @@ public class LoadingDeliveryService { requireCabinetEvidence(tenantId, inboundId); if (repository.completeLoading(tenantId, inboundId, operatorId, request.expectedVersion()) != 1) throw concurrent(); addHistory(tenantId, inboundId, "LOADING", "LOADED", COMPLETE_LOADING, request.remark(), operatorId, operatorName); + pricingService.autoConfirmAfterLoading(tenantId, inboundId); return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, new LoadingActionResponse("LOADED")); } @@ -116,6 +120,7 @@ public class LoadingDeliveryService { CompletionTarget target, boolean acceptedPdaCompletion) { if ("LOADED".equals(target.loadedChildStatus())) { + pricingService.autoConfirmAfterLoading(tenantId, target.completionInboundId()); return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, new LoadingActionResponse("LOADED")); } @@ -131,6 +136,7 @@ public class LoadingDeliveryService { repository.aggregateRootLoaded(tenantId, target.rootInboundId(), operatorId); addHistory(tenantId, target.completionInboundId(), "LOADING", "LOADED", COMPLETE_LOADING, request.remark(), operatorId, operatorName); + pricingService.autoConfirmAfterLoading(tenantId, target.completionInboundId()); return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, new LoadingActionResponse("LOADED")); } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/FbaPricingService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/FbaPricingService.java index 757d74779..5a36673a5 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/FbaPricingService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/FbaPricingService.java @@ -35,6 +35,8 @@ import java.util.List; @Service public class FbaPricingService { private static final int MONEY_SCALE = 2; + private static final long AUTOMATIC_CONFIRM_OPERATOR_ID = 0L; + private static final String AUTOMATIC_CONFIRM_OPERATOR_NAME = "装柜完成自动确认"; @Resource private FbaPricingRepository repository; @Resource @@ -213,6 +215,21 @@ public class FbaPricingService { return response(tenantId, fee.getId(), null); } + /** 客户未操作时,装柜完成以系统身份确认最新一版已发送费用。 */ + @Transactional + public void autoConfirmAfterLoading(long tenantId, long inboundId) { + InboundFeeEntity current = repository.findLatestFeeForUpdate(tenantId, inboundId); + if (current == null || !"SENT".equals(current.getFeeStatus()) + || current.getCustomerConfirmedAt() != null) { + return; + } + LocalDateTime now = LocalDateTime.now(clock); + if (repository.confirmFeeByCustomer(tenantId, current.getId(), current.getFeeVersion(), + AUTOMATIC_CONFIRM_OPERATOR_ID, AUTOMATIC_CONFIRM_OPERATOR_NAME, now) != 1) { + throw conflict("装柜完成自动确认费用失败,请重试"); + } + } + @Transactional public FeeSnapshotResponse confirmByCustomer(long tenantId, long operatorId, String operatorName, long inboundId, int expectedFeeVersion, diff --git a/firstleg/src/test/java/com/baiyue/firstleg/loading/service/LoadingDeliveryServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/loading/service/LoadingDeliveryServiceTest.java index ff9e7681c..facb75365 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/loading/service/LoadingDeliveryServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/loading/service/LoadingDeliveryServiceTest.java @@ -6,6 +6,7 @@ import com.baiyue.firstleg.loading.repository.LoadingRepository; import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper; import com.baiyue.firstleg.mapper.LoadingPickItemMapper.InboundProgress; import com.baiyue.firstleg.repository.LoadingPickRepository; +import com.baiyue.firstleg.service.FbaPricingService; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; @@ -27,6 +28,7 @@ class LoadingDeliveryServiceTest { @MockBean LoadingCompletionGateService completionGate; @MockBean InboundStatusHistoryMapper histories; @MockBean CabinetLoadingEvidenceService cabinetEvidence; + @MockBean FbaPricingService pricingService; @MockBean ObjectMapper objectMapper; @Resource LoadingDeliveryService service; @@ -46,6 +48,7 @@ class LoadingDeliveryServiceTest { new com.baiyue.firstleg.loading.dto.LoadingRequests.CompleteLoadingRequest(3, "done")); verify(cabinetEvidence).requireComplete(99L); + verify(pricingService).autoConfirmAfterLoading(10L, 20L); } @Test @@ -86,6 +89,7 @@ class LoadingDeliveryServiceTest { verify(repository).findInboundForUpdate(10L, 21L); verify(repository).completeLoadedChild(10L, 20L, 9L, 9); + verify(pricingService).autoConfirmAfterLoading(10L, 21L); } @Test diff --git a/firstleg/src/test/java/com/baiyue/firstleg/service/FbaPricingServiceTest.java b/firstleg/src/test/java/com/baiyue/firstleg/service/FbaPricingServiceTest.java index afc86c84c..19954f276 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/service/FbaPricingServiceTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/service/FbaPricingServiceTest.java @@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; @@ -69,6 +70,48 @@ class FbaPricingServiceTest { verify(repository, never()).findLatestFee(TENANT, INBOUND); } + @Test + @DisplayName("装柜完成后系统自动确认客户未确认的已发送费用") + void automaticallyConfirmsSentFeeAfterLoading() { + InboundFeeEntity sent = savedFee("SENT"); + when(repository.findLatestFeeForUpdate(TENANT, INBOUND)).thenReturn(sent); + when(repository.confirmFeeByCustomer(TENANT, 501L, 1, 0L, + "装柜完成自动确认", NOW)).thenReturn(1); + + service.autoConfirmAfterLoading(TENANT, INBOUND); + + verify(repository).confirmFeeByCustomer(TENANT, 501L, 1, 0L, + "装柜完成自动确认", NOW); + } + + @Test + @DisplayName("客户已经确认时装柜完成不覆盖原确认信息") + void keepsExistingCustomerConfirmationAfterLoading() { + InboundFeeEntity sent = savedFee("SENT"); + sent.setCustomerConfirmedBy(88L); + sent.setCustomerConfirmedByName("客户成员"); + sent.setCustomerConfirmedAt(NOW.minusHours(1)); + when(repository.findLatestFeeForUpdate(TENANT, INBOUND)).thenReturn(sent); + + service.autoConfirmAfterLoading(TENANT, INBOUND); + + verify(repository, never()).confirmFeeByCustomer(anyLong(), anyLong(), anyInt(), + anyLong(), anyString(), any()); + } + + @Test + @DisplayName("没有已发送费用时装柜完成不受影响") + void ignoresMissingOrUnsentFeeAfterLoading() { + when(repository.findLatestFeeForUpdate(TENANT, INBOUND)) + .thenReturn(null, savedFee("CONFIRMED")); + + service.autoConfirmAfterLoading(TENANT, INBOUND); + service.autoConfirmAfterLoading(TENANT, INBOUND); + + verify(repository, never()).confirmFeeByCustomer(anyLong(), anyLong(), anyInt(), + anyLong(), anyString(), any()); + } + @Test @DisplayName("包税KG按生效价格表计费且不依赖客户规则或承运商") void calculatesTaxIncludedKgWithoutCustomerRuleOrCarrier() {