From 1f2c4cd8bb6ab15978a09075b246ab60caa003f7 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sat, 22 Aug 2026 10:20:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/sql/海外区域统一增量-MySQL8.sql | 90 +++++++++++++++++++ .../controller/SmartDispatchController.java | 1 - .../src/main/resources/db/lastleg.sql | 17 ++-- .../openapi/overseas-business-v1.yaml | 60 +++++++++++++ .../Phase4BrowserApiBoundaryTest.java | 15 +++- .../DispatchCandidateMapperContractTest.java | 2 +- .../SmartDispatchOpenApiContractTest.java | 7 ++ 7 files changed, 179 insertions(+), 13 deletions(-) diff --git a/deploy/sql/海外区域统一增量-MySQL8.sql b/deploy/sql/海外区域统一增量-MySQL8.sql index 2d11c36ba..4eb930e24 100644 --- a/deploy/sql/海外区域统一增量-MySQL8.sql +++ b/deploy/sql/海外区域统一增量-MySQL8.sql @@ -178,6 +178,96 @@ CREATE TABLE IF NOT EXISTS ll_local_looseboard_history ( KEY idx_ll_local_looseboard_history(customer_id,order_id,created_at) ) ENGINE=InnoDB COMMENT='本土散板操作流水'; +-- 四期平台排车查询、智能预排与草稿锁;不开放编辑、发车、PDA 出库或派送。 +CREATE TABLE IF NOT EXISTS ll_dispatch_order ( + id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', + dispatch_no VARCHAR(64) NOT NULL COMMENT '派车单号', overseas_warehouse_id VARCHAR(128) NULL COMMENT '海外仓ID', + overseas_warehouse_name VARCHAR(255) NULL COMMENT '海外仓名称快照', vehicle_plate VARCHAR(64) NULL COMMENT '车牌号', + driver_name VARCHAR(128) NULL COMMENT '司机姓名', driver_phone VARCHAR(64) NULL COMMENT '司机电话', + carrier_name VARCHAR(255) NULL COMMENT '承运商名称', route VARCHAR(255) NULL COMMENT '运输路线', + depart_at DATETIME(3) NULL COMMENT '发车时间', fba_appointment_no VARCHAR(64) NULL COMMENT 'FBA预约号', + fba_appointment_status VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT 'FBA预约状态', + vehicle_appointment_status VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '车辆预约状态', + estimated_arrival_at DATETIME(3) NULL COMMENT '预计到达时间', + status VARCHAR(32) NOT NULL DEFAULT 'PENDING_DEPARTURE' COMMENT '业务状态', + item_count INT NOT NULL DEFAULT 0 COMMENT '明细数量', pallet_count INT NOT NULL DEFAULT 0 COMMENT '托盘数', + package_count INT NOT NULL DEFAULT 0 COMMENT '箱数', picked_package_count INT NOT NULL DEFAULT 0 COMMENT 'PDA累计已拣箱数', + shipped_package_count INT NOT NULL DEFAULT 0 COMMENT '累计已出库箱数', partial_completion_reason VARCHAR(1000) NULL COMMENT '部分完成原因', + created_by VARCHAR(128) NULL COMMENT '创建人ID', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + PRIMARY KEY(id), UNIQUE KEY uk_ll_dispatch_order_no(customer_id,dispatch_no), + KEY idx_ll_dispatch_order_list(customer_id,status,depart_at), KEY idx_ll_dispatch_order_vehicle(customer_id,vehicle_plate) +) ENGINE=InnoDB COMMENT='尾程派车单'; + +CREATE TABLE IF NOT EXISTS ll_dispatch_item ( + id BIGINT NOT NULL COMMENT '主键ID', dispatch_order_id BIGINT NOT NULL COMMENT '派车单ID', + customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', plan_id BIGINT NULL COMMENT '入仓计划ID', + plan_pallet_id BIGINT NULL COMMENT '计划托盘ID', warehouse_pallet_id BIGINT NULL COMMENT '仓库实物托盘ID', + order_id BIGINT NULL COMMENT '订单ID', order_no VARCHAR(64) NULL COMMENT '订单号', + order_refs VARCHAR(2000) NULL COMMENT '关联订单信息', inbound_code VARCHAR(128) NULL COMMENT '入仓单号快照', + pallet_no VARCHAR(128) NOT NULL COMMENT '托盘号', overseas_warehouse_id VARCHAR(128) NULL COMMENT '海外仓ID', + overseas_warehouse_name VARCHAR(255) NULL COMMENT '海外仓名称快照', destination_warehouse_id VARCHAR(128) NULL COMMENT '目的仓ID', + destination_warehouse_code VARCHAR(128) NULL COMMENT '目的仓编码快照', destination_warehouse_name VARCHAR(255) NULL COMMENT '目的仓名称快照', + fba_warehouse_code VARCHAR(128) NULL COMMENT 'FBA仓库编码', package_count INT NOT NULL DEFAULT 0 COMMENT '箱数', + volume_cbm DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT '体积(立方米)', source_stage VARCHAR(32) NOT NULL COMMENT '数据来源阶段', + created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', PRIMARY KEY(id), + UNIQUE KEY uk_ll_dispatch_item_source(dispatch_order_id,plan_pallet_id,warehouse_pallet_id), + KEY idx_ll_dispatch_item_order(customer_id,order_id), KEY idx_ll_dispatch_item_inbound(customer_id,inbound_code), + KEY idx_ll_dispatch_item_pallet(customer_id,plan_pallet_id,warehouse_pallet_id) +) ENGINE=InnoDB COMMENT='派车货物冻结明细'; + +CREATE TABLE IF NOT EXISTS ll_dispatch_pallet_lock ( + id BIGINT NOT NULL COMMENT '主键ID', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', + source_pallet_key VARCHAR(160) NOT NULL COMMENT '来源托盘业务键', active_key VARCHAR(160) NULL COMMENT '生效中的资源排他键', + plan_id BIGINT NULL COMMENT '入仓计划ID', plan_pallet_id BIGINT NULL COMMENT '计划托盘ID', + warehouse_pallet_id BIGINT NULL COMMENT '仓库实物托盘ID', dispatch_order_id BIGINT NULL COMMENT '正式派车单ID,草稿锁阶段为空', + draft_container_id BIGINT NULL COMMENT '智能排车草稿装车托盘ID', draft_token VARCHAR(64) NULL COMMENT '智能排车草稿随机标识', + operator_id VARCHAR(128) NULL COMMENT '草稿锁操作人ID', status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' COMMENT '业务状态', + locked_at DATETIME(3) NOT NULL COMMENT '锁定时间', expires_at DATETIME(3) NULL COMMENT '草稿锁失效时间,正式派车锁为空', + released_at DATETIME(3) NULL COMMENT '释放时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', + PRIMARY KEY(id), UNIQUE KEY uk_ll_dispatch_active_pallet(customer_id,active_key), + KEY idx_ll_dispatch_pallet_lock_order(customer_id,dispatch_order_id,status), + KEY idx_ll_dispatch_pallet_lock_source(customer_id,source_pallet_key,status) +) ENGINE=InnoDB COMMENT='派车托盘排他锁'; + +CREATE TABLE IF NOT EXISTS ll_dispatch_draft ( + id BIGINT NOT NULL COMMENT '主键ID', draft_token VARCHAR(64) NOT NULL COMMENT '智能排车草稿随机标识', + customer_id VARCHAR(128) NULL COMMENT '客户租户ID', operator_id VARCHAR(128) NULL COMMENT '草稿操作人ID', + plan_ids_json VARCHAR(4000) NULL COMMENT '所选入仓计划ID列表JSON', containers_json LONGTEXT NULL COMMENT '草稿装车布局JSON快照', + status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' COMMENT '草稿状态', expires_at DATETIME(3) NULL COMMENT '草稿失效时间', + released_at DATETIME(3) NULL COMMENT '草稿释放时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', PRIMARY KEY(id), + UNIQUE KEY uk_ll_dispatch_draft_token(draft_token), KEY idx_ll_dispatch_draft_operator(operator_id,status) +) ENGINE=InnoDB COMMENT='智能排车草稿'; + +CREATE TABLE IF NOT EXISTS ll_dispatch_draft_container ( + id BIGINT NOT NULL COMMENT '主键ID', draft_id BIGINT NOT NULL COMMENT '智能排车草稿ID', + draft_token VARCHAR(64) NOT NULL COMMENT '智能排车草稿随机标识', sequence_no INT NOT NULL COMMENT '草稿车厢顺序号', + capacity INT NOT NULL COMMENT '草稿车厢托盘容量', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', PRIMARY KEY(id), + KEY idx_ll_dispatch_draft_container_token(draft_token) +) ENGINE=InnoDB COMMENT='智能排车草稿装车托盘'; + +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_item' AND column_name='order_refs'), + 'SELECT 1', 'ALTER TABLE ll_dispatch_item ADD COLUMN order_refs VARCHAR(2000) NULL COMMENT ''关联订单信息'' AFTER order_no')); +PREPARE phase4_dispatch_stmt FROM @ddl; EXECUTE phase4_dispatch_stmt; DEALLOCATE PREPARE phase4_dispatch_stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_pallet_lock' AND column_name='draft_container_id'), + 'SELECT 1', 'ALTER TABLE ll_dispatch_pallet_lock ADD COLUMN draft_container_id BIGINT NULL COMMENT ''智能排车草稿装车托盘ID'' AFTER dispatch_order_id')); +PREPARE phase4_dispatch_stmt FROM @ddl; EXECUTE phase4_dispatch_stmt; DEALLOCATE PREPARE phase4_dispatch_stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_pallet_lock' AND column_name='draft_token'), + 'SELECT 1', 'ALTER TABLE ll_dispatch_pallet_lock ADD COLUMN draft_token VARCHAR(64) NULL COMMENT ''智能排车草稿随机标识'' AFTER draft_container_id')); +PREPARE phase4_dispatch_stmt FROM @ddl; EXECUTE phase4_dispatch_stmt; DEALLOCATE PREPARE phase4_dispatch_stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_pallet_lock' AND column_name='operator_id'), + 'SELECT 1', 'ALTER TABLE ll_dispatch_pallet_lock ADD COLUMN operator_id VARCHAR(128) NULL COMMENT ''草稿锁操作人ID'' AFTER draft_token')); +PREPARE phase4_dispatch_stmt FROM @ddl; EXECUTE phase4_dispatch_stmt; DEALLOCATE PREPARE phase4_dispatch_stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_pallet_lock' AND column_name='expires_at'), + 'SELECT 1', 'ALTER TABLE ll_dispatch_pallet_lock ADD COLUMN expires_at DATETIME(3) NULL COMMENT ''草稿锁失效时间,正式派车锁为空'' AFTER locked_at')); +PREPARE phase4_dispatch_stmt FROM @ddl; EXECUTE phase4_dispatch_stmt; DEALLOCATE PREPARE phase4_dispatch_stmt; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='ll_dispatch_pallet_lock' AND column_name='dispatch_order_id' AND is_nullable='YES'), + 'SELECT 1', 'ALTER TABLE ll_dispatch_pallet_lock MODIFY COLUMN dispatch_order_id BIGINT NULL COMMENT ''正式派车单ID,草稿锁阶段为空''')); +PREPARE phase4_dispatch_stmt FROM @ddl; EXECUTE phase4_dispatch_stmt; DEALLOCATE PREPARE phase4_dispatch_stmt; + -- 入仓库存按 SKU 冻结维度;旧环境只补结构,不由部署自动执行。 CREATE TABLE IF NOT EXISTS ll_inbound_plan_carton ( id BIGINT NOT NULL COMMENT '箱唛作业主键', customer_id VARCHAR(128) NOT NULL COMMENT '客户租户ID', diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/controller/SmartDispatchController.java b/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/controller/SmartDispatchController.java index 08721e58f..5c1f97f87 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/controller/SmartDispatchController.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/controller/SmartDispatchController.java @@ -22,7 +22,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ResponseStatusException; diff --git a/lastleg-service/src/main/resources/db/lastleg.sql b/lastleg-service/src/main/resources/db/lastleg.sql index f30924493..9baafcd43 100644 --- a/lastleg-service/src/main/resources/db/lastleg.sql +++ b/lastleg-service/src/main/resources/db/lastleg.sql @@ -845,17 +845,20 @@ CREATE TABLE IF NOT EXISTS ll_dispatch_item ( ) ENGINE=InnoDB COMMENT='派车托盘排他锁'; CREATE TABLE IF NOT EXISTS ll_dispatch_draft ( - id BIGINT NOT NULL, draft_token VARCHAR(64) NOT NULL, customer_id VARCHAR(128) NULL, - operator_id VARCHAR(128) NULL, plan_ids_json VARCHAR(4000) NULL, containers_json LONGTEXT NULL, - status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', expires_at DATETIME(3) NULL, released_at DATETIME(3) NULL, - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + id BIGINT NOT NULL COMMENT '主键ID', draft_token VARCHAR(64) NOT NULL COMMENT '智能排车草稿随机标识', + customer_id VARCHAR(128) NULL COMMENT '客户租户ID', operator_id VARCHAR(128) NULL COMMENT '草稿操作人ID', + plan_ids_json VARCHAR(4000) NULL COMMENT '所选入仓计划ID列表JSON', containers_json LONGTEXT NULL COMMENT '草稿装车布局JSON快照', + status VARCHAR(32) NOT NULL DEFAULT 'ACTIVE' COMMENT '草稿状态', expires_at DATETIME(3) NULL COMMENT '草稿失效时间', + released_at DATETIME(3) NULL COMMENT '草稿释放时间', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', PRIMARY KEY(id), UNIQUE KEY uk_ll_dispatch_draft_token(draft_token), KEY idx_ll_dispatch_draft_operator(operator_id,status) ) ENGINE=InnoDB COMMENT='智能排车草稿'; CREATE TABLE IF NOT EXISTS ll_dispatch_draft_container ( - id BIGINT NOT NULL, draft_id BIGINT NOT NULL, draft_token VARCHAR(64) NOT NULL, - sequence_no INT NOT NULL, capacity INT NOT NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY(id), + id BIGINT NOT NULL COMMENT '主键ID', draft_id BIGINT NOT NULL COMMENT '智能排车草稿ID', + draft_token VARCHAR(64) NOT NULL COMMENT '智能排车草稿随机标识', sequence_no INT NOT NULL COMMENT '草稿车厢顺序号', + capacity INT NOT NULL COMMENT '草稿车厢托盘容量', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', PRIMARY KEY(id), KEY idx_ll_dispatch_draft_container_token(draft_token) ) ENGINE=InnoDB COMMENT='智能排车草稿装车托盘'; diff --git a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml index ea2ae77b5..6cf76b0a9 100644 --- a/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml +++ b/lastleg-service/src/main/resources/openapi/overseas-business-v1.yaml @@ -65,6 +65,7 @@ paths: - { name: orderNo, in: query, schema: { type: string } } - { name: fbaWarehouseCode, in: query, schema: { type: string } } - { name: sourceStage, in: query, schema: { type: string, enum: [PLAN, RECEIVED] } } + - { name: draftToken, in: query, schema: { type: string, maxLength: 64 }, description: 当前智能排车草稿标识;当前草稿已锁托盘仍可见 } responses: '200': description: 统一 ApiResponse 包装的候选托盘数组 @@ -98,6 +99,36 @@ paths: security: [{ bearerAuth: [] }] requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/SmartDispatchDraftRelease' } } } } responses: { '200': { description: 草稿锁已全部释放 } } + /api/platform/v1/last-leg/dispatches/drafts: + get: + summary: 查询当前操作人的有效智能排车草稿 + description: 权限 platform:overseas-warehouse:inbound-order:smart-dispatch;只返回当前操作人未失效且未释放的草稿。 + security: [{ bearerAuth: [] }] + responses: + '200': { description: 统一 ApiResponse 包装的智能排车草稿数组, content: { application/json: { schema: { type: array, items: { $ref: '#/components/schemas/SmartDispatchDraft' } } } } } + /api/platform/v1/last-leg/dispatches/drafts/snapshot: + post: + summary: 保存智能排车草稿布局快照 + description: 权限 platform:overseas-warehouse:inbound-order:smart-dispatch;草稿仅归当前操作人。 + security: [{ bearerAuth: [] }] + requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/SmartDispatchDraftSnapshot' } } } } + responses: { '200': { description: 草稿快照保存成功 } } + /api/platform/v1/last-leg/dispatches/drafts/containers: + post: + summary: 在智能排车草稿中新增车厢 + description: 权限 platform:overseas-warehouse:inbound-order:smart-dispatch;返回服务端生成的字符串 ID。 + security: [{ bearerAuth: [] }] + requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/SmartDispatchDraftContainerCreate' } } } } + responses: { '200': { description: 统一 ApiResponse 包装的草稿车厢, content: { application/json: { schema: { $ref: '#/components/schemas/SmartDispatchDraftContainer' } } } } } + /api/platform/v1/last-leg/dispatches/drafts/{draftToken}/containers/{containerId}: + delete: + summary: 删除智能排车草稿中的空车厢 + description: 权限 platform:overseas-warehouse:inbound-order:smart-dispatch;仅允许当前操作人删除未绑定托盘的草稿车厢。 + security: [{ bearerAuth: [] }] + parameters: + - { name: draftToken, in: path, required: true, schema: { type: string, maxLength: 64 } } + - { name: containerId, in: path, required: true, schema: { type: string } } + responses: { '200': { description: 空车厢删除成功 }, '409': { description: 车厢仍绑定托盘或草稿状态冲突 } } /api/platform/v1/last-leg/overseas-procurements: get: summary: 分页查询海外采购订单 @@ -352,11 +383,40 @@ components: properties: planPalletId: { type: string } draftToken: { type: string, minLength: 1, maxLength: 64 } + draftContainerId: { type: string, nullable: true, description: 托盘所在草稿车厢ID } SmartDispatchDraftRelease: type: object required: [draftToken] properties: draftToken: { type: string, minLength: 1, maxLength: 64 } + SmartDispatchDraftSnapshot: + type: object + required: [draftToken, planIds, containersJson] + properties: + draftToken: { type: string, minLength: 1, maxLength: 64 } + planIds: { type: array, minItems: 1, maxItems: 500, uniqueItems: true, items: { type: string } } + containersJson: { type: string, minLength: 1, maxLength: 20000 } + SmartDispatchDraftContainerCreate: + type: object + required: [draftToken, capacity] + properties: + draftToken: { type: string, minLength: 1, maxLength: 64 } + capacity: { type: integer, minimum: 1, maximum: 500 } + SmartDispatchDraft: + type: object + required: [draftToken, containers] + properties: + draftToken: { type: string } + planIdsJson: { type: string, nullable: true } + containersJson: { type: string, nullable: true } + containers: { type: array, items: { $ref: '#/components/schemas/SmartDispatchDraftContainer' } } + SmartDispatchDraftContainer: + type: object + required: [id, capacity, palletIds] + properties: + id: { type: string } + capacity: { type: integer } + palletIds: { type: array, items: { type: string } } DispatchResponse: type: object properties: diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/controller/Phase4BrowserApiBoundaryTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/controller/Phase4BrowserApiBoundaryTest.java index b15d336f9..8270d8f01 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/controller/Phase4BrowserApiBoundaryTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/controller/Phase4BrowserApiBoundaryTest.java @@ -13,6 +13,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.DeleteMapping; import java.util.Arrays; import java.util.Set; @@ -66,18 +67,24 @@ class Phase4BrowserApiBoundaryTest { } @Test - void smartDispatchIsIndependentlyEnabledWithOnlyCandidateAndPreArrangeEndpoints() { + void smartDispatchOnlyExposesCandidatePreArrangeAndDraftLifecycleEndpoints() { ConditionalOnProperty condition = SmartDispatchController.class.getAnnotation(ConditionalOnProperty.class); assertTrue(condition != null); assertTrue(Arrays.asList(condition.name()).contains("lastleg.smart-dispatch-api.enabled")); assertFalse(condition.matchIfMissing()); assertTrue(Arrays.stream(SmartDispatchController.class.getDeclaredMethods()) - .filter(method -> method.isAnnotationPresent(GetMapping.class)).count() == 1); + .filter(method -> method.isAnnotationPresent(GetMapping.class)).count() == 2); assertTrue(Arrays.stream(SmartDispatchController.class.getDeclaredMethods()) - .filter(method -> method.isAnnotationPresent(PostMapping.class)).count() == 1); + .filter(method -> method.isAnnotationPresent(PostMapping.class)).count() == 6); + assertTrue(Arrays.stream(SmartDispatchController.class.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(DeleteMapping.class)).count() == 1); + assertTrue(Arrays.stream(SmartDispatchController.class.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(DeleteMapping.class)) + .allMatch(method -> Arrays.asList(method.getAnnotation(DeleteMapping.class).value()) + .contains("/drafts/{draftToken}/containers/{containerId}"))); assertFalse(Arrays.stream(SmartDispatchController.class.getDeclaredMethods()) .flatMap(method -> Arrays.stream(method.getAnnotations())) - .anyMatch(annotation -> Set.of("PutMapping", "DeleteMapping", "PatchMapping") + .anyMatch(annotation -> Set.of("PutMapping", "PatchMapping") .contains(annotation.annotationType().getSimpleName()))); } diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/DispatchCandidateMapperContractTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/DispatchCandidateMapperContractTest.java index 67193a290..b3b0b1a3b 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/DispatchCandidateMapperContractTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/DispatchCandidateMapperContractTest.java @@ -21,7 +21,7 @@ class DispatchCandidateMapperContractTest { @Test void candidateQueriesKeepRecordConstructorColumnOrder() throws Exception { assertConstructorColumnOrder(DispatchCandidateMapper.class.getMethod("available", - String.class, List.class, String.class, String.class, String.class)); + String.class, List.class, String.class, String.class, String.class, String.class)); assertConstructorColumnOrder(DispatchCandidateMapper.class.getMethod("lockSelected", String.class, List.class)); } diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/SmartDispatchOpenApiContractTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/SmartDispatchOpenApiContractTest.java index 2ffffb31f..a5a775ba4 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/SmartDispatchOpenApiContractTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/dispatch/SmartDispatchOpenApiContractTest.java @@ -19,6 +19,13 @@ class SmartDispatchOpenApiContractTest { assertTrue(yaml.contains("/api/platform/v1/last-leg/dispatches/draft-locks/lock:")); assertTrue(yaml.contains("/api/platform/v1/last-leg/dispatches/draft-locks/unlock:")); assertTrue(yaml.contains("/api/platform/v1/last-leg/dispatches/draft-locks/release:")); + assertTrue(yaml.contains("/api/platform/v1/last-leg/dispatches/drafts:")); + assertTrue(yaml.contains("/api/platform/v1/last-leg/dispatches/drafts/snapshot:")); + assertTrue(yaml.contains("/api/platform/v1/last-leg/dispatches/drafts/containers:")); + assertTrue(yaml.contains("/api/platform/v1/last-leg/dispatches/drafts/{draftToken}/containers/{containerId}:")); + assertTrue(yaml.contains("draftContainerId:")); + assertTrue(yaml.contains("SmartDispatchDraftContainer:")); + assertTrue(yaml.contains("SmartDispatchDraft:")); assertTrue(yaml.contains("platform:overseas-warehouse:inbound-order:list")); assertTrue(yaml.contains("platform:overseas-warehouse:inbound-order:smart-dispatch")); assertTrue(yaml.contains("SmartDispatchPreArrange"));