From 0db11517dba9ff4a911b8ae8621d1fd4276ee7cc Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 11 Sep 2026 15:10:01 +0800 Subject: [PATCH] fix: persist return forecast tail references --- deploy/sql/头程数据库统一增量-MySQL8.sql | 15 ++++++++++++ .../service/ReturnReceiptService.java | 4 +++- .../src/main/resources/db/lastleg.sql | 4 +++- .../service/ReturnReceiptServiceTest.java | 24 +++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index 261b98e52..459e78656 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -19356,3 +19356,18 @@ CALL baiyue_firstleg.assert_unified_final_schema(); DROP PROCEDURE IF EXISTS baiyue_firstleg.assert_unified_final_schema; SELECT 'PASS' AS migration_status, '统一增量全部执行完成;父子订单、入库扣货、履约分片、装柜门禁、订舱拖车、报关船期、船司查询、仓库角色、费用、Finance人工财务、客户资金账户、公共资源、可靠审计及IAM安全事件结构校验通过' AS message; +-- 退货预报关联尾程订单业务编号(测试阶段直接收敛当前结构) +DROP PROCEDURE IF EXISTS baiyue_lastleg.converge_return_order_tail_reference; +DELIMITER $$ +CREATE PROCEDURE baiyue_lastleg.converge_return_order_tail_reference() +BEGIN + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='baiyue_lastleg' AND table_name='ll_return_order' AND column_name='lastleg_order_id') THEN + ALTER TABLE baiyue_lastleg.ll_return_order ADD COLUMN lastleg_order_id BIGINT NULL COMMENT '尾程订单ID' AFTER customer_reference_no; + END IF; + IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema='baiyue_lastleg' AND table_name='ll_return_order' AND column_name='lastleg_order_no') THEN + ALTER TABLE baiyue_lastleg.ll_return_order ADD COLUMN lastleg_order_no VARCHAR(32) NULL COMMENT '尾程订单号' AFTER lastleg_order_id; + END IF; +END$$ +DELIMITER ; +CALL baiyue_lastleg.converge_return_order_tail_reference(); +DROP PROCEDURE IF EXISTS baiyue_lastleg.converge_return_order_tail_reference; diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptService.java index a44bf3f5f..06454179c 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptService.java @@ -106,12 +106,14 @@ public class ReturnReceiptService { tail.setId(IdWorker.getId()); tail.setOrderNo(numbers.next("RETURN_RELABEL", returnOrder.getOverseasWarehouseId(), tail.getId())); tail.setCustomerId(returnOrder.getCustomerId()); tail.setOrderType("RETURN_RELABEL"); - tail.setSourceRegion("CUSTOMER"); tail.setSourceOrderId(String.valueOf(returnOrder.getId())); + tail.setSourceRegion("CUSTOMER"); tail.setSourceInboundId(returnOrder.getReturnNo()); + tail.setSourceOrderId(String.valueOf(returnOrder.getId())); tail.setSourceOrderNo(returnOrder.getReturnNo()); tail.setOverseasWarehouseId(returnOrder.getOverseasWarehouseId()); tail.setCurrentStatus("PENDING_ASSIGNMENT"); tail.setSubOrderStatus(Boolean.TRUE.equals(returnOrder.getNeedRelabel()) ? "RETURN_RELABEL:PENDING_BINDING" : "RETURN_RELABEL:NOT_REQUIRED"); tail.setRelabelStatus(Boolean.TRUE.equals(returnOrder.getNeedRelabel()) ? "PENDING_BINDING" : "NOT_REQUIRED"); tail.setCustomerRemark(returnOrder.getRemark()); tail.setSourceEventId("RETURN_ORDER:" + returnOrder.getReturnNo()); tail.setSourceLoadingPlanId("RETURN_RELABEL"); tail.setCreatedAt(now); tail.setUpdatedAt(now); + tail.setSourcePayloadHash(returnOrder.getRequestHash()); lastlegOrders.insert(tail); returnOrder.setLastlegOrderId(tail.getId()); returnOrder.setLastlegOrderNo(tail.getOrderNo()); returnOrder.setRelabelStatus(Boolean.TRUE.equals(returnOrder.getNeedRelabel()) ? "PENDING_BINDING" : "NOT_REQUIRED"); diff --git a/lastleg-service/src/main/resources/db/lastleg.sql b/lastleg-service/src/main/resources/db/lastleg.sql index 2dfacc2d6..21ec376d0 100644 --- a/lastleg-service/src/main/resources/db/lastleg.sql +++ b/lastleg-service/src/main/resources/db/lastleg.sql @@ -2360,10 +2360,12 @@ CREATE TABLE IF NOT EXISTS ll_destuff_pallet_cost_allocation ( -- 客户退货预报、PDA 签收与仓储费起算批次 CREATE TABLE IF NOT EXISTS ll_return_order ( -- need_relabel is added below for existing deployments - id BIGINT NOT NULL COMMENT '退货订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户ID和隔离键', return_no VARCHAR(128) NOT NULL COMMENT '退货订单号', overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '操作海外仓ID', warehouse_contact_name VARCHAR(128) NULL COMMENT '中转仓联系人快照', warehouse_contact_phone VARCHAR(64) NULL COMMENT '中转仓联系电话快照', customer_reference_no VARCHAR(128) NULL COMMENT '客户参考号', status VARCHAR(32) NOT NULL COMMENT 'PENDING_RECEIPT、PARTIALLY_RECEIVED或COMPLETED', expected_sku_types INT NOT NULL COMMENT '预报SKU种类数', expected_quantity BIGINT NOT NULL COMMENT '预报总数量', received_quantity BIGINT NOT NULL DEFAULT 0 COMMENT '累计实收总数量', remark VARCHAR(1000) NULL COMMENT '客户备注', idempotency_key VARCHAR(255) NOT NULL COMMENT '创建幂等键', request_hash CHAR(64) NOT NULL COMMENT '创建请求SHA-256', created_by VARCHAR(128) NULL COMMENT '创建操作人ID', created_at DATETIME(3) NOT NULL COMMENT '创建时间', updated_at DATETIME(3) NOT NULL COMMENT '最后更新时间', + id BIGINT NOT NULL COMMENT '退货订单ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户ID和隔离键', return_no VARCHAR(128) NOT NULL COMMENT '退货订单号', overseas_warehouse_id VARCHAR(128) NOT NULL COMMENT '操作海外仓ID', warehouse_contact_name VARCHAR(128) NULL COMMENT '中转仓联系人快照', warehouse_contact_phone VARCHAR(64) NULL COMMENT '中转仓联系电话快照', customer_reference_no VARCHAR(128) NULL COMMENT '客户参考号', lastleg_order_id BIGINT NULL COMMENT '尾程订单ID', lastleg_order_no VARCHAR(32) NULL COMMENT '尾程订单号', status VARCHAR(32) NOT NULL COMMENT 'PENDING_RECEIPT、PARTIALLY_RECEIVED或COMPLETED', expected_sku_types INT NOT NULL COMMENT '预报SKU种类数', expected_quantity BIGINT NOT NULL COMMENT '预报总数量', received_quantity BIGINT NOT NULL DEFAULT 0 COMMENT '累计实收总数量', remark VARCHAR(1000) NULL COMMENT '客户备注', idempotency_key VARCHAR(255) NOT NULL COMMENT '创建幂等键', request_hash CHAR(64) NOT NULL COMMENT '创建请求SHA-256', created_by VARCHAR(128) NULL COMMENT '创建操作人ID', created_at DATETIME(3) NOT NULL COMMENT '创建时间', updated_at DATETIME(3) NOT NULL COMMENT '最后更新时间', PRIMARY KEY(id), UNIQUE KEY uk_ll_return_order_customer_no(customer_id,return_no), UNIQUE KEY uk_ll_return_order_customer_key(customer_id,idempotency_key), KEY idx_ll_return_order_warehouse_status(overseas_warehouse_id,status,created_at), CONSTRAINT chk_ll_return_order_quantity CHECK(expected_sku_types>0 AND expected_quantity>0 AND received_quantity>=0 AND received_quantity<=expected_quantity) ) ENGINE=InnoDB COMMENT='客户退货订单及SKU实收汇总'; SET @ll_return_order_need_relabel = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_return_order' AND column_name='need_relabel'), 'SELECT 1', 'ALTER TABLE ll_return_order ADD COLUMN need_relabel TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否需要换标'' AFTER customer_reference_no')); PREPARE stmt FROM @ll_return_order_need_relabel; EXECUTE stmt; DEALLOCATE PREPARE stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_return_order' AND column_name='lastleg_order_id'), 'SELECT 1', 'ALTER TABLE ll_return_order ADD COLUMN lastleg_order_id BIGINT NULL COMMENT ''尾程订单ID'' AFTER customer_reference_no')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_return_order' AND column_name='lastleg_order_no'), 'SELECT 1', 'ALTER TABLE ll_return_order ADD COLUMN lastleg_order_no VARCHAR(32) NULL COMMENT ''尾程订单号'' AFTER lastleg_order_id')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_return_order' AND column_name='warehouse_contact_name'), 'SELECT 1', 'ALTER TABLE ll_return_order ADD COLUMN warehouse_contact_name VARCHAR(128) NULL COMMENT ''中转仓联系人快照'' AFTER overseas_warehouse_id')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_return_order' AND column_name='warehouse_contact_phone'), 'SELECT 1', 'ALTER TABLE ll_return_order ADD COLUMN warehouse_contact_phone VARCHAR(64) NULL COMMENT ''中转仓联系电话快照'' AFTER warehouse_contact_name')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_return_order' AND column_name='relabel_application_id'), 'SELECT 1', 'ALTER TABLE ll_return_order ADD COLUMN relabel_application_id BIGINT NULL COMMENT ''绑定换标申请单ID'' AFTER need_relabel')); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptServiceTest.java index 37bbda188..f4478ba23 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/returnreceipt/service/ReturnReceiptServiceTest.java @@ -1,6 +1,7 @@ package com.baiyue.lastleg.returnreceipt.service; import com.baiyue.lastleg.mapper.LastlegOrderMapper; +import com.baiyue.lastleg.entity.LastlegOrder; import com.baiyue.lastleg.pda.security.PdaWarehouseIdentity; import com.baiyue.lastleg.returnreceipt.dto.PdaReturnReceiptRequests; import com.baiyue.lastleg.returnreceipt.entity.ReturnInventoryCarton; @@ -25,6 +26,7 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.server.ResponseStatusException; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -85,6 +87,28 @@ class ReturnReceiptServiceTest { assertEquals(2, result.expectedItems().get(0).receivedQuantity()); } + @Test + void createForecastPersistsCompleteTailOrderSourceAndReturnsItsBusinessNumber() { + AtomicReference saved = new AtomicReference<>(); + when(orders.findKey("customer-1", "create-key")).thenReturn(null); + when(numbers.next(eq("RETURN"), eq("10"), anyLong())).thenReturn("RT-1"); + when(numbers.next(eq("RETURN_RELABEL"), eq("10"), anyLong())).thenReturn("RETU-1"); + when(orders.insert(any(ReturnOrder.class))).thenAnswer(invocation -> { saved.set(invocation.getArgument(0)); return 1; }); + when(orders.findNo("customer-1", "RT-1")).thenAnswer(invocation -> saved.get()); + when(orderItems.listOrder(anyLong())).thenReturn(List.of()); + + var result = service.create("customer-1", "merchant-1", new com.baiyue.lastleg.returnreceipt.dto.ReturnOrderRequests.Create( + "10", "测试仓", "0000000000", "REF-1", false, "测试", "create-key", + List.of(new com.baiyue.lastleg.returnreceipt.dto.ReturnOrderRequests.Item("SKU-A", "商品A", 2)))); + + ArgumentCaptor tail = ArgumentCaptor.forClass(LastlegOrder.class); + verify(lastlegOrders).insert(tail.capture()); + assertEquals("RT-1", tail.getValue().getSourceInboundId()); + assertEquals("RETURN_ORDER:RT-1", tail.getValue().getSourceEventId()); + assertEquals(saved.get().getRequestHash(), tail.getValue().getSourcePayloadHash()); + assertEquals("RETU-1", result.order().lastlegOrderNo()); + } + @Test void skuLookupReturnsOnlyActiveCandidatesFromTheBoundWarehouse() { ReturnOrder order = order("RT-1", "10", ReturnReceiptService.PENDING_RECEIPT, 5, 1);