优化
This commit is contained in:
-1
@@ -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;
|
||||
|
||||
|
||||
@@ -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='智能排车草稿装车托盘';
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
+11
-4
@@ -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())));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
|
||||
+7
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user