Merge remote-tracking branch 'origin/test' into dev

# Conflicts:
#	scripts/db/incremental.sql
This commit is contained in:
2026-09-08 23:54:12 +08:00
28 changed files with 1631 additions and 6 deletions
@@ -123,6 +123,49 @@ CREATE TABLE IF NOT EXISTS wh_pallet_operation (
KEY idx_wh_pallet_operation_pallet (warehouse_id,pallet_id,created_at)
) ENGINE=InnoDB COMMENT='实物托盘状态幂等变更与补偿记录';
CREATE TABLE IF NOT EXISTS wh_pallet_batch (
id BIGINT NOT NULL COMMENT '批量托盘预留ID',
warehouse_id BIGINT NOT NULL COMMENT '实际海外仓ID',
batch_key VARCHAR(160) NOT NULL COMMENT '柜级入库计划批次稳定业务键',
request_hash CHAR(64) NOT NULL COMMENT '规范化批量请求SHA-256摘要',
status VARCHAR(32) NOT NULL COMMENT '批次状态:RESERVED、CONFIRMED、COMPENSATED',
attempt_no INT NOT NULL DEFAULT 1 COMMENT '同批次重新预留尝试次数',
confirm_command_key VARCHAR(160) NULL COMMENT '确认命令幂等键',
confirm_command_hash CHAR(64) NULL COMMENT '确认命令规范化SHA-256摘要',
compensate_command_key VARCHAR(160) NULL COMMENT '补偿命令幂等键',
compensate_command_hash CHAR(64) NULL COMMENT '补偿命令规范化SHA-256摘要',
compensate_reason VARCHAR(500) NULL COMMENT '补偿原因',
confirmed_at DATETIME(3) NULL COMMENT '确认绑定时间',
compensated_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_wh_pallet_batch_key (warehouse_id,batch_key),
KEY idx_wh_pallet_batch_status (warehouse_id,status,updated_at)
) ENGINE=InnoDB COMMENT='柜级入库计划批量托盘预留与确认记录';
CREATE TABLE IF NOT EXISTS wh_pallet_batch_item (
id BIGINT NOT NULL COMMENT '批量托盘明细ID',
batch_id BIGINT NOT NULL COMMENT '所属批量托盘预留ID',
customer_id BIGINT NOT NULL COMMENT '实际占用客户ID',
warehouse_id BIGINT NOT NULL COMMENT '实际海外仓ID',
business_key VARCHAR(100) NOT NULL COMMENT '柜号入库计划序号组成的稳定业务键',
inbound_plan_no VARCHAR(128) NOT NULL COMMENT '入库计划号快照',
sequence_no INT NOT NULL COMMENT '计划内托盘序号',
pallet_id BIGINT NOT NULL COMMENT '公共实物托盘ID',
pallet_code VARCHAR(64) NOT NULL COMMENT '资源服务生成的规范托盘码快照',
location_id BIGINT NOT NULL COMMENT '公共托盘当前预留库位ID',
resource_lock_id BIGINT NOT NULL COMMENT '按客户隔离的资源锁ID',
status VARCHAR(32) NOT NULL COMMENT '明细状态:RESERVED、CONFIRMED、RELEASED',
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_wh_pallet_batch_item_business (customer_id,warehouse_id,business_key),
UNIQUE KEY uk_wh_pallet_batch_item_sequence (batch_id,sequence_no),
UNIQUE KEY uk_wh_pallet_batch_item_pallet (pallet_id),
KEY idx_wh_pallet_batch_item_batch (batch_id,status)
) ENGINE=InnoDB COMMENT='柜级入库计划批量托盘明细与资源锁关联';
ALTER TABLE wh_pallet COMMENT='仓库托盘表:按租户和仓库维护托盘、容量、单价及状态';
ALTER TABLE wh_pallet
MODIFY COLUMN id BIGINT UNSIGNED NOT NULL COMMENT '托盘ID(雪花ID)',
@@ -0,0 +1,93 @@
openapi: 3.0.3
info:
title: Warehouse Overseas Internal Pallet Batch API
version: 1.0.0
paths:
/internal/v1/warehouse-pallet-batches/ensure-reserved:
post:
summary: 按柜级入库计划稳定业务键批量确保并预留公共托盘
description: 仅内部服务令牌;同柜批次可跨客户。物理托盘固定customer_id=0,每个item资源锁使用其真实customerId。同warehouseId+batchKey同请求重放不重复造码,异请求返回A0409;COMPENSATED批次同请求复用原托盘并开启新attempt。
security: [{ internalServiceToken: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/EnsureReservedRequest' } } } }
responses:
'200': { description: 批量预留结果;业务冲突仍为HTTP 200且响应体code=A0409, content: { application/json: { schema: { $ref: '#/components/schemas/BatchApiResponse' } } } }
'401': { description: 内部服务令牌无效 }
/internal/v1/warehouse-pallet-batches/confirm:
post:
summary: 确认Lastleg已持久化托盘快照并建立业务绑定
description: 仅RESERVED可确认;重复确认返回同一结果,不生成新托盘或资源锁。
security: [{ internalServiceToken: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/ConfirmRequest' } } } }
responses:
'200': { description: 已确认批次;业务冲突仍为HTTP 200且响应体code=A0409, content: { application/json: { schema: { $ref: '#/components/schemas/BatchApiResponse' } } } }
/internal/v1/warehouse-pallet-batches/compensate:
post:
summary: 补偿释放Lastleg本地失败对应的本批未确认资源
description: 仅RESERVED且尚未建立业务绑定的本批资源可补偿;CONFIRMED或已被后续占用时返回A0409。
security: [{ internalServiceToken: [] }]
requestBody: { required: true, content: { application/json: { schema: { $ref: '#/components/schemas/CompensateRequest' } } } }
responses:
'200': { description: 已补偿批次;业务冲突仍为HTTP 200且响应体code=A0409, content: { application/json: { schema: { $ref: '#/components/schemas/BatchApiResponse' } } } }
components:
securitySchemes:
internalServiceToken: { type: apiKey, in: header, name: X-Internal-Service-Token }
schemas:
BatchItemRequest:
type: object
required: [customerId, businessKey, inboundPlanNo, sequenceNo, palletType]
properties:
customerId: { type: string, pattern: '^[1-9][0-9]*$' }
businessKey: { type: string, minLength: 1, maxLength: 100 }
inboundPlanNo: { type: string, minLength: 1, maxLength: 128 }
sequenceNo: { type: integer, minimum: 1 }
palletType: { type: string, minLength: 1, maxLength: 32 }
fbaWarehouseCode: { type: string, nullable: true, maxLength: 128 }
EnsureReservedRequest:
type: object
required: [warehouseId, batchKey, items]
properties:
warehouseId: { type: string, pattern: '^[1-9][0-9]*$' }
batchKey: { type: string, minLength: 1, maxLength: 160 }
items: { type: array, minItems: 1, maxItems: 200, items: { $ref: '#/components/schemas/BatchItemRequest' } }
ConfirmRequest:
type: object
required: [warehouseId, batchKey, commandKey]
properties:
warehouseId: { type: string, pattern: '^[1-9][0-9]*$' }
batchKey: { type: string, minLength: 1, maxLength: 160 }
commandKey: { type: string, minLength: 1, maxLength: 160 }
CompensateRequest:
allOf:
- { $ref: '#/components/schemas/ConfirmRequest' }
- type: object
required: [reason]
properties: { reason: { type: string, minLength: 1, maxLength: 500 } }
BatchItemResponse:
type: object
required: [customerId, businessKey, inboundPlanNo, sequenceNo, palletId, palletCode, locationId, resourceLockId, status]
properties:
customerId: { type: string, pattern: '^[1-9][0-9]*$' }
businessKey: { type: string }
inboundPlanNo: { type: string }
sequenceNo: { type: integer, minimum: 1 }
palletId: { type: string, pattern: '^[1-9][0-9]*$' }
palletCode: { type: string, maxLength: 64 }
locationId: { type: string, pattern: '^[1-9][0-9]*$' }
resourceLockId: { type: string, pattern: '^[1-9][0-9]*$' }
status: { type: string, enum: [RESERVED, CONFIRMED, RELEASED] }
BatchResponse:
type: object
required: [batchId, batchKey, status, attemptNo, replayed, items]
properties:
batchId: { type: string, pattern: '^[1-9][0-9]*$' }
batchKey: { type: string }
status: { type: string, enum: [RESERVED, CONFIRMED, COMPENSATED] }
attemptNo: { type: integer, minimum: 1 }
replayed: { type: boolean }
items: { type: array, items: { $ref: '#/components/schemas/BatchItemResponse' } }
BatchApiResponse:
type: object
properties:
code: { type: string, example: '200' }
message: { type: string }
data: { $ref: '#/components/schemas/BatchResponse' }