feat(firstleg): add indivisible coload groups
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
USE baiyue_firstleg;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_coload_group (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '同柜不可拆组ID',
|
||||
group_no VARCHAR(40) NOT NULL COMMENT '稳定组号CLG-{id}',
|
||||
group_status VARCHAR(16) NOT NULL COMMENT 'ACTIVE、LOCKED、CONSUMED、CANCELLED',
|
||||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '国内收货仓ID快照',
|
||||
channel_code VARCHAR(64) NOT NULL COMMENT '物流渠道编码快照',
|
||||
destination_type VARCHAR(32) NOT NULL COMMENT '目的类型快照',
|
||||
destination_warehouse_id BIGINT UNSIGNED NULL COMMENT '目的仓ID快照',
|
||||
destination_code VARCHAR(64) NULL COMMENT '目的仓或目的港编码快照',
|
||||
locked_plan_id BIGINT UNSIGNED NULL COMMENT 'LOCKED/CONSUMED绑定装柜计划ID',
|
||||
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观版本',
|
||||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建人ID',
|
||||
created_by_name VARCHAR(64) NULL COMMENT '创建人名称快照',
|
||||
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),
|
||||
locked_by BIGINT UNSIGNED NULL,
|
||||
locked_by_name VARCHAR(64) NULL,
|
||||
locked_at DATETIME(3) NULL,
|
||||
consumed_by BIGINT UNSIGNED NULL,
|
||||
consumed_by_name VARCHAR(64) NULL,
|
||||
consumed_at DATETIME(3) NULL,
|
||||
cancelled_by BIGINT UNSIGNED NULL,
|
||||
cancelled_by_name VARCHAR(64) NULL,
|
||||
cancelled_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_coload_group_no (group_no),
|
||||
KEY idx_coload_group_status (group_status,updated_at,id),
|
||||
KEY idx_coload_group_plan (locked_plan_id,group_status),
|
||||
CONSTRAINT fk_coload_group_plan FOREIGN KEY (locked_plan_id) REFERENCES fl_loading_plan(id),
|
||||
CONSTRAINT chk_coload_group_status CHECK (group_status IN ('ACTIVE','LOCKED','CONSUMED','CANCELLED')),
|
||||
CONSTRAINT chk_coload_group_destination CHECK (
|
||||
destination_warehouse_id IS NOT NULL OR destination_code IS NOT NULL),
|
||||
CONSTRAINT chk_coload_group_lock CHECK (
|
||||
(group_status IN ('ACTIVE','CANCELLED') AND locked_plan_id IS NULL)
|
||||
OR (group_status IN ('LOCKED','CONSUMED') AND locked_plan_id IS NOT NULL))
|
||||
) ENGINE=InnoDB COMMENT='同柜且不可拆业务组聚合根';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_coload_group_member (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '组成员ID',
|
||||
group_id BIGINT UNSIGNED NOT NULL COMMENT '同柜不可拆组ID',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '成员客户ID;允许跨客户成组',
|
||||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单ID',
|
||||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '创建时权威入库单ID',
|
||||
full_package_count INT UNSIGNED NOT NULL COMMENT '必须整组分配的满箱数快照',
|
||||
active_flag TINYINT UNSIGNED NULL DEFAULT 1 COMMENT '当前约束唯一标识;取消或消费后置NULL',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
closed_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_coload_group_member (group_id,inbound_id),
|
||||
UNIQUE KEY uk_coload_active_inbound (tenant_id,inbound_id,active_flag),
|
||||
KEY idx_coload_member_order (order_id,created_at,group_id),
|
||||
CONSTRAINT fk_coload_member_group FOREIGN KEY (group_id) REFERENCES fl_coload_group(id),
|
||||
CONSTRAINT fk_coload_member_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order(id),
|
||||
CONSTRAINT chk_coload_member_packages CHECK (full_package_count > 0),
|
||||
CONSTRAINT chk_coload_member_active CHECK (active_flag IS NULL OR active_flag = 1)
|
||||
) ENGINE=InnoDB COMMENT='同柜不可拆组成员与满箱数快照';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_coload_group_history (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '状态历史ID',
|
||||
group_id BIGINT UNSIGNED NOT NULL,
|
||||
action_type VARCHAR(16) NOT NULL COMMENT 'CREATE、CANCEL、LOCK、UNLOCK、CONSUME',
|
||||
from_status VARCHAR(16) NULL,
|
||||
to_status VARCHAR(16) NOT NULL,
|
||||
plan_id BIGINT UNSIGNED NULL,
|
||||
operation_id VARCHAR(128) NOT NULL COMMENT '稳定幂等操作ID',
|
||||
remark VARCHAR(500) NULL,
|
||||
operator_id BIGINT UNSIGNED NOT NULL,
|
||||
operator_name VARCHAR(64) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_coload_history_operation (group_id,action_type,operation_id),
|
||||
KEY idx_coload_history_group (group_id,created_at,id),
|
||||
CONSTRAINT fk_coload_history_group FOREIGN KEY (group_id) REFERENCES fl_coload_group(id),
|
||||
CONSTRAINT chk_coload_history_action CHECK (
|
||||
action_type IN ('CREATE','CANCEL','LOCK','UNLOCK','CONSUME'))
|
||||
) ENGINE=InnoDB COMMENT='同柜不可拆组不可变状态历史';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_coload_command (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '幂等命令ID',
|
||||
actor_id BIGINT UNSIGNED NOT NULL COMMENT '平台或内部操作人ID',
|
||||
action_type VARCHAR(16) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
request_hash CHAR(64) NOT NULL,
|
||||
command_status VARCHAR(16) NOT NULL COMMENT 'PROCESSING、SUCCESS',
|
||||
result_group_id BIGINT UNSIGNED NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
completed_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_coload_command (actor_id,action_type,idempotency_key),
|
||||
CONSTRAINT fk_coload_command_group FOREIGN KEY (result_group_id) REFERENCES fl_coload_group(id),
|
||||
CONSTRAINT chk_coload_command_status CHECK (command_status IN ('PROCESSING','SUCCESS'))
|
||||
) ENGINE=InnoDB COMMENT='同柜不可拆组幂等写命令';
|
||||
@@ -0,0 +1,161 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Firstleg Coload Group API
|
||||
version: 1.0.0
|
||||
description: 同柜且不可拆组平台管理契约;跨客户成组,但仓库、渠道和目的仓港必须一致。
|
||||
paths:
|
||||
/api/platform/v1/first-leg/coload-groups:
|
||||
post:
|
||||
summary: 创建同柜不可拆组
|
||||
description: >-
|
||||
需要 platform:firstleg:smart-loading:assign。orderIds 至少2项;服务端解析每个订单唯一当前权威入库单,
|
||||
要求 WAIT_SPLIT、原始MASTER、未拆分、未分配、无子单或拆分批次,并冻结全部成员满箱数。
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/IdempotencyKey'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CreateColoadGroupRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/ColoadGroupResponse'}
|
||||
'403': {description: 缺少智能排柜分配权限}
|
||||
/api/platform/v1/first-leg/coload-groups/{groupId}/cancel:
|
||||
post:
|
||||
summary: 整组取消同柜不可拆组
|
||||
description: 仅 ACTIVE 组允许取消;不提供单成员编辑,修改成员必须取消后重建。
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/IdempotencyKey'
|
||||
- {name: groupId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CancelColoadGroupRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/ColoadGroupResponse'}
|
||||
'403': {description: 缺少智能排柜分配权限}
|
||||
/api/platform/v1/first-leg/coload-groups/batch-query:
|
||||
post:
|
||||
summary: 按订单ID批量查询同柜不可拆组
|
||||
description: >-
|
||||
需要 platform:firstleg:smart-loading:assign。最多200个订单;返回当前非取消组的组号、状态、
|
||||
全部成员和整组满箱数,供平台订单列表批量补齐,不按单成员修改。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/BatchQueryColoadGroupRequest'}
|
||||
responses:
|
||||
'200':
|
||||
description: 业务响应
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- {$ref: '#/components/schemas/ApiEnvelope'}
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
maxItems: 200
|
||||
items: {$ref: '#/components/schemas/OrderColoadGroupInfo'}
|
||||
'403': {description: 缺少智能排柜分配权限}
|
||||
components:
|
||||
parameters:
|
||||
IdempotencyKey:
|
||||
name: Idempotency-Key
|
||||
in: header
|
||||
required: true
|
||||
schema: {type: string, minLength: 1, maxLength: 128}
|
||||
responses:
|
||||
ColoadGroupResponse:
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- {$ref: '#/components/schemas/ApiEnvelope'}
|
||||
- type: object
|
||||
properties:
|
||||
data: {$ref: '#/components/schemas/ColoadGroup'}
|
||||
schemas:
|
||||
ApiEnvelope:
|
||||
type: object
|
||||
required: [code, message, timestamp]
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
timestamp: {type: string, format: date-time}
|
||||
CreateColoadGroupRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [orderIds]
|
||||
properties:
|
||||
orderIds:
|
||||
type: array
|
||||
minItems: 2
|
||||
maxItems: 100
|
||||
uniqueItems: true
|
||||
items: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
CancelColoadGroupRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [expectedVersion]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
reason: {type: string, maxLength: 500, nullable: true}
|
||||
BatchQueryColoadGroupRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [orderIds]
|
||||
properties:
|
||||
orderIds:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 200
|
||||
uniqueItems: true
|
||||
items: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
ColoadGroupMember:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [tenantId, orderId, inboundId, fullPackageCount]
|
||||
properties:
|
||||
tenantId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
orderId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
inboundId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
fullPackageCount: {type: integer, minimum: 1}
|
||||
ColoadGroup:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [groupId, groupNo, status, warehouseId, channelCode, destinationType,
|
||||
version, members, fullPackageCount, createdAt, updatedAt]
|
||||
properties:
|
||||
groupId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
groupNo: {type: string, pattern: '^CLG-[1-9][0-9]*$'}
|
||||
status: {type: string, enum: [ACTIVE, LOCKED, CONSUMED, CANCELLED]}
|
||||
warehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
channelCode: {type: string}
|
||||
destinationType: {type: string}
|
||||
destinationWarehouseId: {type: string, nullable: true}
|
||||
destinationCode: {type: string, nullable: true}
|
||||
lockedPlanId: {type: string, nullable: true}
|
||||
version: {type: integer, minimum: 0}
|
||||
members:
|
||||
type: array
|
||||
minItems: 2
|
||||
maxItems: 100
|
||||
items: {$ref: '#/components/schemas/ColoadGroupMember'}
|
||||
fullPackageCount: {type: integer, minimum: 2, description: 全部成员满箱数之和}
|
||||
createdAt: {type: string, format: date-time}
|
||||
updatedAt: {type: string, format: date-time}
|
||||
OrderColoadGroupInfo:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [orderId, inboundId, group]
|
||||
properties:
|
||||
orderId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
inboundId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
group: {$ref: '#/components/schemas/ColoadGroup'}
|
||||
Reference in New Issue
Block a user