fix(firstleg): share container evidence across loading owners
This commit is contained in:
+21
-5
@@ -97,13 +97,19 @@ public class LoadingDeliveryService {
|
||||
@Transactional
|
||||
public LoadingActionResponse completeLoading(long tenantId, long inboundId, long operatorId,
|
||||
String operatorName, String key, CompleteLoadingRequest request) {
|
||||
return completeLoading(tenantId, inboundId, inboundId, operatorId, operatorName, key, request);
|
||||
}
|
||||
|
||||
private LoadingActionResponse completeLoading(long tenantId, long inboundId, long evidenceInboundId,
|
||||
long operatorId, String operatorName, String key,
|
||||
CompleteLoadingRequest request) {
|
||||
String hash = hash(request);
|
||||
LoadingActionResponse replay = claimOrReplay(tenantId, inboundId, operatorId, COMPLETE_LOADING, key, hash);
|
||||
if (replay != null) return replay;
|
||||
Snapshot inbound = requireInbound(tenantId, inboundId);
|
||||
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
||||
if (target != null) {
|
||||
return completeFulfillmentSlice(tenantId, inboundId, operatorId, operatorName, key, request, target);
|
||||
return completeFulfillmentSlice(tenantId, evidenceInboundId, operatorId, operatorName, key, request, target);
|
||||
}
|
||||
if ("LOADED".equals(inbound.processStatus()) || "DELIVERED".equals(inbound.processStatus())) {
|
||||
loadingModelBridge.requireCompleted(tenantId, inboundId);
|
||||
@@ -114,7 +120,7 @@ public class LoadingDeliveryService {
|
||||
loadingModelBridge.requireCompleted(tenantId, inboundId);
|
||||
completionGate.requireReleasedBooking(tenantId, inboundId);
|
||||
requireCompletedPicking(tenantId, inboundId);
|
||||
requireCompletionEvidence(tenantId, inboundId);
|
||||
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||
if (repository.completeLoading(tenantId, inboundId, operatorId, request.expectedVersion()) != 1) throw concurrent();
|
||||
addHistory(tenantId, inboundId, "LOADING", "LOADED", COMPLETE_LOADING, request.remark(), operatorId, operatorName);
|
||||
return complete(tenantId, inboundId, operatorId, COMPLETE_LOADING, key,
|
||||
@@ -129,13 +135,19 @@ public class LoadingDeliveryService {
|
||||
public LoadingActionResponse completeAfterPdaSettlement(long tenantId, long inboundId,
|
||||
long operatorId, String operatorName,
|
||||
String key, String remark) {
|
||||
return completeAfterPdaSettlement(tenantId, inboundId, inboundId, operatorId, operatorName, key, remark);
|
||||
}
|
||||
|
||||
public LoadingActionResponse completeAfterPdaSettlement(long tenantId, long inboundId,
|
||||
long evidenceInboundId, long operatorId,
|
||||
String operatorName, String key, String remark) {
|
||||
CompletionTarget target = repository.completionTarget(tenantId, inboundId);
|
||||
long completionInboundId = target == null ? inboundId : target.completionInboundId();
|
||||
Snapshot current = repository.findInboundForUpdate(tenantId, completionInboundId);
|
||||
if (current == null || current.version() == null) {
|
||||
throw new BizException(ResultCode.CONFLICT, "PDA结算后的实装履约分片不存在,请刷新后重试");
|
||||
}
|
||||
return completeLoading(tenantId, inboundId, operatorId, operatorName, key,
|
||||
return completeLoading(tenantId, inboundId, evidenceInboundId, operatorId, operatorName, key,
|
||||
new CompleteLoadingRequest(current.version(), remark));
|
||||
}
|
||||
|
||||
@@ -144,13 +156,17 @@ public class LoadingDeliveryService {
|
||||
* every external/business hard gate must pass before the PDA task is mutated.
|
||||
*/
|
||||
public void requirePdaCompletionReady(long tenantId, long inboundId) {
|
||||
requirePdaCompletionReady(tenantId, inboundId, inboundId);
|
||||
}
|
||||
|
||||
public void requirePdaCompletionReady(long tenantId, long inboundId, long evidenceInboundId) {
|
||||
Snapshot inbound = requireInbound(tenantId, inboundId);
|
||||
if (!"LOADING".equals(inbound.processStatus())) {
|
||||
throw new BizException(ResultCode.CONFLICT, "PDA确认装柜完成前订单必须处于LOADING");
|
||||
}
|
||||
loadingModelBridge.requireStarted(tenantId, inboundId);
|
||||
completionGate.requireReleasedBooking(tenantId, inboundId);
|
||||
requireCompletionEvidence(tenantId, inboundId);
|
||||
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||
}
|
||||
|
||||
private LoadingActionResponse completeFulfillmentSlice(long tenantId, long evidenceInboundId,
|
||||
@@ -167,7 +183,7 @@ public class LoadingDeliveryService {
|
||||
loadingModelBridge.requireCompleted(tenantId, target.sourceInboundId());
|
||||
completionGate.requireReleasedBooking(tenantId, target.sourceInboundId());
|
||||
requireCompletedPicking(tenantId, target.sourceInboundId());
|
||||
requireCompletionEvidence(tenantId, target.sourceInboundId());
|
||||
requireCompletionEvidence(tenantId, evidenceInboundId);
|
||||
if (repository.completeLoadedChild(tenantId, target.sourceInboundId(), operatorId,
|
||||
request.expectedVersion()) != 1) throw concurrent();
|
||||
repository.aggregateRootLoaded(tenantId, target.rootInboundId(), operatorId);
|
||||
|
||||
@@ -107,10 +107,12 @@ public interface LoadingPickItemMapper extends BaseMapper<LoadingPickItemEntity>
|
||||
}
|
||||
|
||||
@Select("""
|
||||
SELECT DISTINCT plan_item.account_id tenantId,plan_item.inbound_id inboundId
|
||||
SELECT plan_item.account_id tenantId,plan_item.inbound_id inboundId
|
||||
FROM fl_loading_pick_item pick_item
|
||||
JOIN fl_loading_plan_item plan_item ON plan_item.id=pick_item.loading_plan_item_id
|
||||
WHERE pick_item.pick_task_id=#{taskId}
|
||||
GROUP BY plan_item.account_id,plan_item.inbound_id
|
||||
ORDER BY MIN(pick_item.id)
|
||||
""")
|
||||
java.util.List<InboundOwner> selectInboundOwners(@Param("taskId") long taskId);
|
||||
|
||||
|
||||
@@ -450,16 +450,20 @@ public class LoadingPickService {
|
||||
throw conflict("存在尚未处理的PDA拣货明细,不能确认装柜完成");
|
||||
}
|
||||
if (repository.review(taskId) != null) throw conflict("PDA任务已经确认装柜完成");
|
||||
List<LoadingPickItemMapper.InboundOwner> owners = repository.inboundOwners(taskId).stream()
|
||||
List<LoadingPickItemMapper.InboundOwner> discoveredOwners = repository.inboundOwners(taskId).stream()
|
||||
.filter(owner -> owner.tenantId() != null && owner.inboundId() != null)
|
||||
.toList();
|
||||
if (discoveredOwners.isEmpty()) throw conflict("PDA任务未关联有效入库单");
|
||||
LoadingPickItemMapper.InboundOwner evidenceOwner = discoveredOwners.get(0);
|
||||
List<LoadingPickItemMapper.InboundOwner> owners = discoveredOwners.stream()
|
||||
.sorted(Comparator.comparing(LoadingPickItemMapper.InboundOwner::tenantId)
|
||||
.thenComparing(LoadingPickItemMapper.InboundOwner::inboundId))
|
||||
.toList();
|
||||
if (owners.isEmpty()) throw conflict("PDA任务未关联有效入库单");
|
||||
for (LoadingPickItemMapper.InboundOwner owner : owners) {
|
||||
LoadingInboundMapper.Snapshot snapshot = repository.inboundForUpdate(owner.tenantId(), owner.inboundId());
|
||||
if (snapshot == null) throw conflict("PDA任务关联入库单不存在");
|
||||
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId());
|
||||
loadingDeliveryService.requirePdaCompletionReady(owner.tenantId(), owner.inboundId(),
|
||||
evidenceOwner.inboundId());
|
||||
}
|
||||
settlementService.settle(taskId, operatorId, operatorName);
|
||||
if (!repository.allPlanItemsPicked(task.getLoadingPlanId())) {
|
||||
@@ -472,8 +476,9 @@ public class LoadingPickService {
|
||||
loadingListService.finishFromPda(task.getLoadingPlanId(), taskId, operatorId, operatorName);
|
||||
repository.syncInboundProjections(taskId, operatorId, operatorName);
|
||||
for (LoadingPickItemMapper.InboundOwner owner : owners) {
|
||||
loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(), operatorId,
|
||||
operatorName, "PDA-" + operationId + '-' + owner.inboundId(), remark);
|
||||
loadingDeliveryService.completeAfterPdaSettlement(owner.tenantId(), owner.inboundId(),
|
||||
evidenceOwner.inboundId(), operatorId, operatorName,
|
||||
"PDA-" + operationId + '-' + owner.inboundId(), remark);
|
||||
}
|
||||
loadingListService.publishCompletedEvent(task.getLoadingPlanId());
|
||||
completeTruckAfterCommit(task.getContainerBookingId(), taskId, operatorId, operatorName);
|
||||
|
||||
@@ -508,9 +508,12 @@ class LoadingPickServiceTest {
|
||||
when(repository.allPlanItemsPicked(10L)).thenReturn(true);
|
||||
when(repository.approve(20L, 9L, "operator", "checked")).thenReturn(1);
|
||||
var owner = new LoadingPickItemMapper.InboundOwner(101L, 50L);
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(owner));
|
||||
var incrementalOwner = new LoadingPickItemMapper.InboundOwner(101L, 60L);
|
||||
when(repository.inboundOwners(20L)).thenReturn(List.of(owner, incrementalOwner));
|
||||
when(repository.inboundForUpdate(101L, 50L)).thenReturn(
|
||||
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
||||
when(repository.inboundForUpdate(101L, 60L)).thenReturn(
|
||||
new LoadingInboundMapper.Snapshot(60L, 101L, "LOADING", 3));
|
||||
|
||||
service.complete(20L, new CompleteLoadingRequest(0, "complete-1", "checked"), 9L, "operator");
|
||||
service.complete(20L, new CompleteLoadingRequest(0, "complete-1", "checked"), 9L, "operator");
|
||||
@@ -519,14 +522,17 @@ class LoadingPickServiceTest {
|
||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(settlementService).settle(20L, 9L, "operator");
|
||||
verify(repository).syncInboundProjections(20L, 9L, "operator");
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 50L, 50L);
|
||||
verify(loadingDeliveryService).requirePdaCompletionReady(101L, 60L, 50L);
|
||||
verify(truckAppointmentService).completeLoadingFromPda(99L, 20L, 9L, "operator");
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
"PDA-complete-1-50", "checked");
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 60L, 50L, 9L, "operator",
|
||||
"PDA-complete-1-60", "checked");
|
||||
verify(loadingListService).publishCompletedEvent(10L);
|
||||
InOrder finalization = inOrder(loadingListService, loadingDeliveryService);
|
||||
finalization.verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
finalization.verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
||||
finalization.verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
"PDA-complete-1-50", "checked");
|
||||
finalization.verify(loadingListService).publishCompletedEvent(10L);
|
||||
}
|
||||
@@ -556,7 +562,7 @@ class LoadingPickServiceTest {
|
||||
verify(settlementService).settle(20L, 9L, "operator");
|
||||
verify(repository).approve(20L, 9L, "operator", "checked");
|
||||
verify(loadingListService).finishFromPda(10L, 20L, 9L, "operator");
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
||||
verify(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
"PDA-complete-not-blocked-50", "checked");
|
||||
verify(loadingListService).publishCompletedEvent(10L);
|
||||
}
|
||||
@@ -574,7 +580,7 @@ class LoadingPickServiceTest {
|
||||
new LoadingInboundMapper.Snapshot(50L, 101L, "LOADING", 7));
|
||||
doThrow(new BizException(com.baiyue.common.core.api.ResultCode.CONFLICT,
|
||||
"入库单并发修改"))
|
||||
.when(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 9L, "operator",
|
||||
.when(loadingDeliveryService).completeAfterPdaSettlement(101L, 50L, 50L, 9L, "operator",
|
||||
"PDA-complete-finalize-fails-50", "checked");
|
||||
|
||||
assertThatThrownBy(() -> service.complete(20L,
|
||||
|
||||
Reference in New Issue
Block a user