fix: validate overseas handover before settlement

This commit is contained in:
2026-09-01 23:20:34 +08:00
parent e19224c4b7
commit f4a3b58959
9 changed files with 89 additions and 24 deletions
@@ -43,9 +43,7 @@ public interface LoadingCompletedEventMapper {
AND actual_task.loading_plan_id = p.id
JOIN fl_inbound_order o ON o.id = i.inbound_id AND o.tenant_id = i.account_id AND o.deleted = 0
LEFT JOIN fl_inbound_order root_order ON root_order.id = COALESCE(o.root_inbound_id, o.id)
WHERE p.id = #{planId} AND p.plan_status = 'COMPLETED'
AND o.process_status = 'LOADED'
AND i.package_count > 0
WHERE p.id = #{planId} AND i.package_count > 0
ORDER BY i.account_id, i.inbound_id, i.inbound_detail_id, i.id
""")
List<Row> rows(@Param("planId") long planId);
@@ -48,16 +48,26 @@ public class LoadingCompletedOutboxService {
@Resource private ObjectMapper objectMapper;
public void publish(long planId) {
preparedEnvelopes(planId).forEach(this::insert);
}
/** 在任何入库结算前验证跨区交接快照,避免整托/箱唛冲突留下半完成状态。 */
public void requirePublishable(long planId) {
preparedEnvelopes(planId);
}
private List<EventEnvelope> preparedEnvelopes(long planId) {
List<Row> rows = snapshotMapper.rows(planId);
if (rows.isEmpty()) throw new BizException(ResultCode.CONFLICT, "装柜计划没有最终实装入仓单,不能生成尾程交接事件");
validateContainerIdentity(rows);
validateCustomerSnapshots(rows);
Map<Long, List<String>> cartonMarks = cartonLabelPools(rows);
List<PalletRow> palletRows = palletRows(planId);
rows.stream().collect(java.util.stream.Collectors.groupingBy(Row::getAccountId,
return rows.stream().collect(java.util.stream.Collectors.groupingBy(Row::getAccountId,
java.util.TreeMap::new, java.util.stream.Collectors.toList()))
.forEach((accountId, accountRows) -> insert(envelope(accountId, accountRows,
palletRows.stream().filter(item -> accountId.equals(item.getAccountId())).toList(), cartonMarks)));
.entrySet().stream().map(entry -> envelope(entry.getKey(), entry.getValue(),
palletRows.stream().filter(item -> entry.getKey().equals(item.getAccountId())).toList(), cartonMarks))
.toList();
}
/** 已开展逐箱打托的入仓单使用现场事实;未开展的存量入仓单继续兼容原托盘分配快照。 */
@@ -571,6 +571,12 @@ public class LoadingListService {
loadingCompletedOutboxService.publish(planId);
}
/** 完成 worker 在远程结算前验证最终跨区快照能够完整生成。 */
@Transactional(readOnly = true)
public void requireCompletedEventReady(long planId) {
loadingCompletedOutboxService.requirePublishable(planId);
}
private AggregateRow requirePlan(long planId) {
AggregateRow row = repository.aggregate(planId);
if (row == null) {
@@ -535,6 +535,8 @@ public class LoadingPickService {
List<GroupSnapshot> taskGroups = coloadGroups(owners);
runCompletionStage("CABINET_EVIDENCE", () ->
cabinetEvidenceService.requireComplete(task.getContainerBookingId()));
runCompletionStage("COMPLETED_EVENT_READY", () ->
loadingListService.requireCompletedEventReady(task.getLoadingPlanId()));
// Phase 1 is read-only: cabinet evidence is checked once, then every owner must pass its business gates
// before any warehouse resource or inbound status can be changed. This prevents a later customer gate from