fix(events): allow container nodes before ocean booking
This commit is contained in:
+4
-2
@@ -31,8 +31,10 @@ public record FirstLegContainerShippingNodeRecordedV1(
|
|||||||
if (queryId != null && queryId.isBlank()) {
|
if (queryId != null && queryId.isBlank()) {
|
||||||
throw new IllegalArgumentException("船节点跟踪任务标识不能为空字符串");
|
throw new IllegalArgumentException("船节点跟踪任务标识不能为空字符串");
|
||||||
}
|
}
|
||||||
if (oceanBookingId == null || oceanBookingId.isBlank()
|
if (oceanBookingId != null && oceanBookingId.isBlank()) {
|
||||||
|| containerBookingId == null || containerBookingId.isBlank()
|
throw new IllegalArgumentException("船节点订舱标识不能为空字符串");
|
||||||
|
}
|
||||||
|
if (containerBookingId == null || containerBookingId.isBlank()
|
||||||
|| containerNo == null || containerNo.isBlank()) {
|
|| containerNo == null || containerNo.isBlank()) {
|
||||||
throw new IllegalArgumentException("船节点交接业务标识不能为空");
|
throw new IllegalArgumentException("船节点交接业务标识不能为空");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ components:
|
|||||||
destinationType: {type: string, enum: [FBA, OVERSEAS]}
|
destinationType: {type: string, enum: [FBA, OVERSEAS]}
|
||||||
FirstLegContainerShippingNodeRecordedV1:
|
FirstLegContainerShippingNodeRecordedV1:
|
||||||
type: object
|
type: object
|
||||||
required: [oceanBookingId, sourceContainerBookingId, containerNo, standardNode, nodeSequence, observedAt, actualAt, providerCode, providerEventId, providerNodeCode, rawPayloadHash]
|
required: [sourceContainerBookingId, containerNo, standardNode, nodeSequence, observedAt, actualAt, providerCode, providerEventId, providerNodeCode, rawPayloadHash]
|
||||||
properties:
|
properties:
|
||||||
queryId: {type: [string, 'null'], description: Automatic tracking task ID; null for a direct platform manual node.}
|
queryId: {type: [string, 'null'], description: Automatic tracking task ID; null for a direct platform manual node.}
|
||||||
oceanBookingId: {type: string}
|
oceanBookingId: {type: [string, 'null'], description: Ocean booking ID when the physical container has already been linked; null is valid for a direct manual node.}
|
||||||
sourceContainerBookingId: {type: string, minLength: 1}
|
sourceContainerBookingId: {type: string, minLength: 1}
|
||||||
containerNo: {type: string, minLength: 1, description: Uppercase normalized physical container number; never cabinetNo or a technical identity}
|
containerNo: {type: string, minLength: 1, description: Uppercase normalized physical container number; never cabinetNo or a technical identity}
|
||||||
standardNode: {type: string}
|
standardNode: {type: string}
|
||||||
|
|||||||
+2
-1
@@ -39,12 +39,13 @@ class FirstLegContainerShippingNodeRecordedV1Test {
|
|||||||
void acceptsDirectManualNodeWithoutTrackingQuery() {
|
void acceptsDirectManualNodeWithoutTrackingQuery() {
|
||||||
var payload = payload();
|
var payload = payload();
|
||||||
var manual = new FirstLegContainerShippingNodeRecordedV1(
|
var manual = new FirstLegContainerShippingNodeRecordedV1(
|
||||||
null, payload.oceanBookingId(), payload.containerBookingId(), payload.containerNo(),
|
null, null, payload.containerBookingId(), payload.containerNo(),
|
||||||
payload.standardNode(), payload.nodeSequence(), payload.observedAt(), payload.actualAt(),
|
payload.standardNode(), payload.nodeSequence(), payload.observedAt(), payload.actualAt(),
|
||||||
payload.locationCode(), payload.locationName(), "MANUAL", "manual-event", payload.providerNodeCode(),
|
payload.locationCode(), payload.locationName(), "MANUAL", "manual-event", payload.providerNodeCode(),
|
||||||
"MANUAL:7", payload.rawPayloadHash());
|
"MANUAL:7", payload.rawPayloadHash());
|
||||||
|
|
||||||
assertThat(manual.queryId()).isNull();
|
assertThat(manual.queryId()).isNull();
|
||||||
|
assertThat(manual.oceanBookingId()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS ll_loading_event_inbox (
|
|||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
|
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
|
||||||
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
|
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
|
||||||
ocean_booking_id VARCHAR(128) NOT NULL COMMENT '来源海运订舱ID', container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
|
ocean_booking_id VARCHAR(128) NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空', container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
|
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
|
||||||
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点', node_sequence INT NOT NULL DEFAULT 0 COMMENT '最新有效节点单调序号',
|
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点', node_sequence INT NOT NULL DEFAULT 0 COMMENT '最新有效节点单调序号',
|
||||||
observed_at DATETIME(3) NULL COMMENT '供应商节点观测时间', actual_at DATETIME(3) NULL COMMENT '节点实际发生时间',
|
observed_at DATETIME(3) NULL COMMENT '供应商节点观测时间', actual_at DATETIME(3) NULL COMMENT '节点实际发生时间',
|
||||||
@@ -3307,7 +3307,7 @@ PREPARE phase4_inventory_stmt FROM @ddl; EXECUTE phase4_inventory_stmt; DEALLOCA
|
|||||||
-- 海运节点达到可提柜后才允许海外 PDA 认领;装柜完成只建立等待门禁快照。
|
-- 海运节点达到可提柜后才允许海外 PDA 认领;装柜完成只建立等待门禁快照。
|
||||||
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
|
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
|
||||||
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
|
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
|
||||||
ocean_booking_id VARCHAR(128) NOT NULL COMMENT '来源海运订舱ID',
|
ocean_booking_id VARCHAR(128) NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空',
|
||||||
container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
|
container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
|
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
|
||||||
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点',
|
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点',
|
||||||
@@ -4604,3 +4604,7 @@ CALL assert_overseas_finance_p0_ready();
|
|||||||
DROP PROCEDURE IF EXISTS assert_overseas_finance_p0_ready;
|
DROP PROCEDURE IF EXISTS assert_overseas_finance_p0_ready;
|
||||||
|
|
||||||
SELECT 'PASS' AS migration_status, '海外财务P0费用来源字段已就绪' AS message;
|
SELECT 'PASS' AS migration_status, '海外财务P0费用来源字段已就绪' AS message;
|
||||||
|
|
||||||
|
-- 人工货柜节点独立于订舱:货柜尚未关联海运订舱时仍可同步当前节点。
|
||||||
|
ALTER TABLE ll_container_shipping_gate
|
||||||
|
MODIFY ocean_booking_id VARCHAR(128) NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空';
|
||||||
|
|||||||
+2
-1
@@ -99,13 +99,14 @@ class CarrierScheduleNodeOutboxServiceTest {
|
|||||||
fact.setProviderCode("MANUAL"); fact.setProviderEventId("manual-event");
|
fact.setProviderCode("MANUAL"); fact.setProviderEventId("manual-event");
|
||||||
fact.setSourceReference("MANUAL:7");
|
fact.setSourceReference("MANUAL:7");
|
||||||
|
|
||||||
service.publishContainerNode(2088000000000000004L, 2088000000000000003L,
|
service.publishContainerNode(null, 2088000000000000003L,
|
||||||
fact, "ZCSU7881970");
|
fact, "ZCSU7881970");
|
||||||
|
|
||||||
ArgumentCaptor<InboundOutboxEntity> captor = ArgumentCaptor.forClass(InboundOutboxEntity.class);
|
ArgumentCaptor<InboundOutboxEntity> captor = ArgumentCaptor.forClass(InboundOutboxEntity.class);
|
||||||
verify(mapper).insert(captor.capture());
|
verify(mapper).insert(captor.capture());
|
||||||
EventEnvelope envelope = objectMapper.readValue(captor.getValue().getPayloadJson(), EventEnvelope.class);
|
EventEnvelope envelope = objectMapper.readValue(captor.getValue().getPayloadJson(), EventEnvelope.class);
|
||||||
assertThat(envelope.payload().path("queryId").isNull()).isTrue();
|
assertThat(envelope.payload().path("queryId").isNull()).isTrue();
|
||||||
|
assertThat(envelope.payload().path("oceanBookingId").isNull()).isTrue();
|
||||||
assertThat(envelope.payload().path("sourceContainerBookingId").asText())
|
assertThat(envelope.payload().path("sourceContainerBookingId").asText())
|
||||||
.isEqualTo("2088000000000000003");
|
.isEqualTo("2088000000000000003");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS ll_loading_event_inbox (
|
|||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
|
CREATE TABLE IF NOT EXISTS ll_container_shipping_gate (
|
||||||
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
|
container_booking_id VARCHAR(128) NOT NULL COMMENT '来源头程柜预订ID',
|
||||||
ocean_booking_id VARCHAR(128) NOT NULL COMMENT '来源海运订舱ID', container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
|
ocean_booking_id VARCHAR(128) NULL COMMENT '来源海运订舱ID;货柜未关联订舱时为空', container_no VARCHAR(64) NOT NULL COMMENT '实际集装箱号',
|
||||||
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
|
status VARCHAR(32) NOT NULL DEFAULT 'AWAITING_PICKUP_READY' COMMENT '海外收货门禁状态:AWAITING_PICKUP_READY、RECEIVING_AVAILABLE',
|
||||||
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点', node_sequence INT NOT NULL DEFAULT 0 COMMENT '最新有效节点单调序号',
|
standard_node VARCHAR(64) NULL COMMENT '最新有效标准船节点', node_sequence INT NOT NULL DEFAULT 0 COMMENT '最新有效节点单调序号',
|
||||||
observed_at DATETIME(3) NULL COMMENT '供应商节点观测时间', actual_at DATETIME(3) NULL COMMENT '节点实际发生时间',
|
observed_at DATETIME(3) NULL COMMENT '供应商节点观测时间', actual_at DATETIME(3) NULL COMMENT '节点实际发生时间',
|
||||||
|
|||||||
Reference in New Issue
Block a user