Merge branch 'test' of http://8.163.30.128:10082/java-project/baiyue-firstleg into dev
This commit is contained in:
@@ -71,12 +71,12 @@ CREATE TABLE IF NOT EXISTS fl_container_fee_detail (
|
||||
fee_item_code_snapshot VARCHAR(50) NOT NULL COMMENT '费用项目稳定编码快照',
|
||||
fee_item_name_snapshot VARCHAR(128) NOT NULL COMMENT '费用项目名称快照',
|
||||
direction VARCHAR(16) NOT NULL COMMENT '费用方向:RECEIVABLE应收、PAYABLE应付',
|
||||
partner_id BIGINT UNSIGNED NULL COMMENT '应付往来单位ID快照;应收为空',
|
||||
partner_name VARCHAR(200) NULL COMMENT '应付往来单位名称快照;应收为空',
|
||||
supplier_id BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空',
|
||||
supplier_name VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空',
|
||||
supplier_category VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商',
|
||||
business_date DATE NOT NULL COMMENT '费用业务日期',
|
||||
amount DECIMAL(18,2) NOT NULL COMMENT 'CNY费用金额',
|
||||
currency CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种,首期固定CNY',
|
||||
settlement_included TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否参与财务结算来源池',
|
||||
review_status VARCHAR(16) NOT NULL DEFAULT 'PENDING' COMMENT '审核状态:PENDING待审核、APPROVED通过、REJECTED驳回',
|
||||
source_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '来源状态:ACTIVE有效、VOIDED作废',
|
||||
source_version INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '费用来源版本;调整递增,作废携带当前版本',
|
||||
@@ -91,22 +91,52 @@ CREATE TABLE IF NOT EXISTS fl_container_fee_detail (
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '最后更新时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_container_fee_customer_plan (tenant_id, loading_plan_id, source_status),
|
||||
KEY idx_container_fee_settlement (tenant_id, review_status, settlement_included, source_status),
|
||||
KEY idx_container_fee_review (tenant_id, review_status, source_status),
|
||||
CONSTRAINT chk_container_fee_direction CHECK (direction IN ('RECEIVABLE', 'PAYABLE')),
|
||||
CONSTRAINT chk_container_fee_currency CHECK (currency = 'CNY'),
|
||||
CONSTRAINT chk_container_fee_amount CHECK (amount >= 0),
|
||||
CONSTRAINT chk_container_fee_review CHECK (review_status IN ('PENDING', 'APPROVED', 'REJECTED')),
|
||||
CONSTRAINT chk_container_fee_source CHECK (source_status IN ('ACTIVE', 'VOIDED'))
|
||||
) ENGINE=InnoDB COMMENT='柜次级人工应收应付费用来源;审核通过且参与结算后同步Finance';
|
||||
) ENGINE=InnoDB COMMENT='柜次级人工应收应付费用来源;审核通过后自动同步Finance';
|
||||
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'partner_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '应付往来单位ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'partner_name',
|
||||
"VARCHAR(200) NULL COMMENT '应付往来单位名称快照;应收为空' AFTER partner_id");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'partner_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '应付往来单位ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'partner_name',
|
||||
"VARCHAR(200) NULL COMMENT '应付往来单位名称快照;应收为空' AFTER partner_id");
|
||||
CREATE TABLE IF NOT EXISTS fl_container_fee_source_snapshot (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '柜次费用快照ID',
|
||||
source_fee_id BIGINT UNSIGNED NOT NULL COMMENT '柜次费用来源ID',
|
||||
source_version INT UNSIGNED NOT NULL COMMENT '费用来源版本',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '柜次ID',
|
||||
container_booking_id BIGINT UNSIGNED NULL COMMENT '订舱柜ID快照',
|
||||
container_no VARCHAR(64) NULL COMMENT '柜号快照',
|
||||
fee_item_id BIGINT UNSIGNED NOT NULL COMMENT '费用项目ID快照',
|
||||
fee_item_code VARCHAR(50) NOT NULL COMMENT '费用项目编码快照',
|
||||
fee_item_name VARCHAR(128) NOT NULL COMMENT '费用项目名称快照',
|
||||
direction VARCHAR(16) NOT NULL COMMENT '费用方向',
|
||||
supplier_id BIGINT UNSIGNED NULL COMMENT 'Warehouse供应商ID快照',
|
||||
supplier_name VARCHAR(200) NULL COMMENT 'Warehouse供应商名称快照',
|
||||
supplier_category VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商',
|
||||
business_date DATE NOT NULL COMMENT '费用业务日期',
|
||||
amount DECIMAL(18,2) NOT NULL COMMENT 'CNY费用金额',
|
||||
currency CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种,首期固定CNY',
|
||||
review_status VARCHAR(16) NOT NULL COMMENT '审核状态快照',
|
||||
reviewed_at DATETIME(3) NULL COMMENT '审核时间快照',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '快照创建时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_container_fee_source_version (loading_plan_id, source_fee_id, source_version),
|
||||
KEY idx_container_fee_snapshot_tenant (tenant_id, loading_plan_id)
|
||||
) ENGINE=InnoDB COMMENT='柜次费用不可变版本快照';
|
||||
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_name',
|
||||
"VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空' AFTER supplier_id");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_category',
|
||||
"VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商' AFTER supplier_name");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_name',
|
||||
"VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空' AFTER supplier_id");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_category',
|
||||
"VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商' AFTER supplier_name");
|
||||
DROP PROCEDURE IF EXISTS firstleg_finance_add_column;
|
||||
|
||||
DROP PROCEDURE IF EXISTS firstleg_finance_assert_schema;
|
||||
@@ -125,13 +155,13 @@ BEGIN
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'fl_container_fee_detail'
|
||||
AND column_name IN ('partner_id', 'partner_name')) <> 2 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Firstleg Finance迁移失败:柜次费用往来单位快照字段不完整';
|
||||
AND column_name IN ('supplier_id', 'supplier_name', 'supplier_category')) <> 3 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Firstleg Finance迁移失败:柜次费用供应商快照字段不完整';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'fl_inbound_fee_detail'
|
||||
AND column_name IN ('partner_id', 'partner_name')) <> 2 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Firstleg Finance迁移失败:入库费用往来单位快照字段不完整';
|
||||
AND column_name IN ('supplier_id', 'supplier_name', 'supplier_category')) <> 3 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Firstleg Finance迁移失败:入库费用供应商快照字段不完整';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
@@ -7,6 +7,7 @@ CREATE TABLE IF NOT EXISTS fl_fee_item (
|
||||
category VARCHAR(64) NOT NULL COMMENT '费用分类',
|
||||
trade_scope VARCHAR(20) NOT NULL COMMENT 'DOMESTIC国内、INTERNATIONAL国际',
|
||||
fee_code VARCHAR(50) NOT NULL COMMENT '稳定费用编码;平台公共主数据固定编码',
|
||||
supplier_category VARCHAR(16) NULL COMMENT 'PAYABLE供应商类别约束;为空时可选任意ACTIVE供应商',
|
||||
fee_type_name VARCHAR(128) NOT NULL COMMENT '费用类型名称',
|
||||
pickup_payable TINYINT(1) NOT NULL DEFAULT 0 COMMENT '提柜应付是否参与',
|
||||
pickup_receivable TINYINT(1) NOT NULL DEFAULT 0 COMMENT '提柜应收是否参与',
|
||||
@@ -36,8 +37,9 @@ CREATE TABLE IF NOT EXISTS fl_inbound_fee_detail (
|
||||
fee_item_id BIGINT UNSIGNED NOT NULL COMMENT '绑定费用项目ID',
|
||||
inbound_code VARCHAR(40) NOT NULL COMMENT '入仓码快照',
|
||||
direction VARCHAR(16) NOT NULL COMMENT 'RECEIVABLE应收、PAYABLE应付',
|
||||
partner_id BIGINT UNSIGNED NULL COMMENT '应付往来单位ID快照;应收为空',
|
||||
partner_name VARCHAR(200) NULL COMMENT '应付往来单位名称快照;应收为空',
|
||||
supplier_id BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空',
|
||||
supplier_name VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空',
|
||||
supplier_category VARCHAR(16) NULL COMMENT '费用项目供应商类别快照;应收为空',
|
||||
business_date DATE NOT NULL COMMENT '业务发生日',
|
||||
fee_category_snapshot VARCHAR(64) NOT NULL COMMENT '费用分类快照',
|
||||
trade_scope_snapshot VARCHAR(20) NOT NULL COMMENT '国内国际快照',
|
||||
|
||||
@@ -45,14 +45,10 @@ CALL phase2_measure_add_column('fl_inbound_measurement_item', 'box_specification
|
||||
'VARCHAR(64) NOT NULL DEFAULT '''' COMMENT ''箱规标识'' AFTER item_no');
|
||||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'girth_cm',
|
||||
'DECIMAL(12,3) NOT NULL DEFAULT 0 COMMENT ''周长cm,规则为长+2*(宽+高)'' AFTER height_cm');
|
||||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'unit_weight_kg',
|
||||
'DECIMAL(18,3) NULL COMMENT ''单箱重量kg'' AFTER girth_cm');
|
||||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'unit_volume_cbm',
|
||||
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''单箱业务体积m3'' AFTER unit_weight_kg');
|
||||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'total_weight_kg',
|
||||
'DECIMAL(18,3) NULL COMMENT ''本箱规总重量kg'' AFTER volume_cbm');
|
||||
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''单箱业务体积m3'' AFTER volume_cbm');
|
||||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'total_volume_cbm',
|
||||
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''本箱规总体积m3'' AFTER total_weight_kg');
|
||||
'DECIMAL(18,4) NOT NULL DEFAULT 0 COMMENT ''本箱规总体积m3'' AFTER volume_cbm');
|
||||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'volume_weight_kg',
|
||||
'DECIMAL(18,3) NOT NULL DEFAULT 0 COMMENT ''本箱规材积重kg'' AFTER total_volume_cbm');
|
||||
CALL phase2_measure_add_column('fl_inbound_measurement_item', 'over_limit_warning',
|
||||
@@ -77,7 +73,8 @@ CREATE TABLE IF NOT EXISTS fl_inbound_weight_revision (
|
||||
before_weight_unit VARCHAR(8) NULL COMMENT '变更前单位',
|
||||
after_weight_value DECIMAL(18,3) NULL COMMENT '变更后原单位重量',
|
||||
after_weight_unit VARCHAR(8) NULL COMMENT '变更后单位',
|
||||
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的重量kg',
|
||||
gross_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的含托盘毛重kg',
|
||||
net_weight_kg DECIMAL(18,3) NULL COMMENT '确认毛重扣除托盘皮重后的货物净重kg',
|
||||
reason VARCHAR(500) NULL COMMENT '人工修正原因',
|
||||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||||
|
||||
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||||
task_id BIGINT UNSIGNED NULL COMMENT '头程履约任务ID',
|
||||
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单服务订单ID,不建立跨库外键',
|
||||
order_no VARCHAR(32) NOT NULL COMMENT '订单号快照',
|
||||
inbound_code VARCHAR(40) NOT NULL COMMENT '客户内唯一入仓码',
|
||||
inbound_code VARCHAR(40) NOT NULL COMMENT '全平台唯一入仓码',
|
||||
process_status VARCHAR(24) NOT NULL DEFAULT 'WAIT_MEASURE' COMMENT '入库流程状态:WAIT_MEASURE待测量、MEASURING测量中、MEASURE_PENDING待审核、MEASURE_REJECTED测量驳回、WAIT_PRICING待计费、FEE_CONFIRMING费用确认中、INBOUNDING入库收口中、WAIT_SPLIT待分柜',
|
||||
order_status_snapshot VARCHAR(24) NOT NULL COMMENT '订单状态快照',
|
||||
measure_review_status VARCHAR(24) NOT NULL DEFAULT 'NOT_SUBMITTED' COMMENT '测量审核状态:NOT_SUBMITTED未提交、PENDING待审核、APPROVED已通过、REJECTED已驳回',
|
||||
@@ -42,13 +42,13 @@ CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||||
hold_flag TINYINT(1) NOT NULL DEFAULT 0 COMMENT '当前是否扣货',
|
||||
current_hold_reason VARCHAR(500) NULL COMMENT '当前扣货理由快照',
|
||||
total_package_count INT UNSIGNED NULL COMMENT '确认后的总箱数',
|
||||
actual_gross_weight_kg DECIMAL(18,3) NULL COMMENT '确认后的实际总毛重kg',
|
||||
actual_gross_weight_kg DECIMAL(18,3) NULL COMMENT '已确认称重图片含托盘毛重汇总kg',
|
||||
chargeable_weight_kg DECIMAL(18,3) NULL COMMENT '当前费用快照的计费重量kg',
|
||||
actual_volume_cbm DECIMAL(18,4) NULL COMMENT '确认后的实际总体积m3',
|
||||
chargeable_volume_cbm DECIMAL(18,4) NULL COMMENT '当前费用快照的计费体积m3',
|
||||
pallet_count INT UNSIGNED NULL COMMENT '已分配总托盘数快照',
|
||||
volume_weight_kg DECIMAL(18,3) NULL COMMENT '总材积重kg',
|
||||
actual_net_weight_kg DECIMAL(18,3) NULL COMMENT '总净重kg',
|
||||
actual_net_weight_kg DECIMAL(18,3) NULL COMMENT '已确认称重图片扣除托盘皮重后的货物净重汇总kg',
|
||||
measure_completed_at DATETIME(3) NULL COMMENT '测量完成时间',
|
||||
inbounded_at DATETIME(3) NULL COMMENT '入库完成时间',
|
||||
express_company_code VARCHAR(64) NULL COMMENT '快递公司编码快照',
|
||||
@@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||||
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
|
||||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_inbound_tenant_code (tenant_id, inbound_code),
|
||||
UNIQUE KEY uk_inbound_code (inbound_code),
|
||||
UNIQUE KEY uk_inbound_tenant_order (tenant_id, order_id),
|
||||
KEY idx_inbound_process_time (tenant_id, process_status, created_at, id),
|
||||
KEY idx_inbound_measure_time (tenant_id, measure_review_status, created_at, id),
|
||||
@@ -77,6 +77,33 @@ CREATE TABLE IF NOT EXISTS fl_inbound_order (
|
||||
CONSTRAINT fk_inbound_task FOREIGN KEY (task_id) REFERENCES fl_fulfillment_task (id)
|
||||
) ENGINE=InnoDB COMMENT='二期头程入库单聚合根和列表查询快照';
|
||||
|
||||
-- 既有开发库若存在跨客户重复码则严格失败,禁止自动覆盖或改写入仓码。
|
||||
SET @inbound_global_code_index_sql = IF(
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_order'
|
||||
AND index_name = 'uk_inbound_code'
|
||||
),
|
||||
'ALTER TABLE baiyue_firstleg.fl_inbound_order ADD UNIQUE KEY uk_inbound_code (inbound_code)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE inbound_global_code_index_stmt FROM @inbound_global_code_index_sql;
|
||||
EXECUTE inbound_global_code_index_stmt;
|
||||
DEALLOCATE PREPARE inbound_global_code_index_stmt;
|
||||
|
||||
SET @inbound_tenant_code_index_drop_sql = IF(
|
||||
EXISTS(
|
||||
SELECT 1 FROM information_schema.statistics
|
||||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_inbound_order'
|
||||
AND index_name = 'uk_inbound_tenant_code'
|
||||
),
|
||||
'ALTER TABLE baiyue_firstleg.fl_inbound_order DROP INDEX uk_inbound_tenant_code',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE inbound_tenant_code_index_drop_stmt FROM @inbound_tenant_code_index_drop_sql;
|
||||
EXECUTE inbound_tenant_code_index_drop_stmt;
|
||||
DEALLOCATE PREPARE inbound_tenant_code_index_drop_stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_inbound_pickup_snapshot (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '提货信息快照ID',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
@@ -215,11 +242,9 @@ CREATE TABLE IF NOT EXISTS fl_inbound_measurement_item (
|
||||
width_cm DECIMAL(12,3) NOT NULL COMMENT '原始宽度cm',
|
||||
height_cm DECIMAL(12,3) NOT NULL COMMENT '原始高度cm',
|
||||
girth_cm DECIMAL(12,3) NOT NULL COMMENT '周长cm,规则为长+2*(宽+高)',
|
||||
unit_weight_kg DECIMAL(18,3) NULL COMMENT '单箱重量kg,第三方仅回传尺寸时可为空',
|
||||
unit_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '单箱业务体积m3',
|
||||
volume_raw_cbm DECIMAL(18,6) NOT NULL COMMENT '原始体积m3',
|
||||
volume_cbm DECIMAL(18,4) NOT NULL COMMENT '业务体积m3',
|
||||
total_weight_kg DECIMAL(18,3) NULL COMMENT '本箱规总重量kg',
|
||||
total_volume_cbm DECIMAL(18,4) NOT NULL COMMENT '本箱规总体积m3',
|
||||
volume_weight_kg DECIMAL(18,3) NOT NULL COMMENT '本箱规材积重kg',
|
||||
calculation_rule_version VARCHAR(64) NOT NULL COMMENT '体积计算和舍入规则版本',
|
||||
@@ -249,7 +274,6 @@ CREATE TABLE IF NOT EXISTS fl_mojia_measurement_receipt (
|
||||
length_cm DECIMAL(12,3) NOT NULL COMMENT '设备实测长度cm',
|
||||
width_cm DECIMAL(12,3) NOT NULL COMMENT '设备实测宽度cm',
|
||||
height_cm DECIMAL(12,3) NOT NULL COMMENT '设备实测高度cm',
|
||||
weight_kg DECIMAL(18,3) NULL COMMENT '设备实测重量kg',
|
||||
machine_no VARCHAR(64) NOT NULL COMMENT '设备编号',
|
||||
measured_at DATETIME(3) NOT NULL COMMENT '设备测量时间或服务接收时间',
|
||||
image_file_id BIGINT UNSIGNED NOT NULL COMMENT 'File服务中的墨家联调图片ID',
|
||||
@@ -270,6 +294,11 @@ CREATE TABLE IF NOT EXISTS fl_inbound_weight_result (
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
inbound_id BIGINT UNSIGNED NOT NULL COMMENT '入库单ID',
|
||||
file_id BIGINT UNSIGNED NOT NULL COMMENT '称重图片文件ID',
|
||||
pallet_id BIGINT UNSIGNED NOT NULL COMMENT 'Warehouse托盘ID',
|
||||
pallet_code VARCHAR(50) NOT NULL COMMENT '托盘编码快照',
|
||||
pallet_type VARCHAR(32) NOT NULL COMMENT '托盘类型快照',
|
||||
pallet_capacity_cbm DECIMAL(18,4) NOT NULL COMMENT '托盘容量CBM快照',
|
||||
pallet_tare_weight_kg DECIMAL(18,3) NOT NULL COMMENT '托盘皮重KG快照',
|
||||
ocr_provider VARCHAR(64) NULL COMMENT 'OCR服务商编码',
|
||||
ocr_raw_text VARCHAR(1000) NULL COMMENT 'OCR原始文本',
|
||||
ocr_weight_value DECIMAL(18,3) NULL COMMENT 'OCR识别原始重量值',
|
||||
@@ -277,7 +306,8 @@ CREATE TABLE IF NOT EXISTS fl_inbound_weight_result (
|
||||
confidence DECIMAL(6,5) NULL COMMENT '识别置信度',
|
||||
adjusted_weight_value DECIMAL(18,3) NULL COMMENT '人工修正后的原单位重量',
|
||||
adjusted_weight_unit VARCHAR(8) NULL COMMENT '人工修正后的单位',
|
||||
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认并统一换算后的重量kg',
|
||||
gross_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的含托盘毛重kg',
|
||||
net_weight_kg DECIMAL(18,3) NULL COMMENT '确认毛重扣除托盘皮重后的货物净重kg',
|
||||
recognition_status VARCHAR(24) NOT NULL DEFAULT 'PENDING' COMMENT '重量识别状态:PENDING待识别、PENDING_CONFIRM待确认、NEEDS_MANUAL需人工录入、CONFIRMED已确认',
|
||||
adjustment_reason VARCHAR(500) NULL COMMENT '人工修正原因',
|
||||
adjusted_by BIGINT UNSIGNED NULL COMMENT '修正人ID',
|
||||
@@ -293,8 +323,9 @@ CREATE TABLE IF NOT EXISTS fl_inbound_weight_result (
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_weight_inbound_file (tenant_id, inbound_id, file_id),
|
||||
KEY idx_weight_inbound_status (tenant_id, inbound_id, recognition_status),
|
||||
KEY idx_weight_inbound_pallet (tenant_id, inbound_id, pallet_id),
|
||||
CONSTRAINT fk_weight_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||||
) ENGINE=InnoDB COMMENT='称重图片OCR原值、人工修正值和确认重量';
|
||||
) ENGINE=InnoDB COMMENT='称重图片、托盘皮重快照、OCR毛重和确认净重';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_inbound_weight_revision (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT 'OCR重量修订记录ID',
|
||||
@@ -307,7 +338,8 @@ CREATE TABLE IF NOT EXISTS fl_inbound_weight_revision (
|
||||
before_weight_unit VARCHAR(8) NULL COMMENT '变更前单位',
|
||||
after_weight_value DECIMAL(18,3) NULL COMMENT '变更后原单位重量',
|
||||
after_weight_unit VARCHAR(8) NULL COMMENT '变更后单位',
|
||||
final_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的重量kg',
|
||||
gross_weight_kg DECIMAL(18,3) NULL COMMENT '确认时统一换算后的含托盘毛重kg',
|
||||
net_weight_kg DECIMAL(18,3) NULL COMMENT '确认毛重扣除托盘皮重后的货物净重kg',
|
||||
reason VARCHAR(500) NULL COMMENT '人工修正原因',
|
||||
operator_id BIGINT UNSIGNED NOT NULL COMMENT '操作人ID',
|
||||
operator_name VARCHAR(64) NULL COMMENT '操作人名称快照',
|
||||
@@ -421,6 +453,8 @@ CREATE TABLE IF NOT EXISTS fl_price_channel_catalog (
|
||||
price_model VARCHAR(32) NOT NULL COMMENT '默认价格模型',
|
||||
source_sheet_name VARCHAR(128) NOT NULL COMMENT '来源Sheet名称;海派为同一Sheet',
|
||||
aliases_json JSON NOT NULL COMMENT '上传名称别名数组',
|
||||
inbound_code_prefix VARCHAR(6) NOT NULL COMMENT '普通目的仓进仓编码前缀;包税与不包税共用',
|
||||
overseas_inbound_code_prefix VARCHAR(6) NULL COMMENT '海外仓中转进仓编码前缀;包税与不包税共用,为空时使用普通前缀',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '目录状态:ACTIVE启用、DISABLED停用',
|
||||
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 '最后更新时间',
|
||||
@@ -511,26 +545,29 @@ CREATE TABLE IF NOT EXISTS fl_price_book_channel (
|
||||
) ENGINE=InnoDB COMMENT='价格版本渠道及船期快照';
|
||||
|
||||
INSERT INTO fl_price_channel_catalog
|
||||
(id, channel_code, display_name, price_model, source_sheet_name, aliases_json, status)
|
||||
(id, channel_code, display_name, price_model, source_sheet_name, aliases_json,
|
||||
inbound_code_prefix, overseas_inbound_code_prefix, status)
|
||||
VALUES
|
||||
(1, 'MATSON_CLX_TRUCK', '美森正班海卡', 'FBA_WAREHOUSE_RATE', '美森正班海卡', JSON_ARRAY('美森正班海卡'), 'ACTIVE'),
|
||||
(2, 'MATSON_MAX_TRUCK', '美森加班海卡', 'FBA_WAREHOUSE_RATE', '美森加班海卡', JSON_ARRAY('美森加班海卡'), 'ACTIVE'),
|
||||
(3, 'EXX_TRUCK', 'EXX海卡', 'FBA_WAREHOUSE_RATE', 'EXX海卡', JSON_ARRAY('EXX海卡'), 'ACTIVE'),
|
||||
(4, 'HEDE_TRUCK', '合德海卡', 'FBA_WAREHOUSE_RATE', '合德海卡', JSON_ARRAY('合德海卡'), 'ACTIVE'),
|
||||
(5, 'EXX_RAIL_NY', '纽约EXX海铁', 'FBA_WAREHOUSE_RATE', '纽约EXX海铁', JSON_ARRAY('纽约EXX海铁'), 'ACTIVE'),
|
||||
(6, 'OA_LA_TRUCK', '洛杉矶普船海卡', 'FBA_WAREHOUSE_RATE', '洛杉矶普船海卡', JSON_ARRAY('洛杉矶普船海卡'), 'ACTIVE'),
|
||||
(7, 'OA_LA_FULL_TRUCK', '洛杉矶普船全卡', 'FBA_WAREHOUSE_RATE', '洛杉矶普船全卡', JSON_ARRAY('洛杉矶普船全卡'), 'ACTIVE'),
|
||||
(8, 'OA_CHICAGO_RAIL', '芝加哥普船海铁', 'FBA_WAREHOUSE_RATE', '芝加哥普船海铁', JSON_ARRAY('芝加哥普船海铁'), 'ACTIVE'),
|
||||
(9, 'OA_HOUSTON_TRUCK', '休斯顿普船海卡', 'FBA_WAREHOUSE_RATE', '休斯顿普船海卡', JSON_ARRAY('休斯顿普船海卡'), 'ACTIVE'),
|
||||
(10, 'OA_NEW_YORK_TRUCK', '纽约普船海卡', 'FBA_WAREHOUSE_RATE', '纽约普船海卡', JSON_ARRAY('纽约普船海卡'), 'ACTIVE'),
|
||||
(11, 'OA_SAVANNAH_TRUCK', '萨凡纳普船海卡', 'FBA_WAREHOUSE_RATE', '萨凡纳普船海卡', JSON_ARRAY('萨凡纳普船海卡'), 'ACTIVE'),
|
||||
(12, 'MATSON_CLX_PARCEL', '美森正班海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('美森正班海派'), 'ACTIVE'),
|
||||
(13, 'MATSON_MAX_PARCEL', '美森加班海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('美森加班海派'), 'ACTIVE'),
|
||||
(14, 'HEDE_PARCEL', '合德海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('合德海派'), 'ACTIVE'),
|
||||
(15, 'OA_PARCEL', '普船海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('普船海派'), 'ACTIVE')
|
||||
(1, 'MATSON_CLX_TRUCK', '美森正班海卡', 'FBA_WAREHOUSE_RATE', '美森正班海卡', JSON_ARRAY('美森正班海卡'), 'ZBYHK', 'ZBYHWK', 'ACTIVE'),
|
||||
(2, 'MATSON_MAX_TRUCK', '美森加班海卡', 'FBA_WAREHOUSE_RATE', '美森加班海卡', JSON_ARRAY('美森加班海卡'), 'BYHK', 'BYHWK', 'ACTIVE'),
|
||||
(3, 'EXX_TRUCK', 'EXX海卡', 'FBA_WAREHOUSE_RATE', 'EXX海卡', JSON_ARRAY('EXX海卡'), 'EXXHK', 'EXXHWK', 'ACTIVE'),
|
||||
(4, 'HEDE_TRUCK', '合德海卡', 'FBA_WAREHOUSE_RATE', '合德海卡', JSON_ARRAY('合德海卡'), 'HDHK', 'HDHWK', 'ACTIVE'),
|
||||
(5, 'EXX_RAIL_NY', '纽约EXX海铁', 'FBA_WAREHOUSE_RATE', '纽约EXX海铁', JSON_ARRAY('纽约EXX海铁'), 'NYEXX', NULL, 'ACTIVE'),
|
||||
(6, 'OA_LA_TRUCK', '洛杉矶普船海卡', 'FBA_WAREHOUSE_RATE', '洛杉矶普船海卡', JSON_ARRAY('洛杉矶普船海卡'), 'PCHK', 'PCHWK', 'ACTIVE'),
|
||||
(7, 'OA_LA_FULL_TRUCK', '洛杉矶普船全卡', 'FBA_WAREHOUSE_RATE', '洛杉矶普船全卡', JSON_ARRAY('洛杉矶普船全卡'), 'PCKK', NULL, 'ACTIVE'),
|
||||
(8, 'OA_CHICAGO_RAIL', '芝加哥普船海铁', 'FBA_WAREHOUSE_RATE', '芝加哥普船海铁', JSON_ARRAY('芝加哥普船海铁'), 'CPHT', NULL, 'ACTIVE'),
|
||||
(9, 'OA_HOUSTON_TRUCK', '休斯顿普船海卡', 'FBA_WAREHOUSE_RATE', '休斯顿普船海卡', JSON_ARRAY('休斯顿普船海卡'), 'HPHK', NULL, 'ACTIVE'),
|
||||
(10, 'OA_NEW_YORK_TRUCK', '纽约普船海卡', 'FBA_WAREHOUSE_RATE', '纽约普船海卡', JSON_ARRAY('纽约普船海卡'), 'NPHK', 'NPHWK', 'ACTIVE'),
|
||||
(11, 'OA_SAVANNAH_TRUCK', '萨凡纳普船海卡', 'FBA_WAREHOUSE_RATE', '萨凡纳普船海卡', JSON_ARRAY('萨凡纳普船海卡'), 'SPHK', NULL, 'ACTIVE'),
|
||||
(12, 'MATSON_CLX_PARCEL', '美森正班海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('美森正班海派'), 'ZBYHP', NULL, 'ACTIVE'),
|
||||
(13, 'MATSON_MAX_PARCEL', '美森加班海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('美森加班海派'), 'BYHP', NULL, 'ACTIVE'),
|
||||
(14, 'HEDE_PARCEL', '合德海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('合德海派'), 'HDHP', NULL, 'ACTIVE'),
|
||||
(15, 'OA_PARCEL', '普船海派', 'REGION_WEIGHT_TIER', '海派', JSON_ARRAY('普船海派'), 'PCHP', NULL, 'ACTIVE')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
display_name = VALUES(display_name), price_model = VALUES(price_model),
|
||||
source_sheet_name = VALUES(source_sheet_name), aliases_json = VALUES(aliases_json), status = VALUES(status);
|
||||
source_sheet_name = VALUES(source_sheet_name), aliases_json = VALUES(aliases_json),
|
||||
inbound_code_prefix = VALUES(inbound_code_prefix),
|
||||
overseas_inbound_code_prefix = VALUES(overseas_inbound_code_prefix), status = VALUES(status);
|
||||
|
||||
INSERT INTO fl_price_origin_alias (id, warehouse_code, alias_name, status)
|
||||
VALUES
|
||||
@@ -566,7 +603,7 @@ CREATE TABLE IF NOT EXISTS fl_inbound_fee (
|
||||
carrier_rate_version INT UNSIGNED NOT NULL COMMENT '船司价格版本快照',
|
||||
price_book_version_id BIGINT UNSIGNED NULL COMMENT '整表价格版本ID快照',
|
||||
rule_code VARCHAR(50) NULL COMMENT '历史客户规则编码快照',
|
||||
customer_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID快照',
|
||||
customer_id BIGINT UNSIGNED NULL COMMENT '可选业务客户快照;平台公共渠道计费不依赖该字段',
|
||||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
|
||||
business_type VARCHAR(32) NOT NULL COMMENT '业务类型快照',
|
||||
tax_mode VARCHAR(24) NULL COMMENT '订单买单或退税模式快照',
|
||||
@@ -659,7 +696,6 @@ CREATE TABLE IF NOT EXISTS fl_inbound_pallet_allocation (
|
||||
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_pallet_allocation_no (tenant_id, inbound_id, allocation_no),
|
||||
UNIQUE KEY uk_pallet_allocation_pallet (tenant_id, inbound_id, pallet_id),
|
||||
KEY idx_allocation_location (tenant_id, warehouse_id, location_id, allocation_status),
|
||||
KEY idx_allocation_reservation (reservation_id),
|
||||
CONSTRAINT fk_allocation_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||||
|
||||
@@ -22,6 +22,8 @@ DELIMITER ;
|
||||
|
||||
CALL phase2_publish_add_column('fl_fee_item', 'fee_code',
|
||||
"VARCHAR(50) NULL COMMENT '稳定费用编码;系统费用使用固定编码' AFTER trade_scope");
|
||||
CALL phase2_publish_add_column('fl_fee_item', 'supplier_category',
|
||||
"VARCHAR(16) NULL COMMENT 'PAYABLE供应商类别约束;为空时可选任意ACTIVE供应商' AFTER fee_code");
|
||||
ALTER TABLE fl_fee_item
|
||||
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '平台公共费用项目作用域,固定为0;物理字段名沿用 tenant_id',
|
||||
COMMENT = '平台公共费用项目主数据';
|
||||
@@ -406,7 +408,7 @@ DEALLOCATE PREPARE statement;
|
||||
CALL phase2_publish_add_column('fl_inbound_fee_detail', 'source_type',
|
||||
"VARCHAR(32) NOT NULL DEFAULT 'MANUAL' COMMENT 'MANUAL或FBA_PRICING' AFTER fee_item_id");
|
||||
CALL phase2_publish_add_column('fl_inbound_fee_detail', 'source_fee_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '来源计费快照ID' AFTER source_type");
|
||||
"BIGINT UNSIGNED NULL COMMENT '来源计费快照ID或人工费用版本链根ID' AFTER source_type");
|
||||
CALL phase2_publish_add_column('fl_inbound_fee_detail', 'source_fee_version',
|
||||
"INT UNSIGNED NULL COMMENT '来源计费快照版本' AFTER source_fee_id");
|
||||
CALL phase2_publish_add_column('fl_inbound_fee_detail', 'source_fee_no',
|
||||
@@ -431,9 +433,6 @@ CALL phase2_publish_add_column('fl_inbound_fee_detail', 'published_at',
|
||||
"DATETIME(3) NULL COMMENT '发布时间' AFTER published_by_name");
|
||||
CALL phase2_publish_add_column('fl_inbound_fee_detail', 'inbounded_at',
|
||||
"DATETIME(3) NULL COMMENT '入库完成时间快照' AFTER published_at");
|
||||
CALL phase2_publish_add_column('fl_inbound_fee_detail', 'settlement_included',
|
||||
"TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否参与结算' AFTER inbounded_at");
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase2_publish_add_column;
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase2_publish_add_index;
|
||||
@@ -457,12 +456,21 @@ DELIMITER ;
|
||||
CALL phase2_publish_add_index('fl_fee_item', 'uk_fee_item_code',
|
||||
'UNIQUE KEY `uk_fee_item_code` (`fee_code`)');
|
||||
CALL phase2_publish_add_index('fl_inbound_fee_detail', 'uk_inbound_fee_source',
|
||||
'UNIQUE KEY `uk_inbound_fee_source` (`tenant_id`, `source_type`, `source_fee_id`)');
|
||||
'UNIQUE KEY `uk_inbound_fee_source` (`tenant_id`, `source_type`, `source_fee_id`, `source_fee_version`)');
|
||||
CALL phase2_publish_add_index('fl_inbound_fee_detail', 'idx_inbound_fee_published',
|
||||
'KEY `idx_inbound_fee_published` (`tenant_id`, `inbound_id`, `publication_status`, `source_fee_version`)');
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase2_publish_add_index;
|
||||
|
||||
-- 系统计费使用的固定公共费用项目;不属于费用类型.xls 的110条受控种子。
|
||||
INSERT INTO fl_fee_item (id, tenant_id, fee_code, category, trade_scope, fee_type_name,
|
||||
pickup_payable, pickup_receivable, warehouse_payable, warehouse_receivable, allocation_fee,
|
||||
description, status, created_by, created_by_name, created_at, updated_by, updated_by_name, updated_at)
|
||||
SELECT 193000000001000111, 0, 'FBA_HEAD_FREIGHT', '头程运费', 'INTERNATIONAL', 'FBA头程运费',
|
||||
0, 0, 0, 1, 0, '系统计费发布使用的固定应收费用项目', 'ACTIVE',
|
||||
0, 'system', '2026-08-14 00:00:00.000', 0, 'system', '2026-08-14 00:00:00.000'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM fl_fee_item WHERE fee_code = 'FBA_HEAD_FREIGHT');
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase2_publish_assert_fee_schema;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase2_publish_assert_fee_schema()
|
||||
@@ -483,6 +491,10 @@ BEGIN
|
||||
OR (SELECT COUNT(*) FROM fl_fee_item WHERE tenant_id = 0 AND status = 'ACTIVE' AND deleted = 0
|
||||
AND id BETWEEN 193000000001000001 AND 193000000001000110
|
||||
AND fee_code = CONCAT('FEE_TYPE_', LPAD(id - 193000000001000000, 3, '0'))) <> 110
|
||||
OR (SELECT COUNT(*) FROM fl_fee_item WHERE tenant_id = 0 AND fee_code = 'FBA_HEAD_FREIGHT'
|
||||
AND category = '头程运费' AND trade_scope = 'INTERNATIONAL' AND fee_type_name = 'FBA头程运费'
|
||||
AND pickup_payable = 0 AND pickup_receivable = 0 AND warehouse_payable = 0
|
||||
AND warehouse_receivable = 1 AND allocation_fee = 0 AND status = 'ACTIVE' AND deleted = 0) <> 1
|
||||
OR EXISTS (SELECT 1 FROM fl_inbound_fee_detail d JOIN fl_fee_item f ON f.id = d.fee_item_id
|
||||
WHERE d.tenant_id = 0 OR f.tenant_id <> 0) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '二期迁移失败:费用项目未完成平台公共作用域或明细跨客户引用校验';
|
||||
|
||||
@@ -90,31 +90,28 @@ BEGIN
|
||||
DECLARE current_plan_status VARCHAR(24);
|
||||
SELECT MAX(plan_status) INTO current_plan_status
|
||||
FROM fl_loading_plan WHERE container_booking_id = OLD.id;
|
||||
IF current_plan_status = 'COMPLETED' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜全部只读,不允许修改';
|
||||
END IF;
|
||||
IF current_plan_status = 'LOADING' AND (
|
||||
IF current_plan_status IN ('LOADING', 'COMPLETED') AND (
|
||||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no)
|
||||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no)
|
||||
OR NOT (OLD.channel_code <=> NEW.channel_code) OR NOT (OLD.channel_name <=> NEW.channel_name)
|
||||
OR NOT (OLD.ocean_booking_id <=> NEW.ocean_booking_id)
|
||||
OR NOT (OLD.ocean_booking_requirement_id <=> NEW.ocean_booking_requirement_id)
|
||||
OR NOT (OLD.warehouse_id <=> NEW.warehouse_id) OR NOT (OLD.warehouse_name <=> NEW.warehouse_name)
|
||||
OR NOT (OLD.bill_of_lading_no <=> NEW.bill_of_lading_no) OR NOT (OLD.vessel_name <=> NEW.vessel_name)
|
||||
OR NOT (OLD.container_type <=> NEW.container_type)
|
||||
OR NOT (OLD.shipping_company_id <=> NEW.shipping_company_id) OR NOT (OLD.shipping_company_name <=> NEW.shipping_company_name)
|
||||
OR NOT (OLD.departure_port_code <=> NEW.departure_port_code) OR NOT (OLD.departure_port_name <=> NEW.departure_port_name)
|
||||
OR NOT (OLD.destination_port_code <=> NEW.destination_port_code) OR NOT (OLD.destination_port_name <=> NEW.destination_port_name)
|
||||
OR NOT (OLD.destination_warehouse_code <=> NEW.destination_warehouse_code) OR NOT (OLD.destination_warehouse_name <=> NEW.destination_warehouse_name)
|
||||
OR NOT (OLD.capacity_cbm <=> NEW.capacity_cbm) OR NOT (OLD.max_weight_kg <=> NEW.max_weight_kg)
|
||||
OR NOT (OLD.terminal_trucking_company_id <=> NEW.terminal_trucking_company_id)
|
||||
OR NOT (OLD.terminal_trucking_company <=> NEW.terminal_trucking_company) OR NOT (OLD.loading_at <=> NEW.loading_at)
|
||||
OR NOT (OLD.estimated_sailing_at <=> NEW.estimated_sailing_at) OR NOT (OLD.actual_sailing_at <=> NEW.actual_sailing_at)
|
||||
OR NOT (OLD.actual_sailing_at <=> NEW.actual_sailing_at)
|
||||
OR NOT (OLD.estimated_port_arrival_at <=> NEW.estimated_port_arrival_at) OR NOT (OLD.actual_port_arrival_at <=> NEW.actual_port_arrival_at)
|
||||
OR NOT (OLD.estimated_warehouse_arrival_at <=> NEW.estimated_warehouse_arrival_at) OR NOT (OLD.actual_warehouse_arrival_at <=> NEW.actual_warehouse_arrival_at)
|
||||
OR NOT (OLD.created_by <=> NEW.created_by) OR NOT (OLD.created_by_name <=> NEW.created_by_name)
|
||||
OR NOT (OLD.created_at <=> NEW.created_at)
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜中仅允许修改集装箱号与航次';
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜作业开始后仅允许修改集装箱号与放舱运输数据';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
@@ -100,6 +100,7 @@ CREATE TABLE IF NOT EXISTS fl_customs_submission_batch (
|
||||
customs_case_id BIGINT UNSIGNED NOT NULL COMMENT '所属报关协同单ID',
|
||||
batch_no VARCHAR(40) NOT NULL COMMENT '全局唯一代理提交批次号',
|
||||
batch_version INT UNSIGNED NOT NULL COMMENT '同一报关协同单内的提交批次版本序号',
|
||||
customs_agent_id BIGINT UNSIGNED NULL COMMENT '本批次报关代理Supplier逻辑ID快照;历史数据无法推导时为空',
|
||||
customs_agent_name VARCHAR(128) NOT NULL COMMENT '本批次接收报关资料的报关代理名称快照',
|
||||
external_reference VARCHAR(128) NULL COMMENT '报关代理或外部系统返回的业务参考号',
|
||||
file_snapshot_json JSON NOT NULL COMMENT '提交时冻结的已审核资料数组;元素包含documentId、fileId、itemCode、documentVersion和fileHash',
|
||||
@@ -115,6 +116,22 @@ CREATE TABLE IF NOT EXISTS fl_customs_submission_batch (
|
||||
CONSTRAINT chk_customs_batch_status CHECK (status IN ('SUBMITTED','ACKNOWLEDGED','REJECTED','COMPLETED'))
|
||||
) ENGINE=InnoDB COMMENT='报关代理提交批次及冻结文件版本';
|
||||
|
||||
-- 历史代理名称无法安全反推Supplier ID,旧行保持NULL;新写入由服务端校验并持久化。
|
||||
DROP PROCEDURE IF EXISTS phase3_customs_supplier_snapshot_add_column;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_customs_supplier_snapshot_add_column()
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema=DATABASE() AND table_name='fl_customs_submission_batch'
|
||||
AND column_name='customs_agent_id') THEN
|
||||
ALTER TABLE fl_customs_submission_batch
|
||||
ADD COLUMN customs_agent_id BIGINT UNSIGNED NULL COMMENT '本批次报关代理Supplier逻辑ID快照;历史数据无法推导时为空' AFTER batch_version;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_customs_supplier_snapshot_add_column();
|
||||
DROP PROCEDURE phase3_customs_supplier_snapshot_add_column;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_customs_receipt (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '报关回执文件版本逻辑ID',
|
||||
customs_case_id BIGINT UNSIGNED NOT NULL COMMENT '所属报关协同单ID',
|
||||
|
||||
@@ -4,13 +4,13 @@ USE baiyue_firstleg;
|
||||
CREATE TABLE IF NOT EXISTS fl_ocean_booking (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '海运订舱ID',
|
||||
booking_no VARCHAR(40) NOT NULL COMMENT '平台海运订舱编号',
|
||||
status VARCHAR(24) NOT NULL DEFAULT 'DRAFT' COMMENT '订舱状态:DRAFT草稿、SUBMITTED已提交、CONFIRMED已确认、RELEASED已放舱、FAILED失败、CHANGE_PENDING船期待变更、CANCELLED已取消',
|
||||
customer_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
customer_name VARCHAR(128) NOT NULL COMMENT '客户名称快照',
|
||||
channel_code VARCHAR(64) NOT NULL COMMENT '订舱渠道编码快照',
|
||||
channel_name VARCHAR(128) NOT NULL COMMENT '订舱渠道名称快照',
|
||||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '国内中转仓ID快照',
|
||||
warehouse_name VARCHAR(128) NOT NULL COMMENT '国内中转仓名称快照',
|
||||
status VARCHAR(24) NOT NULL DEFAULT 'DRAFT' COMMENT '订舱状态:DRAFT草稿、RELEASED已发布且永久只读',
|
||||
customer_id BIGINT UNSIGNED NULL COMMENT '可选客户ID快照',
|
||||
customer_name VARCHAR(128) NULL COMMENT '可选客户名称快照',
|
||||
channel_code VARCHAR(64) NULL COMMENT '订舱渠道编码快照',
|
||||
channel_name VARCHAR(128) NULL COMMENT '订舱渠道名称快照',
|
||||
warehouse_id BIGINT UNSIGNED NULL COMMENT '国内中转仓ID快照',
|
||||
warehouse_name VARCHAR(128) NULL COMMENT '国内中转仓名称快照',
|
||||
bill_of_lading_no VARCHAR(128) NULL COMMENT '提单号',
|
||||
shipping_company_id BIGINT UNSIGNED NULL COMMENT '船公司逻辑ID快照',
|
||||
shipping_company_name VARCHAR(128) NULL COMMENT '船公司名称快照',
|
||||
@@ -23,17 +23,10 @@ CREATE TABLE IF NOT EXISTS fl_ocean_booking (
|
||||
destination_port_code VARCHAR(32) NULL COMMENT '目的港编码快照',
|
||||
destination_port_name VARCHAR(128) NULL COMMENT '目的港名称快照',
|
||||
place_of_delivery VARCHAR(128) NULL COMMENT '最终交货地快照',
|
||||
service_term VARCHAR(32) NULL COMMENT '运输服务条款快照',
|
||||
freight_term VARCHAR(32) NULL COMMENT '海运费支付条款快照',
|
||||
service_term VARCHAR(32) NULL COMMENT '运输服务条款快照;发布前必填',
|
||||
freight_term VARCHAR(32) NULL COMMENT '海运费支付条款快照;发布前必填',
|
||||
estimated_sailing_at DATETIME(3) NULL COMMENT '当前确认预计开船时间',
|
||||
estimated_arrival_at DATETIME(3) NULL COMMENT '当前确认预计到港时间',
|
||||
pending_vessel_name VARCHAR(128) NULL COMMENT '待确认变更船名',
|
||||
pending_voyage_no VARCHAR(64) NULL COMMENT '待确认变更航次号',
|
||||
pending_estimated_sailing_at DATETIME(3) NULL COMMENT '待确认变更预计开船时间',
|
||||
pending_estimated_arrival_at DATETIME(3) NULL COMMENT '待确认变更预计到港时间',
|
||||
change_reason VARCHAR(1000) NULL COMMENT '船期变更原因',
|
||||
failure_reason VARCHAR(1000) NULL COMMENT '订舱失败原因',
|
||||
cancellation_reason VARCHAR(1000) NULL COMMENT '取消订舱原因',
|
||||
remark VARCHAR(1000) NULL COMMENT '平台内部备注',
|
||||
created_by BIGINT UNSIGNED NOT NULL COMMENT '创建操作人ID',
|
||||
created_by_name VARCHAR(64) NULL COMMENT '创建操作人姓名快照',
|
||||
@@ -48,14 +41,55 @@ CREATE TABLE IF NOT EXISTS fl_ocean_booking (
|
||||
KEY idx_ocean_booking_voyage (vessel_name,voyage_no,updated_at),
|
||||
CONSTRAINT chk_ocean_booking_customer CHECK (customer_id > 0),
|
||||
CONSTRAINT chk_ocean_booking_status CHECK (status IN
|
||||
('DRAFT','SUBMITTED','CONFIRMED','RELEASED','FAILED','CHANGE_PENDING','CANCELLED')),
|
||||
('DRAFT','RELEASED')),
|
||||
CONSTRAINT chk_ocean_booking_schedule CHECK
|
||||
(estimated_sailing_at IS NULL OR estimated_arrival_at IS NULL OR estimated_arrival_at >= estimated_sailing_at),
|
||||
CONSTRAINT chk_ocean_booking_pending_schedule CHECK
|
||||
(pending_estimated_sailing_at IS NULL OR pending_estimated_arrival_at IS NULL
|
||||
OR pending_estimated_arrival_at >= pending_estimated_sailing_at)
|
||||
(estimated_sailing_at IS NULL OR estimated_arrival_at IS NULL OR estimated_arrival_at >= estimated_sailing_at)
|
||||
) ENGINE=InnoDB COMMENT='独立海运订舱聚合;柜次仍由fl_container_booking承载';
|
||||
|
||||
-- CREATE TABLE IF NOT EXISTS does not reconcile a database initialized before customer became optional.
|
||||
ALTER TABLE fl_ocean_booking
|
||||
MODIFY COLUMN customer_id BIGINT UNSIGNED NULL COMMENT '可选客户ID快照',
|
||||
MODIFY COLUMN customer_name VARCHAR(128) NULL COMMENT '可选客户名称快照';
|
||||
|
||||
-- Recreate triggers and the current two-state constraint on repeatable initialization.
|
||||
DROP TRIGGER IF EXISTS trg_ocean_booking_before_update;
|
||||
DROP TRIGGER IF EXISTS trg_ocean_party_before_insert;
|
||||
DROP TRIGGER IF EXISTS trg_ocean_party_before_update;
|
||||
DROP TRIGGER IF EXISTS trg_ocean_party_before_delete;
|
||||
DROP TRIGGER IF EXISTS trg_ocean_requirement_before_insert;
|
||||
DROP TRIGGER IF EXISTS trg_ocean_requirement_before_update;
|
||||
DROP TRIGGER IF EXISTS trg_ocean_requirement_before_delete;
|
||||
DROP TRIGGER IF EXISTS trg_ocean_history_before_delete;
|
||||
-- Existing CHECK constraints are not reconciled by CREATE TABLE IF NOT EXISTS.
|
||||
SET @ocean_drop_status_check = (
|
||||
SELECT IF(COUNT(*) > 0,
|
||||
'ALTER TABLE fl_ocean_booking DROP CHECK chk_ocean_booking_status',
|
||||
'SELECT 1')
|
||||
FROM information_schema.table_constraints
|
||||
WHERE table_schema=DATABASE() AND table_name='fl_ocean_booking'
|
||||
AND constraint_name='chk_ocean_booking_status'
|
||||
);
|
||||
PREPARE ocean_drop_status_stmt FROM @ocean_drop_status_check;
|
||||
EXECUTE ocean_drop_status_stmt;
|
||||
DEALLOCATE PREPARE ocean_drop_status_stmt;
|
||||
ALTER TABLE fl_ocean_booking
|
||||
MODIFY COLUMN service_term VARCHAR(32) NULL COMMENT '运输服务条款快照;发布前必填',
|
||||
MODIFY COLUMN freight_term VARCHAR(32) NULL COMMENT '海运费支付条款快照;发布前必填',
|
||||
ADD CONSTRAINT chk_ocean_booking_status CHECK
|
||||
(status IN ('DRAFT','RELEASED'));
|
||||
|
||||
SET @ocean_drop_failure_reason = (
|
||||
SELECT IF(COUNT(*) > 0,
|
||||
'ALTER TABLE fl_ocean_booking DROP COLUMN failure_reason',
|
||||
'SELECT 1')
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema=DATABASE() AND table_name='fl_ocean_booking'
|
||||
AND column_name='failure_reason'
|
||||
);
|
||||
PREPARE ocean_drop_failure_stmt FROM @ocean_drop_failure_reason;
|
||||
EXECUTE ocean_drop_failure_stmt;
|
||||
DEALLOCATE PREPARE ocean_drop_failure_stmt;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_ocean_booking_party (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '订舱提单关系方ID',
|
||||
ocean_booking_id BIGINT UNSIGNED NOT NULL COMMENT '所属海运订舱ID',
|
||||
@@ -66,7 +100,7 @@ CREATE TABLE IF NOT EXISTS fl_ocean_booking_party (
|
||||
contact_phone VARCHAR(64) NULL COMMENT '关系方联系电话快照',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_ocean_booking_party (ocean_booking_id,party_type),
|
||||
CONSTRAINT fk_ocean_booking_party FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||||
CONSTRAINT fk_ocean_booking_party FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_ocean_booking_party_type CHECK (party_type IN ('SHIPPER','CONSIGNEE','NOTIFY_PARTY'))
|
||||
) ENGINE=InnoDB COMMENT='订舱提单关系方快照';
|
||||
|
||||
@@ -81,7 +115,7 @@ CREATE TABLE IF NOT EXISTS fl_ocean_booking_container_requirement (
|
||||
sort_no INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '订舱需求展示顺序',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_ocean_requirement_booking (ocean_booking_id,sort_no,id),
|
||||
CONSTRAINT fk_ocean_requirement_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||||
CONSTRAINT fk_ocean_requirement_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_ocean_requirement_quantity CHECK (quantity > 0)
|
||||
) ENGINE=InnoDB COMMENT='订舱箱型箱种及数量需求';
|
||||
|
||||
@@ -96,7 +130,7 @@ CREATE TABLE IF NOT EXISTS fl_ocean_booking_container_allocation (
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_ocean_allocation_container (container_booking_id),
|
||||
KEY idx_ocean_allocation_booking (ocean_booking_id,requirement_id,allocated_at),
|
||||
CONSTRAINT fk_ocean_allocation_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||||
CONSTRAINT fk_ocean_allocation_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_ocean_allocation_requirement FOREIGN KEY (requirement_id)
|
||||
REFERENCES fl_ocean_booking_container_requirement(id),
|
||||
CONSTRAINT fk_ocean_allocation_container FOREIGN KEY (container_booking_id) REFERENCES fl_container_booking(id)
|
||||
@@ -116,7 +150,7 @@ CREATE TABLE IF NOT EXISTS fl_ocean_booking_status_history (
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '状态历史创建时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_ocean_history_booking (ocean_booking_id,created_at,id),
|
||||
CONSTRAINT fk_ocean_history_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id)
|
||||
CONSTRAINT fk_ocean_history_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB COMMENT='海运订舱不可变状态历史';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_ocean_booking_operation (
|
||||
@@ -133,7 +167,7 @@ CREATE TABLE IF NOT EXISTS fl_ocean_booking_operation (
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_ocean_operation (operator_id,operation,idempotency_key),
|
||||
KEY idx_ocean_operation_booking (ocean_booking_id,created_at),
|
||||
CONSTRAINT fk_ocean_operation_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id),
|
||||
CONSTRAINT fk_ocean_operation_booking FOREIGN KEY (ocean_booking_id) REFERENCES fl_ocean_booking(id) ON DELETE CASCADE,
|
||||
CONSTRAINT chk_ocean_operation_status CHECK (status IN ('PROCESSING','SUCCESS'))
|
||||
) ENGINE=InnoDB COMMENT='海运订舱写操作幂等与请求哈希';
|
||||
|
||||
@@ -141,15 +175,11 @@ DROP TRIGGER IF EXISTS trg_ocean_booking_before_update;
|
||||
DELIMITER $$
|
||||
CREATE TRIGGER trg_ocean_booking_before_update BEFORE UPDATE ON fl_ocean_booking FOR EACH ROW
|
||||
BEGIN
|
||||
IF OLD.status IN ('RELEASED','CANCELLED') THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='已放舱或已取消订舱不可修改';
|
||||
IF OLD.status = 'RELEASED' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='已放舱订舱不可修改';
|
||||
END IF;
|
||||
IF OLD.status <> NEW.status AND NOT (
|
||||
(OLD.status='DRAFT' AND NEW.status IN ('SUBMITTED','CANCELLED'))
|
||||
OR (OLD.status='SUBMITTED' AND NEW.status IN ('CONFIRMED','FAILED','CANCELLED'))
|
||||
OR (OLD.status='CONFIRMED' AND NEW.status IN ('RELEASED','CHANGE_PENDING','CANCELLED'))
|
||||
OR (OLD.status='CHANGE_PENDING' AND NEW.status IN ('CONFIRMED','FAILED','CANCELLED'))
|
||||
OR (OLD.status='FAILED' AND NEW.status='CANCELLED')) THEN
|
||||
(OLD.status='DRAFT' AND NEW.status='RELEASED')) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='海运订舱状态迁移非法';
|
||||
END IF;
|
||||
END$$
|
||||
@@ -186,8 +216,7 @@ BEGIN
|
||||
IF (SELECT ocean_booking_id FROM fl_ocean_booking_container_requirement WHERE id=NEW.requirement_id) <> NEW.ocean_booking_id THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='箱型需求不属于当前订舱';
|
||||
END IF;
|
||||
IF (SELECT status FROM fl_ocean_booking WHERE id=NEW.ocean_booking_id) NOT IN
|
||||
('SUBMITTED','CONFIRMED','CHANGE_PENDING') THEN
|
||||
IF (SELECT status FROM fl_ocean_booking WHERE id=NEW.ocean_booking_id) <> 'RELEASED' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='当前订舱状态不可分配柜次';
|
||||
END IF;
|
||||
END$$
|
||||
@@ -214,12 +243,9 @@ BEGIN
|
||||
WHEN 5102140 THEN 'platform:firstleg:ocean-booking:list'
|
||||
WHEN 5102141 THEN 'platform:firstleg:ocean-booking:list'
|
||||
WHEN 5102142 THEN 'platform:firstleg:ocean-booking:create'
|
||||
WHEN 5102143 THEN 'platform:firstleg:ocean-booking:edit'
|
||||
WHEN 5102144 THEN 'platform:firstleg:ocean-booking:submit'
|
||||
WHEN 5102145 THEN 'platform:firstleg:ocean-booking:confirm'
|
||||
WHEN 5102146 THEN 'platform:firstleg:ocean-booking:release'
|
||||
WHEN 5102147 THEN 'platform:firstleg:ocean-booking:cancel'
|
||||
WHEN 5102148 THEN 'platform:firstleg:ocean-booking:change'
|
||||
WHEN 5102147 THEN 'platform:firstleg:ocean-booking:edit'
|
||||
WHEN 5102148 THEN 'platform:firstleg:ocean-booking:delete'
|
||||
WHEN 5102149 THEN 'platform:firstleg:ocean-booking:allocate'
|
||||
END)
|
||||
OR (permission_code LIKE 'platform:firstleg:ocean-booking:%'
|
||||
@@ -239,12 +265,9 @@ INSERT INTO iam_permission
|
||||
VALUES
|
||||
(5102140,0,'PLATFORM','MENU',5102000,'platform:firstleg:ocean-booking:list','订舱管理','/first-leg/ocean-bookings','GET',80,'ACTIVE',1,0),
|
||||
(5102142,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:create','创建订舱',NULL,'POST',20,'ACTIVE',1,0),
|
||||
(5102143,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:edit','编辑订舱',NULL,'PUT',30,'ACTIVE',1,0),
|
||||
(5102144,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:submit','提交订舱',NULL,'POST',40,'ACTIVE',1,0),
|
||||
(5102145,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:confirm','确认订舱',NULL,'POST',50,'ACTIVE',1,0),
|
||||
(5102146,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:release','确认放舱',NULL,'POST',60,'ACTIVE',1,0),
|
||||
(5102147,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:cancel','取消订舱',NULL,'POST',70,'ACTIVE',1,0),
|
||||
(5102148,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:change','变更船期',NULL,'POST',80,'ACTIVE',1,0),
|
||||
(5102147,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:edit','编辑草稿',NULL,'PUT',70,'ACTIVE',1,0),
|
||||
(5102148,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:delete','删除草稿',NULL,'DELETE',80,'ACTIVE',1,0),
|
||||
(5102149,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:allocate','分配柜次',NULL,'POST',90,'ACTIVE',1,0)
|
||||
ON DUPLICATE KEY UPDATE tenant_id=0,app_code='PLATFORM',permission_type=VALUES(permission_type),
|
||||
permission_code=VALUES(permission_code),permission_name=VALUES(permission_name),parent_id=VALUES(parent_id),
|
||||
@@ -258,5 +281,7 @@ WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
|
||||
DELETE rp FROM iam_role_permission rp JOIN iam_permission p ON p.tenant_id=rp.tenant_id AND p.id=rp.permission_id
|
||||
WHERE p.id=5102141;
|
||||
UPDATE iam_permission SET status='INACTIVE',deleted=1 WHERE id=5102141;
|
||||
DELETE rp FROM iam_role_permission rp WHERE rp.permission_id IN (5102143,5102144,5102145);
|
||||
DELETE FROM iam_permission WHERE id IN (5102143,5102144,5102145);
|
||||
|
||||
USE baiyue_firstleg;
|
||||
|
||||
@@ -28,23 +28,19 @@ PREPARE statement FROM @ddl; EXECUTE statement; DEALLOCATE PREPARE statement;
|
||||
ALTER TABLE fl_container_document ADD CONSTRAINT chk_container_document_type
|
||||
CHECK (document_type IN ('DESTINATION_CLEARANCE', 'CUSTOMS_DECLARATION', 'DELIVERY_ORDER', 'PICKUP_ORDER'));
|
||||
|
||||
-- Keep the container master immutable after loading; only the two document rows remain operable.
|
||||
-- Keep container identity/loading facts immutable while allowing release transport data to arrive later.
|
||||
DROP TRIGGER IF EXISTS trg_container_booking_before_update;
|
||||
DELIMITER $$
|
||||
CREATE TRIGGER trg_container_booking_before_update BEFORE UPDATE ON fl_container_booking FOR EACH ROW
|
||||
BEGIN
|
||||
DECLARE current_plan_status VARCHAR(24);
|
||||
SELECT MAX(plan_status) INTO current_plan_status FROM fl_loading_plan WHERE container_booking_id=OLD.id;
|
||||
IF current_plan_status='COMPLETED' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='装柜完成后货柜全部只读,不允许修改';
|
||||
END IF;
|
||||
IF current_plan_status='LOADING' AND (
|
||||
IF current_plan_status IN ('LOADING','COMPLETED') AND (
|
||||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no)
|
||||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no) OR NOT (OLD.channel_code <=> NEW.channel_code)
|
||||
OR NOT (OLD.channel_name <=> NEW.channel_name) OR NOT (OLD.ocean_booking_id <=> NEW.ocean_booking_id)
|
||||
OR NOT (OLD.ocean_booking_requirement_id <=> NEW.ocean_booking_requirement_id)
|
||||
OR NOT (OLD.warehouse_id <=> NEW.warehouse_id) OR NOT (OLD.warehouse_name <=> NEW.warehouse_name)
|
||||
OR NOT (OLD.bill_of_lading_no <=> NEW.bill_of_lading_no) OR NOT (OLD.vessel_name <=> NEW.vessel_name)
|
||||
OR NOT (OLD.container_type <=> NEW.container_type) OR NOT (OLD.shipping_company_id <=> NEW.shipping_company_id)
|
||||
OR NOT (OLD.shipping_company_name <=> NEW.shipping_company_name)
|
||||
OR NOT (OLD.departure_port_code <=> NEW.departure_port_code) OR NOT (OLD.departure_port_name <=> NEW.departure_port_name)
|
||||
@@ -52,15 +48,16 @@ BEGIN
|
||||
OR NOT (OLD.destination_warehouse_code <=> NEW.destination_warehouse_code)
|
||||
OR NOT (OLD.destination_warehouse_name <=> NEW.destination_warehouse_name)
|
||||
OR NOT (OLD.capacity_cbm <=> NEW.capacity_cbm) OR NOT (OLD.max_weight_kg <=> NEW.max_weight_kg)
|
||||
OR NOT (OLD.terminal_trucking_company_id <=> NEW.terminal_trucking_company_id)
|
||||
OR NOT (OLD.terminal_trucking_company <=> NEW.terminal_trucking_company) OR NOT (OLD.loading_at <=> NEW.loading_at)
|
||||
OR NOT (OLD.estimated_sailing_at <=> NEW.estimated_sailing_at) OR NOT (OLD.actual_sailing_at <=> NEW.actual_sailing_at)
|
||||
OR NOT (OLD.actual_sailing_at <=> NEW.actual_sailing_at)
|
||||
OR NOT (OLD.estimated_port_arrival_at <=> NEW.estimated_port_arrival_at)
|
||||
OR NOT (OLD.actual_port_arrival_at <=> NEW.actual_port_arrival_at)
|
||||
OR NOT (OLD.estimated_warehouse_arrival_at <=> NEW.estimated_warehouse_arrival_at)
|
||||
OR NOT (OLD.actual_warehouse_arrival_at <=> NEW.actual_warehouse_arrival_at)
|
||||
OR NOT (OLD.created_by <=> NEW.created_by) OR NOT (OLD.created_by_name <=> NEW.created_by_name)
|
||||
OR NOT (OLD.created_at <=> NEW.created_at)) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='装柜中仅允许修改集装箱号与航次';
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='装柜作业开始后仅允许修改集装箱号与放舱运输数据';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
@@ -26,21 +26,33 @@ DELIMITER ;
|
||||
CALL phase3_add_allocation_picked_count();
|
||||
DROP PROCEDURE phase3_add_allocation_picked_count;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_document_sequence (
|
||||
sequence_key VARCHAR(32) NOT NULL COMMENT '单据类型与北京时间日期组合键,例如CAB:20260818',
|
||||
current_value INT UNSIGNED NOT NULL COMMENT '该类型当日已分配的最大序号',
|
||||
PRIMARY KEY (sequence_key),
|
||||
CONSTRAINT chk_fl_document_sequence_value CHECK (current_value > 0)
|
||||
) ENGINE=InnoDB COMMENT='头程履约业务单号日序列;生成YYMMDD加四位当日序号';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_container_booking (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '货柜预订ID(雪花ID)',
|
||||
booking_no VARCHAR(40) NOT NULL COMMENT '平台唯一货柜预订单号',
|
||||
container_no VARCHAR(64) NULL COMMENT '集装箱号;确认前允许修订',
|
||||
cabinet_no VARCHAR(64) NOT NULL COMMENT '平台唯一内部柜号',
|
||||
source_type VARCHAR(24) NOT NULL DEFAULT 'SIMULATION' COMMENT '记录来源:SIMULATION内部模拟柜、OCEAN_BOOKING订舱真实柜',
|
||||
record_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '记录状态:ACTIVE可操作、MERGED已合并历史',
|
||||
merged_into_container_booking_id BIGINT UNSIGNED NULL COMMENT '真实柜被模拟柜吸收后的目标稳定柜次ID',
|
||||
channel_code VARCHAR(64) NULL COMMENT '订舱渠道编码快照;存量回填前禁止混装',
|
||||
channel_name VARCHAR(128) NULL COMMENT '订舱渠道名称快照',
|
||||
ocean_booking_id BIGINT UNSIGNED NULL COMMENT '来源订舱ID',
|
||||
ocean_booking_requirement_id BIGINT UNSIGNED NULL COMMENT '来源订舱箱型需求ID',
|
||||
customer_id BIGINT UNSIGNED NULL COMMENT '已关联真实订舱指定的客户范围',
|
||||
customer_name VARCHAR(128) NULL COMMENT '已关联真实订舱客户名称快照',
|
||||
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '装柜仓库ID;平台公共资源',
|
||||
warehouse_name VARCHAR(128) NOT NULL COMMENT '装柜仓库名称快照',
|
||||
bill_of_lading_no VARCHAR(128) NULL COMMENT '提单号',
|
||||
vessel_name VARCHAR(128) NULL COMMENT '船名快照',
|
||||
voyage_no VARCHAR(64) NULL COMMENT '航次;确认前允许修订',
|
||||
container_type VARCHAR(32) NOT NULL COMMENT '箱型,例如20GP、40GP、40HQ',
|
||||
container_type VARCHAR(32) NOT NULL COMMENT '平台固定柜型:20GP、40GP、40HC、40NR、45HC',
|
||||
shipping_company_id BIGINT UNSIGNED NULL COMMENT '船司ID快照',
|
||||
shipping_company_name VARCHAR(128) NULL COMMENT '船司名称快照',
|
||||
departure_port_code VARCHAR(32) NULL COMMENT '起运港编码快照',
|
||||
@@ -51,7 +63,8 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
|
||||
destination_warehouse_name VARCHAR(128) NULL COMMENT '目的仓名称快照',
|
||||
capacity_cbm DECIMAL(18,4) NULL COMMENT '货柜容量m3',
|
||||
max_weight_kg DECIMAL(18,3) NULL COMMENT '货柜最大承重kg',
|
||||
terminal_trucking_company VARCHAR(128) NULL COMMENT '码头拖车公司',
|
||||
terminal_trucking_company_id BIGINT UNSIGNED NULL COMMENT '码头拖车Supplier逻辑ID快照;历史数据无法推导时为空',
|
||||
terminal_trucking_company VARCHAR(128) NULL COMMENT '码头拖车公司名称快照',
|
||||
loading_at DATETIME(3) NULL COMMENT '计划或实际装柜时间',
|
||||
estimated_sailing_at DATETIME(3) NULL COMMENT '预计开船时间',
|
||||
actual_sailing_at DATETIME(3) NULL COMMENT '实际开船时间',
|
||||
@@ -69,14 +82,75 @@ CREATE TABLE IF NOT EXISTS fl_container_booking (
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_container_booking_no (booking_no),
|
||||
UNIQUE KEY uk_container_booking_cabinet (cabinet_no),
|
||||
KEY idx_container_booking_container (container_no),
|
||||
UNIQUE KEY uk_container_booking_container_no (container_no),
|
||||
KEY idx_container_booking_merge (record_status,merged_into_container_booking_id),
|
||||
KEY idx_container_booking_warehouse (warehouse_id, updated_at, id),
|
||||
KEY idx_container_booking_voyage (voyage_no, updated_at),
|
||||
KEY idx_container_booking_transit_summary (actual_sailing_at, actual_warehouse_arrival_at, ocean_booking_id, container_type),
|
||||
CONSTRAINT chk_container_booking_source CHECK (source_type IN ('SIMULATION','OCEAN_BOOKING')),
|
||||
CONSTRAINT chk_container_booking_record_status CHECK (record_status IN ('ACTIVE','MERGED')),
|
||||
CONSTRAINT chk_container_booking_capacity CHECK (capacity_cbm IS NULL OR capacity_cbm > 0),
|
||||
CONSTRAINT chk_container_booking_weight CHECK (max_weight_kg IS NULL OR max_weight_kg > 0)
|
||||
) ENGINE=InnoDB COMMENT='平台货柜预订资料;预装状态唯一来源为装柜计划';
|
||||
|
||||
DROP PROCEDURE IF EXISTS phase3_container_real_unit_columns;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_container_real_unit_columns()
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name='fl_container_booking' AND column_name='source_type') THEN
|
||||
ALTER TABLE fl_container_booking ADD COLUMN source_type VARCHAR(24) NOT NULL DEFAULT 'SIMULATION'
|
||||
COMMENT '记录来源:SIMULATION内部模拟柜、OCEAN_BOOKING订舱真实柜' AFTER cabinet_no;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name='fl_container_booking' AND column_name='record_status') THEN
|
||||
ALTER TABLE fl_container_booking ADD COLUMN record_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE'
|
||||
COMMENT '记录状态:ACTIVE可操作、MERGED已合并历史' AFTER source_type;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE()
|
||||
AND table_name='fl_container_booking' AND column_name='merged_into_container_booking_id') THEN
|
||||
ALTER TABLE fl_container_booking ADD COLUMN merged_into_container_booking_id BIGINT UNSIGNED NULL
|
||||
COMMENT '真实柜被模拟柜吸收后的目标稳定柜次ID' AFTER record_status;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name='fl_container_booking' AND index_name='uk_container_booking_container_no') THEN
|
||||
ALTER TABLE fl_container_booking ADD UNIQUE KEY uk_container_booking_container_no (container_no);
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema=DATABASE()
|
||||
AND table_name='fl_container_booking' AND index_name='idx_container_booking_merge') THEN
|
||||
ALTER TABLE fl_container_booking ADD KEY idx_container_booking_merge (record_status,merged_into_container_booking_id);
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||||
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_source') THEN
|
||||
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_source
|
||||
CHECK (source_type IN ('SIMULATION','OCEAN_BOOKING'));
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.table_constraints WHERE constraint_schema=DATABASE()
|
||||
AND table_name='fl_container_booking' AND constraint_name='chk_container_booking_record_status') THEN
|
||||
ALTER TABLE fl_container_booking ADD CONSTRAINT chk_container_booking_record_status
|
||||
CHECK (record_status IN ('ACTIVE','MERGED'));
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_container_real_unit_columns();
|
||||
DROP PROCEDURE phase3_container_real_unit_columns;
|
||||
|
||||
-- 触发器创建前先收敛旧库货柜供应商ID列;历史名称无法反推ID,保持NULL。
|
||||
DROP PROCEDURE IF EXISTS phase3_container_supplier_snapshot_add_column;
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE phase3_container_supplier_snapshot_add_column()
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_schema=DATABASE() AND table_name='fl_container_booking'
|
||||
AND column_name='terminal_trucking_company_id') THEN
|
||||
ALTER TABLE fl_container_booking
|
||||
ADD COLUMN terminal_trucking_company_id BIGINT UNSIGNED NULL COMMENT '码头拖车Supplier逻辑ID快照;历史数据无法推导时为空' AFTER max_weight_kg;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL phase3_container_supplier_snapshot_add_column();
|
||||
DROP PROCEDURE phase3_container_supplier_snapshot_add_column;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_loading_plan (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '智能装柜计划ID(雪花ID)',
|
||||
plan_no VARCHAR(40) NOT NULL COMMENT '平台唯一装柜计划号',
|
||||
@@ -584,10 +658,7 @@ BEGIN
|
||||
DECLARE current_plan_status VARCHAR(24);
|
||||
SELECT MAX(plan_status) INTO current_plan_status
|
||||
FROM fl_loading_plan WHERE container_booking_id = OLD.id;
|
||||
IF current_plan_status = 'COMPLETED' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '装柜完成后货柜全部只读,不允许修改';
|
||||
END IF;
|
||||
IF current_plan_status = 'LOADING' AND (
|
||||
IF current_plan_status IN ('LOADING', 'COMPLETED') AND (
|
||||
NOT (OLD.id <=> NEW.id) OR NOT (OLD.booking_no <=> NEW.booking_no)
|
||||
OR NOT (OLD.cabinet_no <=> NEW.cabinet_no)
|
||||
OR NOT (OLD.channel_code <=> NEW.channel_code)
|
||||
@@ -596,8 +667,6 @@ BEGIN
|
||||
OR NOT (OLD.ocean_booking_requirement_id <=> NEW.ocean_booking_requirement_id)
|
||||
OR NOT (OLD.warehouse_id <=> NEW.warehouse_id)
|
||||
OR NOT (OLD.warehouse_name <=> NEW.warehouse_name)
|
||||
OR NOT (OLD.bill_of_lading_no <=> NEW.bill_of_lading_no)
|
||||
OR NOT (OLD.vessel_name <=> NEW.vessel_name)
|
||||
OR NOT (OLD.container_type <=> NEW.container_type)
|
||||
OR NOT (OLD.shipping_company_id <=> NEW.shipping_company_id)
|
||||
OR NOT (OLD.shipping_company_name <=> NEW.shipping_company_name)
|
||||
@@ -609,9 +678,9 @@ BEGIN
|
||||
OR NOT (OLD.destination_warehouse_name <=> NEW.destination_warehouse_name)
|
||||
OR NOT (OLD.capacity_cbm <=> NEW.capacity_cbm)
|
||||
OR NOT (OLD.max_weight_kg <=> NEW.max_weight_kg)
|
||||
OR NOT (OLD.terminal_trucking_company_id <=> NEW.terminal_trucking_company_id)
|
||||
OR NOT (OLD.terminal_trucking_company <=> NEW.terminal_trucking_company)
|
||||
OR NOT (OLD.loading_at <=> NEW.loading_at)
|
||||
OR NOT (OLD.estimated_sailing_at <=> NEW.estimated_sailing_at)
|
||||
OR NOT (OLD.actual_sailing_at <=> NEW.actual_sailing_at)
|
||||
OR NOT (OLD.estimated_port_arrival_at <=> NEW.estimated_port_arrival_at)
|
||||
OR NOT (OLD.actual_port_arrival_at <=> NEW.actual_port_arrival_at)
|
||||
@@ -622,7 +691,7 @@ BEGIN
|
||||
OR NOT (OLD.created_at <=> NEW.created_at)
|
||||
) THEN
|
||||
SIGNAL SQLSTATE '45000'
|
||||
SET MESSAGE_TEXT = '装柜中仅允许修改集装箱号与航次';
|
||||
SET MESSAGE_TEXT = '装柜作业开始后仅允许修改集装箱号与放舱运输数据';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
@@ -765,7 +834,44 @@ BEGIN
|
||||
IF current_plan_status = 'LOADING' AND EXISTS (
|
||||
SELECT 1 FROM fl_loading_pick_item
|
||||
WHERE loading_plan_item_id = OLD.id AND picked_package_count > 0) THEN
|
||||
IF NOT EXISTS (
|
||||
IF NOT (NEW.warehouse_remark <=> OLD.warehouse_remark) THEN
|
||||
IF NEW.package_count <> OLD.package_count OR NEW.deleted <> OLD.deleted
|
||||
OR NOT (NEW.customer_name <=> OLD.customer_name)
|
||||
OR NOT (NEW.inbound_id <=> OLD.inbound_id)
|
||||
OR NOT (NEW.inbound_detail_id <=> OLD.inbound_detail_id)
|
||||
OR NOT (NEW.order_id <=> OLD.order_id)
|
||||
OR NOT (NEW.order_no <=> OLD.order_no)
|
||||
OR NOT (NEW.inbound_code <=> OLD.inbound_code)
|
||||
OR NOT (NEW.destination_warehouse_id <=> OLD.destination_warehouse_id)
|
||||
OR NOT (NEW.destination_warehouse_code <=> OLD.destination_warehouse_code)
|
||||
OR NOT (NEW.destination_warehouse_name <=> OLD.destination_warehouse_name)
|
||||
OR NOT (NEW.sku_code <=> OLD.sku_code)
|
||||
OR NOT (NEW.product_name_zh <=> OLD.product_name_zh)
|
||||
OR NOT (NEW.product_name_en <=> OLD.product_name_en)
|
||||
OR NOT (NEW.product_image_file_id <=> OLD.product_image_file_id)
|
||||
OR NOT (NEW.product_image_url <=> OLD.product_image_url)
|
||||
OR NOT (NEW.product_quantity <=> OLD.product_quantity)
|
||||
OR NOT (NEW.units_per_carton <=> OLD.units_per_carton)
|
||||
OR NOT (NEW.pallet_count <=> OLD.pallet_count)
|
||||
OR NOT (NEW.gross_weight_kg <=> OLD.gross_weight_kg)
|
||||
OR NOT (NEW.net_weight_kg <=> OLD.net_weight_kg)
|
||||
OR NOT (NEW.dimensional_weight_kg <=> OLD.dimensional_weight_kg)
|
||||
OR NOT (NEW.volume_cbm <=> OLD.volume_cbm)
|
||||
OR NOT (NEW.fba_tracking_no <=> OLD.fba_tracking_no)
|
||||
OR NOT (NEW.shipment_no <=> OLD.shipment_no)
|
||||
OR NOT (NEW.shipping_mark <=> OLD.shipping_mark)
|
||||
OR NOT (NEW.load_sequence <=> OLD.load_sequence)
|
||||
OR NOT (NEW.position_snapshot <=> OLD.position_snapshot)
|
||||
OR NOT (NEW.internal_remark <=> OLD.internal_remark)
|
||||
OR NOT (NEW.customer_remark <=> OLD.customer_remark)
|
||||
OR NOT (NEW.frozen_at <=> OLD.frozen_at)
|
||||
OR NOT (NEW.frozen_by <=> OLD.frozen_by)
|
||||
OR NOT (NEW.removed_by <=> OLD.removed_by)
|
||||
OR NOT (NEW.removed_by_name <=> OLD.removed_by_name)
|
||||
OR NOT (NEW.removed_at <=> OLD.removed_at) THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '已有PDA拣货记录的装柜明细修改仓库备注时不得变更其他业务字段';
|
||||
END IF;
|
||||
ELSEIF NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_pick_item
|
||||
WHERE loading_plan_item_id = OLD.id
|
||||
AND item_status = 'PARTIAL'
|
||||
@@ -800,7 +906,6 @@ BEGIN
|
||||
OR NOT (NEW.position_snapshot <=> OLD.position_snapshot)
|
||||
OR NOT (NEW.internal_remark <=> OLD.internal_remark)
|
||||
OR NOT (NEW.customer_remark <=> OLD.customer_remark)
|
||||
OR NOT (NEW.warehouse_remark <=> OLD.warehouse_remark)
|
||||
OR NOT (NEW.frozen_at <=> OLD.frozen_at)
|
||||
OR NOT (NEW.frozen_by <=> OLD.frozen_by)
|
||||
OR NOT (NEW.removed_by <=> OLD.removed_by)
|
||||
@@ -1209,7 +1314,7 @@ BEGIN
|
||||
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_container_booking'
|
||||
AND column_name IN ('booking_no', 'cabinet_no', 'warehouse_id', 'bill_of_lading_no',
|
||||
'vessel_name', 'voyage_no', 'container_type', 'capacity_cbm',
|
||||
'max_weight_kg', 'terminal_trucking_company')) <> 10 THEN
|
||||
'max_weight_kg', 'terminal_trucking_company_id', 'terminal_trucking_company')) <> 11 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '智能装柜迁移失败:货柜原型字段缺失';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||||
|
||||
@@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS fl_truck_appointment (
|
||||
customer_name VARCHAR(128) NULL COMMENT '客户名称快照',
|
||||
carrier_id BIGINT UNSIGNED NULL COMMENT '拖车服务商逻辑ID',
|
||||
carrier_name VARCHAR(128) NULL COMMENT '拖车服务商名称快照',
|
||||
bill_of_lading_no VARCHAR(128) NULL COMMENT '派车提单号快照;关联RELEASED订舱时由订舱事实源冻结',
|
||||
fleet_contact VARCHAR(64) NULL COMMENT '车队联系人',
|
||||
fleet_phone VARCHAR(32) NULL COMMENT '车队联系电话',
|
||||
driver_name VARCHAR(64) NULL COMMENT '司机姓名',
|
||||
@@ -52,9 +53,9 @@ CREATE TABLE IF NOT EXISTS fl_truck_appointment_history (
|
||||
operator_name VARCHAR(64) NULL COMMENT '操作人姓名快照', request_id VARCHAR(128) NULL COMMENT '请求链路中的请求ID', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '历史记录创建时间', PRIMARY KEY(id), KEY idx_truck_history(appointment_id,revision)
|
||||
) ENGINE=InnoDB COMMENT='拖车状态与改约历史';
|
||||
CREATE TABLE IF NOT EXISTS fl_truck_appointment_attachment (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '拖车附件关系ID', appointment_id BIGINT UNSIGNED NOT NULL COMMENT '所属拖车预约ID', file_id BIGINT UNSIGNED NOT NULL COMMENT 'File服务文件逻辑ID', attachment_type VARCHAR(32) NOT NULL COMMENT '附件类型编码;由业务动作约定',
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '拖车附件关系ID', appointment_id BIGINT UNSIGNED NOT NULL COMMENT '所属拖车预约ID', file_id BIGINT UNSIGNED NOT NULL COMMENT 'File服务文件逻辑ID', attachment_type VARCHAR(32) NOT NULL COMMENT '附件类型:BILL_OF_LADING提单、DISPATCH_ORDER派车单、DRIVER_DOCUMENT司机证件、VEHICLE_DOCUMENT车辆证件、OTHER其他',
|
||||
file_name VARCHAR(255) NULL COMMENT '上传时文件名快照', file_hash CHAR(64) NULL COMMENT '文件内容SHA-256摘要', visibility VARCHAR(24) NOT NULL DEFAULT 'INTERNAL' COMMENT '可见范围:INTERNAL仅平台内部、CLIENT客户可见', uploaded_by BIGINT UNSIGNED NOT NULL COMMENT '上传操作人ID',
|
||||
uploaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '附件上传时间', PRIMARY KEY(id), UNIQUE KEY uk_truck_attachment(appointment_id,file_id), KEY idx_truck_attachment(appointment_id)
|
||||
uploaded_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '附件上传时间', PRIMARY KEY(id), UNIQUE KEY uk_truck_attachment(appointment_id,file_id), KEY idx_truck_attachment(appointment_id), CONSTRAINT chk_truck_attachment_type CHECK(attachment_type IN ('BILL_OF_LADING','DISPATCH_ORDER','DRIVER_DOCUMENT','VEHICLE_DOCUMENT','OTHER'))
|
||||
) ENGINE=InnoDB COMMENT='拖车附件文件引用';
|
||||
CREATE TABLE IF NOT EXISTS fl_truck_appointment_operation (
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '拖车写操作幂等记录ID', appointment_id BIGINT UNSIGNED NOT NULL COMMENT '所属拖车预约ID', idempotency_key VARCHAR(128) NOT NULL COMMENT '客户端写操作幂等键', request_hash CHAR(64) NOT NULL COMMENT '规范化请求内容SHA-256摘要;用于识别同键不同请求',
|
||||
@@ -83,10 +84,18 @@ BEGIN
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CALL phase3_truck_add_column(
|
||||
'bill_of_lading_no',
|
||||
'VARCHAR(128) NULL COMMENT ''派车提单号快照;关联RELEASED订舱时由订舱事实源冻结'' AFTER carrier_name'
|
||||
);
|
||||
CALL phase3_truck_add_column(
|
||||
'actual_arrived_warehouse_at',
|
||||
'DATETIME(3) NULL COMMENT ''车辆实际到达收货仓时间'' AFTER planned_loading_at'
|
||||
);
|
||||
CALL phase3_truck_add_column(
|
||||
'actual_loading_at',
|
||||
'DATETIME(3) NULL COMMENT ''实际开始装柜时间'' AFTER actual_arrived_warehouse_at'
|
||||
);
|
||||
CALL phase3_truck_add_column(
|
||||
'actual_departed_at',
|
||||
'DATETIME(3) NULL COMMENT ''车辆实际离仓时间;DEPARTED后供LOADED门禁使用'' AFTER actual_loading_at'
|
||||
|
||||
@@ -55,7 +55,14 @@ paths:
|
||||
parameters: [{$ref: '#/components/parameters/CaseId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
post:
|
||||
summary: Submit to customs agent and freeze approved file versions in a batch snapshot
|
||||
responses: {'200': {description: Submission batch}}
|
||||
description: customsAgentId must identify an ACTIVE Warehouse supplier in category CUSTOMS; the persisted agent name is resolved server-side and free text cannot bypass supplier validation.
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/SubmitBatchRequest'}}}}
|
||||
responses:
|
||||
'200':
|
||||
description: Submission batch
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CustomsSubmissionBatch'}
|
||||
/api/platform/v1/customs/submission-batches/{batchId}/receipts:
|
||||
parameters:
|
||||
- {name: batchId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
@@ -91,3 +98,30 @@ components:
|
||||
in: path
|
||||
required: true
|
||||
schema: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
schemas:
|
||||
SubmitBatchRequest:
|
||||
type: object
|
||||
required: [customsAgentId, expectedVersion]
|
||||
properties:
|
||||
customsAgentId: {type: integer, format: int64, minimum: 1, description: ACTIVE Warehouse supplier ID validated as CUSTOMS.}
|
||||
customsAgentName: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint; persisted name always comes from Warehouse.}
|
||||
externalReference: {type: string, maxLength: 128, nullable: true}
|
||||
note: {type: string, maxLength: 500, nullable: true}
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
CustomsSubmissionBatch:
|
||||
type: object
|
||||
required: [id, customsCaseId, batchNo, batchVersion, customsAgentName, status, submittedBy, submittedAt]
|
||||
properties:
|
||||
id: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
customsCaseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
batchNo: {type: string, maxLength: 40}
|
||||
batchVersion: {type: integer, minimum: 1}
|
||||
customsAgentId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: Server-validated CUSTOMS supplier ID snapshot; null only for non-migrated historical rows.}
|
||||
customsAgentName: {type: string, maxLength: 128, description: Server-frozen Warehouse supplier name snapshot.}
|
||||
externalReference: {type: string, maxLength: 128, nullable: true}
|
||||
fileSnapshotJson: {type: string}
|
||||
status: {type: string, enum: [SUBMITTED, ACKNOWLEDGED, REJECTED, COMPLETED]}
|
||||
note: {type: string, maxLength: 500, nullable: true}
|
||||
submittedBy: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
submittedByName: {type: string, maxLength: 64, nullable: true}
|
||||
submittedAt: {type: string, format: date-time}
|
||||
|
||||
@@ -130,38 +130,94 @@ paths:
|
||||
/api/platform/v1/inbounds/{inboundId}/fees/calculate:
|
||||
post:
|
||||
summary: 按审核通过测量快照试算 FBA 费用
|
||||
description: 仅匹配当前唯一 ACTIVE 船期价格表中的 ACTIVE 价格行,按起运仓、渠道、目的仓、pricingTaxMode 和计费单位估价;不再以 carrierId 或客户计费规则作为前置。海派还需按FBA区域和重量阶梯匹配;测量审核后即使主流程已到 WAIT_SPLIT,也可独立试算且不回退主流程状态;需要 firstleg:fba-pricing:calculate 权限。
|
||||
description: 仅匹配当前唯一 ACTIVE 船期价格表中的 ACTIVE 价格行,按起运仓、渠道、目的仓、pricingTaxMode 和计费单位估价;不再以 carrierId、业务 customerId 或客户计费规则作为前置。区域阶梯渠道可按平台 FBA 仓或海外中转仓的邮编区域和重量阶梯匹配;测量审核后即使主流程已到 WAIT_SPLIT,也可独立试算且不回退主流程状态;需要 firstleg:fba-pricing:calculate 权限。
|
||||
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody: {content: {application/json: {schema: {$ref: '#/components/schemas/CalculateRequest'}}}}
|
||||
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/FeeSnapshotApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fees/confirm:
|
||||
post:
|
||||
summary: 确认最新费用快照
|
||||
description: 确认费用只更新费用状态;主流程已到 WAIT_SPLIT 时保持不变。
|
||||
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/FeeSnapshotApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fees/send:
|
||||
post:
|
||||
summary: 发送已确认费用快照
|
||||
description: 该动作涉及后续仓储流程,本轮二期验收不调用。
|
||||
description: 人工发布当前已确认版本;仅此动作将费用置为 SENT、生成 PUBLISHED 的 FBA_PRICING 只读派生明细并写入 Finance Outbox。入库单已到 WAIT_SPLIT 时不得回退或阻塞主流程。
|
||||
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/FeeSnapshotApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fees/latest:
|
||||
get:
|
||||
summary: 查询最新费用快照
|
||||
summary: 查询 fl_inbound_fee 最新费用快照
|
||||
description: fl_inbound_fee 是头程运费唯一事实源;fee-details 中 sourceType=FBA_PRICING 的记录仅为最新版本只读派生。
|
||||
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}]
|
||||
responses: {'200': {description: 成功或业务失败}, '403': {description: 无权限}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/FeeSnapshotApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fees/published:
|
||||
put:
|
||||
summary: 调整已发布费用
|
||||
description: 需要 firstleg:fba-pricing:adjust 权限;生成新 SENT 版本并将旧版本标记 SUPERSEDED,不回退 WAIT_SPLIT 状态。
|
||||
summary: 调整初算或已发布费用
|
||||
description: 需要 firstleg:fba-pricing:adjust 权限;人工修改计费量、单价和附加费,小计与总额由服务端重算并生成新版本,不回退 WAIT_SPLIT 状态。调整 CALCULATED/CONFIRMED 时生成新的 CALCULATED 版本;调整 SENT 时生成新的 SENT 版本并发布调整事件。
|
||||
parameters: [{$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/AdjustFeeRequest'}
|
||||
responses: {'200': {description: 成功或业务失败}, '403': {description: 无调整权限}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/FeeSnapshotApiResponse'}}}}, '403': {description: 无调整权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fee-details:
|
||||
get:
|
||||
summary: 查询入仓费用明细
|
||||
description: 默认隐藏 publicationStatus=SUPERSEDED 的历史版本。FBA_PRICING 是 fl_inbound_fee 最新版本的只读派生;MANUAL 是独立新增、审核和版本调整的其他应收/应付。
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/AccountId'}
|
||||
- {$ref: '#/components/parameters/InboundId'}
|
||||
- {name: includeHistory, in: query, required: false, schema: {type: boolean, default: false}, description: true 时包含 SUPERSEDED 历史版本}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailListApiResponse'}}}}, '403': {description: 无权限}}
|
||||
post:
|
||||
summary: 新增 MANUAL 其他费用
|
||||
description: 只允许创建 MANUAL 来源;FBA_PRICING 由系统从 fl_inbound_fee 派生。
|
||||
parameters: [{$ref: '#/components/parameters/AccountId'}, {$ref: '#/components/parameters/InboundId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailSaveRequest'}}}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}:
|
||||
put:
|
||||
summary: 编辑待审核 MANUAL 费用
|
||||
description: FBA_PRICING 只读派生禁止通过通用费用接口修改。
|
||||
parameters: [{$ref: '#/components/parameters/AccountId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/DetailId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailSaveRequest'}}}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailApiResponse'}}}}, '403': {description: 无权限}}
|
||||
delete:
|
||||
summary: 删除待审核 MANUAL 费用
|
||||
description: FBA_PRICING 只读派生禁止删除。
|
||||
parameters: [{$ref: '#/components/parameters/AccountId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/DetailId'}]
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/VoidApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}/approve:
|
||||
post:
|
||||
summary: 审核通过 MANUAL 费用当前版本
|
||||
parameters: [{$ref: '#/components/parameters/AccountId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/DetailId'}]
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/VoidApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}/reject:
|
||||
post:
|
||||
summary: 驳回 MANUAL 费用当前版本
|
||||
parameters: [{$ref: '#/components/parameters/AccountId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/DetailId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailRejectRequest'}}}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/VoidApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}/adjust:
|
||||
post:
|
||||
summary: 调整已审核 MANUAL 费用并创建新版本
|
||||
description: 原版本置为 SUPERSEDED,新记录使用相同 sourceFeeId、sourceFeeVersion+1 并重新进入 PENDING/DRAFT;FBA_PRICING 必须通过头程费用调整接口处理。
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/AccountId'}
|
||||
- {$ref: '#/components/parameters/InboundId'}
|
||||
- {$ref: '#/components/parameters/DetailId'}
|
||||
- {name: expectedSourceVersion, in: query, required: true, schema: {type: integer, minimum: 1}}
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailSaveRequest'}}}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailApiResponse'}}}}, '403': {description: 无权限}}
|
||||
/api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}/void:
|
||||
post:
|
||||
summary: 作废已审核 MANUAL 费用当前版本
|
||||
parameters: [{$ref: '#/components/parameters/AccountId'}, {$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/DetailId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/InboundFeeDetailVersionRequest'}}}}
|
||||
responses: {'200': {description: 成功或业务失败, content: {application/json: {schema: {$ref: '#/components/schemas/VoidApiResponse'}}}}, '403': {description: 无权限}}
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT}
|
||||
@@ -170,14 +226,187 @@ components:
|
||||
TenantId: {name: tenantId, in: query, required: true, description: 目标客户 ID;参数名为历史兼容, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
Id: {name: id, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
InboundId: {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
DetailId: {name: detailId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
AccountId: {name: accountId, in: query, required: true, description: 目标客户 ID, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
VersionId: {name: versionId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
schemas:
|
||||
FeeSnapshotApiResponse:
|
||||
type: object
|
||||
required: [code, message, data]
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/FeeSnapshotResponse'}
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
timestamp: {type: string, format: date-time, nullable: true}
|
||||
FeeSnapshotResponse:
|
||||
type: object
|
||||
description: fl_inbound_fee 当前版本快照;自动初算为 CALCULATED,人工确认后为 CONFIRMED,只有人工发布后为 SENT。
|
||||
required: [feeId, feeNo, feeVersion, feeStatus, inboundId, totalAmount, currency]
|
||||
properties:
|
||||
feeId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
feeNo: {type: string}
|
||||
feeVersion: {type: integer, minimum: 1}
|
||||
feeStatus: {type: string, enum: [CALCULATED, CONFIRMED, SENT, SUPERSEDED]}
|
||||
inboundId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
inboundCode: {type: string, nullable: true}
|
||||
chargeRuleId: {type: string, nullable: true}
|
||||
ruleCode: {type: string, nullable: true}
|
||||
chargeRuleVersion: {type: integer, nullable: true}
|
||||
carrierRateId: {type: string, nullable: true}
|
||||
rateCode: {type: string, nullable: true}
|
||||
carrierRateVersion: {type: integer, nullable: true}
|
||||
priceBookVersionId: {type: string, nullable: true}
|
||||
customerId: {type: string, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
originWarehouseId: {type: string, nullable: true}
|
||||
originWarehouseName: {type: string, nullable: true}
|
||||
businessType: {type: string, nullable: true}
|
||||
channelCode: {type: string, nullable: true}
|
||||
carrierId: {type: string, nullable: true}
|
||||
carrierName: {type: string, nullable: true}
|
||||
destinationType: {type: string, nullable: true}
|
||||
destinationWarehouseId: {type: string, nullable: true}
|
||||
destinationName: {type: string, nullable: true}
|
||||
destinationCode: {type: string, nullable: true}
|
||||
taxMode: {type: string, nullable: true}
|
||||
pricingTaxMode: {type: string, nullable: true}
|
||||
chargeMode: {type: string, nullable: true}
|
||||
conversionFactor: {type: number, nullable: true}
|
||||
roundingScale: {type: integer, nullable: true}
|
||||
roundingMode: {type: string, nullable: true}
|
||||
actualWeightKg: {type: number, nullable: true}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
convertedQuantity: {type: number, nullable: true}
|
||||
chargeableQuantity: {type: number, nullable: true}
|
||||
chargeUnit: {type: string, nullable: true}
|
||||
unitPrice: {type: number, nullable: true}
|
||||
subtotalAmount: {type: number, nullable: true}
|
||||
additionalAmount: {type: number, nullable: true}
|
||||
totalAmount: {type: number}
|
||||
currency: {type: string, pattern: '^[A-Z]{3}$'}
|
||||
rateEffectiveFrom: {type: string, format: date-time, nullable: true}
|
||||
rateEffectiveTo: {type: string, format: date-time, nullable: true}
|
||||
calculatedAt: {type: string, format: date-time, nullable: true}
|
||||
confirmedAt: {type: string, format: date-time, nullable: true}
|
||||
sentAt: {type: string, format: date-time, nullable: true}
|
||||
nextProcessStatus: {type: string, nullable: true, description: 费用动作后的入库主流程状态;审核完成后保持 WAIT_SPLIT}
|
||||
InboundFeeDetailSaveRequest:
|
||||
type: object
|
||||
required: [feeItemId, direction, businessDate, unitPrice, currency, amount]
|
||||
properties:
|
||||
feeItemId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
direction: {type: string, enum: [RECEIVABLE, PAYABLE]}
|
||||
businessDate: {type: string, format: date}
|
||||
unitPrice: {type: number, minimum: 0}
|
||||
currency: {type: string, pattern: '^[A-Z]{3}$'}
|
||||
amount: {type: number, minimum: 0}
|
||||
supplierId: {type: string, nullable: true}
|
||||
remark: {type: string, maxLength: 500, nullable: true}
|
||||
InboundFeeDetailVersionRequest:
|
||||
type: object
|
||||
required: [expectedSourceVersion]
|
||||
properties: {expectedSourceVersion: {type: integer, minimum: 1}}
|
||||
InboundFeeDetailRejectRequest:
|
||||
type: object
|
||||
required: [reason]
|
||||
properties: {reason: {type: string, minLength: 1, maxLength: 500}}
|
||||
InboundFeeDetailResource:
|
||||
type: object
|
||||
description: 入仓费用展示记录。sourceType=FBA_PRICING 时是只读派生;sourceType=MANUAL 时按 sourceFeeId/sourceFeeVersion 独立版本化。
|
||||
required: [id, tenantId, inboundId, feeItemId, sourceType, sourceFeeVersion, direction, businessDate, unitPrice, currency, amount, reviewStatus, publicationStatus]
|
||||
properties:
|
||||
id: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
tenantId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
inboundId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
feeItemId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
sourceType: {type: string, enum: [MANUAL, FBA_PRICING]}
|
||||
sourceFeeId: {type: string, nullable: true, description: FBA_PRICING 对应 fl_inbound_fee.id;MANUAL V2+ 指向稳定的首版本来源 ID}
|
||||
sourceFeeVersion: {type: integer, minimum: 1}
|
||||
sourceFeeNo: {type: string, nullable: true}
|
||||
inboundCode: {type: string, nullable: true}
|
||||
direction: {type: string, enum: [RECEIVABLE, PAYABLE]}
|
||||
supplierId: {type: string, nullable: true}
|
||||
supplierName: {type: string, nullable: true, readOnly: true}
|
||||
supplierCategory: {type: string, enum: [BOOKING, CUSTOMS, TRUCKING, CERTIFICATE], nullable: true, description: 费用项目供应商类别约束快照;为空表示仅要求供应商ACTIVE}
|
||||
businessDate: {type: string, format: date}
|
||||
customerId: {type: string, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
invoiceTitle: {type: string, nullable: true}
|
||||
feeCategorySnapshot: {type: string, nullable: true}
|
||||
tradeScopeSnapshot: {type: string, nullable: true}
|
||||
feeTypeNameSnapshot: {type: string, nullable: true}
|
||||
channelCode: {type: string, nullable: true}
|
||||
channelName: {type: string, nullable: true}
|
||||
destinationCode: {type: string, nullable: true}
|
||||
destinationName: {type: string, nullable: true}
|
||||
packageCount: {type: integer, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
volumeWeightKg: {type: number, nullable: true}
|
||||
chargeableWeightKg: {type: number, nullable: true}
|
||||
chargeableVolumeCbm: {type: number, nullable: true}
|
||||
chargeableQuantity: {type: number, nullable: true}
|
||||
chargeUnit: {type: string, nullable: true}
|
||||
unitPrice: {type: number}
|
||||
currency: {type: string, pattern: '^[A-Z]{3}$'}
|
||||
amount: {type: number}
|
||||
remark: {type: string, nullable: true}
|
||||
reviewStatus: {type: string, enum: [PENDING, APPROVED, REJECTED]}
|
||||
reviewerId: {type: string, nullable: true}
|
||||
reviewerName: {type: string, nullable: true}
|
||||
reviewedAt: {type: string, format: date-time, nullable: true}
|
||||
reviewReason: {type: string, nullable: true}
|
||||
publicationStatus: {type: string, enum: [DRAFT, PUBLISHED, SUPERSEDED, VOIDED]}
|
||||
publishedBy: {type: string, nullable: true}
|
||||
publishedByName: {type: string, nullable: true}
|
||||
publishedAt: {type: string, format: date-time, nullable: true}
|
||||
inboundedAt: {type: string, format: date-time, nullable: true}
|
||||
createdBy: {type: string, nullable: true}
|
||||
createdAt: {type: string, format: date-time, nullable: true}
|
||||
updatedBy: {type: string, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
deleted: {type: boolean, nullable: true}
|
||||
version: {type: integer, nullable: true}
|
||||
InboundFeeDetailApiResponse:
|
||||
type: object
|
||||
required: [code, message, data]
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/InboundFeeDetailResource'}
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
timestamp: {type: string, format: date-time, nullable: true}
|
||||
InboundFeeDetailListApiResponse:
|
||||
type: object
|
||||
required: [code, message, data]
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {type: array, items: {$ref: '#/components/schemas/InboundFeeDetailResource'}}
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
timestamp: {type: string, format: date-time, nullable: true}
|
||||
VoidApiResponse:
|
||||
type: object
|
||||
required: [code, message]
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {nullable: true}
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
timestamp: {type: string, format: date-time, nullable: true}
|
||||
AdjustFeeRequest:
|
||||
type: object
|
||||
required: [subtotalAmount, additionalAmount, reason]
|
||||
required: [expectedFeeVersion, chargeableQuantity, unitPrice, additionalAmount, reason]
|
||||
properties:
|
||||
subtotalAmount: {type: number, minimum: 0, multipleOf: 0.01}
|
||||
expectedFeeVersion: {type: integer, minimum: 1}
|
||||
chargeableQuantity: {type: number, exclusiveMinimum: 0, description: 计费量,单位沿用当前费用快照的 chargeUnit}
|
||||
unitPrice: {type: number, minimum: 0, description: 人工确认单价}
|
||||
additionalAmount: {type: number, minimum: 0, multipleOf: 0.01}
|
||||
reason: {type: string, minLength: 1, maxLength: 500}
|
||||
CalculateRequest:
|
||||
|
||||
@@ -100,6 +100,7 @@ components:
|
||||
category: {type: string, maxLength: 64}
|
||||
tradeScope: {type: string, enum: [DOMESTIC, INTERNATIONAL]}
|
||||
feeCode: {type: string, maxLength: 50, pattern: '^[A-Za-z0-9_-]+$'}
|
||||
supplierCategory: {type: string, enum: [BOOKING, CUSTOMS, TRUCKING, CERTIFICATE], nullable: true, description: PAYABLE供应商类别约束;为空时可选任意ACTIVE Warehouse Supplier}
|
||||
feeTypeName: {type: string, maxLength: 128}
|
||||
pickupPayable: {type: boolean}
|
||||
pickupReceivable: {type: boolean}
|
||||
@@ -115,6 +116,7 @@ components:
|
||||
category: {type: string}
|
||||
tradeScope: {type: string, enum: [DOMESTIC, INTERNATIONAL]}
|
||||
feeCode: {type: string}
|
||||
supplierCategory: {type: string, enum: [BOOKING, CUSTOMS, TRUCKING, CERTIFICATE], nullable: true, description: PAYABLE供应商类别约束;为空时可选任意ACTIVE Warehouse Supplier}
|
||||
feeTypeName: {type: string}
|
||||
pickupPayable: {type: boolean}
|
||||
pickupReceivable: {type: boolean}
|
||||
|
||||
@@ -49,6 +49,51 @@ paths:
|
||||
- {name: initializationKey, in: query, required: true, schema: {type: string, minLength: 1, maxLength: 128}}
|
||||
responses:
|
||||
'200': {description: 幂等补偿完成}
|
||||
/internal/firstleg/v1/pricing/channels/validate:
|
||||
post:
|
||||
summary: 校验订单渠道并按需返回进仓编码前缀
|
||||
description: 仅供订单服务调用;AUTO 模式要求并返回编码前缀,LEGACY_MANUAL 模式仅校验渠道可用性。
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [tenantId, originWarehouseId, channelCode, destinationType, requireInboundCodePrefix]
|
||||
properties:
|
||||
tenantId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
originWarehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
channelCode: {type: string, minLength: 1, maxLength: 64}
|
||||
destinationType: {type: string, enum: [FBA, OVERSEAS, PRIVATE]}
|
||||
requireInboundCodePrefix: {type: boolean}
|
||||
responses:
|
||||
'200': {description: 成功时 data 返回 channelCode;requireInboundCodePrefix=true 时同时返回 inboundCodePrefix}
|
||||
/internal/firstleg/v1/inbounds/{inboundId}/fees/{feeId}/source-snapshot:
|
||||
get:
|
||||
summary: Finance 按入库费用来源版本精确回查不可变快照
|
||||
description: sourceVersion 必填;只返回 sourceFeeId 与该版本精确匹配的已发布费用,不回退到其他版本。
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
parameters:
|
||||
- {name: inboundId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
- {name: feeId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
- {name: sourceVersion, in: query, required: true, schema: {type: integer, minimum: 1}}
|
||||
responses:
|
||||
'200': {description: 成功时 data 为 FinanceFeeSourceSnapshot;不存在精确版本时返回业务错误}
|
||||
/internal/firstleg/v1/containers/{containerId}/fees/{feeId}/source-snapshot:
|
||||
get:
|
||||
summary: Finance 按柜次费用来源版本精确回查不可变快照
|
||||
description: sourceVersion 必填;只返回 sourceFeeId 与该版本精确匹配的已发布费用,不回退到当前可变版本。
|
||||
security:
|
||||
- internalServiceToken: []
|
||||
parameters:
|
||||
- {name: containerId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
- {name: feeId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
- {name: sourceVersion, in: query, required: true, schema: {type: integer, minimum: 1}}
|
||||
responses:
|
||||
'200': {description: 成功时 data 为 FinanceFeeSourceSnapshot;不存在精确版本时返回业务错误}
|
||||
/api/platform/v1/first-leg/customer-dimension-config:
|
||||
get:
|
||||
summary: 查询客户固定四箱规尺寸配置
|
||||
@@ -205,6 +250,7 @@ paths:
|
||||
- {$ref: '#/components/parameters/Page'}
|
||||
- {$ref: '#/components/parameters/Size'}
|
||||
- {name: keyword, in: query, schema: {type: string, maxLength: 128}}
|
||||
- {name: customerId, in: query, description: 仅返回包含该客户货物的柜次, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: containerNo, in: query, schema: {type: string, maxLength: 64}}
|
||||
- {name: cabinetNo, in: query, schema: {type: string, maxLength: 64}}
|
||||
- {name: warehouseId, in: query, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
@@ -217,16 +263,29 @@ paths:
|
||||
schema: {$ref: '#/components/schemas/ContainerBookingPageEnvelope'}
|
||||
'403': {description: 无货柜查询权限}
|
||||
post:
|
||||
summary: 新增平台货柜
|
||||
description: 需要 platform:firstleg:container:create 权限;新增后聚合状态为 WAIT_LOADING(待装柜)
|
||||
summary: 新增内部模拟柜
|
||||
description: 需要 platform:firstleg:container:create 权限;服务端生成稳定 containerBookingId/cabinetNo,不要求真实柜号或订舱
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ContainerSaveRequest'}
|
||||
schema: {$ref: '#/components/schemas/ContainerSimulationCreateRequest'}
|
||||
responses:
|
||||
'200': {description: 成功或重复号、参数校验等业务失败}
|
||||
'403': {description: 无新增按钮权限}
|
||||
/api/platform/v1/containers/{id}/ocean-booking-link:
|
||||
post:
|
||||
summary: 将内部模拟柜关联到已发布订舱需求并补录真实柜号
|
||||
description: 需要 platform:firstleg:container:edit;请求指定 RELEASED oceanBookingId、未满配 requirementId 和港口提柜后取得的唯一 containerNo。首次关联允许 WAIT_LOADING 或已有预排货的 EDITING 模拟柜,保留原 containerBookingId/cabinetNo/loadingPlanId/items,原子写入订舱、运输和范围快照并创建 allocation;不会创建或消费另一条真实柜记录。存在跨仓、跨渠道、柜型规格不一致、超量、重复柜号、PDA、有效装柜证据、业务单证或 LOADING/COMPLETED 等事实时拒绝。
|
||||
parameters: [{$ref: '#/components/parameters/ContainerBookingId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ContainerOceanBookingLinkRequest'}}}}
|
||||
responses: {'200': {description: 关联后的同一模拟柜 containerBookingId/cabinetNo;列表仍只有这一条 ACTIVE 操作身份}}
|
||||
delete:
|
||||
summary: 解除内部模拟柜的真实订舱关联
|
||||
description: 需要 platform:firstleg:container:edit;解绑或换绑仅允许完全空的 WAIT_LOADING 模拟柜;原子清除当前柜订舱/运输快照和 allocation,不恢复或创建其他柜记录。任何排货、PDA、有效装柜证据或业务单证事实均拒绝。
|
||||
parameters: [{$ref: '#/components/parameters/ContainerBookingId'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ContainerOceanBookingUnlinkRequest'}}}}
|
||||
responses: {'200': {description: 解除后的同一 containerBookingId/cabinetNo}}
|
||||
/api/platform/v1/containers/{id}:
|
||||
get:
|
||||
summary: 查询平台货柜详情
|
||||
@@ -323,6 +382,89 @@ paths:
|
||||
responses:
|
||||
'200': {description: 成功、计划不存在或计划已进入预装流程}
|
||||
'403': {description: 无智能装柜查询权限}
|
||||
/api/platform/v1/first-leg/smart-loading/plans/{planId}/assignments:
|
||||
post:
|
||||
summary: 向预装计划新增入库单明细
|
||||
description: >-
|
||||
EDITING 或 LOADING 且 PDA task 尚未完成时可用。服务端按 planId 锁定同一 PDA task,
|
||||
校验仓库、渠道、客户范围、可用箱数、容积和限重后原子新增/累加明细;LOADING 时同步到原 taskId,
|
||||
递增 planVersion、taskVersion,原 inventorySnapshotToken 随即失效。与 PDA batch/final complete 并发时,
|
||||
锁顺序为 PDA task 行锁 -> plan 乐观锁;仅先取得任务锁且持有匹配版本的一方成功,另一方返回 A0409。
|
||||
PDA 应重新 GET task detail 与 inventory snapshot,Admin 应重新 GET SmartLoadingPlan。新增项沿用原 taskId、
|
||||
loadingPlanId、containerBookingId 和 inbound identity;已全拣任务会因新增 PENDING item 从 AWAITING_REVIEW
|
||||
回到 IN_PROGRESS,已有证据不删除,但 final complete 门禁撤回。
|
||||
parameters:
|
||||
- {name: planId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/SmartLoadingAssignRequest'}}}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,data 为最新 SmartLoadingPlan}
|
||||
/api/platform/v1/first-leg/smart-loading/plans/{planId}/items/{itemId}:
|
||||
patch:
|
||||
summary: 调整预装计划明细箱数
|
||||
description: >-
|
||||
EDITING/LOADING 均可;packageCount 不得小于已拣 pickedPackageCount。成功递增 plan、计划明细和现有 PDA task 版本,
|
||||
原 inventorySnapshotToken 失效。PDA task 已完成或并发版本变化返回 A0409,事务零写入;调用方须刷新
|
||||
SmartLoadingPlan,PDA 在下一次写入前须刷新 task detail 与 inventory snapshot。
|
||||
parameters:
|
||||
- {name: planId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: itemId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/SmartLoadingAdjustRequest'}}}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,data 为最新 SmartLoadingPlan}
|
||||
delete:
|
||||
summary: 删除未拣预装计划明细
|
||||
description: >-
|
||||
仅 pickedPackageCount=0 且计划/PDA task 未最终完成时允许。成功递增 plan 和 task 版本并使原库存快照失效;
|
||||
已拣、COMPLETED 或并发版本变化返回 A0409,事务零写入。PDA final complete 与本操作使用相同 task 行锁,
|
||||
因而只允许一个事务成功;失败方刷新计划、task detail 与 inventory snapshot 后再决定后续操作。
|
||||
parameters:
|
||||
- {name: planId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: itemId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/SmartLoadingDeleteRequest'}}}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,data 为最新 SmartLoadingPlan}
|
||||
/api/platform/v1/first-leg/smart-loading/plans/{planId}/items/{itemId}/warehouse-remark:
|
||||
patch:
|
||||
summary: 修改装柜明细仓库备注
|
||||
description: >-
|
||||
需要 platform:firstleg:preload:edit 权限。EDITING/LOADING 可修改或清空 warehouseRemark;
|
||||
COMPLETED、计划/明细版本冲突返回 A0409 且事务零写入,写入历史且与 PDA final complete 使用同一并发门禁。
|
||||
成功响应中的 version 是递增后的 planVersion,items[].version 是递增后的 itemVersion;调用方同时使用
|
||||
canEdit、items[].canAdjust/canRemove 与自身权限决定操作可用性。数量调整或动态增量不会覆盖已有 warehouseRemark。
|
||||
parameters:
|
||||
- {name: planId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: itemId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/SmartLoadingWarehouseRemarkRequest'}}}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,data 为最新 SmartLoadingPlan}
|
||||
/api/platform/v1/first-leg/smart-loading/plans/{planId}/push-preload:
|
||||
post:
|
||||
summary: 推送智能装柜计划到预装清单
|
||||
description: >-
|
||||
需要 platform:firstleg:smart-loading:push 权限。push-preload 不要求真实柜号,
|
||||
只冻结 EDITING 柜次计划的明细并建立稳定预装关联,不创建 PDA 作业任务;计划必须处于 EDITING 且版本匹配。
|
||||
预装页面随后补录真实柜号;只有预装 CONFIRMED、真实柜号存在、已关联 RELEASED 订舱时才创建待领取 PDA 任务,领取后才进入 LOADING。
|
||||
智能装柜首次分配会创建入库单级 PENDING 预装聚合;push 会在同一事务内对全部实际分配再次确保该聚合存在且仍为 PENDING,
|
||||
但不会覆盖 CONFIRMING/CONFIRMED 记录,也不会在 LOADING 后补建。
|
||||
推送事务会先为所有已分配入库单建立 PENDING 预装计划与 EDITING 柜次计划的正式关联,
|
||||
任一关联缺失、版本冲突或分配数量为零都会整体失败并禁止 PDA/LOADING;不得在 LOADING 后补关联。
|
||||
parameters:
|
||||
- {name: planId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {$ref: '#/components/parameters/IdempotencyKey'}
|
||||
requestBody:
|
||||
required: true
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/SmartLoadingPushRequest'}}}
|
||||
responses:
|
||||
'200': {description: 推送成功或业务失败,按响应体 code 判断}
|
||||
'403': {description: 无智能装柜推送权限}
|
||||
/openapi/v1/inbound-measurements:
|
||||
post:
|
||||
summary: 提交一条第三方箱规测量结果和一张图片
|
||||
@@ -355,12 +497,11 @@ paths:
|
||||
type: object
|
||||
required: [boxCode, length, width, height, machineNo, picture]
|
||||
properties:
|
||||
boxCode: {type: string, maxLength: 128, example: 'RK-2085911321565003778#4-1'}
|
||||
boxCode: {type: string, maxLength: 128, example: 'ZBYHK2608140001#4-1'}
|
||||
shipment_id: {type: string, maxLength: 64, example: 'FBA19L5KMY2CU000002', description: '可选;设备从Amazon FBA箱标实际扫描得到,有则原样上传;系统只保存,不生成且不覆盖客户下单值'}
|
||||
length: {type: number, format: decimal, minimum: 0.001}
|
||||
width: {type: number, format: decimal, minimum: 0.001}
|
||||
height: {type: number, format: decimal, minimum: 0.001}
|
||||
weight: {type: number, format: decimal, minimum: 0.001, nullable: true}
|
||||
machineNo: {type: string, maxLength: 64}
|
||||
measuredAt: {type: string, format: date-time, nullable: true}
|
||||
picture: {type: string, description: 'data:image/jpeg;base64,...'}
|
||||
@@ -457,6 +598,43 @@ paths:
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
'403': {description: 无查看权限}
|
||||
/api/platform/v1/first-leg/inbounds/{inboundId}/measurement:
|
||||
get:
|
||||
summary: 查询入库测量详情
|
||||
description: >-
|
||||
需要 platform:firstleg:inbound:measure:view 权限。尺寸测量图片只随 items 返回,
|
||||
由稳定的 item.id(measurementItemId)与 intelligentImageFileId 关联;称重图片只随
|
||||
weightResults 返回,由稳定的 weightResult.id 与 fileId 关联,并携带确认状态及服务端确认的
|
||||
grossWeightKg/netWeightKg。两类证据不得合并为无明细归属的统一图片列表。
|
||||
actualGrossWeightKg 是服务端确认后的实测毛重,actualNetWeightKg 是扣除托盘皮重后的货物净重;
|
||||
两者语义独立,消费者不得互相回退,也不得根据图片或 OCR 值自行重算。
|
||||
parameters:
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/InboundMeasurementDetailEnvelope'}
|
||||
'403': {description: 无入库测量查看权限}
|
||||
/api/platform/v1/first-leg/inbounds/{inboundId}/measurement/draft:
|
||||
put:
|
||||
summary: 保存测量草稿
|
||||
description: >-
|
||||
原单据号码、申报毛重和申报体积由服务端从客户订单生成快照,客户端不得提交覆盖;
|
||||
actualGoodsName 默认使用客户货物品名汇总,但允许测量人员修正。
|
||||
parameters:
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/InboundMeasurementSaveDraftRequest'}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,按响应体 code 判断}
|
||||
'403': {description: 无测量编辑权限}
|
||||
/api/platform/v1/first-leg/inbounds/{inboundId}/measurement/carton-scans:
|
||||
post:
|
||||
summary: 平台模拟单箱设备扫描
|
||||
@@ -482,7 +660,6 @@ paths:
|
||||
length: {type: number, format: double, minimum: 0.001}
|
||||
width: {type: number, format: double, minimum: 0.001}
|
||||
height: {type: number, format: double, minimum: 0.001}
|
||||
weight: {type: number, format: double, minimum: 0.001, nullable: true}
|
||||
machineNo: {type: string, maxLength: 64}
|
||||
measuredAt: {type: string, format: date-time}
|
||||
image: {type: string, format: binary}
|
||||
@@ -514,20 +691,68 @@ paths:
|
||||
length: {type: number, format: double, minimum: 0.001}
|
||||
width: {type: number, format: double, minimum: 0.001}
|
||||
height: {type: number, format: double, minimum: 0.001}
|
||||
weight: {type: number, format: double, minimum: 0.001, nullable: true}
|
||||
machineNo: {type: string, maxLength: 64}
|
||||
measuredAt: {type: string, format: date-time}
|
||||
image: {type: string, format: binary}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,按响应体 code 判断}
|
||||
'403': {description: 无测量编辑权限}
|
||||
/api/platform/v1/first-leg/inbounds/{inboundId}/weight-pallet-options:
|
||||
get:
|
||||
summary: 查询称重可选托盘快照
|
||||
description: >-
|
||||
需要入库测量查看或称重编辑权限。Firstleg 按入库单 warehouseId 查询 Warehouse 公共托盘快照;
|
||||
仅限定同仓且未删除,不校验托盘状态或占用资格,Admin 不需要 platform:pallet:list。
|
||||
parameters:
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {type: array, items: {$ref: '#/components/schemas/WeightPalletSnapshot'}}
|
||||
'403': {description: 无入库测量查看或称重编辑权限}
|
||||
/api/platform/v1/first-leg/inbounds/{inboundId}/weight-results:
|
||||
post:
|
||||
summary: 登记称重图片 OCR 毛重结果
|
||||
description: >-
|
||||
需要称重编辑权限。请求必须绑定 palletId;Firstleg 按入库单 warehouseId 读取 Warehouse
|
||||
权威快照并固化托盘编码、类型、容量和皮重,不信任前端托盘快照。
|
||||
parameters:
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CreateWeightResultRequest'}
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/InboundWeightResult'}
|
||||
'403': {description: 无称重编辑权限}
|
||||
/api/platform/v1/first-leg/inbounds/{inboundId}/weight-results/{resultId}/confirm:
|
||||
post:
|
||||
summary: 确认 OCR 重量
|
||||
description: >-
|
||||
需要 platform:firstleg:inbound:weight:confirm 权限;携带人工修改字段时还需要
|
||||
platform:firstleg:inbound:weight:edit 权限。修改与确认在同一事务内完成,保留 OCR 原值,
|
||||
分别写入 CORRECT 和 CONFIRM 修订记录,并重算所有已确认图片的实际总毛重。
|
||||
分别写入 CORRECT 和 CONFIRM 修订记录。输入表示含托盘毛重;服务端统一换算为 KG,
|
||||
使用创建时固化的托盘皮重计算 netWeightKg,且毛重必须大于皮重。
|
||||
入库单 actualGrossWeightKg 为实测毛重;actualNetWeightKg 为扣除托盘皮重后的货物净重,两者不得混用。
|
||||
parameters:
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
@@ -538,14 +763,48 @@ paths:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ConfirmWeightResultRequest'}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,按响应体 code 判断}
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/InboundWeightResult'}
|
||||
'403': {description: 无确认权限,或修改重量时无编辑权限}
|
||||
/api/platform/v1/first-leg/inbounds/{inboundId}/weight-results/{resultId}:
|
||||
put:
|
||||
summary: 人工修正 OCR 毛重
|
||||
description: >-
|
||||
需要称重编辑权限。修正值仍表示含托盘毛重;保留 OCR 原值,已确认结果修正后立即移出净重汇总并回到待确认。
|
||||
parameters:
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: resultId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CorrectWeightResultRequest'}
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或业务失败,按响应体 code 判断
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code: {type: string}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/InboundWeightResult'}
|
||||
'403': {description: 无称重编辑权限}
|
||||
delete:
|
||||
summary: 删除称重图片及重量结果
|
||||
description: >-
|
||||
需要 platform:firstleg:inbound:weight:edit 权限;仅允许在可编辑的测量状态删除。
|
||||
删除已确认图片后重新汇总剩余已确认图片的实际总毛重,删除最后一张时总毛重恢复为空。
|
||||
删除已确认图片后由生产者分别重算剩余确认称重的毛重、净重;消费端不得用净重填充毛重。
|
||||
parameters:
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: version, in: query, required: true, schema: {type: integer, format: int32, minimum: 0}}
|
||||
@@ -557,7 +816,7 @@ paths:
|
||||
/api/platform/v1/inbounds/{inboundId}/measurements/approve:
|
||||
post:
|
||||
summary: 审核通过测量
|
||||
description: 需要 firstleg:inbound:measurement-review 权限。审核事务提交后服务端自动执行计费、费用发布和入库编排并返回费用快照;若后续编排失败,审核结果保持已提交,使用同一 Idempotency-Key 重试将从持久化阶段继续,接口不会伪造 WAIT_SPLIT。
|
||||
description: 需要 firstleg:inbound:measurement-review 权限。审核通过后立即进入 WAIT_SPLIT,并按该次测量数据尽力生成 CALCULATED 初算费用;价格未覆盖或初算异常只返回非阻断提示,不影响审核结果和待分柜状态。
|
||||
parameters:
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: tenantId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
@@ -574,16 +833,7 @@ paths:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
reason: {type: string, maxLength: 1000}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败,按响应体 code 判断;成功 data 为 FeeSnapshotResponse 且 nextStatus=WAIT_SPLIT;失败可能表示审核已提交但自动编排尚未完成、版本冲突或费用/仓库门禁失败,不得据此推断 WAIT_SPLIT}
|
||||
/api/platform/v1/inbounds/{inboundId}/measurements/reject:
|
||||
post:
|
||||
summary: 驳回测量
|
||||
description: 需要 firstleg:inbound:measurement-review 权限;只退回测量数据修改,已确认托盘和库位占用保持不动。
|
||||
parameters:
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: Idempotency-Key, in: header, required: true, schema: {type: string}}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败}
|
||||
'200': {description: 成功或业务失败,按响应体 code 判断;审核成功 data.processStatus 固定为 WAIT_SPLIT,initialPricingSucceeded 表示是否同时生成初算费用,初算失败时 feeStatus=UNPRICED 且主流程仍成功}
|
||||
/api/platform/v1/inbounds/{inboundId}/measurements/submit:
|
||||
post:
|
||||
summary: 提交测量完成
|
||||
@@ -607,15 +857,26 @@ paths:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/MerchantInboundBillingEnvelope'}
|
||||
'403': {description: 非当前客户或无订单查看权限}
|
||||
/api/platform/v1/inbounds/{inboundId}/measurements/exception-release:
|
||||
/api/platform/v1/loading-plans/{loadingPlanId}/fee-details/{feeId}/adjust:
|
||||
post:
|
||||
summary: 测量异常放行
|
||||
description: 需要 firstleg:inbound:exception-release 权限
|
||||
summary: 调整已审核柜次费用的日期和金额
|
||||
description: >-
|
||||
仅 ACTIVE、APPROVED 且 sourceVersion 匹配的费用可调整。成功后 sourceVersion 加一并重新进入
|
||||
PENDING;费用方向、费用项目、供应商、币种和备注保持原快照。业务字段需要变化时,
|
||||
必须作废原费用并新增。同一 expectedSourceVersion、businessDate、amount 的原样重试只读返回已生成的
|
||||
PENDING 版本;旧版本异参、已作废或非已审核状态返回 A0409 且零写入。
|
||||
parameters:
|
||||
- {name: inboundId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: Idempotency-Key, in: header, required: true, schema: {type: string}}
|
||||
- {name: accountId, in: query, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: loadingPlanId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
- {name: feeId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ContainerFeeAdjustRequest'}
|
||||
responses:
|
||||
'200': {description: 成功或业务失败}
|
||||
'200': {description: 成功时 data 为调整后柜次费用;业务冲突通过响应体 A0409 返回}
|
||||
'403': {description: 无 firstleg:inbound:fee-detail:edit 权限}
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
@@ -807,6 +1068,7 @@ components:
|
||||
notificationStatus: {type: string, nullable: true}
|
||||
version: {type: integer, nullable: true}
|
||||
expectedArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
requestedPickupAt: {type: string, format: date-time, nullable: true, description: Customer-requested pickup appointment from fl_inbound_pickup_snapshot.pickup_at}
|
||||
customerId: {type: integer, format: int64, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
companyId: {type: integer, format: int64, nullable: true}
|
||||
@@ -830,8 +1092,8 @@ components:
|
||||
currentHoldReason: {type: string, nullable: true}
|
||||
totalPackageCount: {type: integer, nullable: true}
|
||||
locationCodes: {type: string, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true}
|
||||
actualNetWeightKg: {type: number, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true, multipleOf: 0.001, description: 服务端确认后的实测毛重 KG;只读取 fl_inbound_order.actual_gross_weight_kg,不以净重回退}
|
||||
actualNetWeightKg: {type: number, nullable: true, multipleOf: 0.001, description: 服务端确认后的货物净重 KG;只读取 fl_inbound_order.actual_net_weight_kg,不以毛重回退}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
volumeWeightKg: {type: number, nullable: true}
|
||||
chargeableWeightKg: {type: number, nullable: true}
|
||||
@@ -841,14 +1103,18 @@ components:
|
||||
inboundedAt: {type: string, format: date-time, nullable: true}
|
||||
expressCompanyCode: {type: string, nullable: true}
|
||||
expressCompanyName: {type: string, nullable: true}
|
||||
loadingStatus: {type: string, nullable: true}
|
||||
containerNo: {type: string, nullable: true}
|
||||
cabinetNo: {type: string, nullable: true}
|
||||
vesselName: {type: string, nullable: true}
|
||||
voyageNo: {type: string, nullable: true}
|
||||
sailingAt: {type: string, format: date-time, nullable: true}
|
||||
portArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
estimatedSailingAt: {type: string, format: date-time, nullable: true, description: 当前关联柜次预计开船时间}
|
||||
actualSailingAt: {type: string, format: date-time, nullable: true, description: 当前关联柜次实际开船时间}
|
||||
estimatedPortArrivalAt: {type: string, format: date-time, nullable: true, description: 当前关联柜次预计到港时间}
|
||||
actualPortArrivalAt: {type: string, format: date-time, nullable: true, description: 当前关联柜次实际到港时间}
|
||||
expressTrackingNo: {type: string, nullable: true}
|
||||
pickupMethod: {type: string, enum: [SELF_EXPRESS, PICKUP, SELF_TRUCK], nullable: true}
|
||||
requestedPickupAt: {type: string, format: date-time, nullable: true, description: 客户要求平台上门提货的预约时间}
|
||||
createdByName: {type: string, nullable: true}
|
||||
createdAt: {type: string, format: date-time, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
UnifiedPreloadSummary:
|
||||
@@ -965,10 +1231,190 @@ components:
|
||||
properties:
|
||||
configId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
createdByThisInitialization: {type: boolean}
|
||||
WeightPalletSnapshot:
|
||||
type: object
|
||||
required: [palletId, palletCode, palletType, capacityCbm, tareWeightKg]
|
||||
properties:
|
||||
palletId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
palletCode: {type: string}
|
||||
palletType: {type: string}
|
||||
capacityCbm: {type: number, format: double, minimum: 0.0001}
|
||||
tareWeightKg: {type: number, format: double, minimum: 0, multipleOf: 0.001}
|
||||
CreateWeightResultRequest:
|
||||
type: object
|
||||
required: [fileId, palletId]
|
||||
properties:
|
||||
fileId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
palletId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
ocrProvider: {type: string, maxLength: 64, nullable: true}
|
||||
ocrRawText: {type: string, maxLength: 1000, nullable: true}
|
||||
ocrWeightValue: {type: number, format: double, minimum: 0, nullable: true, description: OCR 原始含托盘毛重}
|
||||
ocrWeightUnit: {type: string, maxLength: 8, nullable: true}
|
||||
confidence: {type: number, format: double, minimum: 0, maximum: 1, nullable: true}
|
||||
CorrectWeightResultRequest:
|
||||
type: object
|
||||
required: [version, adjustedWeightValue, adjustedWeightUnit, adjustmentReason]
|
||||
properties:
|
||||
version: {type: integer, minimum: 0}
|
||||
adjustedWeightValue: {type: number, format: double, minimum: 0.001, description: 人工修正后的含托盘毛重}
|
||||
adjustedWeightUnit: {type: string, enum: [KG, LB]}
|
||||
adjustmentReason: {type: string, minLength: 1, maxLength: 500}
|
||||
InboundWeightResult:
|
||||
type: object
|
||||
required: [id, fileId, palletId, palletCode, palletType, palletCapacityCbm, palletTareWeightKg, recognitionStatus, version]
|
||||
properties:
|
||||
id: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
fileId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
palletId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
palletCode: {type: string}
|
||||
palletType: {type: string}
|
||||
palletCapacityCbm: {type: number, format: double, minimum: 0.0001}
|
||||
palletTareWeightKg: {type: number, format: double, minimum: 0, multipleOf: 0.001}
|
||||
ocrProvider: {type: string, nullable: true}
|
||||
ocrRawText: {type: string, nullable: true}
|
||||
ocrWeightValue: {type: number, format: double, nullable: true, description: OCR 原始含托盘毛重}
|
||||
ocrWeightUnit: {type: string, nullable: true}
|
||||
confidence: {type: number, format: double, nullable: true}
|
||||
grossWeightKg: {type: number, format: double, nullable: true, multipleOf: 0.001, description: 确认时标准化的含托盘毛重 KG}
|
||||
adjustedWeightValue: {type: number, format: double, nullable: true, description: 人工修正后的含托盘毛重}
|
||||
adjustedWeightUnit: {type: string, nullable: true}
|
||||
netWeightKg: {type: number, format: double, nullable: true, multipleOf: 0.001, description: grossWeightKg - palletTareWeightKg}
|
||||
recognitionStatus: {type: string, enum: [PENDING, PENDING_CONFIRM, NEEDS_MANUAL, CONFIRMED]}
|
||||
adjustmentReason: {type: string, nullable: true}
|
||||
adjustedBy: {type: string, nullable: true}
|
||||
adjustedAt: {type: string, format: date-time, nullable: true}
|
||||
confirmedBy: {type: string, nullable: true}
|
||||
confirmedAt: {type: string, format: date-time, nullable: true}
|
||||
version: {type: integer, minimum: 0}
|
||||
InboundMeasurementItem:
|
||||
type: object
|
||||
description: >-
|
||||
单条尺寸测量明细。id 是稳定 measurementItemId;intelligentImageFileId 仅表示该明细的
|
||||
尺寸测量原图,不得与 weightResults.fileId 混用。
|
||||
required: [id]
|
||||
properties:
|
||||
id: {type: string, pattern: '^[1-9][0-9]*$', description: 稳定 measurementItemId}
|
||||
inboundDetailId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
itemNo: {type: string, nullable: true}
|
||||
boxSpecification: {type: string, nullable: true}
|
||||
packageCount: {type: integer, nullable: true}
|
||||
lengthCm: {type: number, format: double, nullable: true}
|
||||
widthCm: {type: number, format: double, nullable: true}
|
||||
heightCm: {type: number, format: double, nullable: true}
|
||||
girthCm: {type: number, format: double, nullable: true}
|
||||
unitVolumeCbm: {type: number, format: double, nullable: true}
|
||||
totalVolumeCbm: {type: number, format: double, nullable: true}
|
||||
volumeWeightKg: {type: number, format: double, nullable: true}
|
||||
intelligentImageFileId:
|
||||
type: string
|
||||
pattern: '^[1-9][0-9]*$'
|
||||
nullable: true
|
||||
description: 当前尺寸测量明细的 File 文件 ID;图片类型固定为 DIMENSION
|
||||
overLimitWarning: {type: string, nullable: true}
|
||||
outerLengthCm: {type: number, format: double, nullable: true}
|
||||
outerWidthCm: {type: number, format: double, nullable: true}
|
||||
outerHeightCm: {type: number, format: double, nullable: true}
|
||||
remark: {type: string, nullable: true}
|
||||
boxCode: {type: string, nullable: true}
|
||||
scannedShipmentId: {type: string, nullable: true}
|
||||
confirmedPackageCount: {type: integer, nullable: true}
|
||||
machineNo: {type: string, nullable: true}
|
||||
deviceMeasuredAt: {type: string, format: date-time, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
InboundMeasurementDetail:
|
||||
type: object
|
||||
description: >-
|
||||
入库测量权威详情。尺寸证据天然归属 items,称重证据天然归属 weightResults,响应不提供统一 images。
|
||||
required: [inboundId, inboundVersion, items, weightResults]
|
||||
properties:
|
||||
inboundId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
inboundCode: {type: string, nullable: true}
|
||||
companyName: {type: string, nullable: true}
|
||||
channelCode: {type: string, nullable: true}
|
||||
channelName: {type: string, nullable: true}
|
||||
processStatus: {type: string, nullable: true}
|
||||
measureReviewStatus: {type: string, nullable: true}
|
||||
recommendedPalletType: {type: string, nullable: true}
|
||||
reviewReason: {type: string, nullable: true}
|
||||
reviewerId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
reviewerName: {type: string, nullable: true}
|
||||
measurementId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
sourceType: {type: string, nullable: true}
|
||||
measurementStatus: {type: string, nullable: true}
|
||||
inboundVersion: {type: integer, minimum: 0}
|
||||
version: {type: integer, minimum: 0, nullable: true}
|
||||
originalDocumentNo: {type: string, nullable: true, description: 客户订单 referenceId 的服务端快照;旧空快照按订单事实回退}
|
||||
originalGrossWeightKg: {type: number, format: double, nullable: true, description: 客户订单有效货物行 grossWeightKg 合计的服务端快照}
|
||||
originalVolumeCbm: {type: number, format: double, nullable: true, description: 客户订单有效货物行 volumeCbm 合计的服务端快照}
|
||||
originalPackageCount: {type: integer, nullable: true}
|
||||
actualPackageCount: {type: integer, nullable: true}
|
||||
actualGrossWeightKg:
|
||||
type: number
|
||||
format: double
|
||||
nullable: true
|
||||
multipleOf: 0.001
|
||||
description: 全部 CONFIRMED 称重结果 grossWeightKg 之和,含托盘毛重 KG;无确认结果时为空
|
||||
actualNetWeightKg:
|
||||
type: number
|
||||
format: double
|
||||
nullable: true
|
||||
multipleOf: 0.001
|
||||
description: 全部 CONFIRMED 称重结果 netWeightKg 之和,扣除托盘皮重后的货物净重 KG;无确认结果时为空,审核与计费以此为准
|
||||
actualVolumeCbm: {type: number, format: double, nullable: true}
|
||||
volumeWeightKg: {type: number, format: double, nullable: true}
|
||||
actualGoodsName: {type: string, nullable: true, description: 默认由客户货物品名去重汇总,测量阶段允许人工修正}
|
||||
items:
|
||||
type: array
|
||||
description: DIMENSION 尺寸测量明细及其图片证据
|
||||
items: {$ref: '#/components/schemas/InboundMeasurementItem'}
|
||||
weightResults:
|
||||
type: array
|
||||
description: WEIGHT 称重结果及其图片、确认状态和服务端重量事实
|
||||
items: {$ref: '#/components/schemas/InboundWeightResult'}
|
||||
InboundMeasurementSaveDraftRequest:
|
||||
type: object
|
||||
required: [version, items]
|
||||
additionalProperties: false
|
||||
properties:
|
||||
measurementId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
version: {type: integer, minimum: 0}
|
||||
actualGoodsName: {type: string, maxLength: 255, nullable: true}
|
||||
items:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 500
|
||||
items: {$ref: '#/components/schemas/InboundMeasurementItemRequest'}
|
||||
InboundMeasurementItemRequest:
|
||||
type: object
|
||||
required: [inboundDetailId, itemNo, boxSpecification, packageCount, lengthCm, widthCm, heightCm]
|
||||
additionalProperties: false
|
||||
properties:
|
||||
inboundDetailId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
itemNo: {type: string, minLength: 1, maxLength: 32}
|
||||
boxSpecification: {type: string, minLength: 1, maxLength: 64}
|
||||
packageCount: {type: integer, minimum: 1, maximum: 1000000}
|
||||
lengthCm: {type: number, minimum: 0.001}
|
||||
widthCm: {type: number, minimum: 0.001}
|
||||
heightCm: {type: number, minimum: 0.001}
|
||||
intelligentImageFileId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
overLimitWarning: {type: string, maxLength: 500, nullable: true}
|
||||
outerLengthCm: {type: number, minimum: 0, nullable: true}
|
||||
outerWidthCm: {type: number, minimum: 0, nullable: true}
|
||||
outerHeightCm: {type: number, minimum: 0, nullable: true}
|
||||
remark: {type: string, maxLength: 500, nullable: true}
|
||||
InboundMeasurementDetailEnvelope:
|
||||
type: object
|
||||
required: [code, message]
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
data: {$ref: '#/components/schemas/InboundMeasurementDetail'}
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
ConfirmWeightResultRequest:
|
||||
type: object
|
||||
required: [version]
|
||||
description: 人工修改字段必须同时提供;全部省略时直接确认现有 OCR 或已修正重量。
|
||||
description: 人工修改字段表示含托盘毛重且必须同时提供;全部省略时直接确认现有 OCR 或已修正毛重。
|
||||
properties:
|
||||
version: {type: integer, minimum: 0}
|
||||
adjustedWeightValue: {type: number, format: double, minimum: 0.001, nullable: true}
|
||||
@@ -981,7 +1427,7 @@ components:
|
||||
tenantId: {type: string, pattern: '^[1-9][0-9]*$', description: 当前客户隔离 ID}
|
||||
orderId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
orderNo: {type: string, nullable: true}
|
||||
inboundCode: {type: string, minLength: 1, maxLength: 40, example: RK-2085308606829346818}
|
||||
inboundCode: {type: string, minLength: 1, maxLength: 40, example: ZBYHK2608140001}
|
||||
customerId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
customerName: {type: string, minLength: 1}
|
||||
warehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
@@ -1020,13 +1466,17 @@ components:
|
||||
pages: {type: string, pattern: '^[0-9]+$'}
|
||||
ContainerBooking:
|
||||
type: object
|
||||
required: [id, bookingNo, cabinetNo, warehouseId, warehouseName, containerType, planStatus, version]
|
||||
required: [id, bookingNo, cabinetNo, sourceType, recordStatus, warehouseId, warehouseName, containerType, capacityCbm, maxWeightKg, planStatus, exceptionActive, destinationClearanceDocumentStatus, customsDeclarationDocumentStatus, deliveryOrderDocumentStatus, pickupOrderDocumentStatus, version]
|
||||
properties:
|
||||
id: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
planId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: 关联智能装柜计划ID;未创建计划时为空}
|
||||
bookingNo: {type: string, maxLength: 40}
|
||||
planNo: {type: string, nullable: true, pattern: '^LP-[0-9]{6}-[0-9]{4}$', example: LP-260818-0001, description: 预装与确认装柜清单共用的稳定编号;未创建计划时为空}
|
||||
bookingNo: {type: string, pattern: '^CB-[0-9]{6}-[0-9]{4}$', example: CB-260818-0001, description: 平台内部货柜登记号}
|
||||
containerNo: {type: string, maxLength: 64, nullable: true}
|
||||
cabinetNo: {type: string, maxLength: 64}
|
||||
cabinetNo: {type: string, pattern: '^CAB-[0-9]{6}-[0-9]{4}$', example: CAB-260818-0001, description: 平台稳定柜次号}
|
||||
sourceType: {type: string, enum: [SIMULATION, OCEAN_BOOKING], description: SIMULATION 为内部模拟柜;OCEAN_BOOKING 为订舱登记直接生成的真实柜}
|
||||
recordStatus: {type: string, enum: [ACTIVE, MERGED], description: 正常列表和详情仅返回 ACTIVE;真实订舱柜被模拟柜消费后变为 MERGED 且不再作为可操作柜返回}
|
||||
mergedIntoContainerBookingId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: MERGED 源真实柜指向保留的模拟柜稳定 containerBookingId;ACTIVE 时为空}
|
||||
channelCode: {type: string, maxLength: 64, nullable: true, description: Missing legacy snapshots are blocked from loading until explicitly backfilled}
|
||||
channelName: {type: string, maxLength: 128, nullable: true}
|
||||
oceanBookingId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
@@ -1036,7 +1486,7 @@ components:
|
||||
billOfLadingNo: {type: string, maxLength: 128, nullable: true}
|
||||
vesselName: {type: string, maxLength: 128, nullable: true}
|
||||
voyageNo: {type: string, maxLength: 64, nullable: true}
|
||||
containerType: {type: string, maxLength: 32}
|
||||
containerType: {type: string, enum: [20GP, 40GP, 40HC, 40NR, 45HC], description: 平台固定柜型}
|
||||
shippingCompanyId: {type: string, nullable: true}
|
||||
shippingCompanyName: {type: string, maxLength: 128, nullable: true}
|
||||
departurePortCode: {type: string, maxLength: 32, nullable: true}
|
||||
@@ -1045,9 +1495,10 @@ components:
|
||||
destinationPortName: {type: string, maxLength: 128, nullable: true}
|
||||
destinationWarehouseCode: {type: string, maxLength: 64, nullable: true}
|
||||
destinationWarehouseName: {type: string, maxLength: 128, nullable: true}
|
||||
capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true}
|
||||
maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true}
|
||||
terminalTruckingCompany: {type: string, maxLength: 128, nullable: true}
|
||||
capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, description: 必须等于柜型固定容量}
|
||||
maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, description: 必须等于柜型固定限重}
|
||||
terminalTruckingCompanyId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: ACTIVE Warehouse Supplier ID validated as TRUCKING}
|
||||
terminalTruckingCompany: {type: string, maxLength: 128, nullable: true, description: Server-frozen Warehouse supplier name snapshot}
|
||||
loadingAt: {type: string, format: date-time, nullable: true}
|
||||
estimatedSailingAt: {type: string, format: date-time, nullable: true}
|
||||
actualSailingAt: {type: string, format: date-time, nullable: true}
|
||||
@@ -1056,19 +1507,27 @@ components:
|
||||
estimatedWarehouseArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
actualWarehouseArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
planStatus: {type: string, enum: [WAIT_LOADING, EDITING, LOADING, COMPLETED]}
|
||||
exceptionActive: {type: boolean, description: 最新异常历史动作是否为 MARK}
|
||||
destinationClearanceDocumentStatus: {$ref: '#/components/schemas/ContainerDocumentStatus'}
|
||||
customsDeclarationDocumentStatus: {$ref: '#/components/schemas/ContainerDocumentStatus'}
|
||||
deliveryOrderDocumentStatus: {$ref: '#/components/schemas/ContainerDocumentStatus'}
|
||||
pickupOrderDocumentStatus: {$ref: '#/components/schemas/ContainerDocumentStatus'}
|
||||
version: {type: integer, minimum: 0}
|
||||
ContainerDocumentStatus:
|
||||
type: string
|
||||
enum: [MISSING, UPLOADED, COMPLETED]
|
||||
description: 单证聚合状态;DO/PU 无完成动作,因此只返回 MISSING 或 UPLOADED
|
||||
ContainerSaveRequest:
|
||||
type: object
|
||||
required: [cabinetNo, warehouseId, warehouseName, containerType]
|
||||
required: [warehouseId, warehouseName, containerType, capacityCbm, maxWeightKg]
|
||||
properties:
|
||||
containerNo: {type: string, maxLength: 64, nullable: true}
|
||||
cabinetNo: {type: string, maxLength: 64}
|
||||
warehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
warehouseName: {type: string, maxLength: 128}
|
||||
billOfLadingNo: {type: string, maxLength: 128, nullable: true}
|
||||
vesselName: {type: string, maxLength: 128, nullable: true}
|
||||
voyageNo: {type: string, maxLength: 64, nullable: true}
|
||||
containerType: {type: string, maxLength: 32}
|
||||
containerType: {type: string, enum: [20GP, 40GP, 40HC, 40NR, 45HC], description: 平台固定柜型}
|
||||
shippingCompanyId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
|
||||
shippingCompanyName: {type: string, maxLength: 128, nullable: true}
|
||||
departurePortCode: {type: string, maxLength: 32, nullable: true}
|
||||
@@ -1077,9 +1536,10 @@ components:
|
||||
destinationPortName: {type: string, maxLength: 128, nullable: true}
|
||||
destinationWarehouseCode: {type: string, maxLength: 64, nullable: true}
|
||||
destinationWarehouseName: {type: string, maxLength: 128, nullable: true}
|
||||
capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true}
|
||||
maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true}
|
||||
terminalTruckingCompany: {type: string, maxLength: 128, nullable: true}
|
||||
capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0, description: 必须等于柜型固定容量}
|
||||
maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, description: 必须等于柜型固定限重}
|
||||
terminalTruckingCompanyId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true, description: ACTIVE Warehouse Supplier ID validated as TRUCKING}
|
||||
terminalTruckingCompany: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint; persisted name always comes from Warehouse and cannot replace the ID}
|
||||
loadingAt: {type: string, format: date-time, nullable: true}
|
||||
estimatedSailingAt: {type: string, format: date-time, nullable: true}
|
||||
actualSailingAt: {type: string, format: date-time, nullable: true}
|
||||
@@ -1101,6 +1561,83 @@ components:
|
||||
voyageNo: {type: string, maxLength: 64, nullable: true}
|
||||
version: {type: integer, minimum: 0}
|
||||
reason: {type: string, maxLength: 500}
|
||||
SmartLoadingPushRequest:
|
||||
type: object
|
||||
required: [expectedVersion]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
SmartLoadingAssignRequest:
|
||||
type: object
|
||||
required: [inboundId, inboundDetailId, packageCount, expectedVersion, operationId]
|
||||
properties:
|
||||
inboundId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
inboundDetailId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
packageCount: {type: integer, minimum: 1}
|
||||
expectedVersion: {type: integer, minimum: 0, description: 当前 planVersion}
|
||||
operationId: {type: string, minLength: 1, maxLength: 128}
|
||||
SmartLoadingAdjustRequest:
|
||||
type: object
|
||||
required: [packageCount, expectedPlanVersion, expectedItemVersion, operationId]
|
||||
properties:
|
||||
packageCount: {type: integer, minimum: 1, description: 不得小于响应中的 minPackageCount}
|
||||
expectedPlanVersion: {type: integer, minimum: 0}
|
||||
expectedItemVersion: {type: integer, minimum: 0}
|
||||
operationId: {type: string, minLength: 1, maxLength: 128}
|
||||
SmartLoadingDeleteRequest:
|
||||
type: object
|
||||
required: [expectedPlanVersion, expectedItemVersion, operationId]
|
||||
properties:
|
||||
expectedPlanVersion: {type: integer, minimum: 0}
|
||||
expectedItemVersion: {type: integer, minimum: 0}
|
||||
operationId: {type: string, minLength: 1, maxLength: 128}
|
||||
SmartLoadingWarehouseRemarkRequest:
|
||||
type: object
|
||||
required: [expectedPlanVersion, expectedItemVersion, operationId]
|
||||
properties:
|
||||
warehouseRemark: {type: string, nullable: true, maxLength: 1000, description: 仓库备注;null/空字符串均表示清空}
|
||||
expectedPlanVersion: {type: integer, minimum: 0}
|
||||
expectedItemVersion: {type: integer, minimum: 0}
|
||||
operationId: {type: string, minLength: 1, maxLength: 128}
|
||||
SmartLoadingPlan:
|
||||
type: object
|
||||
required: [id, planNo, status, bookingId, version, canAssign, canEdit, finalCompletionStatus, items]
|
||||
properties:
|
||||
id: {type: string}
|
||||
planNo: {type: string, pattern: '^LP-[0-9]{6}-[0-9]{4}$', example: LP-260818-0001, description: 预装与确认装柜清单共用的稳定编号}
|
||||
status: {type: string, enum: [EDITING, LOADING, COMPLETED]}
|
||||
bookingId: {type: string}
|
||||
containerNo: {type: string, nullable: true}
|
||||
voyageNo: {type: string, nullable: true}
|
||||
version: {type: integer, minimum: 0, description: planVersion,写请求使用此值}
|
||||
canAssign: {type: boolean}
|
||||
canEdit: {type: boolean}
|
||||
blockedReason: {type: string, nullable: true}
|
||||
finalCompletionStatus: {type: string, enum: [NOT_STARTED, IN_PROGRESS, COMPLETED], description: IN_PROGRESS 表示 final complete 门禁尚未通过;增量新增 PENDING item 会保持或恢复此值,COMPLETED 后 canAssign/canEdit 均为 false}
|
||||
pdaTaskId: {type: string, nullable: true}
|
||||
pdaTaskStatus: {type: string, nullable: true, enum: [LOADING, COMPLETED], description: LOADING task 在增量后保持原 taskId;COMPLETED 永久拒绝增量}
|
||||
pdaTaskVersion: {type: integer, minimum: 0, nullable: true, description: 服务端同步后最新 taskVersion;Admin 写操作不提交该值}
|
||||
items:
|
||||
type: array
|
||||
items: {$ref: '#/components/schemas/SmartLoadingPlanItem'}
|
||||
SmartLoadingPlanItem:
|
||||
type: object
|
||||
required: [id, packageCount, version, pickedPackageCount, minPackageCount, canAdjust, canRemove]
|
||||
properties:
|
||||
id: {type: string}
|
||||
inboundId: {type: string}
|
||||
inboundDetailId: {type: string}
|
||||
orderNo: {type: string, nullable: true}
|
||||
inboundCode: {type: string, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
customerRemark: {type: string, nullable: true, readOnly: true, description: 来源为订单 customerRemark}
|
||||
internalRemark: {type: string, nullable: true, readOnly: true, description: 来源为订单最新内部备注,不使用 cargoRemark}
|
||||
warehouseRemark: {type: string, nullable: true, description: 仓库人员逐条维护,可在 EDITING/LOADING 修改}
|
||||
packageCount: {type: integer, minimum: 1}
|
||||
version: {type: integer, minimum: 0, description: itemVersion,调整/删除写请求使用此值}
|
||||
pickedPackageCount: {type: integer, minimum: 0}
|
||||
minPackageCount: {type: integer, minimum: 1}
|
||||
canAdjust: {type: boolean}
|
||||
canRemove: {type: boolean}
|
||||
ContainerDocumentAttachRequest:
|
||||
type: object
|
||||
required: [fileId, expectedVersion]
|
||||
@@ -1178,7 +1715,6 @@ components:
|
||||
publicationStatus: {type: string, enum: [PUBLISHED]}
|
||||
publishedAt: {type: string, format: date-time}
|
||||
inboundedAt: {type: string, format: date-time, nullable: true}
|
||||
settlementIncluded: {type: boolean, example: true}
|
||||
InboundOrderPageEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
@@ -1209,6 +1745,37 @@ components:
|
||||
companyId: {type: string, nullable: true}
|
||||
taxMode: {type: string, nullable: true, description: 订单报关方式快照}
|
||||
pricingTaxMode: {type: string, enum: [TAX_INCLUDED, TAX_EXCLUDED], nullable: true, description: 下单时选择的报价包税维度快照}
|
||||
expectedArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
companyName: {type: string, nullable: true}
|
||||
warehouseId: {type: string, nullable: true}
|
||||
warehouseName: {type: string, nullable: true}
|
||||
deliveryWarehouseId: {type: string, nullable: true}
|
||||
deliveryWarehouseName: {type: string, nullable: true}
|
||||
businessType: {type: string, nullable: true}
|
||||
channelCode: {type: string, nullable: true}
|
||||
channelName: {type: string, nullable: true}
|
||||
containerMode: {type: string, nullable: true}
|
||||
customsInspection: {type: boolean, nullable: true}
|
||||
destinationType: {type: string, nullable: true}
|
||||
destinationWarehouseId: {type: string, nullable: true}
|
||||
destinationCode: {type: string, nullable: true}
|
||||
destinationName: {type: string, nullable: true}
|
||||
totalPackageCount: {type: integer, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true}
|
||||
actualNetWeightKg: {type: number, nullable: true}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
volumeWeightKg: {type: number, nullable: true}
|
||||
chargeableWeightKg: {type: number, nullable: true}
|
||||
chargeableVolumeCbm: {type: number, nullable: true}
|
||||
palletCount: {type: integer, nullable: true}
|
||||
locationCodes: {type: string, nullable: true}
|
||||
measureCompletedAt: {type: string, format: date-time, nullable: true}
|
||||
inboundedAt: {type: string, format: date-time, nullable: true}
|
||||
expressCompanyName: {type: string, nullable: true}
|
||||
expressTrackingNo: {type: string, nullable: true}
|
||||
createdAt: {type: string, format: date-time, nullable: true}
|
||||
updatedAt: {type: string, format: date-time, nullable: true}
|
||||
InboundMeasurementCallbackRequest:
|
||||
type: object
|
||||
required: [eventNo, externalDetailNo, warehouseCode, inboundCode, inboundDetailId, deviceCode, measuredAt, boxSpecification, packageCount, lengthCm, widthCm, heightCm]
|
||||
@@ -1216,7 +1783,7 @@ components:
|
||||
eventNo: {type: string, maxLength: 128, pattern: '^[A-Za-z0-9._:-]+$', example: M202608010001}
|
||||
externalDetailNo: {type: string, maxLength: 128, pattern: '^[A-Za-z0-9._:-]+$', example: BOX-20260801-0001}
|
||||
warehouseCode: {type: string, maxLength: 50, example: CN-YW-01}
|
||||
inboundCode: {type: string, maxLength: 40, example: RK202608010001}
|
||||
inboundCode: {type: string, maxLength: 40, example: ZBYHK2608140001}
|
||||
inboundDetailId: {type: string, example: '190000000000000001', description: 柏悦订单货物明细ID}
|
||||
deviceCode: {type: string, maxLength: 64, example: DEVICE-01}
|
||||
measuredAt: {type: string, format: date-time, example: '2026-08-01T10:30:00+08:00'}
|
||||
@@ -1225,7 +1792,6 @@ components:
|
||||
widthCm: {type: number, exclusiveMinimum: true, minimum: 0, example: 40.10}
|
||||
heightCm: {type: number, exclusiveMinimum: true, minimum: 0, example: 30.00}
|
||||
packageCount: {type: integer, minimum: 1, maximum: 1000000, example: 50}
|
||||
unitWeightKg: {type: number, exclusiveMinimum: true, minimum: 0, nullable: true, example: 20}
|
||||
remark: {type: string, maxLength: 500, nullable: true}
|
||||
InboundMeasurementEventResponse:
|
||||
type: object
|
||||
@@ -1246,3 +1812,70 @@ components:
|
||||
assigneeId: {type: integer, format: int64}
|
||||
assigneeName: {type: string, maxLength: 64}
|
||||
reason: {type: string, maxLength: 500}
|
||||
ContainerSimulationCreateRequest:
|
||||
type: object
|
||||
required: [channelCode, warehouseId, containerType, capacityCbm, maxWeightKg]
|
||||
properties:
|
||||
channelCode: {type: string, maxLength: 64, description: 当前 ACTIVE 价格簿中的真实物流渠道}
|
||||
warehouseId: {type: integer, format: int64, minimum: 1}
|
||||
containerType: {type: string, enum: [20GP, 40GP, 40HC, 40NR, 45HC], description: 与订舱共用的平台固定柜型}
|
||||
capacityCbm: {type: number, minimum: 0, exclusiveMinimum: true, description: 必须等于柜型固定容量}
|
||||
maxWeightKg: {type: number, minimum: 0, exclusiveMinimum: true, description: 必须等于柜型固定限重}
|
||||
ContainerOceanBookingLinkRequest:
|
||||
type: object
|
||||
required: [oceanBookingId, requirementId, containerNo, expectedVersion]
|
||||
properties:
|
||||
oceanBookingId: {type: integer, format: int64, minimum: 1, description: RELEASED订舱ID}
|
||||
requirementId: {type: integer, format: int64, minimum: 1, description: 未满配的订舱柜型需求ID}
|
||||
containerNo: {type: string, maxLength: 64, description: 港口提柜后取得的全局唯一真实集装箱号}
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
ContainerOceanBookingUnlinkRequest:
|
||||
type: object
|
||||
required: [expectedVersion, reason]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
reason: {type: string, minLength: 1, maxLength: 500}
|
||||
ContainerFeeAdjustRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [expectedSourceVersion, businessDate, amount]
|
||||
properties:
|
||||
expectedSourceVersion: {type: integer, minimum: 1}
|
||||
businessDate: {type: string, format: date}
|
||||
amount: {type: number, minimum: 0}
|
||||
FinanceFeeSourceSnapshot:
|
||||
type: object
|
||||
description: Finance 消费的版本化费用来源快照;supplierCategory 为空表示仅约束供应商 ACTIVE,非空才约束明确类别。
|
||||
required: [tenantId, sourceType, feeId, feeVersion, customerId, customerName, feeItemName, direction, businessDate, currency, totalAmount, reviewStatus, publicationStatus, sourceHash, snapshotVersion]
|
||||
properties:
|
||||
tenantId: {type: integer, format: int64}
|
||||
sourceType: {type: string, enum: [INBOUND_FEE, CONTAINER_FEE]}
|
||||
feeId: {type: integer, format: int64}
|
||||
feeVersion: {type: integer, minimum: 1}
|
||||
inboundId: {type: integer, format: int64, nullable: true}
|
||||
inboundCode: {type: string, nullable: true}
|
||||
orderId: {type: integer, format: int64, nullable: true}
|
||||
containerId: {type: integer, format: int64, nullable: true}
|
||||
containerBookingId: {type: integer, format: int64, nullable: true}
|
||||
containerNo: {type: string, nullable: true}
|
||||
customerId: {type: integer, format: int64}
|
||||
customerName: {type: string}
|
||||
supplierId: {type: integer, format: int64, nullable: true}
|
||||
supplierName: {type: string, nullable: true}
|
||||
supplierCategory: {type: string, enum: [BOOKING, CUSTOMS, TRUCKING, CERTIFICATE], nullable: true, description: PAYABLE供应商类别约束;为空时仅要求供应商ACTIVE}
|
||||
feeItemId: {type: integer, format: int64, nullable: true}
|
||||
feeItemCode: {type: string, nullable: true}
|
||||
feeItemName: {type: string}
|
||||
direction: {type: string, enum: [RECEIVABLE, PAYABLE]}
|
||||
businessDate: {type: string, format: date}
|
||||
currency: {type: string, enum: [CNY]}
|
||||
totalAmount: {type: number, minimum: 0}
|
||||
reviewStatus: {type: string, enum: [APPROVED]}
|
||||
publicationStatus: {type: string, enum: [PUBLISHED]}
|
||||
publishedAt: {type: string, format: date-time, nullable: true}
|
||||
chargeableQuantity: {type: number, nullable: true}
|
||||
chargeUnit: {type: string, nullable: true}
|
||||
actualGrossWeightKg: {type: number, nullable: true}
|
||||
actualVolumeCbm: {type: number, nullable: true}
|
||||
sourceHash: {type: string}
|
||||
snapshotVersion: {type: integer, minimum: 1}
|
||||
|
||||
@@ -7,16 +7,17 @@ paths:
|
||||
/api/platform/v1/first-leg/loading-plans:
|
||||
get:
|
||||
summary: 分页查询预装清单
|
||||
description: 默认返回全部仓库的装柜计划;可按仓库筛选,客户账号仅用于展示货物归属。
|
||||
description: 默认返回全部仓库的装柜计划;可按客户和仓库筛选。
|
||||
x-permission: platform:firstleg:preload:list
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/Page'}
|
||||
- {$ref: '#/components/parameters/Size'}
|
||||
- {$ref: '#/components/parameters/Keyword'}
|
||||
- {$ref: '#/components/parameters/CustomerId'}
|
||||
- name: status
|
||||
in: query
|
||||
description: 预装状态
|
||||
schema: {type: string, enum: [LOADING]}
|
||||
schema: {type: string, enum: [EDITING, LOADING]}
|
||||
- {$ref: '#/components/parameters/OptionalWarehouseId'}
|
||||
- {$ref: '#/components/parameters/CreatedFrom'}
|
||||
- {$ref: '#/components/parameters/CreatedTo'}
|
||||
@@ -33,7 +34,7 @@ paths:
|
||||
/api/platform/v1/first-leg/loading-plans/{planId}/transport:
|
||||
patch:
|
||||
summary: 修改预装运输信息
|
||||
description: 仅 LOADING 状态允许修改集装箱号与航次;装柜中可以维护未拣货明细。
|
||||
description: 已推送且仍为 EDITING 的预装清单可补录或修改集装箱号与航次;LOADING 阶段按稳定柜次身份规则维护,不能清空已创建 PDA 任务的柜号。
|
||||
x-permission: platform:firstleg:preload:edit
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/PlanId'}
|
||||
@@ -46,15 +47,27 @@ paths:
|
||||
schema: {$ref: '#/components/schemas/TransportUpdateRequest'}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/PreloadDetailResponse'}
|
||||
/api/platform/v1/first-leg/loading-plans/{planId}/confirm:
|
||||
post:
|
||||
summary: 原子确认整份预装计划
|
||||
description: 以 loadingPlanId 为唯一范围,原子确认全部已分配入库预装票据。要求计划已 push 且仍为 EDITING、真实集装箱号已录入、稳定 bridge 完整、真实订舱已关联并为 RELEASED;任一票不满足均不写入。成功后唯一创建待领取 PDA task,claim 后才推进 LOADING。同一 Idempotency-Key 与相同请求精确重放原响应,不同 Key 重复确认返回业务冲突。
|
||||
x-permission: platform:firstleg:preload:edit
|
||||
parameters: [{$ref: '#/components/parameters/PlanId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/ConfirmPreloadRequest'}}}
|
||||
responses:
|
||||
'200': {$ref: '#/components/responses/PreloadConfirmationResponse'}
|
||||
/api/platform/v1/first-leg/loading-lists:
|
||||
get:
|
||||
summary: 分页查询装柜清单
|
||||
description: 默认返回全部仓库的 COMPLETED 装柜计划;可按仓库筛选,包含全部客户单据。
|
||||
description: 默认返回全部仓库的 COMPLETED 装柜计划;可按客户和仓库筛选。
|
||||
x-permission: platform:firstleg:loading-list:list
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/Page'}
|
||||
- {$ref: '#/components/parameters/Size'}
|
||||
- {$ref: '#/components/parameters/Keyword'}
|
||||
- {$ref: '#/components/parameters/CustomerId'}
|
||||
- {$ref: '#/components/parameters/OptionalWarehouseId'}
|
||||
- {$ref: '#/components/parameters/CreatedFrom'}
|
||||
- {$ref: '#/components/parameters/CreatedTo'}
|
||||
@@ -138,6 +151,12 @@ components:
|
||||
in: query
|
||||
description: 按预装单号、集装箱号或柜号模糊查询
|
||||
schema: {type: string, maxLength: 128}
|
||||
CustomerId:
|
||||
name: customerId
|
||||
in: query
|
||||
required: false
|
||||
description: 仅返回包含该客户货物的清单
|
||||
schema: {$ref: '#/components/schemas/LongId'}
|
||||
WarehouseId:
|
||||
name: warehouseId
|
||||
in: query
|
||||
@@ -167,6 +186,9 @@ components:
|
||||
PreloadDetailResponse:
|
||||
description: 预装清单详情查询或操作成功,也可能返回业务失败
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/PreloadDetailEnvelope'}}}
|
||||
PreloadConfirmationResponse:
|
||||
description: 计划级预装确认成功、精确重放或业务失败
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/PreloadConfirmationEnvelope'}}}
|
||||
ConfirmedPageResponse:
|
||||
description: 装柜清单分页查询成功或业务失败,按响应体 code 判断
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/ConfirmedPageEnvelope'}}}
|
||||
@@ -210,6 +232,14 @@ components:
|
||||
data:
|
||||
allOf: [{$ref: '#/components/schemas/PreloadDetail'}]
|
||||
nullable: true
|
||||
PreloadConfirmationEnvelope:
|
||||
allOf:
|
||||
- {$ref: '#/components/schemas/ApiEnvelope'}
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
allOf: [{$ref: '#/components/schemas/PreloadConfirmation'}]
|
||||
nullable: true
|
||||
ConfirmedPageEnvelope:
|
||||
allOf:
|
||||
- {$ref: '#/components/schemas/ApiEnvelope'}
|
||||
@@ -260,37 +290,72 @@ components:
|
||||
customerName: {type: string, nullable: true, description: 客户名称}
|
||||
PreloadSummary:
|
||||
type: object
|
||||
required: [planId, containerBookingId, preloadNo, customers, status, skuCount, totalPackageCount, totalVolumeCbm, totalGrossWeightKg, createdAt, version]
|
||||
required: [planId, containerBookingId, preloadNo, customers, status, skuCount, totalPackageCount, totalVolumeCbm, totalGrossWeightKg, createdAt, version, readiness]
|
||||
properties:
|
||||
planId: {$ref: '#/components/schemas/LongId'}
|
||||
containerBookingId: {$ref: '#/components/schemas/LongId'}
|
||||
preloadNo: {type: string, description: 预装单号}
|
||||
preloadNo: {type: string, pattern: '^LP-[0-9]{6}-[0-9]{4}$', example: LP-260818-0001, description: 预装单号;按北京时间创建日逐日编号,确认后继续作为同一装柜清单号使用}
|
||||
containerNo: {type: string, nullable: true, description: 集装箱号}
|
||||
internalContainerNo: {type: string, nullable: true, description: 内部柜号}
|
||||
cabinetNo: {type: string, nullable: true, pattern: '^CAB-[0-9]{6}-[0-9]{4}$', example: CAB-260818-0001, description: 稳定柜次号;不得与真实集装箱号混用}
|
||||
customers: {type: array, description: 计划包含的客户, items: {$ref: '#/components/schemas/Customer'}}
|
||||
warehouseId: {$ref: '#/components/schemas/NullableLongId'}
|
||||
warehouseName: {type: string, nullable: true, description: 仓库名称}
|
||||
vesselName: {type: string, nullable: true, description: 船名}
|
||||
voyageNo: {type: string, nullable: true, description: 航次}
|
||||
containerType: {type: string, nullable: true, description: 货柜类型}
|
||||
status: {type: string, enum: [LOADING], description: 装柜状态}
|
||||
status: {type: string, enum: [EDITING, LOADING], description: 装柜计划状态}
|
||||
skuCount: {type: string, pattern: '^[0-9]+$', description: SKU 种类数}
|
||||
totalPackageCount: {type: string, pattern: '^[0-9]+$', description: 总箱数}
|
||||
totalVolumeCbm: {type: number, description: 总体积,单位立方米}
|
||||
totalGrossWeightKg: {type: number, description: 总毛重,单位千克}
|
||||
createdAt: {type: string, format: date-time, description: 创建时间}
|
||||
version: {type: integer, format: int32, minimum: 0, description: 乐观锁版本}
|
||||
readiness: {$ref: '#/components/schemas/PreloadReadiness'}
|
||||
PreloadReadiness:
|
||||
type: object
|
||||
required: [preloadPushed, containerNoReady, preloadLinkCount, confirmedPreloadCount, confirmationStatus, canConfirm, readyForPda, blockingReasons]
|
||||
properties:
|
||||
preloadPushed: {type: boolean, description: 是否已完成 push-preload}
|
||||
containerNoReady: {type: boolean, description: 是否已录入真实集装箱号 containerNo}
|
||||
preloadLinkCount: {type: integer, format: int32, minimum: 0, description: 计划关联的入库预装票据数}
|
||||
confirmedPreloadCount: {type: integer, format: int32, minimum: 0, description: 已确认预装票据数}
|
||||
oceanBookingId: {$ref: '#/components/schemas/NullableLongId'}
|
||||
oceanBookingStatus: {type: string, nullable: true, enum: [CONFIRMED, CHANGE_PENDING, RELEASED, CANCELLED], description: 真实订舱当前状态}
|
||||
pdaTaskId: {$ref: '#/components/schemas/NullableLongId'}
|
||||
pdaTaskStatus: {type: string, nullable: true, description: PDA task 当前状态}
|
||||
confirmationStatus: {type: string, enum: [PENDING, PARTIALLY_CONFIRMED, CONFIRMED], description: 整份计划的预装确认状态}
|
||||
canConfirm: {type: boolean, description: 当前是否可调用计划级确认接口}
|
||||
readyForPda: {type: boolean, description: PDA 创建前置条件是否全部满足;task 已创建后仍为 true}
|
||||
blockingReasons:
|
||||
type: array
|
||||
description: 阻止计划确认的稳定原因码
|
||||
items: {type: string, enum: [PRELOAD_NOT_PUSHED, LOADING_PLAN_NOT_EDITING, CONTAINER_NO_MISSING, PRELOAD_LINK_MISSING, OCEAN_BOOKING_NOT_LINKED, OCEAN_BOOKING_NOT_RELEASED, PDA_TASK_ALREADY_CREATED, PRELOAD_ALREADY_CONFIRMED]}
|
||||
ConfirmPreloadRequest:
|
||||
type: object
|
||||
required: [expectedVersion]
|
||||
additionalProperties: false
|
||||
properties:
|
||||
expectedVersion: {type: integer, format: int32, minimum: 0, description: 当前装柜计划 version}
|
||||
PreloadConfirmation:
|
||||
type: object
|
||||
required: [planId, containerBookingId, confirmationStatus, version, readiness]
|
||||
properties:
|
||||
planId: {$ref: '#/components/schemas/LongId'}
|
||||
containerBookingId: {$ref: '#/components/schemas/LongId'}
|
||||
confirmationStatus: {type: string, enum: [CONFIRMED]}
|
||||
version: {type: integer, format: int32, minimum: 0, description: 确认后的装柜计划 version}
|
||||
readiness: {$ref: '#/components/schemas/PreloadReadiness'}
|
||||
ConfirmedSummary:
|
||||
type: object
|
||||
required: [planId, containerBookingId, loadingListNo, status, totalPackageCount, totalGrossWeightKg, totalNetWeightKg, totalVolumeCbm, totalQuantity, createdAt, version]
|
||||
properties:
|
||||
planId: {$ref: '#/components/schemas/LongId'}
|
||||
containerBookingId: {$ref: '#/components/schemas/LongId'}
|
||||
loadingListNo: {type: string, description: 装柜清单号}
|
||||
loadingListNo: {type: string, pattern: '^LP-[0-9]{6}-[0-9]{4}$', example: LP-260818-0001, description: 装柜清单号;沿用预装阶段生成的同一编号}
|
||||
warehouseId: {$ref: '#/components/schemas/NullableLongId'}
|
||||
warehouseName: {type: string, nullable: true, description: 仓库名称}
|
||||
containerNo: {type: string, nullable: true, description: 集装箱号}
|
||||
internalContainerNo: {type: string, nullable: true, description: 内部柜号}
|
||||
cabinetNo: {type: string, nullable: true, pattern: '^CAB-[0-9]{6}-[0-9]{4}$', example: CAB-260818-0001, description: 稳定柜次号}
|
||||
status: {type: string, enum: [COMPLETED], description: 装柜清单状态}
|
||||
totalPackageCount: {type: string, pattern: '^[0-9]+$', description: 总箱数}
|
||||
totalGrossWeightKg: {type: number, description: 总毛重,单位千克}
|
||||
@@ -304,14 +369,17 @@ components:
|
||||
version: {type: integer, format: int32, minimum: 0, description: 乐观锁版本}
|
||||
LoadingItem:
|
||||
type: object
|
||||
required: [itemId, accountId, inboundId, inboundDetailId, packageCount, palletCount, grossWeightKg, netWeightKg, volumeCbm, dimensionalWeightKg, productQuantity, unitsPerPackage, loadSequence]
|
||||
required: [itemId, accountId, orderId, orderNo, inboundId, inboundDetailId, packageCount, palletCount, grossWeightKg, netWeightKg, volumeCbm, dimensionalWeightKg, productQuantity, unitsPerPackage, loadSequence, itemVersion]
|
||||
properties:
|
||||
itemId: {$ref: '#/components/schemas/LongId'}
|
||||
accountId: {$ref: '#/components/schemas/LongId'}
|
||||
customerName: {type: string, nullable: true, description: 客户名称快照}
|
||||
orderId: {$ref: '#/components/schemas/LongId'}
|
||||
orderNo: {type: string, description: 订单号快照}
|
||||
inboundId: {$ref: '#/components/schemas/LongId'}
|
||||
inboundDetailId: {$ref: '#/components/schemas/LongId'}
|
||||
inboundCode: {type: string, nullable: true, description: 入仓码}
|
||||
containerNo: {type: string, nullable: true, description: 当前 ACTIVE 柜次的真实集装箱号}
|
||||
skuCode: {type: string, nullable: true, description: SKU 编码快照}
|
||||
destinationWarehouse: {type: string, nullable: true, description: 目的仓名称快照}
|
||||
chineseName: {type: string, nullable: true, description: 中文品名快照}
|
||||
@@ -333,6 +401,7 @@ components:
|
||||
warehouseRemark: {type: string, nullable: true, description: 仓库备注}
|
||||
loadSequence: {type: integer, format: int32, minimum: 0, description: 装柜顺序}
|
||||
frozenAt: {type: string, format: date-time, nullable: true, description: 明细冻结时间}
|
||||
itemVersion: {type: integer, format: int32, minimum: 0, description: 明细乐观锁版本;更新仓库备注时作为 expectedItemVersion}
|
||||
OperationHistory:
|
||||
type: object
|
||||
required: [historyId, actionType, operatorId, occurredAt]
|
||||
|
||||
@@ -1,74 +1,108 @@
|
||||
openapi: 3.0.3
|
||||
info: {title: Phase 3 Ocean Booking API, version: 1.1.0}
|
||||
info: {title: Ocean Booking API, version: 2.0.0}
|
||||
paths:
|
||||
/api/platform/v1/ocean-bookings:
|
||||
get: {summary: List DRAFT and RELEASED bookings, responses: {'200': {description: Booking page}}}
|
||||
post:
|
||||
summary: Create an ocean booking draft with immutable channel and warehouse snapshots
|
||||
summary: Create an editable DRAFT
|
||||
description: Creates only the booking aggregate and container requirements; no containerNo, container row, or allocation is created.
|
||||
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CreateRequest'}}}}
|
||||
responses: {'200': {description: Draft detail}}
|
||||
/api/platform/v1/ocean-bookings/{oceanBookingId}/confirm:
|
||||
post:
|
||||
summary: Confirm booking and atomically create one stable cabinet and editing loading plan per requested quantity
|
||||
responses: {'200': {description: DRAFT detail}}
|
||||
/api/platform/v1/ocean-bookings/{oceanBookingId}:
|
||||
get: {summary: Get booking detail, parameters: [{$ref: '#/components/parameters/OceanBookingId'}], responses: {'200': {description: Detail}}}
|
||||
put:
|
||||
summary: Replace a DRAFT
|
||||
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/UpdateRequest'}}}}
|
||||
responses: {'200': {description: Updated DRAFT}}
|
||||
delete:
|
||||
summary: Delete a DRAFT; RELEASED is immutable
|
||||
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ActionRequest'}}}}
|
||||
responses: {'200': {description: CONFIRMED detail; no real container number required}}
|
||||
responses: {'200': {description: Deleted}}
|
||||
/api/platform/v1/ocean-bookings/{oceanBookingId}/release:
|
||||
post:
|
||||
summary: Confirm released transport data and synchronize associated cabinet transport snapshots
|
||||
description: Does not create cabinets, change channels, replace cabinetNo, or recreate loading plans.
|
||||
summary: Validate and permanently release a DRAFT
|
||||
description: Release validates the persisted aggregate and never requires or creates a real container number.
|
||||
parameters: [{$ref: '#/components/parameters/OceanBookingId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/ReleaseRequest'}}}}
|
||||
responses: {'200': {description: RELEASED detail}}
|
||||
/api/platform/v1/ocean-bookings/channels:
|
||||
get:
|
||||
summary: List ACTIVE channels for an origin warehouse
|
||||
description: Requires platform:firstleg:ocean-booking:list. Channel names are resolved from the current ACTIVE price book and frozen by the server on save.
|
||||
parameters:
|
||||
- {name: originWarehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
responses:
|
||||
'200':
|
||||
description: Channel options
|
||||
content:
|
||||
application/json:
|
||||
schema: {type: array, items: {$ref: '#/components/schemas/ChannelOption'}}
|
||||
components:
|
||||
parameters:
|
||||
OceanBookingId: {name: oceanBookingId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
schemas:
|
||||
CreateRequest:
|
||||
type: object
|
||||
required: [expectedVersion, data]
|
||||
properties:
|
||||
expectedVersion: {type: integer, enum: [0]}
|
||||
data: {$ref: '#/components/schemas/DraftData'}
|
||||
CreateRequest: {type: object, required: [expectedVersion, data], properties: {expectedVersion: {type: integer, enum: [0]}, data: {$ref: '#/components/schemas/DraftData'}}}
|
||||
UpdateRequest: {type: object, required: [expectedVersion, data], properties: {expectedVersion: {type: integer, minimum: 0}, data: {$ref: '#/components/schemas/DraftData'}}}
|
||||
DraftData:
|
||||
type: object
|
||||
required: [customerId, customerName, channelCode, channelName, warehouseId, warehouseName, parties, containerRequirements]
|
||||
description: Fields may be incomplete in DRAFT. RELEASE validates the complete persisted aggregate.
|
||||
properties:
|
||||
customerId: {type: integer, format: int64, minimum: 1}
|
||||
customerName: {type: string, maxLength: 128}
|
||||
channelCode: {type: string, maxLength: 64}
|
||||
channelName: {type: string, maxLength: 128}
|
||||
warehouseId: {type: integer, format: int64, minimum: 1}
|
||||
warehouseName: {type: string, maxLength: 128}
|
||||
billOfLadingNo: {type: string, nullable: true, deprecated: true, description: Must be omitted until release}
|
||||
vesselName: {type: string, nullable: true, deprecated: true, description: Must be omitted until release}
|
||||
voyageNo: {type: string, nullable: true, deprecated: true, description: Must be omitted until release}
|
||||
estimatedSailingAt: {type: string, format: date-time, nullable: true, deprecated: true, description: Must be omitted until release}
|
||||
estimatedArrivalAt: {type: string, format: date-time, nullable: true, deprecated: true, description: Must be omitted until release}
|
||||
parties: {type: array, minItems: 3, items: {type: object}}
|
||||
containerRequirements: {type: array, minItems: 1, maxItems: 20, items: {$ref: '#/components/schemas/ContainerRequirement'}}
|
||||
ContainerRequirement:
|
||||
customerId: {type: integer, format: int64, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
channelCode: {type: string, nullable: true}
|
||||
warehouseId: {type: integer, format: int64, nullable: true}
|
||||
billOfLadingNo: {type: string, nullable: true}
|
||||
shippingCompanyId: {type: integer, format: int64, nullable: true}
|
||||
shippingCompanyName: {type: string, nullable: true}
|
||||
bookingAgentId:
|
||||
type: integer
|
||||
format: int64
|
||||
nullable: true
|
||||
description: Warehouse supplier ID; release validates ACTIVE category BOOKING; booking suppliers do not use domestic/international scope.
|
||||
bookingAgentName:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Read-only consistency hint; the persisted snapshot always comes from Warehouse.
|
||||
vesselName: {type: string, nullable: true}
|
||||
voyageNo: {type: string, nullable: true}
|
||||
departurePortCode: {type: string, nullable: true}
|
||||
departurePortName: {type: string, nullable: true}
|
||||
destinationPortCode: {type: string, nullable: true}
|
||||
destinationPortName: {type: string, nullable: true}
|
||||
placeOfDelivery: {type: string, nullable: true}
|
||||
serviceTerm: {type: string, nullable: true}
|
||||
freightTerm: {type: string, nullable: true}
|
||||
estimatedSailingAt: {type: string, format: date-time, nullable: true}
|
||||
estimatedArrivalAt: {type: string, format: date-time, nullable: true}
|
||||
remark: {type: string, nullable: true}
|
||||
parties: {type: array, nullable: true, items: {$ref: '#/components/schemas/PartyRequest'}}
|
||||
containerRequirements: {type: array, nullable: true, items: {$ref: '#/components/schemas/ContainerRequirementRequest'}}
|
||||
PartyRequest: {type: object, required: [partyType, partyName], properties: {partyType: {type: string, enum: [SHIPPER, CONSIGNEE, NOTIFY_PARTY]}, partyName: {type: string}, address: {type: string, nullable: true}, contactName: {type: string, nullable: true}, contactPhone: {type: string, nullable: true}}}
|
||||
ContainerRequirementRequest: {type: object, required: [containerType, containerKind, quantity, capacityCbm, maxWeightKg], properties: {containerType: {type: string}, containerKind: {type: string}, quantity: {type: integer, minimum: 1}, capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0}, maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0}}}
|
||||
ReleaseRequest: {type: object, required: [expectedVersion], properties: {expectedVersion: {type: integer, minimum: 0}, reason: {type: string, nullable: true}}}
|
||||
ActionRequest: {type: object, required: [expectedVersion], properties: {expectedVersion: {type: integer, minimum: 0}, reason: {type: string, nullable: true}}}
|
||||
ChannelOption:
|
||||
type: object
|
||||
required: [containerType, containerKind, capacityCbm, maxWeightKg, quantity]
|
||||
required: [priceBookVersionId, channelCode, channelName, priceModel, pricingTaxModes]
|
||||
properties:
|
||||
containerType: {type: string, maxLength: 32}
|
||||
containerKind: {type: string, maxLength: 32}
|
||||
capacityCbm: {type: number, exclusiveMinimum: true, minimum: 0}
|
||||
maxWeightKg: {type: number, exclusiveMinimum: true, minimum: 0}
|
||||
quantity: {type: integer, minimum: 1}
|
||||
ActionRequest:
|
||||
priceBookVersionId: {type: integer, format: int64}
|
||||
channelCode: {type: string}
|
||||
channelName: {type: string}
|
||||
priceModel: {type: string}
|
||||
pricingTaxModes: {type: array, items: {type: string}}
|
||||
Detail:
|
||||
type: object
|
||||
required: [expectedVersion]
|
||||
properties: {expectedVersion: {type: integer, minimum: 0}, reason: {type: string, maxLength: 1000}}
|
||||
ReleaseRequest:
|
||||
type: object
|
||||
required: [expectedVersion, billOfLadingNo, vesselName, voyageNo, estimatedSailingAt, estimatedArrivalAt]
|
||||
required: [oceanBooking, parties, containerRequirements, containers, histories]
|
||||
properties:
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
billOfLadingNo: {type: string, maxLength: 128}
|
||||
vesselName: {type: string, maxLength: 128}
|
||||
voyageNo: {type: string, maxLength: 64}
|
||||
estimatedSailingAt: {type: string, format: date-time}
|
||||
estimatedArrivalAt: {type: string, format: date-time}
|
||||
reason: {type: string, maxLength: 1000}
|
||||
oceanBooking:
|
||||
type: object
|
||||
properties:
|
||||
bookingNo: {type: string, pattern: '^OB-[0-9]{6}-[0-9]{4}$', example: OB-260818-0001, description: 平台订舱号;按北京时间创建日逐日编号}
|
||||
parties: {type: array}
|
||||
containerRequirements: {type: array}
|
||||
containers: {type: array, description: Existing containers allocated after port pickup linking}
|
||||
histories: {type: array}
|
||||
|
||||
@@ -11,6 +11,12 @@ paths:
|
||||
parameters: [{$ref: '#/components/parameters/ContainerId'}]
|
||||
post: {operationId: markContainerException, description: '权限:platform:firstleg:container:edit', parameters: [{name: Idempotency-Key, in: header, required: true, schema: {type: string, minLength: 1, maxLength: 128}}], requestBody: {$ref: '#/components/requestBodies/ExceptionRequest'}, responses: {'200': {description: OK}}}
|
||||
delete: {operationId: cancelContainerException, description: '权限:platform:firstleg:container:edit', parameters: [{name: Idempotency-Key, in: header, required: true, schema: {type: string, minLength: 1, maxLength: 128}}], requestBody: {$ref: '#/components/requestBodies/ExceptionRequest'}, responses: {'200': {description: OK}}}
|
||||
/api/platform/v1/containers/{id}/pda/photos/{fileId}/download:
|
||||
get:
|
||||
operationId: downloadContainerPdaPhoto
|
||||
description: '权限:platform:firstleg:container:list;Firstleg按柜次、装柜计划、入库单和文件归属代理下载,不暴露内部服务令牌'
|
||||
parameters: [{$ref: '#/components/parameters/ContainerId'}, {name: fileId, in: path, required: true, schema: {type: integer, format: int64}}]
|
||||
responses: {'200': {description: photo bytes, content: {application/octet-stream: {schema: {type: string, format: binary}}}}, '404': {description: photo missing or not owned by the container}}
|
||||
/api/platform/v1/containers/{id}/documents/{documentType}:
|
||||
get:
|
||||
operationId: getContainerDocument
|
||||
@@ -49,10 +55,10 @@ components:
|
||||
exception: {$ref: '#/components/schemas/ExceptionState'}
|
||||
exceptionHistory: {type: array, items: {$ref: '#/components/schemas/ExceptionHistory'}}
|
||||
timeline: {type: array, items: {$ref: '#/components/schemas/TimelineEntry'}}
|
||||
Container: {type: object, properties: {id: {type: integer, format: int64}, bookingNo: {type: string}, cabinetNo: {type: string}, containerNo: {type: string}, status: {type: string}, warehouseId: {type: integer, format: int64}, warehouseName: {type: string}, voyageNo: {type: string}, vesselName: {type: string}, version: {type: integer, minimum: 0}}}
|
||||
PdaSummary: {type: object, properties: {taskId: {type: integer, format: int64}, loadingPlanId: {type: integer, format: int64}, containerBookingId: {type: integer, format: int64}, taskStatus: {type: string}, taskVersion: {type: integer}, itemCount: {type: integer}, completedItemCount: {type: integer}, unresolvedItemCount: {type: integer}, photoCounts: {type: array, items: {$ref: '#/components/schemas/PhotoCount'}}, photos: {type: array, items: {$ref: '#/components/schemas/Photo'}}, exceptionReasons: {type: array, items: {type: string}}}}
|
||||
Container: {type: object, properties: {id: {type: integer, format: int64}, bookingNo: {type: string, pattern: '^CB-[0-9]{6}-[0-9]{4}$', example: CB-260818-0001}, cabinetNo: {type: string, pattern: '^CAB-[0-9]{6}-[0-9]{4}$', example: CAB-260818-0001}, containerNo: {type: string}, status: {type: string}, warehouseId: {type: integer, format: int64}, warehouseName: {type: string}, voyageNo: {type: string}, vesselName: {type: string}, version: {type: integer, minimum: 0}}}
|
||||
PdaSummary: {type: object, properties: {taskId: {type: integer, format: int64}, loadingPlanId: {type: integer, format: int64}, containerBookingId: {type: integer, format: int64}, taskStatus: {type: string, description: '持久化任务态:LOADING/COMPLETED'}, workflowStatus: {type: string, enum: [IN_PROGRESS, AWAITING_REVIEW, COMPLETED], description: 'PDA作业展示态'}, taskVersion: {type: integer}, itemCount: {type: integer}, completedItemCount: {type: integer}, unresolvedItemCount: {type: integer}, photoCounts: {type: array, items: {$ref: '#/components/schemas/PhotoCount'}}, photos: {type: array, items: {$ref: '#/components/schemas/Photo'}}, exceptionReasons: {type: array, items: {type: string}}}}
|
||||
PhotoCount: {type: object, properties: {evidenceType: {type: string}, count: {type: integer}}}
|
||||
Photo: {type: object, properties: {evidenceId: {type: integer, format: int64}, tenantId: {type: integer, format: int64}, inboundId: {type: integer, format: int64}, evidenceType: {type: string}, fileId: {type: integer, format: int64}, fileName: {type: string}, status: {type: string}, downloadPath: {type: string}, uploadedAt: {type: string, format: date-time}}}
|
||||
Photo: {type: object, properties: {evidenceId: {type: integer, format: int64}, tenantId: {type: integer, format: int64}, inboundId: {type: integer, format: int64}, evidenceType: {type: string}, fileId: {type: integer, format: int64}, fileName: {type: string}, status: {type: string, enum: [ACTIVE, VOID]}, downloadPath: {type: string, nullable: true, description: '仅 ACTIVE 照片提供鉴权下载路径;VOID 历史照片为空'}, uploadedAt: {type: string, format: date-time}}}
|
||||
Document: {type: object, properties: {id: {type: integer, format: int64}, documentType: {type: string}, fileId: {type: integer, format: int64}, fileName: {type: string}, fileHash: {type: string}, uploadedBy: {type: integer, format: int64}, uploadedByName: {type: string}, uploadedAt: {type: string, format: date-time}, version: {type: integer}, completed: {type: boolean}}}
|
||||
ExceptionState: {type: object, properties: {active: {type: boolean}, reason: {type: string}, markedBy: {type: integer, format: int64}, markedByName: {type: string}, markedAt: {type: string, format: date-time}}}
|
||||
ExceptionHistory: {type: object, properties: {id: {type: integer, format: int64}, action: {type: string, enum: [MARK, CANCEL]}, reason: {type: string}, operatorId: {type: integer, format: int64}, operatorName: {type: string}, occurredAt: {type: string, format: date-time}}}
|
||||
|
||||
@@ -6,11 +6,13 @@ paths:
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence/pda:
|
||||
get:
|
||||
summary: List loading evidence for the active PDA worker
|
||||
description: Requires the associated inbound to have entered the loading stage. PDA workflowStatus AWAITING_REVIEW does not bypass this gate; canonical claim or picking advances WAIT_SPLIT to LOADING.
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}]
|
||||
responses:
|
||||
'200': {description: Business envelope; requires platform:firstleg:pda:work}
|
||||
post:
|
||||
summary: Append one PDA-captured loading evidence photo
|
||||
description: Requires the associated inbound process status to be LOADING. PDA workflowStatus AWAITING_REVIEW is a review projection and does not replace the inbound loading-stage gate.
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -33,6 +35,13 @@ paths:
|
||||
application/json: {schema: {$ref: '#/components/schemas/UpsertEvidenceRequest'}}
|
||||
responses:
|
||||
'200': {description: Business envelope; ownership, type and version failures use business error codes}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence/{evidenceId}/content/pda:
|
||||
get:
|
||||
summary: Download one loading evidence photo for the active PDA worker
|
||||
description: Uses the existing platform:firstleg:pda:work authority and validates customer, inbound and evidence ownership before proxying File service content.
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/EvidenceId'}, {$ref: '#/components/parameters/TenantId'}]
|
||||
responses:
|
||||
'200': {description: Authenticated image bytes}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/evidence/{evidenceId}/void/pda:
|
||||
post:
|
||||
summary: Void an incorrect PDA loading evidence photo
|
||||
@@ -85,7 +94,8 @@ paths:
|
||||
'200': {description: Business envelope containing task or null}
|
||||
/api/platform/v1/inbounds/{inboundId}/loading/complete:
|
||||
post:
|
||||
summary: Complete loading only after picking and five evidence types each have an active photo
|
||||
summary: Compatibility endpoint for completing loading after the canonical PDA confirmation
|
||||
description: Canonical PDA confirmation atomically completes task, plan and inbound LOADED. This endpoint remains an idempotent server-side completion primitive; RELEASED booking, completed plan/picking and five ACTIVE evidence types remain hard gates. Truck status is not a prerequisite; the canonical PDA action completes the unique active appointment for the stable container booking when one exists.
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -100,6 +110,19 @@ components:
|
||||
EvidenceId: {name: evidenceId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
schemas:
|
||||
EvidenceResponse:
|
||||
type: object
|
||||
required: [id, inboundId, tenantId, evidenceType, fileId, previewUrl, version, status]
|
||||
properties:
|
||||
id: {type: integer, format: int64}
|
||||
inboundId: {type: integer, format: int64}
|
||||
tenantId: {type: integer, format: int64}
|
||||
evidenceType: {type: string, enum: [PHOTO_STEP_1, PHOTO_STEP_2, PHOTO_STEP_3, PHOTO_STEP_4, SEAL]}
|
||||
fileId: {type: integer, format: int64}
|
||||
fileName: {type: string}
|
||||
previewUrl: {type: string, description: Authenticated PDA image-content path; request with the current bearer token}
|
||||
version: {type: integer}
|
||||
status: {type: string}
|
||||
UpsertEvidenceRequest:
|
||||
type: object
|
||||
required: [expectedVersion, evidenceType, fileId]
|
||||
|
||||
@@ -2,6 +2,7 @@ openapi: 3.0.3
|
||||
info:
|
||||
title: Phase 3 Preload and Container Loading Bridge API
|
||||
version: 1.0.0
|
||||
description: Links must be created while preload=PENDING and loadingPlan=EDITING. Smart-loading push creates all allocated links atomically before any PDA task creation; push-preload never requires containerNo and never advances LOADING. LOADING plans cannot be repaired through post-hoc binding.
|
||||
paths:
|
||||
/api/platform/v1/inbounds/{inboundId}/preload/loading-plans:
|
||||
get:
|
||||
@@ -12,7 +13,7 @@ paths:
|
||||
/api/platform/v1/inbounds/{inboundId}/preload/loading-plans/{loadingPlanId}:
|
||||
post:
|
||||
summary: Atomically link an inbound preload and a container loading plan
|
||||
description: Both optimistic-lock versions must match; either conflict rolls back the whole link operation.
|
||||
description: Both optimistic-lock versions must match; either conflict rolls back the whole link operation. This endpoint never permits post-hoc binding after the loading plan enters LOADING.
|
||||
parameters: [{$ref: '#/components/parameters/InboundId'}, {$ref: '#/components/parameters/LoadingPlanId'}, {$ref: '#/components/parameters/TenantId'}, {$ref: '#/components/parameters/IdempotencyKey'}]
|
||||
requestBody:
|
||||
required: true
|
||||
|
||||
@@ -11,7 +11,14 @@ paths:
|
||||
- {$ref: '#/components/parameters/InboundId'}
|
||||
- {$ref: '#/components/parameters/ContainerBookingId'}
|
||||
responses:
|
||||
'200': {description: Business envelope containing matching aggregates}
|
||||
'200':
|
||||
description: Business envelope containing matching aggregates, each with stable cabinetNo and real containerNo
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data: {type: array, items: {$ref: '#/components/schemas/Aggregate'}}
|
||||
/api/platform/v1/first-leg/outbound-dispatches/detail:
|
||||
get:
|
||||
summary: Query one aggregate and its unified timeline
|
||||
@@ -20,7 +27,17 @@ paths:
|
||||
- {$ref: '#/components/parameters/InboundId'}
|
||||
- {$ref: '#/components/parameters/ContainerBookingId'}
|
||||
responses:
|
||||
'200': {description: Business envelope containing aggregate detail}
|
||||
'200':
|
||||
description: Business envelope containing aggregate detail
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
aggregate: {$ref: '#/components/schemas/Aggregate'}
|
||||
/api/platform/v1/first-leg/outbound-dispatches/check-gate:
|
||||
get:
|
||||
summary: Evaluate the fail-closed domestic outbound gate
|
||||
@@ -59,6 +76,32 @@ components:
|
||||
ContainerBookingId: {name: containerBookingId, in: query, schema: {type: integer, format: int64, minimum: 1}}
|
||||
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
schemas:
|
||||
Aggregate:
|
||||
type: object
|
||||
required: [orderId, orderNo, inboundId, inboundCode, tenantId, inboundStatus, containerBookingId, cabinetNo, loadingPlanId, loadingPlanStatus, dispatchStatus, version, gate]
|
||||
properties:
|
||||
orderId: {type: integer, format: int64}
|
||||
orderNo: {type: string}
|
||||
inboundId: {type: integer, format: int64}
|
||||
inboundCode: {type: string}
|
||||
tenantId: {type: integer, format: int64}
|
||||
customerId: {type: integer, format: int64, nullable: true}
|
||||
customerName: {type: string, nullable: true}
|
||||
inboundStatus: {type: string}
|
||||
containerBookingId: {type: integer, format: int64}
|
||||
cabinetNo: {type: string, description: 稳定柜次号}
|
||||
containerNo: {type: string, nullable: true, description: 真实集装箱号}
|
||||
loadingPlanId: {type: integer, format: int64}
|
||||
loadingPlanStatus: {type: string}
|
||||
pickTaskId: {type: integer, format: int64, nullable: true}
|
||||
pickTaskStatus: {type: string, nullable: true}
|
||||
oceanBookingId: {type: integer, format: int64, nullable: true}
|
||||
oceanBookingStatus: {type: string, nullable: true}
|
||||
truckAppointmentId: {type: integer, format: int64, nullable: true}
|
||||
truckAppointmentStatus: {type: string, nullable: true}
|
||||
dispatchStatus: {type: string}
|
||||
version: {type: integer, minimum: 0}
|
||||
gate: {type: object}
|
||||
ActionRequest:
|
||||
type: object
|
||||
required: [inboundId, containerBookingId, expectedVersion]
|
||||
|
||||
@@ -7,7 +7,6 @@ paths:
|
||||
get:
|
||||
summary: Find the active container picking task in one platform warehouse
|
||||
parameters:
|
||||
- {name: containerNo, in: query, required: false, deprecated: true, description: Compatibility lookup only; taskId is the stable identity, schema: {type: string}}
|
||||
- {name: cabinetNo, in: query, required: false, schema: {type: string}}
|
||||
- {name: loadingPlanId, in: query, required: false, schema: {type: integer, format: int64}}
|
||||
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
@@ -22,7 +21,7 @@ paths:
|
||||
- {name: keyword, in: query, required: false, schema: {type: string, maxLength: 64}}
|
||||
responses:
|
||||
'200':
|
||||
description: Unassigned tasks and tasks assigned to the current operator; no customer selector required
|
||||
description: Unassigned tasks and tasks assigned to the current operator, including AWAITING_REVIEW tasks with zero unresolved items so PDA can enter evidence and final completion; no customer selector required
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -35,10 +34,13 @@ paths:
|
||||
- {$ref: '#/components/parameters/TaskId'}
|
||||
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
|
||||
responses:
|
||||
'200': {description: Task with current transport display fields}
|
||||
'200':
|
||||
description: Task with current transport display and ACTIVE container lifecycle fields
|
||||
content: {application/json: {schema: {$ref: '#/components/schemas/Task'}}}
|
||||
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/claim:
|
||||
post:
|
||||
summary: Atomically claim an unassigned task for the authenticated operator
|
||||
description: Claiming the canonical PDA task idempotently starts its plan and advances every associated inbound from WAIT_SPLIT to LOADING with optimistic locking; workflowStatus may later become AWAITING_REVIEW while the task remains LOADING until manual completion.
|
||||
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -67,6 +69,7 @@ paths:
|
||||
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/pick:
|
||||
post:
|
||||
summary: Increment a picked item owned by the authenticated assignee
|
||||
description: The first successful pick also repairs the same WAIT_SPLIT to LOADING transition when the plan was already started, without using containerNo as an association key.
|
||||
parameters: [{$ref: '#/components/parameters/TaskId'}, {$ref: '#/components/parameters/ItemId'}]
|
||||
responses: {'200': {description: Updated task}}
|
||||
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/items/{itemId}/partial:
|
||||
@@ -82,6 +85,7 @@ paths:
|
||||
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/batch:
|
||||
post:
|
||||
summary: Atomically submit the complete online PDA draft once
|
||||
description: A batch containing PICK or PARTIAL idempotently advances associated inbound process status to LOADING before evidence operations become available.
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/TaskId'}
|
||||
- {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
@@ -102,7 +106,7 @@ paths:
|
||||
/api/platform/v1/first-leg/pda-loading/tasks/{taskId}/complete:
|
||||
post:
|
||||
summary: PDA拣货全部处理后人工确认装柜完成
|
||||
description: PDA拣货只锁定数量和仓储资源,本接口执行结算、释放资源并生成装柜清单。
|
||||
description: PDA拣货只锁定数量和仓储资源。本接口先无写入校验全部关联入库单的RELEASED订舱、稳定bridge和五类ACTIVE证据;拖车状态不是前置门禁。若当前稳定containerBookingId没有有效拖车则直接继续;恰有一条有效拖车时由服务端加锁推进为COMPLETED并写历史;多条有效拖车或并发修改返回A0409且全部Firstleg本地状态回滚。随后在同一事务内结算资源、完成task/plan并推进inbound为LOADED。同operationId重放返回冻结结果,不重复推进拖车或写历史。
|
||||
parameters: [{$ref: '#/components/parameters/TaskId'}]
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -122,13 +126,19 @@ components:
|
||||
schemas:
|
||||
Task:
|
||||
type: object
|
||||
required: [id, loadingPlanId, containerBookingId, cabinetNo, taskStatus, workflowStatus, version, items]
|
||||
required: [id, loadingPlanId, containerBookingId, inboundId, inboundVersion, loadingVersion, cabinetNo, sourceType, recordStatus, taskStatus, workflowStatus, version, items]
|
||||
properties:
|
||||
id: {type: integer, format: int64}
|
||||
loadingPlanId: {type: integer, format: int64}
|
||||
containerBookingId: {type: integer, format: int64}
|
||||
inboundId: {type: integer, format: int64, description: Stable inbound identity selected from the task item; evidence identity is taskId+inboundId+loadingPlanId+containerBookingId}
|
||||
inboundVersion: {type: integer, minimum: 0, description: Current fl_inbound_order.version for inboundId; never inferred from PDA task version}
|
||||
loadingVersion: {type: integer, minimum: 0, description: Compatibility alias of inboundVersion from the same fl_inbound_order row; /loading/complete uses this inbound loading-stage version}
|
||||
cabinetNo: {type: string}
|
||||
currentContainerNo: {type: string, nullable: true, description: Mutable transport display field; changing it never resynchronizes the task or item versions and it is never task identity}
|
||||
sourceType: {type: string, enum: [SIMULATION, OCEAN_BOOKING], description: Source of the surviving stable container identity}
|
||||
recordStatus: {type: string, enum: [ACTIVE], description: Task detail is served only from the ACTIVE container booking currently owned by the loading plan}
|
||||
mergedIntoContainerBookingId: {type: integer, format: int64, nullable: true, description: Always null for the surviving ACTIVE operational container; populated only on hidden MERGED source history}
|
||||
taskStatus: {type: string, enum: [LOADING, COMPLETED]}
|
||||
workflowStatus: {type: string, enum: [UNASSIGNED, IN_PROGRESS, AWAITING_REVIEW, COMPLETED]}
|
||||
version: {type: integer, minimum: 0}
|
||||
@@ -159,17 +169,20 @@ components:
|
||||
version: {type: integer, minimum: 0}
|
||||
PendingTask:
|
||||
type: object
|
||||
required: [taskId, loadingPlanId, containerBookingId, cabinetNo, warehouseId, taskStatus, workflowStatus, version, assignmentVersion, itemCount, pendingItemCount]
|
||||
required: [taskId, loadingPlanId, containerBookingId, cabinetNo, sourceType, recordStatus, warehouseId, taskStatus, workflowStatus, version, assignmentVersion, itemCount, pendingItemCount]
|
||||
properties:
|
||||
taskId: {type: integer, format: int64}
|
||||
loadingPlanId: {type: integer, format: int64}
|
||||
containerBookingId: {type: integer, format: int64}
|
||||
cabinetNo: {type: string}
|
||||
currentContainerNo: {type: string, nullable: true}
|
||||
sourceType: {type: string, enum: [SIMULATION, OCEAN_BOOKING]}
|
||||
recordStatus: {type: string, enum: [ACTIVE], description: Enforced by the server-side pending-task query; PDA clients must not infer or relax this filter}
|
||||
mergedIntoContainerBookingId: {type: integer, format: int64, nullable: true, description: Always null for an ACTIVE pending task}
|
||||
warehouseId: {type: integer, format: int64}
|
||||
warehouseName: {type: string, nullable: true}
|
||||
taskStatus: {type: string, enum: [LOADING]}
|
||||
workflowStatus: {type: string, enum: [UNASSIGNED, IN_PROGRESS]}
|
||||
workflowStatus: {type: string, enum: [UNASSIGNED, IN_PROGRESS, AWAITING_REVIEW], description: Server projection aligned with task detail; assigned LOADING tasks with pendingItemCount=0 remain visible as AWAITING_REVIEW}
|
||||
version: {type: integer, minimum: 0}
|
||||
assignmentVersion: {type: integer, minimum: 0}
|
||||
itemCount: {type: integer, minimum: 1}
|
||||
@@ -195,7 +208,6 @@ components:
|
||||
warehouseId: {type: integer, format: int64, minimum: 1}
|
||||
loadingPlanId: {type: integer, format: int64, minimum: 1}
|
||||
containerBookingId: {type: integer, format: int64, minimum: 1}
|
||||
containerNo: {type: string, maxLength: 64, nullable: true, deprecated: true, description: Accepted during compatibility period but ignored for identity and idempotency}
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
inventorySnapshotToken: {type: string, minLength: 64, maxLength: 64}
|
||||
items:
|
||||
|
||||
@@ -1,28 +1,41 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Truck Appointment API
|
||||
version: 1.1.0
|
||||
version: 1.2.0
|
||||
description: Truck appointments are independent operational records. Ocean booking, container booking and loading plan are optional links. Draft creation/editing does not require a bill of lading or attachment; DISPATCH requires a nonblank billOfLadingNo and at least one active BILL_OF_LADING attachment owned by the appointment customer. When linked to a RELEASED booking, the server freezes and validates the booking bill of lading snapshot. After a PDA loading transaction commits, the server best-effort completes the unique active appointment explicitly linked to that container in an isolated transaction; absence, duplicates or concurrent truck edits never roll back loading completion.
|
||||
paths:
|
||||
/api/platform/v1/truck-appointments:
|
||||
get:
|
||||
summary: Query truck appointments
|
||||
parameters:
|
||||
- {name: customerId, in: query, description: Filter by customer, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
responses:
|
||||
'200': {description: Paged truck appointments}
|
||||
'200':
|
||||
description: Paged truck appointments with stable booking/container snapshots and attachment count
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
records: {type: array, items: {$ref: '#/components/schemas/DetailStableReferences'}}
|
||||
post:
|
||||
summary: Create a truck appointment linked to a released ocean booking
|
||||
summary: Create an independent truck appointment with optional fulfillment links
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/SaveRequest'}
|
||||
responses:
|
||||
'200': {description: Created appointment}
|
||||
'200': {description: Created appointment, content: {application/json: {schema: {$ref: '#/components/schemas/DetailEnvelope'}}}}
|
||||
/api/platform/v1/truck-appointments/{id}:
|
||||
parameters: [{$ref: '#/components/parameters/AppointmentId'}]
|
||||
get:
|
||||
summary: Query a truck appointment
|
||||
responses:
|
||||
'200': {description: Appointment detail}
|
||||
'200': {description: Appointment detail, content: {application/json: {schema: {$ref: '#/components/schemas/DetailStableReferences'}}}}
|
||||
put:
|
||||
summary: Update an editable truck appointment with optimistic locking
|
||||
requestBody:
|
||||
@@ -76,25 +89,29 @@ paths:
|
||||
parameters: [{$ref: '#/components/parameters/AppointmentId'}]
|
||||
get:
|
||||
summary: Query attachment references owned by the appointment
|
||||
description: 'Requires platform:firstleg:truck-appointment:list.'
|
||||
responses:
|
||||
'200': {description: Attachment references}
|
||||
'200': {description: Attachment references, content: {application/json: {schema: {type: array, items: {$ref: '#/components/schemas/Attachment'}}}}}
|
||||
post:
|
||||
summary: Reference a file already owned by the appointment customer and business scope
|
||||
description: 'Requires platform:firstleg:truck-appointment:edit. The file must belong to customerId + TRUCK_APPOINTMENT_ATTACHMENT + appointmentId.'
|
||||
requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/AttachmentRequest'}}}}
|
||||
responses:
|
||||
'200': {description: Created attachment reference}
|
||||
'200': {description: Created attachment reference, content: {application/json: {schema: {$ref: '#/components/schemas/Attachment'}}}}
|
||||
/api/platform/v1/truck-appointments/{id}/attachments/upload:
|
||||
parameters: [{$ref: '#/components/parameters/AppointmentId'}]
|
||||
post:
|
||||
summary: Upload and reference a truck appointment attachment
|
||||
description: 'Requires platform:firstleg:truck-appointment:edit.'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
required: [file]
|
||||
required: [attachmentType, file]
|
||||
properties:
|
||||
attachmentType: {type: string, maxLength: 32}
|
||||
attachmentType: {type: string, enum: [BILL_OF_LADING, DISPATCH_ORDER, DRIVER_DOCUMENT, VEHICLE_DOCUMENT, OTHER], description: Attachment classification; BILL_OF_LADING is required before DISPATCH}
|
||||
file: {type: string, format: binary}
|
||||
responses:
|
||||
'200': {description: Uploaded attachment reference}
|
||||
@@ -104,6 +121,7 @@ paths:
|
||||
- {$ref: '#/components/parameters/AttachmentId'}
|
||||
delete:
|
||||
summary: Delete an attachment reference owned by the appointment
|
||||
description: 'Requires platform:firstleg:truck-appointment:edit. Deletes the appointment reference only; the File object is retained.'
|
||||
responses:
|
||||
'200': {description: Reference deleted}
|
||||
components:
|
||||
@@ -120,6 +138,38 @@ components:
|
||||
IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}}
|
||||
RequestHash: {name: X-Request-Hash, in: header, required: false, schema: {type: string, pattern: '^[0-9a-fA-F]{64}$'}}
|
||||
schemas:
|
||||
DetailEnvelope:
|
||||
type: object
|
||||
required: [code, message, data, timestamp]
|
||||
properties:
|
||||
code: {type: string, example: '200'}
|
||||
message: {type: string}
|
||||
data:
|
||||
allOf: [{$ref: '#/components/schemas/DetailStableReferences'}]
|
||||
nullable: true
|
||||
requestId: {type: string, nullable: true}
|
||||
traceId: {type: string, nullable: true}
|
||||
timestamp: {type: string, format: date-time}
|
||||
AttachmentRequest:
|
||||
type: object
|
||||
required: [fileId, attachmentType]
|
||||
properties:
|
||||
fileId: {type: integer, format: int64, minimum: 1}
|
||||
attachmentType: {type: string, enum: [BILL_OF_LADING, DISPATCH_ORDER, DRIVER_DOCUMENT, VEHICLE_DOCUMENT, OTHER], description: Attachment classification; BILL_OF_LADING is required before DISPATCH}
|
||||
fileName: {type: string, maxLength: 255, description: Ignored; authoritative metadata comes from File service}
|
||||
fileHash: {type: string, maxLength: 128, description: Ignored; authoritative metadata comes from File service}
|
||||
Attachment:
|
||||
type: object
|
||||
properties:
|
||||
id: {type: integer, format: int64}
|
||||
appointmentId: {type: integer, format: int64}
|
||||
fileId: {type: integer, format: int64}
|
||||
attachmentType: {type: string, enum: [BILL_OF_LADING, DISPATCH_ORDER, DRIVER_DOCUMENT, VEHICLE_DOCUMENT, OTHER]}
|
||||
fileName: {type: string}
|
||||
fileHash: {type: string}
|
||||
visibility: {type: string, enum: [PLATFORM]}
|
||||
uploadedBy: {type: integer, format: int64}
|
||||
uploadedAt: {type: string, format: date-time}
|
||||
ActionRequest:
|
||||
type: object
|
||||
required: [expectedVersion]
|
||||
@@ -128,10 +178,41 @@ components:
|
||||
reason: {type: string, maxLength: 500}
|
||||
SaveRequest:
|
||||
type: object
|
||||
required: [oceanBookingId, plannedLoadingAt, expectedVersion]
|
||||
required: [customerId, carrierId, plannedLoadingAt, expectedVersion]
|
||||
properties:
|
||||
oceanBookingId: {type: integer, format: int64, minimum: 1}
|
||||
containerBookingId: {type: integer, format: int64, minimum: 1}
|
||||
loadingPlanId: {type: integer, format: int64, minimum: 1}
|
||||
outboundOrderId: {type: integer, format: int64, minimum: 1, nullable: true}
|
||||
oceanBookingId: {type: integer, format: int64, minimum: 1, nullable: true, description: Optional booking association; no RELEASED-state prerequisite. When supplied, customer and warehouse must match.}
|
||||
containerBookingId: {type: integer, format: int64, minimum: 1, nullable: true, description: Optional ACTIVE container association. When supplied, customer and warehouse must match.}
|
||||
loadingPlanId: {type: integer, format: int64, minimum: 1, nullable: true, description: Optional loading-plan association. It may be supplied only with containerBookingId and must be that container's current plan.}
|
||||
warehouseId: {type: integer, format: int64, minimum: 1, nullable: true, description: Optional warehouse scope. Linked booking/container warehouse facts must agree; when omitted the linked fact is frozen if available.}
|
||||
customerId: {type: integer, format: int64, minimum: 1, description: Required ACTIVE IAM customer used for isolation and attachment ownership.}
|
||||
customerName: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint only; persisted value is resolved from IAM customer master data.}
|
||||
carrierId: {type: integer, format: int64, minimum: 1, description: Required ACTIVE Warehouse supplier ID validated as TRUCKING.}
|
||||
carrierName: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint only; the persisted snapshot comes from Warehouse supplier master data.}
|
||||
billOfLadingNo: {type: string, maxLength: 128, nullable: true, description: Optional on draft; required before DISPATCH and server-frozen from a linked RELEASED booking when present.}
|
||||
plannedLoadingAt: {type: string, format: date-time}
|
||||
expectedVersion: {type: integer, minimum: 0}
|
||||
expectedVersion: {type: integer, minimum: 0, description: Must be 0 on create; current version on edit.}
|
||||
DetailStableReferences:
|
||||
type: object
|
||||
required: [appointmentNo, customerId, customerName, carrierId, carrierName, attachmentCount]
|
||||
description: Stable IDs plus server-resolved saved resource names returned by appointment detail. Fulfillment associations remain null for an independently created appointment.
|
||||
properties:
|
||||
appointmentNo: {type: string, pattern: '^TA-[0-9]{6}-[0-9]{4}$', example: TA-260818-0001, description: 派车单号;按北京时间创建日逐日编号}
|
||||
oceanBookingId: {type: integer, format: int64, nullable: true}
|
||||
oceanBookingNo: {type: string, nullable: true, pattern: '^OB-[0-9]{6}-[0-9]{4}$', example: OB-260818-0001}
|
||||
containerBookingId: {type: integer, format: int64, nullable: true}
|
||||
cabinetNo: {type: string, nullable: true, pattern: '^CAB-[0-9]{6}-[0-9]{4}$', example: CAB-260818-0001}
|
||||
containerNo: {type: string, nullable: true}
|
||||
loadingPlanId: {type: integer, format: int64, nullable: true}
|
||||
warehouseId: {type: integer, format: int64, nullable: true}
|
||||
warehouseName: {type: string, nullable: true}
|
||||
customerId: {type: integer, format: int64}
|
||||
customerName: {type: string, description: Frozen from ACTIVE IAM customer master data; request input cannot override it}
|
||||
shippingCompanyId: {type: integer, format: int64, nullable: true}
|
||||
shippingCompanyName: {type: string, nullable: true}
|
||||
bookingAgentId: {type: integer, format: int64, nullable: true}
|
||||
bookingAgentName: {type: string, nullable: true}
|
||||
carrierId: {type: integer, format: int64}
|
||||
carrierName: {type: string}
|
||||
billOfLadingNo: {type: string, maxLength: 128, nullable: true, description: Frozen bill of lading snapshot used by dispatch readiness.}
|
||||
attachmentCount: {type: integer, format: int64, minimum: 0, description: Number of attachment references owned by this appointment}
|
||||
|
||||
Reference in New Issue
Block a user