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 aa064081f..92909fb6a 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 @@ -16,6 +16,7 @@ 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.InboundHoldGuard; +import com.baiyue.firstleg.service.FbaPricingService; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; @@ -43,6 +44,7 @@ public class LoadingDeliveryService { @Resource private CabinetLoadingEvidenceService cabinetEvidenceService; @Resource private ObjectMapper objectMapper; @Resource private InboundHoldGuard inboundHoldGuard; + @Resource private FbaPricingService fbaPricingService; @Transactional public LoadingActionResponse completeLoading(long tenantId, long inboundId, long operatorId, @@ -67,6 +69,7 @@ public class LoadingDeliveryService { } if ("LOADED".equals(inbound.processStatus()) || "DELIVERED".equals(inbound.processStatus())) { requireLoadingPlanGate(tenantId, inboundId, acceptedPdaCompletion); + fbaPricingService.autoSendAfterLoadingCompleted(tenantId, inboundId); return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, new LoadingActionResponse(inbound.processStatus())); } @@ -77,6 +80,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); + fbaPricingService.autoSendAfterLoadingCompleted(tenantId, inboundId); return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, new LoadingActionResponse("LOADED")); } @@ -120,6 +124,7 @@ public class LoadingDeliveryService { CompletionTarget target, boolean acceptedPdaCompletion) { if ("LOADED".equals(target.loadedChildStatus())) { + fbaPricingService.autoSendAfterLoadingCompleted(tenantId, inboundId); return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, new LoadingActionResponse("LOADED")); } @@ -135,6 +140,7 @@ public class LoadingDeliveryService { repository.aggregateRootLoaded(tenantId, target.rootInboundId(), operatorId); addHistory(tenantId, target.completionInboundId(), "LOADING", "LOADED", COMPLETE_LOADING, request.remark(), operatorId, operatorName); + fbaPricingService.autoSendAfterLoadingCompleted(tenantId, inboundId); 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 e27119f62..b81e9ce57 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/FbaPricingService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/FbaPricingService.java @@ -439,6 +439,25 @@ public class FbaPricingService { return response(tenantId, current.getId(), nextProcessStatus); } + /** + * 装柜完成是客户费用的最后自动推送节点。人工已发送时直接幂等返回; + * 仍为初算时先自动确认,再复用正式发送状态机,确保费用明细和 Finance outbox 只生成一次。 + */ + @Transactional + public void autoSendAfterLoadingCompleted(long tenantId, long inboundId) { + InboundPricingEntity inbound = repository.findInbound(tenantId, inboundId); + if (inbound == null) return; + InboundFeeEntity current = repository.findLatestFeeForUpdate(tenantId, inboundId); + if (current == null || "SENT".equals(current.getFeeStatus())) return; + String prefix = "loading-completed-" + inboundId; + if ("CALCULATED".equals(current.getFeeStatus())) { + confirm(tenantId, AUTOMATIC_CONFIRM_OPERATOR_ID, inboundId, prefix + "-confirm"); + } else if (!"CONFIRMED".equals(current.getFeeStatus())) { + return; + } + send(tenantId, AUTOMATIC_CONFIRM_OPERATOR_ID, inboundId, prefix + "-send"); + } + public FeeSnapshotResponse latest(long tenantId, long inboundId) { InboundFeeEntity fee = repository.findLatestFee(tenantId, inboundId); if (fee == null) throw new BizException(ResultCode.NOT_FOUND, "费用快照不存在"); 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 49977142c..4a6763a34 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 @@ -7,6 +7,7 @@ import com.baiyue.firstleg.mapper.InboundStatusHistoryMapper; import com.baiyue.firstleg.mapper.LoadingPickItemMapper.InboundProgress; import com.baiyue.firstleg.repository.LoadingPickRepository; import com.baiyue.firstleg.service.InboundHoldGuard; +import com.baiyue.firstleg.service.FbaPricingService; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; @@ -30,6 +31,7 @@ class LoadingDeliveryServiceTest { @MockBean CabinetLoadingEvidenceService cabinetEvidence; @MockBean ObjectMapper objectMapper; @MockBean InboundHoldGuard inboundHoldGuard; + @MockBean FbaPricingService fbaPricingService; @Resource LoadingDeliveryService service; @Test @@ -48,6 +50,7 @@ class LoadingDeliveryServiceTest { new com.baiyue.firstleg.loading.dto.LoadingRequests.CompleteLoadingRequest(3, "done")); verify(cabinetEvidence).requireComplete(99L); + verify(fbaPricingService).autoSendAfterLoadingCompleted(10L, 20L); } @Test